mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-10 03:21:30 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
debba76dc4 | ||
|
|
163a7c2e87 | ||
|
|
fdcfd3fc71 | ||
|
|
2178a14952 | ||
|
|
34bdea0f1c | ||
|
|
2815d5f5cb | ||
|
|
562b28caa7 | ||
|
|
38a4141eef | ||
|
|
525d9b3cf5 | ||
|
|
0eb49deaa8 | ||
|
|
239e491382 | ||
|
|
ed75cb8031 | ||
|
|
ba0e7d5f2a | ||
|
|
0308d3fe2b | ||
|
|
a2013276d5 | ||
|
|
eca337ee55 | ||
|
|
996d6d25b7 | ||
|
|
35a9123bf8 | ||
|
|
5d4e32ef9d | ||
|
|
29ccd1a5a1 | ||
|
|
8ff9ae6b32 | ||
|
|
b52110f642 | ||
|
|
a7b486cef1 | ||
|
|
a33c610149 | ||
|
|
1cc8a24b3c | ||
|
|
75661e7527 | ||
|
|
765ba65188 | ||
|
|
db7be5736b | ||
|
|
23f9cf8618 | ||
|
|
cf5fb8baaa | ||
|
|
6b88330540 | ||
|
|
9653e1f028 | ||
|
|
a860a71016 | ||
|
|
07c839f003 | ||
|
|
dc1616b7ce | ||
|
|
5553774f6f | ||
|
|
eee0233abb | ||
|
|
f8509ff325 | ||
|
|
a3e504ec49 | ||
|
|
105b6dbddc | ||
|
|
24f5b2675b | ||
|
|
da075bb261 | ||
|
|
3ae401a2a4 | ||
|
|
104c92fa5d | ||
|
|
29f1138ec6 | ||
|
|
e7419d2fa5 | ||
|
|
6d0b8d1129 | ||
|
|
6332ba3025 | ||
|
|
45021bda3f | ||
|
|
64fa27cdad | ||
|
|
e75c1e9923 | ||
|
|
a63968377a | ||
|
|
066e9608a1 | ||
|
|
b273de1e99 | ||
|
|
739bbe3aa6 | ||
|
|
1ed6a622f1 | ||
|
|
5d535f6d54 | ||
|
|
c79e536b17 | ||
|
|
d73ec9b645 | ||
|
|
0726204505 | ||
|
|
a0a27658bd | ||
|
|
db6b9ba4a3 | ||
|
|
38ae16f425 | ||
|
|
a569625ce9 | ||
|
|
42b756480b | ||
|
|
4a5104bfcf | ||
|
|
6e24901d95 | ||
|
|
e5a38bbd51 | ||
|
|
7ffc9db413 | ||
|
|
63274f6f61 | ||
|
|
b907cb667a | ||
|
|
5272008f54 | ||
|
|
4e772003c8 | ||
|
|
48cddde84a | ||
|
|
acdc6179bb | ||
|
|
fadd954618 | ||
|
|
e0295a726a | ||
|
|
f442c8ab5f | ||
|
|
885c165716 | ||
|
|
bd90e1c1a3 | ||
|
|
1b995917ce | ||
|
|
4e3359ec19 | ||
|
|
8ab536a968 | ||
|
|
ca25e51812 | ||
|
|
56e5c9fcec | ||
|
|
cd0973d213 | ||
|
|
72efdbd916 | ||
|
|
13373f4873 | ||
|
|
edad44c950 | ||
|
|
6cd800f6ae | ||
|
|
0ee6aa948e | ||
|
|
7974533452 | ||
|
|
6c8dd72cd8 | ||
|
|
6b45efa7bc | ||
|
|
224db6a4c8 | ||
|
|
ef72584568 | ||
|
|
2a6551a62a | ||
|
|
9ab02b12be | ||
|
|
e6b3b96400 | ||
|
|
47dcb54c1f | ||
|
|
6cd4989651 | ||
|
|
dd9f4be2aa | ||
|
|
36885e35d7 | ||
|
|
49f27c68ff | ||
|
|
5d6c5af8be | ||
|
|
0a2b3ede59 | ||
|
|
07a4619a70 | ||
|
|
37b8585df4 | ||
|
|
71189737de | ||
|
|
8781b5c7d3 | ||
|
|
ed4a81e497 | ||
|
|
e1bbaee64c | ||
|
|
d7afd20346 | ||
|
|
fe0a6c8d07 | ||
|
|
a328bd578e | ||
|
|
7f4d701b19 | ||
|
|
06a951ef7f | ||
|
|
51b3cafed7 | ||
|
|
81988fc1ef | ||
|
|
1f00ce1812 | ||
|
|
2295c4abd8 | ||
|
|
48230ba533 | ||
|
|
4737d28644 | ||
|
|
5c4ad2884e | ||
|
|
e30966bfd0 | ||
|
|
c5d8e1a75c | ||
|
|
c2cc7a278f | ||
|
|
5cad1e4c39 | ||
|
|
8a6b628bee | ||
|
|
f78df09c38 | ||
|
|
40035908f3 | ||
|
|
b0de19f3e0 | ||
|
|
a47bcf6cc1 | ||
|
|
bc307d4b63 | ||
|
|
bdb8b3f0fe | ||
|
|
6053b61b5b | ||
|
|
7489f387c1 | ||
|
|
31be6766c6 | ||
|
|
6b552cfba1 | ||
|
|
848a67252f | ||
|
|
8d5385d1d7 | ||
|
|
7d8d8f39f7 | ||
|
|
760d3bbecf | ||
|
|
ac070a6b2b | ||
|
|
94f09d2093 | ||
|
|
b8e6443710 | ||
|
|
8823b47a05 | ||
|
|
a3351e17f1 | ||
|
|
c1544e88aa | ||
|
|
4e68eb12ab | ||
|
|
428050904c | ||
|
|
ee806a0e2c | ||
|
|
e318a1d5a7 | ||
|
|
ca0986064d | ||
|
|
057846e45b | ||
|
|
d4c1cf4853 | ||
|
|
9708536cd8 | ||
|
|
f61348e53f | ||
|
|
556e158020 | ||
|
|
d9337a2589 | ||
|
|
cc476af1fe | ||
|
|
4365b77577 | ||
|
|
f7c2bfe530 | ||
|
|
44b873af5b | ||
|
|
4ff4957a45 | ||
|
|
3239edccb9 | ||
|
|
fe0da105b5 | ||
|
|
4c361bc648 | ||
|
|
9d17ccf4f3 | ||
|
|
25a97b1d8c | ||
|
|
cd9dd6511d | ||
|
|
37c29144d8 | ||
|
|
3c2591f71f | ||
|
|
6ef7eae423 | ||
|
|
4959b8e20e | ||
|
|
7e1e293547 | ||
|
|
4bd53c0b96 | ||
|
|
47f95020f0 | ||
|
|
e4506d5a3e | ||
|
|
b612bb5290 | ||
|
|
f453ee7954 | ||
|
|
0b3f0de625 | ||
|
|
80a680a7ee | ||
|
|
762c2cb170 | ||
|
|
7b4d48a69f | ||
|
|
bd500ae249 | ||
|
|
ad5c890603 | ||
|
|
f46889daae | ||
|
|
b82d9be0ae | ||
|
|
449b08e9d5 | ||
|
|
a2fe0330eb | ||
|
|
e3c5ebd937 | ||
|
|
afdcae3ee8 | ||
|
|
d2e3d8db28 | ||
|
|
10ac66e734 | ||
|
|
5ebd0b4ed1 | ||
|
|
227ae4ca74 | ||
|
|
379614ca1c | ||
|
|
24e807f520 | ||
|
|
2463924d69 | ||
|
|
6e0da33c60 | ||
|
|
fd54b26532 | ||
|
|
ea0233716e | ||
|
|
d891230800 | ||
|
|
1bf564955c | ||
|
|
f548ebb1a2 | ||
|
|
6e1d48a625 | ||
|
|
b73d8bb569 | ||
|
|
ed5e67425c | ||
|
|
d76d8a7d9c | ||
|
|
8ef9e99960 | ||
|
|
cd08339051 | ||
|
|
85ee49b2cd | ||
|
|
5f3453bc38 | ||
|
|
d8720ab9d8 | ||
|
|
04a1388640 | ||
|
|
db87f664b8 | ||
|
|
ae5900aaa1 | ||
|
|
4b67a3c59e | ||
|
|
c6fa39fde0 | ||
|
|
051c82daed | ||
|
|
de9db9bcea | ||
|
|
9a1cf7525e | ||
|
|
90984ce3cc | ||
|
|
dc1d9b674a | ||
|
|
47c9fda3c9 | ||
|
|
452f3f4c43 | ||
|
|
6c64a024eb | ||
|
|
59dbc4727b | ||
|
|
57e17d9b14 | ||
|
|
7b0b1a7004 | ||
|
|
fd99d9f3bc | ||
|
|
1386c97ad2 | ||
|
|
9d19ef4969 | ||
|
|
ffc6fc5e24 | ||
|
|
11720b8113 | ||
|
|
308d89ead7 | ||
|
|
c5cee1f945 | ||
|
|
ee99acfd71 | ||
|
|
08414d1f5c | ||
|
|
18aebfedc2 | ||
|
|
c8173aaf62 | ||
|
|
c975145914 | ||
|
|
2fecfc7fca | ||
|
|
a4b3fbcebe | ||
|
|
f01663550c | ||
|
|
24817543e0 | ||
|
|
b50ddfc87a | ||
|
|
302dc9460f | ||
|
|
351e58b856 | ||
|
|
8aa2eb9741 | ||
|
|
90165492a1 | ||
|
|
afa6776b1d | ||
|
|
60edff831a | ||
|
|
98bd071fd3 | ||
|
|
ac5fb991a2 | ||
|
|
2e5e5f3c35 | ||
|
|
9d0a55285d | ||
|
|
9b4a91fd29 | ||
|
|
b4fe5198b3 | ||
|
|
c20df730e3 | ||
|
|
37ed625bab | ||
|
|
3446774547 | ||
|
|
a5c47a568e | ||
|
|
e70ea21a3a | ||
|
|
c310e32dd0 | ||
|
|
248f5efbd6 | ||
|
|
a03c11d7d0 | ||
|
|
2e9fade00a | ||
|
|
3dc5b21b78 | ||
|
|
dd57e0ae63 | ||
|
|
bdc8cd1cb1 | ||
|
|
cacf15a065 | ||
|
|
989962f5a9 | ||
|
|
7c15f5a537 | ||
|
|
6643bf3e1c | ||
|
|
60256eb52f | ||
|
|
74de5350d2 | ||
|
|
cd54cc6822 | ||
|
|
2fba62867f | ||
|
|
5f330cbfa5 | ||
|
|
9bad6f2bba | ||
|
|
0496ffc007 | ||
|
|
bb824b5679 | ||
|
|
1ea4b5ce7c | ||
|
|
f5a4ea55f5 | ||
|
|
aecd9a1ecb | ||
|
|
a797e1dbf4 | ||
|
|
9c664b875a | ||
|
|
375d05ed13 | ||
|
|
a13684c80c | ||
|
|
678269039b | ||
|
|
ece5935ef9 | ||
|
|
341baf6918 | ||
|
|
4acf66edfd | ||
|
|
10cfe3daa8 | ||
|
|
be3839a9df | ||
|
|
0023b4b2ce | ||
|
|
17e0f19859 | ||
|
|
383b2abe58 | ||
|
|
5ce54cbbab | ||
|
|
2c2dcd58f2 | ||
|
|
ee76898cab | ||
|
|
a41e84749a | ||
|
|
d68658b7fb | ||
|
|
c833a71d2c | ||
|
|
f1b135978c | ||
|
|
6f04c38fda | ||
|
|
cc979581eb | ||
|
|
fe0ce2b78b | ||
|
|
b8382416ee | ||
|
|
9bcbd27988 | ||
|
|
3845844b83 | ||
|
|
d7a29a66b4 | ||
|
|
6a7144d41c | ||
|
|
12cf639dfe | ||
|
|
4604a995e9 | ||
|
|
0b9ee6055f | ||
|
|
acd51a7dad | ||
|
|
6d98da16b0 | ||
|
|
3407e765c3 | ||
|
|
2ef54a7e01 | ||
|
|
aac2533075 | ||
|
|
fd2be071f8 | ||
|
|
947d2a3514 | ||
|
|
3fd630e062 | ||
|
|
471dfcd478 | ||
|
|
8e8f56b5e8 | ||
|
|
744a433595 | ||
|
|
f9ab3711eb | ||
|
|
96cb22dbaa | ||
|
|
f2f09ce2ed | ||
|
|
6d1ab7ebfd | ||
|
|
356d565ce6 | ||
|
|
8ec0cb4f6c | ||
|
|
fd9f020afa | ||
|
|
52402d02c5 | ||
|
|
1373ba167d | ||
|
|
668aa9a9ac | ||
|
|
3585e046de | ||
|
|
2760b5b0c2 | ||
|
|
441cd2b82a | ||
|
|
fe2134cb8c | ||
|
|
04e2541694 | ||
|
|
878e612584 | ||
|
|
8dcf132b0c | ||
|
|
da7cf7e01d | ||
|
|
317f255eae | ||
|
|
b87e1c4f48 | ||
|
|
3cc042e2a6 | ||
|
|
2cb946ec8b | ||
|
|
8459f39933 | ||
|
|
56e57d15bc | ||
|
|
975536ddbf | ||
|
|
df997f6d23 | ||
|
|
cb3064201e | ||
|
|
48d589ffce | ||
|
|
16c740ffd8 | ||
|
|
80372f54a6 | ||
|
|
a6a068e09e | ||
|
|
227f347e70 | ||
|
|
438ac4ca4b | ||
|
|
69a5faaaab | ||
|
|
1b28ea4945 | ||
|
|
55a400f521 | ||
|
|
d77560f1ea | ||
|
|
786adb23ac | ||
|
|
a03dd57166 | ||
|
|
bb642d3010 | ||
|
|
21e39eea2b | ||
|
|
761f5d7a5c | ||
|
|
e0408c30ae | ||
|
|
78136e0191 | ||
|
|
77e55bb577 | ||
|
|
89b99428f9 | ||
|
|
beeb660103 | ||
|
|
9969474326 | ||
|
|
1307648d08 | ||
|
|
eeafd335fa | ||
|
|
32a52f5d01 | ||
|
|
08f3313a3b | ||
|
|
98f30469eb | ||
|
|
36309050c4 | ||
|
|
5a3b88bdd6 | ||
|
|
000d863ae0 | ||
|
|
38d73e9da2 | ||
|
|
5f649bf2be | ||
|
|
9609eca940 | ||
|
|
5f71f48973 | ||
|
|
5aa6acf031 | ||
|
|
ccfa448a66 | ||
|
|
1090eeb159 | ||
|
|
3a66829933 | ||
|
|
045e72661a | ||
|
|
b9ef2ff133 | ||
|
|
80136435d2 | ||
|
|
9c861f13be | ||
|
|
1cff87c758 | ||
|
|
ffc0c1d77e | ||
|
|
2148ce9dec | ||
|
|
390628b1e3 | ||
|
|
6e753d7d99 | ||
|
|
c07bcbea53 | ||
|
|
c0cba36dec | ||
|
|
34b85e115b | ||
|
|
40c3158d7e | ||
|
|
64728e38a4 | ||
|
|
979b54178e | ||
|
|
3a33c5e1ec | ||
|
|
aeeb491a49 | ||
|
|
38fb6f0a62 | ||
|
|
b094151abb | ||
|
|
e5e19edc65 | ||
|
|
2cd7349cac | ||
|
|
dc1a32f7f7 | ||
|
|
315b28862a | ||
|
|
f6edb9a607 | ||
|
|
93072b926e | ||
|
|
c161e3bc35 | ||
|
|
3bda32d6f1 | ||
|
|
fea632b0e9 | ||
|
|
d959bc968a | ||
|
|
e33221b52a | ||
|
|
c32d321b06 | ||
|
|
18d6d58a2b | ||
|
|
996794134f | ||
|
|
02996fbe2e | ||
|
|
e85471a239 | ||
|
|
0e2de25f85 | ||
|
|
402be572ea | ||
|
|
1317ce1aea | ||
|
|
884e39c58d | ||
|
|
0bb0b96623 | ||
|
|
61c1b2bb1b | ||
|
|
979b7e6479 | ||
|
|
862082915b | ||
|
|
fba6f36cb8 | ||
|
|
0cb2436a59 | ||
|
|
18c259e19a | ||
|
|
e3a2ab0347 | ||
|
|
15bc0e0813 | ||
|
|
a328dbd213 | ||
|
|
09a6566be0 | ||
|
|
3eaa942e58 | ||
|
|
40ce42c87c | ||
|
|
a14bdb82b1 | ||
|
|
ee5d43486c | ||
|
|
02c3977e9d | ||
|
|
a509d40bf6 | ||
|
|
4501832997 | ||
|
|
56e862edf8 | ||
|
|
945a086edf | ||
|
|
72f5748b28 | ||
|
|
79f29a3966 | ||
|
|
6843514f86 | ||
|
|
ff6b9bd0bf | ||
|
|
3576613f52 | ||
|
|
02cfa3f651 | ||
|
|
87a0d3f413 | ||
|
|
407205a4e5 | ||
|
|
6f368200b9 | ||
|
|
f669f02407 | ||
|
|
e7d3e524bb | ||
|
|
d3d97bf11e | ||
|
|
766bdf727c | ||
|
|
f2264ebb3e | ||
|
|
2c61f2b1e7 | ||
|
|
cab8fd2a80 | ||
|
|
56a1877d15 | ||
|
|
3096e154c9 | ||
|
|
accb011196 | ||
|
|
93068a376a | ||
|
|
5be17933e9 | ||
|
|
f4183d2c8f | ||
|
|
576855de4f | ||
|
|
35b386df0d | ||
|
|
2702299e7b | ||
|
|
65e4357af7 | ||
|
|
904cdd3fb4 | ||
|
|
e48ec1ea20 | ||
|
|
467023692f | ||
|
|
b861c11253 | ||
|
|
e538fe4364 | ||
|
|
e0aa65efc3 | ||
|
|
c569d9e179 | ||
|
|
ef280919d3 | ||
|
|
734afe2058 | ||
|
|
b4e3786aa1 | ||
|
|
feec19940a | ||
|
|
ec97970206 | ||
|
|
4a9e7f7b7e | ||
|
|
73fa4ea199 | ||
|
|
ce08251f53 | ||
|
|
bbd6e16bbe | ||
|
|
959411c094 | ||
|
|
a64ae92d9d | ||
|
|
bad72987d0 | ||
|
|
8deab9ea35 | ||
|
|
35e5f2dbc6 | ||
|
|
cd11f65956 | ||
|
|
08686bcd06 | ||
|
|
5b0a948797 | ||
|
|
b060c71de4 | ||
|
|
fac78083ca | ||
|
|
230f47d00c | ||
|
|
1cfc4c6cc0 | ||
|
|
f89060e7ff | ||
|
|
16faee9ae2 | ||
|
|
352b8e4cef | ||
|
|
2c40567030 | ||
|
|
ac3f80d738 | ||
|
|
2721ccb52a | ||
|
|
0fc36bfe0a | ||
|
|
559fc5b67f | ||
|
|
bd15206599 | ||
|
|
e5d3894bc4 | ||
|
|
0bc0ae40a5 | ||
|
|
3c15bb06db | ||
|
|
9ce3331f04 | ||
|
|
9508cc4eb3 | ||
|
|
0bb1f7292c | ||
|
|
b56b09af35 | ||
|
|
a685701805 | ||
|
|
d207eb0b1e | ||
|
|
e8b2cde4cf | ||
|
|
bedd5a691b | ||
|
|
57fbad1a54 | ||
|
|
12deb9c54a | ||
|
|
8b1be981d3 | ||
|
|
30d229c30b | ||
|
|
f3d9588e37 | ||
|
|
b5f77bf65e | ||
|
|
5bfc7be450 | ||
|
|
fcab353201 | ||
|
|
e968d26ba8 | ||
|
|
db1765e788 | ||
|
|
6e4277d9a6 | ||
|
|
be56e901bb | ||
|
|
f803b5fbd3 | ||
|
|
be62e537c6 | ||
|
|
7af7d55068 | ||
|
|
15c5c69241 | ||
|
|
b89554d11a | ||
|
|
60760d69db | ||
|
|
ab687190a9 | ||
|
|
a07514c88b | ||
|
|
f9de1a8831 | ||
|
|
badf807ae9 | ||
|
|
36e2b51827 | ||
|
|
afae4a5cb4 | ||
|
|
e9fb18f594 | ||
|
|
a061fa081c | ||
|
|
88b819ffff | ||
|
|
730fb03c07 | ||
|
|
a571cf5e92 | ||
|
|
7ce5733a45 | ||
|
|
e73df5e6a9 | ||
|
|
c2cb9fead0 | ||
|
|
01fb168f51 | ||
|
|
f16b269685 | ||
|
|
1bfabea14a | ||
|
|
1160aeed3d | ||
|
|
61019ac48c | ||
|
|
4642e0ce96 | ||
|
|
0a81b47932 | ||
|
|
0faa8c3855 | ||
|
|
388e2bb5fc | ||
|
|
14ffd07b70 | ||
|
|
9cb5eea20b | ||
|
|
be03b8ac95 | ||
|
|
b9ce2d4142 | ||
|
|
422c13242f | ||
|
|
9e917d4835 | ||
|
|
1cc8a8a121 | ||
|
|
05e49db12a | ||
|
|
2acc30e1a4 | ||
|
|
dd1d83768b | ||
|
|
1451ba9610 | ||
|
|
3d8c50a768 | ||
|
|
f5f8a71af2 | ||
|
|
5368d0d148 | ||
|
|
c9fc51fe2a | ||
|
|
0e5a9a7772 | ||
|
|
6ff762f088 | ||
|
|
bda70a9168 | ||
|
|
03a892c7f2 | ||
|
|
e6f2aa81f7 | ||
|
|
42b101b024 | ||
|
|
38845d6ad5 | ||
|
|
a5db76d7db | ||
|
|
5181463606 | ||
|
|
a9ffab7854 | ||
|
|
2797ec3a79 | ||
|
|
8c672bc613 | ||
|
|
458abe3611 | ||
|
|
08a9525508 | ||
|
|
730983bf59 | ||
|
|
c7692e7a72 | ||
|
|
8da940d0e5 | ||
|
|
05607880f8 | ||
|
|
94ff302111 | ||
|
|
aa32810151 | ||
|
|
6614e4ef92 | ||
|
|
7374480126 | ||
|
|
d5dc910e5b | ||
|
|
367d554fff | ||
|
|
ea49b02380 | ||
|
|
b0ddf78db0 | ||
|
|
d4d97bfae1 | ||
|
|
5706713905 | ||
|
|
2fd7c1bb9f | ||
|
|
e46f09c3b1 | ||
|
|
a4dd8bdf30 | ||
|
|
2be040a8a7 | ||
|
|
e14f5e9ea5 | ||
|
|
f9870dfb24 | ||
|
|
ec5612a12b | ||
|
|
d12532aef1 | ||
|
|
7082561e21 | ||
|
|
763f257d92 | ||
|
|
d55c86bc41 | ||
|
|
3695cf78b9 | ||
|
|
26cb567bb9 | ||
|
|
fabceb0e84 | ||
|
|
0a3dc05c27 | ||
|
|
8f0a47e6c3 | ||
|
|
20528b9d1f | ||
|
|
d262049f6b | ||
|
|
75408b689d | ||
|
|
d551d93ce4 | ||
|
|
99f622de0e | ||
|
|
64cbfd0183 | ||
|
|
64de1a3260 | ||
|
|
4d95765cb5 | ||
|
|
e90b7e6a03 | ||
|
|
e044275c3e | ||
|
|
c5e8eb0283 | ||
|
|
8e74333ef4 | ||
|
|
2089521856 | ||
|
|
057d29bea1 | ||
|
|
026fd10bd4 | ||
|
|
c492195ab1 | ||
|
|
d9a72ea076 | ||
|
|
955c0e85c8 | ||
|
|
3c99021b60 | ||
|
|
5453cc0448 | ||
|
|
c7e32cbbd2 | ||
|
|
5edf67bba8 | ||
|
|
d109c22b49 | ||
|
|
98f2f1c23d | ||
|
|
94bc6ff3a2 | ||
|
|
62dc63542c | ||
|
|
dd8acd5872 | ||
|
|
d1113a5af0 | ||
|
|
024e10b5a9 | ||
|
|
c4b2492872 | ||
|
|
44bc9fe291 | ||
|
|
ee7bacd2bc | ||
|
|
90a2070718 | ||
|
|
abf34c06c1 | ||
|
|
661836fd77 | ||
|
|
260f3ac7ce | ||
|
|
f57ab1ccd8 | ||
|
|
ef206b3a27 | ||
|
|
2dce493240 | ||
|
|
83c3d7be57 | ||
|
|
7b0e1e8022 | ||
|
|
86950b4d2a | ||
|
|
fe02783fc7 | ||
|
|
072406e3c3 | ||
|
|
751761d99c | ||
|
|
fe993fafd5 | ||
|
|
057785539b | ||
|
|
048d2f65e0 | ||
|
|
c305f4347d | ||
|
|
3ca62fbbbc | ||
|
|
225699f545 | ||
|
|
35ae61fbc7 | ||
|
|
86dac8c979 | ||
|
|
95431414ae | ||
|
|
b3f7224ea1 | ||
|
|
e583546778 | ||
|
|
a39c37e1ee | ||
|
|
67fb35b645 | ||
|
|
3c1e4c879a | ||
|
|
83a11be669 | ||
|
|
08e35a62d4 | ||
|
|
5461c0d86e | ||
|
|
4eab7c4387 | ||
|
|
59aeb77744 | ||
|
|
c6fc8e45ff | ||
|
|
4dde927c12 | ||
|
|
6325b4cd2e | ||
|
|
4beec3870f | ||
|
|
36a2c96cb7 | ||
|
|
a4a68a2ad5 | ||
|
|
13a2010ed6 | ||
|
|
e37fd21424 | ||
|
|
a2698061a6 | ||
|
|
d00257382a | ||
|
|
51ab2f2509 | ||
|
|
61f9ceccab | ||
|
|
f836a291a0 | ||
|
|
97725e69eb | ||
|
|
d186e14eeb | ||
|
|
08aaa1021c | ||
|
|
b4248038fb | ||
|
|
ca587ab92e | ||
|
|
6f5c33848e | ||
|
|
17d380400e | ||
|
|
2e456b2570 | ||
|
|
fe17a42e62 | ||
|
|
c46866c6e5 | ||
|
|
3117633adf | ||
|
|
8a49321648 | ||
|
|
18f027ed41 | ||
|
|
79b9c68cb5 | ||
|
|
2b1344c3cf | ||
|
|
da26925118 | ||
|
|
f2a152f7c9 | ||
|
|
1953ea7312 | ||
|
|
e55dbe944d | ||
|
|
f5ad906e7b | ||
|
|
7cad2bcd6e | ||
|
|
cc921f25ed | ||
|
|
435109ceae | ||
|
|
ca375f65d5 | ||
|
|
76212f47bf | ||
|
|
6efb904919 | ||
|
|
072342cdab | ||
|
|
09761c0786 | ||
|
|
c48fab00e8 | ||
|
|
d1c562ce4e | ||
|
|
45cae81d51 | ||
|
|
30e28acfb3 | ||
|
|
00459365c4 | ||
|
|
649884bc05 | ||
|
|
e1af420b2d | ||
|
|
613f50dd14 | ||
|
|
a18c0b2b14 | ||
|
|
d7f8500b3a | ||
|
|
7ecf9681c5 | ||
|
|
e9ca7acce9 | ||
|
|
ea68de8070 | ||
|
|
0fa0bfeb74 | ||
|
|
4a5d0d8dfb | ||
|
|
af35a18515 | ||
|
|
5560babc11 | ||
|
|
3e76201d80 | ||
|
|
5b15d8626c | ||
|
|
f8a03a7d5d | ||
|
|
b7b9acb336 | ||
|
|
44e2d66cf5 | ||
|
|
9747767474 | ||
|
|
cee33327f8 | ||
|
|
f70b52a0df | ||
|
|
0b62771b54 | ||
|
|
b168109672 | ||
|
|
d8b6aebe9a | ||
|
|
4812b632c3 | ||
|
|
8320d0a0e9 | ||
|
|
a287ebe736 | ||
|
|
ebea0220cd | ||
|
|
2b01f36fc0 | ||
|
|
7e3092dc93 | ||
|
|
114c723d59 | ||
|
|
da39f8d3d6 | ||
|
|
33b76628aa | ||
|
|
94162f452d | ||
|
|
318d875981 | ||
|
|
520878272c | ||
|
|
fc87336c45 | ||
|
|
d66e039536 | ||
|
|
eeebc2d06b | ||
|
|
7d1ebdf94a | ||
|
|
3d1b0ef35b | ||
|
|
c2ba0d474c | ||
|
|
793c00e614 | ||
|
|
d4093dc31c | ||
|
|
bc66841a29 | ||
|
|
f4cc8a4bca | ||
|
|
539563f0f4 | ||
|
|
8c5d86a6eb | ||
|
|
884c113645 | ||
|
|
70587bc0df | ||
|
|
df4e0bb1e4 | ||
|
|
cdc92af959 | ||
|
|
763a5bf2c0 | ||
|
|
8301cf8a2f | ||
|
|
44da8a6749 | ||
|
|
06309ba1e0 | ||
|
|
004ca0fe89 | ||
|
|
40cce7c9d6 | ||
|
|
1c29d31859 | ||
|
|
d3af29fa1f | ||
|
|
c5d1fd32d5 | ||
|
|
a4767d3d43 | ||
|
|
c26f527c20 | ||
|
|
0c8d85d589 | ||
|
|
0c16e1a134 | ||
|
|
0a887395eb | ||
|
|
9118976c49 | ||
|
|
7f1e599186 | ||
|
|
672102d377 | ||
|
|
fe7f05e717 | ||
|
|
0724ab2ca1 | ||
|
|
c272ba56ad | ||
|
|
381498e945 | ||
|
|
70f07196ea | ||
|
|
c8dee57278 | ||
|
|
0ab305156b | ||
|
|
eac468ba19 | ||
|
|
18f407110b | ||
|
|
ceee10b56c | ||
|
|
9805bcc5f9 | ||
|
|
d5fca50a2f | ||
|
|
4ca72bd7e6 | ||
|
|
62ead00f0f | ||
|
|
5dc573ed38 | ||
|
|
fa053ffc2b | ||
|
|
be6c7bad24 | ||
|
|
2680971bb3 | ||
|
|
630f19f248 | ||
|
|
ef4bee2e4e | ||
|
|
f182a2ad24 | ||
|
|
3eb98951f7 | ||
|
|
f9d4bbf4d3 | ||
|
|
eff4fcecae | ||
|
|
cbfdc9cf97 | ||
|
|
034c7c70fb | ||
|
|
44ad016b91 | ||
|
|
5ed0d00249 | ||
|
|
6336fb4a1b | ||
|
|
99497e21cf | ||
|
|
0181d97cae | ||
|
|
968c73a748 | ||
|
|
13f9e94f61 | ||
|
|
8c1018d573 | ||
|
|
d4d00a35d7 | ||
|
|
e3177fd489 | ||
|
|
dd1300f1cc | ||
|
|
7f03af2b2b | ||
|
|
f431f77834 | ||
|
|
7c28a62b40 | ||
|
|
a360042f77 | ||
|
|
82af27cf33 | ||
|
|
0b78abb31d | ||
|
|
2fd8ffbf22 | ||
|
|
83f7be0cbb | ||
|
|
7f5db4d70f | ||
|
|
4f04c1288b | ||
|
|
8a2c5bf9e1 | ||
|
|
5c05e309c1 | ||
|
|
bd147270eb | ||
|
|
528cf8d472 | ||
|
|
0fce2f409f | ||
|
|
fd7b0b05b8 | ||
|
|
9abc7c00c9 | ||
|
|
ebc5f433cd | ||
|
|
aa4f79f236 | ||
|
|
62f5c53b72 | ||
|
|
534163a57f | ||
|
|
325a9fb48e | ||
|
|
ee43531a99 | ||
|
|
757fa71469 | ||
|
|
bfee7986bd | ||
|
|
9aaad17c1a | ||
|
|
93874bba83 | ||
|
|
9c67452f10 | ||
|
|
0416a2ae61 | ||
|
|
8bbde14019 | ||
|
|
0e8f2339d8 | ||
|
|
9f37427d7a | ||
|
|
6ad4b1f15a | ||
|
|
a39c0f01f9 | ||
|
|
1136820124 | ||
|
|
2a3f3bad23 | ||
|
|
74def91a53 | ||
|
|
bc62e1be08 | ||
|
|
25aea134ef | ||
|
|
e488c3081d | ||
|
|
331f46fb24 | ||
|
|
e88662cab0 | ||
|
|
395f6d1d3c | ||
|
|
94bd78b3e1 | ||
|
|
dc373fb7d0 | ||
|
|
140812d64a | ||
|
|
a91c4e556f | ||
|
|
372c622166 | ||
|
|
d961d68e9c | ||
|
|
33a325a730 | ||
|
|
909b25c7ac | ||
|
|
77f110c23e | ||
|
|
9dc9416ef9 | ||
|
|
0b20f11617 | ||
|
|
c9594776f8 | ||
|
|
8f55b9904d | ||
|
|
093ba1c33d | ||
|
|
67a434d563 | ||
|
|
ff0c3f047a | ||
|
|
2934ba301b | ||
|
|
f3627aaf59 | ||
|
|
dc8a2dea72 | ||
|
|
329969eb05 | ||
|
|
71923af675 | ||
|
|
18e58ca5f4 | ||
|
|
8c3aebbbc8 | ||
|
|
7f40c11eb7 | ||
|
|
0ed668d1b5 | ||
|
|
1c8632cac9 | ||
|
|
e7324989fc | ||
|
|
a7fc3807b9 | ||
|
|
99ad7bcc51 | ||
|
|
5b88e338df | ||
|
|
425af71450 | ||
|
|
19c5eab07a | ||
|
|
e5286cbe39 | ||
|
|
02d40aa212 | ||
|
|
8a5c537e15 | ||
|
|
c6c4f2f4d1 | ||
|
|
5bc7ea4045 | ||
|
|
077acf7894 | ||
|
|
c5f86f12c0 | ||
|
|
243e941d81 | ||
|
|
d67e6b7c15 | ||
|
|
f76bf59fed | ||
|
|
1c4d88eb04 | ||
|
|
ad04db8365 | ||
|
|
a63ccbbdab | ||
|
|
278f314c03 | ||
|
|
a3abda7e7b | ||
|
|
c0a9db111d | ||
|
|
676ce10119 | ||
|
|
c18dce4783 | ||
|
|
088b01b52e | ||
|
|
c0f436b8ab | ||
|
|
3377020242 | ||
|
|
6db58fe1dd | ||
|
|
67c7cd8de9 | ||
|
|
457a3d458a | ||
|
|
6538312d86 | ||
|
|
7f1a5075b1 | ||
|
|
e412ecdefe | ||
|
|
67b89d6f42 | ||
|
|
c8d3278dbc | ||
|
|
3556445660 | ||
|
|
f28fc0cabb | ||
|
|
e0bc548400 | ||
|
|
011a4c8a89 | ||
|
|
87bc5220eb | ||
|
|
673d0ed1a2 | ||
|
|
a5079bdda1 | ||
|
|
fc43e7155e | ||
|
|
4935333b63 | ||
|
|
5a3232adff | ||
|
|
d03f351f33 | ||
|
|
8d5bf997a9 | ||
|
|
0738480439 | ||
|
|
9a5e1a31ad | ||
|
|
5ab7664f49 | ||
|
|
1cb2c60ccc | ||
|
|
1356d0ab4c | ||
|
|
b4c4a65f2d | ||
|
|
ab3cf8daca | ||
|
|
2b6c252747 | ||
|
|
ec9a6ac90a | ||
|
|
a207549f18 | ||
|
|
efbf9354f9 | ||
|
|
216ddc9fd6 | ||
|
|
3b668cccd3 | ||
|
|
30a86a2845 | ||
|
|
a7870aa888 | ||
|
|
271b99981c | ||
|
|
08ef01bb13 | ||
|
|
e778af462a | ||
|
|
9d124b2380 | ||
|
|
f24e7cd665 | ||
|
|
e7f1526942 | ||
|
|
647b1ba53c | ||
|
|
eac8d9f191 | ||
|
|
50babe8432 | ||
|
|
8171977ed7 | ||
|
|
f8aaf1df1e | ||
|
|
04d0d84d01 | ||
|
|
f4aa9fa865 | ||
|
|
40ef7d6f00 | ||
|
|
66b7b1691c | ||
|
|
43972d4c35 | ||
|
|
632545c409 | ||
|
|
3a47e2cd63 | ||
|
|
a221e3e4cd | ||
|
|
b87f50dc9d | ||
|
|
a5b3f421d1 | ||
|
|
182525afa9 | ||
|
|
419afbcb83 | ||
|
|
12ca70a308 | ||
|
|
1fa7663d3b | ||
|
|
2e34c6fc5c | ||
|
|
cb7002559d | ||
|
|
36ec92793f | ||
|
|
4187d70b30 | ||
|
|
5969655c3c | ||
|
|
09d2ec7d3a | ||
|
|
27b975d0fc | ||
|
|
6da64013fa | ||
|
|
d8eb5de380 | ||
|
|
23aa8bbe7a | ||
|
|
3b5472dbac | ||
|
|
3b4e60aada | ||
|
|
8d8fb44c18 | ||
|
|
b46fb60a6e | ||
|
|
f7399bbc8b | ||
|
|
7f7d47b120 | ||
|
|
e7aa3ed0cc | ||
|
|
0ece8b1319 | ||
|
|
557e05dd12 | ||
|
|
08a1496501 | ||
|
|
5a24037c61 | ||
|
|
5dbd1ea35c |
No files matched your search
+9
-1
@@ -1,7 +1,15 @@
|
||||
*.o
|
||||
*.bmpobj
|
||||
*.so
|
||||
*.dll
|
||||
ssnes
|
||||
/ssnes
|
||||
/config.h
|
||||
/config.mk
|
||||
/tools/ssnes-joyconfig
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.suo
|
||||
Debug
|
||||
Release
|
||||
ipch
|
||||
*.user
|
||||
+884
@@ -0,0 +1,884 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define NONET
|
||||
#include <xtl.h>
|
||||
#include <malloc.h>
|
||||
#include "xdk360_video.h"
|
||||
#include "fonts.h"
|
||||
#include "../general.h"
|
||||
|
||||
static video_console_t video_console;
|
||||
static xdk360_video_font_t m_Font;
|
||||
|
||||
void xdk360_console_draw(void)
|
||||
{
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
D3DDevice *m_pd3dDevice = vid->xdk360_render_device;
|
||||
|
||||
// The top line
|
||||
unsigned int nTextLine = ( video_console.m_nCurLine -
|
||||
video_console.m_cScreenHeight + video_console.m_cScreenHeightVirtual -
|
||||
video_console.m_nScrollOffset + 1 )
|
||||
% video_console.m_cScreenHeightVirtual;
|
||||
|
||||
xdk360_video_font_begin(&m_Font);
|
||||
|
||||
for( unsigned int nScreenLine = 0; nScreenLine < video_console.m_cScreenHeight; nScreenLine++ )
|
||||
{
|
||||
xdk360_video_font_draw_text(&m_Font, (float)( video_console.m_cxSafeAreaOffset ),
|
||||
(float)( video_console.m_cySafeAreaOffset +
|
||||
video_console.m_fLineHeight * nScreenLine ),
|
||||
video_console.m_colTextColor,
|
||||
video_console.m_Lines[nTextLine], 0.0f );
|
||||
|
||||
nTextLine = ( nTextLine + 1 ) % video_console.m_cScreenHeightVirtual;
|
||||
}
|
||||
|
||||
xdk360_video_font_end(&m_Font);
|
||||
}
|
||||
|
||||
HRESULT xdk360_console_init( LPCSTR strFontFileName, unsigned long colBackColor,
|
||||
unsigned long colTextColor)
|
||||
{
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
D3DDevice *m_pd3dDevice = vid->xdk360_render_device;
|
||||
|
||||
video_console.first_message = true;
|
||||
video_console.m_Buffer = NULL;
|
||||
video_console.m_Lines = NULL;
|
||||
video_console.m_nScrollOffset = 0;
|
||||
|
||||
// Calculate the safe area
|
||||
unsigned int uiSafeAreaPct = vid->video_mode.fIsHiDef ? SAFE_AREA_PCT_HDTV
|
||||
: SAFE_AREA_PCT_4x3;
|
||||
|
||||
video_console.m_cxSafeArea = ( vid->d3dpp.BackBufferWidth * uiSafeAreaPct ) / 100;
|
||||
video_console.m_cySafeArea = ( vid->d3dpp.BackBufferHeight * uiSafeAreaPct ) / 100;
|
||||
|
||||
video_console.m_cxSafeAreaOffset = ( vid->d3dpp.BackBufferWidth - video_console.m_cxSafeArea ) / 2;
|
||||
video_console.m_cySafeAreaOffset = ( vid->d3dpp.BackBufferHeight - video_console.m_cySafeArea ) / 2;
|
||||
|
||||
// Create the font
|
||||
HRESULT hr = xdk360_video_font_init(&m_Font, strFontFileName );
|
||||
if( FAILED( hr ) )
|
||||
{
|
||||
SSNES_ERR( "Could not create font.\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Save the colors
|
||||
video_console.m_colBackColor = colBackColor;
|
||||
video_console.m_colTextColor = colTextColor;
|
||||
|
||||
// Calculate the number of lines on the screen
|
||||
float fCharWidth, fCharHeight;
|
||||
xdk360_video_font_get_text_width(&m_Font, L"i", &fCharWidth, &fCharHeight, FALSE);
|
||||
|
||||
video_console.m_cScreenHeight = (unsigned int)( video_console.m_cySafeArea / fCharHeight );
|
||||
video_console.m_cScreenWidth = (unsigned int)( video_console.m_cxSafeArea / fCharWidth );
|
||||
|
||||
video_console.m_cScreenHeightVirtual = video_console.m_cScreenHeight;
|
||||
|
||||
video_console.m_fLineHeight = fCharHeight;
|
||||
|
||||
// Allocate memory to hold the lines
|
||||
video_console.m_Buffer = new wchar_t[ video_console.m_cScreenHeightVirtual * ( video_console.m_cScreenWidth + 1 ) ];
|
||||
video_console.m_Lines = new wchar_t *[ video_console.m_cScreenHeightVirtual ];
|
||||
|
||||
// Set the line pointers as indexes into the buffer
|
||||
for( unsigned int i = 0; i < video_console.m_cScreenHeightVirtual; i++ )
|
||||
video_console.m_Lines[ i ] = video_console.m_Buffer + ( video_console.m_cScreenWidth + 1 ) * i;
|
||||
|
||||
video_console.m_nCurLine = 0;
|
||||
video_console.m_cCurLineLength = 0;
|
||||
memset( video_console.m_Buffer, 0, video_console.m_cScreenHeightVirtual * ( video_console.m_cScreenWidth + 1 ) * sizeof( wchar_t ) );
|
||||
xdk360_console_draw();
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
void xdk360_console_deinit()
|
||||
{
|
||||
// Delete the memory we've allocated
|
||||
if(video_console.m_Lines)
|
||||
{
|
||||
delete[] video_console.m_Lines;
|
||||
video_console.m_Lines = NULL;
|
||||
}
|
||||
|
||||
if(video_console.m_Buffer)
|
||||
{
|
||||
delete[] video_console.m_Buffer;
|
||||
video_console.m_Buffer = NULL;
|
||||
}
|
||||
|
||||
// Destroy the font
|
||||
xdk360_video_font_deinit(&m_Font);
|
||||
}
|
||||
|
||||
void xdk360_console_add( wchar_t wch )
|
||||
{
|
||||
// If this is a newline, just increment lines and move on
|
||||
if( wch == L'\n' )
|
||||
{
|
||||
video_console.m_nCurLine = ( video_console.m_nCurLine + 1 )
|
||||
% video_console.m_cScreenHeightVirtual;
|
||||
video_console.m_cCurLineLength = 0;
|
||||
memset(video_console.m_Lines[video_console.m_nCurLine], 0,
|
||||
( video_console.m_cScreenWidth + 1 ) * sizeof( wchar_t ) );
|
||||
return;
|
||||
}
|
||||
|
||||
int bIncrementLine = FALSE; // Whether to wrap to the next line
|
||||
|
||||
if( video_console.m_cCurLineLength == video_console.m_cScreenWidth )
|
||||
bIncrementLine = TRUE;
|
||||
else
|
||||
{
|
||||
// Try to append the character to the line
|
||||
video_console.m_Lines[ video_console.m_nCurLine ]
|
||||
[ video_console.m_cCurLineLength ] = wch;
|
||||
|
||||
float fTextWidth, fTextHeight;
|
||||
xdk360_video_font_get_text_width(&m_Font, video_console.m_Lines[ video_console.m_nCurLine ], &fTextWidth, &fTextHeight, 0);
|
||||
if( fTextHeight > video_console.m_cxSafeArea )
|
||||
{
|
||||
// The line is too long, we need to wrap the character to the next line
|
||||
video_console.m_Lines[video_console.m_nCurLine]
|
||||
[ video_console.m_cCurLineLength ] = L'\0';
|
||||
bIncrementLine = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// If we need to skip to the next line, do so
|
||||
if( bIncrementLine )
|
||||
{
|
||||
video_console.m_nCurLine = ( video_console.m_nCurLine + 1 )
|
||||
% video_console.m_cScreenHeightVirtual;
|
||||
video_console.m_cCurLineLength = 0;
|
||||
memset( video_console.m_Lines[video_console.m_nCurLine],
|
||||
0, ( video_console.m_cScreenWidth + 1 ) * sizeof( wchar_t ) );
|
||||
video_console.m_Lines[video_console.m_nCurLine ][0] = wch;
|
||||
}
|
||||
|
||||
video_console.m_cCurLineLength++;
|
||||
}
|
||||
|
||||
void xdk360_console_format(_In_z_ _Printf_format_string_ LPCSTR strFormat, ... )
|
||||
{
|
||||
video_console.m_nCurLine = 0;
|
||||
video_console.m_cCurLineLength = 0;
|
||||
memset( video_console.m_Buffer, 0,
|
||||
video_console.m_cScreenHeightVirtual *
|
||||
( video_console.m_cScreenWidth + 1 ) * sizeof( wchar_t ) );
|
||||
|
||||
va_list pArgList;
|
||||
va_start( pArgList, strFormat );
|
||||
|
||||
// Count the required length of the string
|
||||
unsigned long dwStrLen = _vscprintf( strFormat, pArgList ) + 1;
|
||||
// +1 = null terminator
|
||||
char * strMessage = ( char * )_malloca( dwStrLen );
|
||||
vsprintf_s( strMessage, dwStrLen, strFormat, pArgList );
|
||||
|
||||
// Output the string to the console
|
||||
unsigned long uStringLength = strlen( strMessage );
|
||||
for( unsigned long i = 0; i < uStringLength; i++ )
|
||||
{
|
||||
wchar_t wch;
|
||||
int ret = MultiByteToWideChar( CP_ACP, // ANSI code page
|
||||
0, // No flags
|
||||
&strMessage[i], // Character to convert
|
||||
1, // Convert one byte
|
||||
&wch, // Target wide character buffer
|
||||
1 ); // One wide character
|
||||
xdk360_console_add( wch );
|
||||
}
|
||||
|
||||
_freea( strMessage );
|
||||
|
||||
va_end( pArgList );
|
||||
}
|
||||
|
||||
void xdk360_console_format_w(_In_z_ _Printf_format_string_ LPCWSTR wstrFormat, ... )
|
||||
{
|
||||
video_console.m_nCurLine = 0;
|
||||
video_console.m_cCurLineLength = 0;
|
||||
memset( video_console.m_Buffer, 0, video_console.m_cScreenHeightVirtual
|
||||
* ( video_console.m_cScreenWidth + 1 ) * sizeof( wchar_t ) );
|
||||
|
||||
va_list pArgList;
|
||||
va_start( pArgList, wstrFormat );
|
||||
|
||||
// Count the required length of the string
|
||||
unsigned long dwStrLen = _vscwprintf( wstrFormat, pArgList ) + 1; // +1 = null terminator
|
||||
wchar_t * strMessage = ( wchar_t * )_malloca( dwStrLen * sizeof( wchar_t ) );
|
||||
vswprintf_s( strMessage, dwStrLen, wstrFormat, pArgList );
|
||||
|
||||
// Output the string to the console
|
||||
unsigned long uStringLength = wcslen( strMessage );
|
||||
for( unsigned long i = 0; i < uStringLength; i++ )
|
||||
xdk360_console_add( strMessage[i] );
|
||||
|
||||
_freea( strMessage );
|
||||
|
||||
va_end( pArgList );
|
||||
}
|
||||
|
||||
#define CALCFONTFILEHEADERSIZE(x) ( sizeof(unsigned long) + (sizeof(float)* 4) + sizeof(unsigned short) + (sizeof(wchar_t)*(x)) )
|
||||
#define FONTFILEVERSION 5
|
||||
|
||||
typedef struct FontFileHeaderImage_t {
|
||||
unsigned long m_dwFileVersion; // Version of the font file (Must match FONTFILEVERSION)
|
||||
float m_fFontHeight; // Height of the font strike in pixels
|
||||
float m_fFontTopPadding; // Padding above the strike zone
|
||||
float m_fFontBottomPadding; // Padding below the strike zone
|
||||
float m_fFontYAdvance; // Number of pixels to move the cursor for a line feed
|
||||
unsigned short m_cMaxGlyph; // Number of font characters (Should be an odd number to maintain DWORD Alignment)
|
||||
wchar_t m_TranslatorTable[1]; // ASCII to Glyph lookup table, NOTE: It's m_cMaxGlyph+1 in size.
|
||||
// Entry 0 maps to the "Unknown" glyph.
|
||||
} FontFileHeaderImage_t;
|
||||
|
||||
// Font strike array. Immediately follows the FontFileHeaderImage_t
|
||||
// structure image
|
||||
|
||||
typedef struct FontFileStrikesImage_t {
|
||||
unsigned long m_dwNumGlyphs; // Size of font strike array (First entry is the unknown glyph)
|
||||
GLYPH_ATTR m_Glyphs[1]; // Array of font strike uv's etc... NOTE: It's m_dwNumGlyphs in size
|
||||
} FontFileStrikesImage_t;
|
||||
|
||||
static PackedResource m_xprResource;
|
||||
|
||||
static const char g_strFontShader[] =
|
||||
"struct VS_IN\n"
|
||||
"{\n"
|
||||
"float2 Pos : POSITION;\n"
|
||||
"float2 Tex : TEXCOORD0;\n"
|
||||
"float4 ChannelSelector : TEXCOORD1;\n"
|
||||
"};\n"
|
||||
"struct VS_OUT\n"
|
||||
"{\n"
|
||||
"float4 Position : POSITION;\n"
|
||||
"float4 Diffuse : COLOR0_center;\n"
|
||||
"float2 TexCoord0 : TEXCOORD0;\n"
|
||||
"float4 ChannelSelector : TEXCOORD1;\n"
|
||||
"};\n"
|
||||
"uniform float4 Color : register(c1);\n"
|
||||
"uniform float2 TexScale : register(c2);\n"
|
||||
"sampler FontTexture : register(s0);\n"
|
||||
"VS_OUT FontVertexShader( VS_IN In )\n"
|
||||
"{\n"
|
||||
"VS_OUT Out;\n"
|
||||
"Out.Position.x = (In.Pos.x-0.5);\n"
|
||||
"Out.Position.y = (In.Pos.y-0.5);\n"
|
||||
"Out.Position.z = ( 0.0 );\n"
|
||||
"Out.Position.w = ( 1.0 );\n"
|
||||
"Out.Diffuse = Color;\n"
|
||||
"Out.TexCoord0.x = In.Tex.x * TexScale.x;\n"
|
||||
"Out.TexCoord0.y = In.Tex.y * TexScale.y;\n"
|
||||
"Out.ChannelSelector = In.ChannelSelector;\n"
|
||||
"return Out;\n"
|
||||
"}\n"
|
||||
"float4 FontPixelShader( VS_OUT In ) : COLOR0\n"
|
||||
"{\n"
|
||||
"float4 FontTexel = tex2D( FontTexture, In.TexCoord0 );\n"
|
||||
"if( dot( In.ChannelSelector, float4(1,1,1,1) ) )\n"
|
||||
"{\n"
|
||||
"float value = dot( FontTexel, In.ChannelSelector );\n"
|
||||
"float4 Color;\n"
|
||||
"Color.rgb = ( value > 0.5f ? 2*value-1 : 0.0f );\n"
|
||||
"Color.a = 2 * ( value > 0.5f ? 1.0f : value );\n"
|
||||
"return Color * In.Diffuse;\n"
|
||||
"}\n"
|
||||
"else\n"
|
||||
"{\n"
|
||||
"return FontTexel * In.Diffuse;\n"
|
||||
"}\n"
|
||||
"}\n";
|
||||
|
||||
typedef struct Font_Locals_t {
|
||||
D3DVertexDeclaration* m_pFontVertexDecl; // Shared vertex buffer
|
||||
D3DVertexShader* m_pFontVertexShader; // Created vertex shader
|
||||
D3DPixelShader* m_pFontPixelShader; // Created pixel shader
|
||||
} Font_Locals_t;
|
||||
|
||||
// All elements are defaulted to NULL
|
||||
static Font_Locals_t s_FontLocals; // Global static instance
|
||||
|
||||
static HRESULT xdk360_video_font_create_shaders (xdk360_video_font_t * font)
|
||||
{
|
||||
//
|
||||
// There are only two states the globals could be in,
|
||||
// Initialized, in which the ref count is increased,
|
||||
// Uninialized, in which the vertex/pixel shaders need to be
|
||||
// started up and a vertex array created.
|
||||
///
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
if (!s_FontLocals.m_pFontVertexDecl)
|
||||
{
|
||||
// Use the do {} while(0); trick for a fake goto
|
||||
// It simplies tear down on error conditions.
|
||||
do
|
||||
{
|
||||
|
||||
// Step #1, create my vertex array with 16 bytes per entry
|
||||
// Floats for the position,
|
||||
// shorts for the uvs
|
||||
// 32 bit packed ARGB 8:8:8:8 for color
|
||||
|
||||
static const D3DVERTEXELEMENT9 decl[] =
|
||||
{
|
||||
{ 0, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
|
||||
{ 0, 8, D3DDECLTYPE_USHORT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
|
||||
{ 0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 1 },
|
||||
D3DDECL_END()
|
||||
};
|
||||
|
||||
// Cache this global into a register
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
D3DDevice *pd3dDevice = vid->xdk360_render_device;
|
||||
|
||||
hr = pd3dDevice->CreateVertexDeclaration( decl, &s_FontLocals.m_pFontVertexDecl );
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
|
||||
// Step #2, create my vertex shader
|
||||
ID3DXBuffer* pShaderCode;
|
||||
hr = D3DXCompileShader( g_strFontShader, sizeof(g_strFontShader)-1 ,
|
||||
NULL, NULL, "FontVertexShader", "vs.2.0", 0,&pShaderCode, NULL, NULL );
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = pd3dDevice->CreateVertexShader( ( unsigned long * )pShaderCode->GetBufferPointer(),
|
||||
&s_FontLocals.m_pFontVertexShader );
|
||||
// Release the compiled shader
|
||||
pShaderCode->Release();
|
||||
|
||||
if(SUCCEEDED(hr))
|
||||
{
|
||||
// Step #3, create my pixel shader
|
||||
hr = D3DXCompileShader( g_strFontShader, sizeof(g_strFontShader)-1 ,
|
||||
NULL, NULL, "FontPixelShader", "ps.2.0", 0,&pShaderCode, NULL, NULL );
|
||||
if ( SUCCEEDED(hr))
|
||||
{
|
||||
hr = pd3dDevice->CreatePixelShader( ( DWORD* )pShaderCode->GetBufferPointer(),
|
||||
&s_FontLocals.m_pFontPixelShader );
|
||||
// Release the compiled shader
|
||||
pShaderCode->Release();
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = S_OK;
|
||||
break; // Skip the teardown code
|
||||
}
|
||||
}
|
||||
// If the code got to here, a fatal error has occured
|
||||
// and a clean shutdown needs to be performed.
|
||||
|
||||
D3DResource_Release((D3DResource *)s_FontLocals.m_pFontVertexShader);
|
||||
}
|
||||
// Ensure the pointer is NULL
|
||||
s_FontLocals.m_pFontVertexShader = NULL;
|
||||
}
|
||||
D3DResource_Release((D3DResource *)s_FontLocals.m_pFontVertexDecl);
|
||||
}
|
||||
// Ensure this pointer is NULL
|
||||
s_FontLocals.m_pFontVertexDecl = NULL;
|
||||
}while(0); // Exit point for the break command.
|
||||
return hr;
|
||||
}
|
||||
else
|
||||
{
|
||||
D3DResource_AddRef((D3DResource *)s_FontLocals.m_pFontVertexDecl);
|
||||
D3DResource_AddRef((D3DResource *)s_FontLocals.m_pFontVertexShader);
|
||||
D3DResource_AddRef((D3DResource *)s_FontLocals.m_pFontPixelShader);
|
||||
hr = S_OK;
|
||||
}
|
||||
return hr; // Return the error code if any
|
||||
}
|
||||
|
||||
void xdk360_video_font_set_size(xdk360_video_font_t * font, float x, float y)
|
||||
{
|
||||
font->m_fXScaleFactor = x;
|
||||
font->m_fYScaleFactor = y;
|
||||
}
|
||||
|
||||
HRESULT xdk360_video_font_init(xdk360_video_font_t * font, const char * strFontFileName)
|
||||
{
|
||||
font->m_pFontTexture = NULL;
|
||||
font->m_dwNumGlyphs = 0L;
|
||||
font->m_Glyphs = NULL;
|
||||
font->m_fCursorX = 0.0f;
|
||||
font->m_fCursorY = 0.0f;
|
||||
font->m_fXScaleFactor = 2.0f;
|
||||
font->m_fYScaleFactor = 2.0f;
|
||||
font->m_cMaxGlyph = 0;
|
||||
font->m_TranslatorTable = NULL;
|
||||
font->m_dwNestedBeginCount = 0L;
|
||||
|
||||
// Create the font
|
||||
if( FAILED( m_xprResource.Create( strFontFileName ) ) )
|
||||
return E_FAIL;
|
||||
|
||||
D3DTexture * pFontTexture = m_xprResource.GetTexture( "FontTexture" );
|
||||
const void * pFontData = m_xprResource.GetData( "FontData");
|
||||
|
||||
// Save a copy of the texture
|
||||
font->m_pFontTexture = pFontTexture;
|
||||
|
||||
// Check version of file (to make sure it matches up with the FontMaker tool)
|
||||
const unsigned char * pData = static_cast<const unsigned char *>(pFontData);
|
||||
unsigned long dwFileVersion = reinterpret_cast<const FontFileHeaderImage_t *>(pData)->m_dwFileVersion;
|
||||
|
||||
if( dwFileVersion == FONTFILEVERSION )
|
||||
{
|
||||
font->m_fFontHeight = reinterpret_cast<const FontFileHeaderImage_t *>(pData)->m_fFontHeight;
|
||||
font->m_fFontTopPadding = reinterpret_cast<const FontFileHeaderImage_t *>(pData)->m_fFontTopPadding;
|
||||
font->m_fFontBottomPadding = reinterpret_cast<const FontFileHeaderImage_t *>(pData)->m_fFontBottomPadding;
|
||||
font->m_fFontYAdvance = reinterpret_cast<const FontFileHeaderImage_t *>(pData)->m_fFontYAdvance;
|
||||
|
||||
// Point to the translator string which immediately follows the 4 floats
|
||||
font->m_cMaxGlyph = reinterpret_cast<const FontFileHeaderImage_t *>(pData)->m_cMaxGlyph;
|
||||
|
||||
font->m_TranslatorTable = const_cast<FontFileHeaderImage_t*>(reinterpret_cast<const FontFileHeaderImage_t *>(pData))->m_TranslatorTable;
|
||||
|
||||
pData += CALCFONTFILEHEADERSIZE( font->m_cMaxGlyph + 1 );
|
||||
|
||||
// Read the glyph attributes from the file
|
||||
font->m_dwNumGlyphs = reinterpret_cast<const FontFileStrikesImage_t *>(pData)->m_dwNumGlyphs;
|
||||
font->m_Glyphs = reinterpret_cast<const FontFileStrikesImage_t *>(pData)->m_Glyphs; // Pointer
|
||||
}
|
||||
else
|
||||
{
|
||||
SSNES_ERR( "Incorrect version number on font file.\n" );
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Create the vertex and pixel shaders for rendering the font
|
||||
if( FAILED( xdk360_video_font_create_shaders(font) ) )
|
||||
{
|
||||
SSNES_ERR( "Could not create font shaders.\n" );
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
D3DDevice *pd3dDevice = vid->xdk360_render_device;
|
||||
|
||||
// Initialize the window
|
||||
D3DDISPLAYMODE DisplayMode;
|
||||
pd3dDevice->GetDisplayMode( 0, &DisplayMode );
|
||||
font->m_rcWindow.x1 = 0;
|
||||
font->m_rcWindow.y1 = 0;
|
||||
font->m_rcWindow.x2 = DisplayMode.Width;
|
||||
font->m_rcWindow.y2 = DisplayMode.Height;
|
||||
|
||||
// Determine whether we should save/restore state
|
||||
font->m_bSaveState = TRUE;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void xdk360_video_font_deinit(xdk360_video_font_t * font)
|
||||
{
|
||||
font->m_pFontTexture = NULL;
|
||||
font->m_dwNumGlyphs = 0L;
|
||||
font->m_Glyphs = NULL;
|
||||
font->m_cMaxGlyph = 0;
|
||||
font->m_TranslatorTable = NULL;
|
||||
font->m_dwNestedBeginCount = 0L;
|
||||
|
||||
// Safely release shaders
|
||||
// NOTE: They are released in reverse order of creation
|
||||
// to make sure any interdependencies are dealt with
|
||||
|
||||
if( ( s_FontLocals.m_pFontPixelShader != NULL ) && ( s_FontLocals.m_pFontPixelShader->Release() == 0 ) )
|
||||
s_FontLocals.m_pFontPixelShader = NULL;
|
||||
if( ( s_FontLocals.m_pFontVertexShader != NULL ) && ( s_FontLocals.m_pFontVertexShader->Release() == 0 ) )
|
||||
s_FontLocals.m_pFontVertexShader = NULL;
|
||||
if( ( s_FontLocals.m_pFontVertexDecl != NULL ) && ( s_FontLocals.m_pFontVertexDecl->Release() == 0 ) )
|
||||
s_FontLocals.m_pFontVertexDecl = NULL;
|
||||
|
||||
if( m_xprResource.m_bInitialized)
|
||||
m_xprResource.Destroy();
|
||||
}
|
||||
|
||||
void xdk360_video_font_set_cursor_position(xdk360_video_font_t *font, float fCursorX, float fCursorY )
|
||||
{
|
||||
font->m_fCursorX = floorf( fCursorX );
|
||||
font->m_fCursorY = floorf( fCursorY );
|
||||
}
|
||||
|
||||
void xdk360_video_font_get_text_width(xdk360_video_font_t * font, const wchar_t * strText, float * pWidth, float * pHeight, int bFirstLineOnly)
|
||||
{
|
||||
// Set default text extent in output parameters
|
||||
int iWidth = 0;
|
||||
float fHeight = 0.0f;
|
||||
|
||||
if( strText )
|
||||
{
|
||||
// Initialize counters that keep track of text extent
|
||||
int ix = 0;
|
||||
float fy = font->m_fFontHeight; // One character high to start
|
||||
if( fy > fHeight )
|
||||
fHeight = fy;
|
||||
|
||||
// Loop through each character and update text extent
|
||||
unsigned long letter;
|
||||
while( (letter = *strText) != 0 )
|
||||
{
|
||||
++strText;
|
||||
|
||||
// Handle newline character
|
||||
if( letter == L'\n' )
|
||||
{
|
||||
if( bFirstLineOnly )
|
||||
break;
|
||||
ix = 0;
|
||||
fy += font->m_fFontYAdvance;
|
||||
// since the height has changed, test against the height extent
|
||||
if( fy > fHeight )
|
||||
fHeight = fy;
|
||||
}
|
||||
|
||||
// Handle carriage return characters by ignoring them. This helps when
|
||||
// displaying text from a file.
|
||||
if( letter == L'\r' )
|
||||
continue;
|
||||
|
||||
// Translate unprintable characters
|
||||
const GLYPH_ATTR* pGlyph;
|
||||
|
||||
if( letter > font->m_cMaxGlyph )
|
||||
letter = 0; // Out of bounds?
|
||||
else
|
||||
letter = font->m_TranslatorTable[letter]; // Remap ASCII to glyph
|
||||
|
||||
pGlyph = &font->m_Glyphs[letter]; // Get the requested glyph
|
||||
|
||||
// Get text extent for this character's glyph
|
||||
ix += pGlyph->wOffset;
|
||||
ix += pGlyph->wAdvance;
|
||||
|
||||
// Since the x widened, test against the x extent
|
||||
|
||||
if( ix > iWidth )
|
||||
iWidth = ix;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert the width to a float here, load/hit/store. :(
|
||||
float fWidth = static_cast<float>(iWidth); // Delay the use if fWidth to reduce LHS pain
|
||||
// Apply the scale factor to the result
|
||||
fHeight *= font->m_fYScaleFactor;
|
||||
// Store the final results
|
||||
*pHeight = fHeight;
|
||||
|
||||
fWidth *= font->m_fXScaleFactor;
|
||||
*pWidth = fWidth;
|
||||
}
|
||||
|
||||
void xdk360_video_font_begin (xdk360_video_font_t * font)
|
||||
{
|
||||
// Set state on the first call
|
||||
if( font->m_dwNestedBeginCount == 0 )
|
||||
{
|
||||
// Cache the global pointer into a register
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
D3DDevice *pD3dDevice = vid->xdk360_render_device;
|
||||
|
||||
// Save state
|
||||
if( font->m_bSaveState )
|
||||
{
|
||||
// Note, we are not saving the texture, vertex, or pixel shader,
|
||||
// since it's not worth the performance. We're more interested
|
||||
// in saving state that would cause hard to find problems.
|
||||
pD3dDevice->GetRenderState( D3DRS_ALPHABLENDENABLE,
|
||||
&font->m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHABLENDENABLE ] );
|
||||
pD3dDevice->GetRenderState( D3DRS_SRCBLEND, &font->m_dwSavedState[ SAVEDSTATE_D3DRS_SRCBLEND ] );
|
||||
pD3dDevice->GetRenderState( D3DRS_DESTBLEND, &font->m_dwSavedState[ SAVEDSTATE_D3DRS_DESTBLEND ] );
|
||||
pD3dDevice->GetRenderState( D3DRS_BLENDOP, &font->m_dwSavedState[ SAVEDSTATE_D3DRS_BLENDOP ] );
|
||||
pD3dDevice->GetRenderState( D3DRS_ALPHATESTENABLE, &font->m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHATESTENABLE ] );
|
||||
pD3dDevice->GetRenderState( D3DRS_ALPHAREF, &font->m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHAREF ] );
|
||||
pD3dDevice->GetRenderState( D3DRS_ALPHAFUNC, &font->m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHAFUNC ] );
|
||||
pD3dDevice->GetRenderState( D3DRS_FILLMODE, &font->m_dwSavedState[ SAVEDSTATE_D3DRS_FILLMODE ] );
|
||||
pD3dDevice->GetRenderState( D3DRS_CULLMODE, &font->m_dwSavedState[ SAVEDSTATE_D3DRS_CULLMODE ] );
|
||||
pD3dDevice->GetRenderState( D3DRS_VIEWPORTENABLE, &font->m_dwSavedState[ SAVEDSTATE_D3DRS_VIEWPORTENABLE ] );
|
||||
font->m_dwSavedState[ SAVEDSTATE_D3DSAMP_MINFILTER ] = D3DDevice_GetSamplerState_MinFilter( pD3dDevice, 0 );
|
||||
font->m_dwSavedState[ SAVEDSTATE_D3DSAMP_MAGFILTER ] = D3DDevice_GetSamplerState_MagFilter( pD3dDevice, 0 );
|
||||
font->m_dwSavedState[ SAVEDSTATE_D3DSAMP_ADDRESSU ] = D3DDevice_GetSamplerState_AddressU( pD3dDevice, 0);
|
||||
font->m_dwSavedState[ SAVEDSTATE_D3DSAMP_ADDRESSV ] = D3DDevice_GetSamplerState_AddressV( pD3dDevice, 0);
|
||||
}
|
||||
|
||||
// Set the texture scaling factor as a vertex shader constant
|
||||
D3DSURFACE_DESC TextureDesc;
|
||||
D3DTexture_GetLevelDesc(font->m_pFontTexture, 0, &TextureDesc); // Get the description
|
||||
|
||||
// Set render state
|
||||
D3DDevice_SetTexture_Inline(pD3dDevice, 0, font->m_pFontTexture);
|
||||
|
||||
// Read the TextureDesc here to ensure no load/hit/store from GetLevelDesc()
|
||||
float vTexScale[4];
|
||||
vTexScale[0] = 1.0f / TextureDesc.Width; // LHS due to int->float conversion
|
||||
vTexScale[1] = 1.0f / TextureDesc.Height;
|
||||
vTexScale[2] = 0.0f;
|
||||
vTexScale[3] = 0.0f;
|
||||
|
||||
D3DDevice_SetRenderState_AlphaBlendEnable( pD3dDevice, TRUE );
|
||||
D3DDevice_SetRenderState_SrcBlend(pD3dDevice, D3DBLEND_SRCALPHA );
|
||||
D3DDevice_SetRenderState_DestBlend( pD3dDevice, D3DBLEND_INVSRCALPHA );
|
||||
D3DDevice_SetRenderState_BlendOp( pD3dDevice, D3DBLENDOP_ADD );
|
||||
pD3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, TRUE );
|
||||
pD3dDevice->SetRenderState( D3DRS_ALPHAREF, 0x08 );
|
||||
pD3dDevice->SetRenderState( D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL );
|
||||
pD3dDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
|
||||
pD3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
|
||||
pD3dDevice->SetRenderState( D3DRS_VIEWPORTENABLE, FALSE );
|
||||
D3DDevice_SetSamplerState_MinFilter(pD3dDevice, 0, D3DTEXF_LINEAR );
|
||||
D3DDevice_SetSamplerState_MagFilter(pD3dDevice, 0, D3DTEXF_LINEAR );
|
||||
D3DDevice_SetSamplerState_AddressU_Inline(pD3dDevice, 0, D3DTADDRESS_CLAMP );
|
||||
D3DDevice_SetSamplerState_AddressV_Inline(pD3dDevice, 0, D3DTADDRESS_CLAMP );
|
||||
|
||||
D3DDevice_SetVertexDeclaration(pD3dDevice, s_FontLocals.m_pFontVertexDecl );
|
||||
D3DDevice_SetVertexShader(pD3dDevice, s_FontLocals.m_pFontVertexShader );
|
||||
D3DDevice_SetPixelShader(pD3dDevice, s_FontLocals.m_pFontPixelShader );
|
||||
|
||||
// Set the texture scaling factor as a vertex shader constant
|
||||
// Call here to avoid load hit store from writing to vTexScale above
|
||||
pD3dDevice->SetVertexShaderConstantF( 2, vTexScale, 1 );
|
||||
}
|
||||
|
||||
// Keep track of the nested begin/end calls.
|
||||
font->m_dwNestedBeginCount++;
|
||||
}
|
||||
|
||||
void xdk360_video_font_end(xdk360_video_font_t * font)
|
||||
{
|
||||
if( --font->m_dwNestedBeginCount > 0 )
|
||||
return;
|
||||
|
||||
// Restore state
|
||||
if( font->m_bSaveState )
|
||||
{
|
||||
// Cache the global pointer into a register
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
D3DDevice *pD3dDevice = vid->xdk360_render_device;
|
||||
|
||||
D3DDevice_SetTexture_Inline(pD3dDevice, 0, NULL);
|
||||
D3DDevice_SetVertexDeclaration(pD3dDevice, NULL);
|
||||
D3DDevice_SetVertexShader(pD3dDevice, NULL );
|
||||
D3DDevice_SetPixelShader(pD3dDevice, NULL );
|
||||
D3DDevice_SetRenderState_AlphaBlendEnable(pD3dDevice, font->m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHABLENDENABLE ]);
|
||||
D3DDevice_SetRenderState_SrcBlend(pD3dDevice, font->m_dwSavedState[ SAVEDSTATE_D3DRS_SRCBLEND ] );
|
||||
D3DDevice_SetRenderState_DestBlend( pD3dDevice, font->m_dwSavedState[ SAVEDSTATE_D3DRS_DESTBLEND ] );
|
||||
D3DDevice_SetRenderState_BlendOp( pD3dDevice, font->m_dwSavedState[ SAVEDSTATE_D3DRS_BLENDOP ] );
|
||||
pD3dDevice->SetRenderState( D3DRS_ALPHATESTENABLE, font->m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHATESTENABLE ] );
|
||||
pD3dDevice->SetRenderState( D3DRS_ALPHAREF, font->m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHAREF ] );
|
||||
pD3dDevice->SetRenderState( D3DRS_ALPHAFUNC, font->m_dwSavedState[ SAVEDSTATE_D3DRS_ALPHAFUNC ] );
|
||||
pD3dDevice->SetRenderState( D3DRS_FILLMODE, font->m_dwSavedState[ SAVEDSTATE_D3DRS_FILLMODE ] );
|
||||
pD3dDevice->SetRenderState( D3DRS_CULLMODE, font->m_dwSavedState[ SAVEDSTATE_D3DRS_CULLMODE ] );
|
||||
pD3dDevice->SetRenderState( D3DRS_VIEWPORTENABLE, font->m_dwSavedState[ SAVEDSTATE_D3DRS_VIEWPORTENABLE ] );
|
||||
D3DDevice_SetSamplerState_MinFilter(pD3dDevice, 0, font->m_dwSavedState[ SAVEDSTATE_D3DSAMP_MINFILTER ] );
|
||||
D3DDevice_SetSamplerState_MagFilter(pD3dDevice, 0, font->m_dwSavedState[ SAVEDSTATE_D3DSAMP_MAGFILTER ] );
|
||||
D3DDevice_SetSamplerState_AddressU_Inline(pD3dDevice, 0, font->m_dwSavedState[ SAVEDSTATE_D3DSAMP_ADDRESSU ] );
|
||||
D3DDevice_SetSamplerState_AddressV_Inline(pD3dDevice, 0, font->m_dwSavedState[ SAVEDSTATE_D3DSAMP_ADDRESSV ] );
|
||||
}
|
||||
}
|
||||
|
||||
void xdk360_video_font_draw_text(xdk360_video_font_t * font, float fOriginX, float fOriginY, unsigned long dwColor,
|
||||
const wchar_t * strText, float fMaxPixelWidth )
|
||||
{
|
||||
if( strText == NULL || strText[0] == L'\0')
|
||||
return;
|
||||
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
D3DDevice *pd3dDevice = vid->xdk360_render_device;
|
||||
|
||||
// Set the color as a vertex shader constant
|
||||
float vColor[4];
|
||||
vColor[0] = ( ( dwColor & 0x00ff0000 ) >> 16L ) / 255.0F;
|
||||
vColor[1] = ( ( dwColor & 0x0000ff00 ) >> 8L ) / 255.0F;
|
||||
vColor[2] = ( ( dwColor & 0x000000ff ) >> 0L ) / 255.0F;
|
||||
vColor[3] = ( ( dwColor & 0xff000000 ) >> 24L ) / 255.0F;
|
||||
|
||||
// Set up stuff to prepare for drawing text
|
||||
xdk360_video_font_begin(font);
|
||||
|
||||
// Perform the actual storing of the color constant here to prevent
|
||||
// a load-hit-store by inserting work between the store and the use of
|
||||
// the vColor array.
|
||||
pd3dDevice->SetVertexShaderConstantF( 1, vColor, 1 );
|
||||
|
||||
// Set the starting screen position
|
||||
if((fOriginX < 0.0f))
|
||||
fOriginX += font->m_rcWindow.x2;
|
||||
if( fOriginY < 0.0f )
|
||||
fOriginY += font->m_rcWindow.y2;
|
||||
|
||||
font->m_fCursorX = floorf( fOriginX );
|
||||
font->m_fCursorY = floorf( fOriginY );
|
||||
|
||||
// Adjust for padding
|
||||
fOriginY -= font->m_fFontTopPadding;
|
||||
|
||||
// Add window offsets
|
||||
float Winx = 0.0f;
|
||||
float Winy = 0.0f;
|
||||
fOriginX += Winx;
|
||||
fOriginY += Winy;
|
||||
font->m_fCursorX += Winx;
|
||||
font->m_fCursorY += Winy;
|
||||
|
||||
// Begin drawing the vertices
|
||||
|
||||
// Declared as volatile to force writing in ascending
|
||||
// address order. It prevents out of sequence writing in write combined
|
||||
// memory.
|
||||
|
||||
volatile float * pVertex;
|
||||
|
||||
unsigned long dwNumChars = wcslen(strText);
|
||||
HRESULT hr = pd3dDevice->BeginVertices( D3DPT_QUADLIST, 4 * dwNumChars, sizeof( XMFLOAT4 ) ,
|
||||
( VOID** )&pVertex );
|
||||
// The ring buffer may run out of space when tiling, doing z-prepasses,
|
||||
// or using BeginCommandBuffer. If so, make the buffer larger.
|
||||
if( FAILED( hr ) )
|
||||
SSNES_ERR( "Ring buffer out of memory.\n" );
|
||||
|
||||
// Draw four vertices for each glyph
|
||||
while( *strText )
|
||||
{
|
||||
wchar_t letter;
|
||||
|
||||
// Get the current letter in the string
|
||||
letter = *strText++;
|
||||
|
||||
// Handle the newline character
|
||||
if( letter == L'\n' )
|
||||
{
|
||||
font->m_fCursorX = fOriginX;
|
||||
font->m_fCursorY += font->m_fFontYAdvance * font->m_fYScaleFactor;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Translate unprintable characters
|
||||
const GLYPH_ATTR * pGlyph = &font->m_Glyphs[ ( letter <= font->m_cMaxGlyph ) ? font->m_TranslatorTable[letter] : 0 ];
|
||||
|
||||
float fOffset = font->m_fXScaleFactor * (float)pGlyph->wOffset;
|
||||
float fAdvance = font->m_fXScaleFactor * (float)pGlyph->wAdvance;
|
||||
float fWidth = font->m_fXScaleFactor * (float)pGlyph->wWidth;
|
||||
float fHeight = font->m_fYScaleFactor * font->m_fFontHeight;
|
||||
|
||||
// Setup the screen coordinates
|
||||
font->m_fCursorX += fOffset;
|
||||
float X4 = font->m_fCursorX;
|
||||
float X1 = X4;
|
||||
float X3 = X4 + fWidth;
|
||||
float X2 = X1 + fWidth;
|
||||
float Y1 = font->m_fCursorY;
|
||||
float Y3 = Y1 + fHeight;
|
||||
float Y2 = Y1;
|
||||
float Y4 = Y3;
|
||||
|
||||
font->m_fCursorX += fAdvance;
|
||||
|
||||
// Select the RGBA channel that the compressed glyph is stored in
|
||||
// Takes a 4 bit per pixel ARGB value and expand it to an 8 bit per pixel ARGB value
|
||||
|
||||
unsigned long dwChannelSelector = pGlyph->wMask; // Convert to 32 bit
|
||||
// Perform the conversion without branching
|
||||
|
||||
// Splat the 4 bit per pixels from 0x1234 to 0x01020304
|
||||
dwChannelSelector = ((dwChannelSelector&0xF000)<<(24-12))|((dwChannelSelector&0xF00)<<(16-8))|
|
||||
((dwChannelSelector&0xF0)<<(8-4))|(dwChannelSelector&0xF);
|
||||
|
||||
// Perform a vectorized multiply to make 0x01020304 into 0x11223344
|
||||
dwChannelSelector *= 0x11;
|
||||
|
||||
// Add the vertices to draw this glyph
|
||||
|
||||
unsigned long tu1 = pGlyph->tu1; // Convert shorts to 32 bit longs for in register merging
|
||||
unsigned long tv1 = pGlyph->tv1;
|
||||
unsigned long tu2 = pGlyph->tu2;
|
||||
unsigned long tv2 = pGlyph->tv2;
|
||||
|
||||
// NOTE: The vertexs are 2 floats for the screen coordinates,
|
||||
// followed by two USHORTS for the u/vs of the character,
|
||||
// terminated with the ARGB 32 bit color.
|
||||
// This makes for 16 bytes per vertex data (Easier to read)
|
||||
// Second NOTE: The uvs are merged and written using a DWORD due
|
||||
// to the write combining hardware being only able to handle 32,
|
||||
// 64 and 128 writes. Never store to write combined memory with
|
||||
// 8 or 16 bit instructions. You've been warned.
|
||||
|
||||
pVertex[0] = X1;
|
||||
pVertex[1] = Y1;
|
||||
reinterpret_cast<volatile unsigned long *>(pVertex)[2] = (tu1<<16)|tv1; // Merged using big endian rules
|
||||
reinterpret_cast<volatile unsigned long *>(pVertex)[3] = dwChannelSelector;
|
||||
pVertex[4] = X2;
|
||||
pVertex[5] = Y2;
|
||||
reinterpret_cast<volatile unsigned long *>(pVertex)[6] = (tu2<<16)|tv1; // Merged using big endian rules
|
||||
reinterpret_cast<volatile unsigned long *>(pVertex)[7] = dwChannelSelector;
|
||||
pVertex[8] = X3;
|
||||
pVertex[9] = Y3;
|
||||
reinterpret_cast<volatile unsigned long *>(pVertex)[10] = (tu2<<16)|tv2; // Merged using big endian rules
|
||||
reinterpret_cast<volatile unsigned long *>(pVertex)[11] = dwChannelSelector;
|
||||
pVertex[12] = X4;
|
||||
pVertex[13] = Y4;
|
||||
reinterpret_cast<volatile unsigned long *>(pVertex)[14] = (tu1<<16)|tv2; // Merged using big endian rules
|
||||
reinterpret_cast<volatile unsigned long *>(pVertex)[15] = dwChannelSelector;
|
||||
pVertex+=16;
|
||||
|
||||
dwNumChars--;
|
||||
}
|
||||
|
||||
// Since we allocated vertex data space based on the string length, we now need to
|
||||
// add some dummy verts for any skipped characters (like newlines, etc.)
|
||||
while( dwNumChars )
|
||||
{
|
||||
pVertex[0] = 0;
|
||||
pVertex[1] = 0;
|
||||
pVertex[2] = 0;
|
||||
pVertex[3] = 0;
|
||||
pVertex[4] = 0;
|
||||
pVertex[5] = 0;
|
||||
pVertex[6] = 0;
|
||||
pVertex[7] = 0;
|
||||
pVertex[8] = 0;
|
||||
pVertex[9] = 0;
|
||||
pVertex[10] = 0;
|
||||
pVertex[11] = 0;
|
||||
pVertex[12] = 0;
|
||||
pVertex[13] = 0;
|
||||
pVertex[14] = 0;
|
||||
pVertex[15] = 0;
|
||||
pVertex+=16;
|
||||
dwNumChars--;
|
||||
}
|
||||
|
||||
// Stop drawing vertices
|
||||
D3DDevice_EndVertices(pd3dDevice);
|
||||
|
||||
// Undo window offsets
|
||||
font->m_fCursorX -= Winx;
|
||||
font->m_fCursorY -= Winy;
|
||||
|
||||
// Call End() to complete the begin/end pair for drawing text
|
||||
xdk360_video_font_end(font);
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef SSNES_360_FONTS_H
|
||||
#define SSNES_360_FONTS_H
|
||||
|
||||
#include "xdk360_video_resources.h"
|
||||
|
||||
#define PAGE_UP (255)
|
||||
#define PAGE_DOWN (-255)
|
||||
|
||||
#define SCREEN_SIZE_X_DEFAULT 640
|
||||
#define SCREEN_SIZE_Y_DEFAULT 480
|
||||
|
||||
#define SAFE_AREA_PCT_4x3 85
|
||||
#define SAFE_AREA_PCT_HDTV 90
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float m_fLineHeight; // height of a single line in pixels
|
||||
unsigned int m_nScrollOffset; // offset to display text (in lines)
|
||||
unsigned int first_message;
|
||||
unsigned int m_cxSafeArea;
|
||||
unsigned int m_cySafeArea;
|
||||
unsigned int m_cxSafeAreaOffset;
|
||||
unsigned int m_cySafeAreaOffset;
|
||||
unsigned int m_nCurLine; // index of current line being written to
|
||||
unsigned int m_cCurLineLength; // length of the current line
|
||||
unsigned long m_colBackColor;
|
||||
unsigned long m_colTextColor;
|
||||
unsigned int m_cScreenHeight; // height in lines of screen area
|
||||
unsigned int m_cScreenHeightVirtual; // height in lines of text storage buffer
|
||||
unsigned int m_cScreenWidth; // width in characters
|
||||
wchar_t * m_Buffer; // buffer big enough to hold a full screen
|
||||
wchar_t ** m_Lines; // pointers to individual lines
|
||||
} video_console_t;
|
||||
|
||||
typedef struct GLYPH_ATTR
|
||||
{
|
||||
unsigned short tu1, tv1, tu2, tv2; // Texture coordinates for the image
|
||||
short wOffset; // Pixel offset for glyph start
|
||||
short wWidth; // Pixel width of the glyph
|
||||
short wAdvance; // Pixels to advance after the glyph
|
||||
unsigned short wMask; // Channel mask
|
||||
} GLYPH_ATTR;
|
||||
|
||||
enum SavedStates
|
||||
{
|
||||
SAVEDSTATE_D3DRS_ALPHABLENDENABLE,
|
||||
SAVEDSTATE_D3DRS_SRCBLEND,
|
||||
SAVEDSTATE_D3DRS_DESTBLEND,
|
||||
SAVEDSTATE_D3DRS_BLENDOP,
|
||||
SAVEDSTATE_D3DRS_ALPHATESTENABLE,
|
||||
SAVEDSTATE_D3DRS_ALPHAREF,
|
||||
SAVEDSTATE_D3DRS_ALPHAFUNC,
|
||||
SAVEDSTATE_D3DRS_FILLMODE,
|
||||
SAVEDSTATE_D3DRS_CULLMODE,
|
||||
SAVEDSTATE_D3DRS_ZENABLE,
|
||||
SAVEDSTATE_D3DRS_STENCILENABLE,
|
||||
SAVEDSTATE_D3DRS_VIEWPORTENABLE,
|
||||
SAVEDSTATE_D3DSAMP_MINFILTER,
|
||||
SAVEDSTATE_D3DSAMP_MAGFILTER,
|
||||
SAVEDSTATE_D3DSAMP_ADDRESSU,
|
||||
SAVEDSTATE_D3DSAMP_ADDRESSV,
|
||||
|
||||
SAVEDSTATE_COUNT
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned int m_bSaveState;
|
||||
unsigned long m_dwSavedState[ SAVEDSTATE_COUNT ];
|
||||
unsigned long m_dwNestedBeginCount;
|
||||
unsigned long m_cMaxGlyph; // Number of entries in the translator table
|
||||
unsigned long m_dwNumGlyphs; // Number of valid glyphs
|
||||
float m_fFontHeight; // Height of the font strike in pixels
|
||||
float m_fFontTopPadding; // Padding above the strike zone
|
||||
float m_fFontBottomPadding; // Padding below the strike zone
|
||||
float m_fFontYAdvance; // Number of pixels to move the cursor for a line feed
|
||||
float m_fXScaleFactor; // Scaling constants
|
||||
float m_fYScaleFactor;
|
||||
float m_fCursorX; // Current text cursor
|
||||
float m_fCursorY;
|
||||
D3DRECT m_rcWindow; // Bounds rect of the text window, modify via accessors only!
|
||||
wchar_t * m_TranslatorTable; // ASCII to glyph lookup table
|
||||
D3DTexture* m_pFontTexture;
|
||||
const GLYPH_ATTR* m_Glyphs; // Array of glyphs
|
||||
} xdk360_video_font_t;
|
||||
|
||||
HRESULT xdk360_console_init ( LPCSTR strFontFileName, D3DCOLOR colBackColor, D3DCOLOR colTextColor);
|
||||
void xdk360_console_deinit (void);
|
||||
void xdk360_console_format (_In_z_ _Printf_format_string_ LPCSTR strFormat, ... );
|
||||
void xdk360_console_format_w (_In_z_ _Printf_format_string_ LPCWSTR wstrFormat, ... );
|
||||
void xdk360_console_draw (void);
|
||||
|
||||
HRESULT xdk360_video_font_init(xdk360_video_font_t * font, const char * strFontFileName);
|
||||
void xdk360_video_font_get_text_width(xdk360_video_font_t * font, const wchar_t * strText, float * pWidth, float * pHeight, int bFirstLineOnly);
|
||||
void xdk360_video_font_deinit(xdk360_video_font_t * font);
|
||||
void xdk360_video_font_set_cursor_position(xdk360_video_font_t *font, float fCursorX, float fCursorY );
|
||||
void xdk360_video_font_begin (xdk360_video_font_t * font);
|
||||
void xdk360_video_font_end (xdk360_video_font_t * font);
|
||||
void xdk360_video_font_set_size(float x, float y);
|
||||
void xdk360_video_font_draw_text(xdk360_video_font_t * font, float fOriginX, float fOriginY, unsigned long dwColor,
|
||||
const wchar_t * strText, float fMaxPixelWidth );
|
||||
|
||||
#endif
|
||||
+596
@@ -0,0 +1,596 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <xtl.h>
|
||||
#include <xfilecache.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <xbdm.h>
|
||||
#include "menu.h"
|
||||
#include "xdk360_input.h"
|
||||
#include "xdk360_video.h"
|
||||
|
||||
#include "../console/console_ext.h"
|
||||
#include "../console/main_wrap.h"
|
||||
#include "../conf/config_file.h"
|
||||
#include "../conf/config_file_macros.h"
|
||||
#include "../file.h"
|
||||
#include "../general.h"
|
||||
#include "shared.h"
|
||||
|
||||
#define DEVICE_MEMORY_UNIT0 1
|
||||
#define DEVICE_MEMORY_UNIT1 2
|
||||
#define DEVICE_MEMORY_ONBOARD 3
|
||||
#define DEVICE_CDROM0 4
|
||||
#define DEVICE_HARDISK0_PART1 5
|
||||
#define DEVICE_HARDISK0_SYSPART 6
|
||||
#define DEVICE_USB0 7
|
||||
#define DEVICE_USB1 8
|
||||
#define DEVICE_USB2 9
|
||||
#define DEVICE_TEST 10
|
||||
#define DEVICE_CACHE 11
|
||||
|
||||
typedef struct _STRING {
|
||||
unsigned short Length;
|
||||
unsigned short MaximumLength;
|
||||
char * Buffer;
|
||||
} STRING;
|
||||
|
||||
char DEFAULT_SHADER_FILE[MAX_PATH_LENGTH];
|
||||
char SYS_CONFIG_FILE[MAX_PATH_LENGTH];
|
||||
|
||||
extern "C" int __stdcall ObCreateSymbolicLink( STRING*, STRING*);
|
||||
|
||||
int Mounted[20];
|
||||
|
||||
int ssnes_main(int argc, char *argv[]);
|
||||
|
||||
#undef main
|
||||
|
||||
static int DriveMounted(std::string path)
|
||||
{
|
||||
WIN32_FIND_DATA findFileData;
|
||||
memset(&findFileData,0,sizeof(WIN32_FIND_DATA));
|
||||
std::string searchcmd = path + "\\*.*";
|
||||
HANDLE hFind = FindFirstFile(searchcmd.c_str(), &findFileData);
|
||||
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
return 0;
|
||||
|
||||
FindClose(hFind);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int Mount( int Device, char* MountPoint )
|
||||
{
|
||||
char MountConv[260];
|
||||
sprintf_s( MountConv,"\\??\\%s", MountPoint );
|
||||
|
||||
char * SysPath = NULL;
|
||||
switch( Device )
|
||||
{
|
||||
case DEVICE_MEMORY_UNIT0:
|
||||
SysPath = "\\Device\\Mu0";
|
||||
break;
|
||||
case DEVICE_MEMORY_UNIT1:
|
||||
SysPath = "\\Device\\Mu1";
|
||||
break;
|
||||
case DEVICE_MEMORY_ONBOARD:
|
||||
SysPath = "\\Device\\BuiltInMuSfc";
|
||||
break;
|
||||
case DEVICE_CDROM0:
|
||||
SysPath = "\\Device\\Cdrom0";
|
||||
break;
|
||||
case DEVICE_HARDISK0_PART1:
|
||||
SysPath = "\\Device\\Harddisk0\\Partition1";
|
||||
break;
|
||||
case DEVICE_HARDISK0_SYSPART:
|
||||
SysPath = "\\Device\\Harddisk0\\SystemPartition";
|
||||
break;
|
||||
case DEVICE_USB0:
|
||||
SysPath = "\\Device\\Mass0";
|
||||
break;
|
||||
case DEVICE_USB1:
|
||||
SysPath = "\\Device\\Mass1";
|
||||
break;
|
||||
case DEVICE_USB2:
|
||||
SysPath = "\\Device\\Mass2";
|
||||
break;
|
||||
case DEVICE_CACHE:
|
||||
SysPath = "\\Device\\Harddisk0\\Cache0";
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
STRING sSysPath = { (USHORT)strlen( SysPath ), (USHORT)strlen( SysPath ) + 1, SysPath };
|
||||
STRING sMountConv = { (USHORT)strlen( MountConv ), (USHORT)strlen( MountConv ) + 1, MountConv };
|
||||
int res = ObCreateSymbolicLink( &sMountConv, &sSysPath );
|
||||
|
||||
if (res != 0)
|
||||
return res;
|
||||
|
||||
return DriveMounted(MountPoint);
|
||||
}
|
||||
|
||||
static void set_default_settings (void)
|
||||
{
|
||||
//g_settings
|
||||
g_settings.rewind_enable = false;
|
||||
strlcpy(g_settings.video.cg_shader_path, DEFAULT_SHADER_FILE, sizeof(g_settings.video.cg_shader_path));
|
||||
g_settings.video.vsync = true;
|
||||
g_settings.video.smooth = true;
|
||||
g_settings.video.aspect_ratio = -1.0f;
|
||||
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_B] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_A];
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_Y] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_X];
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_SELECT] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_BACK];
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_START] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_START];
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_UP] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_UP];
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_DOWN] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_DOWN];
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_LEFT] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_LEFT];
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_RIGHT] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_RIGHT];
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_A] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_B];
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_X] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_Y];
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_L] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_LB];
|
||||
ssnes_default_keybind_lut[SNES_DEVICE_ID_JOYPAD_R] = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_RB];
|
||||
|
||||
for(uint32_t x = 0; x < MAX_PLAYERS; x++)
|
||||
{
|
||||
for(uint32_t y = 0; y < SSNES_FIRST_META_KEY; y++)
|
||||
{
|
||||
g_settings.input.binds[x][y].id = y;
|
||||
g_settings.input.binds[x][y].joykey = ssnes_default_keybind_lut[y];
|
||||
}
|
||||
g_settings.input.dpad_emulation[x] = DPAD_EMULATION_LSTICK;
|
||||
}
|
||||
|
||||
//g_console
|
||||
g_console.block_config_read = true;
|
||||
g_console.gamma_correction_enable = false;
|
||||
g_console.initialize_ssnes_enable = false;
|
||||
g_console.emulator_initialized = 0;
|
||||
g_console.mode_switch = MODE_MENU;
|
||||
g_console.screen_orientation = ORIENTATION_NORMAL;
|
||||
g_console.throttle_enable = true;
|
||||
g_console.aspect_ratio_index = 0;
|
||||
strlcpy(g_console.aspect_ratio_name, "4:3", sizeof(g_console.aspect_ratio_name));
|
||||
strlcpy(g_console.default_rom_startup_dir, "game:", sizeof(g_console.default_rom_startup_dir));
|
||||
g_console.custom_viewport_width = 0;
|
||||
g_console.custom_viewport_height = 0;
|
||||
g_console.custom_viewport_x = 0;
|
||||
g_console.custom_viewport_y = 0;
|
||||
|
||||
//g_extern
|
||||
g_extern.state_slot = 0;
|
||||
g_extern.audio_data.mute = 0;
|
||||
g_extern.verbose = true;
|
||||
}
|
||||
|
||||
static char **dir_list_new_360(const char *dir, const char *ext)
|
||||
{
|
||||
size_t cur_ptr = 0;
|
||||
size_t cur_size = 32;
|
||||
char **dir_list = NULL;
|
||||
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
|
||||
char path_buf[PATH_MAX];
|
||||
|
||||
if (strlcpy(path_buf, dir, sizeof(path_buf)) >= sizeof(path_buf))
|
||||
goto error;
|
||||
if (strlcat(path_buf, "*", sizeof(path_buf)) >= sizeof(path_buf))
|
||||
goto error;
|
||||
|
||||
if (ext)
|
||||
{
|
||||
if (strlcat(path_buf, ext, sizeof(path_buf)) >= sizeof(path_buf))
|
||||
goto error;
|
||||
}
|
||||
|
||||
hFind = FindFirstFile(path_buf, &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
|
||||
dir_list = (char**)calloc(cur_size, sizeof(char*));
|
||||
if (!dir_list)
|
||||
goto error;
|
||||
|
||||
do
|
||||
{
|
||||
// Not a perfect search of course, but hopefully good enough in practice.
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
continue;
|
||||
if (ext && !strstr(ffd.cFileName, ext))
|
||||
continue;
|
||||
|
||||
dir_list[cur_ptr] = (char*)malloc(PATH_MAX);
|
||||
if (!dir_list[cur_ptr])
|
||||
goto error;
|
||||
|
||||
strlcpy(dir_list[cur_ptr], dir, PATH_MAX);
|
||||
strlcat(dir_list[cur_ptr], ffd.cFileName, PATH_MAX);
|
||||
|
||||
cur_ptr++;
|
||||
if (cur_ptr + 1 == cur_size) // Need to reserve for NULL.
|
||||
{
|
||||
cur_size *= 2;
|
||||
dir_list = (char**)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*));
|
||||
}
|
||||
}while (FindNextFile(hFind, &ffd) != 0);
|
||||
|
||||
FindClose(hFind);
|
||||
return dir_list;
|
||||
|
||||
error:
|
||||
SSNES_ERR("Failed to open directory: \"%s\"\n", dir);
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
FindClose(hFind);
|
||||
dir_list_free(dir_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void init_settings (bool load_libsnes_path)
|
||||
{
|
||||
char fname_tmp[MAX_PATH_LENGTH];
|
||||
|
||||
if(!path_file_exists(SYS_CONFIG_FILE))
|
||||
{
|
||||
SSNES_ERR("Config file \"%s\" desn't exist. Creating...\n", "game:\\ssnes.cfg");
|
||||
FILE * f;
|
||||
f = fopen(SYS_CONFIG_FILE, "w");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
config_file_t * conf = config_file_new(SYS_CONFIG_FILE);
|
||||
|
||||
if(load_libsnes_path)
|
||||
{
|
||||
CONFIG_GET_STRING(libsnes, "libsnes_path");
|
||||
|
||||
if(!strcmp(g_settings.libsnes, ""))
|
||||
{
|
||||
//We need to set libsnes to the first entry in the cores
|
||||
//directory so that it will be saved to the config file
|
||||
char ** dir_list = dir_list_new_360("game:\\", ".xex");
|
||||
if (!dir_list)
|
||||
{
|
||||
SSNES_ERR("Couldn't read directory.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const char * first_xex = dir_list[0];
|
||||
|
||||
if(first_xex)
|
||||
{
|
||||
fill_pathname_base(fname_tmp, first_xex, sizeof(fname_tmp));
|
||||
|
||||
if(strcmp(fname_tmp, "SSNES-Salamander.xex") == 0)
|
||||
{
|
||||
SSNES_WARN("First entry is SSNES Salamander itself, increment entry by one and check if it exists.\n");
|
||||
first_xex = dir_list[1];
|
||||
fill_pathname_base(fname_tmp, first_xex, sizeof(fname_tmp));
|
||||
|
||||
if(!first_xex)
|
||||
{
|
||||
//This is very unlikely to happen
|
||||
SSNES_WARN("There is no second entry - no choice but to set it to SSNES Salamander\n");
|
||||
first_xex = dir_list[0];
|
||||
fill_pathname_base(fname_tmp, first_xex, sizeof(fname_tmp));
|
||||
}
|
||||
}
|
||||
SSNES_LOG("Set first .xex entry in dir: [%s] to libsnes path.\n", fname_tmp);
|
||||
snprintf(g_settings.libsnes, sizeof(g_settings.libsnes), "game:\\%s", fname_tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
SSNES_ERR("Failed to set first .xex entry to libsnes path.\n");
|
||||
}
|
||||
|
||||
dir_list_free(dir_list);
|
||||
}
|
||||
}
|
||||
|
||||
// g_settings
|
||||
CONFIG_GET_BOOL(rewind_enable, "rewind_enable");
|
||||
CONFIG_GET_BOOL(video.smooth, "video_smooth");
|
||||
CONFIG_GET_BOOL(video.vsync, "video_vsync");
|
||||
CONFIG_GET_FLOAT(video.aspect_ratio, "video_aspect_ratio");
|
||||
|
||||
// g_console
|
||||
CONFIG_GET_BOOL_CONSOLE(throttle_enable, "throttle_enable");
|
||||
CONFIG_GET_BOOL_CONSOLE(gamma_correction_enable, "gamma_correction_enable");
|
||||
CONFIG_GET_STRING_CONSOLE(default_rom_startup_dir, "default_rom_startup_dir");
|
||||
CONFIG_GET_INT_CONSOLE(aspect_ratio_index, "aspect_ratio_index");
|
||||
CONFIG_GET_INT_CONSOLE(screen_orientation, "screen_orientation");
|
||||
CONFIG_GET_STRING_CONSOLE(aspect_ratio_name, "aspect_ratio_name");
|
||||
|
||||
// g_extern
|
||||
CONFIG_GET_INT_EXTERN(state_slot, "state_slot");
|
||||
CONFIG_GET_INT_EXTERN(audio_data.mute, "audio_mute");
|
||||
}
|
||||
|
||||
static void save_settings (void)
|
||||
{
|
||||
if(!path_file_exists(SYS_CONFIG_FILE))
|
||||
{
|
||||
FILE * f;
|
||||
f = fopen(SYS_CONFIG_FILE, "w");
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
config_file_t * conf = config_file_new(SYS_CONFIG_FILE);
|
||||
|
||||
if(conf == NULL)
|
||||
conf = config_file_new(NULL);
|
||||
|
||||
// g_settings
|
||||
config_set_string(conf, "libsnes_path", g_settings.libsnes);
|
||||
config_set_bool(conf, "rewind_enable", g_settings.rewind_enable);
|
||||
config_set_bool(conf, "video_smooth", g_settings.video.smooth);
|
||||
config_set_bool(conf, "video_vsync", g_settings.video.vsync);
|
||||
|
||||
// g_console
|
||||
config_set_string(conf, "default_rom_startup_dir", g_console.default_rom_startup_dir);
|
||||
config_set_bool(conf, "gamma_correction_enable", g_console.gamma_correction_enable);
|
||||
config_set_bool(conf, "throttle_enable", g_console.throttle_enable);
|
||||
config_set_int(conf, "aspect_ratio_index", g_console.aspect_ratio_index);
|
||||
config_set_int(conf, "custom_viewport_width", g_console.custom_viewport_width);
|
||||
config_set_int(conf, "custom_viewport_height", g_console.custom_viewport_height);
|
||||
config_set_int(conf, "custom_viewport_x", g_console.custom_viewport_x);
|
||||
config_set_int(conf, "custom_viewport_y", g_console.custom_viewport_y);
|
||||
config_set_int(conf, "screen_orientation", g_console.screen_orientation);
|
||||
config_set_string(conf, "aspect_ratio_name", g_console.aspect_ratio_name);
|
||||
|
||||
// g_extern
|
||||
config_set_int(conf, "state_slot", g_extern.state_slot);
|
||||
config_set_int(conf, "audio_mute", g_extern.audio_data.mute);
|
||||
|
||||
if (!config_file_write(conf, SYS_CONFIG_FILE))
|
||||
SSNES_ERR("Failed to write config file to \"%s\". Check permissions.\n", SYS_CONFIG_FILE);
|
||||
|
||||
free(conf);
|
||||
}
|
||||
|
||||
static void get_environment_settings (void)
|
||||
{
|
||||
DWORD ret;
|
||||
|
||||
//for devkits only, we will need to mount all partitions for retail
|
||||
//in a different way
|
||||
//DmMapDevkitDrive();
|
||||
|
||||
memset(&Mounted, 0, 20);
|
||||
|
||||
Mounted[DEVICE_USB0] = Mount(DEVICE_USB0,"Usb0:");
|
||||
Mounted[DEVICE_USB1] = Mount(DEVICE_USB1,"Usb1:");
|
||||
Mounted[DEVICE_USB2] = Mount(DEVICE_USB2,"Usb2:");
|
||||
Mounted[DEVICE_HARDISK0_PART1] = Mount(DEVICE_HARDISK0_PART1,"Hdd1:");
|
||||
Mounted[DEVICE_HARDISK0_SYSPART] = Mount(DEVICE_HARDISK0_SYSPART,"HddX:");
|
||||
Mounted[DEVICE_MEMORY_UNIT0] = Mount(DEVICE_MEMORY_UNIT0,"Memunit0:");
|
||||
Mounted[DEVICE_MEMORY_UNIT1] = Mount(DEVICE_MEMORY_UNIT1,"Memunit1:");
|
||||
Mounted[DEVICE_MEMORY_ONBOARD] = Mount(DEVICE_MEMORY_ONBOARD,"OnBoardMU:");
|
||||
Mounted[DEVICE_CDROM0] = Mount(DEVICE_CDROM0,"Dvd:");
|
||||
|
||||
ret = XSetFileCacheSize(0x100000);
|
||||
|
||||
if(ret != TRUE)
|
||||
{
|
||||
SSNES_ERR("Couldn't change number of bytes reserved for file system cache.\n");
|
||||
}
|
||||
|
||||
ret = XFileCacheInit(XFILECACHE_CLEAR_ALL, 0x100000, XFILECACHE_DEFAULT_THREAD, 0, 1);
|
||||
|
||||
if(ret != ERROR_SUCCESS)
|
||||
{
|
||||
SSNES_ERR("File cache could not be initialized.\n");
|
||||
}
|
||||
|
||||
XFlushUtilityDrive();
|
||||
//unsigned long result = XMountUtilityDriveEx(XMOUNTUTILITYDRIVE_FORMAT0,8192, 0);
|
||||
|
||||
//if(result != ERROR_SUCCESS)
|
||||
//{
|
||||
// SSNES_ERR("Couldn't mount/format utility drive.\n");
|
||||
//}
|
||||
|
||||
// detect install environment
|
||||
unsigned long license_mask;
|
||||
|
||||
if (XContentGetLicenseMask(&license_mask, NULL) != ERROR_SUCCESS)
|
||||
{
|
||||
printf("SSNES was launched as a standalone DVD, or using DVD emulation, or from the development area of the HDD.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
XContentQueryVolumeDeviceType("GAME",&g_console.volume_device_type, NULL);
|
||||
|
||||
switch(g_console.volume_device_type)
|
||||
{
|
||||
case XCONTENTDEVICETYPE_HDD:
|
||||
printf("SSNES was launched from a content package on HDD.\n");
|
||||
break;
|
||||
case XCONTENTDEVICETYPE_MU:
|
||||
printf("SSNES was launched from a content package on USB or Memory Unit.\n");
|
||||
break;
|
||||
case XCONTENTDEVICETYPE_ODD:
|
||||
printf("SSNES was launched from a content package on Optical Disc Drive.\n");
|
||||
break;
|
||||
default:
|
||||
printf("SSNES was launched from a content package on an unknown device type.\n");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
strlcpy(DEFAULT_SHADER_FILE, "game:\\media\\shaders\\stock.cg", sizeof(DEFAULT_SHADER_FILE));
|
||||
strlcpy(SYS_CONFIG_FILE, "game:\\ssnes.cfg", sizeof(SYS_CONFIG_FILE));
|
||||
}
|
||||
|
||||
static bool manage_libsnes_core(void)
|
||||
{
|
||||
g_extern.verbose = true;
|
||||
bool return_code;
|
||||
|
||||
bool set_libsnes_path = false;
|
||||
char tmp_path[1024], tmp_path2[1024], tmp_pathnewfile[1024];
|
||||
snprintf(tmp_path, sizeof(tmp_path), "game:\\CORE.xex");
|
||||
SSNES_LOG("Assumed path of CORE.xex: [%s]\n", tmp_path);
|
||||
if(path_file_exists(tmp_path))
|
||||
{
|
||||
//if CORE.xex exists, this indicates we have just installed
|
||||
//a new libsnes port and that we need to change it to a more
|
||||
//sane name.
|
||||
|
||||
int ret;
|
||||
|
||||
ssnes_console_name_from_id(tmp_path2, sizeof(tmp_path2));
|
||||
strlcat(tmp_path2, ".xex", sizeof(tmp_path2));
|
||||
snprintf(tmp_pathnewfile, sizeof(tmp_pathnewfile), "game:\\%s", tmp_path2);
|
||||
|
||||
if(path_file_exists(tmp_pathnewfile))
|
||||
{
|
||||
SSNES_LOG("Upgrading emulator core...\n");
|
||||
//if libsnes core already exists, then that means we are
|
||||
//upgrading the libsnes core - so delete pre-existing
|
||||
//file first
|
||||
ret = DeleteFile(tmp_pathnewfile);
|
||||
if(ret != 0)
|
||||
{
|
||||
SSNES_LOG("Succeeded in removing pre-existing libsnes core: [%s].\n", tmp_pathnewfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
SSNES_LOG("Failed to remove pre-existing libsnes core: [%s].\n", tmp_pathnewfile);
|
||||
}
|
||||
}
|
||||
|
||||
//now attempt the renaming
|
||||
ret = MoveFileExA(tmp_path, tmp_pathnewfile, NULL);
|
||||
if(ret == 0)
|
||||
{
|
||||
SSNES_ERR("Failed to rename CORE.xex.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
SSNES_LOG("Libsnes core [%s] renamed to: [%s].\n", tmp_path, tmp_pathnewfile);
|
||||
set_libsnes_path = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SSNES_LOG("CORE.xex was not found, libsnes core path will be loaded from config file.\n");
|
||||
}
|
||||
|
||||
if(set_libsnes_path)
|
||||
{
|
||||
//CORE.xex has been renamed, libsnes path will now be set to the recently
|
||||
//renamed new libsnes core
|
||||
strlcpy(g_settings.libsnes, tmp_pathnewfile, sizeof(g_settings.libsnes));
|
||||
return_code = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//There was no CORE.xex present, or the CORE.xex file was not renamed.
|
||||
//The libsnes core path will still be loaded from the config file
|
||||
return_code = 1;
|
||||
}
|
||||
|
||||
g_extern.verbose = false;
|
||||
|
||||
return return_code;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
get_environment_settings();
|
||||
|
||||
ssnes_main_clear_state();
|
||||
config_set_defaults();
|
||||
|
||||
bool load_libsnes_path = manage_libsnes_core();
|
||||
|
||||
set_default_settings();
|
||||
init_settings(load_libsnes_path);
|
||||
init_libsnes_sym();
|
||||
|
||||
xdk360_video_init();
|
||||
xdk360_input_init();
|
||||
|
||||
ssnes_input_set_default_keybind_names_for_emulator();
|
||||
|
||||
menu_init();
|
||||
|
||||
begin_loop:
|
||||
if(g_console.mode_switch == MODE_EMULATION)
|
||||
{
|
||||
bool repeat = false;
|
||||
|
||||
input_xdk360.poll(NULL);
|
||||
|
||||
do{
|
||||
repeat = ssnes_main_iterate();
|
||||
}while(repeat && !g_console.frame_advance_enable);
|
||||
}
|
||||
else if(g_console.mode_switch == MODE_MENU)
|
||||
{
|
||||
menu_loop();
|
||||
|
||||
if(g_console.initialize_ssnes_enable)
|
||||
{
|
||||
if(g_console.emulator_initialized)
|
||||
ssnes_main_deinit();
|
||||
|
||||
struct ssnes_main_wrap args = {0};
|
||||
|
||||
args.verbose = g_extern.verbose;
|
||||
args.config_path = SYS_CONFIG_FILE;
|
||||
args.rom_path = g_console.rom_path;
|
||||
|
||||
int init_ret = ssnes_main_init_wrap(&args);
|
||||
g_console.emulator_initialized = 1;
|
||||
g_console.initialize_ssnes_enable = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
goto begin_shutdown;
|
||||
|
||||
goto begin_loop;
|
||||
|
||||
begin_shutdown:
|
||||
if(path_file_exists(SYS_CONFIG_FILE))
|
||||
save_settings();
|
||||
xdk360_video_deinit();
|
||||
|
||||
if(g_console.return_to_launcher)
|
||||
{
|
||||
SSNES_LOG("Attempt to load XEX: [%s].\n", g_console.launch_app_on_exit);
|
||||
XLaunchNewImage(g_console.launch_app_on_exit, NULL);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
|
||||
<RDF Version="XPR2">
|
||||
|
||||
<Texture
|
||||
Name = "FontTexture"
|
||||
Source = "Arial_12.tga"
|
||||
Format = "D3DFMT_LIN_A4R4G4B4"
|
||||
Levels = "1"
|
||||
/>
|
||||
|
||||
<UserData
|
||||
Name = "FontData"
|
||||
DataFile = "Arial_12.abc"
|
||||
/>
|
||||
|
||||
</RDF>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 256 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
VERTEX_SHADER
|
||||
*/
|
||||
void main_vertex
|
||||
(
|
||||
float2 position : POSITION,
|
||||
float2 texCoord : TEXCOORD0,
|
||||
uniform float4x4 modelViewProj : register(c0),
|
||||
out float4 oPosition : POSITION,
|
||||
out float2 otexCoord : TEXCOORD
|
||||
)
|
||||
{
|
||||
oPosition = mul(modelViewProj, float4(position, 0.0, 1.0));
|
||||
otexCoord = texCoord;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
FRAGMENT SHADER
|
||||
*/
|
||||
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 : register(s0), uniform input IN)
|
||||
{
|
||||
output OUT;
|
||||
OUT.color = tex2D(decal, texCoord);
|
||||
return OUT;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,159 @@
|
||||
<XuiCanvas version="000c">
|
||||
<Properties>
|
||||
<Width>1280.000000</Width>
|
||||
<Height>720.000000</Height>
|
||||
</Properties>
|
||||
<XuiScene>
|
||||
<Properties>
|
||||
<Id>XuiSceneFileBrowser</Id>
|
||||
<Width>575.888062</Width>
|
||||
<Height>336.017609</Height>
|
||||
<Position>353.995880,209.035965,0.000000</Position>
|
||||
<ClassOverride>SSNESFileBrowser</ClassOverride>
|
||||
</Properties>
|
||||
<XuiText>
|
||||
<Properties>
|
||||
<Id>XuiTxtTitle</Id>
|
||||
<Width>111.840027</Width>
|
||||
<Height>40.000000</Height>
|
||||
<Position>28.893612,12.519989,0.000000</Position>
|
||||
<Text>Filebrowser</Text>
|
||||
<TextColor>0xff0f0f0f</TextColor>
|
||||
<DropShadowColor>0x800f0f0f</DropShadowColor>
|
||||
<Font>Arial Unicode MS</Font>
|
||||
<TextStyle>21</TextStyle>
|
||||
</Properties>
|
||||
</XuiText>
|
||||
<XuiCommonList>
|
||||
<Properties>
|
||||
<Id>XuiRomList</Id>
|
||||
<Width>536.582458</Width>
|
||||
<Height>170.800018</Height>
|
||||
<Position>22.799980,81.600006,0.000000</Position>
|
||||
<NavLeft>XuiBackButton1</NavLeft>
|
||||
<NavRight>XuiBackButton1</NavRight>
|
||||
<NavUp>XuiBtnGameDir</NavUp>
|
||||
<NavDown>XuiBtnGameDir</NavDown>
|
||||
</Properties>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
</XuiCommonList>
|
||||
<XuiBackButton>
|
||||
<Properties>
|
||||
<Id>XuiBackButton1</Id>
|
||||
<Width>204.599976</Width>
|
||||
<Height>36.000000</Height>
|
||||
<Position>353.115234,281.396820,0.000000</Position>
|
||||
<NavLeft>XuiBtnCacheDir</NavLeft>
|
||||
<NavRight>XuiBtnGameDir</NavRight>
|
||||
<NavUp>XuiRomList</NavUp>
|
||||
<NavDown>XuiRomList</NavDown>
|
||||
<Text>Go back to menu</Text>
|
||||
<PressKey>22593</PressKey>
|
||||
</Properties>
|
||||
</XuiBackButton>
|
||||
<XuiText>
|
||||
<Properties>
|
||||
<Id>XuiTxtRomPath</Id>
|
||||
<Width>166.559998</Width>
|
||||
<Height>28.480011</Height>
|
||||
<Position>96.000000,42.480011,0.000000</Position>
|
||||
<TextColor>0xff0f0f0f</TextColor>
|
||||
<DropShadowColor>0x800f0f0f</DropShadowColor>
|
||||
</Properties>
|
||||
</XuiText>
|
||||
<XuiLabel>
|
||||
<Properties>
|
||||
<Id>XuiLblPath</Id>
|
||||
<Width>72.880005</Width>
|
||||
<Height>28.000000</Height>
|
||||
<Position>26.000000,43.000000,0.000000</Position>
|
||||
<Text>PATH:</Text>
|
||||
</Properties>
|
||||
</XuiLabel>
|
||||
<XuiButton>
|
||||
<Properties>
|
||||
<Id>XuiBtnGameDir</Id>
|
||||
<Width>115.640007</Width>
|
||||
<Height>38.000000</Height>
|
||||
<Position>30.000000,277.000000,0.000000</Position>
|
||||
<NavLeft>XuiBackButton1</NavLeft>
|
||||
<NavRight>XuiBtnCacheDir</NavRight>
|
||||
<NavUp>XuiRomList</NavUp>
|
||||
<NavDown>XuiRomList</NavDown>
|
||||
<Text>game:</Text>
|
||||
</Properties>
|
||||
</XuiButton>
|
||||
<XuiButton>
|
||||
<Properties>
|
||||
<Id>XuiBtnCacheDir</Id>
|
||||
<Width>119.960014</Width>
|
||||
<Height>38.000000</Height>
|
||||
<Position>160.000000,277.000000,0.000000</Position>
|
||||
<NavLeft>XuiBtnGameDir</NavLeft>
|
||||
<NavRight>XuiBackButton1</NavRight>
|
||||
<NavUp>XuiRomList</NavUp>
|
||||
<NavDown>XuiRomList</NavDown>
|
||||
<Text>cache:</Text>
|
||||
</Properties>
|
||||
</XuiButton>
|
||||
</XuiScene>
|
||||
</XuiCanvas>
|
||||
@@ -0,0 +1,145 @@
|
||||
<XuiCanvas version="000c">
|
||||
<Properties>
|
||||
<Width>1280.000000</Width>
|
||||
<Height>720.000000</Height>
|
||||
</Properties>
|
||||
<XuiScene>
|
||||
<Properties>
|
||||
<Id>XuiSceneCoreFileBrowser</Id>
|
||||
<Width>575.888062</Width>
|
||||
<Height>336.017609</Height>
|
||||
<Position>353.995880,209.035965,0.000000</Position>
|
||||
<ClassOverride>SSNESCoreBrowser</ClassOverride>
|
||||
</Properties>
|
||||
<XuiText>
|
||||
<Properties>
|
||||
<Id>XuiTxtTitle</Id>
|
||||
<Width>326.640015</Width>
|
||||
<Height>40.000000</Height>
|
||||
<Position>28.893612,12.519989,0.000000</Position>
|
||||
<Text>Filebrowser - Select a libsnes core</Text>
|
||||
<TextColor>0xff0f0f0f</TextColor>
|
||||
<DropShadowColor>0x800f0f0f</DropShadowColor>
|
||||
<Font>Arial Unicode MS</Font>
|
||||
<TextStyle>21</TextStyle>
|
||||
</Properties>
|
||||
</XuiText>
|
||||
<XuiCommonList>
|
||||
<Properties>
|
||||
<Id>XuiRomList</Id>
|
||||
<Width>536.582458</Width>
|
||||
<Height>170.800018</Height>
|
||||
<Position>22.799980,81.600006,0.000000</Position>
|
||||
<NavLeft>XuiBackButton1</NavLeft>
|
||||
<NavRight>XuiBackButton1</NavRight>
|
||||
<NavUp>XuiBackButton1</NavUp>
|
||||
<NavDown>XuiBackButton1</NavDown>
|
||||
</Properties>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
</XuiCommonList>
|
||||
<XuiBackButton>
|
||||
<Properties>
|
||||
<Id>XuiBackButton1</Id>
|
||||
<Width>219.000000</Width>
|
||||
<Height>36.000000</Height>
|
||||
<Position>338.715210,281.396820,0.000000</Position>
|
||||
<NavLeft>XuiRomList</NavLeft>
|
||||
<NavRight>XuiRomList</NavRight>
|
||||
<NavUp>XuiRomList</NavUp>
|
||||
<NavDown>XuiRomList</NavDown>
|
||||
<Text>Go back to menu</Text>
|
||||
<PressKey>22593</PressKey>
|
||||
</Properties>
|
||||
</XuiBackButton>
|
||||
<XuiText>
|
||||
<Properties>
|
||||
<Id>XuiTxtRomPath</Id>
|
||||
<Width>166.559998</Width>
|
||||
<Height>28.480011</Height>
|
||||
<Position>96.000000,42.480011,0.000000</Position>
|
||||
<TextColor>0xff0f0f0f</TextColor>
|
||||
<DropShadowColor>0x800f0f0f</DropShadowColor>
|
||||
</Properties>
|
||||
</XuiText>
|
||||
<XuiLabel>
|
||||
<Properties>
|
||||
<Id>XuiLblPath</Id>
|
||||
<Width>72.880005</Width>
|
||||
<Height>28.000000</Height>
|
||||
<Position>26.000000,43.000000,0.000000</Position>
|
||||
<Text>PATH:</Text>
|
||||
</Properties>
|
||||
</XuiLabel>
|
||||
</XuiScene>
|
||||
</XuiCanvas>
|
||||
@@ -0,0 +1,116 @@
|
||||
<XuiCanvas version="000c">
|
||||
<Properties>
|
||||
<Width>1280.000000</Width>
|
||||
<Height>720.000000</Height>
|
||||
</Properties>
|
||||
<XuiScene>
|
||||
<Properties>
|
||||
<Id>XuiScene1</Id>
|
||||
<Width>584.528076</Width>
|
||||
<Height>363.320007</Height>
|
||||
<Position>347.429474,184.843964,0.000000</Position>
|
||||
<ClassOverride>SSNESMain</ClassOverride>
|
||||
</Properties>
|
||||
<XuiButton>
|
||||
<Properties>
|
||||
<Id>XuiBtnRomBrowser</Id>
|
||||
<Width>488.312012</Width>
|
||||
<Height>30.799988</Height>
|
||||
<Position>38.127724,64.215515,0.000000</Position>
|
||||
<Pivot>245.362122,102.247192,0.000000</Pivot>
|
||||
<NavUp>XuiBtnQuit</NavUp>
|
||||
<NavDown>XuiBtnQuickMenu</NavDown>
|
||||
<Text>ROM Browser</Text>
|
||||
</Properties>
|
||||
</XuiButton>
|
||||
<XuiText>
|
||||
<Properties>
|
||||
<Id>XuiTxtTitle</Id>
|
||||
<Width>247.200012</Width>
|
||||
<Height>40.000000</Height>
|
||||
<Position>43.581604,21.159988,0.000000</Position>
|
||||
<Text>SSNES 360</Text>
|
||||
<TextColor>0xff0f0f0f</TextColor>
|
||||
<DropShadowColor>0x800f0f0f</DropShadowColor>
|
||||
<Font>Arial Unicode MS</Font>
|
||||
<TextStyle>21</TextStyle>
|
||||
</Properties>
|
||||
</XuiText>
|
||||
<XuiButton>
|
||||
<Properties>
|
||||
<Id>XuiBtnSettings</Id>
|
||||
<Width>488.312012</Width>
|
||||
<Height>30.799988</Height>
|
||||
<Position>38.127960,196.615982,0.000000</Position>
|
||||
<NavUp>XuiBtnControls</NavUp>
|
||||
<NavDown>XuiBtnLibsnesCore</NavDown>
|
||||
<Text>Settings</Text>
|
||||
</Properties>
|
||||
</XuiButton>
|
||||
<XuiButton>
|
||||
<Properties>
|
||||
<Id>XuiBtnQuit</Id>
|
||||
<Width>488.312012</Width>
|
||||
<Height>30.799988</Height>
|
||||
<Position>38.327736,287.007996,0.000000</Position>
|
||||
<Pivot>245.162109,-84.153275,0.000000</Pivot>
|
||||
<NavUp>XuiBtnLibsnesCore</NavUp>
|
||||
<NavDown>XuiBtnRomBrowser</NavDown>
|
||||
<Text>Quit</Text>
|
||||
</Properties>
|
||||
</XuiButton>
|
||||
<XuiLabel>
|
||||
<Properties>
|
||||
<Id>XuiLblCore</Id>
|
||||
<Width>124.000000</Width>
|
||||
<Height>28.000000</Height>
|
||||
<Position>12.544006,324.792023,0.000000</Position>
|
||||
<Text>Libsnes core:</Text>
|
||||
</Properties>
|
||||
</XuiLabel>
|
||||
<XuiText>
|
||||
<Properties>
|
||||
<Id>XuiTxtCoreText</Id>
|
||||
<Width>224.399963</Width>
|
||||
<Height>34.000000</Height>
|
||||
<Position>134.944031,324.391998,0.000000</Position>
|
||||
<TextColor>0xff0f0f0f</TextColor>
|
||||
<DropShadowColor>0x800f0f0f</DropShadowColor>
|
||||
<Font>Arial Unicode MS</Font>
|
||||
</Properties>
|
||||
</XuiText>
|
||||
<XuiButton>
|
||||
<Properties>
|
||||
<Id>XuiBtnQuickMenu</Id>
|
||||
<Width>488.312012</Width>
|
||||
<Height>30.799988</Height>
|
||||
<Position>38.130001,106.655991,0.000000</Position>
|
||||
<NavUp>XuiBtnRomBrowser</NavUp>
|
||||
<NavDown>XuiBtnControls</NavDown>
|
||||
<Text>Quick Menu</Text>
|
||||
</Properties>
|
||||
</XuiButton>
|
||||
<XuiButton>
|
||||
<Properties>
|
||||
<Id>XuiBtnControls</Id>
|
||||
<Width>488.312012</Width>
|
||||
<Height>30.799988</Height>
|
||||
<Position>38.130001,151.655991,0.000000</Position>
|
||||
<NavUp>XuiBtnQuickMenu</NavUp>
|
||||
<NavDown>XuiBtnSettings</NavDown>
|
||||
<Text>Controls</Text>
|
||||
</Properties>
|
||||
</XuiButton>
|
||||
<XuiButton>
|
||||
<Properties>
|
||||
<Id>XuiBtnLibsnesCore</Id>
|
||||
<Width>488.312012</Width>
|
||||
<Height>30.799988</Height>
|
||||
<Position>38.127960,241.615982,0.000000</Position>
|
||||
<NavUp>XuiBtnSettings</NavUp>
|
||||
<NavDown>XuiBtnQuit</NavDown>
|
||||
<Text>Change libsnes core</Text>
|
||||
</Properties>
|
||||
</XuiButton>
|
||||
</XuiScene>
|
||||
</XuiCanvas>
|
||||
@@ -0,0 +1,79 @@
|
||||
<XuiCanvas version="000c">
|
||||
<Properties>
|
||||
<Width>1280.000000</Width>
|
||||
<Height>720.000000</Height>
|
||||
</Properties>
|
||||
<XuiScene>
|
||||
<Properties>
|
||||
<Id>XuiScene1</Id>
|
||||
<Width>584.528076</Width>
|
||||
<Height>363.320007</Height>
|
||||
<Position>347.429474,184.843964,0.000000</Position>
|
||||
<ClassOverride>SSNESQuickMenu</ClassOverride>
|
||||
</Properties>
|
||||
<XuiText>
|
||||
<Properties>
|
||||
<Id>XuiTxtTitle</Id>
|
||||
<Width>121.056023</Width>
|
||||
<Height>40.000000</Height>
|
||||
<Position>43.581604,21.159988,0.000000</Position>
|
||||
<Text>Quick Menu</Text>
|
||||
<TextColor>0xff0f0f0f</TextColor>
|
||||
<DropShadowColor>0x800f0f0f</DropShadowColor>
|
||||
<Font>Arial Unicode MS</Font>
|
||||
<TextStyle>21</TextStyle>
|
||||
</Properties>
|
||||
</XuiText>
|
||||
<XuiBackButton>
|
||||
<Properties>
|
||||
<Id>XuiBackButton</Id>
|
||||
<Width>208.632019</Width>
|
||||
<Height>36.000000</Height>
|
||||
<Position>356.670013,309.000000,0.000000</Position>
|
||||
<NavLeft>XuiQuickMenuList</NavLeft>
|
||||
<NavRight>XuiQuickMenuList</NavRight>
|
||||
<NavUp>XuiQuickMenuList</NavUp>
|
||||
<NavDown>XuiQuickMenuList</NavDown>
|
||||
<Text>Go back to menu</Text>
|
||||
<PressKey>22593</PressKey>
|
||||
</Properties>
|
||||
</XuiBackButton>
|
||||
<XuiCommonList>
|
||||
<Properties>
|
||||
<Id>XuiQuickMenuList</Id>
|
||||
<Width>492.000000</Width>
|
||||
<Height>232.720001</Height>
|
||||
<Position>44.880005,64.959991,0.000000</Position>
|
||||
<NavLeft>XuiBackButton</NavLeft>
|
||||
<NavRight>XuiBackButton</NavRight>
|
||||
<NavUp>XuiBackButton</NavUp>
|
||||
<NavDown>XuiBackButton</NavDown>
|
||||
<ItemsText>Load State
|
||||
Save State
|
||||
Hardware filtering:
|
||||
Aspect Ratio:
|
||||
Overscan:
|
||||
Orientation:
|
||||
Resize Mode
|
||||
Frame Advance
|
||||
Screenshot Mode
|
||||
Reset
|
||||
Return to Game
|
||||
Return to Dashboard
|
||||
</ItemsText>
|
||||
</Properties>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
</XuiCommonList>
|
||||
</XuiScene>
|
||||
</XuiCanvas>
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,94 @@
|
||||
<XuiCanvas version="000c">
|
||||
<Properties>
|
||||
<Width>1280.000000</Width>
|
||||
<Height>720.000000</Height>
|
||||
</Properties>
|
||||
<XuiScene>
|
||||
<Properties>
|
||||
<Id>XuiScene1</Id>
|
||||
<Width>584.528076</Width>
|
||||
<Height>363.320007</Height>
|
||||
<Position>344.000031,184.000015,0.000000</Position>
|
||||
<ClassOverride>SSNESSettings</ClassOverride>
|
||||
</Properties>
|
||||
<XuiText>
|
||||
<Properties>
|
||||
<Id>XuiTxtTitle</Id>
|
||||
<Width>121.056023</Width>
|
||||
<Height>40.000000</Height>
|
||||
<Position>43.581604,21.159988,0.000000</Position>
|
||||
<Text>Settings</Text>
|
||||
<TextColor>0xff0f0f0f</TextColor>
|
||||
<DropShadowColor>0x800f0f0f</DropShadowColor>
|
||||
<Font>Arial Unicode MS</Font>
|
||||
<TextStyle>21</TextStyle>
|
||||
</Properties>
|
||||
</XuiText>
|
||||
<XuiBackButton>
|
||||
<Properties>
|
||||
<Id>XuiBackButton</Id>
|
||||
<Width>208.632019</Width>
|
||||
<Height>36.000000</Height>
|
||||
<Position>356.670013,309.000000,0.000000</Position>
|
||||
<NavLeft>XuiSettingsList</NavLeft>
|
||||
<NavRight>XuiSettingsList</NavRight>
|
||||
<NavUp>XuiSettingsList</NavUp>
|
||||
<NavDown>XuiSettingsList</NavDown>
|
||||
<Text>Go back to menu</Text>
|
||||
<PressKey>22593</PressKey>
|
||||
</Properties>
|
||||
</XuiBackButton>
|
||||
<XuiCommonList>
|
||||
<Properties>
|
||||
<Id>XuiSettingsList</Id>
|
||||
<Width>492.000000</Width>
|
||||
<Height>232.720001</Height>
|
||||
<Position>44.880005,64.959991,0.000000</Position>
|
||||
<NavLeft>XuiBackButton</NavLeft>
|
||||
<NavRight>XuiBackButton</NavRight>
|
||||
<NavUp>XuiBackButton</NavUp>
|
||||
<NavDown>XuiBackButton</NavDown>
|
||||
<ItemsText>Rewind:
|
||||
Gamma Correction:
|
||||
Hardware filtering:
|
||||
</ItemsText>
|
||||
</Properties>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
<XuiListItem>
|
||||
<Properties>
|
||||
<Id>control_ListItem</Id>
|
||||
<Width>226.000000</Width>
|
||||
<Height>45.000000</Height>
|
||||
<Position>7.000000,22.000000,0.000000</Position>
|
||||
<Anchor>5</Anchor>
|
||||
<Show>false</Show>
|
||||
<Visual>XuiButton</Visual>
|
||||
<InterItemSpacing>0.000000,10.000000,0.000000</InterItemSpacing>
|
||||
</Properties>
|
||||
</XuiListItem>
|
||||
</XuiCommonList>
|
||||
</XuiScene>
|
||||
</XuiCanvas>
|
||||
+558
@@ -0,0 +1,558 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <crtdefs.h>
|
||||
#include <tchar.h>
|
||||
#include <xtl.h>
|
||||
#include "../console/fileio/file_browser.h"
|
||||
#include "../console/console_ext.h"
|
||||
#include "xdk360_video.h"
|
||||
#include "menu.h"
|
||||
#include "shared.h"
|
||||
|
||||
#include "../general.h"
|
||||
|
||||
CSSNES app;
|
||||
filebrowser_t browser;
|
||||
filebrowser_t tmp_browser;
|
||||
char strbuffer[1024];
|
||||
|
||||
static void return_to_game (void)
|
||||
{
|
||||
g_console.frame_advance_enable = false;
|
||||
g_console.menu_enable = false;
|
||||
g_console.mode_switch = MODE_EMULATION;
|
||||
}
|
||||
|
||||
static void return_to_dashboard (void)
|
||||
{
|
||||
g_console.menu_enable = false;
|
||||
g_console.mode_switch = MODE_EXIT;
|
||||
g_console.initialize_ssnes_enable = false;
|
||||
}
|
||||
|
||||
/* Register custom classes */
|
||||
HRESULT CSSNES::RegisterXuiClasses (void)
|
||||
{
|
||||
CSSNESMain::Register();
|
||||
CSSNESFileBrowser::Register();
|
||||
CSSNESCoreBrowser::Register();
|
||||
CSSNESQuickMenu::Register();
|
||||
CSSNESSettings::Register();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Unregister custom classes */
|
||||
HRESULT CSSNES::UnregisterXuiClasses (void)
|
||||
{
|
||||
CSSNESMain::Unregister();
|
||||
CSSNESCoreBrowser::Unregister();
|
||||
CSSNESFileBrowser::Unregister();
|
||||
CSSNESQuickMenu::Register();
|
||||
CSSNESSettings::Unregister();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static void filebrowser_fetch_directory_entries(const char *path, filebrowser_t * browser, CXuiList * romlist,
|
||||
CXuiTextElement * rompath_title)
|
||||
{
|
||||
filebrowser_push_directory(browser, path, true);
|
||||
|
||||
unsigned long dwNum_rompath = MultiByteToWideChar(CP_ACP, 0, path, -1, NULL, 0);
|
||||
wchar_t * rompath_name = new wchar_t[dwNum_rompath];
|
||||
MultiByteToWideChar(CP_ACP, 0, path, -1, rompath_name, dwNum_rompath);
|
||||
rompath_title->SetText(rompath_name);
|
||||
|
||||
romlist->DeleteItems(0, romlist->GetItemCount());
|
||||
romlist->InsertItems(0, browser->file_count);
|
||||
for(unsigned i = 0; i < browser->file_count; i++)
|
||||
{
|
||||
unsigned long dwNum = MultiByteToWideChar(CP_ACP, 0, browser->cur[i].d_name, -1, NULL, 0);
|
||||
wchar_t * entry_name = new wchar_t[dwNum];
|
||||
MultiByteToWideChar(CP_ACP, 0, browser->cur[i].d_name, -1, entry_name, dwNum);
|
||||
romlist->SetText(i, entry_name);
|
||||
delete []entry_name;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT CSSNESFileBrowser::OnInit(XUIMessageInit * pInitData, BOOL& bHandled)
|
||||
{
|
||||
GetChildById(L"XuiRomList", &m_romlist);
|
||||
GetChildById(L"XuiBackButton1", &m_back);
|
||||
GetChildById(L"XuiTxtRomPath", &m_rompathtitle);
|
||||
GetChildById(L"XuiBtnGameDir", &m_dir_game);
|
||||
GetChildById(L"XuiBtnCacheDir", &m_dir_cache);
|
||||
|
||||
filebrowser_fetch_directory_entries(g_console.default_rom_startup_dir, &browser, &m_romlist, &m_rompathtitle);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CSSNESCoreBrowser::OnInit(XUIMessageInit * pInitData, BOOL& bHandled)
|
||||
{
|
||||
GetChildById(L"XuiRomList", &m_romlist);
|
||||
GetChildById(L"XuiBackButton1", &m_back);
|
||||
GetChildById(L"XuiTxtRomPath", &m_rompathtitle);
|
||||
|
||||
filebrowser_new(&tmp_browser, "game:", "xex|XEX");
|
||||
filebrowser_fetch_directory_entries("game:", &tmp_browser, &m_romlist, &m_rompathtitle);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
static const wchar_t * set_filter_element(int index)
|
||||
{
|
||||
switch(index)
|
||||
{
|
||||
case FALSE:
|
||||
return L"Hardware filtering: Point filtering";
|
||||
case TRUE:
|
||||
return L"Hardware filtering: Linear interpolation";
|
||||
}
|
||||
|
||||
return L"";
|
||||
}
|
||||
|
||||
HRESULT CSSNESSettings::OnInit(XUIMessageInit * pInitData, BOOL& bHandled)
|
||||
{
|
||||
GetChildById(L"XuiSettingsList", &m_settingslist);
|
||||
GetChildById(L"XuiBackButton", &m_back);
|
||||
|
||||
m_settingslist.SetText(SETTING_EMU_REWIND_ENABLED, g_settings.rewind_enable ? L"Rewind: ON" : L"Rewind: OFF");
|
||||
m_settingslist.SetText(SETTING_GAMMA_CORRECTION_ENABLED, g_console.gamma_correction_enable ? L"Gamma correction: ON" : L"Gamma correction: OFF");
|
||||
m_settingslist.SetText(SETTING_HARDWARE_FILTERING, set_filter_element(g_settings.video.smooth));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CSSNESQuickMenu::OnInit(XUIMessageInit * pInitData, BOOL& bHandled)
|
||||
{
|
||||
GetChildById(L"XuiQuickMenuList", &m_quickmenulist);
|
||||
GetChildById(L"XuiBackButton", &m_back);
|
||||
|
||||
m_quickmenulist.SetText(MENU_ITEM_HARDWARE_FILTERING, set_filter_element(g_settings.video.smooth));
|
||||
switch(g_console.screen_orientation)
|
||||
{
|
||||
case ORIENTATION_NORMAL:
|
||||
m_quickmenulist.SetText(MENU_ITEM_ORIENTATION, L"Orientation: Normal");
|
||||
break;
|
||||
case ORIENTATION_VERTICAL:
|
||||
m_quickmenulist.SetText(MENU_ITEM_ORIENTATION, L"Orientation: Vertical");
|
||||
break;
|
||||
case ORIENTATION_FLIPPED:
|
||||
m_quickmenulist.SetText(MENU_ITEM_ORIENTATION, L"Orientation: Flipped");
|
||||
break;
|
||||
case ORIENTATION_FLIPPED_ROTATED:
|
||||
m_quickmenulist.SetText(MENU_ITEM_ORIENTATION, L"Orientation: Flipped Rotated");
|
||||
break;
|
||||
}
|
||||
char aspectratio_label[32];
|
||||
sprintf(aspectratio_label, "Aspect Ratio: %s", g_console.aspect_ratio_name);
|
||||
unsigned long dwNum = MultiByteToWideChar(CP_ACP, 0, aspectratio_label, -1, NULL, 0);
|
||||
wchar_t * aspectratio_label_w = new wchar_t[dwNum];
|
||||
MultiByteToWideChar(CP_ACP, 0, aspectratio_label, -1, aspectratio_label_w, dwNum);
|
||||
m_quickmenulist.SetText(MENU_ITEM_KEEP_ASPECT_RATIO, aspectratio_label_w);
|
||||
delete[] aspectratio_label_w;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CSSNESQuickMenu::OnNotifyPress( HXUIOBJ hObjPressed, int & bHandled )
|
||||
{
|
||||
int current_index;
|
||||
|
||||
if ( hObjPressed == m_quickmenulist)
|
||||
{
|
||||
current_index = m_quickmenulist.GetCurSel();
|
||||
|
||||
switch(current_index)
|
||||
{
|
||||
case MENU_ITEM_LOAD_STATE:
|
||||
if (g_console.emulator_initialized)
|
||||
{
|
||||
ssnes_load_state();
|
||||
return_to_game();
|
||||
}
|
||||
break;
|
||||
case MENU_ITEM_SAVE_STATE:
|
||||
if (g_console.emulator_initialized)
|
||||
{
|
||||
ssnes_save_state();
|
||||
return_to_game();
|
||||
}
|
||||
break;
|
||||
case MENU_ITEM_HARDWARE_FILTERING:
|
||||
g_settings.video.smooth = !g_settings.video.smooth;
|
||||
m_quickmenulist.SetText(MENU_ITEM_HARDWARE_FILTERING, set_filter_element(g_settings.video.smooth));
|
||||
break;
|
||||
case MENU_ITEM_KEEP_ASPECT_RATIO:
|
||||
{
|
||||
if(g_console.aspect_ratio_index < LAST_ASPECT_RATIO)
|
||||
g_console.aspect_ratio_index++;
|
||||
else
|
||||
g_console.aspect_ratio_index = 0;
|
||||
|
||||
xdk360_set_aspect_ratio(g_console.aspect_ratio_index);
|
||||
char aspectratio_label[32];
|
||||
sprintf(aspectratio_label, "Aspect Ratio: %s", g_console.aspect_ratio_name);
|
||||
unsigned long dwNum = MultiByteToWideChar(CP_ACP, 0, aspectratio_label, -1, NULL, 0);
|
||||
wchar_t * aspectratio_label_w = new wchar_t[dwNum];
|
||||
MultiByteToWideChar(CP_ACP, 0, aspectratio_label, -1, aspectratio_label_w, dwNum);
|
||||
m_quickmenulist.SetText(MENU_ITEM_KEEP_ASPECT_RATIO, aspectratio_label_w);
|
||||
delete[] aspectratio_label_w;
|
||||
}
|
||||
break;
|
||||
case MENU_ITEM_OVERSCAN_AMOUNT:
|
||||
break;
|
||||
case MENU_ITEM_ORIENTATION:
|
||||
switch(g_console.screen_orientation)
|
||||
{
|
||||
case ORIENTATION_NORMAL:
|
||||
g_console.screen_orientation = ORIENTATION_VERTICAL;
|
||||
m_quickmenulist.SetText(MENU_ITEM_ORIENTATION, L"Orientation: Vertical");
|
||||
break;
|
||||
case ORIENTATION_VERTICAL:
|
||||
g_console.screen_orientation = ORIENTATION_FLIPPED;
|
||||
m_quickmenulist.SetText(MENU_ITEM_ORIENTATION, L"Orientation: Flipped");
|
||||
break;
|
||||
case ORIENTATION_FLIPPED:
|
||||
g_console.screen_orientation = ORIENTATION_FLIPPED_ROTATED;
|
||||
m_quickmenulist.SetText(MENU_ITEM_ORIENTATION, L"Orientation: Flipped Rotated");
|
||||
break;
|
||||
case ORIENTATION_FLIPPED_ROTATED:
|
||||
g_console.screen_orientation = ORIENTATION_NORMAL;
|
||||
m_quickmenulist.SetText(MENU_ITEM_ORIENTATION, L"Orientation: Normal");
|
||||
break;
|
||||
}
|
||||
video_xdk360.set_rotation(NULL, g_console.screen_orientation);
|
||||
break;
|
||||
case MENU_ITEM_RESIZE_MODE:
|
||||
break;
|
||||
case MENU_ITEM_FRAME_ADVANCE:
|
||||
if (g_console.emulator_initialized)
|
||||
{
|
||||
g_console.frame_advance_enable = true;
|
||||
g_console.menu_enable = false;
|
||||
g_console.mode_switch = MODE_EMULATION;
|
||||
}
|
||||
break;
|
||||
case MENU_ITEM_SCREENSHOT_MODE:
|
||||
break;
|
||||
case MENU_ITEM_RESET:
|
||||
if (g_console.emulator_initialized)
|
||||
{
|
||||
return_to_game();
|
||||
ssnes_game_reset();
|
||||
}
|
||||
break;
|
||||
case MENU_ITEM_RETURN_TO_GAME:
|
||||
if (g_console.emulator_initialized)
|
||||
return_to_game();
|
||||
break;
|
||||
case MENU_ITEM_RETURN_TO_DASHBOARD:
|
||||
return_to_dashboard();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( hObjPressed == m_back )
|
||||
NavigateBack(app.hMainScene);
|
||||
|
||||
bHandled = TRUE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CSSNESMain::OnInit(XUIMessageInit * pInitData, BOOL& bHandled)
|
||||
{
|
||||
GetChildById(L"XuiBtnRomBrowser", &m_filebrowser);
|
||||
GetChildById(L"XuiBtnSettings", &m_settings);
|
||||
GetChildById(L"XuiBtnQuickMenu", &m_quick_menu);
|
||||
GetChildById(L"XuiBtnControls", &m_controls);
|
||||
GetChildById(L"XuiBtnQuit", &m_quit);
|
||||
GetChildById(L"XuiTxtTitle", &m_title);
|
||||
GetChildById(L"XuiTxtCoreText", &m_core);
|
||||
GetChildById(L"XuiBtnLibsnesCore", &m_change_libsnes_core);
|
||||
|
||||
const char * core_text = snes_library_id();
|
||||
char package_version[32];
|
||||
sprintf(package_version, "SSNES %s", PACKAGE_VERSION);
|
||||
unsigned long dwNum = MultiByteToWideChar(CP_ACP, 0, core_text, -1, NULL, 0);
|
||||
unsigned long dwNum_package = MultiByteToWideChar(CP_ACP, 0, package_version, -1, NULL, 0);
|
||||
wchar_t * core_text_utf = new wchar_t[dwNum];
|
||||
wchar_t * package_version_utf = new wchar_t[dwNum_package];
|
||||
MultiByteToWideChar(CP_ACP, 0, core_text, -1, core_text_utf, dwNum);
|
||||
MultiByteToWideChar(CP_ACP, 0, package_version, -1, package_version_utf, dwNum_package);
|
||||
m_core.SetText(core_text_utf);
|
||||
m_title.SetText(package_version_utf);
|
||||
delete []core_text_utf;
|
||||
delete []package_version_utf;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CSSNESFileBrowser::OnNotifyPress( HXUIOBJ hObjPressed, BOOL& bHandled )
|
||||
{
|
||||
char path[MAX_PATH_LENGTH];
|
||||
|
||||
if(hObjPressed == m_romlist)
|
||||
{
|
||||
int index = m_romlist.GetCurSel();
|
||||
if(browser.cur[index].d_type != FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
memset(strbuffer, 0, sizeof(strbuffer));
|
||||
wcstombs(strbuffer, (const wchar_t *)m_romlist.GetText(index), sizeof(strbuffer));
|
||||
if((strstr(strbuffer, ".zip") || strstr(strbuffer, ".ZIP")) && !g_console.block_zip_extract)
|
||||
{
|
||||
char path_tmp[1024];
|
||||
sprintf(path_tmp, "%s\\%s", FILEBROWSER_GET_CURRENT_DIRECTORY_NAME(browser), strbuffer);
|
||||
ssnes_extract_zipfile(path_tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(g_console.rom_path, 0, sizeof(g_console.rom_path));
|
||||
sprintf(g_console.rom_path, "%s\\%s", FILEBROWSER_GET_CURRENT_DIRECTORY_NAME(browser), strbuffer);
|
||||
return_to_game();
|
||||
g_console.initialize_ssnes_enable = 1;
|
||||
}
|
||||
}
|
||||
else if(browser.cur[index].d_type == FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
memset(strbuffer, 0, sizeof(strbuffer));
|
||||
wcstombs(strbuffer, (const wchar_t *)m_romlist.GetText(index), sizeof(strbuffer));
|
||||
snprintf(path, sizeof(path), "%s\\%s", FILEBROWSER_GET_CURRENT_DIRECTORY_NAME(browser), strbuffer);
|
||||
filebrowser_fetch_directory_entries(path, &browser, &m_romlist, &m_rompathtitle);
|
||||
}
|
||||
}
|
||||
else if (hObjPressed == m_dir_game)
|
||||
{
|
||||
filebrowser_new(&browser, g_console.default_rom_startup_dir, ssnes_console_get_rom_ext());
|
||||
filebrowser_fetch_directory_entries(g_console.default_rom_startup_dir, &browser, &m_romlist, &m_rompathtitle);
|
||||
}
|
||||
else if (hObjPressed == m_dir_cache)
|
||||
{
|
||||
filebrowser_new(&browser, "cache:", ssnes_console_get_rom_ext());
|
||||
filebrowser_fetch_directory_entries("cache:", &browser, &m_romlist, &m_rompathtitle);
|
||||
}
|
||||
else if(hObjPressed == m_back)
|
||||
NavigateBack(app.hMainScene);
|
||||
|
||||
bHandled = TRUE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CSSNESCoreBrowser::OnNotifyPress( HXUIOBJ hObjPressed, BOOL& bHandled )
|
||||
{
|
||||
char path[MAX_PATH_LENGTH];
|
||||
|
||||
if(hObjPressed == m_romlist)
|
||||
{
|
||||
int index = m_romlist.GetCurSel();
|
||||
if(browser.cur[index].d_type != FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
memset(strbuffer, 0, sizeof(strbuffer));
|
||||
wcstombs(strbuffer, (const wchar_t *)m_romlist.GetText(index), sizeof(strbuffer));
|
||||
sprintf(g_console.launch_app_on_exit, "%s\\%s", FILEBROWSER_GET_CURRENT_DIRECTORY_NAME(tmp_browser), strbuffer);
|
||||
g_console.return_to_launcher = true;
|
||||
g_console.menu_enable = false;
|
||||
g_console.mode_switch = MODE_EXIT;
|
||||
}
|
||||
else if(tmp_browser.cur[index].d_type == FILE_ATTRIBUTE_DIRECTORY)
|
||||
{
|
||||
memset(strbuffer, 0, sizeof(strbuffer));
|
||||
wcstombs(strbuffer, (const wchar_t *)m_romlist.GetText(index), sizeof(strbuffer));
|
||||
snprintf(path, sizeof(path), "%s%s\\", FILEBROWSER_GET_CURRENT_DIRECTORY_NAME(tmp_browser), strbuffer);
|
||||
filebrowser_fetch_directory_entries(path, &tmp_browser, &m_romlist, &m_rompathtitle);
|
||||
}
|
||||
}
|
||||
else if(hObjPressed == m_back)
|
||||
NavigateBack(app.hMainScene);
|
||||
|
||||
bHandled = TRUE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CSSNESSettings::OnNotifyPress( HXUIOBJ hObjPressed, int & bHandled )
|
||||
{
|
||||
int current_index;
|
||||
|
||||
if ( hObjPressed == m_settingslist)
|
||||
{
|
||||
current_index = m_settingslist.GetCurSel();
|
||||
|
||||
switch(current_index)
|
||||
{
|
||||
case SETTING_EMU_REWIND_ENABLED:
|
||||
g_settings.rewind_enable = !g_settings.rewind_enable;
|
||||
m_settingslist.SetText(SETTING_EMU_REWIND_ENABLED, g_settings.rewind_enable ? L"Rewind: ON" : L"Rewind: OFF");
|
||||
break;
|
||||
case SETTING_GAMMA_CORRECTION_ENABLED:
|
||||
g_console.gamma_correction_enable = !g_console.gamma_correction_enable;
|
||||
m_settingslist.SetText(SETTING_GAMMA_CORRECTION_ENABLED, g_console.gamma_correction_enable ? L"Gamma correction: ON" : L"Gamma correction: OFF");
|
||||
break;
|
||||
case SETTING_HARDWARE_FILTERING:
|
||||
g_settings.video.smooth = !g_settings.video.smooth;
|
||||
m_settingslist.SetText(SETTING_HARDWARE_FILTERING, set_filter_element(g_settings.video.smooth));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( hObjPressed == m_back )
|
||||
NavigateBack(app.hMainScene);
|
||||
|
||||
bHandled = TRUE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CSSNESMain::OnNotifyPress( HXUIOBJ hObjPressed, int & bHandled )
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
if ( hObjPressed == m_filebrowser )
|
||||
{
|
||||
hr = XuiSceneCreate(L"file://game:/media/", L"ssnes_filebrowser.xur", NULL, &app.hFileBrowser);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
SSNES_ERR("Failed to load scene.\n");
|
||||
}
|
||||
|
||||
NavigateForward(app.hFileBrowser);
|
||||
}
|
||||
else if ( hObjPressed == m_quick_menu)
|
||||
{
|
||||
hr = XuiSceneCreate(L"file://game:/media/", L"ssnes_quickmenu.xur", NULL, &app.hQuickMenu);
|
||||
|
||||
if (FAILED(hr))
|
||||
SSNES_ERR("Failed to load scene.\n");
|
||||
|
||||
NavigateForward(app.hQuickMenu);
|
||||
}
|
||||
else if ( hObjPressed == m_change_libsnes_core )
|
||||
{
|
||||
hr = XuiSceneCreate(L"file://game:/media/", L"ssnes_libsnescore_browser.xur", NULL, &app.hCoreBrowser);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
SSNES_ERR("Failed to load scene.\n");
|
||||
}
|
||||
NavigateForward(app.hCoreBrowser);
|
||||
}
|
||||
else if ( hObjPressed == m_settings )
|
||||
{
|
||||
hr = XuiSceneCreate(L"file://game:/media/", L"ssnes_settings.xur", NULL, &app.hSSNESSettings);
|
||||
|
||||
if (FAILED(hr))
|
||||
SSNES_ERR("Failed to load scene.\n");
|
||||
|
||||
NavigateForward(app.hSSNESSettings);
|
||||
}
|
||||
else if ( hObjPressed == m_quit )
|
||||
return_to_dashboard();
|
||||
|
||||
bHandled = TRUE;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
int menu_init (void)
|
||||
{
|
||||
HRESULT hr;
|
||||
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
|
||||
hr = app.InitShared(vid->xdk360_render_device, &vid->d3dpp, XuiPNGTextureLoader);
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
SSNES_ERR("Failed initializing XUI application.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Register font */
|
||||
hr = app.RegisterDefaultTypeface(L"Arial Unicode MS", L"file://game:/media/ssnes.ttf" );
|
||||
if (FAILED(hr))
|
||||
{
|
||||
SSNES_ERR("Failed to register default typeface.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
hr = app.LoadSkin( L"file://game:/media/ssnes_scene_skin.xur");
|
||||
if (FAILED(hr))
|
||||
{
|
||||
SSNES_ERR("Failed to load skin.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
hr = XuiSceneCreate(L"file://game:/media/", L"ssnes_main.xur", NULL, &app.hMainScene);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
SSNES_ERR("Failed to create scene 'ssnes_main.xur'.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
XuiSceneNavigateFirst(app.GetRootObj(), app.hMainScene, XUSER_INDEX_FOCUS);
|
||||
|
||||
filebrowser_new(&browser, g_console.default_rom_startup_dir, ssnes_console_get_rom_ext());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void menu_loop(void)
|
||||
{
|
||||
g_console.menu_enable = true;
|
||||
|
||||
HRESULT hr;
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
|
||||
if(g_console.emulator_initialized)
|
||||
video_xdk360.set_swap_block_state(NULL, true);
|
||||
|
||||
do
|
||||
{
|
||||
g_frame_count++;
|
||||
if(g_console.emulator_initialized)
|
||||
{
|
||||
ssnes_render_cached_frame();
|
||||
}
|
||||
else
|
||||
D3DDevice_Clear(vid->xdk360_render_device, 0, NULL,
|
||||
D3DCLEAR_TARGET, D3DCOLOR_ARGB(255, 32, 32, 64), 1.0, 0, FALSE);
|
||||
|
||||
XINPUT_STATE state;
|
||||
XInputGetState(0, &state);
|
||||
|
||||
g_console.menu_enable = !((state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB)
|
||||
&& (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB) && (g_console.emulator_initialized)
|
||||
&& IS_TIMER_EXPIRED());
|
||||
g_console.mode_switch = g_console.menu_enable ? MODE_MENU : MODE_EMULATION;
|
||||
|
||||
app.RunFrame(); /* Update XUI */
|
||||
hr = app.Render(); /* Render XUI */
|
||||
hr = XuiTimersRun(); /* Update XUI timers */
|
||||
|
||||
if(g_console.mode_switch == MODE_EMULATION && !g_console.frame_advance_enable)
|
||||
{
|
||||
SET_TIMER_EXPIRATION(30);
|
||||
}
|
||||
|
||||
video_xdk360.swap(NULL);
|
||||
}while(g_console.menu_enable);
|
||||
|
||||
if(g_console.emulator_initialized)
|
||||
video_xdk360.set_swap_block_state(NULL, false);
|
||||
|
||||
g_console.ingame_menu_enable = false;
|
||||
}
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _MENU_XUI_H_
|
||||
#define _MENU_XUI_H_
|
||||
|
||||
#include <xui.h>
|
||||
#include <xuiapp.h>
|
||||
|
||||
enum
|
||||
{
|
||||
SETTING_EMU_REWIND_ENABLED = 0,
|
||||
SETTING_GAMMA_CORRECTION_ENABLED,
|
||||
SETTING_HARDWARE_FILTERING
|
||||
};
|
||||
|
||||
class CSSNES : public CXuiModule
|
||||
{
|
||||
public:
|
||||
HXUIOBJ hMainScene;
|
||||
HXUIOBJ hFileBrowser;
|
||||
HXUIOBJ hCoreBrowser;
|
||||
HXUIOBJ hQuickMenu;
|
||||
HXUIOBJ hSSNESSettings;
|
||||
protected:
|
||||
/* Override so that Cssnes can register classes */
|
||||
virtual HRESULT RegisterXuiClasses();
|
||||
/* Override so that Cssnes can unregister classes */
|
||||
virtual HRESULT UnregisterXuiClasses();
|
||||
};
|
||||
|
||||
class CSSNESMain: public CXuiSceneImpl
|
||||
{
|
||||
protected:
|
||||
CXuiControl m_filebrowser;
|
||||
CXuiControl m_quick_menu;
|
||||
CXuiControl m_controls;
|
||||
CXuiControl m_settings;
|
||||
CXuiControl m_change_libsnes_core;
|
||||
CXuiControl m_quit;
|
||||
CXuiTextElement m_title;
|
||||
CXuiTextElement m_core;
|
||||
public:
|
||||
HRESULT OnInit( XUIMessageInit* pInitData, int & bHandled );
|
||||
HRESULT OnNotifyPress( HXUIOBJ hObjPressed, int & bHandled );
|
||||
|
||||
XUI_BEGIN_MSG_MAP()
|
||||
XUI_ON_XM_INIT( OnInit)
|
||||
XUI_ON_XM_NOTIFY_PRESS( OnNotifyPress )
|
||||
XUI_END_MSG_MAP();
|
||||
|
||||
XUI_IMPLEMENT_CLASS(CSSNESMain, L"SSNESMain", XUI_CLASS_SCENE)
|
||||
};
|
||||
|
||||
class CSSNESFileBrowser: public CXuiSceneImpl
|
||||
{
|
||||
protected:
|
||||
CXuiList m_romlist;
|
||||
CXuiControl m_back;
|
||||
CXuiControl m_dir_game;
|
||||
CXuiControl m_dir_cache;
|
||||
CXuiTextElement m_rompathtitle;
|
||||
public:
|
||||
HRESULT OnInit( XUIMessageInit* pInitData, int & bHandled );
|
||||
HRESULT OnNotifyPress( HXUIOBJ hObjPressed, int & bHandled );
|
||||
|
||||
XUI_BEGIN_MSG_MAP()
|
||||
XUI_ON_XM_INIT( OnInit)
|
||||
XUI_ON_XM_NOTIFY_PRESS( OnNotifyPress )
|
||||
XUI_END_MSG_MAP();
|
||||
|
||||
XUI_IMPLEMENT_CLASS(CSSNESFileBrowser, L"SSNESFileBrowser", XUI_CLASS_SCENE)
|
||||
};
|
||||
|
||||
class CSSNESCoreBrowser: public CXuiSceneImpl
|
||||
{
|
||||
protected:
|
||||
CXuiList m_romlist;
|
||||
CXuiControl m_back;
|
||||
CXuiTextElement m_rompathtitle;
|
||||
public:
|
||||
HRESULT OnInit( XUIMessageInit* pInitData, int & bHandled );
|
||||
HRESULT OnNotifyPress( HXUIOBJ hObjPressed, int & bHandled );
|
||||
|
||||
XUI_BEGIN_MSG_MAP()
|
||||
XUI_ON_XM_INIT( OnInit)
|
||||
XUI_ON_XM_NOTIFY_PRESS( OnNotifyPress )
|
||||
XUI_END_MSG_MAP();
|
||||
|
||||
XUI_IMPLEMENT_CLASS(CSSNESCoreBrowser, L"SSNESCoreBrowser", XUI_CLASS_SCENE)
|
||||
};
|
||||
|
||||
class CSSNESQuickMenu: public CXuiSceneImpl
|
||||
{
|
||||
protected:
|
||||
CXuiList m_quickmenulist;
|
||||
CXuiControl m_back;
|
||||
public:
|
||||
HRESULT OnInit( XUIMessageInit* pInitData, int & bHandled );
|
||||
HRESULT OnNotifyPress( HXUIOBJ hObjPressed, int & bHandled );
|
||||
|
||||
XUI_BEGIN_MSG_MAP()
|
||||
XUI_ON_XM_INIT( OnInit)
|
||||
XUI_ON_XM_NOTIFY_PRESS( OnNotifyPress )
|
||||
XUI_END_MSG_MAP();
|
||||
|
||||
XUI_IMPLEMENT_CLASS(CSSNESQuickMenu, L"SSNESQuickMenu", XUI_CLASS_SCENE)
|
||||
};
|
||||
|
||||
class CSSNESSettings: public CXuiSceneImpl
|
||||
{
|
||||
protected:
|
||||
CXuiList m_settingslist;
|
||||
CXuiControl m_back;
|
||||
public:
|
||||
HRESULT OnInit( XUIMessageInit* pInitData, int & bHandled );
|
||||
HRESULT OnNotifyPress( HXUIOBJ hObjPressed, int & bHandled );
|
||||
|
||||
XUI_BEGIN_MSG_MAP()
|
||||
XUI_ON_XM_INIT( OnInit)
|
||||
XUI_ON_XM_NOTIFY_PRESS( OnNotifyPress )
|
||||
XUI_END_MSG_MAP();
|
||||
|
||||
XUI_IMPLEMENT_CLASS(CSSNESSettings, L"SSNESSettings", XUI_CLASS_SCENE)
|
||||
};
|
||||
|
||||
int menu_init (void);
|
||||
void menu_loop (void);
|
||||
|
||||
extern CSSNES app;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,323 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* SSNES Salamander - A frontend for managing some pre-launch tasks.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <xtl.h>
|
||||
|
||||
#include "../../compat/strl.h"
|
||||
#include "../../conf/config_file.h"
|
||||
#include "../../msvc/msvc_compat.h"
|
||||
|
||||
#define MAX_PATH_LENGTH 1024
|
||||
|
||||
#define SSNES_LOG(...) do { \
|
||||
fprintf(stderr, "SSNES Salamander: " __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
|
||||
#define SSNES_ERR(...) do { \
|
||||
fprintf(stderr, "SSNES Salamander [ERROR] :: " __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
|
||||
#define SSNES_WARN(...) do { \
|
||||
fprintf(stderr, "SSNES Salamander [WARN] :: " __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
|
||||
char LIBSNES_DIR_PATH[MAX_PATH_LENGTH];
|
||||
char SYS_CONFIG_FILE[MAX_PATH_LENGTH];
|
||||
char libsnes_path[MAX_PATH_LENGTH];
|
||||
DWORD volume_device_type;
|
||||
|
||||
static bool path_file_exists(const char *path)
|
||||
{
|
||||
FILE *dummy = fopen(path, "rb");
|
||||
if (dummy)
|
||||
{
|
||||
fclose(dummy);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void dir_list_free(char **dir_list)
|
||||
{
|
||||
if (!dir_list)
|
||||
return;
|
||||
|
||||
char **orig = dir_list;
|
||||
while (*dir_list)
|
||||
free(*dir_list++);
|
||||
free(orig);
|
||||
}
|
||||
|
||||
static void fill_pathname_base(char *out_dir, const char *in_path, size_t size)
|
||||
{
|
||||
const char *ptr = strrchr(in_path, '/');
|
||||
if (!ptr)
|
||||
ptr = strrchr(in_path, '\\');
|
||||
|
||||
if (ptr)
|
||||
ptr++;
|
||||
else
|
||||
ptr = in_path;
|
||||
|
||||
strlcpy(out_dir, ptr, size);
|
||||
}
|
||||
|
||||
static char **dir_list_new(const char *dir, const char *ext)
|
||||
{
|
||||
size_t cur_ptr = 0;
|
||||
size_t cur_size = 32;
|
||||
char **dir_list = NULL;
|
||||
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
|
||||
char path_buf[PATH_MAX];
|
||||
|
||||
if (strlcpy(path_buf, dir, sizeof(path_buf)) >= sizeof(path_buf))
|
||||
goto error;
|
||||
if (strlcat(path_buf, "*", sizeof(path_buf)) >= sizeof(path_buf))
|
||||
goto error;
|
||||
|
||||
if (ext)
|
||||
{
|
||||
if (strlcat(path_buf, ext, sizeof(path_buf)) >= sizeof(path_buf))
|
||||
goto error;
|
||||
}
|
||||
|
||||
hFind = FindFirstFile(path_buf, &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
goto error;
|
||||
|
||||
dir_list = (char**)calloc(cur_size, sizeof(char*));
|
||||
if (!dir_list)
|
||||
goto error;
|
||||
|
||||
do
|
||||
{
|
||||
// Not a perfect search of course, but hopefully good enough in practice.
|
||||
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
continue;
|
||||
if (ext && !strstr(ffd.cFileName, ext))
|
||||
continue;
|
||||
|
||||
dir_list[cur_ptr] = (char*)malloc(PATH_MAX);
|
||||
if (!dir_list[cur_ptr])
|
||||
goto error;
|
||||
|
||||
strlcpy(dir_list[cur_ptr], dir, PATH_MAX);
|
||||
strlcat(dir_list[cur_ptr], ffd.cFileName, PATH_MAX);
|
||||
|
||||
cur_ptr++;
|
||||
if (cur_ptr + 1 == cur_size) // Need to reserve for NULL.
|
||||
{
|
||||
cur_size *= 2;
|
||||
dir_list = (char**)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*));
|
||||
}
|
||||
}while (FindNextFile(hFind, &ffd) != 0);
|
||||
|
||||
FindClose(hFind);
|
||||
return dir_list;
|
||||
|
||||
error:
|
||||
SSNES_ERR("Failed to open directory: \"%s\"\n", dir);
|
||||
if (hFind != INVALID_HANDLE_VALUE)
|
||||
FindClose(hFind);
|
||||
dir_list_free(dir_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void find_and_set_first_file(void)
|
||||
{
|
||||
//Last fallback - we'll need to start the first .xex file
|
||||
// we can find in the SSNES cores directory
|
||||
char ** dir_list = dir_list_new("game:\\", ".xex");
|
||||
if (!dir_list)
|
||||
{
|
||||
SSNES_ERR("Failed last fallback - SSNES Salamander will exit.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char * first_xex = dir_list[0];
|
||||
|
||||
if(first_xex)
|
||||
{
|
||||
//Check if it's SSNES Salamander itself - if so, first_xex needs to
|
||||
//be overridden
|
||||
char fname_tmp[MAX_PATH_LENGTH], fname[MAX_PATH_LENGTH];
|
||||
|
||||
fill_pathname_base(fname_tmp, first_xex, sizeof(fname_tmp));
|
||||
|
||||
if(strcmp(fname_tmp, "SSNES-Salamander.xex") == 0)
|
||||
{
|
||||
SSNES_WARN("First entry is SSNES Salamander itself, increment entry by one and check if it exists.\n");
|
||||
first_xex = dir_list[1];
|
||||
fill_pathname_base(fname_tmp, first_xex, sizeof(fname_tmp));
|
||||
|
||||
if(!first_xex)
|
||||
{
|
||||
SSNES_WARN("There is no second entry - no choice but to boot SSNES Salamander\n");
|
||||
first_xex = dir_list[0];
|
||||
fill_pathname_base(fname_tmp, first_xex, sizeof(fname_tmp));
|
||||
}
|
||||
}
|
||||
|
||||
SSNES_LOG("Start first entry in libsnes cores dir: [%s].\n", first_xex);
|
||||
|
||||
snprintf(fname, sizeof(fname), "game:\\%s", fname_tmp);
|
||||
strlcpy(libsnes_path, fname, sizeof(libsnes_path));
|
||||
}
|
||||
else
|
||||
{
|
||||
SSNES_ERR("Failed last fallback - SSNES Salamander will exit.\n");
|
||||
}
|
||||
|
||||
dir_list_free(dir_list);
|
||||
}
|
||||
|
||||
static void init_settings(void)
|
||||
{
|
||||
char tmp_str[MAX_PATH_LENGTH];
|
||||
bool config_file_exists;
|
||||
|
||||
|
||||
if(!path_file_exists(SYS_CONFIG_FILE))
|
||||
{
|
||||
config_file_exists = false;
|
||||
SSNES_ERR("Config file \"%s\" doesn't exist. Creating...\n", SYS_CONFIG_FILE);
|
||||
FILE * f;
|
||||
f = fopen(SYS_CONFIG_FILE, "w");
|
||||
fclose(f);
|
||||
}
|
||||
else
|
||||
config_file_exists = true;
|
||||
|
||||
|
||||
//try to find CORE.xex
|
||||
char core_xex[1024];
|
||||
snprintf(core_xex, sizeof(core_xex), "game:\\CORE.xex");
|
||||
|
||||
if(path_file_exists(core_xex))
|
||||
{
|
||||
//Start CORE.xex
|
||||
snprintf(libsnes_path, sizeof(libsnes_path), core_xex);
|
||||
SSNES_LOG("Start [%s].\n", libsnes_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(config_file_exists)
|
||||
{
|
||||
config_file_t * conf = config_file_new(SYS_CONFIG_FILE);
|
||||
config_get_array(conf, "libsnes_path", tmp_str, sizeof(tmp_str));
|
||||
snprintf(libsnes_path, sizeof(libsnes_path), tmp_str);
|
||||
}
|
||||
|
||||
if(!config_file_exists || !strcmp(libsnes_path, ""))
|
||||
find_and_set_first_file();
|
||||
else
|
||||
{
|
||||
SSNES_LOG("Start [%s] found in ssnes.cfg.\n", libsnes_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void get_environment_settings (void)
|
||||
{
|
||||
//for devkits only, we will need to mount all partitions for retail
|
||||
//in a different way
|
||||
//DmMapDevkitDrive();
|
||||
|
||||
int result_filecache = XSetFileCacheSize(0x100000);
|
||||
|
||||
if(result_filecache != TRUE)
|
||||
{
|
||||
SSNES_ERR("Couldn't change number of bytes reserved for file system cache.\n");
|
||||
}
|
||||
unsigned long result = XMountUtilityDriveEx(XMOUNTUTILITYDRIVE_FORMAT0,8192, 0);
|
||||
|
||||
if(result != ERROR_SUCCESS)
|
||||
{
|
||||
SSNES_ERR("Couldn't mount/format utility drive.\n");
|
||||
}
|
||||
|
||||
// detect install environment
|
||||
unsigned long license_mask;
|
||||
|
||||
if (XContentGetLicenseMask(&license_mask, NULL) != ERROR_SUCCESS)
|
||||
{
|
||||
SSNES_LOG("SSNES was launched as a standalone DVD, or using DVD emulation, or from the development area of the HDD.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
XContentQueryVolumeDeviceType("GAME",&volume_device_type, NULL);
|
||||
|
||||
switch(volume_device_type)
|
||||
{
|
||||
case XCONTENTDEVICETYPE_HDD:
|
||||
SSNES_LOG("SSNES was launched from a content package on HDD.\n");
|
||||
break;
|
||||
case XCONTENTDEVICETYPE_MU:
|
||||
SSNES_LOG("SSNES was launched from a content package on USB or Memory Unit.\n");
|
||||
break;
|
||||
case XCONTENTDEVICETYPE_ODD:
|
||||
SSNES_LOG("SSNES was launched from a content package on Optical Disc Drive.\n");
|
||||
break;
|
||||
default:
|
||||
SSNES_LOG("SSNES was launched from a content package on an unknown device type.\n");
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
strlcpy(SYS_CONFIG_FILE, "game:\\ssnes.cfg", sizeof(SYS_CONFIG_FILE));
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
XINPUT_STATE state;
|
||||
|
||||
get_environment_settings();
|
||||
|
||||
XInputGetState(0, &state);
|
||||
|
||||
if(state.Gamepad.wButtons & XINPUT_GAMEPAD_Y)
|
||||
{
|
||||
//override path, boot first XEX in cores directory
|
||||
SSNES_LOG("Fallback - Will boot first XEX in SSNES directory.\n");
|
||||
find_and_set_first_file();
|
||||
}
|
||||
else
|
||||
{
|
||||
//normal XEX loading path
|
||||
init_settings();
|
||||
}
|
||||
|
||||
XLaunchNewImage(libsnes_path, NULL);
|
||||
SSNES_LOG("Launch libsnes core: [%s] (return code: %x]).\n", libsnes_path, ret);
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _360_SHARED_H
|
||||
#define _360_SHARED_H
|
||||
|
||||
#define MAX_PATH_LENGTH 1024
|
||||
|
||||
enum
|
||||
{
|
||||
DPAD_EMULATION_NONE,
|
||||
DPAD_EMULATION_LSTICK,
|
||||
DPAD_EMULATION_RSTICK
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
MODE_EMULATION,
|
||||
MODE_MENU,
|
||||
MODE_EXIT
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
ORIENTATION_NORMAL,
|
||||
ORIENTATION_VERTICAL,
|
||||
ORIENTATION_FLIPPED,
|
||||
ORIENTATION_FLIPPED_ROTATED,
|
||||
ORIENTATION_END
|
||||
};
|
||||
|
||||
enum {
|
||||
MENU_ITEM_LOAD_STATE = 0,
|
||||
MENU_ITEM_SAVE_STATE,
|
||||
MENU_ITEM_HARDWARE_FILTERING,
|
||||
MENU_ITEM_KEEP_ASPECT_RATIO,
|
||||
MENU_ITEM_OVERSCAN_AMOUNT,
|
||||
MENU_ITEM_ORIENTATION,
|
||||
MENU_ITEM_RESIZE_MODE,
|
||||
MENU_ITEM_FRAME_ADVANCE,
|
||||
MENU_ITEM_SCREENSHOT_MODE,
|
||||
MENU_ITEM_RESET,
|
||||
MENU_ITEM_RETURN_TO_GAME,
|
||||
MENU_ITEM_RETURN_TO_DASHBOARD
|
||||
};
|
||||
|
||||
#define MENU_ITEM_LAST MENU_ITEM_RETURN_TO_DASHBOARD+1
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,767 @@
|
||||
#if !defined( _XOBJBASE_H_ )
|
||||
#define _XOBJBASE_H_
|
||||
|
||||
#include <sal.h>
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#define __RPC_FAR
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned char boolean;
|
||||
|
||||
typedef double DOUBLE;
|
||||
|
||||
#ifdef CONST_VTABLE
|
||||
#define CONST_VTBL const
|
||||
#else
|
||||
#define CONST_VTBL
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Special things for VC5 Com support
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef DECLSPEC_SELECTANY
|
||||
#if (_MSC_VER >= 1100)
|
||||
#define DECLSPEC_SELECTANY __declspec(selectany)
|
||||
#else
|
||||
#define DECLSPEC_SELECTANY
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef DECLSPEC_NOVTABLE
|
||||
#define DECLSPEC_NOVTABLE
|
||||
#endif
|
||||
|
||||
#ifndef DECLSPEC_UUID
|
||||
#define DECLSPEC_UUID(x)
|
||||
#endif
|
||||
|
||||
#define MIDL_INTERFACE(x) struct DECLSPEC_UUID(x) DECLSPEC_NOVTABLE
|
||||
|
||||
#if _MSC_VER >= 1100
|
||||
#define EXTERN_GUID(itf,l1,s1,s2,c1,c2,c3,c4,c5,c6,c7,c8) \
|
||||
EXTERN_C const IID DECLSPEC_SELECTANY itf = {l1,s1,s2,{c1,c2,c3,c4,c5,c6,c7,c8}}
|
||||
#else
|
||||
#define EXTERN_GUID(itf,l1,s1,s2,c1,c2,c3,c4,c5,c6,c7,c8) EXTERN_C const IID itf
|
||||
#endif
|
||||
|
||||
#include <pshpack8.h>
|
||||
|
||||
#define WINOLEAPI EXTERN_C DECLSPEC_IMPORT HRESULT STDAPICALLTYPE
|
||||
#define WINOLEAPI_(type) EXTERN_C DECLSPEC_IMPORT type STDAPICALLTYPE
|
||||
|
||||
/****** Interface Declaration ***********************************************/
|
||||
|
||||
/*
|
||||
* These are macros for declaring interfaces. They exist so that
|
||||
* a single definition of the interface is simulataneously a proper
|
||||
* declaration of the interface structures (C++ abstract classes)
|
||||
* for both C and C++.
|
||||
*
|
||||
* DECLARE_INTERFACE(iface) is used to declare an interface that does
|
||||
* not derive from a base interface.
|
||||
* DECLARE_INTERFACE_(iface, baseiface) is used to declare an interface
|
||||
* that does derive from a base interface.
|
||||
*
|
||||
* By default if the source file has a .c extension the C version of
|
||||
* the interface declaratations will be expanded; if it has a .cpp
|
||||
* extension the C++ version will be expanded. if you want to force
|
||||
* the C version expansion even though the source file has a .cpp
|
||||
* extension, then define the macro "CINTERFACE".
|
||||
* eg. cl -DCINTERFACE file.cpp
|
||||
*
|
||||
*
|
||||
* Example C expansion:
|
||||
*
|
||||
* typedef struct IClassFactory
|
||||
* {
|
||||
* const struct IClassFactoryVtbl FAR* lpVtbl;
|
||||
* } IClassFactory;
|
||||
*
|
||||
* typedef struct IClassFactoryVtbl IClassFactoryVtbl;
|
||||
*
|
||||
* struct IClassFactoryVtbl
|
||||
* {
|
||||
* HRESULT (STDMETHODCALLTYPE * QueryInterface) (
|
||||
* IClassFactory FAR* This,
|
||||
* IID FAR* riid,
|
||||
* LPVOID FAR* ppvObj) ;
|
||||
* ULONG (STDMETHODCALLTYPE * AddRef) (IClassFactory FAR* This) ;
|
||||
* ULONG (STDMETHODCALLTYPE * Release) (IClassFactory FAR* This) ;
|
||||
* HRESULT (STDMETHODCALLTYPE * CreateInstance) (
|
||||
* IClassFactory FAR* This,
|
||||
* LPUNKNOWN pUnkOuter,
|
||||
* IID FAR* riid,
|
||||
* LPVOID FAR* ppvObject);
|
||||
* HRESULT (STDMETHODCALLTYPE * LockServer) (
|
||||
* IClassFactory FAR* This,
|
||||
* BOOL fLock);
|
||||
* };
|
||||
*/
|
||||
|
||||
#define interface struct
|
||||
|
||||
#define STDMETHOD(method) HRESULT (STDMETHODCALLTYPE * method)
|
||||
#define STDMETHOD_(type,method) type (STDMETHODCALLTYPE * method)
|
||||
#define STDMETHODV(method) HRESULT (STDMETHODVCALLTYPE * method)
|
||||
#define STDMETHODV_(type,method) type (STDMETHODVCALLTYPE * method)
|
||||
|
||||
#if !defined(BEGIN_INTERFACE)
|
||||
#define BEGIN_INTERFACE
|
||||
#define END_INTERFACE
|
||||
#endif
|
||||
|
||||
|
||||
#define PURE
|
||||
#define THIS_ INTERFACE FAR* This,
|
||||
#define THIS INTERFACE FAR* This
|
||||
#ifdef CONST_VTABLE
|
||||
#undef CONST_VTBL
|
||||
#define CONST_VTBL const
|
||||
#define DECLARE_INTERFACE(iface) typedef interface iface { \
|
||||
const struct iface##Vtbl FAR* lpVtbl; \
|
||||
} iface; \
|
||||
typedef const struct iface##Vtbl iface##Vtbl; \
|
||||
const struct iface##Vtbl
|
||||
#else
|
||||
#undef CONST_VTBL
|
||||
#define CONST_VTBL
|
||||
#define DECLARE_INTERFACE(iface) typedef interface iface { \
|
||||
struct iface##Vtbl FAR* lpVtbl; \
|
||||
} iface; \
|
||||
typedef struct iface##Vtbl iface##Vtbl; \
|
||||
struct iface##Vtbl
|
||||
#endif
|
||||
#define DECLARE_INTERFACE_(iface, baseiface) DECLARE_INTERFACE(iface)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/****** Additional basic types **********************************************/
|
||||
|
||||
|
||||
#ifndef FARSTRUCT
|
||||
#define FARSTRUCT
|
||||
#endif // FARSTRUCT
|
||||
|
||||
|
||||
|
||||
#ifndef HUGEP
|
||||
#if defined(_WIN32) || defined(_MPPC_)
|
||||
#define HUGEP
|
||||
#else
|
||||
#define HUGEP __huge
|
||||
#endif // WIN32
|
||||
#endif // HUGEP
|
||||
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#define LISet32(li, v) ((li).HighPart = (v) < 0 ? -1 : 0, (li).LowPart = (v))
|
||||
|
||||
#define ULISet32(li, v) ((li).HighPart = 0, (li).LowPart = (v))
|
||||
|
||||
|
||||
#if defined(_WIN32) && !defined(OLE2ANSI)
|
||||
typedef WCHAR OLECHAR;
|
||||
|
||||
typedef /* [string] */ OLECHAR __RPC_FAR *LPOLESTR;
|
||||
|
||||
typedef /* [string] */ const OLECHAR __RPC_FAR *LPCOLESTR;
|
||||
|
||||
#define OLESTR(str) L##str
|
||||
|
||||
#else
|
||||
|
||||
typedef char OLECHAR;
|
||||
typedef LPSTR LPOLESTR;
|
||||
typedef LPCSTR LPCOLESTR;
|
||||
#define OLESTR(str) str
|
||||
#endif
|
||||
|
||||
|
||||
/* Forward Declarations */
|
||||
|
||||
#ifndef __IUnknown_FWD_DEFINED__
|
||||
#define __IUnknown_FWD_DEFINED__
|
||||
typedef interface IUnknown IUnknown;
|
||||
#endif /* __IUnknown_FWD_DEFINED__ */
|
||||
|
||||
|
||||
#ifndef __IUnknown_INTERFACE_DEFINED__
|
||||
#define __IUnknown_INTERFACE_DEFINED__
|
||||
|
||||
|
||||
/* interface IUnknown */
|
||||
/* [unique][uuid][object][local] */
|
||||
|
||||
typedef /* [unique] */ IUnknown __RPC_FAR *LPUNKNOWN;
|
||||
typedef /* [unique] */ IUnknown __RPC_FAR *PUNKNOWN;
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// IID_IUnknown and all other system IIDs are provided in UUID.LIB
|
||||
// Link that library in with your proxies, clients and servers
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
EXTERN_C const IID IID_IUnknown;
|
||||
|
||||
typedef struct IUnknownVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
|
||||
IUnknown __RPC_FAR * This,
|
||||
/* [in] */ __in REFIID riid,
|
||||
/* [iid_is][out] */ __deref_out void __RPC_FAR *__RPC_FAR *ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
|
||||
IUnknown __RPC_FAR * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
|
||||
IUnknown __RPC_FAR * This);
|
||||
|
||||
END_INTERFACE
|
||||
} IUnknownVtbl;
|
||||
|
||||
interface IUnknown
|
||||
{
|
||||
CONST_VTBL struct IUnknownVtbl __RPC_FAR *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
#define IUnknown_QueryInterface(This,riid,ppvObject) \
|
||||
(This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
|
||||
|
||||
#define IUnknown_AddRef(This) \
|
||||
(This)->lpVtbl -> AddRef(This)
|
||||
|
||||
#define IUnknown_Release(This) \
|
||||
(This)->lpVtbl -> Release(This)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* interface __MIDL_itf_unknwn_0005 */
|
||||
/* [local] */
|
||||
|
||||
#endif // VC6 hack
|
||||
|
||||
|
||||
#ifndef __ISequentialStream_FWD_DEFINED__
|
||||
#define __ISequentialStream_FWD_DEFINED__
|
||||
typedef interface ISequentialStream ISequentialStream;
|
||||
#endif /* __ISequentialStream_FWD_DEFINED__ */
|
||||
|
||||
|
||||
#ifndef __ISequentialStream_INTERFACE_DEFINED__
|
||||
#define __ISequentialStream_INTERFACE_DEFINED__
|
||||
|
||||
/* interface ISequentialStream */
|
||||
/* [unique][uuid][object] */
|
||||
|
||||
|
||||
EXTERN_C const IID IID_ISequentialStream;
|
||||
|
||||
typedef struct ISequentialStreamVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
|
||||
ISequentialStream __RPC_FAR * This,
|
||||
/* [in] */ __in REFIID riid,
|
||||
/* [iid_is][out] */ __deref_out void __RPC_FAR *__RPC_FAR *ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
|
||||
ISequentialStream __RPC_FAR * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
|
||||
ISequentialStream __RPC_FAR * This);
|
||||
|
||||
/* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Read )(
|
||||
ISequentialStream __RPC_FAR * This,
|
||||
/* [length_is][size_is][out] */ __out_bcount_part_opt(cb, *pcbRead) void __RPC_FAR *pv,
|
||||
/* [in] */ __in ULONG cb,
|
||||
/* [out] */ __out_opt ULONG __RPC_FAR *pcbRead);
|
||||
|
||||
/* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Write )(
|
||||
ISequentialStream __RPC_FAR * This,
|
||||
/* [size_is][in] */ __in_bcount_opt(cb) const void __RPC_FAR *pv,
|
||||
/* [in] */ __in ULONG cb,
|
||||
/* [out] */ __out_opt ULONG __RPC_FAR *pcbWritten);
|
||||
|
||||
END_INTERFACE
|
||||
} ISequentialStreamVtbl;
|
||||
|
||||
interface ISequentialStream
|
||||
{
|
||||
CONST_VTBL struct ISequentialStreamVtbl __RPC_FAR *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
#define ISequentialStream_QueryInterface(This,riid,ppvObject) \
|
||||
(This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
|
||||
|
||||
#define ISequentialStream_AddRef(This) \
|
||||
(This)->lpVtbl -> AddRef(This)
|
||||
|
||||
#define ISequentialStream_Release(This) \
|
||||
(This)->lpVtbl -> Release(This)
|
||||
|
||||
|
||||
#define ISequentialStream_Read(This,pv,cb,pcbRead) \
|
||||
(This)->lpVtbl -> Read(This,pv,cb,pcbRead)
|
||||
|
||||
#define ISequentialStream_Write(This,pv,cb,pcbWritten) \
|
||||
(This)->lpVtbl -> Write(This,pv,cb,pcbWritten)
|
||||
|
||||
|
||||
#endif /* __ISequentialStream_INTERFACE_DEFINED__ */
|
||||
|
||||
|
||||
|
||||
#ifndef __IStream_FWD_DEFINED__
|
||||
#define __IStream_FWD_DEFINED__
|
||||
typedef interface IStream IStream;
|
||||
#endif /* __IStream_FWD_DEFINED__ */
|
||||
|
||||
|
||||
#ifndef __IStream_INTERFACE_DEFINED__
|
||||
#define __IStream_INTERFACE_DEFINED__
|
||||
|
||||
/* interface IStream */
|
||||
/* [unique][uuid][object] */
|
||||
|
||||
typedef /* [unique] */ IStream __RPC_FAR *LPSTREAM;
|
||||
|
||||
typedef struct tagSTATSTG
|
||||
{
|
||||
LPOLESTR pwcsName;
|
||||
DWORD type;
|
||||
ULARGE_INTEGER cbSize;
|
||||
FILETIME mtime;
|
||||
FILETIME ctime;
|
||||
FILETIME atime;
|
||||
DWORD grfMode;
|
||||
DWORD grfLocksSupported;
|
||||
CLSID clsid;
|
||||
DWORD grfStateBits;
|
||||
DWORD reserved;
|
||||
} STATSTG;
|
||||
|
||||
typedef
|
||||
enum tagSTGTY
|
||||
{ STGTY_STORAGE = 1,
|
||||
STGTY_STREAM = 2,
|
||||
STGTY_LOCKBYTES = 3,
|
||||
STGTY_PROPERTY = 4
|
||||
} STGTY;
|
||||
|
||||
typedef
|
||||
enum tagSTREAM_SEEK
|
||||
{ STREAM_SEEK_SET = 0,
|
||||
STREAM_SEEK_CUR = 1,
|
||||
STREAM_SEEK_END = 2
|
||||
} STREAM_SEEK;
|
||||
|
||||
typedef
|
||||
enum tagLOCKTYPE
|
||||
{ LOCK_WRITE = 1,
|
||||
LOCK_EXCLUSIVE = 2,
|
||||
LOCK_ONLYONCE = 4
|
||||
} LOCKTYPE;
|
||||
|
||||
|
||||
EXTERN_C const IID IID_IStream;
|
||||
|
||||
|
||||
typedef struct IStreamVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
|
||||
IStream __RPC_FAR * This,
|
||||
/* [in] */ __in REFIID riid,
|
||||
/* [iid_is][out] */ __deref_out void __RPC_FAR *__RPC_FAR *ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
|
||||
IStream __RPC_FAR * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
|
||||
IStream __RPC_FAR * This);
|
||||
|
||||
/* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Read )(
|
||||
IStream __RPC_FAR * This,
|
||||
/* [length_is][size_is][out] */ __out_bcount_part_opt(cb, *pcbRead) void __RPC_FAR *pv,
|
||||
/* [in] */ __in ULONG cb,
|
||||
/* [out] */ __out_opt ULONG __RPC_FAR *pcbRead);
|
||||
|
||||
/* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Write )(
|
||||
IStream __RPC_FAR * This,
|
||||
/* [size_is][in] */ __in_bcount_opt(cb) const void __RPC_FAR *pv,
|
||||
/* [in] */ __in ULONG cb,
|
||||
/* [out] */ __out_opt ULONG __RPC_FAR *pcbWritten);
|
||||
|
||||
/* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Seek )(
|
||||
IStream __RPC_FAR * This,
|
||||
/* [in] */ __in LARGE_INTEGER dlibMove,
|
||||
/* [in] */ __in DWORD dwOrigin,
|
||||
/* [out] */ __out_opt ULARGE_INTEGER __RPC_FAR *plibNewPosition);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetSize )(
|
||||
IStream __RPC_FAR * This,
|
||||
/* [in] */ __in ULARGE_INTEGER libNewSize);
|
||||
|
||||
/* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CopyTo )(
|
||||
IStream __RPC_FAR * This,
|
||||
/* [unique][in] */ __in IStream __RPC_FAR *pstm,
|
||||
/* [in] */ __in ULARGE_INTEGER cb,
|
||||
/* [out] */ __out_opt ULARGE_INTEGER __RPC_FAR *pcbRead,
|
||||
/* [out] */ __out_opt ULARGE_INTEGER __RPC_FAR *pcbWritten);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Commit )(
|
||||
IStream __RPC_FAR * This,
|
||||
/* [in] */ __in DWORD grfCommitFlags);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Revert )(
|
||||
IStream __RPC_FAR * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *LockRegion )(
|
||||
IStream __RPC_FAR * This,
|
||||
/* [in] */ __in ULARGE_INTEGER libOffset,
|
||||
/* [in] */ __in ULARGE_INTEGER cb,
|
||||
/* [in] */ __in DWORD dwLockType);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *UnlockRegion )(
|
||||
IStream __RPC_FAR * This,
|
||||
/* [in] */ __in ULARGE_INTEGER libOffset,
|
||||
/* [in] */ __in ULARGE_INTEGER cb,
|
||||
/* [in] */ __in DWORD dwLockType);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Stat )(
|
||||
IStream __RPC_FAR * This,
|
||||
/* [out] */ __out STATSTG __RPC_FAR *pstatstg,
|
||||
/* [in] */ __in DWORD grfStatFlag);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Clone )(
|
||||
IStream __RPC_FAR * This,
|
||||
/* [out] */ __deref_out_opt IStream __RPC_FAR *__RPC_FAR *ppstm);
|
||||
|
||||
END_INTERFACE
|
||||
} IStreamVtbl;
|
||||
|
||||
interface IStream
|
||||
{
|
||||
CONST_VTBL struct IStreamVtbl __RPC_FAR *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
#define IStream_QueryInterface(This,riid,ppvObject) \
|
||||
(This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
|
||||
|
||||
#define IStream_AddRef(This) \
|
||||
(This)->lpVtbl -> AddRef(This)
|
||||
|
||||
#define IStream_Release(This) \
|
||||
(This)->lpVtbl -> Release(This)
|
||||
|
||||
|
||||
#define IStream_Read(This,pv,cb,pcbRead) \
|
||||
(This)->lpVtbl -> Read(This,pv,cb,pcbRead)
|
||||
|
||||
#define IStream_Write(This,pv,cb,pcbWritten) \
|
||||
(This)->lpVtbl -> Write(This,pv,cb,pcbWritten)
|
||||
|
||||
|
||||
#define IStream_Seek(This,dlibMove,dwOrigin,plibNewPosition) \
|
||||
(This)->lpVtbl -> Seek(This,dlibMove,dwOrigin,plibNewPosition)
|
||||
|
||||
#define IStream_SetSize(This,libNewSize) \
|
||||
(This)->lpVtbl -> SetSize(This,libNewSize)
|
||||
|
||||
#define IStream_CopyTo(This,pstm,cb,pcbRead,pcbWritten) \
|
||||
(This)->lpVtbl -> CopyTo(This,pstm,cb,pcbRead,pcbWritten)
|
||||
|
||||
#define IStream_Commit(This,grfCommitFlags) \
|
||||
(This)->lpVtbl -> Commit(This,grfCommitFlags)
|
||||
|
||||
#define IStream_Revert(This) \
|
||||
(This)->lpVtbl -> Revert(This)
|
||||
|
||||
#define IStream_LockRegion(This,libOffset,cb,dwLockType) \
|
||||
(This)->lpVtbl -> LockRegion(This,libOffset,cb,dwLockType)
|
||||
|
||||
#define IStream_UnlockRegion(This,libOffset,cb,dwLockType) \
|
||||
(This)->lpVtbl -> UnlockRegion(This,libOffset,cb,dwLockType)
|
||||
|
||||
#define IStream_Stat(This,pstatstg,grfStatFlag) \
|
||||
(This)->lpVtbl -> Stat(This,pstatstg,grfStatFlag)
|
||||
|
||||
#define IStream_Clone(This,ppstm) \
|
||||
(This)->lpVtbl -> Clone(This,ppstm)
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __IStream_INTERFACE_DEFINED__ */
|
||||
|
||||
|
||||
#ifndef __IClassFactory_FWD_DEFINED__
|
||||
#define __IClassFactory_FWD_DEFINED__
|
||||
typedef interface IClassFactory IClassFactory;
|
||||
#endif /* __IClassFactory_FWD_DEFINED__ */
|
||||
|
||||
|
||||
#ifndef __IClassFactory_INTERFACE_DEFINED__
|
||||
#define __IClassFactory_INTERFACE_DEFINED__
|
||||
|
||||
/* interface IClassFactory */
|
||||
/* [unique][uuid][object] */
|
||||
|
||||
typedef /* [unique] */ IClassFactory __RPC_FAR *LPCLASSFACTORY;
|
||||
|
||||
|
||||
EXTERN_C const IID IID_IClassFactory;
|
||||
|
||||
|
||||
typedef struct IClassFactoryVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
|
||||
IClassFactory __RPC_FAR * This,
|
||||
/* [in] */ __in REFIID riid,
|
||||
/* [iid_is][out] */ __deref_out void __RPC_FAR *__RPC_FAR *ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
|
||||
IClassFactory __RPC_FAR * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
|
||||
IClassFactory __RPC_FAR * This);
|
||||
|
||||
/* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *CreateInstance )(
|
||||
IClassFactory __RPC_FAR * This,
|
||||
/* [unique][in] */ __in_opt IUnknown __RPC_FAR *pUnkOuter,
|
||||
/* [in] */ __in REFIID riid,
|
||||
/* [iid_is][out] */ __deref_out void __RPC_FAR *__RPC_FAR *ppvObject);
|
||||
|
||||
/* [local] */ HRESULT ( STDMETHODCALLTYPE __RPC_FAR *LockServer )(
|
||||
IClassFactory __RPC_FAR * This,
|
||||
/* [in] */ __in BOOL fLock);
|
||||
|
||||
END_INTERFACE
|
||||
} IClassFactoryVtbl;
|
||||
|
||||
interface IClassFactory
|
||||
{
|
||||
CONST_VTBL struct IClassFactoryVtbl __RPC_FAR *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IClassFactory_QueryInterface(This,riid,ppvObject) \
|
||||
(This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
|
||||
|
||||
#define IClassFactory_AddRef(This) \
|
||||
(This)->lpVtbl -> AddRef(This)
|
||||
|
||||
#define IClassFactory_Release(This) \
|
||||
(This)->lpVtbl -> Release(This)
|
||||
|
||||
|
||||
#define IClassFactory_CreateInstance(This,pUnkOuter,riid,ppvObject) \
|
||||
(This)->lpVtbl -> CreateInstance(This,pUnkOuter,riid,ppvObject)
|
||||
|
||||
#define IClassFactory_LockServer(This,fLock) \
|
||||
(This)->lpVtbl -> LockServer(This,fLock)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __IClassFactory_INTERFACE_DEFINED__ */
|
||||
|
||||
|
||||
|
||||
#ifndef __IPersist_FWD_DEFINED__
|
||||
#define __IPersist_FWD_DEFINED__
|
||||
typedef interface IPersist IPersist;
|
||||
#endif /* __IPersist_FWD_DEFINED__ */
|
||||
|
||||
|
||||
#ifndef __IPersist_INTERFACE_DEFINED__
|
||||
#define __IPersist_INTERFACE_DEFINED__
|
||||
|
||||
/* interface IPersist */
|
||||
/* [uuid][object] */
|
||||
|
||||
typedef /* [unique] */ IPersist __RPC_FAR *LPPERSIST;
|
||||
|
||||
|
||||
EXTERN_C const IID IID_IPersist;
|
||||
|
||||
|
||||
typedef struct IPersistVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
|
||||
IPersist __RPC_FAR * This,
|
||||
/* [in] */ __in REFIID riid,
|
||||
/* [iid_is][out] */ __deref_out void __RPC_FAR *__RPC_FAR *ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
|
||||
IPersist __RPC_FAR * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
|
||||
IPersist __RPC_FAR * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassID )(
|
||||
IPersist __RPC_FAR * This,
|
||||
/* [out] */ __out CLSID __RPC_FAR *pClassID);
|
||||
|
||||
END_INTERFACE
|
||||
} IPersistVtbl;
|
||||
|
||||
interface IPersist
|
||||
{
|
||||
CONST_VTBL struct IPersistVtbl __RPC_FAR *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IPersist_QueryInterface(This,riid,ppvObject) \
|
||||
(This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
|
||||
|
||||
#define IPersist_AddRef(This) \
|
||||
(This)->lpVtbl -> AddRef(This)
|
||||
|
||||
#define IPersist_Release(This) \
|
||||
(This)->lpVtbl -> Release(This)
|
||||
|
||||
|
||||
#define IPersist_GetClassID(This,pClassID) \
|
||||
(This)->lpVtbl -> GetClassID(This,pClassID)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __IPersist_INTERFACE_DEFINED__ */
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef __IPersistStream_FWD_DEFINED__
|
||||
#define __IPersistStream_FWD_DEFINED__
|
||||
typedef interface IPersistStream IPersistStream;
|
||||
#endif /* __IPersistStream_FWD_DEFINED__ */
|
||||
|
||||
|
||||
#ifndef __IPersistStream_INTERFACE_DEFINED__
|
||||
#define __IPersistStream_INTERFACE_DEFINED__
|
||||
|
||||
/* interface IPersistStream */
|
||||
/* [unique][uuid][object] */
|
||||
|
||||
typedef /* [unique] */ IPersistStream __RPC_FAR *LPPERSISTSTREAM;
|
||||
|
||||
|
||||
EXTERN_C const IID IID_IPersistStream;
|
||||
|
||||
typedef struct IPersistStreamVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )(
|
||||
IPersistStream __RPC_FAR * This,
|
||||
/* [in] */ __in REFIID riid,
|
||||
/* [iid_is][out] */ __deref_out void __RPC_FAR *__RPC_FAR *ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )(
|
||||
IPersistStream __RPC_FAR * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )(
|
||||
IPersistStream __RPC_FAR * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetClassID )(
|
||||
IPersistStream __RPC_FAR * This,
|
||||
/* [out] */ __out CLSID __RPC_FAR *pClassID);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *IsDirty )(
|
||||
IPersistStream __RPC_FAR * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Load )(
|
||||
IPersistStream __RPC_FAR * This,
|
||||
/* [unique][in] */ __in_opt IStream __RPC_FAR *pStm);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Save )(
|
||||
IPersistStream __RPC_FAR * This,
|
||||
/* [unique][in] */ __in_opt IStream __RPC_FAR *pStm,
|
||||
/* [in] */ __in BOOL fClearDirty);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetSizeMax )(
|
||||
IPersistStream __RPC_FAR * This,
|
||||
/* [out] */ __out ULARGE_INTEGER __RPC_FAR *pcbSize);
|
||||
|
||||
END_INTERFACE
|
||||
} IPersistStreamVtbl;
|
||||
|
||||
interface IPersistStream
|
||||
{
|
||||
CONST_VTBL struct IPersistStreamVtbl __RPC_FAR *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#define IPersistStream_QueryInterface(This,riid,ppvObject) \
|
||||
(This)->lpVtbl -> QueryInterface(This,riid,ppvObject)
|
||||
|
||||
#define IPersistStream_AddRef(This) \
|
||||
(This)->lpVtbl -> AddRef(This)
|
||||
|
||||
#define IPersistStream_Release(This) \
|
||||
(This)->lpVtbl -> Release(This)
|
||||
|
||||
|
||||
#define IPersistStream_GetClassID(This,pClassID) \
|
||||
(This)->lpVtbl -> GetClassID(This,pClassID)
|
||||
|
||||
|
||||
#define IPersistStream_IsDirty(This) \
|
||||
(This)->lpVtbl -> IsDirty(This)
|
||||
|
||||
#define IPersistStream_Load(This,pStm) \
|
||||
(This)->lpVtbl -> Load(This,pStm)
|
||||
|
||||
#define IPersistStream_Save(This,pStm,fClearDirty) \
|
||||
(This)->lpVtbl -> Save(This,pStm,fClearDirty)
|
||||
|
||||
#define IPersistStream_GetSizeMax(This,pcbSize) \
|
||||
(This)->lpVtbl -> GetSizeMax(This,pcbSize)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __IPersistStream_INTERFACE_DEFINED__ */
|
||||
|
||||
|
||||
|
||||
#include <guiddef.h>
|
||||
|
||||
#ifndef INITGUID
|
||||
#include <cguid.h>
|
||||
#endif
|
||||
|
||||
#ifndef RC_INVOKED
|
||||
#include <poppack.h>
|
||||
#endif // RC_INVOKED
|
||||
|
||||
#endif // _XOBJBASE_H_
|
||||
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
xaudio.h (2010-08-14) / (2012-12-04)
|
||||
authors: OV2, Themaister
|
||||
*/
|
||||
|
||||
// Kinda stripped down. Only contains the bare essentials used in SSNES.
|
||||
|
||||
#ifndef XAUDIO2_XDK360_H
|
||||
#define XAUDIO2_XDK360_H
|
||||
|
||||
#include <comdecl.h> // For DEFINE_CLSID and DEFINE_IID
|
||||
|
||||
DEFINE_CLSID(XAudio2, 3eda9b49, 2085, 498b, 9b, b2, 39, a6, 77, 84, 93, de);
|
||||
DEFINE_CLSID(XAudio2_Debug, 47199894, 7cc2, 444d, 98, 73, ce, d2, 56, 2c, c6, 0e);
|
||||
DEFINE_IID(IXAudio2, 8bcf1f58, 9fe7, 4583, 8a, c6, e2, ad, c4, 65, c8, bb);
|
||||
|
||||
|
||||
// Ignore the rest of this header if only the GUID definitions were requested
|
||||
#ifndef GUID_DEFS_ONLY
|
||||
|
||||
#include "objbase.h" // Xbox COM declarations (IUnknown, etc)
|
||||
|
||||
#include <audiodefs.h> // Basic audio data types and constants
|
||||
#include <xma2defs.h> // Data types and constants for XMA2 audio
|
||||
|
||||
// All structures defined in this file use tight field packing
|
||||
#pragma pack(push, 1)
|
||||
|
||||
// Numeric boundary values
|
||||
#define XAUDIO2_MAX_BUFFER_BYTES 0x80000000 // Maximum bytes allowed in a source buffer
|
||||
#define XAUDIO2_MAX_QUEUED_BUFFERS 64 // Maximum buffers allowed in a voice queue
|
||||
#define XAUDIO2_MAX_BUFFERS_SYSTEM 2 // Maximum buffers allowed for system threads (Xbox 360 only)
|
||||
#define XAUDIO2_MAX_AUDIO_CHANNELS 64 // Maximum channels in an audio stream
|
||||
#define XAUDIO2_MIN_SAMPLE_RATE 1000 // Minimum audio sample rate supported
|
||||
#define XAUDIO2_MAX_SAMPLE_RATE 200000 // Maximum audio sample rate supported
|
||||
#define XAUDIO2_MAX_VOLUME_LEVEL 16777216.0f // Maximum acceptable volume level (2^24)
|
||||
#define XAUDIO2_MIN_FREQ_RATIO (1/1024.0f) // Minimum SetFrequencyRatio argument
|
||||
#define XAUDIO2_MAX_FREQ_RATIO 1024.0f // Maximum MaxFrequencyRatio argument
|
||||
#define XAUDIO2_DEFAULT_FREQ_RATIO 2.0f // Default MaxFrequencyRatio argument
|
||||
#define XAUDIO2_MAX_FILTER_ONEOVERQ 1.5f // Maximum XAUDIO2_FILTER_PARAMETERS.OneOverQ
|
||||
#define XAUDIO2_MAX_FILTER_FREQUENCY 1.0f // Maximum XAUDIO2_FILTER_PARAMETERS.Frequency
|
||||
#define XAUDIO2_MAX_LOOP_COUNT 254 // Maximum non-infinite XAUDIO2_BUFFER.LoopCount
|
||||
#define XAUDIO2_MAX_INSTANCES 8 // Maximum simultaneous XAudio2 objects on Xbox 360
|
||||
|
||||
#define XAUDIO2_MAX_RATIO_TIMES_RATE_XMA_MONO 600000
|
||||
#define XAUDIO2_MAX_RATIO_TIMES_RATE_XMA_MULTICHANNEL 300000
|
||||
|
||||
// Numeric values with special meanings
|
||||
#define XAUDIO2_COMMIT_NOW 0 // Used as an OperationSet argument
|
||||
#define XAUDIO2_COMMIT_ALL 0 // Used in IXAudio2::CommitChanges
|
||||
#define XAUDIO2_INVALID_OPSET (UINT32)(-1) // Not allowed for OperationSet arguments
|
||||
#define XAUDIO2_NO_LOOP_REGION 0 // Used in XAUDIO2_BUFFER.LoopCount
|
||||
#define XAUDIO2_LOOP_INFINITE 255 // Used in XAUDIO2_BUFFER.LoopCount
|
||||
#define XAUDIO2_DEFAULT_CHANNELS 0 // Used in CreateMasteringVoice
|
||||
#define XAUDIO2_DEFAULT_SAMPLERATE 0 // Used in CreateMasteringVoice
|
||||
|
||||
// Flags
|
||||
#define XAUDIO2_DEBUG_ENGINE 0x0001 // Used in XAudio2Create on Windows only
|
||||
#define XAUDIO2_VOICE_NOPITCH 0x0002 // Used in IXAudio2::CreateSourceVoice
|
||||
#define XAUDIO2_VOICE_NOSRC 0x0004 // Used in IXAudio2::CreateSourceVoice
|
||||
#define XAUDIO2_VOICE_USEFILTER 0x0008 // Used in IXAudio2::CreateSource/SubmixVoice
|
||||
#define XAUDIO2_VOICE_MUSIC 0x0010 // Used in IXAudio2::CreateSourceVoice
|
||||
#define XAUDIO2_PLAY_TAILS 0x0020 // Used in IXAudio2SourceVoice::Stop
|
||||
#define XAUDIO2_END_OF_STREAM 0x0040 // Used in XAUDIO2_BUFFER.Flags
|
||||
#define XAUDIO2_SEND_USEFILTER 0x0080 // Used in XAUDIO2_SEND_DESCRIPTOR.Flags
|
||||
|
||||
// Default parameters for the built-in filter
|
||||
#define XAUDIO2_DEFAULT_FILTER_TYPE LowPassFilter
|
||||
#define XAUDIO2_DEFAULT_FILTER_FREQUENCY XAUDIO2_MAX_FILTER_FREQUENCY
|
||||
#define XAUDIO2_DEFAULT_FILTER_ONEOVERQ 1.0f
|
||||
|
||||
// Internal XAudio2 constants
|
||||
#define XAUDIO2_QUANTUM_NUMERATOR 2 // On Xbox 360, XAudio2 processes audio
|
||||
#define XAUDIO2_QUANTUM_DENOMINATOR 375 // in 5.333ms chunks (= 2/375 seconds)
|
||||
#define XAUDIO2_QUANTUM_MS (1000.0f * XAUDIO2_QUANTUM_NUMERATOR / XAUDIO2_QUANTUM_DENOMINATOR)
|
||||
|
||||
// XAudio2 error codes
|
||||
#define FACILITY_XAUDIO2 0x896
|
||||
#define XAUDIO2_E_INVALID_CALL 0x88960001 // An API call or one of its arguments was illegal
|
||||
#define XAUDIO2_E_XMA_DECODER_ERROR 0x88960002 // The XMA hardware suffered an unrecoverable error
|
||||
#define XAUDIO2_E_XAPO_CREATION_FAILED 0x88960003 // XAudio2 failed to initialize an XAPO effect
|
||||
#define XAUDIO2_E_DEVICE_INVALIDATED 0x88960004 // An audio device became unusable (unplugged, etc)
|
||||
|
||||
#define FWD_DECLARE(x) typedef interface x x
|
||||
|
||||
FWD_DECLARE(IXAudio2);
|
||||
FWD_DECLARE(IXAudio2Voice);
|
||||
FWD_DECLARE(IXAudio2SourceVoice);
|
||||
FWD_DECLARE(IXAudio2SubmixVoice);
|
||||
FWD_DECLARE(IXAudio2MasteringVoice);
|
||||
FWD_DECLARE(IXAudio2EngineCallback);
|
||||
FWD_DECLARE(IXAudio2VoiceCallback);
|
||||
|
||||
typedef enum XAUDIO2_XBOX_HWTHREAD_SPECIFIER
|
||||
{
|
||||
XboxThread0 = 0x01,
|
||||
XboxThread1 = 0x02,
|
||||
XboxThread2 = 0x04,
|
||||
XboxThread3 = 0x08,
|
||||
XboxThread4 = 0x10,
|
||||
XboxThread5 = 0x20,
|
||||
XAUDIO2_ANY_PROCESSOR = XboxThread4,
|
||||
XAUDIO2_DEFAULT_PROCESSOR = XAUDIO2_ANY_PROCESSOR
|
||||
} XAUDIO2_XBOX_HWTHREAD_SPECIFIER, XAUDIO2_PROCESSOR;
|
||||
|
||||
typedef enum XAUDIO2_DEVICE_ROLE
|
||||
{
|
||||
NotDefaultDevice = 0x0,
|
||||
DefaultConsoleDevice = 0x1,
|
||||
DefaultMultimediaDevice = 0x2,
|
||||
DefaultCommunicationsDevice = 0x4,
|
||||
DefaultGameDevice = 0x8,
|
||||
GlobalDefaultDevice = 0xf,
|
||||
InvalidDeviceRole = ~GlobalDefaultDevice
|
||||
} XAUDIO2_DEVICE_ROLE;
|
||||
|
||||
typedef struct XAUDIO2_DEVICE_DETAILS
|
||||
{
|
||||
WCHAR DeviceID[256]; // String identifier for the audio device.
|
||||
WCHAR DisplayName[256]; // Friendly name suitable for display to a human.
|
||||
XAUDIO2_DEVICE_ROLE Role; // Roles that the device should be used for.
|
||||
WAVEFORMATEXTENSIBLE OutputFormat; // The device's native PCM audio output format.
|
||||
} XAUDIO2_DEVICE_DETAILS;
|
||||
|
||||
typedef struct XAUDIO2_VOICE_DETAILS
|
||||
{
|
||||
UINT32 CreationFlags; // Flags the voice was created with.
|
||||
UINT32 InputChannels; // Channels in the voice's input audio.
|
||||
UINT32 InputSampleRate; // Sample rate of the voice's input audio.
|
||||
} XAUDIO2_VOICE_DETAILS;
|
||||
|
||||
// Used in XAUDIO2_VOICE_SENDS below
|
||||
typedef struct XAUDIO2_SEND_DESCRIPTOR
|
||||
{
|
||||
UINT32 Flags; // Either 0 or XAUDIO2_SEND_USEFILTER.
|
||||
IXAudio2Voice* pOutputVoice; // This send's destination voice.
|
||||
} XAUDIO2_SEND_DESCRIPTOR;
|
||||
|
||||
// Used in the voice creation functions and in IXAudio2Voice::SetOutputVoices
|
||||
typedef struct XAUDIO2_VOICE_SENDS
|
||||
{
|
||||
UINT32 SendCount; // Number of sends from this voice.
|
||||
XAUDIO2_SEND_DESCRIPTOR* pSends; // Array of SendCount send descriptors.
|
||||
} XAUDIO2_VOICE_SENDS;
|
||||
|
||||
// Used in XAUDIO2_EFFECT_CHAIN below
|
||||
typedef struct XAUDIO2_EFFECT_DESCRIPTOR
|
||||
{
|
||||
IUnknown* pEffect; // Pointer to the effect object's IUnknown interface.
|
||||
BOOL InitialState; // TRUE if the effect should begin in the enabled state.
|
||||
UINT32 OutputChannels; // How many output channels the effect should produce.
|
||||
} XAUDIO2_EFFECT_DESCRIPTOR;
|
||||
|
||||
// Used in the voice creation functions and in IXAudio2Voice::SetEffectChain
|
||||
typedef struct XAUDIO2_EFFECT_CHAIN
|
||||
{
|
||||
UINT32 EffectCount; // Number of effects in this voice's effect chain.
|
||||
XAUDIO2_EFFECT_DESCRIPTOR* pEffectDescriptors; // Array of effect descriptors.
|
||||
} XAUDIO2_EFFECT_CHAIN;
|
||||
|
||||
// Used in XAUDIO2_FILTER_PARAMETERS below
|
||||
typedef enum XAUDIO2_FILTER_TYPE
|
||||
{
|
||||
LowPassFilter, // Attenuates frequencies above the cutoff frequency.
|
||||
BandPassFilter, // Attenuates frequencies outside a given range.
|
||||
HighPassFilter, // Attenuates frequencies below the cutoff frequency.
|
||||
NotchFilter // Attenuates frequencies inside a given range.
|
||||
} XAUDIO2_FILTER_TYPE;
|
||||
|
||||
// Used in IXAudio2Voice::Set/GetFilterParameters and Set/GetOutputFilterParameters
|
||||
typedef struct XAUDIO2_FILTER_PARAMETERS
|
||||
{
|
||||
XAUDIO2_FILTER_TYPE Type; // Low-pass, band-pass or high-pass.
|
||||
float Frequency; // Radian frequency (2 * sin(pi*CutoffFrequency/SampleRate));
|
||||
// must be >= 0 and <= XAUDIO2_MAX_FILTER_FREQUENCY
|
||||
// (giving a maximum CutoffFrequency of SampleRate/6).
|
||||
float OneOverQ; // Reciprocal of the filter's quality factor Q;
|
||||
// must be > 0 and <= XAUDIO2_MAX_FILTER_ONEOVERQ.
|
||||
} XAUDIO2_FILTER_PARAMETERS;
|
||||
|
||||
// Used in IXAudio2SourceVoice::SubmitSourceBuffer
|
||||
typedef struct XAUDIO2_BUFFER
|
||||
{
|
||||
UINT32 Flags; // Either 0 or XAUDIO2_END_OF_STREAM.
|
||||
UINT32 AudioBytes; // Size of the audio data buffer in bytes.
|
||||
const BYTE* pAudioData; // Pointer to the audio data buffer.
|
||||
UINT32 PlayBegin; // First sample in this buffer to be played.
|
||||
UINT32 PlayLength; // Length of the region to be played in samples,
|
||||
// or 0 to play the whole buffer.
|
||||
UINT32 LoopBegin; // First sample of the region to be looped.
|
||||
UINT32 LoopLength; // Length of the desired loop region in samples,
|
||||
// or 0 to loop the entire buffer.
|
||||
UINT32 LoopCount; // Number of times to repeat the loop region,
|
||||
// or XAUDIO2_LOOP_INFINITE to loop forever.
|
||||
void* pContext; // Context value to be passed back in callbacks.
|
||||
} XAUDIO2_BUFFER;
|
||||
|
||||
typedef struct XAUDIO2_BUFFER_WMA
|
||||
{
|
||||
const UINT32* pDecodedPacketCumulativeBytes; // Decoded packet's cumulative size array.
|
||||
// Each element is the number of bytes accumulated
|
||||
// when the corresponding XWMA packet is decoded in
|
||||
// order. The array must have PacketCount elements.
|
||||
UINT32 PacketCount; // Number of XWMA packets submitted. Must be >= 1 and
|
||||
// divide evenly into XAUDIO2_BUFFER.AudioBytes.
|
||||
} XAUDIO2_BUFFER_WMA;
|
||||
|
||||
// Returned by IXAudio2SourceVoice::GetState
|
||||
typedef struct XAUDIO2_VOICE_STATE
|
||||
{
|
||||
void* pCurrentBufferContext; // The pContext value provided in the XAUDIO2_BUFFER
|
||||
// that is currently being processed, or NULL if
|
||||
// there are no buffers in the queue.
|
||||
UINT32 BuffersQueued; // Number of buffers currently queued on the voice
|
||||
// (including the one that is being processed).
|
||||
UINT64 SamplesPlayed; // Total number of samples produced by the voice since
|
||||
// it began processing the current audio stream.
|
||||
} XAUDIO2_VOICE_STATE;
|
||||
|
||||
#define XAUDIO2_LOG_ERRORS 0x0001 // For handled errors with serious effects.
|
||||
#define XAUDIO2_LOG_WARNINGS 0x0002 // For handled errors that may be recoverable.
|
||||
#define XAUDIO2_LOG_INFO 0x0004 // Informational chit-chat (e.g. state changes).
|
||||
#define XAUDIO2_LOG_DETAIL 0x0008 // More detailed chit-chat.
|
||||
#define XAUDIO2_LOG_API_CALLS 0x0010 // Public API function entries and exits.
|
||||
#define XAUDIO2_LOG_FUNC_CALLS 0x0020 // Internal function entries and exits.
|
||||
#define XAUDIO2_LOG_TIMING 0x0040 // Delays detected and other timing data.
|
||||
#define XAUDIO2_LOG_LOCKS 0x0080 // Usage of critical sections and mutexes.
|
||||
#define XAUDIO2_LOG_MEMORY 0x0100 // Memory heap usage information.
|
||||
#define XAUDIO2_LOG_STREAMING 0x1000 // Audio streaming information.
|
||||
|
||||
#define X2DEFAULT(x)
|
||||
|
||||
#undef INTERFACE
|
||||
#define INTERFACE IXAudio2
|
||||
DECLARE_INTERFACE_(IXAudio2, IUnknown)
|
||||
{
|
||||
STDMETHOD(QueryInterface) (THIS_ REFIID riid, __deref_out void** ppvInterface) PURE;
|
||||
STDMETHOD_(ULONG, AddRef) (THIS) PURE;
|
||||
STDMETHOD_(ULONG, Release) (THIS) PURE;
|
||||
STDMETHOD(GetDeviceCount) (THIS_ __out UINT32* pCount) PURE;
|
||||
STDMETHOD(GetDeviceDetails) (THIS_ UINT32 Index, __out XAUDIO2_DEVICE_DETAILS* pDeviceDetails) PURE;
|
||||
STDMETHOD(Initialize) (THIS_ UINT32 Flags X2DEFAULT(0),
|
||||
XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR)) PURE;
|
||||
STDMETHOD(RegisterForCallbacks) (__in IXAudio2EngineCallback* pCallback) PURE;
|
||||
STDMETHOD_(void, UnregisterForCallbacks) (__in IXAudio2EngineCallback* pCallback) PURE;
|
||||
STDMETHOD(CreateSourceVoice) (THIS_ __deref_out IXAudio2SourceVoice** ppSourceVoice,
|
||||
__in const WAVEFORMATEX* pSourceFormat,
|
||||
UINT32 Flags X2DEFAULT(0),
|
||||
float MaxFrequencyRatio X2DEFAULT(XAUDIO2_DEFAULT_FREQ_RATIO),
|
||||
__in_opt IXAudio2VoiceCallback* pCallback X2DEFAULT(NULL),
|
||||
__in_opt const XAUDIO2_VOICE_SENDS* pSendList X2DEFAULT(NULL),
|
||||
__in_opt const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
|
||||
STDMETHOD(CreateSubmixVoice) (THIS_ __deref_out IXAudio2SubmixVoice** ppSubmixVoice,
|
||||
UINT32 InputChannels, UINT32 InputSampleRate,
|
||||
UINT32 Flags X2DEFAULT(0), UINT32 ProcessingStage X2DEFAULT(0),
|
||||
__in_opt const XAUDIO2_VOICE_SENDS* pSendList X2DEFAULT(NULL),
|
||||
__in_opt const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
|
||||
STDMETHOD(CreateMasteringVoice) (THIS_ __deref_out IXAudio2MasteringVoice** ppMasteringVoice,
|
||||
UINT32 InputChannels X2DEFAULT(XAUDIO2_DEFAULT_CHANNELS),
|
||||
UINT32 InputSampleRate X2DEFAULT(XAUDIO2_DEFAULT_SAMPLERATE),
|
||||
UINT32 Flags X2DEFAULT(0), UINT32 DeviceIndex X2DEFAULT(0),
|
||||
__in_opt const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
|
||||
|
||||
|
||||
STDMETHOD(StartEngine) (THIS) PURE;
|
||||
STDMETHOD_(void, StopEngine) (THIS) PURE;
|
||||
STDMETHOD(CommitChanges) (THIS_ UINT32 OperationSet) PURE;
|
||||
};
|
||||
|
||||
#undef INTERFACE
|
||||
#define INTERFACE IXAudio2Voice
|
||||
DECLARE_INTERFACE(IXAudio2Voice)
|
||||
{
|
||||
#define Declare_IXAudio2Voice_Methods() \
|
||||
STDMETHOD_(void, GetVoiceDetails) (THIS_ __out XAUDIO2_VOICE_DETAILS* pVoiceDetails) PURE; \
|
||||
\
|
||||
STDMETHOD(SetOutputVoices) (THIS_ __in_opt const XAUDIO2_VOICE_SENDS* pSendList) PURE; \
|
||||
\
|
||||
STDMETHOD(SetEffectChain) (THIS_ __in_opt const XAUDIO2_EFFECT_CHAIN* pEffectChain) PURE; \
|
||||
\
|
||||
STDMETHOD(EnableEffect) (THIS_ UINT32 EffectIndex, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
\
|
||||
STDMETHOD(DisableEffect) (THIS_ UINT32 EffectIndex, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
\
|
||||
STDMETHOD_(void, GetEffectState) (THIS_ UINT32 EffectIndex, __out BOOL* pEnabled) PURE; \
|
||||
\
|
||||
STDMETHOD(SetEffectParameters) (THIS_ UINT32 EffectIndex, \
|
||||
__in_bcount(ParametersByteSize) const void* pParameters, \
|
||||
UINT32 ParametersByteSize, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
\
|
||||
STDMETHOD(GetEffectParameters) (THIS_ UINT32 EffectIndex, \
|
||||
__out_bcount(ParametersByteSize) void* pParameters, \
|
||||
UINT32 ParametersByteSize) PURE; \
|
||||
\
|
||||
STDMETHOD(SetFilterParameters) (THIS_ __in const XAUDIO2_FILTER_PARAMETERS* pParameters, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
\
|
||||
STDMETHOD_(void, GetFilterParameters) (THIS_ __out XAUDIO2_FILTER_PARAMETERS* pParameters) PURE; \
|
||||
\
|
||||
STDMETHOD(SetOutputFilterParameters) (THIS_ __in_opt IXAudio2Voice* pDestinationVoice, \
|
||||
__in const XAUDIO2_FILTER_PARAMETERS* pParameters, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
\
|
||||
STDMETHOD_(void, GetOutputFilterParameters) (THIS_ __in_opt IXAudio2Voice* pDestinationVoice, \
|
||||
__out XAUDIO2_FILTER_PARAMETERS* pParameters) PURE; \
|
||||
\
|
||||
STDMETHOD(SetVolume) (THIS_ float Volume, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
\
|
||||
STDMETHOD_(void, GetVolume) (THIS_ __out float* pVolume) PURE; \
|
||||
\
|
||||
STDMETHOD(SetChannelVolumes) (THIS_ UINT32 Channels, __in_ecount(Channels) const float* pVolumes, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
\
|
||||
STDMETHOD_(void, GetChannelVolumes) (THIS_ UINT32 Channels, __out_ecount(Channels) float* pVolumes) PURE; \
|
||||
\
|
||||
STDMETHOD(SetOutputMatrix) (THIS_ __in_opt IXAudio2Voice* pDestinationVoice, \
|
||||
UINT32 SourceChannels, UINT32 DestinationChannels, \
|
||||
__in_ecount(SourceChannels * DestinationChannels) const float* pLevelMatrix, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
\
|
||||
STDMETHOD_(void, GetOutputMatrix) (THIS_ __in_opt IXAudio2Voice* pDestinationVoice, \
|
||||
UINT32 SourceChannels, UINT32 DestinationChannels, \
|
||||
__out_ecount(SourceChannels * DestinationChannels) float* pLevelMatrix) PURE; \
|
||||
\
|
||||
STDMETHOD_(void, DestroyVoice) (THIS) PURE
|
||||
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
#undef INTERFACE
|
||||
#define INTERFACE IXAudio2SourceVoice
|
||||
DECLARE_INTERFACE_(IXAudio2SourceVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
STDMETHOD(Start) (THIS_ UINT32 Flags X2DEFAULT(0), UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD(Stop) (THIS_ UINT32 Flags X2DEFAULT(0), UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD(SubmitSourceBuffer) (THIS_ __in const XAUDIO2_BUFFER* pBuffer, __in_opt const XAUDIO2_BUFFER_WMA* pBufferWMA X2DEFAULT(NULL)) PURE;
|
||||
STDMETHOD(FlushSourceBuffers) (THIS) PURE;
|
||||
STDMETHOD(Discontinuity) (THIS) PURE;
|
||||
STDMETHOD(ExitLoop) (THIS_ UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD_(void, GetState) (THIS_ __out XAUDIO2_VOICE_STATE* pVoiceState) PURE;
|
||||
STDMETHOD(SetFrequencyRatio) (THIS_ float Ratio,
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD_(void, GetFrequencyRatio) (THIS_ __out float* pRatio) PURE;
|
||||
STDMETHOD(SetSourceSampleRate) (THIS_ UINT32 NewSourceSampleRate) PURE;
|
||||
};
|
||||
|
||||
#undef INTERFACE
|
||||
#define INTERFACE IXAudio2SubmixVoice
|
||||
DECLARE_INTERFACE_(IXAudio2SubmixVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
#undef INTERFACE
|
||||
#define INTERFACE IXAudio2MasteringVoice
|
||||
DECLARE_INTERFACE_(IXAudio2MasteringVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
#undef INTERFACE
|
||||
#define INTERFACE IXAudio2EngineCallback
|
||||
DECLARE_INTERFACE(IXAudio2EngineCallback)
|
||||
{
|
||||
STDMETHOD_(void, OnProcessingPassStart) (THIS) PURE;
|
||||
STDMETHOD_(void, OnProcessingPassEnd) (THIS) PURE;
|
||||
STDMETHOD_(void, OnCriticalError) (THIS_ HRESULT Error) PURE;
|
||||
};
|
||||
|
||||
#undef INTERFACE
|
||||
#define INTERFACE IXAudio2VoiceCallback
|
||||
DECLARE_INTERFACE(IXAudio2VoiceCallback)
|
||||
{
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (THIS_ UINT32 BytesRequired) PURE;
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) (THIS) PURE;
|
||||
STDMETHOD_(void, OnStreamEnd) (THIS) PURE;
|
||||
STDMETHOD_(void, OnBufferStart) (THIS_ void* pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnBufferEnd) (THIS_ void* pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnLoopEnd) (THIS_ void* pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnVoiceError) (THIS_ void* pBufferContext, HRESULT Error) PURE;
|
||||
};
|
||||
|
||||
// IXAudio2
|
||||
#define IXAudio2_QueryInterface(This,riid,ppvInterface) ((This)->lpVtbl->QueryInterface(This,riid,ppvInterface))
|
||||
#define IXAudio2_AddRef(This) ((This)->lpVtbl->AddRef(This))
|
||||
#define IXAudio2_Release(This) ((This)->lpVtbl->Release(This))
|
||||
#define IXAudio2_GetDeviceCount(This,puCount) ((This)->lpVtbl->GetDeviceCount(This,puCount))
|
||||
#define IXAudio2_GetDeviceDetails(This,Index,pDeviceDetails) ((This)->lpVtbl->GetDeviceDetails(This,Index,pDeviceDetails))
|
||||
#define IXAudio2_Initialize(This,Flags,XAudio2Processor) ((This)->lpVtbl->Initialize(This,Flags,XAudio2Processor))
|
||||
#define IXAudio2_CreateSourceVoice(This,ppSourceVoice,pSourceFormat,Flags,MaxFrequencyRatio,pCallback,pSendList,pEffectChain) ((This)->lpVtbl->CreateSourceVoice(This,ppSourceVoice,pSourceFormat,Flags,MaxFrequencyRatio,pCallback,pSendList,pEffectChain))
|
||||
#define IXAudio2_CreateSubmixVoice(This,ppSubmixVoice,InputChannels,InputSampleRate,Flags,ProcessingStage,pSendList,pEffectChain) ((This)->lpVtbl->CreateSubmixVoice(This,ppSubmixVoice,InputChannels,InputSampleRate,Flags,ProcessingStage,pSendList,pEffectChain))
|
||||
#define IXAudio2_CreateMasteringVoice(This,ppMasteringVoice,InputChannels,InputSampleRate,Flags,DeviceIndex,pEffectChain) ((This)->lpVtbl->CreateMasteringVoice(This,ppMasteringVoice,InputChannels,InputSampleRate,Flags,DeviceIndex,pEffectChain))
|
||||
#define IXAudio2_StartEngine(This) ((This)->lpVtbl->StartEngine(This))
|
||||
#define IXAudio2_StopEngine(This) ((This)->lpVtbl->StopEngine(This))
|
||||
#define IXAudio2_CommitChanges(This,OperationSet) ((This)->lpVtbl->CommitChanges(This,OperationSet))
|
||||
|
||||
// IXAudio2Voice
|
||||
#define IXAudio2Voice_GetVoiceDetails(This,pVoiceDetails) ((This)->lpVtbl->GetVoiceDetails(This,pVoiceDetails))
|
||||
#define IXAudio2Voice_SetOutputVoices(This,pSendList) ((This)->lpVtbl->SetOutputVoices(This,pSendList))
|
||||
#define IXAudio2Voice_SetEffectChain(This,pEffectChain) ((This)->lpVtbl->SetEffectChain(This,pEffectChain))
|
||||
#define IXAudio2Voice_EnableEffect(This,EffectIndex,OperationSet) ((This)->lpVtbl->EnableEffect(This,EffectIndex,OperationSet))
|
||||
#define IXAudio2Voice_DisableEffect(This,EffectIndex,OperationSet) ((This)->lpVtbl->DisableEffect(This,EffectIndex,OperationSet))
|
||||
#define IXAudio2Voice_GetEffectState(This,EffectIndex,pEnabled) ((This)->lpVtbl->GetEffectState(This,EffectIndex,pEnabled))
|
||||
#define IXAudio2Voice_SetEffectParameters(This,EffectIndex,pParameters,ParametersByteSize, OperationSet) ((This)->lpVtbl->SetEffectParameters(This,EffectIndex,pParameters,ParametersByteSize,OperationSet))
|
||||
#define IXAudio2Voice_GetEffectParameters(This,EffectIndex,pParameters,ParametersByteSize) ((This)->lpVtbl->GetEffectParameters(This,EffectIndex,pParameters,ParametersByteSize))
|
||||
#define IXAudio2Voice_SetFilterParameters(This,pParameters,OperationSet) ((This)->lpVtbl->SetFilterParameters(This,pParameters,OperationSet))
|
||||
#define IXAudio2Voice_GetFilterParameters(This,pParameters) ((This)->lpVtbl->GetFilterParameters(This,pParameters))
|
||||
#define IXAudio2Voice_SetOutputFilterParameters(This,pDestinationVoice,pParameters,OperationSet) ((This)->lpVtbl->SetOutputFilterParameters(This,pDestinationVoice,pParameters,OperationSet))
|
||||
#define IXAudio2Voice_GetOutputFilterParameters(This,pDestinationVoice,pParameters) ((This)->lpVtbl->GetOutputFilterParameters(This,pDestinationVoice,pParameters))
|
||||
#define IXAudio2Voice_SetVolume(This,Volume,OperationSet) ((This)->lpVtbl->SetVolume(This,Volume,OperationSet))
|
||||
#define IXAudio2Voice_GetVolume(This,pVolume) ((This)->lpVtbl->GetVolume(This,pVolume))
|
||||
#define IXAudio2Voice_SetChannelVolumes(This,Channels,pVolumes,OperationSet) ((This)->lpVtbl->SetChannelVolumes(This,Channels,pVolumes,OperationSet))
|
||||
#define IXAudio2Voice_GetChannelVolumes(This,Channels,pVolumes) ((This)->lpVtbl->GetChannelVolumes(This,Channels,pVolumes))
|
||||
#define IXAudio2Voice_SetOutputMatrix(This,pDestinationVoice,SourceChannels,DestinationChannels,pLevelMatrix,OperationSet) ((This)->lpVtbl->SetOutputMatrix(This,pDestinationVoice,SourceChannels,DestinationChannels,pLevelMatrix,OperationSet))
|
||||
#define IXAudio2Voice_GetOutputMatrix(This,pDestinationVoice,SourceChannels,DestinationChannels,pLevelMatrix) ((This)->lpVtbl->GetOutputMatrix(This,pDestinationVoice,SourceChannels,DestinationChannels,pLevelMatrix))
|
||||
#define IXAudio2Voice_DestroyVoice(This) ((This)->lpVtbl->DestroyVoice(This))
|
||||
|
||||
// IXAudio2SourceVoice
|
||||
#define IXAudio2SourceVoice_GetVoiceDetails IXAudio2Voice_GetVoiceDetails
|
||||
#define IXAudio2SourceVoice_SetOutputVoices IXAudio2Voice_SetOutputVoices
|
||||
#define IXAudio2SourceVoice_SetEffectChain IXAudio2Voice_SetEffectChain
|
||||
#define IXAudio2SourceVoice_EnableEffect IXAudio2Voice_EnableEffect
|
||||
#define IXAudio2SourceVoice_DisableEffect IXAudio2Voice_DisableEffect
|
||||
#define IXAudio2SourceVoice_GetEffectState IXAudio2Voice_GetEffectState
|
||||
#define IXAudio2SourceVoice_SetEffectParameters IXAudio2Voice_SetEffectParameters
|
||||
#define IXAudio2SourceVoice_GetEffectParameters IXAudio2Voice_GetEffectParameters
|
||||
#define IXAudio2SourceVoice_SetFilterParameters IXAudio2Voice_SetFilterParameters
|
||||
#define IXAudio2SourceVoice_GetFilterParameters IXAudio2Voice_GetFilterParameters
|
||||
#define IXAudio2SourceVoice_SetOutputFilterParameters IXAudio2Voice_SetOutputFilterParameters
|
||||
#define IXAudio2SourceVoice_GetOutputFilterParameters IXAudio2Voice_GetOutputFilterParameters
|
||||
#define IXAudio2SourceVoice_SetVolume IXAudio2Voice_SetVolume
|
||||
#define IXAudio2SourceVoice_GetVolume IXAudio2Voice_GetVolume
|
||||
#define IXAudio2SourceVoice_SetChannelVolumes IXAudio2Voice_SetChannelVolumes
|
||||
#define IXAudio2SourceVoice_GetChannelVolumes IXAudio2Voice_GetChannelVolumes
|
||||
#define IXAudio2SourceVoice_SetOutputMatrix IXAudio2Voice_SetOutputMatrix
|
||||
#define IXAudio2SourceVoice_GetOutputMatrix IXAudio2Voice_GetOutputMatrix
|
||||
#define IXAudio2SourceVoice_DestroyVoice IXAudio2Voice_DestroyVoice
|
||||
#define IXAudio2SourceVoice_Start(This,Flags,OperationSet) ((This)->lpVtbl->Start(This,Flags,OperationSet))
|
||||
#define IXAudio2SourceVoice_Stop(This,Flags,OperationSet) ((This)->lpVtbl->Stop(This,Flags,OperationSet))
|
||||
#define IXAudio2SourceVoice_SubmitSourceBuffer(This,pBuffer,pBufferWMA) ((This)->lpVtbl->SubmitSourceBuffer(This,pBuffer,pBufferWMA))
|
||||
#define IXAudio2SourceVoice_FlushSourceBuffers(This) ((This)->lpVtbl->FlushSourceBuffers(This))
|
||||
#define IXAudio2SourceVoice_Discontinuity(This) ((This)->lpVtbl->Discontinuity(This))
|
||||
#define IXAudio2SourceVoice_ExitLoop(This,OperationSet) ((This)->lpVtbl->ExitLoop(This,OperationSet))
|
||||
#define IXAudio2SourceVoice_GetState(This,pVoiceState) ((This)->lpVtbl->GetState(This,pVoiceState))
|
||||
#define IXAudio2SourceVoice_SetFrequencyRatio(This,Ratio,OperationSet) ((This)->lpVtbl->SetFrequencyRatio(This,Ratio,OperationSet))
|
||||
#define IXAudio2SourceVoice_GetFrequencyRatio(This,pRatio) ((This)->lpVtbl->GetFrequencyRatio(This,pRatio))
|
||||
#define IXAudio2SourceVoice_SetSourceSampleRate(This,NewSourceSampleRate) ((This)->lpVtbl->SetSourceSampleRate(This,NewSourceSampleRate))
|
||||
|
||||
// IXAudio2SubmixVoice
|
||||
#define IXAudio2SubmixVoice_GetVoiceDetails IXAudio2Voice_GetVoiceDetails
|
||||
#define IXAudio2SubmixVoice_SetOutputVoices IXAudio2Voice_SetOutputVoices
|
||||
#define IXAudio2SubmixVoice_SetEffectChain IXAudio2Voice_SetEffectChain
|
||||
#define IXAudio2SubmixVoice_EnableEffect IXAudio2Voice_EnableEffect
|
||||
#define IXAudio2SubmixVoice_DisableEffect IXAudio2Voice_DisableEffect
|
||||
#define IXAudio2SubmixVoice_GetEffectState IXAudio2Voice_GetEffectState
|
||||
#define IXAudio2SubmixVoice_SetEffectParameters IXAudio2Voice_SetEffectParameters
|
||||
#define IXAudio2SubmixVoice_GetEffectParameters IXAudio2Voice_GetEffectParameters
|
||||
#define IXAudio2SubmixVoice_SetFilterParameters IXAudio2Voice_SetFilterParameters
|
||||
#define IXAudio2SubmixVoice_GetFilterParameters IXAudio2Voice_GetFilterParameters
|
||||
#define IXAudio2SubmixVoice_SetOutputFilterParameters IXAudio2Voice_SetOutputFilterParameters
|
||||
#define IXAudio2SubmixVoice_GetOutputFilterParameters IXAudio2Voice_GetOutputFilterParameters
|
||||
#define IXAudio2SubmixVoice_SetVolume IXAudio2Voice_SetVolume
|
||||
#define IXAudio2SubmixVoice_GetVolume IXAudio2Voice_GetVolume
|
||||
#define IXAudio2SubmixVoice_SetChannelVolumes IXAudio2Voice_SetChannelVolumes
|
||||
#define IXAudio2SubmixVoice_GetChannelVolumes IXAudio2Voice_GetChannelVolumes
|
||||
#define IXAudio2SubmixVoice_SetOutputMatrix IXAudio2Voice_SetOutputMatrix
|
||||
#define IXAudio2SubmixVoice_GetOutputMatrix IXAudio2Voice_GetOutputMatrix
|
||||
#define IXAudio2SubmixVoice_DestroyVoice IXAudio2Voice_DestroyVoice
|
||||
|
||||
// IXAudio2MasteringVoice
|
||||
#define IXAudio2MasteringVoice_GetVoiceDetails IXAudio2Voice_GetVoiceDetails
|
||||
#define IXAudio2MasteringVoice_SetOutputVoices IXAudio2Voice_SetOutputVoices
|
||||
#define IXAudio2MasteringVoice_SetEffectChain IXAudio2Voice_SetEffectChain
|
||||
#define IXAudio2MasteringVoice_EnableEffect IXAudio2Voice_EnableEffect
|
||||
#define IXAudio2MasteringVoice_DisableEffect IXAudio2Voice_DisableEffect
|
||||
#define IXAudio2MasteringVoice_GetEffectState IXAudio2Voice_GetEffectState
|
||||
#define IXAudio2MasteringVoice_SetEffectParameters IXAudio2Voice_SetEffectParameters
|
||||
#define IXAudio2MasteringVoice_GetEffectParameters IXAudio2Voice_GetEffectParameters
|
||||
#define IXAudio2MasteringVoice_SetFilterParameters IXAudio2Voice_SetFilterParameters
|
||||
#define IXAudio2MasteringVoice_GetFilterParameters IXAudio2Voice_GetFilterParameters
|
||||
#define IXAudio2MasteringVoice_SetOutputFilterParameters IXAudio2Voice_SetOutputFilterParameters
|
||||
#define IXAudio2MasteringVoice_GetOutputFilterParameters IXAudio2Voice_GetOutputFilterParameters
|
||||
#define IXAudio2MasteringVoice_SetVolume IXAudio2Voice_SetVolume
|
||||
#define IXAudio2MasteringVoice_GetVolume IXAudio2Voice_GetVolume
|
||||
#define IXAudio2MasteringVoice_SetChannelVolumes IXAudio2Voice_SetChannelVolumes
|
||||
#define IXAudio2MasteringVoice_GetChannelVolumes IXAudio2Voice_GetChannelVolumes
|
||||
#define IXAudio2MasteringVoice_SetOutputMatrix IXAudio2Voice_SetOutputMatrix
|
||||
#define IXAudio2MasteringVoice_GetOutputMatrix IXAudio2Voice_GetOutputMatrix
|
||||
#define IXAudio2MasteringVoice_DestroyVoice IXAudio2Voice_DestroyVoice
|
||||
|
||||
STDAPI XAudio2Create(__deref_out IXAudio2** ppXAudio2, UINT32 Flags X2DEFAULT(0),
|
||||
XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR));
|
||||
|
||||
|
||||
// Undo the #pragma pack(push, 1) directive at the top of this file
|
||||
#pragma pack(pop)
|
||||
|
||||
#endif // #ifndef GUID_DEFS_ONLY
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
/* 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 <xtl.h>
|
||||
#include "xaudio-c/xaudio.h"
|
||||
#include "../general.h"
|
||||
|
||||
#define MAX_BUFFERS 16
|
||||
#define MAX_BUFFERS_MASK 15
|
||||
|
||||
struct XAudio : public IXAudio2VoiceCallback
|
||||
{
|
||||
XAudio() :
|
||||
buf(0), pXAudio2(0), pMasteringVoice(0),
|
||||
pSourceVoice(0), nonblock(false), bufsize(0),
|
||||
bufptr(0), write_buffer(0), buffers(0), hEvent(0)
|
||||
{}
|
||||
|
||||
~XAudio()
|
||||
{
|
||||
if (pSourceVoice)
|
||||
{
|
||||
pSourceVoice->Stop(0, XAUDIO2_COMMIT_NOW);
|
||||
pSourceVoice->DestroyVoice();
|
||||
}
|
||||
|
||||
if (pMasteringVoice)
|
||||
pMasteringVoice->DestroyVoice();
|
||||
|
||||
if (pXAudio2)
|
||||
pXAudio2->Release();
|
||||
|
||||
if (hEvent)
|
||||
CloseHandle(hEvent);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
bool init(unsigned rate, unsigned latency)
|
||||
{
|
||||
size_t bufsize_ = latency * rate / 1000;
|
||||
size_t size = bufsize_ * 2 * sizeof(int16_t);
|
||||
|
||||
SSNES_LOG("XAudio2: Requesting %d ms latency, using %d ms latency.\n", latency, (int)bufsize_ * 1000 / rate);
|
||||
|
||||
if (FAILED(XAudio2Create(&pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR)))
|
||||
return false;
|
||||
|
||||
if (FAILED(pXAudio2->CreateMasteringVoice(&pMasteringVoice, 2, rate, 0, 0, NULL)))
|
||||
return false;
|
||||
|
||||
WAVEFORMATEX wfx = {0};
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = 2;
|
||||
wfx.nSamplesPerSec = rate;
|
||||
wfx.nBlockAlign = 2 * sizeof(int16_t);
|
||||
wfx.wBitsPerSample = sizeof(int16_t) * 8;
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
wfx.cbSize = 0;
|
||||
|
||||
if (FAILED(pXAudio2->CreateSourceVoice(&pSourceVoice, &wfx,
|
||||
XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, 0, 0)))
|
||||
return false;
|
||||
|
||||
hEvent = CreateEvent(0, FALSE, FALSE, 0);
|
||||
|
||||
bufsize = size / MAX_BUFFERS;
|
||||
buf = (uint8_t*)malloc(bufsize * MAX_BUFFERS);
|
||||
memset(buf, 0, bufsize * MAX_BUFFERS);
|
||||
|
||||
if (FAILED(pSourceVoice->Start(0, XAUDIO2_COMMIT_NOW)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's really 16-bit, but we have to byteswap.
|
||||
size_t write(const uint8_t *buffer, size_t size)
|
||||
{
|
||||
if (nonblock)
|
||||
{
|
||||
size_t avail = bufsize * (MAX_BUFFERS - buffers - 1);
|
||||
if (avail == 0)
|
||||
return 0;
|
||||
if (avail < size)
|
||||
size = avail;
|
||||
}
|
||||
|
||||
unsigned bytes = size;
|
||||
while (bytes)
|
||||
{
|
||||
unsigned need = min(bytes, bufsize - bufptr);
|
||||
uint8_t *base_write = buf + write_buffer * bufsize + bufptr;
|
||||
memcpy(base_write, buffer, need);
|
||||
|
||||
bufptr += need;
|
||||
buffer += need;
|
||||
bytes -= 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;
|
||||
|
||||
if (FAILED(pSourceVoice->SubmitSourceBuffer(&xa2buffer, NULL)))
|
||||
return 0;
|
||||
|
||||
InterlockedIncrement(&buffers);
|
||||
bufptr = 0;
|
||||
write_buffer = (write_buffer + 1) & MAX_BUFFERS_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
STDMETHOD_(void, OnBufferStart) (void *) {}
|
||||
STDMETHOD_(void, OnBufferEnd) (void *)
|
||||
{
|
||||
InterlockedDecrement(&buffers);
|
||||
SetEvent(hEvent);
|
||||
}
|
||||
STDMETHOD_(void, OnLoopEnd) (void *) {}
|
||||
STDMETHOD_(void, OnStreamEnd) () {}
|
||||
STDMETHOD_(void, OnVoiceError) (void *, HRESULT) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
|
||||
|
||||
uint8_t *buf;
|
||||
IXAudio2 *pXAudio2;
|
||||
IXAudio2MasteringVoice *pMasteringVoice;
|
||||
IXAudio2SourceVoice *pSourceVoice;
|
||||
bool nonblock;
|
||||
unsigned bufsize;
|
||||
unsigned bufptr;
|
||||
unsigned write_buffer;
|
||||
volatile long buffers;
|
||||
HANDLE hEvent;
|
||||
};
|
||||
|
||||
static void *xa_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
if (latency < 8)
|
||||
latency = 8; // Do not allow shenanigans.
|
||||
|
||||
XAudio *xa = new XAudio;
|
||||
if (!xa->init(rate, latency))
|
||||
goto error;
|
||||
|
||||
return xa;
|
||||
|
||||
error:
|
||||
SSNES_ERR("Failed to init XAudio2.\n");
|
||||
delete xa;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t xa_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
XAudio *xa = (XAudio*)data;
|
||||
size_t ret = xa->write((const uint8_t*)buf, size);
|
||||
|
||||
if (ret == 0 && !xa->nonblock)
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool xa_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void xa_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
XAudio *xa = (XAudio*)data;
|
||||
xa->nonblock = state;
|
||||
}
|
||||
|
||||
static bool xa_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void xa_free(void *data)
|
||||
{
|
||||
XAudio *xa = (XAudio*)data;
|
||||
if (xa)
|
||||
delete xa;
|
||||
}
|
||||
|
||||
static bool xa_use_float(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return false;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_xdk360 = {
|
||||
xa_init,
|
||||
xa_write,
|
||||
xa_stop,
|
||||
xa_start,
|
||||
xa_set_nonblock_state,
|
||||
xa_free,
|
||||
xa_use_float,
|
||||
"xdk360"
|
||||
};
|
||||
@@ -0,0 +1,189 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <xtl.h>
|
||||
#include "../driver.h"
|
||||
#include "../general.h"
|
||||
#include "../libsnes.hpp"
|
||||
#include "../console/console_ext_input.h"
|
||||
#include "xdk360_input.h"
|
||||
#include "xdk360_video_general.h"
|
||||
#include "shared.h"
|
||||
|
||||
static uint64_t state[4];
|
||||
static unsigned pads_connected;
|
||||
|
||||
static void xdk360_input_poll(void *data)
|
||||
{
|
||||
(void)data;
|
||||
|
||||
pads_connected = 0;
|
||||
|
||||
for (unsigned i = 0; i < 4; i++)
|
||||
{
|
||||
XINPUT_STATE state_tmp;
|
||||
unsigned long retval = XInputGetState(i, &state_tmp);
|
||||
pads_connected += (retval == ERROR_DEVICE_NOT_CONNECTED) ? 0 : 1;
|
||||
state[i] = state_tmp.Gamepad.wButtons;
|
||||
state[i] |= ((state_tmp.Gamepad.sThumbLX < -DEADZONE)) << 16;
|
||||
state[i] |= ((state_tmp.Gamepad.sThumbLX > DEADZONE)) << 17;
|
||||
state[i] |= ((state_tmp.Gamepad.sThumbLY > DEADZONE)) << 18;
|
||||
state[i] |= ((state_tmp.Gamepad.sThumbLY < -DEADZONE)) << 19;
|
||||
state[i] |= ((state_tmp.Gamepad.sThumbRX < -DEADZONE)) << 20;
|
||||
state[i] |= ((state_tmp.Gamepad.sThumbRX > DEADZONE)) << 21;
|
||||
state[i] |= ((state_tmp.Gamepad.sThumbRY > DEADZONE)) << 22;
|
||||
state[i] |= ((state_tmp.Gamepad.sThumbRY < -DEADZONE)) << 23;
|
||||
state[i] |= ((state_tmp.Gamepad.bLeftTrigger > 128 ? 1 : 0)) << 24;
|
||||
state[i] |= ((state_tmp.Gamepad.bRightTrigger > 128 ? 1 : 0)) << 25;
|
||||
}
|
||||
}
|
||||
|
||||
static int16_t xdk360_input_state(void *data, const struct snes_keybind **binds,
|
||||
bool port, unsigned device,
|
||||
unsigned index, unsigned id)
|
||||
{
|
||||
(void)data;
|
||||
unsigned player;
|
||||
uint64_t button;
|
||||
|
||||
player = 0;
|
||||
|
||||
if (device != SNES_DEVICE_JOYPAD)
|
||||
return 0;
|
||||
|
||||
if (port == SNES_PORT_2)
|
||||
{
|
||||
if(pads_connected < 2)
|
||||
return 0;
|
||||
|
||||
player = 1;
|
||||
|
||||
if (device == SNES_DEVICE_MULTITAP)
|
||||
player += index;
|
||||
}
|
||||
|
||||
button = binds[player][id].joykey;
|
||||
|
||||
return (state[player] & button) ? 1 : 0;
|
||||
}
|
||||
|
||||
static void xdk360_free_input(void *data)
|
||||
{
|
||||
(void)data;
|
||||
}
|
||||
|
||||
static void* xdk360_input_initialize(void)
|
||||
{
|
||||
return (void*)-1;
|
||||
}
|
||||
|
||||
void xdk360_input_init(void)
|
||||
{
|
||||
for(unsigned i = 0; i < 4; i++)
|
||||
xdk360_input_map_dpad_to_stick(g_settings.input.dpad_emulation[i], i);
|
||||
}
|
||||
|
||||
void xdk360_input_map_dpad_to_stick(uint32_t map_dpad_enum, uint32_t controller_id)
|
||||
{
|
||||
switch(map_dpad_enum)
|
||||
{
|
||||
case DPAD_EMULATION_NONE:
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_UP].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_UP];
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_DOWN].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_DOWN];
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_LEFT].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_LEFT];
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_RIGHT].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_JOYPAD_RIGHT];
|
||||
break;
|
||||
case DPAD_EMULATION_LSTICK:
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_UP].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_LSTICK_UP_DPAD];
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_DOWN].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_LSTICK_DOWN_DPAD];
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_LEFT].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_LSTICK_LEFT_DPAD];
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_RIGHT].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_LSTICK_RIGHT_DPAD];
|
||||
break;
|
||||
case DPAD_EMULATION_RSTICK:
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_UP].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_RSTICK_UP_DPAD];
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_DOWN].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_RSTICK_DOWN_DPAD];
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_LEFT].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_RSTICK_LEFT_DPAD];
|
||||
g_settings.input.binds[controller_id][SNES_DEVICE_ID_JOYPAD_RIGHT].joykey = ssnes_platform_keybind_lut[XDK360_DEVICE_ID_RSTICK_RIGHT_DPAD];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static bool xdk360_key_pressed(void *data, int key)
|
||||
{
|
||||
(void)data;
|
||||
XINPUT_STATE state;
|
||||
bool retval;
|
||||
|
||||
XInputGetState(0, &state);
|
||||
retval = false;
|
||||
|
||||
switch(key)
|
||||
{
|
||||
case SSNES_FAST_FORWARD_HOLD_KEY:
|
||||
return ((state.Gamepad.sThumbRY < -DEADZONE) && !(state.Gamepad.bRightTrigger > 128));
|
||||
case SSNES_LOAD_STATE_KEY:
|
||||
return ((state.Gamepad.sThumbRY > DEADZONE) && (state.Gamepad.bRightTrigger > 128));
|
||||
case SSNES_SAVE_STATE_KEY:
|
||||
return ((state.Gamepad.sThumbRY < -DEADZONE) && (state.Gamepad.bRightTrigger > 128));
|
||||
case SSNES_STATE_SLOT_PLUS:
|
||||
return ((state.Gamepad.sThumbRX > DEADZONE) && (state.Gamepad.bRightTrigger > 128));
|
||||
case SSNES_STATE_SLOT_MINUS:
|
||||
return ((state.Gamepad.sThumbRX < -DEADZONE) && (state.Gamepad.bRightTrigger > 128));
|
||||
case SSNES_FRAMEADVANCE:
|
||||
if(g_console.frame_advance_enable)
|
||||
{
|
||||
g_console.menu_enable = true;
|
||||
g_console.ingame_menu_enable = true;
|
||||
g_console.mode_switch = MODE_MENU;
|
||||
}
|
||||
return false;
|
||||
case SSNES_REWIND:
|
||||
return ((state.Gamepad.sThumbRY > DEADZONE) && !(state.Gamepad.bRightTrigger > 128));
|
||||
case SSNES_QUIT_KEY:
|
||||
if(IS_TIMER_EXPIRED())
|
||||
{
|
||||
uint32_t left_thumb_pressed = (state.Gamepad.wButtons & XINPUT_GAMEPAD_LEFT_THUMB);
|
||||
uint32_t right_thumb_pressed = (state.Gamepad.wButtons & XINPUT_GAMEPAD_RIGHT_THUMB);
|
||||
|
||||
g_console.menu_enable = right_thumb_pressed && left_thumb_pressed && IS_TIMER_EXPIRED();
|
||||
g_console.ingame_menu_enable = right_thumb_pressed && !left_thumb_pressed;
|
||||
|
||||
if(g_console.menu_enable || (g_console.ingame_menu_enable
|
||||
&& !g_console.menu_enable))
|
||||
{
|
||||
g_console.mode_switch = MODE_MENU;
|
||||
SET_TIMER_EXPIRATION(30);
|
||||
retval = g_console.menu_enable;
|
||||
}
|
||||
|
||||
retval = g_console.ingame_menu_enable ? g_console.ingame_menu_enable : g_console.menu_enable;
|
||||
}
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
const input_driver_t input_xdk360 = {
|
||||
xdk360_input_initialize,
|
||||
xdk360_input_poll,
|
||||
xdk360_input_state,
|
||||
xdk360_key_pressed,
|
||||
xdk360_free_input,
|
||||
"xdk360"};
|
||||
@@ -0,0 +1,37 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _XDK360_INPUT_H
|
||||
#define _XDK360_INPUT_H
|
||||
|
||||
#define XINPUT_GAMEPAD_LSTICK_LEFT_MASK (65536)
|
||||
#define XINPUT_GAMEPAD_LSTICK_RIGHT_MASK (131072)
|
||||
#define XINPUT_GAMEPAD_LSTICK_UP_MASK (262144)
|
||||
#define XINPUT_GAMEPAD_LSTICK_DOWN_MASK (524288)
|
||||
#define XINPUT_GAMEPAD_RSTICK_LEFT_MASK (1048576)
|
||||
#define XINPUT_GAMEPAD_RSTICK_RIGHT_MASK (2097152)
|
||||
#define XINPUT_GAMEPAD_RSTICK_UP_MASK (4194304)
|
||||
#define XINPUT_GAMEPAD_RSTICK_DOWN_MASK (8388608)
|
||||
#define XINPUT_GAMEPAD_LEFT_TRIGGER (16777216)
|
||||
#define XINPUT_GAMEPAD_RIGHT_TRIGGER (33554432)
|
||||
#define DEADZONE (16000)
|
||||
|
||||
extern void xdk360_input_init(void);
|
||||
extern void xdk360_input_map_dpad_to_stick(uint32_t map_dpad_enum, uint32_t controller_id);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,680 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <xtl.h>
|
||||
#include <xgraphics.h>
|
||||
#include "../driver.h"
|
||||
#include "xdk360_video.h"
|
||||
#include "xdk360_video_resources.h"
|
||||
#include "../general.h"
|
||||
#include "../message.h"
|
||||
#include "shared.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
static bool g_quitting;
|
||||
static bool g_first_msg;
|
||||
unsigned g_frame_count;
|
||||
void *g_d3d;
|
||||
|
||||
struct hlsl_program_t
|
||||
{
|
||||
D3DXHANDLE vid_size_f;
|
||||
D3DXHANDLE tex_size_f;
|
||||
D3DXHANDLE out_size_f;
|
||||
D3DXHANDLE vid_size_v;
|
||||
D3DXHANDLE tex_size_v;
|
||||
D3DXHANDLE out_size_v;
|
||||
XMMATRIX modelViewProj;
|
||||
} hlsl_program;
|
||||
|
||||
struct XPR_HEADER
|
||||
{
|
||||
unsigned long dwMagic;
|
||||
unsigned long dwHeaderSize;
|
||||
unsigned long dwDataSize;
|
||||
};
|
||||
|
||||
#define XPR2_MAGIC_VALUE (0x58505232)
|
||||
|
||||
PackedResource::PackedResource()
|
||||
{
|
||||
m_pSysMemData = NULL;
|
||||
m_dwSysMemDataSize = 0L;
|
||||
m_pVidMemData = NULL;
|
||||
m_dwVidMemDataSize = 0L;
|
||||
m_pResourceTags = NULL;
|
||||
m_dwNumResourceTags = 0L;
|
||||
m_bInitialized = FALSE;
|
||||
}
|
||||
|
||||
PackedResource::~PackedResource()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
void * PackedResource::GetData( const char * strName ) const
|
||||
{
|
||||
if( m_pResourceTags == NULL || strName == NULL )
|
||||
return NULL;
|
||||
|
||||
for( unsigned long i = 0; i < m_dwNumResourceTags; i++ )
|
||||
{
|
||||
if( !_stricmp( strName, m_pResourceTags[i].strName ) )
|
||||
return &m_pSysMemData[m_pResourceTags[i].dwOffset];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HRESULT PackedResource::Create( const char * strFilename )
|
||||
{
|
||||
unsigned long dwNumBytesRead;
|
||||
HANDLE hFile = CreateFile( strFilename, GENERIC_READ, FILE_SHARE_READ, NULL,
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_READONLY, NULL );
|
||||
if( hFile == INVALID_HANDLE_VALUE )
|
||||
{
|
||||
SSNES_ERR( "File <%s> not found.\n", strFilename );
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Read in and verify the XPR magic header
|
||||
XPR_HEADER xprh;
|
||||
if( !ReadFile( hFile, &xprh, sizeof( XPR_HEADER ), &dwNumBytesRead, NULL ) )
|
||||
{
|
||||
SSNES_ERR( "Error reading XPR header in file <%s>.\n", strFilename );
|
||||
CloseHandle( hFile );
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
if( xprh.dwMagic != XPR2_MAGIC_VALUE )
|
||||
{
|
||||
SSNES_ERR( "Invalid Xbox Packed Resource (.xpr) file: Magic = 0x%08lx.\n", xprh.dwMagic );
|
||||
CloseHandle( hFile );
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Compute memory requirements
|
||||
m_dwSysMemDataSize = xprh.dwHeaderSize;
|
||||
m_dwVidMemDataSize = xprh.dwDataSize;
|
||||
|
||||
// Allocate memory
|
||||
m_pSysMemData = (BYTE*)malloc(m_dwSysMemDataSize);
|
||||
if( m_pSysMemData == NULL )
|
||||
{
|
||||
SSNES_ERR( "Could not allocate system memory.\n" );
|
||||
m_dwSysMemDataSize = 0;
|
||||
return E_FAIL;
|
||||
}
|
||||
m_pVidMemData = ( BYTE* )XMemAlloc( m_dwVidMemDataSize, MAKE_XALLOC_ATTRIBUTES( 0, 0, 0, 0, eXALLOCAllocatorId_GameMax,
|
||||
XALLOC_PHYSICAL_ALIGNMENT_4K, XALLOC_MEMPROTECT_WRITECOMBINE, 0, XALLOC_MEMTYPE_PHYSICAL ) );
|
||||
|
||||
if( m_pVidMemData == NULL )
|
||||
{
|
||||
SSNES_ERR( "Could not allocate physical memory.\n" );
|
||||
m_dwSysMemDataSize = 0;
|
||||
m_dwVidMemDataSize = 0;
|
||||
free(m_pSysMemData);
|
||||
m_pSysMemData = NULL;
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Read in the data from the file
|
||||
if( !ReadFile( hFile, m_pSysMemData, m_dwSysMemDataSize, &dwNumBytesRead, NULL ) ||
|
||||
!ReadFile( hFile, m_pVidMemData, m_dwVidMemDataSize, &dwNumBytesRead, NULL ) )
|
||||
{
|
||||
SSNES_ERR( "Unable to read Xbox Packed Resource (.xpr) file.\n" );
|
||||
CloseHandle( hFile );
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
// Done with the file
|
||||
CloseHandle( hFile );
|
||||
|
||||
// Extract resource table from the header data
|
||||
m_dwNumResourceTags = *( unsigned long * )( m_pSysMemData + 0 );
|
||||
m_pResourceTags = ( RESOURCE* )( m_pSysMemData + 4 );
|
||||
|
||||
// Patch up the resources
|
||||
for( unsigned long i = 0; i < m_dwNumResourceTags; i++ )
|
||||
{
|
||||
m_pResourceTags[i].strName = ( char * )( m_pSysMemData + ( unsigned long )m_pResourceTags[i].strName );
|
||||
|
||||
// Fixup the texture memory
|
||||
if( ( m_pResourceTags[i].dwType & 0xffff0000 ) == ( RESOURCETYPE_TEXTURE & 0xffff0000 ) )
|
||||
{
|
||||
D3DTexture* pTexture = ( D3DTexture* )&m_pSysMemData[m_pResourceTags[i].dwOffset];
|
||||
// Adjust Base address according to where memory was allocated
|
||||
XGOffsetBaseTextureAddress( pTexture, m_pVidMemData, m_pVidMemData );
|
||||
}
|
||||
}
|
||||
|
||||
m_bInitialized = TRUE;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void PackedResource::Destroy()
|
||||
{
|
||||
delete[] m_pSysMemData;
|
||||
m_pSysMemData = NULL;
|
||||
m_dwSysMemDataSize = 0L;
|
||||
|
||||
if( m_pVidMemData != NULL )
|
||||
XMemFree( m_pVidMemData, MAKE_XALLOC_ATTRIBUTES( 0, 0, 0, 0, eXALLOCAllocatorId_GameMax,
|
||||
0, 0, 0, XALLOC_MEMTYPE_PHYSICAL ) );
|
||||
|
||||
m_pVidMemData = NULL;
|
||||
m_dwVidMemDataSize = 0L;
|
||||
|
||||
m_pResourceTags = NULL;
|
||||
m_dwNumResourceTags = 0L;
|
||||
|
||||
m_bInitialized = FALSE;
|
||||
}
|
||||
|
||||
static void xdk360_gfx_free(void * data)
|
||||
{
|
||||
if (g_d3d)
|
||||
return;
|
||||
|
||||
xdk360_video_t *vid = (xdk360_video_t*)data;
|
||||
|
||||
if (!vid)
|
||||
return;
|
||||
|
||||
D3DResource_Release((D3DResource *)vid->lpTexture);
|
||||
D3DResource_Release((D3DResource *)vid->vertex_buf);
|
||||
D3DResource_Release((D3DResource *)vid->pVertexDecl);
|
||||
D3DResource_Release((D3DResource *)vid->pPixelShader);
|
||||
D3DResource_Release((D3DResource *)vid->pVertexShader);
|
||||
D3DDevice_Release(vid->xdk360_render_device);
|
||||
Direct3D_Release();
|
||||
|
||||
free(vid);
|
||||
}
|
||||
|
||||
static void set_viewport(bool force_full)
|
||||
{
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
D3DDevice_Clear(vid->xdk360_render_device, 0, NULL, D3DCLEAR_TARGET,
|
||||
0xff000000, 1.0f, 0, FALSE);
|
||||
|
||||
int width = vid->video_mode.fIsHiDef ? 1280 : 640;
|
||||
int height = vid->video_mode.fIsHiDef ? 720 : 480;
|
||||
int m_viewport_x_temp, m_viewport_y_temp, m_viewport_width_temp, m_viewport_height_temp;
|
||||
float m_zNear, m_zFar;
|
||||
|
||||
m_viewport_x_temp = 0;
|
||||
m_viewport_y_temp = 0;
|
||||
m_viewport_width_temp = width;
|
||||
m_viewport_height_temp = height;
|
||||
|
||||
m_zNear = 0.0f;
|
||||
m_zFar = 1.0f;
|
||||
|
||||
if (!force_full)
|
||||
{
|
||||
float desired_aspect = g_settings.video.aspect_ratio;
|
||||
float device_aspect = (float)width / height;
|
||||
float delta;
|
||||
|
||||
// If the aspect ratios of screen and desired aspect ratio are sufficiently equal (floating point stuff),
|
||||
//if(g_console.aspect_ratio_index == ASPECT_RATIO_CUSTOM)
|
||||
//{
|
||||
// m_viewport_x_temp = g_console.custom_viewport_x;
|
||||
// m_viewport_y_temp = g_console.custom_viewport_y;
|
||||
// m_viewport_width_temp = g_console.custom_viewport_width;
|
||||
// m_viewport_height_temp = g_console.custom_viewport_height;
|
||||
//}
|
||||
if (device_aspect > desired_aspect)
|
||||
{
|
||||
delta = (desired_aspect / device_aspect - 1.0) / 2.0 + 0.5;
|
||||
m_viewport_x_temp = (int)(width * (0.5 - delta));
|
||||
m_viewport_width_temp = (int)(2.0 * width * delta);
|
||||
width = (unsigned)(2.0 * width * delta);
|
||||
}
|
||||
else
|
||||
{
|
||||
delta = (device_aspect / desired_aspect - 1.0) / 2.0 + 0.5;
|
||||
m_viewport_y_temp = (int)(height * (0.5 - delta));
|
||||
m_viewport_height_temp = (int)(2.0 * height * delta);
|
||||
height = (unsigned)(2.0 * height * delta);
|
||||
}
|
||||
}
|
||||
|
||||
D3DVIEWPORT9 vp = {0};
|
||||
vp.Width = m_viewport_width_temp;
|
||||
vp.Height = m_viewport_height_temp;
|
||||
vp.X = m_viewport_x_temp;
|
||||
vp.Y = m_viewport_y_temp;
|
||||
vp.MinZ = m_zNear;
|
||||
vp.MaxZ = m_zFar;
|
||||
D3DDevice_SetViewport(vid->xdk360_render_device, &vp);
|
||||
|
||||
//if(gl->overscan_enable && !force_full)
|
||||
//{
|
||||
// m_left = -gl->overscan_amount/2;
|
||||
// m_right = 1 + gl->overscan_amount/2;
|
||||
// m_bottom = -gl->overscan_amount/2;
|
||||
//}
|
||||
}
|
||||
|
||||
static void xdk360_set_orientation(void * data, uint32_t orientation)
|
||||
{
|
||||
(void)data;
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
FLOAT angle;
|
||||
|
||||
switch(orientation)
|
||||
{
|
||||
case ORIENTATION_NORMAL:
|
||||
angle = M_PI * 0 / 180;
|
||||
break;
|
||||
case ORIENTATION_VERTICAL:
|
||||
angle = M_PI * 270 / 180;
|
||||
break;
|
||||
case ORIENTATION_FLIPPED:
|
||||
angle = M_PI * 180 / 180;
|
||||
break;
|
||||
case ORIENTATION_FLIPPED_ROTATED:
|
||||
angle = M_PI * 90 / 180;
|
||||
break;
|
||||
}
|
||||
hlsl_program.modelViewProj = XMMatrixRotationZ(angle);
|
||||
}
|
||||
|
||||
void xdk360_set_aspect_ratio(uint32_t aspectratio_index)
|
||||
{
|
||||
switch(aspectratio_index)
|
||||
{
|
||||
case ASPECT_RATIO_4_3:
|
||||
g_settings.video.aspect_ratio = 1.33333333333;
|
||||
strlcpy(g_console.aspect_ratio_name, "4:3", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_5_4:
|
||||
g_settings.video.aspect_ratio = 1.25;
|
||||
strlcpy(g_console.aspect_ratio_name, "5:4", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_8_7:
|
||||
g_settings.video.aspect_ratio = 1.14287142857;
|
||||
strlcpy(g_console.aspect_ratio_name, "8:7", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_16_9:
|
||||
g_settings.video.aspect_ratio = 1.777778;
|
||||
strlcpy(g_console.aspect_ratio_name, "16:9", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_16_10:
|
||||
g_settings.video.aspect_ratio = 1.6;
|
||||
strlcpy(g_console.aspect_ratio_name, "16:10", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_16_15:
|
||||
g_settings.video.aspect_ratio = 3.2;
|
||||
strlcpy(g_console.aspect_ratio_name, "16:15", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_19_14:
|
||||
g_settings.video.aspect_ratio = 1.36;
|
||||
strlcpy(g_console.aspect_ratio_name, "19:14", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_2_1:
|
||||
g_settings.video.aspect_ratio = 2.0;
|
||||
strlcpy(g_console.aspect_ratio_name, "2:1", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_3_2:
|
||||
g_settings.video.aspect_ratio = 1.5;
|
||||
strlcpy(g_console.aspect_ratio_name, "3:2", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_3_4:
|
||||
g_settings.video.aspect_ratio = 1.5;
|
||||
strlcpy(g_console.aspect_ratio_name, "3:4", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_1_1:
|
||||
g_settings.video.aspect_ratio = 1.0;
|
||||
strlcpy(g_console.aspect_ratio_name, "1:1", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_AUTO:
|
||||
strlcpy(g_console.aspect_ratio_name, "(Auto)", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
case ASPECT_RATIO_CUSTOM:
|
||||
strlcpy(g_console.aspect_ratio_name, "(Custom)", sizeof(g_console.aspect_ratio_name));
|
||||
break;
|
||||
}
|
||||
g_settings.video.force_aspect = false;
|
||||
set_viewport(false);
|
||||
}
|
||||
|
||||
static void *xdk360_gfx_init(const video_info_t *video, const input_driver_t **input, void **input_data)
|
||||
{
|
||||
HRESULT ret;
|
||||
if (g_d3d)
|
||||
return g_d3d;
|
||||
|
||||
xdk360_video_t *vid = (xdk360_video_t*)calloc(1, sizeof(xdk360_video_t));
|
||||
if (!vid)
|
||||
return NULL;
|
||||
|
||||
vid->xdk360_device = Direct3DCreate9(D3D_SDK_VERSION);
|
||||
if (!vid->xdk360_device)
|
||||
{
|
||||
free(vid);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Get video settings
|
||||
|
||||
memset(&vid->video_mode, 0, sizeof(vid->video_mode));
|
||||
|
||||
XGetVideoMode(&vid->video_mode);
|
||||
|
||||
memset(&vid->d3dpp, 0, sizeof(vid->d3dpp));
|
||||
|
||||
// no letterboxing in 4:3 mode (if widescreen is
|
||||
// unsupported
|
||||
if(!vid->video_mode.fIsWideScreen)
|
||||
vid->d3dpp.Flags |= D3DPRESENTFLAG_NO_LETTERBOX;
|
||||
|
||||
vid->d3dpp.BackBufferWidth = vid->video_mode.fIsHiDef ? 1280 : 640;
|
||||
vid->d3dpp.BackBufferHeight = vid->video_mode.fIsHiDef ? 720 : 480;
|
||||
vid->d3dpp.BackBufferFormat = g_console.gamma_correction_enable ? (D3DFORMAT)MAKESRGBFMT(D3DFMT_A8R8G8B8) : D3DFMT_A8R8G8B8;
|
||||
vid->d3dpp.FrontBufferFormat = g_console.gamma_correction_enable ? (D3DFORMAT)MAKESRGBFMT(D3DFMT_LE_X8R8G8B8) : D3DFMT_LE_X8R8G8B8;
|
||||
vid->d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
|
||||
vid->d3dpp.MultiSampleQuality = 0;
|
||||
vid->d3dpp.BackBufferCount = 2;
|
||||
vid->d3dpp.EnableAutoDepthStencil = FALSE;
|
||||
vid->d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
|
||||
vid->d3dpp.PresentationInterval = video->vsync ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE;
|
||||
|
||||
// D3DCREATE_HARDWARE_VERTEXPROCESSING is ignored on 360
|
||||
ret = Direct3D_CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, NULL, D3DCREATE_HARDWARE_VERTEXPROCESSING, &vid->d3dpp, &vid->xdk360_render_device);
|
||||
|
||||
ID3DXBuffer* pShaderCodeV = NULL;
|
||||
ID3DXBuffer* pShaderCodeP = NULL;
|
||||
ID3DXBuffer* pErrorMsg = NULL;
|
||||
|
||||
ret = D3DXCompileShaderFromFile(
|
||||
g_settings.video.cg_shader_path, //filepath
|
||||
NULL, //macros
|
||||
NULL, //includes
|
||||
"main_vertex", // main function
|
||||
"vs_2_0", // shader profile
|
||||
0, // flags
|
||||
&pShaderCodeV, // compiled operations
|
||||
&pErrorMsg, // errors
|
||||
NULL); // constants
|
||||
|
||||
if (SUCCEEDED(ret))
|
||||
{
|
||||
SSNES_LOG("Vertex shader program from [%s] successfully compiled.\n", "game:\\media\\shaders\\stock.cg");
|
||||
ret = D3DXCompileShaderFromFile(
|
||||
g_settings.video.cg_shader_path, //filepath
|
||||
NULL, //macros
|
||||
NULL, //includes
|
||||
"main_fragment", // main function
|
||||
"ps_2_0", // shader profile
|
||||
0, // flags
|
||||
&pShaderCodeP, // compiled operations
|
||||
&pErrorMsg, // errors
|
||||
NULL); // constants
|
||||
}
|
||||
|
||||
if (FAILED(ret))
|
||||
{
|
||||
if(pErrorMsg)
|
||||
SSNES_LOG("%s\n", (char*)pErrorMsg->GetBufferPointer());
|
||||
D3DDevice_Release(vid->xdk360_render_device);
|
||||
Direct3D_Release();
|
||||
free(vid);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
SSNES_LOG("Pixel shader program from [%s] successfully compiled.\n", "game:\\media\\shaders\\stock.cg");
|
||||
}
|
||||
|
||||
vid->pVertexShader = D3DDevice_CreateVertexShader((const DWORD*)pShaderCodeV->GetBufferPointer());
|
||||
vid->pPixelShader = D3DDevice_CreatePixelShader((const DWORD*)pShaderCodeP->GetBufferPointer());
|
||||
pShaderCodeV->Release();
|
||||
pShaderCodeP->Release();
|
||||
|
||||
vid->lpTexture = (D3DTexture*) D3DDevice_CreateTexture(512, 512, 1, 1, 0, D3DFMT_LIN_X1R5G5B5,
|
||||
0, D3DRTYPE_TEXTURE);
|
||||
|
||||
D3DLOCKED_RECT d3dlr;
|
||||
D3DTexture_LockRect(vid->lpTexture, 0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);
|
||||
memset(d3dlr.pBits, 0, 512 * d3dlr.Pitch);
|
||||
D3DTexture_UnlockRect(vid->lpTexture, 0);
|
||||
|
||||
vid->last_width = 512;
|
||||
vid->last_height = 512;
|
||||
|
||||
vid->vertex_buf = D3DDevice_CreateVertexBuffer(4 * sizeof(DrawVerticeFormats), 0, 0);
|
||||
|
||||
static const DrawVerticeFormats init_verts[] = {
|
||||
{ -1.0f, -1.0f, 0.0f, 1.0f },
|
||||
{ 1.0f, -1.0f, 1.0f, 1.0f },
|
||||
{ -1.0f, 1.0f, 0.0f, 0.0f },
|
||||
{ 1.0f, 1.0f, 1.0f, 0.0f },
|
||||
};
|
||||
|
||||
void *verts_ptr = (BYTE*)D3DVertexBuffer_Lock(vid->vertex_buf, 0, 0, 0);
|
||||
memcpy(verts_ptr, init_verts, sizeof(init_verts));
|
||||
D3DVertexBuffer_Unlock(vid->vertex_buf);
|
||||
|
||||
static const D3DVERTEXELEMENT9 VertexElements[] =
|
||||
{
|
||||
{ 0, 0 * sizeof(float), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
|
||||
{ 0, 2 * sizeof(float), D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0 },
|
||||
D3DDECL_END()
|
||||
};
|
||||
|
||||
vid->pVertexDecl = D3DDevice_CreateVertexDeclaration(VertexElements);
|
||||
|
||||
D3DDevice_Clear(vid->xdk360_render_device, 0, NULL, D3DCLEAR_TARGET,
|
||||
0xff000000, 1.0f, 0, FALSE);
|
||||
|
||||
D3DDevice_SetRenderState_CullMode(vid->xdk360_render_device, D3DCULL_NONE);
|
||||
D3DDevice_SetRenderState_ZEnable(vid->xdk360_render_device, FALSE);
|
||||
|
||||
D3DVIEWPORT9 vp = {0};
|
||||
vp.Width = vid->video_mode.fIsHiDef ? 1280 : 640;
|
||||
vp.Height = vid->video_mode.fIsHiDef ? 720 : 480;
|
||||
vp.MinZ = 0.0f;
|
||||
vp.MaxZ = 1.0f;
|
||||
D3DDevice_SetViewport(vid->xdk360_render_device, &vp);
|
||||
|
||||
xdk360_set_orientation(NULL, g_console.screen_orientation);
|
||||
|
||||
return vid;
|
||||
}
|
||||
|
||||
static bool xdk360_gfx_frame(void *data, const void *frame,
|
||||
unsigned width, unsigned height, unsigned pitch, const char *msg)
|
||||
{
|
||||
xdk360_video_t *vid = (xdk360_video_t*)data;
|
||||
g_frame_count++;
|
||||
|
||||
D3DDevice_Clear(vid->xdk360_render_device, 0, NULL, D3DCLEAR_TARGET,
|
||||
0xff000000, 1.0f, 0, FALSE);
|
||||
|
||||
if (vid->last_width != width || vid->last_height != height)
|
||||
{
|
||||
D3DLOCKED_RECT d3dlr;
|
||||
|
||||
D3DTexture_LockRect(vid->lpTexture, 0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);
|
||||
memset(d3dlr.pBits, 0, 512 * d3dlr.Pitch);
|
||||
D3DTexture_UnlockRect(vid->lpTexture, 0);
|
||||
|
||||
float tex_w = width / 512.0f;
|
||||
float tex_h = height / 512.0f;
|
||||
|
||||
const DrawVerticeFormats verts[] = {
|
||||
{ -1.0f, -1.0f, 0.0f, tex_h },
|
||||
{ 1.0f, -1.0f, tex_w, tex_h },
|
||||
{ -1.0f, 1.0f, 0.0f, 0.0f },
|
||||
{ 1.0f, 1.0f, tex_w, 0.0f },
|
||||
};
|
||||
|
||||
void *verts_ptr = (BYTE*)D3DVertexBuffer_Lock(vid->vertex_buf, 0, 0, 0);
|
||||
memcpy(verts_ptr, verts, sizeof(verts));
|
||||
D3DVertexBuffer_Unlock(vid->vertex_buf);
|
||||
|
||||
vid->last_width = width;
|
||||
vid->last_height = height;
|
||||
}
|
||||
|
||||
vid->xdk360_render_device->SetVertexShaderConstantF(0, (FLOAT*)&hlsl_program.modelViewProj, 4);
|
||||
|
||||
//TODO: Update the shader constants
|
||||
|
||||
D3DLOCKED_RECT d3dlr;
|
||||
D3DTexture_LockRect(vid->lpTexture, 0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK);
|
||||
for (unsigned y = 0; y < height; y++)
|
||||
{
|
||||
const uint8_t *in = (const uint8_t*)frame + y * pitch;
|
||||
uint8_t *out = (uint8_t*)d3dlr.pBits + y * d3dlr.Pitch;
|
||||
memcpy(out, in, width * sizeof(uint16_t));
|
||||
}
|
||||
D3DTexture_UnlockRect(vid->lpTexture, 0);
|
||||
|
||||
D3DDevice_SetTexture_Inline(vid->xdk360_render_device, 0, vid->lpTexture);
|
||||
D3DDevice_SetSamplerState(vid->xdk360_render_device, 0, D3DSAMP_MINFILTER, g_settings.video.smooth ? D3DTEXF_LINEAR : D3DTEXF_POINT);
|
||||
D3DDevice_SetSamplerState(vid->xdk360_render_device, 0, D3DSAMP_MAGFILTER, g_settings.video.smooth ? D3DTEXF_LINEAR : D3DTEXF_POINT);
|
||||
D3DDevice_SetSamplerState(vid->xdk360_render_device, 0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER);
|
||||
D3DDevice_SetSamplerState(vid->xdk360_render_device, 0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER);
|
||||
|
||||
D3DDevice_SetVertexShader(vid->xdk360_render_device, vid->pVertexShader);
|
||||
D3DDevice_SetPixelShader(vid->xdk360_render_device, vid->pPixelShader);
|
||||
|
||||
D3DDevice_SetVertexDeclaration(vid->xdk360_render_device, vid->pVertexDecl);
|
||||
D3DDevice_SetStreamSource_Inline(vid->xdk360_render_device, 0, vid->vertex_buf, 0,
|
||||
sizeof(DrawVerticeFormats));
|
||||
|
||||
D3DDevice_DrawVertices(vid->xdk360_render_device, D3DPT_TRIANGLESTRIP, 0, D3DVERTEXCOUNT(D3DPT_TRIANGLESTRIP, 2));
|
||||
if (msg)
|
||||
{
|
||||
if(IS_TIMER_EXPIRED() || g_first_msg)
|
||||
{
|
||||
xdk360_console_format(msg);
|
||||
g_first_msg = 0;
|
||||
SET_TIMER_EXPIRATION(30);
|
||||
}
|
||||
|
||||
xdk360_console_draw();
|
||||
}
|
||||
|
||||
if(!vid->block_swap)
|
||||
D3DDevice_Present(vid->xdk360_render_device);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void xdk360_set_swap_block_swap (void * data, bool toggle)
|
||||
{
|
||||
(void)data;
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
vid->block_swap = toggle;
|
||||
|
||||
if(toggle)
|
||||
SSNES_LOG("Swap is set to blocked.\n");
|
||||
else
|
||||
SSNES_LOG("Swap is set to non-blocked.\n");
|
||||
}
|
||||
|
||||
static void xdk360_swap (void * data)
|
||||
{
|
||||
(void)data;
|
||||
xdk360_video_t *vid = (xdk360_video_t*)g_d3d;
|
||||
D3DDevice_Present(vid->xdk360_render_device);
|
||||
}
|
||||
|
||||
static void xdk360_gfx_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
xdk360_video_t *vid = (xdk360_video_t*)data;
|
||||
SSNES_LOG("D3D Vsync => %s\n", state ? "off" : "on");
|
||||
if(state)
|
||||
D3DDevice_SetRenderState_PresentInterval(vid->xdk360_render_device, D3DPRESENT_INTERVAL_IMMEDIATE);
|
||||
else
|
||||
D3DDevice_SetRenderState_PresentInterval(vid->xdk360_render_device, D3DPRESENT_INTERVAL_ONE);
|
||||
}
|
||||
|
||||
static bool xdk360_gfx_alive(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return !g_quitting;
|
||||
}
|
||||
|
||||
static bool xdk360_gfx_focus(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
void xdk360_video_set_vsync(bool vsync)
|
||||
{
|
||||
xdk360_gfx_set_nonblock_state(g_d3d, vsync);
|
||||
}
|
||||
|
||||
// 360 needs a working graphics stack before SSNESeven starts.
|
||||
// To deal with this main.c,
|
||||
// the top level module owns the instance, and is created beforehand.
|
||||
// When SSNES gets around to init it, it is already allocated.
|
||||
// When SSNES wants to free it, it is ignored.
|
||||
void xdk360_video_init(void)
|
||||
{
|
||||
video_info_t video_info = {0};
|
||||
// Might have to supply correct values here.
|
||||
video_info.vsync = g_settings.video.vsync;
|
||||
video_info.force_aspect = false;
|
||||
video_info.smooth = g_settings.video.smooth;
|
||||
video_info.input_scale = 2;
|
||||
|
||||
g_d3d = xdk360_gfx_init(&video_info, NULL, NULL);
|
||||
|
||||
g_first_msg = true;
|
||||
|
||||
HRESULT hr = xdk360_console_init("game:\\media\\Arial_12.xpr",
|
||||
0xff000000, 0xffffffff );
|
||||
if(FAILED(hr))
|
||||
{
|
||||
SSNES_ERR("Couldn't create debug console.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void xdk360_video_deinit(void)
|
||||
{
|
||||
void *data = g_d3d;
|
||||
g_d3d = NULL;
|
||||
xdk360_console_deinit();
|
||||
xdk360_gfx_free(data);
|
||||
}
|
||||
|
||||
const video_driver_t video_xdk360 = {
|
||||
xdk360_gfx_init,
|
||||
xdk360_gfx_frame,
|
||||
xdk360_gfx_set_nonblock_state,
|
||||
xdk360_gfx_alive,
|
||||
xdk360_gfx_focus,
|
||||
NULL,
|
||||
xdk360_gfx_free,
|
||||
"xdk360",
|
||||
xdk360_set_swap_block_swap,
|
||||
xdk360_swap,
|
||||
xdk360_set_orientation
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _XDK360_VIDEO_H
|
||||
#define _XDK360_VIDEO_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "fonts.h"
|
||||
#include "xdk360_video_general.h"
|
||||
|
||||
typedef struct {
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
float rhw;
|
||||
float u;
|
||||
float v;
|
||||
} primitive_t;
|
||||
|
||||
#define DFONT_MAX 4096
|
||||
#define PRIM_FVF (D3DFVF_XYZRHW | D3DFVF_TEX1)
|
||||
|
||||
typedef struct DrawVerticeFormats
|
||||
{
|
||||
float x, y;
|
||||
float u, v;
|
||||
} DrawVerticeFormats;
|
||||
|
||||
typedef struct xdk360_video
|
||||
{
|
||||
bool block_swap;
|
||||
bool vsync;
|
||||
unsigned last_width, last_height;
|
||||
IDirect3D9* xdk360_device;
|
||||
IDirect3DDevice9* xdk360_render_device;
|
||||
IDirect3DVertexShader9 *pVertexShader;
|
||||
IDirect3DPixelShader9* pPixelShader;
|
||||
IDirect3DVertexDeclaration9* pVertexDecl;
|
||||
IDirect3DVertexBuffer9* vertex_buf;
|
||||
IDirect3DTexture9* lpTexture;
|
||||
D3DPRESENT_PARAMETERS d3dpp;
|
||||
XVIDEO_MODE video_mode;
|
||||
} xdk360_video_t;
|
||||
|
||||
enum
|
||||
{
|
||||
ASPECT_RATIO_4_3,
|
||||
ASPECT_RATIO_5_4,
|
||||
ASPECT_RATIO_8_7,
|
||||
ASPECT_RATIO_16_9,
|
||||
ASPECT_RATIO_16_10,
|
||||
ASPECT_RATIO_16_15,
|
||||
ASPECT_RATIO_19_14,
|
||||
ASPECT_RATIO_2_1,
|
||||
ASPECT_RATIO_3_2,
|
||||
ASPECT_RATIO_3_4,
|
||||
ASPECT_RATIO_1_1,
|
||||
ASPECT_RATIO_AUTO,
|
||||
ASPECT_RATIO_CUSTOM
|
||||
};
|
||||
|
||||
void xdk360_video_init(void);
|
||||
void xdk360_video_deinit(void);
|
||||
void xdk360_video_set_vsync(bool vsync);
|
||||
void xdk360_set_aspect_ratio(uint32_t aspectratio_index);
|
||||
|
||||
extern void *g_d3d;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _XDK360_VIDEO_GENERAL_H
|
||||
#define _XDK360_VIDEO_GENERAL_H
|
||||
|
||||
#define LAST_ASPECT_RATIO ASPECT_RATIO_CUSTOM
|
||||
|
||||
#define IS_TIMER_NOT_EXPIRED() (g_frame_count < g_console.timer_expiration_frame_count)
|
||||
#define IS_TIMER_EXPIRED() (!(IS_TIMER_NOT_EXPIRED()))
|
||||
#define SET_TIMER_EXPIRATION(value) g_console.timer_expiration_frame_count = g_frame_count + value;
|
||||
|
||||
extern unsigned g_frame_count;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,131 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef SSNES360_RESOURCES_H
|
||||
#define SSNES360_RESOURCES_H
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Name tag for resources. An app may initialize this structure, and pass
|
||||
// it to the resource's Create() function. From then on, the app may call
|
||||
// GetResource() to retrieve a resource using an ascii name.
|
||||
//--------------------------------------------------------------------------------------
|
||||
struct RESOURCE
|
||||
{
|
||||
unsigned long dwType;
|
||||
unsigned long dwOffset;
|
||||
unsigned long dwSize;
|
||||
char * strName;
|
||||
};
|
||||
|
||||
|
||||
// Resource types
|
||||
enum
|
||||
{
|
||||
RESOURCETYPE_USERDATA = ( ( 'U' << 24 ) | ( 'S' << 16 ) | ( 'E' << 8 ) | ( 'R' ) ),
|
||||
RESOURCETYPE_TEXTURE = ( ( 'T' << 24 ) | ( 'X' << 16 ) | ( '2' << 8 ) | ( 'D' ) ),
|
||||
RESOURCETYPE_CUBEMAP = ( ( 'T' << 24 ) | ( 'X' << 16 ) | ( 'C' << 8 ) | ( 'M' ) ),
|
||||
RESOURCETYPE_VOLUMETEXTURE = ( ( 'T' << 24 ) | ( 'X' << 16 ) | ( '3' << 8 ) | ( 'D' ) ),
|
||||
RESOURCETYPE_VERTEXBUFFER = ( ( 'V' << 24 ) | ( 'B' << 16 ) | ( 'U' << 8 ) | ( 'F' ) ),
|
||||
RESOURCETYPE_INDEXBUFFER = ( ( 'I' << 24 ) | ( 'B' << 16 ) | ( 'U' << 8 ) | ( 'F' ) ),
|
||||
RESOURCETYPE_EOF = 0xffffffff
|
||||
};
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------------
|
||||
// Name: PackedResource
|
||||
//--------------------------------------------------------------------------------------
|
||||
class PackedResource
|
||||
{
|
||||
protected:
|
||||
unsigned char * m_pSysMemData; // Alloc'ed memory for resource headers etc.
|
||||
unsigned long m_dwSysMemDataSize;
|
||||
|
||||
unsigned char * m_pVidMemData; // Alloc'ed memory for resource data, etc.
|
||||
unsigned long m_dwVidMemDataSize;
|
||||
|
||||
RESOURCE* m_pResourceTags; // Tags to associate names with the resources
|
||||
unsigned long m_dwNumResourceTags; // Number of resource tags
|
||||
public:
|
||||
int m_bInitialized; // Resource is fully initialized
|
||||
// Loads the resources out of the specified bundle
|
||||
HRESULT Create( const char * strFilename );
|
||||
|
||||
void Destroy();
|
||||
|
||||
// Helper function to make sure a resource is registered
|
||||
D3DResource* RegisterResource( D3DResource* pResource ) const
|
||||
{
|
||||
return pResource;
|
||||
}
|
||||
|
||||
// Functions to retrieve resources by their offset
|
||||
void * GetData( unsigned long dwOffset ) const
|
||||
{
|
||||
return &m_pSysMemData[dwOffset];
|
||||
}
|
||||
|
||||
D3DResource* GetResource( unsigned long dwOffset ) const
|
||||
{
|
||||
return (( D3DResource* )GetData( dwOffset ) );
|
||||
}
|
||||
|
||||
D3DTexture* GetTexture( unsigned long dwOffset ) const
|
||||
{
|
||||
return ( D3DTexture* )GetResource( dwOffset );
|
||||
}
|
||||
|
||||
D3DArrayTexture* GetArrayTexture( unsigned long dwOffset ) const
|
||||
{
|
||||
return ( D3DArrayTexture* )GetResource( dwOffset );
|
||||
}
|
||||
|
||||
D3DVertexBuffer* GetVertexBuffer( unsigned long dwOffset ) const
|
||||
{
|
||||
return ( D3DVertexBuffer* )GetResource( dwOffset );
|
||||
}
|
||||
|
||||
// Functions to retrieve resources by their name
|
||||
void * GetData( const char * strName ) const;
|
||||
|
||||
D3DResource* GetResource( const char * strName ) const
|
||||
{
|
||||
return ( ( D3DResource* )GetData( strName ) );
|
||||
}
|
||||
|
||||
D3DTexture* GetTexture( const char * strName ) const
|
||||
{
|
||||
return ( D3DTexture* )GetResource( strName );
|
||||
}
|
||||
|
||||
D3DArrayTexture* GetArrayTexture( const char * strName ) const
|
||||
{
|
||||
return ( D3DArrayTexture* )GetResource( strName );
|
||||
}
|
||||
|
||||
D3DVertexBuffer* GetVertexBuffer( const char * strName ) const
|
||||
{
|
||||
return ( D3DVertexBuffer* )GetResource( strName );
|
||||
}
|
||||
|
||||
PackedResource();
|
||||
~PackedResource();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -6,3 +6,8 @@ Devin J. Pohly - <[email protected]>
|
||||
|
||||
Chris Moeller - <[email protected]>
|
||||
- CoreAudio audio driver
|
||||
|
||||
Daniel De Matteis - <[email protected]>
|
||||
- PlayStation 3 port
|
||||
- XBox 360 XDK port
|
||||
|
||||
@@ -2,12 +2,17 @@ include config.mk
|
||||
|
||||
TARGET = ssnes tools/ssnes-joyconfig
|
||||
|
||||
OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o ups.o bps.o strl.o screenshot.o thread.o audio/hermite.o
|
||||
JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o strl.o
|
||||
OBJ = ssnes.o file.o hash.o driver.o settings.o dynamic.o message.o rewind.o gfx/gfx_common.o patch.o compat/compat.o screenshot.o audio/utils.o
|
||||
JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.o compat/compat.o
|
||||
HEADERS = $(wildcard */*.h) $(wildcard *.h)
|
||||
|
||||
LIBS = -lm -lpthread
|
||||
DEFINES = -DHAVE_CONFIG_H
|
||||
LIBS = -lm
|
||||
DEFINES = -DHAVE_CONFIG_H -DHAVE_SCREENSHOTS
|
||||
|
||||
ifeq ($(REENTRANT_TEST), 1)
|
||||
DEFINES += -Dmain=ssnes_main
|
||||
OBJ += console/test.o
|
||||
endif
|
||||
|
||||
ifneq ($(findstring Darwin,$(OS)),)
|
||||
OSX := 1
|
||||
@@ -27,10 +32,19 @@ ifneq ($(findstring Linux,$(OS)),)
|
||||
LIBS += -lrt
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_THREADS), 1)
|
||||
OBJ += autosave.o thread.o
|
||||
LIBS += -lpthread
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CONFIGFILE), 1)
|
||||
OBJ += conf/config_file.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_BSV_MOVIE), 1)
|
||||
OBJ += movie.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_NETPLAY), 1)
|
||||
OBJ += netplay.o
|
||||
endif
|
||||
@@ -86,7 +100,7 @@ ifeq ($(HAVE_COREAUDIO), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL), 1)
|
||||
OBJ += gfx/sdl.o gfx/sdlwrap.o input/sdl.o audio/sdl.o fifo_buffer.o
|
||||
OBJ += gfx/sdl_gfx.o gfx/sdlwrap.o input/sdl_input.o audio/sdl_audio.o fifo_buffer.o
|
||||
DEFINES += $(SDL_CFLAGS) $(BSD_LOCAL_INC)
|
||||
LIBS += $(SDL_LIBS)
|
||||
|
||||
@@ -112,7 +126,7 @@ ifeq ($(HAVE_CG), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XML), 1)
|
||||
OBJ += sha256.o cheats.o
|
||||
OBJ += cheats.o
|
||||
LIBS += $(XML_LIBS)
|
||||
DEFINES += $(XML_CFLAGS)
|
||||
|
||||
@@ -128,7 +142,7 @@ else ifeq ($(HAVE_CG), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
OBJ += gfx/ext.o audio/ext.o
|
||||
OBJ += gfx/ext_gfx.o audio/ext_audio.o
|
||||
LIBS += $(DYLIB_LIB)
|
||||
endif
|
||||
|
||||
@@ -155,16 +169,18 @@ 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
|
||||
|
||||
ifeq ($(HAVE_SINC), 1)
|
||||
OBJ += audio/sinc.o
|
||||
else
|
||||
OBJ += audio/hermite.o
|
||||
endif
|
||||
|
||||
ifneq ($(V),1)
|
||||
Q := @
|
||||
endif
|
||||
@@ -175,11 +191,15 @@ ifeq ($(DEBUG), 1)
|
||||
endif
|
||||
|
||||
CFLAGS += -Wall $(OPTIMIZE_FLAG) -g -I. -pedantic
|
||||
ifeq ($(CXX_BUILD), 1)
|
||||
CFLAGS += -std=c++0x -xc++ -D__STDC_CONSTANT_MACROS
|
||||
else
|
||||
ifneq ($(findstring icc,$(CC)),)
|
||||
CFLAGS += -std=c99 -D_GNU_SOURCE
|
||||
else
|
||||
CFLAGS += -std=gnu99
|
||||
endif
|
||||
endif
|
||||
|
||||
all: $(TARGET) config.mk
|
||||
|
||||
@@ -192,7 +212,11 @@ ssnes: $(OBJ)
|
||||
@$(if $(Q), $(shell echo echo LD $@),)
|
||||
|
||||
tools/ssnes-joyconfig: $(JOYCONFIG_OBJ)
|
||||
ifeq ($(CXX_BUILD), 1)
|
||||
$(Q)$(CXX) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS) $(LIBRARY_DIRS)
|
||||
else
|
||||
$(Q)$(CC) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS) $(LIBRARY_DIRS)
|
||||
endif
|
||||
@$(if $(Q), $(shell echo echo LD $@),)
|
||||
|
||||
%.o: %.c config.h config.mk $(HEADERS)
|
||||
|
||||
+157
@@ -0,0 +1,157 @@
|
||||
SSNES_VERSION = "0.9.5"
|
||||
|
||||
#which compiler to build with - GCC or SNC
|
||||
#set to GCC for debug builds for use with debugger
|
||||
CELL_BUILD_TOOLS = SNC
|
||||
CELL_GPU_TYPE = RSX
|
||||
CELL_PSGL_VERSION = ultra-opt
|
||||
|
||||
DEBUG = 0
|
||||
DOWNLOAD_SHADERS = 1
|
||||
STRIPPING_ENABLE = 0
|
||||
HAVE_SSNES_GL = 0
|
||||
HAVE_LOGGER = 0
|
||||
|
||||
CONTENT_ID_FULL = UP0001-SSNE10000_00-0000000000000001
|
||||
CONTENT_ID = SSNE10000
|
||||
|
||||
PC_DEVELOPMENT_IP_ADDRESS = "192.168.1.7"
|
||||
PC_DEVELOPMENT_UDP_PORT = 3490
|
||||
|
||||
CELL_MK_DIR ?= $(CELL_SDK)/samples/mk
|
||||
include $(CELL_MK_DIR)/sdk.makedef.mk
|
||||
|
||||
PPU_TARGET = ssnes.elf
|
||||
SALAMANDER_TARGET = ssnes-salamander.elf
|
||||
|
||||
EBOOT_PATH = ps3/pkg/USRDIR/EBOOT.BIN
|
||||
CORE_PATH = ps3/pkg/USRDIR/cores/CORE.SELF
|
||||
|
||||
LDDIRS = -L. -L$(CELL_SDK)/target/ppu/lib/PSGL/RSX/ultra-opt
|
||||
INCDIRS = -I. -Ips3 -Icommon
|
||||
|
||||
PKG_SCRIPT = ps3/ps3py/pkg.py
|
||||
ifeq ($(shell uname), Linux)
|
||||
PKG_FINALIZE = package_finalize
|
||||
MAKE_SELF_WC = make_self_wc
|
||||
MAKE_SELF = make_self_npdrm
|
||||
PYTHON2 = python2
|
||||
GIT = git
|
||||
else
|
||||
PKG_FINALIZE = package_finalize.exe
|
||||
MAKE_SELF_WC = make_self_wc.exe
|
||||
MAKE_SELF = make_self_npdrm.exe
|
||||
PYTHON2 = python2.exe
|
||||
GIT = git.exe
|
||||
endif
|
||||
|
||||
PPU_SRCS = console/griffin/griffin.c \
|
||||
console/szlib/szlib.c
|
||||
|
||||
ifeq ($(HAVE_SSNES_GL), 1)
|
||||
DEFINES = -DHAVE_SSNES_GL
|
||||
GL_LIBS := -lSSNESGL -lSSNESGLcgc
|
||||
else
|
||||
GL_LIBS := -L$(CELL_SDK)/target/ppu/lib/PSGL/RSX/ultra-opt -lPSGL -lPSGLcgc
|
||||
endif
|
||||
|
||||
ifeq ($(CELL_BUILD_TOOLS), SNC)
|
||||
PPU_CXXLD = $(CELL_SDK)/host-win32/sn/bin/ps3ppuld.exe
|
||||
PPU_CXX = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe
|
||||
PPU_CC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe
|
||||
else
|
||||
PPU_CXX = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-g++.exe
|
||||
PPU_CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe
|
||||
PPU_CXXLD = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ld.exe
|
||||
endif
|
||||
|
||||
|
||||
PPU_LDLIBS = -ldbgfont $(GL_LIBS) -lsnes -lcgc -lgcm_cmd -lgcm_sys_stub -lresc_stub -lm -lio_stub -lfs_stub -lsysutil_stub -lsysutil_game_stub -lsysutil_screenshot_stub -lsysutil_np_stub -lpngdec_stub -ljpgdec_stub -lsysmodule_stub -laudio_stub -lnet_stub -lnetctl_stub -lpthread
|
||||
|
||||
DEFINES += -DSSNES_CONSOLE -DHAVE_OPENGL -DHAVE_CG -DHAVE_FBO -DHAVE_RSOUND -DHAVE_ZLIB -D__CELLOS_LV2__ -DHAVE_CONFIGFILE=1 -DHAVE_NETPLAY=1 -DHAVE_SOCKET_LEGACY=1 -DHAVE_GRIFFIN=1 -DPACKAGE_VERSION=\"$(SSNES_VERSION)\" -Dmain=ssnes_main -DPC_DEVELOPMENT_IP_ADDRESS=\"$(PC_DEVELOPMENT_IP_ADDRESS)\" -DPC_DEVELOPMENT_UDP_PORT=$(PC_DEVELOPMENT_UDP_PORT)
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
PPU_OPTIMIZE_LV := -O0 -g
|
||||
else
|
||||
PPU_OPTIMIZE_LV := -O3 -g
|
||||
endif
|
||||
|
||||
PPU_CFLAGS = $(PPU_OPTIMIZE_LV) $(INCDIRS) $(DEFINES)
|
||||
PPU_CXXFLAGS = $(PPU_OPTIMIZE_LV) $(INCDIRS) $(DEFINES)
|
||||
|
||||
ifeq ($(HAVE_LOGGER), 1)
|
||||
PPU_CFLAGS += -DHAVE_LOGGER
|
||||
PPU_SRCS += console/logger/logger.c
|
||||
endif
|
||||
|
||||
EXIST_EBOOT_WILDCARD := $(wildcard $(EBOOT_PATH))
|
||||
EXIST_CORE_WILDCARD := $(wildcard $(CORE_PATH))
|
||||
|
||||
EBOOT_EXISTS = 1
|
||||
CORE_EXISTS = 1
|
||||
|
||||
ifneq ($(strip $(EXIST_EBOOT_WILDCARD)),)
|
||||
EBOOT_EXISTS = 0
|
||||
endif
|
||||
|
||||
ifneq ($(strip $(EXIST_CORE_WILDCARD)),)
|
||||
CORE_EXISTS = 0
|
||||
endif
|
||||
|
||||
include $(CELL_MK_DIR)/sdk.target.mk
|
||||
|
||||
MAKE_FSELF = $(CELL_SDK)/host-win32/bin/make_fself.exe
|
||||
MAKE_FSELF_NPDRM = $(CELL_SDK)/host-win32/bin/make_fself_npdrm.exe
|
||||
MAKE_PACKAGE_NPDRM = $(CELL_SDK)/host-win32/bin/make_package_npdrm.exe
|
||||
|
||||
.PHONY: create-selfs-npdrm create-npdrm-core create-core create-npdrm-salamander create-npdrm-salamander create-shaders clean-selfs
|
||||
|
||||
create-npdrm-core:
|
||||
$(MAKE_FSELF_NPDRM) $(PPU_TARGET) $(CORE_PATH)
|
||||
|
||||
create-core:
|
||||
$(MAKE_SELF_WC) $(PPU_TARGET) $(CORE_PATH)
|
||||
|
||||
create-npdrm-salamander:
|
||||
$(MAKE_FSELF_NPDRM) $(SALAMANDER_TARGET) $(EBOOT_PATH)
|
||||
|
||||
create-salamander:
|
||||
$(MAKE_SELF) $(SALAMANDER_TARGET) $(EBOOT_PATH) $(CONTENT_ID_FULL)
|
||||
|
||||
|
||||
create-shaders:
|
||||
ifeq ($(DOWNLOAD_SHADERS),1)
|
||||
$(RM) -rf ps3/pkg/USRDIR/cores/shaders
|
||||
$(GIT) clone git://github.com/twinaphex/common-shaders.git ps3/pkg/USRDIR/cores/shaders
|
||||
endif
|
||||
|
||||
pkg: $(PPU_TARGET) create-shaders create-npdrm-salamander create-npdrm-core
|
||||
$(MAKE_PACKAGE_NPDRM) ps3/pkg/package.conf ps3/pkg
|
||||
|
||||
pkg-signed: $(PPU_TARGET) create-shaders create-salamander create-core
|
||||
$(PYTHON2) $(PKG_SCRIPT) --contentid $(CONTENT_ID_FULL) ps3/pkg/ ssnes-ps3-cfw-$(SSNES_VERSION).pkg
|
||||
|
||||
pkg-signed-standalone: $(PPU_TARGET) create-shaders create-core
|
||||
$(MAKE_SELF) $(PPU_TARGET) $(EBOOT_PATH) $(CONTENT_ID)
|
||||
$(PYTHON2) $(PKG_SCRIPT) --contentid $(CONTENT_ID_FULL) ps3/pkg/ ssnes-ps3-cfw-$(SSNES_VERSION).pkg
|
||||
|
||||
pkg-signed-cfw: $(PPU_TARGET) create-shaders create-salamander create-core
|
||||
$(PYTHON2) $(PKG_SCRIPT) --contentid $(CONTENT_ID_FULL) ps3/pkg/ ssnes-ps3-cfw-$(SSNES_VERSION)-kmeaw.pkg
|
||||
$(PKG_FINALIZE) ssnes-ps3-cfw-$(SSNES_VERSION)-kmeaw.pkg
|
||||
|
||||
pkg-signed-cfw-standalone: $(PPU_TARGET) create-shaders create-core
|
||||
$(MAKE_SELF) $(PPU_TARGET) $(EBOOT_PATH) $(CONTENT_ID)
|
||||
$(PYTHON2) $(PKG_SCRIPT) --contentid $(CONTENT_ID_FULL) ps3/pkg/ ssnes-ps3-cfw-$(SSNES_VERSION).pkg
|
||||
$(PKG_FINALIZE) ssnes-ps3-cfw-$(SSNES_VERSION)-kmeaw.pkg
|
||||
|
||||
clean-selfs:
|
||||
ifeq ($(EBOOT_EXISTS),1)
|
||||
@echo "WARNING: Couldn't find file to delete: [$(EBOOT_PATH)], skipping this step."
|
||||
else
|
||||
rm $(EBOOT_PATH)
|
||||
endif
|
||||
ifeq ($(CORE_EXISTS),1)
|
||||
@echo "WARNING: Couldn't find file to delete: [$(CORE_PATH)], skipping this step."
|
||||
else
|
||||
rm $(CORE_PATH)
|
||||
endif
|
||||
@@ -0,0 +1,50 @@
|
||||
CELL_BUILD_TOOLS = SNC
|
||||
CELL_SDK ?= /usr/local/cell
|
||||
CELL_GPU_TYPE = RSX
|
||||
CELL_PSGL_VERSION = opt
|
||||
HAVE_LOGGER = 0
|
||||
|
||||
CELL_MK_DIR ?= $(CELL_SDK)/samples/mk
|
||||
include $(CELL_MK_DIR)/sdk.makedef.mk
|
||||
|
||||
MAKE_SELF_WC = make_self_wc
|
||||
MAKE_SELF = make_self_npdrm
|
||||
|
||||
PKG_SCRIPT = ps3/ps3py/pkg.py
|
||||
PKG_FINALIZE = package_finalize
|
||||
|
||||
STRIP = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-strip.exe
|
||||
COPY = cp
|
||||
MOVE = mv
|
||||
|
||||
PPU_CFLAGS += -I. -D__CELLOS_LV2__
|
||||
PPU_SRCS = ps3/salamander/main.c compat/compat.c conf/config_file.c
|
||||
|
||||
ifeq ($(HAVE_LOGGER), 1)
|
||||
PPU_CFLAGS += -DHAVE_LOGGER
|
||||
PPU_SRCS += console/logger/logger.c
|
||||
endif
|
||||
|
||||
PPU_TARGET = ssnes-salamander.elf
|
||||
|
||||
ifeq ($(CELL_BUILD_TOOLS),SNC)
|
||||
PPU_CFLAGS += -Xbranchless=1 -Xfastmath=1 -Xassumecorrectsign=1 -Xassumecorrectalignment=1 -Xunroll=1 -Xautovecreg=1
|
||||
PPU_CXXFLAGS += -Xbranchless=1 -Xfastmath=1 -Xassumecorrectsign=1 -Xassumecorrectalignment=1 -Xunroll=1 -Xautovecreg=1
|
||||
PPU_CXXLD = $(CELL_SDK)/host-win32/sn/bin/ps3ppuld.exe
|
||||
PPU_CXX = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe
|
||||
PPU_CC = $(CELL_SDK)/host-win32/sn/bin/ps3ppusnc.exe
|
||||
else
|
||||
PPU_CXX = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-g++.exe
|
||||
PPU_CC = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-gcc.exe
|
||||
PPU_CXXLD = $(CELL_SDK)/host-win32/ppu/bin/ppu-lv2-ld.exe
|
||||
endif
|
||||
|
||||
PPU_LDLIBS += -lpthread -lm -lnet_stub -lnetctl_stub -lm -ldbgfont_gcm -lgcm_cmd -lgcm_sys_stub -lio_stub -lsysmodule_stub -lsysutil_stub -lsysutil_game_stub -lfs_stub -lsysutil_np_stub
|
||||
|
||||
PPU_OPTIMIZE_LV := -O2
|
||||
|
||||
include $(CELL_MK_DIR)/sdk.target.mk
|
||||
|
||||
MAKE_FSELF = $(CELL_SDK)/host-win32/bin/make_fself.exe
|
||||
MAKE_FSELF_NPDRM = $(CELL_SDK)/host-win32/bin/make_fself_npdrm.exe
|
||||
MAKE_PACKAGE_NPDRM = $(CELL_SDK)/host-win32/bin/make_package_npdrm.exe
|
||||
@@ -0,0 +1,67 @@
|
||||
###
|
||||
##
|
||||
# Makefile for SSNES Wii.
|
||||
## Doesn't really work yet, beware :V
|
||||
###
|
||||
##
|
||||
|
||||
DEBUG = 0
|
||||
HAVE_LOGGER = 0
|
||||
|
||||
PC_DEVELOPMENT_IP_ADDRESS = "192.168.1.7"
|
||||
PC_DEVELOPMENT_UDP_PORT = 3490
|
||||
|
||||
CC = $(DEVKITPPC)/bin/powerpc-eabi-gcc
|
||||
CXX = $(DEVKITPPC)/bin/powerpc-eabi-g++
|
||||
LD = $(DEVKITPPC)/bin/powerpc-eabi-ld
|
||||
|
||||
DOL_TARGET := ssnes.dol
|
||||
ELF_TARGET := ssnes.elf
|
||||
|
||||
INCLUDE := -I. -I$(DEVKITPRO)/libogc/include
|
||||
LIBDIRS := -L$(DEVKITPRO)/libogc/lib/wii -L.
|
||||
|
||||
MACHDEP := -DGEKKO -mrvl -mcpu=750 -meabi -mhard-float
|
||||
CFLAGS += -Wall -std=gnu99 $(MACHDEP) $(INCLUDE)
|
||||
CXXFLAGS += -Wall -std=gnu99 $(MACHDEP) $(INCLUDE)
|
||||
LDFLAGS := $(MACHDEP)
|
||||
LIBS := -lfat -lsnes -lwiiuse -logc -lbte -lfreetype
|
||||
|
||||
OBJ = wii/main.o fifo_buffer.o ssnes.o driver.o gfx/fonts.o file.o settings.o message.o rewind.o movie.o ups.o bps.o strl.o screenshot.o audio/hermite.o dynamic.o audio/utils.o conf/config_file.o wii/audio.o wii/input.o wii/video.o console/sgui/sgui.o console/sgui/list.o console/sgui/font.bmpobj console/main_wrap.o console/console_ext.o console/szlib/szlib.o
|
||||
|
||||
ifeq ($(HAVE_LOGGER), 1)
|
||||
CFLAGS += -DHAVE_LOGGER
|
||||
OBJ += console/logger/logger.o
|
||||
endif
|
||||
|
||||
CFLAGS += -std=gnu99 -DSSNES_CONSOLE -DHAVE_CONFIGFILE=1 -DHAVE_GETOPT_LONG -DHAVE_FREETYPE -DPACKAGE_VERSION=\"0.9.5\" -Dmain=ssnes_main -Wno-char-subscripts -DPC_DEVELOPMENT_IP_ADDRESS=\"$(PC_DEVELOPMENT_IP_ADDRESS)\" -DPC_DEVELOPMENT_UDP_PORT=$(PC_DEVELOPMENT_UDP_PORT)
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
CFLAGS += -O0 -g
|
||||
CXXFLAGS += -O0 -g
|
||||
else
|
||||
CLFAGS += -O3
|
||||
CXXFLAGS += -O3
|
||||
endif
|
||||
|
||||
all: $(DOL_TARGET)
|
||||
|
||||
%.dol: %.elf
|
||||
$(DEVKITPPC)/bin/elf2dol $< $@
|
||||
|
||||
$(ELF_TARGET): $(OBJ)
|
||||
$(CXX) -o $@ $(LDFLAGS) $(LIBDIRS) $(OBJ) $(LIBS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
%.bmpobj: %.bmp
|
||||
$(LD) -r -b binary -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(DOL_TARGET)
|
||||
rm -f $(ELF_TARGET)
|
||||
rm -f $(OBJ)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
+37
-18
@@ -1,7 +1,7 @@
|
||||
TARGET = ssnes.exe
|
||||
JTARGET = ssnes-joyconfig.exe
|
||||
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o rewind.o movie.o autosave.o gfx/gfx_common.o bps.o ups.o strl.o screenshot.o audio/hermite.o thread.o
|
||||
JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o strl.o
|
||||
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o hash.o dynamic.o message.o rewind.o movie.o gfx/gfx_common.o patch.o compat/compat.o screenshot.o audio/utils.o
|
||||
JOBJ = conf/config_file.o tools/ssnes-joyconfig.o compat/compat.o
|
||||
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
@@ -13,6 +13,7 @@ HAVE_SDL = 1
|
||||
HAVE_OPENGL = 1
|
||||
HAVE_DYLIB = 1
|
||||
HAVE_NETPLAY = 1
|
||||
HAVE_THREADS = 1
|
||||
DYNAMIC = 1
|
||||
|
||||
ifeq ($(SLIM),)
|
||||
@@ -28,17 +29,26 @@ endif
|
||||
|
||||
libsnes ?= -lsnes
|
||||
|
||||
LIBS = -lm -lshlwapi
|
||||
DEFINES = -I. -DHAVE_CONFIGFILE
|
||||
LIBS = -lm
|
||||
DEFINES = -I. -DHAVE_CONFIGFILE -DHAVE_SCREENSHOTS -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.5\"
|
||||
LDFLAGS = -L. -static-libgcc
|
||||
LDCXXFLAGS = -static-libstdc++ -s
|
||||
LDCXXFLAGS = -s
|
||||
|
||||
ifeq ($(TDM_GCC),)
|
||||
LDCXXFLAGS += -static-libstdc++
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL), 1)
|
||||
OBJ += gfx/sdl.o gfx/gl.o gfx/sdlwrap.o input/sdl.o audio/sdl.o fifo_buffer.o
|
||||
OBJ += gfx/sdl_gfx.o gfx/gl.o gfx/sdlwrap.o input/sdl_input.o audio/sdl_audio.o fifo_buffer.o
|
||||
LIBS += -lSDL
|
||||
DEFINES += -ISDL -DHAVE_SDL
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_THREADS), 1)
|
||||
OBJ += autosave.o thread.o
|
||||
DEFINES += -DHAVE_THREADS
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_OPENGL), 1)
|
||||
DEFINES += -DHAVE_OPENGL
|
||||
LIBS += -lopengl32
|
||||
@@ -74,7 +84,7 @@ ifeq ($(HAVE_RSOUND), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XML), 1)
|
||||
OBJ += gfx/shader_glsl.o gfx/image.o gfx/snes_state.o sha256.o cheats.o
|
||||
OBJ += gfx/shader_glsl.o gfx/image.o gfx/snes_state.o cheats.o
|
||||
DEFINES += -Ilibxml2 -DHAVE_XML
|
||||
LIBS += -lxml2 -liconv
|
||||
endif
|
||||
@@ -106,7 +116,7 @@ ifeq ($(HAVE_FBO), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
OBJ += gfx/ext.o audio/ext.o
|
||||
OBJ += gfx/ext_gfx.o audio/ext_audio.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_PYTHON), 1)
|
||||
@@ -133,12 +143,22 @@ ifeq ($(HAVE_FFMPEG), 1)
|
||||
OBJ += record/ffemu.o
|
||||
endif
|
||||
|
||||
ifneq ($(V),1)
|
||||
ifeq ($(HAVE_SINC), 1)
|
||||
OBJ += audio/sinc.o
|
||||
else
|
||||
OBJ += audio/hermite.o
|
||||
endif
|
||||
|
||||
ifneq ($(V), 1)
|
||||
Q := @
|
||||
endif
|
||||
|
||||
CFLAGS += -Wall -O3 -std=gnu99 -I.
|
||||
CXXFLAGS += -Wall -O3 -pedantic -I.
|
||||
CFLAGS += -Wall -O3 -I.
|
||||
ifeq ($(CXX_BUILD), 1)
|
||||
CFLAGS += -std=c++0x -xc++ -D__STDC_CONSTANT_MACROS
|
||||
else
|
||||
CFLAGS += -std=gnu99
|
||||
endif
|
||||
|
||||
all: $(TARGET) $(JTARGET)
|
||||
|
||||
@@ -150,13 +170,12 @@ $(TARGET): $(OBJ)
|
||||
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
@$(if $(Q), $(shell echo echo CC $<),)
|
||||
|
||||
%.o: %.cpp
|
||||
$(Q)$(CXX) $(CXXFLAGS) $(DEFINES) -c -o $@ $<
|
||||
@$(if $(Q), $(shell echo echo CXX $<),)
|
||||
|
||||
|
||||
$(JTARGET): $(JOBJ)
|
||||
ifeq ($(CXX_BUILD), 1)
|
||||
$(Q)$(CXX) -o $@ $(JOBJ) -lSDL $(LDFLAGS)
|
||||
else
|
||||
$(Q)$(CC) -o $@ $(JOBJ) -lSDL $(LDFLAGS)
|
||||
endif
|
||||
@$(if $(Q), $(shell echo echo LD $@),)
|
||||
|
||||
clean:
|
||||
@@ -173,10 +192,10 @@ clean:
|
||||
rm -f tools/*.o
|
||||
|
||||
dist_x86: all
|
||||
zip -r ssnes-win32-0.9.2.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
zip -r ssnes-win32-0.9.5.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
|
||||
dist_x86_64: all
|
||||
zip -r ssnes-win64-0.9.2.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
zip -r ssnes-win64-0.9.5.zip $(TARGET) $(JTARGET) ssnes.cfg snes.dll SDL.dll
|
||||
|
||||
libs_x86:
|
||||
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
###
|
||||
##
|
||||
# Makefile for SSNES libxenon.
|
||||
##
|
||||
###
|
||||
##
|
||||
|
||||
DEBUG = 0
|
||||
|
||||
CC = xenon-gcc
|
||||
CXX = xenon-g++
|
||||
OBJCOPY = xenon-objcopy
|
||||
STRIP = xenon-strip
|
||||
|
||||
PPU_TARGET := ssnes-libxenon.elf
|
||||
PPU_TARGET_ADJUSTED := ssnes-libxenon.elf32
|
||||
|
||||
LDDIRS = -L. -L$(DEVKITXENON)/usr/lib -L$(DEVKITXENON)/xenon/lib/32
|
||||
INCDIRS = -I. -I$(DEVKITXENON)/usr/include
|
||||
|
||||
OBJ = fifo_buffer.o ssnes.o driver.o file.o settings.o message.o rewind.o movie.o gfx/gfx_common.o ups.o bps.o strl.o screenshot.o audio/hermite.o dynamic.o audio/utils.o conf/config_file.o xenon/main.o xenon/xenon360_audio.o xenon/xenon360_input.o xenon/xenon360_video.o
|
||||
|
||||
LIBS = -lsnes -lxenon -lm -lc
|
||||
DEFINES = -std=gnu99 -DHAVE_CONFIGFILE=1 -DPACKAGE_VERSION=\"0.9.5\" -DSSNES_CONSOLE -DHAVE_GETOPT_LONG=1 -Dmain=ssnes_main
|
||||
DEFINES += -maltivec -mhard-float -m32 -mpowerpc64 -mcpu=cell -mtune=cell -fno-pic -g -Wall -DXENON $(INCDIRS)
|
||||
DEFINES += -u read -u _start -u exc_base
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
OPTIMIZE_LV := -O0 -g
|
||||
else
|
||||
OPTIMIZE_LV := -O3
|
||||
endif
|
||||
|
||||
all: $(PPU_TARGET_ADJUSTED)
|
||||
|
||||
CFLAGS := $(OPTIMIZE_LV) $(DEFINES)
|
||||
|
||||
LDFLAGS := -m32 -n -T $(DEVKITXENON)/app.lds
|
||||
|
||||
$(PPU_TARGET_ADJUSTED): $(PPU_TARGET)
|
||||
$(OBJCOPY) -O elf32-powerpc --adjust-vma 0x80000000 $< $@
|
||||
$(STRIP) $@
|
||||
|
||||
$(PPU_TARGET): $(OBJ)
|
||||
$(CXX) -o $@ $(OBJ) $(LDFLAGS) $(LDDIRS) $(LIBS)
|
||||
|
||||
%.o: %.c config.h
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f $(PPU_TARGET)
|
||||
rm -f $(OBJ)
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
@@ -16,6 +16,14 @@ Latest Windows binaries are currently hosted on my [homepage](http://themaister.
|
||||
SSNES attempts to be very small and lean, while still having all the useful core features expected from an emulator.
|
||||
It is used through command-line.
|
||||
|
||||
# Platforms
|
||||
|
||||
SSNES has been ported to the following platforms :
|
||||
|
||||
- PlayStation3
|
||||
- Xbox 360 (Libxenon/XeXDK)
|
||||
- Wii (Libogc)
|
||||
|
||||
# Dependencies
|
||||
|
||||
SSNES requires these libraries to build:
|
||||
@@ -86,7 +94,48 @@ In MSYS:
|
||||
<tt>mingw32-make -f Makefile.win32 libs</tt>. # You will need to have wget in your patch for this command! MSYS should provide this.</br>
|
||||
<tt>mingw32-make -f Makefile.win32</tt>
|
||||
|
||||
<b>Win32 (MSVC)</b><br />
|
||||
In addition to Mingw, it is also possible to compile a Win32 version of SSNES with Microsoft Visual Studio 2010.
|
||||
|
||||
You will need Microsoft Visual Studio 2010 intalled (or higher) in order to compile SSNES with the MSVC compiler.
|
||||
|
||||
The solution file can be found at the following location:
|
||||
|
||||
<tt>msvc/SSNES/SSNES.sln</tt>
|
||||
|
||||
<b>PlayStation3</b><br/>
|
||||
|
||||
<tt>make -f Makefile.ps3</tt>
|
||||
|
||||
A PKG file will be built which you will be able to install on a jailbroken PS3.
|
||||
|
||||
NOTE: A pre-existing libsnes library needs to be present in the root directory in order to link SSNES PS3. This file needs to be called 'libsnes.a'.
|
||||
|
||||
<b> Xbox 360 (XeXDK)</b><br />
|
||||
|
||||
You will need Microsoft Visual Studio 2010 installed (or higher) in order to compile SSNES 360.
|
||||
|
||||
The solution file can be found at the following location:
|
||||
|
||||
<tt>msvc-360/SSNES-360/SSNES-360.sln</tt>
|
||||
|
||||
NOTE: A pre-existing libsnes library needs to be present in the root directory in order to link SSNES 360.
|
||||
|
||||
<b> Xbox 360 (Libxenon)</b><br />
|
||||
|
||||
You will need to have the libxenon libraries and a working Devkit Xenon toolchain installed in order to compile SSNES 360 Libxenon.
|
||||
|
||||
<tt>make -f Makefile.xenon</tt>
|
||||
|
||||
NOTE: A pre-existing libsnes library needs to be present in the root directory in order to link SSNES 360 Libxenon. This file needs to be called 'libsnes.a'.
|
||||
|
||||
<b> Wii</b><br >
|
||||
|
||||
You will need to have the libogc libraries and a working Devkit PPC toolchain installed in order to compile SSNES Wii.
|
||||
|
||||
<tt>make -f Makefile.wii</tt>
|
||||
|
||||
NOTE: A pre-existing libsnes library needs to be present in the root directory in order to link SSNES Wii. This file needs to be called 'libsnes.a'.
|
||||
|
||||
# Filters, bSNES XML shaders and Cg shader support
|
||||
|
||||
|
||||
+41
-23
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -16,10 +16,10 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
#include "../driver.h"
|
||||
#include <stdlib.h>
|
||||
#include <asoundlib.h>
|
||||
#include "general.h"
|
||||
#include "../general.h"
|
||||
|
||||
#define TRY_ALSA(x) if (x < 0) { \
|
||||
goto error; \
|
||||
@@ -30,11 +30,13 @@ typedef struct alsa
|
||||
snd_pcm_t *pcm;
|
||||
bool nonblock;
|
||||
bool has_float;
|
||||
|
||||
size_t buffer_size;
|
||||
} alsa_t;
|
||||
|
||||
static bool alsa_use_float(void *data)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
alsa_t *alsa = (alsa_t*)data;
|
||||
return alsa->has_float;
|
||||
}
|
||||
|
||||
@@ -51,7 +53,7 @@ static bool find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
||||
|
||||
static void *alsa_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
alsa_t *alsa = calloc(1, sizeof(alsa_t));
|
||||
alsa_t *alsa = (alsa_t*)calloc(1, sizeof(alsa_t));
|
||||
if (!alsa)
|
||||
return NULL;
|
||||
|
||||
@@ -62,14 +64,17 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency)
|
||||
if (device)
|
||||
alsa_dev = device;
|
||||
|
||||
TRY_ALSA(snd_pcm_open(&alsa->pcm, alsa_dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK));
|
||||
unsigned latency_usec = latency * 1000;
|
||||
unsigned channels = 2;
|
||||
unsigned periods = 4;
|
||||
snd_pcm_uframes_t buffer_size;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
unsigned int latency_usec = latency * 1000;
|
||||
TRY_ALSA(snd_pcm_open(&alsa->pcm, alsa_dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK));
|
||||
|
||||
TRY_ALSA(snd_pcm_hw_params_malloc(¶ms));
|
||||
alsa->has_float = find_float_format(alsa->pcm, params);
|
||||
snd_pcm_format_t format = alsa->has_float ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16;
|
||||
unsigned int channels = 2;
|
||||
format = alsa->has_float ? SND_PCM_FORMAT_FLOAT : SND_PCM_FORMAT_S16;
|
||||
|
||||
TRY_ALSA(snd_pcm_hw_params_any(alsa->pcm, params));
|
||||
TRY_ALSA(snd_pcm_hw_params_set_access(alsa->pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED));
|
||||
@@ -78,16 +83,15 @@ static void *alsa_init(const char *device, unsigned rate, unsigned latency)
|
||||
TRY_ALSA(snd_pcm_hw_params_set_rate(alsa->pcm, params, rate, 0));
|
||||
|
||||
TRY_ALSA(snd_pcm_hw_params_set_buffer_time_near(alsa->pcm, params, &latency_usec, NULL));
|
||||
unsigned periods = 4;
|
||||
TRY_ALSA(snd_pcm_hw_params_set_periods_near(alsa->pcm, params, &periods, NULL));
|
||||
|
||||
TRY_ALSA(snd_pcm_hw_params(alsa->pcm, params));
|
||||
|
||||
snd_pcm_uframes_t buffer_size;
|
||||
snd_pcm_hw_params_get_period_size(params, &buffer_size, NULL);
|
||||
SSNES_LOG("ALSA: Period size: %d frames\n", (int)buffer_size);
|
||||
snd_pcm_hw_params_get_buffer_size(params, &buffer_size);
|
||||
SSNES_LOG("ALSA: Buffer size: %d frames\n", (int)buffer_size);
|
||||
alsa->buffer_size = snd_pcm_frames_to_bytes(alsa->pcm, buffer_size);
|
||||
|
||||
TRY_ALSA(snd_pcm_sw_params_malloc(&sw_params));
|
||||
TRY_ALSA(snd_pcm_sw_params_current(alsa->pcm, sw_params));
|
||||
@@ -119,14 +123,14 @@ error:
|
||||
|
||||
static ssize_t alsa_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
alsa_t *alsa = (alsa_t*)data;
|
||||
|
||||
snd_pcm_sframes_t frames;
|
||||
snd_pcm_sframes_t written = 0;
|
||||
int rc;
|
||||
size = snd_pcm_bytes_to_frames(alsa->pcm, size); // Frames to write
|
||||
|
||||
while (written < size)
|
||||
while (written < (snd_pcm_sframes_t)size)
|
||||
{
|
||||
if (!alsa->nonblock)
|
||||
{
|
||||
@@ -166,7 +170,7 @@ static bool alsa_stop(void *data)
|
||||
|
||||
static void alsa_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
alsa_t *alsa = (alsa_t*)data;
|
||||
alsa->nonblock = state;
|
||||
}
|
||||
|
||||
@@ -177,7 +181,7 @@ static bool alsa_start(void *data)
|
||||
|
||||
static void alsa_free(void *data)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
alsa_t *alsa = (alsa_t*)data;
|
||||
if (alsa)
|
||||
{
|
||||
if (alsa->pcm)
|
||||
@@ -189,14 +193,28 @@ static void alsa_free(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
static size_t alsa_write_avail(void *data)
|
||||
{
|
||||
alsa_t *alsa = (alsa_t*)data;
|
||||
return snd_pcm_frames_to_bytes(alsa->pcm, snd_pcm_avail_update(alsa->pcm));
|
||||
}
|
||||
|
||||
static size_t alsa_buffer_size(void *data)
|
||||
{
|
||||
alsa_t *alsa = (alsa_t*)data;
|
||||
return alsa->buffer_size;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_alsa = {
|
||||
.init = alsa_init,
|
||||
.write = alsa_write,
|
||||
.stop = alsa_stop,
|
||||
.start = alsa_start,
|
||||
.use_float = alsa_use_float,
|
||||
.set_nonblock_state = alsa_set_nonblock_state,
|
||||
.free = alsa_free,
|
||||
.ident = "alsa"
|
||||
alsa_init,
|
||||
alsa_write,
|
||||
alsa_stop,
|
||||
alsa_start,
|
||||
alsa_set_nonblock_state,
|
||||
alsa_free,
|
||||
alsa_use_float,
|
||||
"alsa",
|
||||
alsa_write_avail,
|
||||
alsa_buffer_size,
|
||||
};
|
||||
|
||||
+69
-48
@@ -1,6 +1,6 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011 - Chris Moeller
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Chris Moeller
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "general.h"
|
||||
#include "fifo_buffer.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "../boolean.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
@@ -39,11 +39,12 @@ typedef struct coreaudio
|
||||
|
||||
fifo_buffer_t *buffer;
|
||||
bool nonblock;
|
||||
size_t buffer_size;
|
||||
} coreaudio_t;
|
||||
|
||||
static void coreaudio_free(void *data)
|
||||
{
|
||||
coreaudio_t *dev = data;
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
@@ -66,7 +67,7 @@ static OSStatus audio_cb(void *userdata, AudioUnitRenderActionFlags *action_flag
|
||||
const AudioTimeStamp *time_stamp, UInt32 bus_number,
|
||||
UInt32 number_frames, AudioBufferList *io_data)
|
||||
{
|
||||
coreaudio_t *dev = userdata;
|
||||
coreaudio_t *dev = (coreaudio_t*)userdata;
|
||||
(void)time_stamp;
|
||||
(void)bus_number;
|
||||
(void)number_frames;
|
||||
@@ -99,7 +100,7 @@ static void *coreaudio_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
|
||||
coreaudio_t *dev = calloc(1, sizeof(*dev));
|
||||
coreaudio_t *dev = (coreaudio_t*)calloc(1, sizeof(*dev));
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
@@ -115,40 +116,46 @@ static void *coreaudio_init(const char *device, unsigned rate, unsigned latency)
|
||||
AudioObjectSetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
|
||||
sizeof(CFRunLoopRef), &run_loop);
|
||||
|
||||
ComponentDescription desc = {
|
||||
.componentType = kAudioUnitType_Output,
|
||||
.componentSubType = kAudioUnitSubType_HALOutput,
|
||||
.componentManufacturer = kAudioUnitManufacturer_Apple,
|
||||
};
|
||||
ComponentDescription desc = {0};
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
|
||||
Component comp = FindNextComponent(NULL, &desc);
|
||||
AudioStreamBasicDescription stream_desc = {0};
|
||||
AudioStreamBasicDescription real_desc;
|
||||
AudioChannelLayout layout = {0};
|
||||
AURenderCallbackStruct cb = {0};
|
||||
|
||||
Component comp = NULL;
|
||||
OSStatus res = noErr;
|
||||
UInt32 i_size = 0;
|
||||
size_t fifo_size;
|
||||
|
||||
comp = FindNextComponent(NULL, &desc);
|
||||
if (comp == NULL)
|
||||
goto error;
|
||||
|
||||
OSStatus res = OpenAComponent(comp, &dev->dev);
|
||||
res = OpenAComponent(comp, &dev->dev);
|
||||
if (res != noErr)
|
||||
goto error;
|
||||
|
||||
dev->dev_alive = true;
|
||||
|
||||
AudioStreamBasicDescription stream_desc = {
|
||||
.mSampleRate = rate,
|
||||
.mBitsPerChannel = sizeof(float) * CHAR_BIT,
|
||||
.mChannelsPerFrame = 2,
|
||||
.mBytesPerPacket = 2 * sizeof(float),
|
||||
.mBytesPerFrame = 2 * sizeof(float),
|
||||
.mFramesPerPacket = 1,
|
||||
.mFormatID = kAudioFormatLinearPCM,
|
||||
.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | (is_little_endian() ? 0 : kAudioFormatFlagIsBigEndian),
|
||||
};
|
||||
stream_desc.mSampleRate = rate;
|
||||
stream_desc.mBitsPerChannel = sizeof(float) * CHAR_BIT;
|
||||
stream_desc.mChannelsPerFrame = 2;
|
||||
stream_desc.mBytesPerPacket = 2 * sizeof(float);
|
||||
stream_desc.mBytesPerFrame = 2 * sizeof(float);
|
||||
stream_desc.mFramesPerPacket = 1;
|
||||
stream_desc.mFormatID = kAudioFormatLinearPCM;
|
||||
stream_desc.mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsPacked | (is_little_endian() ? 0 : kAudioFormatFlagIsBigEndian);
|
||||
|
||||
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_StreamFormat,
|
||||
kAudioUnitScope_Input, 0, &stream_desc, sizeof(stream_desc));
|
||||
if (res != noErr)
|
||||
goto error;
|
||||
|
||||
AudioStreamBasicDescription real_desc;
|
||||
UInt32 i_size = sizeof(real_desc);
|
||||
i_size = sizeof(real_desc);
|
||||
res = AudioUnitGetProperty(dev->dev, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &real_desc, &i_size);
|
||||
if (res != noErr)
|
||||
goto error;
|
||||
@@ -165,20 +172,16 @@ static void *coreaudio_init(const char *device, unsigned rate, unsigned latency)
|
||||
if (real_desc.mFormatID != stream_desc.mFormatID)
|
||||
goto error;
|
||||
|
||||
AudioChannelLayout layout = {
|
||||
.mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap,
|
||||
.mChannelBitmap = (1 << 2) - 1,
|
||||
};
|
||||
layout.mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap;
|
||||
layout.mChannelBitmap = 0x03;
|
||||
|
||||
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_AudioChannelLayout,
|
||||
kAudioUnitScope_Input, 0, &layout, sizeof(layout));
|
||||
if (res != noErr)
|
||||
goto error;
|
||||
|
||||
AURenderCallbackStruct cb = {
|
||||
.inputProc = audio_cb,
|
||||
.inputProcRefCon = dev,
|
||||
};
|
||||
cb.inputProc = audio_cb;
|
||||
cb.inputProcRefCon = dev;
|
||||
|
||||
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, 0, &cb, sizeof(cb));
|
||||
@@ -189,8 +192,9 @@ static void *coreaudio_init(const char *device, unsigned rate, unsigned latency)
|
||||
if (res != noErr)
|
||||
goto error;
|
||||
|
||||
size_t fifo_size = (latency * g_settings.audio.out_rate) / 1000;
|
||||
fifo_size = (latency * g_settings.audio.out_rate) / 1000;
|
||||
fifo_size *= 2 * sizeof(float);
|
||||
dev->buffer_size = fifo_size;
|
||||
|
||||
dev->buffer = fifo_new(fifo_size);
|
||||
if (!dev->buffer)
|
||||
@@ -212,9 +216,9 @@ error:
|
||||
|
||||
static ssize_t coreaudio_write(void *data, const void *buf_, size_t size)
|
||||
{
|
||||
coreaudio_t *dev = data;
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
|
||||
const uint8_t *buf = buf_;
|
||||
const uint8_t *buf = (const uint8_t*)buf_;
|
||||
size_t written = 0;
|
||||
|
||||
while (size > 0)
|
||||
@@ -246,19 +250,19 @@ static ssize_t coreaudio_write(void *data, const void *buf_, size_t size)
|
||||
|
||||
static bool coreaudio_stop(void *data)
|
||||
{
|
||||
coreaudio_t *dev = data;
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
return AudioOutputUnitStop(dev->dev) == noErr;
|
||||
}
|
||||
|
||||
static void coreaudio_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
coreaudio_t *dev = data;
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
dev->nonblock = state;
|
||||
}
|
||||
|
||||
static bool coreaudio_start(void *data)
|
||||
{
|
||||
coreaudio_t *dev = data;
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
return AudioOutputUnitStart(dev->dev) == noErr;
|
||||
}
|
||||
|
||||
@@ -268,14 +272,31 @@ static bool coreaudio_use_float(void *data)
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t coreaudio_write_avail(void *data)
|
||||
{
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
pthread_mutex_lock(&dev->lock);
|
||||
size_t avail = fifo_write_avail(dev->buffer);
|
||||
pthread_mutex_unlock(&dev->lock);
|
||||
return avail;
|
||||
}
|
||||
|
||||
static size_t coreaudio_buffer_size(void *data)
|
||||
{
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
return dev->buffer_size;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_coreaudio = {
|
||||
.init = coreaudio_init,
|
||||
.write = coreaudio_write,
|
||||
.stop = coreaudio_stop,
|
||||
.start = coreaudio_start,
|
||||
.set_nonblock_state = coreaudio_set_nonblock_state,
|
||||
.free = coreaudio_free,
|
||||
.use_float = coreaudio_use_float,
|
||||
.ident = "coreaudio"
|
||||
coreaudio_init,
|
||||
coreaudio_write,
|
||||
coreaudio_stop,
|
||||
coreaudio_start,
|
||||
coreaudio_set_nonblock_state,
|
||||
coreaudio_free,
|
||||
coreaudio_use_float,
|
||||
"coreaudio",
|
||||
coreaudio_write_avail,
|
||||
coreaudio_buffer_size,
|
||||
};
|
||||
|
||||
|
||||
+75
-36
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -15,16 +15,16 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
#include "../driver.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "../boolean.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dsound.h>
|
||||
#include "fifo_buffer.h"
|
||||
#include "general.h"
|
||||
#include "../fifo_buffer.h"
|
||||
#include "../general.h"
|
||||
|
||||
typedef struct dsound
|
||||
{
|
||||
@@ -111,7 +111,7 @@ static inline void release_region(dsound_t *ds, const struct audio_lock *region)
|
||||
|
||||
static DWORD CALLBACK dsound_thread(PVOID data)
|
||||
{
|
||||
dsound_t *ds = data;
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
|
||||
DWORD write_ptr;
|
||||
@@ -218,7 +218,7 @@ static void dsound_clear_buffer(dsound_t *ds)
|
||||
|
||||
static void dsound_free(void *data)
|
||||
{
|
||||
dsound_t *ds = data;
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
if (ds)
|
||||
{
|
||||
if (ds->thread)
|
||||
@@ -249,28 +249,53 @@ static void dsound_free(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
struct dsound_dev
|
||||
{
|
||||
unsigned device;
|
||||
unsigned total_count;
|
||||
LPGUID guid;
|
||||
};
|
||||
|
||||
static BOOL CALLBACK enumerate_cb(LPGUID guid, LPCSTR desc, LPCSTR module, LPVOID context)
|
||||
{
|
||||
struct dsound_dev *dev = (struct dsound_dev*)context;
|
||||
SSNES_LOG("\t%u: %s\n", dev->total_count, desc);
|
||||
if (dev->device == dev->total_count)
|
||||
dev->guid = guid;
|
||||
dev->total_count++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void *dsound_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
dsound_t *ds = calloc(1, sizeof(*ds));
|
||||
WAVEFORMATEX wfx = {0};
|
||||
DSBUFFERDESC bufdesc = {0};
|
||||
struct dsound_dev dev = {0};
|
||||
|
||||
dsound_t *ds = (dsound_t*)calloc(1, sizeof(*ds));
|
||||
if (!ds)
|
||||
goto error;
|
||||
|
||||
InitializeCriticalSection(&ds->crit);
|
||||
|
||||
if (DirectSoundCreate(NULL, &ds->ds, NULL) != DS_OK)
|
||||
if (device)
|
||||
dev.device = strtoul(device, NULL, 0);
|
||||
|
||||
SSNES_LOG("DirectSound devices:\n");
|
||||
DirectSoundEnumerate(enumerate_cb, &dev);
|
||||
|
||||
if (DirectSoundCreate(dev.guid, &ds->ds, NULL) != DS_OK)
|
||||
goto error;
|
||||
|
||||
if (IDirectSound_SetCooperativeLevel(ds->ds, GetDesktopWindow(), DSSCL_PRIORITY) != DS_OK)
|
||||
goto error;
|
||||
|
||||
WAVEFORMATEX wfx = {
|
||||
.wFormatTag = WAVE_FORMAT_PCM,
|
||||
.nChannels = 2,
|
||||
.nSamplesPerSec = rate,
|
||||
.wBitsPerSample = 16,
|
||||
.nBlockAlign = 2 * sizeof(int16_t),
|
||||
.nAvgBytesPerSec = rate * 2 * sizeof(int16_t),
|
||||
};
|
||||
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
||||
wfx.nChannels = 2;
|
||||
wfx.nSamplesPerSec = rate;
|
||||
wfx.wBitsPerSample = 16;
|
||||
wfx.nBlockAlign = 2 * sizeof(int16_t);
|
||||
wfx.nAvgBytesPerSec = rate * 2 * sizeof(int16_t);
|
||||
|
||||
ds->buffer_size = (latency * wfx.nAvgBytesPerSec) / 1000;
|
||||
ds->buffer_size /= CHUNK_SIZE;
|
||||
@@ -281,12 +306,10 @@ static void *dsound_init(const char *device, unsigned rate, unsigned latency)
|
||||
SSNES_LOG("[DirectSound]: Setting buffer size of %u bytes\n", ds->buffer_size);
|
||||
SSNES_LOG("[DirectSound]: Latency = %u ms\n", (unsigned)((1000 * ds->buffer_size) / wfx.nAvgBytesPerSec));
|
||||
|
||||
DSBUFFERDESC bufdesc = {
|
||||
.dwSize = sizeof(DSBUFFERDESC),
|
||||
.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS,
|
||||
.dwBufferBytes = ds->buffer_size,
|
||||
.lpwfxFormat = &wfx,
|
||||
};
|
||||
bufdesc.dwSize = sizeof(DSBUFFERDESC);
|
||||
bufdesc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS;
|
||||
bufdesc.dwBufferBytes = ds->buffer_size;
|
||||
bufdesc.lpwfxFormat = &wfx;
|
||||
|
||||
ds->event = CreateEvent(NULL, false, false, NULL);
|
||||
if (!ds->event)
|
||||
@@ -312,21 +335,21 @@ static void *dsound_init(const char *device, unsigned rate, unsigned latency)
|
||||
return ds;
|
||||
|
||||
error:
|
||||
SSNES_ERR("[DirectSound] Error occured in init!\n");
|
||||
SSNES_ERR("[DirectSound] Error occured in init.\n");
|
||||
dsound_free(ds);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool dsound_stop(void *data)
|
||||
{
|
||||
dsound_t *ds = data;
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
dsound_stop_thread(ds);
|
||||
return IDirectSoundBuffer_Stop(ds->dsb) == DS_OK;
|
||||
}
|
||||
|
||||
static bool dsound_start(void *data)
|
||||
{
|
||||
dsound_t *ds = data;
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
dsound_clear_buffer(ds);
|
||||
|
||||
if (!dsound_start_thread(ds))
|
||||
@@ -337,14 +360,14 @@ static bool dsound_start(void *data)
|
||||
|
||||
static void dsound_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
dsound_t *ds = data;
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
ds->nonblock = state;
|
||||
}
|
||||
|
||||
static ssize_t dsound_write(void *data, const void *buf_, size_t size)
|
||||
{
|
||||
dsound_t *ds = data;
|
||||
const uint8_t *buf = buf_;
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
const uint8_t *buf = (const uint8_t*)buf_;
|
||||
|
||||
if (!ds->thread_alive)
|
||||
return -1;
|
||||
@@ -374,14 +397,30 @@ static ssize_t dsound_write(void *data, const void *buf_, size_t size)
|
||||
return written;
|
||||
}
|
||||
|
||||
static size_t dsound_write_avail(void *data)
|
||||
{
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
EnterCriticalSection(&ds->crit);
|
||||
size_t avail = fifo_write_avail(ds->buffer);
|
||||
LeaveCriticalSection(&ds->crit);
|
||||
return avail;
|
||||
}
|
||||
|
||||
static size_t dsound_buffer_size(void *data)
|
||||
{
|
||||
return 4 * 1024;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_dsound = {
|
||||
.init = dsound_init,
|
||||
.write = dsound_write,
|
||||
.stop = dsound_stop,
|
||||
.start = dsound_start,
|
||||
.set_nonblock_state = dsound_set_nonblock_state,
|
||||
.free = dsound_free,
|
||||
.ident = "dsound"
|
||||
dsound_init,
|
||||
dsound_write,
|
||||
dsound_stop,
|
||||
dsound_start,
|
||||
dsound_set_nonblock_state,
|
||||
dsound_free,
|
||||
NULL,
|
||||
"dsound",
|
||||
dsound_write_avail,
|
||||
dsound_buffer_size,
|
||||
};
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
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
|
||||
@@ -1,16 +0,0 @@
|
||||
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
|
||||
@@ -1,418 +0,0 @@
|
||||
#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
|
||||
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -16,12 +16,12 @@
|
||||
*/
|
||||
|
||||
#include "ext/ssnes_audio.h"
|
||||
#include <stdbool.h>
|
||||
#include "../boolean.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "driver.h"
|
||||
#include "dynamic.h"
|
||||
#include "general.h"
|
||||
#include "../driver.h"
|
||||
#include "../dynamic.h"
|
||||
#include "../general.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef struct audio_ext
|
||||
@@ -34,7 +34,7 @@ typedef struct audio_ext
|
||||
|
||||
static void audio_ext_free(void *data)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
audio_ext_t *ext = (audio_ext_t*)data;
|
||||
if (ext)
|
||||
{
|
||||
if (ext->driver && ext->handle)
|
||||
@@ -53,10 +53,13 @@ static void *audio_ext_init(const char *device, unsigned rate, unsigned latency)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
audio_ext_t *ext = calloc(1, sizeof(*ext));
|
||||
audio_ext_t *ext = (audio_ext_t*)calloc(1, sizeof(*ext));
|
||||
if (!ext)
|
||||
return NULL;
|
||||
|
||||
ssnes_audio_driver_info_t info = {0};
|
||||
const ssnes_audio_driver_t *(*plugin_load)(void) = NULL;
|
||||
|
||||
ext->lib = dylib_load(g_settings.audio.external_driver);
|
||||
if (!ext->lib)
|
||||
{
|
||||
@@ -64,7 +67,7 @@ static void *audio_ext_init(const char *device, unsigned rate, unsigned latency)
|
||||
goto error;
|
||||
}
|
||||
|
||||
const ssnes_audio_driver_t* (*plugin_load)(void) = (const ssnes_audio_driver_t* (*)(void))dylib_proc(ext->lib, "ssnes_audio_driver_init");
|
||||
plugin_load = (const ssnes_audio_driver_t *(*)(void))dylib_proc(ext->lib, "ssnes_audio_driver_init");
|
||||
|
||||
if (!plugin_load)
|
||||
{
|
||||
@@ -87,11 +90,9 @@ static void *audio_ext_init(const char *device, unsigned rate, unsigned latency)
|
||||
goto error;
|
||||
}
|
||||
|
||||
ssnes_audio_driver_info_t info = {
|
||||
.device = device,
|
||||
.sample_rate = rate,
|
||||
.latency = latency
|
||||
};
|
||||
info.device = device;
|
||||
info.sample_rate = rate;
|
||||
info.latency = latency;
|
||||
|
||||
ext->handle = ext->driver->init(&info);
|
||||
if (!ext->handle)
|
||||
@@ -115,7 +116,7 @@ error:
|
||||
|
||||
static ssize_t audio_ext_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
audio_ext_t *ext = (audio_ext_t*)data;
|
||||
unsigned frame_size = ext->is_float ? (2 * sizeof(float)) : (2 * sizeof(int16_t));
|
||||
size /= frame_size;
|
||||
|
||||
@@ -127,37 +128,37 @@ static ssize_t audio_ext_write(void *data, const void *buf, size_t size)
|
||||
|
||||
static bool audio_ext_start(void *data)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
audio_ext_t *ext = (audio_ext_t*)data;
|
||||
return ext->driver->start(ext->handle);
|
||||
}
|
||||
|
||||
static bool audio_ext_stop(void *data)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
audio_ext_t *ext = (audio_ext_t*)data;
|
||||
return ext->driver->stop(ext->handle);
|
||||
}
|
||||
|
||||
static void audio_ext_set_nonblock_state(void *data, bool toggle)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
audio_ext_t *ext = (audio_ext_t*)data;
|
||||
ext->driver->set_nonblock_state(ext->handle, toggle);
|
||||
}
|
||||
|
||||
static bool audio_ext_use_float(void *data)
|
||||
{
|
||||
audio_ext_t *ext = data;
|
||||
audio_ext_t *ext = (audio_ext_t*)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"
|
||||
audio_ext_init,
|
||||
audio_ext_write,
|
||||
audio_ext_stop,
|
||||
audio_ext_start,
|
||||
audio_ext_set_nonblock_state,
|
||||
audio_ext_free,
|
||||
audio_ext_use_float,
|
||||
"ext"
|
||||
};
|
||||
|
||||
+24
-14
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -17,17 +17,31 @@
|
||||
|
||||
// Hermite resampler based on bsnes' audio library.
|
||||
|
||||
#include "hermite.h"
|
||||
#include "resampler.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "../boolean.h"
|
||||
|
||||
#define CHANNELS 2
|
||||
|
||||
struct hermite_resampler
|
||||
struct ssnes_resampler
|
||||
{
|
||||
float chan_data[CHANNELS][4];
|
||||
double r_frac;
|
||||
};
|
||||
|
||||
void resampler_preinit(ssnes_resampler_t *re, double omega, double *samples_offset)
|
||||
{
|
||||
*samples_offset = 2.0;
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
re->chan_data[0][i] = (float)cos((i - 2) * omega);
|
||||
re->chan_data[1][i] = re->chan_data[0][i];
|
||||
}
|
||||
|
||||
re->r_frac = 0.0;
|
||||
}
|
||||
|
||||
static inline float hermite_kernel(float mu1, float a, float b, float c, float d)
|
||||
{
|
||||
float mu2, mu3, m0, m1, a0, a1, a2, a3;
|
||||
@@ -35,8 +49,8 @@ static inline float hermite_kernel(float mu1, float a, float b, float c, float d
|
||||
mu2 = mu1 * mu1;
|
||||
mu3 = mu2 * mu1;
|
||||
|
||||
m0 = (c - a) * 0.5;
|
||||
m1 = (d - b) * 0.5;
|
||||
m0 = (c - a) * 0.5f;
|
||||
m1 = (d - b) * 0.5f;
|
||||
|
||||
a0 = +2 * mu3 - 3 * mu2 + 1;
|
||||
a1 = mu3 - 2 * mu2 + mu1;
|
||||
@@ -46,16 +60,12 @@ static inline float hermite_kernel(float mu1, float a, float b, float c, float d
|
||||
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
|
||||
hermite_resampler_t *hermite_new(void)
|
||||
ssnes_resampler_t *resampler_new(void)
|
||||
{
|
||||
hermite_resampler_t *re = calloc(1, sizeof(*re));
|
||||
if (!re)
|
||||
return NULL;
|
||||
return re;
|
||||
return (ssnes_resampler_t*)calloc(1, sizeof(ssnes_resampler_t));
|
||||
}
|
||||
|
||||
// We make sure to allocate enough output data beforehand ... ;)
|
||||
void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
|
||||
void resampler_process(ssnes_resampler_t *re, struct resampler_data *data)
|
||||
{
|
||||
double r_step = 1.0 / data->ratio;
|
||||
size_t processed_out = 0;
|
||||
@@ -71,7 +81,7 @@ void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
|
||||
re->r_frac += r_step;
|
||||
for (unsigned i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
float res = hermite_kernel(re->r_frac,
|
||||
float res = hermite_kernel((float)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;
|
||||
}
|
||||
@@ -91,7 +101,7 @@ void hermite_process(hermite_resampler_t *re, struct hermite_data *data)
|
||||
data->output_frames = processed_out;
|
||||
}
|
||||
|
||||
void hermite_free(hermite_resampler_t *re)
|
||||
void resampler_free(ssnes_resampler_t *re)
|
||||
{
|
||||
free(re);
|
||||
}
|
||||
|
||||
+53
-35
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -16,16 +16,16 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
#include "../driver.h"
|
||||
#include <stdlib.h>
|
||||
#include "general.h"
|
||||
#include "../general.h"
|
||||
|
||||
#include <jack/jack.h>
|
||||
#include <jack/types.h>
|
||||
#include <jack/ringbuffer.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "../boolean.h"
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
@@ -41,11 +41,12 @@ typedef struct jack
|
||||
|
||||
pthread_cond_t cond;
|
||||
pthread_mutex_t cond_lock;
|
||||
size_t buffer_size;
|
||||
} jack_t;
|
||||
|
||||
static int process_cb(jack_nframes_t nframes, void *data)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jack_t *jd = (jack_t*)data;
|
||||
if (nframes <= 0)
|
||||
{
|
||||
pthread_cond_signal(&jd->cond);
|
||||
@@ -62,7 +63,7 @@ static int process_cb(jack_nframes_t nframes, void *data)
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
jack_default_audio_sample_t *out = jack_port_get_buffer(jd->ports[i], nframes);
|
||||
jack_default_audio_sample_t *out = (jack_default_audio_sample_t*)jack_port_get_buffer(jd->ports[i], nframes);
|
||||
assert(out);
|
||||
jack_ringbuffer_read(jd->buffer[i], (char*)out, min_avail * sizeof(jack_default_audio_sample_t));
|
||||
|
||||
@@ -77,7 +78,7 @@ static int process_cb(jack_nframes_t nframes, void *data)
|
||||
|
||||
static void shutdown_cb(void *data)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jack_t *jd = (jack_t*)data;
|
||||
jd->shutdown = true;
|
||||
pthread_cond_signal(&jd->cond);
|
||||
}
|
||||
@@ -108,7 +109,7 @@ static size_t find_buffersize(jack_t *jd, int latency)
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
jack_port_get_latency_range(jd->ports[i], JackPlaybackLatency, &range);
|
||||
if (range.max > jack_latency)
|
||||
if ((int)range.max > jack_latency)
|
||||
jack_latency = range.max;
|
||||
}
|
||||
|
||||
@@ -126,7 +127,7 @@ static size_t find_buffersize(jack_t *jd, int latency)
|
||||
|
||||
static void *ja_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
jack_t *jd = calloc(1, sizeof(jack_t));
|
||||
jack_t *jd = (jack_t*)calloc(1, sizeof(jack_t));
|
||||
if (!jd)
|
||||
return NULL;
|
||||
|
||||
@@ -134,6 +135,9 @@ static void *ja_init(const char *device, unsigned rate, unsigned latency)
|
||||
pthread_mutex_init(&jd->cond_lock, NULL);
|
||||
|
||||
const char **jports = NULL;
|
||||
char *dest_ports[2];
|
||||
size_t bufsize = 0;
|
||||
int parsed = 0;
|
||||
|
||||
jd->client = jack_client_open("SSNES", JackNullOption, NULL);
|
||||
if (jd->client == NULL)
|
||||
@@ -152,7 +156,6 @@ static void *ja_init(const char *device, unsigned rate, unsigned latency)
|
||||
goto error;
|
||||
}
|
||||
|
||||
char *dest_ports[2];
|
||||
jports = jack_get_ports(jd->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
|
||||
if (jports == NULL)
|
||||
{
|
||||
@@ -160,7 +163,9 @@ static void *ja_init(const char *device, unsigned rate, unsigned latency)
|
||||
goto error;
|
||||
}
|
||||
|
||||
size_t bufsize = find_buffersize(jd, latency);
|
||||
bufsize = find_buffersize(jd, latency);
|
||||
jd->buffer_size = bufsize;
|
||||
|
||||
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
@@ -172,7 +177,7 @@ static void *ja_init(const char *device, unsigned rate, unsigned latency)
|
||||
}
|
||||
}
|
||||
|
||||
int parsed = parse_ports(dest_ports, jports);
|
||||
parsed = parse_ports(dest_ports, jports);
|
||||
|
||||
if (jack_activate(jd->client) < 0)
|
||||
{
|
||||
@@ -191,7 +196,6 @@ static void *ja_init(const char *device, unsigned rate, unsigned latency)
|
||||
|
||||
for (int i = 0; i < parsed; i++)
|
||||
free(dest_ports[i]);
|
||||
|
||||
|
||||
jack_free(jports);
|
||||
return jd;
|
||||
@@ -204,7 +208,7 @@ error:
|
||||
|
||||
static size_t write_buffer(jack_t *jd, const float *buf, size_t size)
|
||||
{
|
||||
jack_default_audio_sample_t out_deinterleaved_buffer[2][FRAMES(size)];
|
||||
jack_default_audio_sample_t out_deinterleaved_buffer[2][AUDIO_CHUNK_SIZE_NONBLOCKING];
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
for (size_t j = 0; j < FRAMES(size); j++)
|
||||
@@ -218,9 +222,11 @@ static size_t write_buffer(jack_t *jd, const float *buf, size_t size)
|
||||
if (jd->shutdown)
|
||||
return 0;
|
||||
|
||||
size_t avail[2];
|
||||
avail[0] = jack_ringbuffer_write_space(jd->buffer[0]);
|
||||
avail[1] = jack_ringbuffer_write_space(jd->buffer[1]);
|
||||
size_t avail[2] = {
|
||||
jack_ringbuffer_write_space(jd->buffer[0]),
|
||||
jack_ringbuffer_write_space(jd->buffer[1]),
|
||||
};
|
||||
|
||||
size_t min_avail = avail[0] < avail[1] ? avail[0] : avail[1];
|
||||
min_avail /= sizeof(float);
|
||||
|
||||
@@ -229,7 +235,10 @@ static size_t write_buffer(jack_t *jd, const float *buf, size_t size)
|
||||
if (write_frames > 0)
|
||||
{
|
||||
for (int i = 0; i < 2; i++)
|
||||
jack_ringbuffer_write(jd->buffer[i], (const char*)&out_deinterleaved_buffer[i][written], write_frames * sizeof(jack_default_audio_sample_t));
|
||||
{
|
||||
jack_ringbuffer_write(jd->buffer[i], (const char*)&out_deinterleaved_buffer[i][written],
|
||||
write_frames * sizeof(jack_default_audio_sample_t));
|
||||
}
|
||||
written += write_frames;
|
||||
}
|
||||
else
|
||||
@@ -248,9 +257,9 @@ static size_t write_buffer(jack_t *jd, const float *buf, size_t size)
|
||||
|
||||
static ssize_t ja_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jack_t *jd = (jack_t*)data;
|
||||
|
||||
return write_buffer(jd, buf, size);
|
||||
return write_buffer(jd, (const float*)buf, size);
|
||||
}
|
||||
|
||||
static bool ja_stop(void *data)
|
||||
@@ -261,7 +270,7 @@ static bool ja_stop(void *data)
|
||||
|
||||
static void ja_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jack_t *jd = (jack_t*)data;
|
||||
jd->nonblock = state;
|
||||
}
|
||||
|
||||
@@ -273,7 +282,7 @@ static bool ja_start(void *data)
|
||||
|
||||
static void ja_free(void *data)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jack_t *jd = (jack_t*)data;
|
||||
|
||||
jd->shutdown = true;
|
||||
|
||||
@@ -298,19 +307,28 @@ static bool ja_use_float(void *data)
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t ja_write_avail(void *data)
|
||||
{
|
||||
jack_t *jd = (jack_t*)data;
|
||||
return jack_ringbuffer_write_space(jd->buffer[0]);
|
||||
}
|
||||
|
||||
static size_t ja_buffer_size(void *data)
|
||||
{
|
||||
jack_t *jd = (jack_t*)data;
|
||||
return jd->buffer_size;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_jack = {
|
||||
.init = ja_init,
|
||||
.write = ja_write,
|
||||
.stop = ja_stop,
|
||||
.start = ja_start,
|
||||
.set_nonblock_state = ja_set_nonblock_state,
|
||||
.free = ja_free,
|
||||
.use_float = ja_use_float,
|
||||
.ident = "jack"
|
||||
ja_init,
|
||||
ja_write,
|
||||
ja_stop,
|
||||
ja_start,
|
||||
ja_set_nonblock_state,
|
||||
ja_free,
|
||||
ja_use_float,
|
||||
"jack",
|
||||
ja_write_avail,
|
||||
ja_buffer_size,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+20
-21
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -15,8 +15,7 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
#include "../driver.h"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenAL/al.h>
|
||||
@@ -59,7 +58,7 @@ typedef struct al
|
||||
static void *al_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
al_t *al = calloc(1, sizeof(al_t));
|
||||
al_t *al = (al_t*)calloc(1, sizeof(al_t));
|
||||
if (!al)
|
||||
return NULL;
|
||||
|
||||
@@ -76,8 +75,8 @@ static void *al_init(const char *device, unsigned rate, unsigned latency)
|
||||
al->rate = rate;
|
||||
|
||||
al->num_buffers = (latency * rate * 2 * 2) / (1000 * BUFSIZE);
|
||||
al->buffers = calloc(al->num_buffers, sizeof(ALuint));
|
||||
al->res_buf = calloc(al->num_buffers, sizeof(ALuint));
|
||||
al->buffers = (ALuint*)calloc(al->num_buffers, sizeof(ALuint));
|
||||
al->res_buf = (ALuint*)calloc(al->num_buffers, sizeof(ALuint));
|
||||
if (al->buffers == NULL || al->res_buf == NULL)
|
||||
goto error;
|
||||
|
||||
@@ -127,10 +126,9 @@ static bool al_get_buffer(al_t *al, ALuint *buffer)
|
||||
if (al->res_ptr == 0)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
struct timespec tv = {
|
||||
.tv_sec = 0,
|
||||
.tv_nsec = 1000000
|
||||
};
|
||||
struct timespec tv = {0};
|
||||
tv.tv_sec = 0;
|
||||
tv.tv_nsec = 1000000;
|
||||
#endif
|
||||
|
||||
for (;;)
|
||||
@@ -155,7 +153,7 @@ static bool al_get_buffer(al_t *al, ALuint *buffer)
|
||||
|
||||
static size_t al_fill_internal_buf(al_t *al, const void *buf, size_t size)
|
||||
{
|
||||
size_t read_size = (BUFSIZE - al->tmpbuf_ptr > size) ? size : (BUFSIZE - al->tmpbuf_ptr);
|
||||
size_t read_size = (BUFSIZE - al->tmpbuf_ptr > (ssize_t)size) ? size : (BUFSIZE - al->tmpbuf_ptr);
|
||||
memcpy(al->tmpbuf + al->tmpbuf_ptr, buf, read_size);
|
||||
al->tmpbuf_ptr += read_size;
|
||||
return read_size;
|
||||
@@ -163,7 +161,7 @@ static size_t al_fill_internal_buf(al_t *al, const void *buf, size_t size)
|
||||
|
||||
static ssize_t al_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
al_t *al = data;
|
||||
al_t *al = (al_t*)data;
|
||||
|
||||
size_t written = 0;
|
||||
while (written < size)
|
||||
@@ -204,7 +202,7 @@ static bool al_stop(void *data)
|
||||
|
||||
static void al_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
al_t *al = data;
|
||||
al_t *al = (al_t*)data;
|
||||
al->nonblock = state;
|
||||
}
|
||||
|
||||
@@ -216,7 +214,7 @@ static bool al_start(void *data)
|
||||
|
||||
static void al_free(void *data)
|
||||
{
|
||||
al_t *al= data;
|
||||
al_t *al = (al_t*)data;
|
||||
if (al)
|
||||
{
|
||||
alSourceStop(al->source);
|
||||
@@ -235,12 +233,13 @@ static void al_free(void *data)
|
||||
}
|
||||
|
||||
const audio_driver_t audio_openal = {
|
||||
.init = al_init,
|
||||
.write = al_write,
|
||||
.stop = al_stop,
|
||||
.start = al_start,
|
||||
.set_nonblock_state = al_set_nonblock_state,
|
||||
.free = al_free,
|
||||
.ident = "openal"
|
||||
al_init,
|
||||
al_write,
|
||||
al_stop,
|
||||
al_start,
|
||||
al_set_nonblock_state,
|
||||
al_free,
|
||||
NULL,
|
||||
"openal"
|
||||
};
|
||||
|
||||
+61
-23
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
static void *oss_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
int *fd = calloc(1, sizeof(int));
|
||||
int *fd = (int*)calloc(1, sizeof(int));
|
||||
if (fd == NULL)
|
||||
return NULL;
|
||||
|
||||
@@ -55,18 +55,15 @@ static void *oss_init(const char *device, unsigned rate, unsigned latency)
|
||||
if ((*fd = open(oss_device, O_WRONLY)) < 0)
|
||||
{
|
||||
free(fd);
|
||||
perror("open");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int frags = (latency * rate * 4)/(1000 * (1 << 9));
|
||||
int frag = (frags << 16) | 9;
|
||||
int frags = (latency * rate * 4) / (1000 * (1 << 10));
|
||||
int frag = (frags << 16) | 10;
|
||||
|
||||
if (ioctl(*fd, SNDCTL_DSP_SETFRAGMENT, &frag) < 0)
|
||||
{
|
||||
close(*fd);
|
||||
free(fd);
|
||||
return NULL;
|
||||
}
|
||||
SSNES_WARN("Cannot set fragment sizes. Latency might not be as expected ...\n");
|
||||
|
||||
int channels = 2;
|
||||
int format = is_little_endian() ?
|
||||
@@ -76,6 +73,7 @@ static void *oss_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
close(*fd);
|
||||
free(fd);
|
||||
perror("ioctl");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -83,22 +81,31 @@ static void *oss_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
close(*fd);
|
||||
free(fd);
|
||||
perror("ioctl");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ioctl(*fd, SNDCTL_DSP_SPEED, &rate) < 0)
|
||||
int new_rate = rate;
|
||||
if (ioctl(*fd, SNDCTL_DSP_SPEED, &new_rate) < 0)
|
||||
{
|
||||
close(*fd);
|
||||
free(fd);
|
||||
perror("ioctl");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (new_rate != (int)rate)
|
||||
{
|
||||
SSNES_WARN("Requested sample rate not supported. Adjusting output rate to %d Hz.\n", new_rate);
|
||||
g_settings.audio.out_rate = new_rate;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static ssize_t oss_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
int *fd = data;
|
||||
int *fd = (int*)data;
|
||||
|
||||
if (size == 0)
|
||||
return 0;
|
||||
@@ -116,7 +123,7 @@ static ssize_t oss_write(void *data, const void *buf, size_t size)
|
||||
|
||||
static bool oss_stop(void *data)
|
||||
{
|
||||
int *fd = data;
|
||||
int *fd = (int*)data;
|
||||
ioctl(*fd, SNDCTL_DSP_RESET, 0);
|
||||
return true;
|
||||
}
|
||||
@@ -128,32 +135,63 @@ static bool oss_start(void *data)
|
||||
|
||||
static void oss_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
int *fd = data;
|
||||
int *fd = (int*)data;
|
||||
int rc;
|
||||
if (state)
|
||||
rc = fcntl(*fd, F_SETFL, fcntl(*fd, F_GETFL) | O_NONBLOCK);
|
||||
else
|
||||
rc = fcntl(*fd, F_SETFL, fcntl(*fd, F_GETFL) & (~O_NONBLOCK));
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "SSNES [ERROR]: Could not set nonblocking on OSS file descriptor. Will not be able to fast-forward.\n");
|
||||
SSNES_WARN("Could not set nonblocking on OSS file descriptor. Will not be able to fast-forward.\n");
|
||||
}
|
||||
|
||||
static void oss_free(void *data)
|
||||
{
|
||||
int *fd = data;
|
||||
int *fd = (int*)data;
|
||||
|
||||
ioctl(*fd, SNDCTL_DSP_RESET, 0);
|
||||
close(*fd);
|
||||
free(fd);
|
||||
}
|
||||
|
||||
static size_t oss_write_avail(void *data)
|
||||
{
|
||||
int *fd = (int*)data;
|
||||
|
||||
audio_buf_info info;
|
||||
if (ioctl(*fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
|
||||
{
|
||||
SSNES_ERR("SNDCTL_DSP_GETOSPACE failed ...\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return info.bytes;
|
||||
}
|
||||
|
||||
static size_t oss_buffer_size(void *data)
|
||||
{
|
||||
int *fd = (int*)data;
|
||||
|
||||
audio_buf_info info;
|
||||
if (ioctl(*fd, SNDCTL_DSP_GETOSPACE, &info) < 0)
|
||||
{
|
||||
SSNES_ERR("SNDCTL_DSP_GETOSPACE failed ...\n");
|
||||
return 1; // Return something non-zero to avoid SIGFPE.
|
||||
}
|
||||
|
||||
return info.fragsize * info.fragstotal;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_oss = {
|
||||
.init = oss_init,
|
||||
.write = oss_write,
|
||||
.stop = oss_stop,
|
||||
.start = oss_start,
|
||||
.set_nonblock_state = oss_set_nonblock_state,
|
||||
.free = oss_free,
|
||||
.ident = "oss"
|
||||
oss_init,
|
||||
oss_write,
|
||||
oss_stop,
|
||||
oss_start,
|
||||
oss_set_nonblock_state,
|
||||
oss_free,
|
||||
NULL,
|
||||
"oss",
|
||||
oss_write_avail,
|
||||
oss_buffer_size,
|
||||
};
|
||||
|
||||
|
||||
+52
-36
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "driver.h"
|
||||
#include "general.h"
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include <stdbool.h>
|
||||
#include "../boolean.h"
|
||||
#include <string.h>
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -30,11 +30,12 @@ typedef struct
|
||||
pa_context *context;
|
||||
pa_stream *stream;
|
||||
bool nonblock;
|
||||
size_t buffer_size;
|
||||
} pa_t;
|
||||
|
||||
static void pulse_free(void *data)
|
||||
{
|
||||
pa_t *pa = data;
|
||||
pa_t *pa = (pa_t*)data;
|
||||
if (pa)
|
||||
{
|
||||
if (pa->mainloop)
|
||||
@@ -61,7 +62,7 @@ static void pulse_free(void *data)
|
||||
|
||||
static void context_state_cb(pa_context *c, void *data)
|
||||
{
|
||||
pa_t *pa = data;
|
||||
pa_t *pa = (pa_t*)data;
|
||||
switch (pa_context_get_state(c))
|
||||
{
|
||||
case PA_CONTEXT_READY:
|
||||
@@ -76,7 +77,7 @@ static void context_state_cb(pa_context *c, void *data)
|
||||
|
||||
static void stream_state_cb(pa_stream *s, void *data)
|
||||
{
|
||||
pa_t *pa = data;
|
||||
pa_t *pa = (pa_t*)data;
|
||||
switch (pa_stream_get_state(s)) {
|
||||
case PA_STREAM_READY:
|
||||
case PA_STREAM_FAILED:
|
||||
@@ -92,20 +93,24 @@ static void stream_request_cb(pa_stream *s, size_t length, void *data)
|
||||
{
|
||||
(void)length;
|
||||
(void)s;
|
||||
pa_t *pa = data;
|
||||
pa_t *pa = (pa_t*)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_t *pa = (pa_t*)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));
|
||||
pa_sample_spec spec;
|
||||
memset(&spec, 0, sizeof(spec));
|
||||
pa_buffer_attr buffer_attr = {0};
|
||||
|
||||
pa_t *pa = (pa_t*)calloc(1, sizeof(*pa));
|
||||
if (!pa)
|
||||
goto error;
|
||||
|
||||
@@ -131,11 +136,9 @@ static void *pulse_init(const char *device, unsigned rate, unsigned latency)
|
||||
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
|
||||
};
|
||||
spec.format = is_little_endian() ? PA_SAMPLE_FLOAT32LE : PA_SAMPLE_FLOAT32BE;
|
||||
spec.channels = 2;
|
||||
spec.rate = rate;
|
||||
|
||||
pa->stream = pa_stream_new(pa->context, "audio", &spec, NULL);
|
||||
if (!pa->stream)
|
||||
@@ -145,13 +148,13 @@ static void *pulse_init(const char *device, unsigned rate, unsigned latency)
|
||||
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
|
||||
};
|
||||
buffer_attr.maxlength = -1;
|
||||
buffer_attr.tlength = pa_usec_to_bytes(latency * PA_USEC_PER_MSEC, &spec);
|
||||
buffer_attr.prebuf = -1;
|
||||
buffer_attr.minreq = -1;
|
||||
buffer_attr.fragsize = -1;
|
||||
|
||||
pa->buffer_size = buffer_attr.tlength;
|
||||
|
||||
if (pa_stream_connect_playback(pa->stream, NULL, &buffer_attr, PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0)
|
||||
goto error;
|
||||
@@ -174,11 +177,12 @@ error:
|
||||
|
||||
static ssize_t pulse_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
pa_t *pa = data;
|
||||
pa_t *pa = (pa_t*)data;
|
||||
|
||||
pa_threaded_mainloop_lock(pa->mainloop);
|
||||
unsigned length = pa_stream_writable_size(pa->stream);
|
||||
size_t length = pa_stream_writable_size(pa->stream);
|
||||
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||
|
||||
while (length < size)
|
||||
{
|
||||
pa_threaded_mainloop_wait(pa->mainloop);
|
||||
@@ -212,7 +216,7 @@ static bool pulse_start(void *data)
|
||||
|
||||
static void pulse_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
pa_t *pa = data;
|
||||
pa_t *pa = (pa_t*)data;
|
||||
pa->nonblock = state;
|
||||
}
|
||||
|
||||
@@ -222,19 +226,31 @@ static bool pulse_use_float(void *data)
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t pulse_write_avail(void *data)
|
||||
{
|
||||
pa_t *pa = (pa_t*)data;
|
||||
pa_threaded_mainloop_lock(pa->mainloop);
|
||||
size_t length = pa_stream_writable_size(pa->stream);
|
||||
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||
return length;
|
||||
}
|
||||
|
||||
static size_t pulse_buffer_size(void *data)
|
||||
{
|
||||
pa_t *pa = (pa_t*)data;
|
||||
return pa->buffer_size;
|
||||
}
|
||||
|
||||
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"
|
||||
pulse_init,
|
||||
pulse_write,
|
||||
pulse_stop,
|
||||
pulse_start,
|
||||
pulse_set_nonblock_state,
|
||||
pulse_free,
|
||||
pulse_use_float,
|
||||
"pulse",
|
||||
pulse_write_avail,
|
||||
pulse_buffer_size,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -15,20 +15,21 @@
|
||||
* 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
|
||||
#ifndef __SSNES_RESAMPLER_H
|
||||
#define __SSNES_RESAMPLER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include <math.h>
|
||||
|
||||
typedef struct hermite_resampler hermite_resampler_t;
|
||||
// M_PI is left out of ISO C99 :(
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846264338327
|
||||
#endif
|
||||
|
||||
hermite_resampler_t *hermite_new(void);
|
||||
typedef struct ssnes_resampler ssnes_resampler_t;
|
||||
|
||||
struct hermite_data
|
||||
struct resampler_data
|
||||
{
|
||||
const float *data_in;
|
||||
float *data_out;
|
||||
@@ -39,8 +40,12 @@ struct hermite_data
|
||||
double ratio;
|
||||
};
|
||||
|
||||
void hermite_process(hermite_resampler_t *re, struct hermite_data *data);
|
||||
void hermite_free(hermite_resampler_t *re);
|
||||
ssnes_resampler_t *resampler_new(void);
|
||||
void resampler_process(ssnes_resampler_t *re, struct resampler_data *data);
|
||||
void resampler_free(ssnes_resampler_t *re);
|
||||
|
||||
// Generate a starting cosine pulse with given frequency for testing (SNR, etc) purposes.
|
||||
void resampler_preinit(ssnes_resampler_t *re, double omega, double *samples_offset);
|
||||
|
||||
#endif
|
||||
|
||||
+14
-18
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <roaraudio.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include "../boolean.h"
|
||||
#include "general.h"
|
||||
|
||||
typedef struct
|
||||
@@ -34,7 +34,7 @@ static void *ra_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)latency;
|
||||
int err;
|
||||
roar_t *roar = calloc(1, sizeof(roar_t));
|
||||
roar_t *roar = (roar_t*)calloc(1, sizeof(roar_t));
|
||||
if (roar == NULL)
|
||||
return NULL;
|
||||
|
||||
@@ -54,7 +54,7 @@ static void *ra_init(const char *device, unsigned rate, unsigned latency)
|
||||
|
||||
static ssize_t ra_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
roar_t *roar = (roar_t*)data;
|
||||
ssize_t rc;
|
||||
|
||||
if (size == 0)
|
||||
@@ -86,7 +86,7 @@ static bool ra_stop(void *data)
|
||||
|
||||
static void ra_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
roar_t *roar = (roar_t*)data;
|
||||
if (roar_vs_blocking(roar->vss, (state) ? ROAR_VS_FALSE : ROAR_VS_TRUE, NULL) < 0)
|
||||
fprintf(stderr, "SSNES [ERROR]: Can't set nonblocking. Will not be able to fast-forward.\n");
|
||||
roar->nonblocking = state;
|
||||
@@ -100,23 +100,19 @@ static bool ra_start(void *data)
|
||||
|
||||
static void ra_free(void *data)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
roar_t *roar = (roar_t*)data;
|
||||
roar_vs_close(roar->vss, ROAR_VS_TRUE, NULL);
|
||||
free(data);
|
||||
}
|
||||
|
||||
const audio_driver_t audio_roar = {
|
||||
.init = ra_init,
|
||||
.write = ra_write,
|
||||
.stop = ra_stop,
|
||||
.start = ra_start,
|
||||
.set_nonblock_state = ra_set_nonblock_state,
|
||||
.free = ra_free,
|
||||
.ident = "roar"
|
||||
ra_init,
|
||||
ra_write,
|
||||
ra_stop,
|
||||
ra_start,
|
||||
ra_set_nonblock_state,
|
||||
ra_free,
|
||||
NULL,
|
||||
"roar"
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+55
-30
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -16,12 +16,16 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
#include "../driver.h"
|
||||
#include <stdlib.h>
|
||||
#ifdef SSNES_CONSOLE
|
||||
#include "../console/librsound/rsound.h"
|
||||
#else
|
||||
#include <rsound.h>
|
||||
#endif
|
||||
#include "fifo_buffer.h"
|
||||
#include <stdbool.h>
|
||||
#include "SDL.h"
|
||||
#include "../boolean.h"
|
||||
#include "../thread.h"
|
||||
|
||||
typedef struct rsd
|
||||
{
|
||||
@@ -31,32 +35,32 @@ typedef struct rsd
|
||||
|
||||
fifo_buffer_t *buffer;
|
||||
|
||||
SDL_mutex *cond_lock;
|
||||
SDL_cond *cond;
|
||||
slock_t *cond_lock;
|
||||
scond_t *cond;
|
||||
} rsd_t;
|
||||
|
||||
static ssize_t audio_cb(void *data, size_t bytes, void *userdata)
|
||||
{
|
||||
rsd_t *rsd = userdata;
|
||||
rsd_t *rsd = (rsd_t*)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);
|
||||
scond_signal(rsd->cond);
|
||||
|
||||
return write_size;
|
||||
}
|
||||
|
||||
static void err_cb(void *userdata)
|
||||
{
|
||||
rsd_t *rsd = userdata;
|
||||
rsd_t *rsd = (rsd_t*)userdata;
|
||||
rsd->has_error = true;
|
||||
SDL_CondSignal(rsd->cond);
|
||||
scond_signal(rsd->cond);
|
||||
}
|
||||
|
||||
static void *rs_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
rsd_t *rsd = calloc(1, sizeof(rsd_t));
|
||||
rsd_t *rsd = (rsd_t*)calloc(1, sizeof(rsd_t));
|
||||
if (!rsd)
|
||||
return NULL;
|
||||
|
||||
@@ -68,8 +72,8 @@ static void *rs_init(const char *device, unsigned rate, unsigned latency)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rsd->cond_lock = SDL_CreateMutex();
|
||||
rsd->cond = SDL_CreateCond();
|
||||
rsd->cond_lock = slock_new();
|
||||
rsd->cond = scond_new();
|
||||
|
||||
rsd->buffer = fifo_new(1024 * 4);
|
||||
|
||||
@@ -100,7 +104,7 @@ static void *rs_init(const char *device, unsigned rate, unsigned latency)
|
||||
|
||||
static ssize_t rs_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd_t *rsd = (rsd_t*)data;
|
||||
|
||||
if (rsd->has_error)
|
||||
return -1;
|
||||
@@ -127,9 +131,9 @@ static ssize_t rs_write(void *data, const void *buf, size_t size)
|
||||
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);
|
||||
slock_lock(rsd->cond_lock);
|
||||
scond_wait(rsd->cond, rsd->cond_lock);
|
||||
slock_unlock(rsd->cond_lock);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -146,7 +150,7 @@ static ssize_t rs_write(void *data, const void *buf, size_t size)
|
||||
|
||||
static bool rs_stop(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd_t *rsd = (rsd_t*)data;
|
||||
rsd_stop(rsd->rd);
|
||||
|
||||
return true;
|
||||
@@ -154,13 +158,13 @@ static bool rs_stop(void *data)
|
||||
|
||||
static void rs_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd_t *rsd = (rsd_t*)data;
|
||||
rsd->nonblock = state;
|
||||
}
|
||||
|
||||
static bool rs_start(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd_t *rsd = (rsd_t*)data;
|
||||
if (rsd_start(rsd->rd) < 0)
|
||||
return false;
|
||||
|
||||
@@ -169,25 +173,46 @@ static bool rs_start(void *data)
|
||||
|
||||
static void rs_free(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd_t *rsd = (rsd_t*)data;
|
||||
|
||||
rsd_stop(rsd->rd);
|
||||
rsd_free(rsd->rd);
|
||||
|
||||
fifo_free(rsd->buffer);
|
||||
SDL_DestroyMutex(rsd->cond_lock);
|
||||
SDL_DestroyCond(rsd->cond);
|
||||
slock_free(rsd->cond_lock);
|
||||
scond_free(rsd->cond);
|
||||
|
||||
free(rsd);
|
||||
}
|
||||
|
||||
static size_t rs_write_avail(void *data)
|
||||
{
|
||||
rsd_t *rsd = (rsd_t*)data;
|
||||
|
||||
if (rsd->has_error)
|
||||
return 0;
|
||||
rsd_callback_lock(rsd->rd);
|
||||
size_t val = fifo_write_avail(rsd->buffer);
|
||||
rsd_callback_unlock(rsd->rd);
|
||||
return val;
|
||||
}
|
||||
|
||||
static size_t rs_buffer_size(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return 1024 * 4;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_rsound = {
|
||||
.init = rs_init,
|
||||
.write = rs_write,
|
||||
.stop = rs_stop,
|
||||
.start = rs_start,
|
||||
.set_nonblock_state = rs_set_nonblock_state,
|
||||
.free = rs_free,
|
||||
.ident = "rsound"
|
||||
rs_init,
|
||||
rs_write,
|
||||
rs_stop,
|
||||
rs_start,
|
||||
rs_set_nonblock_state,
|
||||
rs_free,
|
||||
NULL,
|
||||
"rsound",
|
||||
rs_write_avail,
|
||||
rs_buffer_size,
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -16,36 +16,37 @@
|
||||
*/
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
#include "../driver.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "../boolean.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "SDL.h"
|
||||
#include "SDL_audio.h"
|
||||
#include "SDL_thread.h"
|
||||
#include "general.h"
|
||||
#include "fifo_buffer.h"
|
||||
#include "../thread.h"
|
||||
|
||||
#include "../general.h"
|
||||
#include "../fifo_buffer.h"
|
||||
|
||||
typedef struct sdl_audio
|
||||
{
|
||||
bool nonblock;
|
||||
|
||||
SDL_mutex *lock;
|
||||
SDL_cond *cond;
|
||||
slock_t *lock;
|
||||
scond_t *cond;
|
||||
fifo_buffer_t *buffer;
|
||||
} sdl_audio_t;
|
||||
|
||||
static void sdl_audio_cb(void *data, Uint8 *stream, int len)
|
||||
{
|
||||
sdl_audio_t *sdl = data;
|
||||
sdl_audio_t *sdl = (sdl_audio_t*)data;
|
||||
|
||||
size_t avail = fifo_read_avail(sdl->buffer);
|
||||
size_t write_size = len > avail ? avail : len;
|
||||
size_t write_size = len > (int)avail ? avail : len;
|
||||
fifo_read(sdl->buffer, stream, write_size);
|
||||
SDL_CondSignal(sdl->cond);
|
||||
scond_signal(sdl->cond);
|
||||
|
||||
// If underrun, fill rest with silence.
|
||||
memset(stream + write_size, 0, len - write_size);
|
||||
@@ -64,21 +65,20 @@ static void *sdl_audio_init(const char *device, unsigned rate, unsigned latency)
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
|
||||
return NULL;
|
||||
|
||||
sdl_audio_t *sdl = calloc(1, sizeof(*sdl));
|
||||
sdl_audio_t *sdl = (sdl_audio_t*)calloc(1, sizeof(*sdl));
|
||||
if (!sdl)
|
||||
return NULL;
|
||||
|
||||
// We have to buffer up some data ourselves, so we let SDL carry approx half of the latency. SDL double buffers audio and we do as well.
|
||||
int frames = find_num_frames(rate, latency / 4);
|
||||
|
||||
SDL_AudioSpec spec = {
|
||||
.freq = rate,
|
||||
.format = AUDIO_S16SYS,
|
||||
.channels = 2,
|
||||
.samples = frames, // This is in audio frames, not samples ... :(
|
||||
.callback = sdl_audio_cb,
|
||||
.userdata = sdl
|
||||
};
|
||||
SDL_AudioSpec spec = {0};
|
||||
spec.freq = rate;
|
||||
spec.format = AUDIO_S16SYS;
|
||||
spec.channels = 2;
|
||||
spec.samples = frames; // This is in audio frames, not samples ... :(
|
||||
spec.callback = sdl_audio_cb;
|
||||
spec.userdata = sdl;
|
||||
|
||||
SDL_AudioSpec out;
|
||||
|
||||
@@ -90,8 +90,8 @@ static void *sdl_audio_init(const char *device, unsigned rate, unsigned latency)
|
||||
}
|
||||
g_settings.audio.out_rate = out.freq;
|
||||
|
||||
sdl->lock = SDL_CreateMutex();
|
||||
sdl->cond = SDL_CreateCond();
|
||||
sdl->lock = slock_new();
|
||||
sdl->cond = scond_new();
|
||||
|
||||
SSNES_LOG("SDL audio: Requested %d ms latency, got %d ms\n", latency, (int)(out.samples * 4 * 1000 / g_settings.audio.out_rate));
|
||||
|
||||
@@ -111,7 +111,7 @@ static void *sdl_audio_init(const char *device, unsigned rate, unsigned latency)
|
||||
|
||||
static ssize_t sdl_audio_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
sdl_audio_t *sdl = data;
|
||||
sdl_audio_t *sdl = (sdl_audio_t*)data;
|
||||
|
||||
ssize_t ret = 0;
|
||||
if (sdl->nonblock)
|
||||
@@ -134,9 +134,9 @@ static ssize_t sdl_audio_write(void *data, const void *buf, size_t size)
|
||||
if (avail == 0)
|
||||
{
|
||||
SDL_UnlockAudio();
|
||||
SDL_mutexP(sdl->lock);
|
||||
SDL_CondWait(sdl->cond, sdl->lock);
|
||||
SDL_mutexV(sdl->lock);
|
||||
slock_lock(sdl->lock);
|
||||
scond_wait(sdl->cond, sdl->lock);
|
||||
slock_unlock(sdl->lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -168,7 +168,7 @@ static bool sdl_audio_start(void *data)
|
||||
|
||||
static void sdl_audio_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
sdl_audio_t *sdl = data;
|
||||
sdl_audio_t *sdl = (sdl_audio_t*)data;
|
||||
sdl->nonblock = state;
|
||||
}
|
||||
|
||||
@@ -177,23 +177,24 @@ static void sdl_audio_free(void *data)
|
||||
SDL_CloseAudio();
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
|
||||
sdl_audio_t *sdl = data;
|
||||
sdl_audio_t *sdl = (sdl_audio_t*)data;
|
||||
if (sdl)
|
||||
{
|
||||
fifo_free(sdl->buffer);
|
||||
SDL_DestroyMutex(sdl->lock);
|
||||
SDL_DestroyCond(sdl->cond);
|
||||
slock_free(sdl->lock);
|
||||
scond_free(sdl->cond);
|
||||
}
|
||||
free(sdl);
|
||||
}
|
||||
|
||||
const audio_driver_t audio_sdl = {
|
||||
.init = sdl_audio_init,
|
||||
.write = sdl_audio_write,
|
||||
.stop = sdl_audio_stop,
|
||||
.start = sdl_audio_start,
|
||||
.set_nonblock_state = sdl_audio_set_nonblock_state,
|
||||
.free = sdl_audio_free,
|
||||
.ident = "sdl"
|
||||
sdl_audio_init,
|
||||
sdl_audio_write,
|
||||
sdl_audio_stop,
|
||||
sdl_audio_start,
|
||||
sdl_audio_set_nonblock_state,
|
||||
sdl_audio_free,
|
||||
NULL,
|
||||
"sdl"
|
||||
};
|
||||
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - 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/>.
|
||||
*/
|
||||
|
||||
// Bog-standard windowed SINC implementation.
|
||||
// Only suitable as an upsampler, as there is no low-pass filter stage.
|
||||
|
||||
#include "resampler.h"
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef RESAMPLER_TEST
|
||||
#include "../general.h"
|
||||
#else
|
||||
#define SSNES_LOG(...)
|
||||
#endif
|
||||
|
||||
#if __SSE__
|
||||
#include <xmmintrin.h>
|
||||
#endif
|
||||
|
||||
#define PHASE_BITS 8
|
||||
#define SUBPHASE_BITS 16
|
||||
|
||||
#define PHASES (1 << PHASE_BITS)
|
||||
#define PHASES_SHIFT (SUBPHASE_BITS)
|
||||
#define SUBPHASES (1 << SUBPHASE_BITS)
|
||||
#define SUBPHASES_SHIFT 0
|
||||
#define SUBPHASES_MASK ((1 << SUBPHASE_BITS) - 1)
|
||||
#define PHASES_WRAP (1 << (PHASE_BITS + SUBPHASE_BITS))
|
||||
#define FRAMES_SHIFT (PHASE_BITS + SUBPHASE_BITS)
|
||||
|
||||
#define SIDELOBES 16
|
||||
#define TAPS (SIDELOBES * 2)
|
||||
#define CUTOFF 0.9
|
||||
|
||||
#define PHASE_INDEX 0
|
||||
#define DELTA_INDEX 1
|
||||
|
||||
struct ssnes_resampler
|
||||
{
|
||||
float phase_table[PHASES][2][TAPS];
|
||||
float buffer_l[2 * TAPS];
|
||||
float buffer_r[2 * TAPS];
|
||||
|
||||
unsigned ptr;
|
||||
|
||||
uint32_t time;
|
||||
};
|
||||
|
||||
void resampler_preinit(ssnes_resampler_t *re, double omega, double *samples_offset)
|
||||
{
|
||||
*samples_offset = SIDELOBES + 1;
|
||||
for (int i = 0; i < 2 * SIDELOBES; i++)
|
||||
{
|
||||
re->buffer_l[i] = re->buffer_l[i + TAPS] = cos((i - (SIDELOBES - 1)) * omega);
|
||||
re->buffer_r[i] = re->buffer_r[i + TAPS] = re->buffer_l[i];
|
||||
}
|
||||
|
||||
re->time = 0;
|
||||
re->ptr = 0;
|
||||
}
|
||||
|
||||
static inline double sinc(double val)
|
||||
{
|
||||
if (fabs(val) < 0.00001)
|
||||
return 1.0;
|
||||
else
|
||||
return sin(val) / val;
|
||||
}
|
||||
|
||||
static inline double lanzcos(double index)
|
||||
{
|
||||
return sinc(index);
|
||||
}
|
||||
|
||||
static void init_sinc_table(ssnes_resampler_t *resamp)
|
||||
{
|
||||
// Sinc phases: [..., p + 3, p + 2, p + 1, p + 0, p - 1, p - 2, p - 3, p - 4, ...]
|
||||
for (int i = 0; i < PHASES; i++)
|
||||
{
|
||||
for (int j = 0; j < TAPS; j++)
|
||||
{
|
||||
double p = (double)i / PHASES;
|
||||
double sinc_phase = M_PI * (p + (SIDELOBES - 1 - j));
|
||||
resamp->phase_table[i][PHASE_INDEX][j] = CUTOFF * sinc(CUTOFF * sinc_phase) *
|
||||
lanzcos(sinc_phase / SIDELOBES);
|
||||
}
|
||||
}
|
||||
|
||||
// Optimize linear interpolation.
|
||||
for (int i = 0; i < PHASES - 1; i++)
|
||||
{
|
||||
for (int j = 0; j < TAPS; j++)
|
||||
{
|
||||
resamp->phase_table[i][DELTA_INDEX][j] =
|
||||
(resamp->phase_table[i + 1][PHASE_INDEX][j] - resamp->phase_table[i][PHASE_INDEX][j]) / SUBPHASES;
|
||||
}
|
||||
}
|
||||
|
||||
// Interpolation between [PHASES - 1] => [PHASES]
|
||||
for (int j = 0; j < TAPS; j++)
|
||||
{
|
||||
double p = 1.0;
|
||||
double sinc_phase = M_PI * (p + (SIDELOBES - 1 - j));
|
||||
double phase = CUTOFF * sinc(CUTOFF * sinc_phase) * lanzcos(sinc_phase / SIDELOBES);
|
||||
|
||||
float result = (phase - resamp->phase_table[PHASES - 1][PHASE_INDEX][j]) / SUBPHASES;
|
||||
resamp->phase_table[PHASES - 1][DELTA_INDEX][j] = result;
|
||||
}
|
||||
}
|
||||
|
||||
// No memalign() for us on Win32 ...
|
||||
static void *aligned_alloc(size_t boundary, size_t size)
|
||||
{
|
||||
void *ptr = malloc(boundary + size + sizeof(uintptr_t));
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
uintptr_t addr = ((uintptr_t)ptr + sizeof(uintptr_t) + boundary) & ~(boundary - 1);
|
||||
void **place = (void**)addr;
|
||||
place[-1] = ptr;
|
||||
|
||||
return (void*)addr;
|
||||
}
|
||||
|
||||
static void aligned_free(void *ptr)
|
||||
{
|
||||
void **p = (void**)ptr;
|
||||
free(p[-1]);
|
||||
}
|
||||
|
||||
ssnes_resampler_t *resampler_new(void)
|
||||
{
|
||||
ssnes_resampler_t *re = (ssnes_resampler_t*)aligned_alloc(16, sizeof(*re));
|
||||
if (!re)
|
||||
return NULL;
|
||||
|
||||
memset(re, 0, sizeof(*re));
|
||||
|
||||
init_sinc_table(re);
|
||||
|
||||
#if __SSE__
|
||||
SSNES_LOG("Sinc resampler [SSE]\n");
|
||||
#else
|
||||
SSNES_LOG("Sinc resampler [C]\n");
|
||||
#endif
|
||||
|
||||
return re;
|
||||
}
|
||||
|
||||
#if __SSE__
|
||||
static void process_sinc(ssnes_resampler_t *resamp, float *out_buffer)
|
||||
{
|
||||
__m128 sum_l = _mm_setzero_ps();
|
||||
__m128 sum_r = _mm_setzero_ps();
|
||||
|
||||
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||
|
||||
unsigned phase = resamp->time >> PHASES_SHIFT;
|
||||
unsigned delta = (resamp->time >> SUBPHASES_SHIFT) & SUBPHASES_MASK;
|
||||
__m128 delta_f = _mm_set1_ps(delta);
|
||||
|
||||
const float *phase_table = resamp->phase_table[phase][PHASE_INDEX];
|
||||
const float *delta_table = resamp->phase_table[phase][DELTA_INDEX];
|
||||
|
||||
for (unsigned i = 0; i < TAPS; i += 4)
|
||||
{
|
||||
__m128 buf_l = _mm_loadu_ps(buffer_l + i);
|
||||
__m128 buf_r = _mm_loadu_ps(buffer_r + i);
|
||||
|
||||
__m128 phases = _mm_load_ps(phase_table + i);
|
||||
__m128 deltas = _mm_load_ps(delta_table + i);
|
||||
|
||||
__m128 sinc = _mm_add_ps(phases, _mm_mul_ps(deltas, delta_f));
|
||||
|
||||
sum_l = _mm_add_ps(sum_l, _mm_mul_ps(buf_l, sinc));
|
||||
sum_r = _mm_add_ps(sum_r, _mm_mul_ps(buf_r, sinc));
|
||||
}
|
||||
|
||||
// Them annoying shuffles :V
|
||||
// sum_l = { l3, l2, l1, l0 }
|
||||
// sum_r = { r3, r2, r1, r0 }
|
||||
|
||||
__m128 sum = _mm_add_ps(_mm_shuffle_ps(sum_l, sum_r, _MM_SHUFFLE(1, 0, 1, 0)),
|
||||
_mm_shuffle_ps(sum_l, sum_r, _MM_SHUFFLE(3, 2, 3, 2)));
|
||||
|
||||
// sum = { r1, r0, l1, l0 } + { r3, r2, l3, l2 }
|
||||
// sum = { R1, R0, L1, L0 }
|
||||
|
||||
sum = _mm_add_ps(_mm_shuffle_ps(sum, sum, _MM_SHUFFLE(3, 3, 1, 1)), sum);
|
||||
|
||||
// sum = {R1, R1, L1, L1 } + { R1, R0, L1, L0 }
|
||||
// sum = { X, R, X, L }
|
||||
|
||||
// Store L
|
||||
_mm_store_ss(out_buffer + 0, sum);
|
||||
|
||||
// movehl { X, R, X, L } == { X, R, X, R }
|
||||
_mm_store_ss(out_buffer + 1, _mm_movehl_ps(sum, sum));
|
||||
}
|
||||
#else // Plain ol' C99
|
||||
static void process_sinc(ssnes_resampler_t *resamp, float *out_buffer)
|
||||
{
|
||||
float sum_l = 0.0f;
|
||||
float sum_r = 0.0f;
|
||||
const float *buffer_l = resamp->buffer_l + resamp->ptr;
|
||||
const float *buffer_r = resamp->buffer_r + resamp->ptr;
|
||||
|
||||
unsigned phase = resamp->time >> PHASES_SHIFT;
|
||||
unsigned delta = (resamp->time >> SUBPHASES_SHIFT) & SUBPHASES_MASK;
|
||||
float delta_f = (float)delta;
|
||||
|
||||
const float *phase_table = resamp->phase_table[phase][PHASE_INDEX];
|
||||
const float *delta_table = resamp->phase_table[phase][DELTA_INDEX];
|
||||
|
||||
for (unsigned i = 0; i < TAPS; i++)
|
||||
{
|
||||
float sinc_val = phase_table[i] + delta_f * delta_table[i];
|
||||
sum_l += buffer_l[i] * sinc_val;
|
||||
sum_r += buffer_r[i] * sinc_val;
|
||||
}
|
||||
|
||||
out_buffer[0] = sum_l;
|
||||
out_buffer[1] = sum_r;
|
||||
}
|
||||
#endif
|
||||
|
||||
void resampler_process(ssnes_resampler_t *re, struct resampler_data *data)
|
||||
{
|
||||
uint32_t ratio = PHASES_WRAP / data->ratio;
|
||||
|
||||
const float *input = data->data_in;
|
||||
float *output = data->data_out;
|
||||
size_t frames = data->input_frames;
|
||||
size_t out_frames = 0;
|
||||
|
||||
while (frames)
|
||||
{
|
||||
process_sinc(re, output);
|
||||
output += 2;
|
||||
out_frames++;
|
||||
|
||||
re->time += ratio;
|
||||
while (re->time >= PHASES_WRAP)
|
||||
{
|
||||
re->buffer_l[re->ptr + TAPS] = re->buffer_l[re->ptr] = *input++;
|
||||
re->buffer_r[re->ptr + TAPS] = re->buffer_r[re->ptr] = *input++;
|
||||
re->ptr = (re->ptr + 1) & (TAPS - 1);
|
||||
|
||||
re->time -= PHASES_WRAP;
|
||||
frames--;
|
||||
}
|
||||
}
|
||||
|
||||
data->output_frames = out_frames;
|
||||
}
|
||||
|
||||
void resampler_free(ssnes_resampler_t *re)
|
||||
{
|
||||
aligned_free(re);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
TESTS := test-hermite test-sinc test-snr-sinc test-snr-hermite
|
||||
|
||||
CFLAGS += -O3 -g -Wall -pedantic -std=gnu99 -DRESAMPLER_TEST -march=native
|
||||
LDFLAGS += -lm
|
||||
|
||||
all: $(TESTS)
|
||||
|
||||
test-hermite: ../hermite.o ../utils.o main.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
test-sinc: ../sinc.o ../utils.o main.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
test-snr-sinc: ../sinc.o ../utils.o snr.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
test-snr-hermite: ../hermite.o ../utils.o snr.o
|
||||
$(CC) -o $@ $^ $(LDFLAGS)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c -o $@ $< $(CFLAGS)
|
||||
|
||||
clean:
|
||||
rm -f $(TESTS)
|
||||
rm -f *.o
|
||||
rm -f ../*.o
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - 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/>.
|
||||
*/
|
||||
|
||||
// Resampler that reads raw S16NE/stereo from stdin and outputs to stdout in S16NE/stereo.
|
||||
// Used for testing and performance benchmarking.
|
||||
|
||||
#include "../resampler.h"
|
||||
#include "../utils.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int16_t input_i[1024];
|
||||
float input_f[1024];
|
||||
int16_t output_i[1024 * 8];
|
||||
float output_f[1024 * 8];
|
||||
|
||||
if (argc != 3)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <in-rate> <out-rate> (max ratio: 8.0)\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
double in_rate = strtod(argv[1], NULL);
|
||||
double out_rate = strtod(argv[2], NULL);
|
||||
|
||||
double ratio = out_rate / in_rate;
|
||||
if (ratio >= 7.99)
|
||||
{
|
||||
fprintf(stderr, "Ratio is too high.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
ssnes_resampler_t *resamp = resampler_new();
|
||||
if (!resamp)
|
||||
{
|
||||
fprintf(stderr, "Failed to allocate resampler ...\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (fread(input_i, sizeof(int16_t), 1024, stdin) != 1024)
|
||||
break;
|
||||
|
||||
audio_convert_s16_to_float(input_f, input_i, 1024);
|
||||
|
||||
struct resampler_data data = {
|
||||
.data_in = input_f,
|
||||
.data_out = output_f,
|
||||
.input_frames = sizeof(input_f) / (2 * sizeof(float)),
|
||||
.ratio = ratio,
|
||||
};
|
||||
|
||||
resampler_process(resamp, &data);
|
||||
|
||||
size_t output_samples = data.output_frames * 2;
|
||||
audio_convert_float_to_s16(output_i, output_f, output_samples);
|
||||
|
||||
if (fwrite(output_i, sizeof(int16_t), output_samples, stdout) != output_samples)
|
||||
break;
|
||||
}
|
||||
|
||||
resampler_free(resamp);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,288 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - 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 "../resampler.h"
|
||||
#include "../utils.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <complex.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
static void gen_signal(float *out, double omega, double bias_samples, size_t samples)
|
||||
{
|
||||
for (size_t i = 0; i < samples; i += 2)
|
||||
{
|
||||
out[i + 0] = cos(((i >> 1) + bias_samples) * omega);
|
||||
out[i + 1] = out[i + 0];
|
||||
}
|
||||
}
|
||||
|
||||
struct snr_result
|
||||
{
|
||||
double snr;
|
||||
double gain;
|
||||
};
|
||||
|
||||
static unsigned bitrange(unsigned len)
|
||||
{
|
||||
unsigned ret = 0;
|
||||
while ((len >>= 1))
|
||||
ret++;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned bitswap(unsigned i, unsigned range)
|
||||
{
|
||||
unsigned ret = 0;
|
||||
for (unsigned shifts = 0; shifts < range; shifts++)
|
||||
ret |= i & (1 << (range - shifts - 1)) ? (1 << shifts) : 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// When interleaving the butterfly buffer, addressing puts bits in reverse.
|
||||
// [0, 1, 2, 3, 4, 5, 6, 7] => [0, 4, 2, 6, 1, 5, 3, 7]
|
||||
static void interleave(complex double *butterfly_buf, size_t samples)
|
||||
{
|
||||
unsigned range = bitrange(samples);
|
||||
for (unsigned i = 0; i < samples; i++)
|
||||
{
|
||||
unsigned target = bitswap(i, range);
|
||||
if (target > i)
|
||||
{
|
||||
complex double tmp = butterfly_buf[target];
|
||||
butterfly_buf[target] = butterfly_buf[i];
|
||||
butterfly_buf[i] = tmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static complex double gen_phase(double index)
|
||||
{
|
||||
return cexp(M_PI * I * index);
|
||||
}
|
||||
|
||||
static void butterfly(complex double *a, complex double *b, complex double mod)
|
||||
{
|
||||
mod *= *b;
|
||||
complex double a_ = *a + mod;
|
||||
complex double b_ = *a - mod;
|
||||
*a = a_;
|
||||
*b = b_;
|
||||
}
|
||||
|
||||
static void butterflies(complex double *butterfly_buf, double phase_dir, size_t step_size, size_t samples)
|
||||
{
|
||||
for (unsigned i = 0; i < samples; i += 2 * step_size)
|
||||
for (unsigned j = i; j < i + step_size; j++)
|
||||
butterfly(&butterfly_buf[j], &butterfly_buf[j + step_size], gen_phase((phase_dir * (j - i)) / step_size));
|
||||
}
|
||||
|
||||
static void calculate_fft(const float *data, complex double *butterfly_buf, size_t samples)
|
||||
{
|
||||
// Enforce POT.
|
||||
assert((samples & (samples - 1)) == 0);
|
||||
|
||||
for (unsigned i = 0; i < samples; i++)
|
||||
butterfly_buf[i] = data[2 * i];
|
||||
|
||||
// Interleave buffer to work with FFT.
|
||||
interleave(butterfly_buf, samples);
|
||||
|
||||
// Fly, lovely butterflies! :D
|
||||
for (unsigned step_size = 1; step_size < samples; step_size *= 2)
|
||||
butterflies(butterfly_buf, -1.0, step_size, samples);
|
||||
}
|
||||
|
||||
static void calculate_fft_adjust(complex double *butterfly_buf, double gain, bool merge_high, size_t samples)
|
||||
{
|
||||
if (merge_high)
|
||||
{
|
||||
for (unsigned i = 1; i < samples / 2; i++)
|
||||
butterfly_buf[i] += conj(butterfly_buf[samples - i]);
|
||||
}
|
||||
|
||||
// Normalize amplitudes.
|
||||
for (unsigned i = 0; i < samples; i++)
|
||||
butterfly_buf[i] *= gain;
|
||||
}
|
||||
|
||||
static void calculate_ifft(complex double *butterfly_buf, size_t samples, bool normalize)
|
||||
{
|
||||
// Enforce POT.
|
||||
assert((samples & (samples - 1)) == 0);
|
||||
|
||||
interleave(butterfly_buf, samples);
|
||||
|
||||
// Fly, lovely butterflies! In opposite direction! :D
|
||||
for (unsigned step_size = 1; step_size < samples; step_size *= 2)
|
||||
butterflies(butterfly_buf, 1.0, step_size, samples);
|
||||
|
||||
if (normalize)
|
||||
calculate_fft_adjust(butterfly_buf, 1.0 / samples, false, samples);
|
||||
}
|
||||
|
||||
static void test_fft(void)
|
||||
{
|
||||
fprintf(stderr, "Sanity checking FFT ...\n");
|
||||
float signal[32];
|
||||
complex double butterfly_buf[16];
|
||||
complex double buf_tmp[16];
|
||||
|
||||
const float cos_freqs[] = {
|
||||
1.0, 4.0, 6.0,
|
||||
};
|
||||
|
||||
const float sin_freqs[] = {
|
||||
-2.0, 5.0, 7.0,
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < 16; i++)
|
||||
{
|
||||
signal[2 * i] = 0.0;
|
||||
for (unsigned j = 0; j < sizeof(cos_freqs) / sizeof(cos_freqs[0]); j++)
|
||||
signal[2 * i] += cos(2.0 * M_PI * i * cos_freqs[j] / 16.0);
|
||||
for (unsigned j = 0; j < sizeof(sin_freqs) / sizeof(sin_freqs[0]); j++)
|
||||
signal[2 * i] += sin(2.0 * M_PI * i * sin_freqs[j] / 16.0);
|
||||
}
|
||||
|
||||
calculate_fft(signal, butterfly_buf, 16);
|
||||
memcpy(buf_tmp, butterfly_buf, sizeof(buf_tmp));
|
||||
calculate_fft_adjust(buf_tmp, 1.0 / 16, true, 16);
|
||||
|
||||
printf("FFT: { ");
|
||||
for (unsigned i = 0; i < 7; i++)
|
||||
printf("(%4.2lf, %4.2lf), ", creal(buf_tmp[i]), cimag(buf_tmp[i]));
|
||||
printf("(%4.2lf, %4.2lf) }\n", creal(buf_tmp[7]), cimag(buf_tmp[7]));
|
||||
|
||||
calculate_ifft(butterfly_buf, 16, true);
|
||||
|
||||
printf("Original: { ");
|
||||
for (unsigned i = 0; i < 15; i++)
|
||||
printf("%5.2f, ", signal[2 * i]);
|
||||
printf("%5.2f }\n", signal[2 * 15]);
|
||||
|
||||
printf("FFT => IFFT: { ");
|
||||
for (unsigned i = 0; i < 15; i++)
|
||||
printf("%5.2lf, ", creal(butterfly_buf[i]));
|
||||
printf("%5.2lf }\n", creal(butterfly_buf[15]));
|
||||
}
|
||||
|
||||
// This doesn't yet take account for slight phase distortions,
|
||||
// so reported SNR is lower than reality.
|
||||
static void calculate_snr(struct snr_result *res,
|
||||
unsigned in_rate,
|
||||
const float *resamp, complex double *butterfly_buf, size_t samples)
|
||||
{
|
||||
samples >>= 1;
|
||||
calculate_fft(resamp, butterfly_buf, samples);
|
||||
calculate_fft_adjust(butterfly_buf, 1.0 / samples, true, samples);
|
||||
|
||||
double signal = cabs(butterfly_buf[in_rate] * butterfly_buf[in_rate]);
|
||||
butterfly_buf[in_rate] = 0.0;
|
||||
|
||||
double noise = 0.0;
|
||||
for (unsigned i = 0; i < samples / 2; i++)
|
||||
noise += cabs(butterfly_buf[i] * butterfly_buf[i]);
|
||||
|
||||
res->snr = 10.0 * log10(signal / noise);
|
||||
res->gain = 10.0 * log10(signal);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc != 2)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <ratio> (out-rate is fixed for FFT).\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
double ratio = strtod(argv[1], NULL);
|
||||
|
||||
const unsigned fft_samples = 1024 * 128;
|
||||
unsigned out_rate = fft_samples;
|
||||
unsigned in_rate = out_rate / ratio;
|
||||
ratio = (double)out_rate / in_rate;
|
||||
|
||||
if (ratio <= 1.0)
|
||||
{
|
||||
fprintf(stderr, "Ratio too low ...\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const float freq_list[] = {
|
||||
0.001, 0.002, 0.003, 0.004, 0.005, 0.008,
|
||||
0.010, 0.015, 0.020, 0.025, 0.030, 0.035, 0.040, 0.045, 0.050,
|
||||
0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45,
|
||||
0.46, 0.47, 0.48, 0.49, 0.495,
|
||||
};
|
||||
|
||||
unsigned samples = in_rate * 2;
|
||||
float *input = calloc(sizeof(float), samples);
|
||||
float *output = calloc(sizeof(float), (fft_samples + 1) * 2);
|
||||
complex double *butterfly_buf = calloc(sizeof(complex double), fft_samples);
|
||||
bool warned = false;
|
||||
assert(input);
|
||||
assert(output);
|
||||
|
||||
ssnes_resampler_t *re = resampler_new();
|
||||
assert(re);
|
||||
|
||||
test_fft();
|
||||
|
||||
for (unsigned i = 0; i < sizeof(freq_list) / sizeof(freq_list[0]); i++)
|
||||
{
|
||||
unsigned freq = freq_list[i] * in_rate;
|
||||
double omega = 2.0 * M_PI * freq / in_rate;
|
||||
double sample_offset;
|
||||
resampler_preinit(re, omega, &sample_offset);
|
||||
gen_signal(input, omega, sample_offset, samples);
|
||||
|
||||
struct resampler_data data = {
|
||||
.data_in = input,
|
||||
.data_out = output,
|
||||
.input_frames = in_rate,
|
||||
.ratio = ratio,
|
||||
};
|
||||
|
||||
resampler_process(re, &data);
|
||||
|
||||
unsigned out_samples = data.output_frames * 2;
|
||||
|
||||
if (out_samples != fft_samples * 2 && !warned)
|
||||
{
|
||||
fprintf(stderr, "Out samples != fft_samples ... %u / %u\n", out_samples, fft_samples * 2);
|
||||
warned = true;
|
||||
}
|
||||
|
||||
struct snr_result res;
|
||||
calculate_snr(&res, freq, output, butterfly_buf, fft_samples * 2);
|
||||
|
||||
printf("SNR @ w = %5.3f : %6.2lf dB, Gain: %6.1lf dB\n",
|
||||
freq_list[i], res.snr, res.gain);
|
||||
}
|
||||
|
||||
resampler_free(re);
|
||||
free(input);
|
||||
free(output);
|
||||
free(butterfly_buf);
|
||||
}
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
#include "utils.h"
|
||||
|
||||
#if __SSE2__
|
||||
#include <emmintrin.h>
|
||||
#elif __ALTIVEC__
|
||||
#include <altivec.h>
|
||||
#endif
|
||||
|
||||
void audio_convert_s16_to_float_C(float *out,
|
||||
const int16_t *in, size_t samples)
|
||||
{
|
||||
for (size_t i = 0; i < samples; i++)
|
||||
out[i] = (float)in[i] / 0x8000;
|
||||
}
|
||||
|
||||
void audio_convert_float_to_s16_C(int16_t *out,
|
||||
const float *in, size_t samples)
|
||||
{
|
||||
for (size_t i = 0; i < samples; i++)
|
||||
{
|
||||
int32_t val = (int32_t)(in[i] * 0x8000);
|
||||
out[i] = (val > 0x7FFF) ? 0x7FFF : (val < -0x8000 ? -0x8000 : (int16_t)val);
|
||||
}
|
||||
}
|
||||
|
||||
#if __SSE2__
|
||||
void audio_convert_s16_to_float_SSE2(float *out,
|
||||
const int16_t *in, size_t samples)
|
||||
{
|
||||
__m128 factor = _mm_set1_ps(1.0f / (0x7fff * 0x10000));
|
||||
size_t i;
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
__m128i input = _mm_loadu_si128((const __m128i *)in);
|
||||
__m128i regs[2] = {
|
||||
_mm_unpacklo_epi16(_mm_setzero_si128(), input),
|
||||
_mm_unpackhi_epi16(_mm_setzero_si128(), input),
|
||||
};
|
||||
|
||||
__m128 output[2] = {
|
||||
_mm_mul_ps(_mm_cvtepi32_ps(regs[0]), factor),
|
||||
_mm_mul_ps(_mm_cvtepi32_ps(regs[1]), factor),
|
||||
};
|
||||
|
||||
_mm_storeu_ps(out + 0, output[0]);
|
||||
_mm_storeu_ps(out + 4, output[1]);
|
||||
}
|
||||
|
||||
audio_convert_s16_to_float_C(out, in, samples - i);
|
||||
}
|
||||
|
||||
void audio_convert_float_to_s16_SSE2(int16_t *out,
|
||||
const float *in, size_t samples)
|
||||
{
|
||||
__m128 factor = _mm_set1_ps((float)0x7fff);
|
||||
size_t i;
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
__m128 input[2] = { _mm_loadu_ps(in + 0), _mm_loadu_ps(in + 4) };
|
||||
__m128 res[2] = { _mm_mul_ps(input[0], factor), _mm_mul_ps(input[1], factor) };
|
||||
|
||||
__m128i ints[2] = { _mm_cvtps_epi32(res[0]), _mm_cvtps_epi32(res[1]) };
|
||||
__m128i packed = _mm_packs_epi32(ints[0], ints[1]);
|
||||
|
||||
_mm_storeu_si128((__m128i *)out, packed);
|
||||
}
|
||||
|
||||
audio_convert_float_to_s16_C(out, in, samples - i);
|
||||
}
|
||||
#elif __ALTIVEC__
|
||||
void audio_convert_s16_to_float_altivec(float *out,
|
||||
const int16_t *in, size_t samples)
|
||||
{
|
||||
// Unaligned loads/store is a bit expensive, so we optimize for the good path (very likely).
|
||||
if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
vector signed short input = vec_ld(0, in);
|
||||
vector signed int hi = vec_unpackh(input);
|
||||
vector signed int lo = vec_unpackl(input);
|
||||
vector float out_hi = vec_ctf(hi, 15);
|
||||
vector float out_lo = vec_ctf(lo, 15);
|
||||
|
||||
vec_st(out_hi, 0, out);
|
||||
vec_st(out_lo, 16, out);
|
||||
}
|
||||
|
||||
audio_convert_s16_to_float_C(out, in, samples - i);
|
||||
}
|
||||
else
|
||||
audio_convert_s16_to_float_C(out, in, samples);
|
||||
}
|
||||
|
||||
void audio_convert_float_to_s16_altivec(int16_t *out,
|
||||
const float *in, size_t samples)
|
||||
{
|
||||
// Unaligned loads/store is a bit expensive, so we optimize for the good path (very likely).
|
||||
if (((uintptr_t)out & 15) + ((uintptr_t)in & 15) == 0)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
vector float input0 = vec_ld( 0, in);
|
||||
vector float input1 = vec_ld(16, in);
|
||||
vector signed int result0 = vec_cts(input0, 15);
|
||||
vector signed int result1 = vec_cts(input1, 15);
|
||||
vec_st(vec_packs(result0, result1), 0, out);
|
||||
}
|
||||
|
||||
audio_convert_float_to_s16_C(out, in, samples - i);
|
||||
}
|
||||
else
|
||||
audio_convert_float_to_s16_C(out, in, samples);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+22
-66
@@ -2,81 +2,37 @@
|
||||
#define AUDIO_UTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if __SSE2__
|
||||
#include <mmintrin.h>
|
||||
#include <emmintrin.h>
|
||||
#define audio_convert_s16_to_float audio_convert_s16_to_float_SSE2
|
||||
#define audio_convert_float_to_s16 audio_convert_float_to_s16_SSE2
|
||||
|
||||
void audio_convert_s16_to_float_SSE2(float *out,
|
||||
const int16_t *in, size_t samples);
|
||||
|
||||
void audio_convert_float_to_s16_SSE2(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
|
||||
#elif __ALTIVEC__
|
||||
#define audio_convert_s16_to_float audio_convert_s16_to_float_altivec
|
||||
#define audio_convert_float_to_s16 audio_convert_float_to_s16_altivec
|
||||
|
||||
void audio_convert_s16_to_float_altivec(float *out,
|
||||
const int16_t *in, size_t samples);
|
||||
|
||||
void audio_convert_float_to_s16_altivec(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
|
||||
#else
|
||||
#define audio_convert_s16_to_float audio_convert_s16_to_float_C
|
||||
#define audio_convert_float_to_s16 audio_convert_float_to_s16_C
|
||||
#endif
|
||||
|
||||
static inline void audio_convert_s16_to_float_C(float *out,
|
||||
const int16_t *in, unsigned samples)
|
||||
{
|
||||
for (unsigned i = 0; i < samples; i++)
|
||||
out[i] = (float)in[i] / 0x8000;
|
||||
}
|
||||
|
||||
static inline void audio_convert_float_to_s16_C(int16_t *out,
|
||||
const float *in, unsigned samples)
|
||||
{
|
||||
for (unsigned i = 0; i < samples; i++)
|
||||
{
|
||||
int32_t val = in[i] * 0x8000;
|
||||
out[i] = (val > 0x7FFF) ? 0x7FFF : (val < -0x8000 ? -0x8000 : (int16_t)val);
|
||||
}
|
||||
}
|
||||
|
||||
#if __SSE2__
|
||||
static inline void audio_convert_s16_to_float_SSE2(float *out,
|
||||
const int16_t *in, unsigned samples)
|
||||
{
|
||||
__m128 factor = _mm_set1_ps(1.0f / (0x7fff * 0x10000));
|
||||
unsigned i;
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
__m128i input = _mm_loadu_si128((const __m128i *)in);
|
||||
__m128i regs[2] = {
|
||||
_mm_unpacklo_epi16(_mm_setzero_si128(), input),
|
||||
_mm_unpackhi_epi16(_mm_setzero_si128(), input),
|
||||
};
|
||||
|
||||
__m128 output[2] = {
|
||||
_mm_mul_ps(_mm_cvtepi32_ps(regs[0]), factor),
|
||||
_mm_mul_ps(_mm_cvtepi32_ps(regs[1]), factor),
|
||||
};
|
||||
|
||||
_mm_storeu_ps(out + 0, output[0]);
|
||||
_mm_storeu_ps(out + 4, output[1]);
|
||||
}
|
||||
|
||||
audio_convert_s16_to_float_C(out, in, samples - i);
|
||||
}
|
||||
|
||||
static inline void audio_convert_float_to_s16_SSE2(int16_t *out,
|
||||
const float *in, unsigned samples)
|
||||
{
|
||||
__m128 factor = _mm_set1_ps((float)0x7fff);
|
||||
unsigned i;
|
||||
for (i = 0; i + 8 <= samples; i += 8, in += 8, out += 8)
|
||||
{
|
||||
__m128 input[2] = { _mm_loadu_ps(in + 0), _mm_loadu_ps(in + 4) };
|
||||
__m128 res[2] = { _mm_mul_ps(input[0], factor), _mm_mul_ps(input[1], factor) };
|
||||
|
||||
__m128i ints[2] = { _mm_cvtps_epi32(res[0]), _mm_cvtps_epi32(res[1]) };
|
||||
__m128i packed = _mm_packs_epi32(ints[0], ints[1]);
|
||||
|
||||
_mm_storeu_si128((__m128i *)out, packed);
|
||||
}
|
||||
|
||||
audio_convert_float_to_s16_C(out, in, samples - i);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
void audio_convert_s16_to_float_C(float *out,
|
||||
const int16_t *in, size_t samples);
|
||||
void audio_convert_float_to_s16_C(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
Simple C interface for XAudio2
|
||||
Author: Hans-Kristian Arntzen
|
||||
License: Public Domain
|
||||
*/
|
||||
|
||||
#include "xaudio.h"
|
||||
#include "xaudio-c.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "../../msvc/msvc_compat.h"
|
||||
|
||||
#define MAX_BUFFERS 16
|
||||
#define MAX_BUFFERS_MASK (MAX_BUFFERS - 1)
|
||||
|
||||
#undef min
|
||||
#undef max
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
struct xaudio2
|
||||
{
|
||||
const IXAudio2VoiceCallbackVtbl *lpVtbl;
|
||||
|
||||
uint8_t *buf;
|
||||
IXAudio2 *pXAudio2;
|
||||
IXAudio2MasteringVoice *pMasterVoice;
|
||||
IXAudio2SourceVoice *pSourceVoice;
|
||||
HANDLE hEvent;
|
||||
|
||||
volatile long buffers;
|
||||
unsigned bufsize;
|
||||
unsigned bufptr;
|
||||
unsigned write_buffer;
|
||||
};
|
||||
|
||||
static void WINAPI voice_on_buffer_end(void *handle_, void *data)
|
||||
{
|
||||
(void)data;
|
||||
xaudio2_t *handle = (xaudio2_t*)handle_;
|
||||
InterlockedDecrement(&handle->buffers);
|
||||
SetEvent(handle->hEvent);
|
||||
}
|
||||
|
||||
static void WINAPI dummy_voidp(void *handle, void *data) { (void)handle; (void)data; }
|
||||
static void WINAPI dummy_nil(void *handle) { (void)handle; }
|
||||
static void WINAPI dummy_uint32(void *handle, UINT32 dummy) { (void)handle; (void)dummy; }
|
||||
static void WINAPI dummy_voidp_hresult(void *handle, void *data, HRESULT dummy) { (void)handle; (void)data; (void)dummy; }
|
||||
|
||||
const struct IXAudio2VoiceCallbackVtbl voice_vtable = {
|
||||
dummy_uint32,
|
||||
dummy_nil,
|
||||
dummy_nil,
|
||||
dummy_voidp,
|
||||
voice_on_buffer_end,
|
||||
dummy_voidp,
|
||||
dummy_voidp_hresult,
|
||||
};
|
||||
|
||||
void xaudio2_enumerate_devices(xaudio2_t *xa)
|
||||
{
|
||||
UINT32 dev_count;
|
||||
IXAudio2_GetDeviceCount(xa->pXAudio2, &dev_count);
|
||||
fprintf(stderr, "XAudio2 devices:\n");
|
||||
for (unsigned i = 0; i < dev_count; i++)
|
||||
{
|
||||
XAUDIO2_DEVICE_DETAILS dev_detail;
|
||||
IXAudio2_GetDeviceDetails(xa->pXAudio2, i, &dev_detail);
|
||||
fwprintf(stderr, L"\t%u: %s\n", i, dev_detail.DisplayName);
|
||||
}
|
||||
}
|
||||
|
||||
xaudio2_t *xaudio2_new(unsigned samplerate, unsigned channels,
|
||||
size_t size, unsigned device)
|
||||
{
|
||||
xaudio2_t *handle = (xaudio2_t*)calloc(1, sizeof(*handle));
|
||||
if (!handle)
|
||||
return NULL;
|
||||
|
||||
handle->lpVtbl = &voice_vtable;
|
||||
CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||
WAVEFORMATEX wfx = {0};
|
||||
|
||||
if (FAILED(XAudio2Create(&handle->pXAudio2)))
|
||||
goto error;
|
||||
|
||||
if (FAILED(IXAudio2_CreateMasteringVoice(handle->pXAudio2,
|
||||
&handle->pMasterVoice, channels, samplerate, 0, device, NULL)))
|
||||
goto error;
|
||||
|
||||
wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
wfx.nChannels = channels;
|
||||
wfx.nSamplesPerSec = samplerate;
|
||||
wfx.nBlockAlign = channels * sizeof(float);
|
||||
wfx.wBitsPerSample = sizeof(float) * 8;
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
wfx.cbSize = 0;
|
||||
|
||||
if (FAILED(IXAudio2_CreateSourceVoice(handle->pXAudio2,
|
||||
&handle->pSourceVoice, &wfx,
|
||||
XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO,
|
||||
(IXAudio2VoiceCallback*)handle, 0, 0)))
|
||||
goto error;
|
||||
|
||||
handle->hEvent = CreateEvent(0, FALSE, FALSE, 0);
|
||||
if (!handle->hEvent)
|
||||
goto error;
|
||||
|
||||
handle->bufsize = size / MAX_BUFFERS;
|
||||
handle->buf = (uint8_t*)calloc(1, handle->bufsize * MAX_BUFFERS);
|
||||
if (!handle->buf)
|
||||
goto error;
|
||||
|
||||
if (FAILED(IXAudio2SourceVoice_Start(handle->pSourceVoice, 0, XAUDIO2_COMMIT_NOW)))
|
||||
goto error;
|
||||
|
||||
return handle;
|
||||
|
||||
error:
|
||||
xaudio2_free(handle);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t xaudio2_write_avail(xaudio2_t *handle)
|
||||
{
|
||||
return handle->bufsize * (MAX_BUFFERS - handle->buffers - 1);
|
||||
}
|
||||
|
||||
size_t xaudio2_write(xaudio2_t *handle, const void *buf, size_t bytes_)
|
||||
{
|
||||
unsigned bytes = bytes_;
|
||||
const uint8_t *buffer = (const uint8_t*)buf;
|
||||
while (bytes)
|
||||
{
|
||||
unsigned need = min(bytes, handle->bufsize - handle->bufptr);
|
||||
memcpy(handle->buf + handle->write_buffer * handle->bufsize + handle->bufptr,
|
||||
buffer, need);
|
||||
|
||||
handle->bufptr += need;
|
||||
buffer += need;
|
||||
bytes -= need;
|
||||
|
||||
if (handle->bufptr == handle->bufsize)
|
||||
{
|
||||
while (handle->buffers == MAX_BUFFERS - 1)
|
||||
WaitForSingleObject(handle->hEvent, INFINITE);
|
||||
|
||||
XAUDIO2_BUFFER xa2buffer = {0};
|
||||
xa2buffer.AudioBytes = handle->bufsize;
|
||||
xa2buffer.pAudioData = handle->buf + handle->write_buffer * handle->bufsize;
|
||||
|
||||
if (FAILED(IXAudio2SourceVoice_SubmitSourceBuffer(handle->pSourceVoice, &xa2buffer, NULL)))
|
||||
return 0;
|
||||
|
||||
InterlockedIncrement(&handle->buffers);
|
||||
handle->bufptr = 0;
|
||||
handle->write_buffer = (handle->write_buffer + 1) & MAX_BUFFERS_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_;
|
||||
}
|
||||
|
||||
void xaudio2_free(xaudio2_t *handle)
|
||||
{
|
||||
if (handle)
|
||||
{
|
||||
if (handle->pSourceVoice)
|
||||
{
|
||||
IXAudio2SourceVoice_Stop(handle->pSourceVoice, 0, XAUDIO2_COMMIT_NOW);
|
||||
IXAudio2SourceVoice_DestroyVoice(handle->pSourceVoice);
|
||||
}
|
||||
|
||||
if (handle->pMasterVoice)
|
||||
IXAudio2MasteringVoice_DestroyVoice(handle->pMasterVoice);
|
||||
|
||||
if (handle->pXAudio2)
|
||||
IXAudio2_Release(handle->pXAudio2);
|
||||
|
||||
if (handle->hEvent)
|
||||
CloseHandle(handle->hEvent);
|
||||
|
||||
free(handle->buf);
|
||||
free(handle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
/*
|
||||
Simple C interface for XAudio2
|
||||
Author: Hans-Kristian Arntzen
|
||||
License: Public Domain
|
||||
*/
|
||||
|
||||
#include "xaudio-c.h"
|
||||
#include "xaudio2.hpp"
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
|
||||
struct xaudio2 : public IXAudio2VoiceCallback
|
||||
{
|
||||
xaudio2(unsigned samplerate, unsigned channels, unsigned size) :
|
||||
buf(0), pXAudio2(0), pMasterVoice(0), pSourceVoice(0),
|
||||
buffers(0), bufptr(0)
|
||||
{
|
||||
CoInitializeEx(0, COINIT_MULTITHREADED);
|
||||
if (FAILED(XAudio2Create(&pXAudio2, 0)))
|
||||
throw -1;
|
||||
if (FAILED(pXAudio2->CreateMasteringVoice(
|
||||
&pMasterVoice, channels, samplerate, 0, 0, 0)))
|
||||
throw -1;
|
||||
|
||||
WAVEFORMATEX wfx;
|
||||
wfx.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
|
||||
wfx.nChannels = channels;
|
||||
wfx.nSamplesPerSec = samplerate;
|
||||
wfx.nBlockAlign = channels * sizeof(float);
|
||||
wfx.wBitsPerSample = sizeof(float) * 8;
|
||||
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
|
||||
wfx.cbSize = 0;
|
||||
if (FAILED(pXAudio2->CreateSourceVoice(
|
||||
&pSourceVoice, &wfx, XAUDIO2_VOICE_NOSRC, XAUDIO2_DEFAULT_FREQ_RATIO, this, 0, 0)))
|
||||
throw -1;
|
||||
|
||||
hEvent = CreateEvent(0, FALSE, FALSE, 0);
|
||||
if (!hEvent)
|
||||
throw -1;
|
||||
|
||||
pSourceVoice->Start(0);
|
||||
|
||||
bufsize = size / max_buffers;
|
||||
buf = new uint8_t[bufsize * max_buffers];
|
||||
write_buffer = 0;
|
||||
}
|
||||
|
||||
virtual ~xaudio2()
|
||||
{
|
||||
if (pSourceVoice)
|
||||
{
|
||||
pSourceVoice->Stop(0);
|
||||
pSourceVoice->DestroyVoice();
|
||||
}
|
||||
|
||||
if (pMasterVoice)
|
||||
pMasterVoice->DestroyVoice();
|
||||
|
||||
if (pXAudio2)
|
||||
pXAudio2->Release();
|
||||
|
||||
if (hEvent)
|
||||
CloseHandle(hEvent);
|
||||
|
||||
if (buf)
|
||||
delete[] buf;
|
||||
}
|
||||
|
||||
size_t write_avail() const
|
||||
{
|
||||
return bufsize * (max_buffers - buffers - 1);
|
||||
}
|
||||
|
||||
size_t write(const void *buffer_, size_t bytes_)
|
||||
{
|
||||
unsigned bytes = bytes_;
|
||||
const uint8_t *buffer = reinterpret_cast<const uint8_t*>(buffer_);
|
||||
while (bytes > 0)
|
||||
{
|
||||
unsigned need = std::min(bytes, bufsize - bufptr);
|
||||
memcpy(buf + write_buffer * bufsize + bufptr,
|
||||
buffer, need);
|
||||
|
||||
bufptr += need;
|
||||
buffer += need;
|
||||
bytes -= need;
|
||||
|
||||
if (bufptr == bufsize)
|
||||
{
|
||||
WaitForSingleObject(hEvent, 0); // Clear out ready state.
|
||||
while (buffers == max_buffers - 1)
|
||||
WaitForSingleObject(hEvent, INFINITE);
|
||||
|
||||
XAUDIO2_BUFFER xa2buffer = {0};
|
||||
xa2buffer.AudioBytes = bufsize;
|
||||
xa2buffer.pAudioData = buf + write_buffer * bufsize;
|
||||
xa2buffer.pContext = 0;
|
||||
if (FAILED(pSourceVoice->SubmitSourceBuffer(&xa2buffer)))
|
||||
return 0;
|
||||
|
||||
InterlockedIncrement(&buffers);
|
||||
bufptr = 0;
|
||||
write_buffer = (write_buffer + 1) & (max_buffers_mask);
|
||||
}
|
||||
}
|
||||
|
||||
return bytes_;
|
||||
}
|
||||
|
||||
enum { max_buffers = 16, max_buffers_mask = max_buffers - 1 };
|
||||
|
||||
STDMETHOD_(void, OnBufferStart) (void *) {}
|
||||
STDMETHOD_(void, OnBufferEnd) (void *)
|
||||
{
|
||||
InterlockedDecrement(&buffers);
|
||||
SetEvent(hEvent);
|
||||
}
|
||||
STDMETHOD_(void, OnLoopEnd) (void *) {}
|
||||
STDMETHOD_(void, OnStreamEnd) () {}
|
||||
STDMETHOD_(void, OnVoiceError) (void *, HRESULT) {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) () {}
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) {}
|
||||
|
||||
uint8_t *buf;
|
||||
IXAudio2 *pXAudio2;
|
||||
IXAudio2MasteringVoice *pMasterVoice;
|
||||
IXAudio2SourceVoice *pSourceVoice;
|
||||
HANDLE hEvent;
|
||||
|
||||
volatile long buffers;
|
||||
unsigned bufsize;
|
||||
unsigned bufptr;
|
||||
unsigned write_buffer;
|
||||
};
|
||||
|
||||
xaudio2_t *xaudio2_new(unsigned samplerate, unsigned channels, size_t size)
|
||||
{
|
||||
xaudio2 *handle = 0;
|
||||
try {
|
||||
handle = new xaudio2(samplerate, channels, size);
|
||||
} catch(...) { delete handle; return 0; }
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
size_t xaudio2_write_avail(xaudio2_t *handle)
|
||||
{
|
||||
try {
|
||||
return handle->write_avail();
|
||||
} catch(...) { return 0; }
|
||||
}
|
||||
|
||||
size_t xaudio2_write(xaudio2_t *handle, const void *buf, size_t bytes)
|
||||
{
|
||||
try {
|
||||
return handle->write(buf, bytes);
|
||||
} catch(...) { return 0; }
|
||||
}
|
||||
|
||||
void xaudio2_free(xaudio2_t *handle)
|
||||
{
|
||||
delete handle;
|
||||
}
|
||||
|
||||
@@ -15,10 +15,11 @@ extern "C" {
|
||||
|
||||
typedef struct xaudio2 xaudio2_t;
|
||||
|
||||
xaudio2_t* xaudio2_new(unsigned samplerate, unsigned channels, size_t bufsize);
|
||||
xaudio2_t* xaudio2_new(unsigned samplerate, unsigned channels, size_t bufsize, unsigned device);
|
||||
size_t xaudio2_write_avail(xaudio2_t *handle);
|
||||
size_t xaudio2_write(xaudio2_t *handle, const void *data, size_t bytes);
|
||||
void xaudio2_free(xaudio2_t *handle);
|
||||
void xaudio2_enumerate_devices(xaudio2_t *handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
Executable → Regular
+62
-151
@@ -1,35 +1,37 @@
|
||||
/*
|
||||
xaudio2.hpp (2010-08-14)
|
||||
author: OV2
|
||||
xaudio.h (2010-08-14) / (2012-12-04)
|
||||
authors: OV2, Themaister
|
||||
*/
|
||||
|
||||
// Kinda stripped down. Only contains the bare essentials used in SSNES.
|
||||
|
||||
#ifndef XAUDIO2_MINGW_H
|
||||
#define XAUDIO2_MINGW_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define CINTERFACE
|
||||
#endif
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#undef min
|
||||
#undef max
|
||||
#include <basetyps.h>
|
||||
#include <objbase.h>
|
||||
#include <mmreg.h>
|
||||
|
||||
// 64-bit GCC fix
|
||||
#define GUID_EXT EXTERN_C
|
||||
#define GUID_SECT
|
||||
|
||||
#define DEFINE_GUID_X(n,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) GUID_EXT const GUID n GUID_SECT = {l,w1,w2,{b1,b2,b3,b4,b5,b6,b7,b8}}
|
||||
#define DEFINE_GUID_X(n, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
static const GUID n = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
|
||||
#define DEFINE_CLSID_X(className, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
DEFINE_GUID_X(CLSID_##className, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
|
||||
#define DEFINE_IID_X(interfaceName, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
|
||||
DEFINE_GUID_X(IID_##interfaceName, 0x##l, 0x##w1, 0x##w2, 0x##b1, 0x##b2, 0x##b3, 0x##b4, 0x##b5, 0x##b6, 0x##b7, 0x##b8)
|
||||
#define X2DEFAULT(x) =x
|
||||
#define X2DEFAULT(x)
|
||||
|
||||
DEFINE_CLSID_X(XAudio2, e21a7345, eb21, 468e, be, 50, 80, 4d, b9, 7c, f7, 08);
|
||||
DEFINE_CLSID_X(XAudio2_Debug, f7a76c21, 53d4, 46bb, ac, 53, 8b, 45, 9c, ae, 46, bd);
|
||||
DEFINE_IID_X(IXAudio2, 8bcf1f58, 9fe7, 4583, 8a, c6, e2, ad, c4, 65, c8, bb);
|
||||
|
||||
DECLARE_INTERFACE(IXAudio2Voice);
|
||||
#ifndef INTERFACE
|
||||
#define INTERFACE void
|
||||
#endif
|
||||
|
||||
#define XAUDIO2_COMMIT_NOW 0
|
||||
#define XAUDIO2_DEFAULT_CHANNELS 0
|
||||
@@ -46,9 +48,21 @@ typedef enum XAUDIO2_DEVICE_ROLE
|
||||
DefaultCommunicationsDevice = 0x4,
|
||||
DefaultGameDevice = 0x8,
|
||||
GlobalDefaultDevice = 0xf,
|
||||
InvalidDeviceRole = ~GlobalDefaultDevice
|
||||
InvalidDeviceRole = ~GlobalDefaultDevice
|
||||
} XAUDIO2_DEVICE_ROLE;
|
||||
|
||||
typedef enum XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER
|
||||
{
|
||||
XAUDIO2_ANY_PROCESSOR = 0xffffffff,
|
||||
XAUDIO2_DEFAULT_PROCESSOR = XAUDIO2_ANY_PROCESSOR
|
||||
} XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER, XAUDIO2_PROCESSOR;
|
||||
|
||||
typedef enum XAUDIO2_FILTER_TYPE {
|
||||
LowPassFilter,
|
||||
BandPassFilter,
|
||||
HighPassFilter
|
||||
} XAUDIO2_FILTER_TYPE;
|
||||
|
||||
typedef struct XAUDIO2_DEVICE_DETAILS
|
||||
{
|
||||
WCHAR DeviceID[256];
|
||||
@@ -57,84 +71,17 @@ typedef struct XAUDIO2_DEVICE_DETAILS
|
||||
WAVEFORMATEXTENSIBLE OutputFormat;
|
||||
} XAUDIO2_DEVICE_DETAILS;
|
||||
|
||||
typedef struct XAUDIO2_VOICE_DETAILS
|
||||
{
|
||||
UINT32 CreationFlags;
|
||||
UINT32 InputChannels;
|
||||
UINT32 InputSampleRate;
|
||||
} XAUDIO2_VOICE_DETAILS;
|
||||
|
||||
typedef enum XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER
|
||||
{
|
||||
Processor1 = 0x00000001,
|
||||
Processor2 = 0x00000002,
|
||||
Processor3 = 0x00000004,
|
||||
Processor4 = 0x00000008,
|
||||
Processor5 = 0x00000010,
|
||||
Processor6 = 0x00000020,
|
||||
Processor7 = 0x00000040,
|
||||
Processor8 = 0x00000080,
|
||||
Processor9 = 0x00000100,
|
||||
Processor10 = 0x00000200,
|
||||
Processor11 = 0x00000400,
|
||||
Processor12 = 0x00000800,
|
||||
Processor13 = 0x00001000,
|
||||
Processor14 = 0x00002000,
|
||||
Processor15 = 0x00004000,
|
||||
Processor16 = 0x00008000,
|
||||
Processor17 = 0x00010000,
|
||||
Processor18 = 0x00020000,
|
||||
Processor19 = 0x00040000,
|
||||
Processor20 = 0x00080000,
|
||||
Processor21 = 0x00100000,
|
||||
Processor22 = 0x00200000,
|
||||
Processor23 = 0x00400000,
|
||||
Processor24 = 0x00800000,
|
||||
Processor25 = 0x01000000,
|
||||
Processor26 = 0x02000000,
|
||||
Processor27 = 0x04000000,
|
||||
Processor28 = 0x08000000,
|
||||
Processor29 = 0x10000000,
|
||||
Processor30 = 0x20000000,
|
||||
Processor31 = 0x40000000,
|
||||
Processor32 = 0x80000000,
|
||||
XAUDIO2_ANY_PROCESSOR = 0xffffffff,
|
||||
XAUDIO2_DEFAULT_PROCESSOR = XAUDIO2_ANY_PROCESSOR
|
||||
} XAUDIO2_WINDOWS_PROCESSOR_SPECIFIER, XAUDIO2_PROCESSOR;
|
||||
|
||||
typedef struct XAUDIO2_VOICE_SENDS
|
||||
{
|
||||
UINT32 OutputCount;
|
||||
IXAudio2Voice** pOutputVoices;
|
||||
} XAUDIO2_VOICE_SENDS;
|
||||
|
||||
typedef struct XAUDIO2_EFFECT_DESCRIPTOR
|
||||
{
|
||||
IUnknown* pEffect;
|
||||
BOOL InitialState;
|
||||
UINT32 OutputChannels;
|
||||
} XAUDIO2_EFFECT_DESCRIPTOR;
|
||||
|
||||
typedef struct XAUDIO2_EFFECT_CHAIN
|
||||
{
|
||||
UINT32 EffectCount;
|
||||
const XAUDIO2_EFFECT_DESCRIPTOR* pEffectDescriptors;
|
||||
} XAUDIO2_EFFECT_CHAIN;
|
||||
|
||||
typedef enum XAUDIO2_FILTER_TYPE
|
||||
{
|
||||
LowPassFilter,
|
||||
BandPassFilter,
|
||||
HighPassFilter
|
||||
} XAUDIO2_FILTER_TYPE;
|
||||
|
||||
typedef struct XAUDIO2_FILTER_PARAMETERS
|
||||
{
|
||||
XAUDIO2_FILTER_TYPE Type;
|
||||
float Frequency;
|
||||
float OneOverQ;
|
||||
|
||||
} XAUDIO2_FILTER_PARAMETERS;
|
||||
typedef struct XAUDIO2_VOICE_DETAILS XAUDIO2_VOICE_DETAILS;
|
||||
typedef struct XAUDIO2_VOICE_SENDS XAUDIO2_VOICE_SENDS;
|
||||
typedef struct XAUDIO2_EFFECT_DESCRIPTOR XAUDIO2_EFFECT_DESCRIPTOR;
|
||||
typedef struct XAUDIO2_EFFECT_CHAIN XAUDIO2_EFFECT_CHAIN;
|
||||
typedef struct XAUDIO2_FILTER_PARAMETERS XAUDIO2_FILTER_PARAMETERS;
|
||||
typedef struct XAUDIO2_BUFFER_WMA XAUDIO2_BUFFER_WMA;
|
||||
typedef struct XAUDIO2_VOICE_STATE XAUDIO2_VOICE_STATE;
|
||||
typedef struct XAUDIO2_PERFORMANCE_DATA XAUDIO2_PERFORMANCE_DATA;
|
||||
typedef struct XAUDIO2_DEBUG_CONFIGURATION XAUDIO2_DEBUG_CONFIGURATION;
|
||||
typedef struct IXAudio2EngineCallback IXAudio2EngineCallback;
|
||||
typedef struct IXAudio2SubmixVoice IXAudio2SubmixVoice;
|
||||
|
||||
typedef struct XAUDIO2_BUFFER
|
||||
{
|
||||
@@ -149,53 +96,6 @@ typedef struct XAUDIO2_BUFFER
|
||||
void *pContext;
|
||||
} XAUDIO2_BUFFER;
|
||||
|
||||
typedef struct XAUDIO2_BUFFER_WMA
|
||||
{
|
||||
const UINT32* pDecodedPacketCumulativeBytes;
|
||||
UINT32 PacketCount;
|
||||
} XAUDIO2_BUFFER_WMA;
|
||||
|
||||
typedef struct XAUDIO2_VOICE_STATE
|
||||
{
|
||||
void *pCurrentBufferContext;
|
||||
UINT32 BuffersQueued;
|
||||
UINT64 SamplesPlayed;
|
||||
} XAUDIO2_VOICE_STATE;
|
||||
|
||||
typedef struct XAUDIO2_PERFORMANCE_DATA
|
||||
{
|
||||
UINT64 AudioCyclesSinceLastQuery;
|
||||
UINT64 TotalCyclesSinceLastQuery;
|
||||
UINT32 MinimumCyclesPerQuantum;
|
||||
UINT32 MaximumCyclesPerQuantum;
|
||||
UINT32 MemoryUsageInBytes;
|
||||
UINT32 CurrentLatencyInSamples;
|
||||
UINT32 GlitchesSinceEngineStarted;
|
||||
UINT32 ActiveSourceVoiceCount;
|
||||
UINT32 TotalSourceVoiceCount;
|
||||
UINT32 ActiveSubmixVoiceCount;
|
||||
UINT32 TotalSubmixVoiceCount;
|
||||
UINT32 ActiveXmaSourceVoices;
|
||||
UINT32 ActiveXmaStreams;
|
||||
} XAUDIO2_PERFORMANCE_DATA;
|
||||
|
||||
typedef struct XAUDIO2_DEBUG_CONFIGURATION
|
||||
{
|
||||
UINT32 TraceMask;
|
||||
UINT32 BreakMask;
|
||||
BOOL LogThreadID;
|
||||
BOOL LogFileline;
|
||||
BOOL LogFunctionName;
|
||||
BOOL LogTiming;
|
||||
} XAUDIO2_DEBUG_CONFIGURATION;
|
||||
|
||||
DECLARE_INTERFACE(IXAudio2EngineCallback)
|
||||
{
|
||||
STDMETHOD_(void, OnProcessingPassStart) (THIS) PURE;
|
||||
STDMETHOD_(void, OnProcessingPassEnd) (THIS) PURE;
|
||||
STDMETHOD_(void, OnCriticalError) (THIS_ HRESULT Error) PURE;
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE(IXAudio2VoiceCallback)
|
||||
{
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (THIS_ UINT32 BytesRequired) PURE;
|
||||
@@ -245,17 +145,11 @@ DECLARE_INTERFACE(IXAudio2Voice)
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2MasteringVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2SubmixVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2SourceVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
@@ -307,20 +201,37 @@ DECLARE_INTERFACE_(IXAudio2, IUnknown)
|
||||
void *pReserved X2DEFAULT(NULL)) PURE;
|
||||
};
|
||||
|
||||
static inline HRESULT XAudio2Create(IXAudio2** ppXAudio2, UINT32 Flags X2DEFAULT(0),
|
||||
XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR))
|
||||
// C hooks.
|
||||
#define IXAudio2_Initialize(THIS, ...) (THIS)->lpVtbl->Initialize(THIS, __VA_ARGS__)
|
||||
#define IXAudio2_Release(THIS) (THIS)->lpVtbl->Release(THIS)
|
||||
#define IXAudio2_CreateSourceVoice(THIS, ...) (THIS)->lpVtbl->CreateSourceVoice(THIS, __VA_ARGS__)
|
||||
#define IXAudio2_CreateMasteringVoice(THIS, ...) (THIS)->lpVtbl->CreateMasteringVoice(THIS, __VA_ARGS__)
|
||||
#define IXAudio2_GetDeviceCount(THIS, ...) (THIS)->lpVtbl->GetDeviceCount(THIS, __VA_ARGS__)
|
||||
#define IXAudio2_GetDeviceDetails(THIS, ...) (THIS)->lpVtbl->GetDeviceDetails(THIS, __VA_ARGS__)
|
||||
|
||||
#define IXAudio2SourceVoice_Start(THIS, ...) (THIS)->lpVtbl->Start(THIS, __VA_ARGS__)
|
||||
#define IXAudio2SourceVoice_Stop(THIS, ...) (THIS)->lpVtbl->Stop(THIS, __VA_ARGS__)
|
||||
#define IXAudio2SourceVoice_SubmitSourceBuffer(THIS, ...) (THIS)->lpVtbl->SubmitSourceBuffer(THIS, __VA_ARGS__)
|
||||
#define IXAudio2SourceVoice_DestroyVoice(THIS) (THIS)->lpVtbl->DestroyVoice(THIS)
|
||||
#define IXAudio2MasteringVoice_DestroyVoice(THIS) (THIS)->lpVtbl->DestroyVoice(THIS)
|
||||
|
||||
static inline HRESULT XAudio2Create(IXAudio2 **ppXAudio2)
|
||||
{
|
||||
IXAudio2* pXAudio2;
|
||||
HRESULT hr = CoCreateInstance((Flags & XAUDIO2_DEBUG_ENGINE) ? CLSID_XAudio2_Debug : CLSID_XAudio2,
|
||||
NULL, CLSCTX_INPROC_SERVER, IID_IXAudio2, reinterpret_cast<void**>(&pXAudio2));
|
||||
IXAudio2 *pXAudio2;
|
||||
|
||||
#ifdef __cplusplus
|
||||
HRESULT hr = CoCreateInstance(CLSID_XAudio2, NULL, CLSCTX_INPROC_SERVER, IID_IXAudio2, (void**)&pXAudio2);
|
||||
#else
|
||||
HRESULT hr = CoCreateInstance(&CLSID_XAudio2, NULL, CLSCTX_INPROC_SERVER, &IID_IXAudio2, (void**)&pXAudio2);
|
||||
#endif
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = pXAudio2->Initialize(Flags, XAudio2Processor);
|
||||
hr = IXAudio2_Initialize(pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
|
||||
if (SUCCEEDED(hr))
|
||||
*ppXAudio2 = pXAudio2;
|
||||
else
|
||||
pXAudio2->Release();
|
||||
IXAudio2_Release(pXAudio2);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
+44
-19
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -15,15 +15,16 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "driver.h"
|
||||
#include "../driver.h"
|
||||
#include <stdlib.h>
|
||||
#include "xaudio-c/xaudio-c.h"
|
||||
#include "general.h"
|
||||
#include "../general.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
xaudio2_t *xa;
|
||||
bool nonblock;
|
||||
size_t bufsize;
|
||||
} xa_t;
|
||||
|
||||
static void *xa_init(const char *device, unsigned rate, unsigned latency)
|
||||
@@ -31,27 +32,37 @@ static void *xa_init(const char *device, unsigned rate, unsigned latency)
|
||||
if (latency < 8)
|
||||
latency = 8; // Do not allow shenanigans.
|
||||
|
||||
xa_t *xa = calloc(1, sizeof(xa_t));
|
||||
if (xa == NULL)
|
||||
xa_t *xa = (xa_t*)calloc(1, sizeof(*xa));
|
||||
if (!xa)
|
||||
return NULL;
|
||||
|
||||
size_t bufsize = latency * rate / 1000;
|
||||
|
||||
SSNES_LOG("XAudio2: Requesting %d ms latency, using %d ms latency.\n", latency, (int)bufsize * 1000 / rate);
|
||||
|
||||
xa->xa = xaudio2_new(rate, 2, bufsize * 2 * sizeof(float));
|
||||
xa->bufsize = bufsize * 2 * sizeof(float);
|
||||
|
||||
unsigned device_index = 0;
|
||||
if (device)
|
||||
device_index = strtoul(device, NULL, 0);
|
||||
|
||||
xa->xa = xaudio2_new(rate, 2, xa->bufsize, device_index);
|
||||
if (!xa->xa)
|
||||
{
|
||||
SSNES_ERR("Failed to init XAudio2.\n");
|
||||
free(xa);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (g_extern.verbose)
|
||||
xaudio2_enumerate_devices(xa->xa);
|
||||
|
||||
return xa;
|
||||
}
|
||||
|
||||
static ssize_t xa_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
xa_t *xa = data;
|
||||
xa_t *xa = (xa_t*)data;
|
||||
if (xa->nonblock)
|
||||
{
|
||||
size_t avail = xaudio2_write_avail(xa->xa);
|
||||
@@ -60,8 +71,9 @@ static ssize_t xa_write(void *data, const void *buf, size_t size)
|
||||
if (avail < size)
|
||||
size = avail;
|
||||
}
|
||||
|
||||
size_t ret = xaudio2_write(xa->xa, buf, size);
|
||||
if (ret == 0)
|
||||
if (ret == 0 && size > 0)
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
@@ -74,7 +86,7 @@ static bool xa_stop(void *data)
|
||||
|
||||
static void xa_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
xa_t *xa = data;
|
||||
xa_t *xa = (xa_t*)data;
|
||||
xa->nonblock = state;
|
||||
}
|
||||
|
||||
@@ -92,7 +104,7 @@ static bool xa_use_float(void *data)
|
||||
|
||||
static void xa_free(void *data)
|
||||
{
|
||||
xa_t *xa = data;
|
||||
xa_t *xa = (xa_t*)data;
|
||||
if (xa)
|
||||
{
|
||||
if (xa->xa)
|
||||
@@ -101,14 +113,27 @@ static void xa_free(void *data)
|
||||
}
|
||||
}
|
||||
|
||||
static size_t xa_write_avail(void *data)
|
||||
{
|
||||
xa_t *xa = (xa_t*)data;
|
||||
return xaudio2_write_avail(xa->xa);
|
||||
}
|
||||
|
||||
static size_t xa_buffer_size(void *data)
|
||||
{
|
||||
xa_t *xa = (xa_t*)data;
|
||||
return xa->bufsize;
|
||||
}
|
||||
|
||||
const audio_driver_t audio_xa = {
|
||||
.init = xa_init,
|
||||
.write = xa_write,
|
||||
.stop = xa_stop,
|
||||
.start = xa_start,
|
||||
.set_nonblock_state = xa_set_nonblock_state,
|
||||
.use_float = xa_use_float,
|
||||
.free = xa_free,
|
||||
.ident = "xaudio"
|
||||
xa_init,
|
||||
xa_write,
|
||||
xa_stop,
|
||||
xa_start,
|
||||
xa_set_nonblock_state,
|
||||
xa_free,
|
||||
xa_use_float,
|
||||
"xaudio",
|
||||
xa_write_avail,
|
||||
xa_buffer_size,
|
||||
};
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "autosave.h"
|
||||
#include "thread.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include "boolean.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "general.h"
|
||||
@@ -41,7 +41,7 @@ struct autosave
|
||||
|
||||
static void autosave_thread(void *data)
|
||||
{
|
||||
autosave_t *save = data;
|
||||
autosave_t *save = (autosave_t*)data;
|
||||
|
||||
bool first_log = true;
|
||||
|
||||
@@ -73,7 +73,7 @@ static void autosave_thread(void *data)
|
||||
failed |= fflush(file) != 0;
|
||||
failed |= fclose(file) != 0;
|
||||
if (failed)
|
||||
SSNES_WARN("Failed to autosave SRAM! Disk might be full.\n");
|
||||
SSNES_WARN("Failed to autosave SRAM. Disk might be full.\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ static void autosave_thread(void *data)
|
||||
|
||||
autosave_t *autosave_new(const char *path, const void *data, size_t size, unsigned interval)
|
||||
{
|
||||
autosave_t *handle = calloc(1, sizeof(*handle));
|
||||
autosave_t *handle = (autosave_t*)calloc(1, sizeof(*handle));
|
||||
if (!handle)
|
||||
return NULL;
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - 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_BOOLEAN_H
|
||||
#define __SSNES_BOOLEAN_H
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "bps.h"
|
||||
#include "movie.h"
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
enum bps_mode
|
||||
{
|
||||
SOURCE_READ = 0,
|
||||
TARGET_READ,
|
||||
SOURCE_COPY,
|
||||
TARGET_COPY
|
||||
};
|
||||
|
||||
struct bps_data
|
||||
{
|
||||
const uint8_t *modify_data, *source_data;
|
||||
uint8_t *target_data;
|
||||
size_t modify_length, source_length, target_length;
|
||||
size_t modify_offset, source_offset, target_offset;
|
||||
uint32_t modify_checksum, source_checksum, target_checksum;
|
||||
|
||||
size_t source_relative_offset, target_relative_offset, output_offset;
|
||||
};
|
||||
|
||||
static uint8_t bps_read(struct bps_data *bps)
|
||||
{
|
||||
uint8_t data = bps->modify_data[bps->modify_offset++];
|
||||
bps->modify_checksum = crc32_adjust(bps->modify_checksum, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
static uint64_t bps_decode(struct bps_data *bps)
|
||||
{
|
||||
uint64_t data = 0, shift = 1;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
uint8_t x = bps_read(bps);
|
||||
data += (x & 0x7f) * shift;
|
||||
if (x & 0x80)
|
||||
break;
|
||||
shift <<= 7;
|
||||
data += shift;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static void bps_write(struct bps_data *bps, uint8_t data)
|
||||
{
|
||||
bps->target_data[bps->output_offset++] = data;
|
||||
bps->target_checksum = crc32_adjust(bps->target_checksum, data);
|
||||
}
|
||||
|
||||
bps_error_t bps_apply_patch(
|
||||
const uint8_t *modify_data, size_t modify_length,
|
||||
const uint8_t *source_data, size_t source_length,
|
||||
uint8_t *target_data, size_t *target_length)
|
||||
{
|
||||
if (modify_length < 19)
|
||||
return BPS_PATCH_TOO_SMALL;
|
||||
|
||||
struct bps_data bps = {
|
||||
.modify_data = modify_data,
|
||||
.modify_length = modify_length,
|
||||
.target_data = target_data,
|
||||
.target_length = *target_length,
|
||||
.source_data = source_data,
|
||||
.source_length = source_length,
|
||||
|
||||
.modify_checksum = ~0,
|
||||
.target_checksum = ~0,
|
||||
};
|
||||
|
||||
if ((bps_read(&bps) != 'B') || (bps_read(&bps) != 'P') || (bps_read(&bps) != 'S') || (bps_read(&bps) != '1'))
|
||||
return BPS_PATCH_INVALID_HEADER;
|
||||
|
||||
size_t modify_source_size = bps_decode(&bps);
|
||||
size_t modify_target_size = bps_decode(&bps);
|
||||
size_t modify_markup_size = bps_decode(&bps);
|
||||
for (size_t i = 0; i < modify_markup_size; i++)
|
||||
bps_read(&bps);
|
||||
|
||||
if (modify_source_size > bps.source_length)
|
||||
return BPS_SOURCE_TOO_SMALL;
|
||||
if (modify_target_size > bps.target_length)
|
||||
return BPS_TARGET_TOO_SMALL;
|
||||
|
||||
while (bps.modify_offset < bps.modify_length - 12)
|
||||
{
|
||||
size_t length = bps_decode(&bps);
|
||||
unsigned mode = length & 3;
|
||||
length = (length >> 2) + 1;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case SOURCE_READ:
|
||||
while (length--)
|
||||
bps_write(&bps, bps.source_data[bps.output_offset]);
|
||||
break;
|
||||
|
||||
case TARGET_READ:
|
||||
while (length--)
|
||||
bps_write(&bps, bps_read(&bps));
|
||||
break;
|
||||
|
||||
case SOURCE_COPY:
|
||||
case TARGET_COPY:
|
||||
{
|
||||
int offset = bps_decode(&bps);
|
||||
bool negative = offset & 1;
|
||||
offset >>= 1;
|
||||
if (negative)
|
||||
offset = -offset;
|
||||
|
||||
if (mode == SOURCE_COPY)
|
||||
{
|
||||
bps.source_offset += offset;
|
||||
while (length--)
|
||||
bps_write(&bps, bps.source_data[bps.source_offset++]);
|
||||
}
|
||||
else
|
||||
{
|
||||
bps.target_offset += offset;
|
||||
while (length--)
|
||||
bps_write(&bps, bps.target_data[bps.target_offset++]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t modify_source_checksum = 0, modify_target_checksum = 0, modify_modify_checksum = 0;
|
||||
for (unsigned i = 0; i < 32; i += 8)
|
||||
modify_source_checksum |= bps_read(&bps) << i;
|
||||
for (unsigned i = 0; i < 32; i += 8)
|
||||
modify_target_checksum |= bps_read(&bps) << i;
|
||||
|
||||
uint32_t checksum = ~bps.modify_checksum;
|
||||
for (unsigned i = 0; i < 32; i += 8)
|
||||
modify_modify_checksum |= bps_read(&bps) << i;
|
||||
|
||||
bps.source_checksum = crc32_calculate(bps.source_data, bps.source_length);
|
||||
bps.target_checksum = ~bps.target_checksum;
|
||||
|
||||
if (bps.source_checksum != modify_source_checksum)
|
||||
return BPS_SOURCE_CHECKSUM_INVALID;
|
||||
if (bps.target_checksum != modify_target_checksum)
|
||||
return BPS_TARGET_CHECKSUM_INVALID;
|
||||
if (checksum != modify_modify_checksum)
|
||||
return BPS_PATCH_CHECKSUM_INVALID;
|
||||
|
||||
*target_length = modify_target_size;
|
||||
|
||||
return BPS_SUCCESS;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -16,10 +16,10 @@
|
||||
*/
|
||||
|
||||
#include "cheats.h"
|
||||
#include "sha256.h"
|
||||
#include "hash.h"
|
||||
#include "dynamic.h"
|
||||
#include "general.h"
|
||||
#include "strl.h"
|
||||
#include "compat/strl.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
@@ -57,7 +57,7 @@ static char *strcat_alloc(char *dest, const char *input)
|
||||
size_t input_len = strlen(input);
|
||||
size_t required_len = dest_len + input_len + 1;
|
||||
|
||||
char *output = realloc(dest, required_len);
|
||||
char *output = (char*)realloc(dest, required_len);
|
||||
if (!output)
|
||||
return NULL;
|
||||
|
||||
@@ -126,7 +126,7 @@ static bool xml_grab_cheats(cheat_manager_t *handle, xmlNodePtr ptr)
|
||||
if (handle->size == handle->buf_size)
|
||||
{
|
||||
handle->buf_size *= 2;
|
||||
handle->cheats = realloc(handle->cheats, handle->buf_size * sizeof(struct cheat));
|
||||
handle->cheats = (struct cheat*)realloc(handle->cheats, handle->buf_size * sizeof(struct cheat));
|
||||
if (!handle->cheats)
|
||||
return false;
|
||||
}
|
||||
@@ -201,7 +201,7 @@ static void cheat_manager_save_config(cheat_manager_t *handle, const char *path,
|
||||
|
||||
if (!conf)
|
||||
{
|
||||
SSNES_ERR("Cannot save XML cheat settings!\n");
|
||||
SSNES_ERR("Cannot save XML cheat settings.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -223,7 +223,7 @@ static void cheat_manager_save_config(cheat_manager_t *handle, const char *path,
|
||||
config_set_string(conf, sha256, conf_str);
|
||||
|
||||
if (!config_file_write(conf, path))
|
||||
SSNES_ERR("Failed to write XML cheat settings to \"%s\"! Check permissions!\n", path);
|
||||
SSNES_ERR("Failed to write XML cheat settings to \"%s\". Check permissions.\n", path);
|
||||
|
||||
config_file_free(conf);
|
||||
|
||||
@@ -242,12 +242,15 @@ cheat_manager_t* cheat_manager_new(const char *path)
|
||||
|
||||
xmlParserCtxtPtr ctx = NULL;
|
||||
xmlDocPtr doc = NULL;
|
||||
cheat_manager_t *handle = calloc(1, sizeof(struct cheat_manager));
|
||||
cheat_manager_t *handle = (cheat_manager_t*)calloc(1, sizeof(struct cheat_manager));
|
||||
if (!handle)
|
||||
return NULL;
|
||||
|
||||
xmlNodePtr head = NULL;
|
||||
xmlNodePtr cur = NULL;
|
||||
|
||||
handle->buf_size = 1;
|
||||
handle->cheats = calloc(handle->buf_size, sizeof(struct cheat));
|
||||
handle->cheats = (struct cheat*)calloc(handle->buf_size, sizeof(struct cheat));
|
||||
if (!handle->cheats)
|
||||
{
|
||||
handle->buf_size = 0;
|
||||
@@ -271,8 +274,7 @@ cheat_manager_t* cheat_manager_new(const char *path)
|
||||
goto error;
|
||||
}
|
||||
|
||||
xmlNodePtr head = xmlDocGetRootElement(doc);
|
||||
xmlNodePtr cur = NULL;
|
||||
head = xmlDocGetRootElement(doc);
|
||||
for (cur = head; cur; cur = cur->next)
|
||||
{
|
||||
if (cur->type == XML_ELEMENT_NODE && strcmp((const char*)cur->name, "database") == 0)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
|
||||
+284
@@ -0,0 +1,284 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "getopt_ssnes.h"
|
||||
#include "strl.h"
|
||||
#include "posix_string.h"
|
||||
|
||||
#ifndef HAVE_GETOPT_LONG
|
||||
|
||||
#include <string.h>
|
||||
#include "../boolean.h"
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef _MSC_VER
|
||||
#include "../msvc/msvc_compat.h"
|
||||
#endif
|
||||
#include "../general.h"
|
||||
|
||||
char *optarg;
|
||||
int optind, opterr, optopt;
|
||||
|
||||
static bool is_short_option(const char *str)
|
||||
{
|
||||
return str[0] == '-' && str[1] != '-';
|
||||
}
|
||||
|
||||
static bool is_long_option(const char *str)
|
||||
{
|
||||
return str[0] == '-' && str[1] == '-';
|
||||
}
|
||||
|
||||
static int find_short_index(char * const *argv)
|
||||
{
|
||||
for (int index = 0; argv[index]; index++)
|
||||
{
|
||||
if (is_short_option(argv[index]))
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_long_index(char * const *argv)
|
||||
{
|
||||
for (int index = 0; argv[index]; index++)
|
||||
{
|
||||
if (is_long_option(argv[index]))
|
||||
return index;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int parse_short(const char *optstring, char * const *argv)
|
||||
{
|
||||
char arg = argv[0][1];
|
||||
if (arg == ':')
|
||||
return '?';
|
||||
|
||||
const char *opt = strchr(optstring, arg);
|
||||
if (!opt)
|
||||
return '?';
|
||||
|
||||
bool extra_opt = argv[0][2];
|
||||
bool takes_arg = opt[1] == ':';
|
||||
|
||||
// If we take an argument, and we see additional characters,
|
||||
// this is in fact the argument (i.e. -cfoo is same as -c foo).
|
||||
bool embedded_arg = extra_opt && takes_arg;
|
||||
|
||||
if (takes_arg)
|
||||
{
|
||||
if (embedded_arg)
|
||||
{
|
||||
optarg = argv[0] + 2;
|
||||
optind++;
|
||||
}
|
||||
else
|
||||
{
|
||||
optarg = argv[1];
|
||||
optind += 2;
|
||||
}
|
||||
|
||||
return optarg ? opt[0] : '?';
|
||||
}
|
||||
else if (embedded_arg) // If we see additional characters, and they don't take arguments, this means we have multiple flags in one.
|
||||
{
|
||||
memmove(&argv[0][1], &argv[0][2], strlen(&argv[0][2]) + 1);
|
||||
return opt[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
optind++;
|
||||
return opt[0];
|
||||
}
|
||||
}
|
||||
|
||||
static int parse_long(const struct option *longopts, char * const *argv)
|
||||
{
|
||||
const struct option *opt = NULL;
|
||||
for (size_t indice = 0; longopts[indice].name; indice++)
|
||||
{
|
||||
if (strcmp(longopts[indice].name, &argv[0][2]) == 0)
|
||||
{
|
||||
opt = &longopts[indice];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!opt)
|
||||
return '?';
|
||||
|
||||
// getopt_long has an "optional" arg, but we don't bother with that.
|
||||
if (opt->has_arg && !argv[1])
|
||||
return '?';
|
||||
|
||||
if (opt->has_arg)
|
||||
{
|
||||
optarg = argv[1];
|
||||
optind += 2;
|
||||
}
|
||||
else
|
||||
optind++;
|
||||
|
||||
if (opt->flag)
|
||||
{
|
||||
*opt->flag = opt->val;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
return opt->val;
|
||||
}
|
||||
|
||||
static void shuffle_block(char **begin, char **last, char **end)
|
||||
{
|
||||
ptrdiff_t len = last - begin;
|
||||
const char **tmp = (const char**)calloc(len, sizeof(const char*));
|
||||
ssnes_assert(tmp);
|
||||
|
||||
memcpy(tmp, begin, sizeof(tmp));
|
||||
memmove(begin, last, (end - last) * sizeof(char*));
|
||||
memcpy(end - len, tmp, sizeof(tmp));
|
||||
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
int getopt_long(int argc, char *argv[],
|
||||
const char *optstring, const struct option *longopts, int *longindex)
|
||||
{
|
||||
(void)longindex;
|
||||
if (argc == 1)
|
||||
return -1;
|
||||
|
||||
if (optind == 0)
|
||||
optind = 1;
|
||||
|
||||
int short_index = find_short_index(&argv[optind]);
|
||||
int long_index = find_long_index(&argv[optind]);
|
||||
|
||||
// We're done here.
|
||||
if (short_index == -1 && long_index == -1)
|
||||
return -1;
|
||||
|
||||
// Reorder argv so that non-options come last.
|
||||
// Non-POSIXy, but that's what getopt does by default.
|
||||
if ((short_index > 0) && ((short_index < long_index) || (long_index == -1)))
|
||||
{
|
||||
shuffle_block(&argv[optind], &argv[optind + short_index], &argv[argc]);
|
||||
short_index = 0;
|
||||
}
|
||||
else if ((long_index > 0) && ((long_index < short_index) || (short_index == -1)))
|
||||
{
|
||||
shuffle_block(&argv[optind], &argv[optind + long_index], &argv[argc]);
|
||||
long_index = 0;
|
||||
}
|
||||
|
||||
ssnes_assert(short_index == 0 || long_index == 0);
|
||||
|
||||
if (short_index == 0)
|
||||
return parse_short(optstring, &argv[optind]);
|
||||
else if (long_index == 0)
|
||||
return parse_long(longopts, &argv[optind]);
|
||||
else
|
||||
return '?';
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRL
|
||||
|
||||
// Implementation of strlcpy()/strlcat() based on OpenBSD.
|
||||
|
||||
size_t strlcpy(char *dest, const char *source, size_t size)
|
||||
{
|
||||
size_t src_size = 0;
|
||||
size_t n = size;
|
||||
|
||||
if (n)
|
||||
while (--n && (*dest++ = *source++)) src_size++;
|
||||
|
||||
if (!n)
|
||||
{
|
||||
if (size) *dest = '\0';
|
||||
while (*source++) src_size++;
|
||||
}
|
||||
|
||||
return src_size;
|
||||
}
|
||||
|
||||
size_t strlcat(char *dest, const char *source, size_t size)
|
||||
{
|
||||
size_t len = strlen(dest);
|
||||
dest += len;
|
||||
|
||||
if (len > size)
|
||||
size = 0;
|
||||
else
|
||||
size -= len;
|
||||
|
||||
return len + strlcpy(dest, source, size);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#undef strcasecmp
|
||||
#undef strdup
|
||||
#undef isblank
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include "strl.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
int strcasecmp_ssnes__(const char *a, const char *b)
|
||||
{
|
||||
while (*a && *b)
|
||||
{
|
||||
int a_ = tolower(*a);
|
||||
int b_ = tolower(*b);
|
||||
if (a_ != b_)
|
||||
return a_ - b_;
|
||||
|
||||
a++;
|
||||
b++;
|
||||
}
|
||||
|
||||
return tolower(*a) - tolower(*b);
|
||||
}
|
||||
|
||||
char *strdup_ssnes__(const char *orig)
|
||||
{
|
||||
size_t len = strlen(orig) + 1;
|
||||
char *ret = (char*)malloc(len);
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
strlcpy(ret, orig, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int isblank_ssnes__(int c)
|
||||
{
|
||||
return (c == ' ') || (c == '\t');
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __SSNES_GETOPT_H
|
||||
#define __SSNES_GETOPT_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
// Custom implementation of the GNU getopt_long for portability.
|
||||
// Not designed to be fully compatible,
|
||||
// but compatible with the features SSNES uses.
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
#include <getopt.h>
|
||||
#else
|
||||
// Avoid possible naming collitions during link since we prefer to use the actual name.
|
||||
#define getopt_long(argc, argv, optstring, longopts, longindex) __getopt_long_ssnes(argc, argv, optstring, longopts, longindex)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct option
|
||||
{
|
||||
const char *name;
|
||||
int has_arg;
|
||||
int *flag;
|
||||
int val;
|
||||
};
|
||||
|
||||
// argv[] is declared with char * const argv[] in GNU,
|
||||
// but this makes no sense, as non-POSIX getopt_long mutates argv (non-opts are moved to the end).
|
||||
int getopt_long(int argc, char *argv[],
|
||||
const char *optstring, const struct option *longopts, int *longindex);
|
||||
extern char *optarg;
|
||||
extern int optind, opterr, optopt;
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - 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_POSIX_STRING_H
|
||||
#define __SSNES_POSIX_STRING_H
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#undef strcasecmp
|
||||
#define strcasecmp(a, b) strcasecmp_ssnes__(a, b)
|
||||
#define strdup(orig) strdup_ssnes__(orig)
|
||||
#define isblank(c) isblank_ssnes__(c)
|
||||
int strcasecmp(const char *a, const char *b);
|
||||
char *strdup(const char *orig);
|
||||
int isblank(int c);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+12
-6
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -21,18 +21,24 @@
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef HAVE_CONFIG
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_STRL
|
||||
// Avoid possible naming collitions during link since we prefer to use the actual name.
|
||||
#define strlcpy(dst, src, size) __strlcpy_ssnes(dst, src, size)
|
||||
#define strlcat(dst, src, size) __strlcat_ssnes(dst, src, size)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
// Avoid possible naming collisions during link since we prefer to use the actual name.
|
||||
#define strlcpy(dst, src, size) strlcpy_ssnes__(dst, src, size)
|
||||
#define strlcat(dst, src, size) strlcat_ssnes__(dst, src, size)
|
||||
|
||||
size_t strlcpy(char *dest, const char *source, size_t size);
|
||||
size_t strlcat(char *dest, const char *source, size_t size);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+137
-43
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -22,20 +22,26 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include "strl.h"
|
||||
#include "../compat/strl.h"
|
||||
#include "../compat/posix_string.h"
|
||||
#include "../msvc/msvc_compat.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/param.h> // MAXPATHLEN
|
||||
#else
|
||||
#if !defined(_WIN32) && !defined(__CELLOS_LV2__) && !defined(_XBOX)
|
||||
#include <sys/param.h> // PATH_MAX
|
||||
#elif defined(_WIN32) && !defined(_XBOX)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#elif defined(_XBOX)
|
||||
#define NOD3D
|
||||
#define NONET
|
||||
#include <xtl.h>
|
||||
#endif
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
#ifndef PATH_MAX
|
||||
#ifdef PATH_MAX
|
||||
#define MAXPATHLEN PATH_MAX
|
||||
#define PATH_MAX PATH_MAX
|
||||
#else
|
||||
#define MAXPATHLEN 512
|
||||
#define PATH_MAX 512
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -69,7 +75,7 @@ static config_file_t *config_file_new_internal(const char *path, unsigned depth)
|
||||
|
||||
static char *getaline(FILE *file)
|
||||
{
|
||||
char *newline = malloc(9);
|
||||
char *newline = (char*)malloc(9);
|
||||
size_t cur_size = 8;
|
||||
size_t index = 0;
|
||||
|
||||
@@ -79,7 +85,7 @@ static char *getaline(FILE *file)
|
||||
if (index == cur_size)
|
||||
{
|
||||
cur_size *= 2;
|
||||
newline = realloc(newline, cur_size + 1);
|
||||
newline = (char*)realloc(newline, cur_size + 1);
|
||||
}
|
||||
|
||||
newline[index++] = in;
|
||||
@@ -98,7 +104,7 @@ static char *extract_value(char *line, bool is_value)
|
||||
|
||||
// If we don't have an equal sign here, we've got an invalid string...
|
||||
if (*line != '=')
|
||||
return false;
|
||||
return NULL;
|
||||
|
||||
line++;
|
||||
}
|
||||
@@ -112,7 +118,7 @@ static char *extract_value(char *line, bool is_value)
|
||||
line++;
|
||||
char *tok = strtok(line, "\"");
|
||||
if (!tok)
|
||||
return false;
|
||||
return NULL;
|
||||
return strdup(tok);
|
||||
}
|
||||
else if (*line == '\0') // Nothing :(
|
||||
@@ -171,7 +177,7 @@ static void add_child_list(config_file_t *parent, config_file_t *child)
|
||||
static void add_include_list(config_file_t *conf, const char *path)
|
||||
{
|
||||
struct include_list *head = conf->includes;
|
||||
struct include_list *node = calloc(1, sizeof(*node));
|
||||
struct include_list *node = (struct include_list*)calloc(1, sizeof(*node));
|
||||
node->path = strdup(path);
|
||||
|
||||
if (head)
|
||||
@@ -193,19 +199,42 @@ static void add_sub_conf(config_file_t *conf, char *line)
|
||||
|
||||
add_include_list(conf, path);
|
||||
|
||||
char real_path[MAXPATHLEN];
|
||||
char real_path[PATH_MAX];
|
||||
|
||||
#ifndef _WIN32
|
||||
if (*path == '/')
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// Accomodate POSIX systems on Win32.
|
||||
bool is_full_path = *path == '/';
|
||||
|
||||
if (is_full_path)
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
else
|
||||
{
|
||||
// Workaround GetFullPathName not existing on XDK.
|
||||
// : is not a valid element in a path name. (NTFS streams?)
|
||||
if (strchr(path, ':') == NULL)
|
||||
{
|
||||
strlcpy(real_path, conf->path, sizeof(real_path));
|
||||
char *split = strrchr(real_path, '/');
|
||||
if (!split)
|
||||
split = strrchr(real_path, '\\');
|
||||
|
||||
split[1] = '\0';
|
||||
strlcat(real_path, path, sizeof(real_path));
|
||||
}
|
||||
else
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
}
|
||||
#else
|
||||
if (*path == '/')
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
#ifndef __CELLOS_LV2__
|
||||
else if (*path == '~')
|
||||
{
|
||||
const char *home = getenv("HOME");
|
||||
strlcpy(real_path, home ? home : "/", sizeof(real_path));
|
||||
strlcat(real_path, path + 1, sizeof(real_path));
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
strlcpy(real_path, conf->path, sizeof(real_path));
|
||||
@@ -218,25 +247,6 @@ static void add_sub_conf(config_file_t *conf, char *line)
|
||||
else
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
}
|
||||
#else
|
||||
// Accomodate POSIX systems on Win32.
|
||||
bool is_full_path = *path == '/';
|
||||
|
||||
if (is_full_path)
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
else
|
||||
GetFullPathNameA(path, sizeof(real_path), real_path, NULL);
|
||||
|
||||
if (strcmp(path, real_path) != 0)
|
||||
{
|
||||
strlcpy(real_path, conf->path, sizeof(real_path));
|
||||
char *split = strrchr(real_path, '/');
|
||||
if (!split)
|
||||
split = strrchr(real_path, '\\');
|
||||
|
||||
split[1] = '\0';
|
||||
strlcat(real_path, path, sizeof(real_path));
|
||||
}
|
||||
#endif
|
||||
|
||||
config_file_t *sub_conf = config_file_new_internal(real_path, conf->include_depth + 1);
|
||||
@@ -246,7 +256,7 @@ static void add_sub_conf(config_file_t *conf, char *line)
|
||||
return;
|
||||
}
|
||||
|
||||
// Pilfer internal list! :D
|
||||
// Pilfer internal list. :D
|
||||
add_child_list(conf, sub_conf);
|
||||
config_file_free(sub_conf);
|
||||
free(path);
|
||||
@@ -270,13 +280,13 @@ static bool parse_line(config_file_t *conf, struct entry_list *list, char *line)
|
||||
}
|
||||
}
|
||||
else if (conf->include_depth >= MAX_INCLUDE_DEPTH)
|
||||
fprintf(stderr, "!!! #include depth exceeded for config! Might be a cycle.\n");
|
||||
fprintf(stderr, "!!! #include depth exceeded for config. Might be a cycle.\n");
|
||||
|
||||
// Skips to first character.
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
char *key = malloc(9);
|
||||
char *key = (char*)malloc(9);
|
||||
size_t cur_size = 8;
|
||||
size_t index = 0;
|
||||
|
||||
@@ -285,7 +295,7 @@ static bool parse_line(config_file_t *conf, struct entry_list *list, char *line)
|
||||
if (index == cur_size)
|
||||
{
|
||||
cur_size *= 2;
|
||||
key = realloc(key, cur_size + 1);
|
||||
key = (char*)realloc(key, cur_size + 1);
|
||||
}
|
||||
|
||||
key[index++] = *line++;
|
||||
@@ -306,7 +316,7 @@ static bool parse_line(config_file_t *conf, struct entry_list *list, char *line)
|
||||
|
||||
static config_file_t *config_file_new_internal(const char *path, unsigned depth)
|
||||
{
|
||||
struct config_file *conf = calloc(1, sizeof(*conf));
|
||||
struct config_file *conf = (struct config_file*)calloc(1, sizeof(*conf));
|
||||
if (!conf)
|
||||
return NULL;
|
||||
|
||||
@@ -332,7 +342,7 @@ static config_file_t *config_file_new_internal(const char *path, unsigned depth)
|
||||
|
||||
while (!feof(file))
|
||||
{
|
||||
struct entry_list *list = calloc(1, sizeof(*list));
|
||||
struct entry_list *list = (struct entry_list*)calloc(1, sizeof(*list));
|
||||
char *line = getaline(file);
|
||||
|
||||
if (line)
|
||||
@@ -411,6 +421,27 @@ bool config_get_double(config_file_t *conf, const char *key, double *in)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_get_float(config_file_t *conf, const char *key, float *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
#ifdef __cplusplus
|
||||
// strtof() is C99/POSIX.
|
||||
*in = (float)strtod(list->value, NULL);
|
||||
#else
|
||||
*in = strtof(list->value, NULL);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_get_int(config_file_t *conf, const char *key, int *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
@@ -434,6 +465,47 @@ bool config_get_int(config_file_t *conf, const char *key, int *in)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_get_uint64(config_file_t *conf, const char *key, uint64_t *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
errno = 0;
|
||||
uint64_t val = strtoull(list->value, NULL, 0);
|
||||
if (errno == 0)
|
||||
{
|
||||
*in = val;
|
||||
return true;
|
||||
}
|
||||
return
|
||||
false;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool config_get_uint(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, 0);
|
||||
return true;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_get_hex(config_file_t *conf, const char *key, unsigned *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
@@ -550,7 +622,7 @@ void config_set_string(config_file_t *conf, const char *key, const char *val)
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
struct entry_list *elem = calloc(1, sizeof(*elem));
|
||||
struct entry_list *elem = (struct entry_list*)calloc(1, sizeof(*elem));
|
||||
elem->key = strdup(key);
|
||||
elem->value = strdup(val);
|
||||
|
||||
@@ -563,7 +635,18 @@ void config_set_string(config_file_t *conf, const char *key, const char *val)
|
||||
void config_set_double(config_file_t *conf, const char *key, double val)
|
||||
{
|
||||
char buf[128];
|
||||
#ifdef __cplusplus
|
||||
snprintf(buf, sizeof(buf), "%f", (float)val);
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), "%lf", val);
|
||||
#endif
|
||||
config_set_string(conf, key, buf);
|
||||
}
|
||||
|
||||
void config_set_float(config_file_t *conf, const char *key, float val)
|
||||
{
|
||||
char buf[128];
|
||||
snprintf(buf, sizeof(buf), "%f", val);
|
||||
config_set_string(conf, key, buf);
|
||||
}
|
||||
|
||||
@@ -574,6 +657,17 @@ void config_set_int(config_file_t *conf, const char *key, int val)
|
||||
config_set_string(conf, key, buf);
|
||||
}
|
||||
|
||||
void config_set_uint64(config_file_t *conf, const char *key, uint64_t val)
|
||||
{
|
||||
char buf[128];
|
||||
#ifdef _WIN32
|
||||
snprintf(buf, sizeof(buf), "%I64u", val);
|
||||
#else
|
||||
snprintf(buf, sizeof(buf), "%llu", (long long unsigned)val);
|
||||
#endif
|
||||
config_set_string(conf, key, buf);
|
||||
}
|
||||
|
||||
void config_set_char(config_file_t *conf, const char *key, char val)
|
||||
{
|
||||
char buf[2];
|
||||
|
||||
+11
-3
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -20,7 +20,7 @@
|
||||
#define __CONFIG_FILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "../boolean.h"
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -49,9 +49,15 @@ bool config_entry_exists(config_file_t *conf, const char *entry);
|
||||
|
||||
// Extracts a double from config file.
|
||||
bool config_get_double(config_file_t *conf, const char *entry, double *in);
|
||||
// Extracts a float from config file.
|
||||
bool config_get_float(config_file_t *conf, const char *entry, float *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)
|
||||
// Extracts an uint from config file.
|
||||
bool config_get_uint(config_file_t *conf, const char *entry, unsigned *in);
|
||||
// Extracts an uint64 from config file.
|
||||
bool config_get_uint64(config_file_t *conf, const char *entry, uint64_t *in);
|
||||
// Extracts an unsigned int from config file treating input as hex.
|
||||
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);
|
||||
@@ -65,7 +71,9 @@ bool config_get_bool(config_file_t *conf, const char *entry, bool *in);
|
||||
// Setters. Similar to the getters. Will not write to entry if the entry
|
||||
// was obtained from an #include.
|
||||
void config_set_double(config_file_t *conf, const char *entry, double value);
|
||||
void config_set_float(config_file_t *conf, const char *entry, float value);
|
||||
void config_set_int(config_file_t *conf, const char *entry, int val);
|
||||
void config_set_uint64(config_file_t *conf, const char *entry, uint64_t val);
|
||||
void config_set_char(config_file_t *conf, const char *entry, char val);
|
||||
void config_set_string(config_file_t *conf, const char *entry, const char *val);
|
||||
void config_set_bool(config_file_t *conf, const char *entry, bool val);
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_FILE_MACROS_H__
|
||||
#define CONFIG_FILE_MACROS_H__
|
||||
|
||||
// Macros to ease config getting.
|
||||
#include <stdint.h>
|
||||
|
||||
#define CONFIG_GET_BOOL_BASE(conf, base, var, key) do { \
|
||||
bool tmp = false; \
|
||||
if (config_get_bool(conf, key, &tmp)) \
|
||||
base.var = tmp; \
|
||||
} while(0)
|
||||
|
||||
#define CONFIG_GET_INT_BASE(conf, base, var, key) do { \
|
||||
int tmp = 0; \
|
||||
if (config_get_int(conf, key, &tmp)) \
|
||||
base.var = tmp; \
|
||||
} while(0)
|
||||
|
||||
#define CONFIG_GET_UINT64_BASE(conf, base, var, key) do { \
|
||||
uint64_t tmp = 0; \
|
||||
if (config_get_int(conf, key, &tmp)) \
|
||||
base.var = tmp; \
|
||||
} while(0)
|
||||
|
||||
#define CONFIG_GET_FLOAT_BASE(conf, base, var, key) do { \
|
||||
float tmp = 0.0f; \
|
||||
if (config_get_float(conf, key, &tmp)) \
|
||||
base.var = tmp; \
|
||||
} while(0)
|
||||
|
||||
#define CONFIG_GET_STRING_BASE(conf, base, var, key) \
|
||||
config_get_array(conf, key, base.var, sizeof(base.var))
|
||||
|
||||
#define CONFIG_GET_BOOL(var, key) CONFIG_GET_BOOL_BASE(conf, g_settings, var, key)
|
||||
#define CONFIG_GET_INT(var, key) CONFIG_GET_INT_BASE(conf, g_settings, var, key)
|
||||
#define CONFIG_GET_UINT64(var, key) CONFIG_GET_UINT64_BASE(conf, g_settings, var, key)
|
||||
#define CONFIG_GET_FLOAT(var, key) CONFIG_GET_FLOAT_BASE(conf, g_settings, var, key)
|
||||
#define CONFIG_GET_STRING(var, key) CONFIG_GET_STRING_BASE(conf, g_settings, var, key)
|
||||
|
||||
#define CONFIG_GET_BOOL_EXTERN(var, key) CONFIG_GET_BOOL_BASE(conf, g_extern, var, key)
|
||||
#define CONFIG_GET_INT_EXTERN(var, key) CONFIG_GET_INT_BASE(conf, g_extern, var, key)
|
||||
#define CONFIG_GET_FLOAT_EXTERN(var, key) CONFIG_GET_FLOAT_BASE(conf, g_extern, var, key)
|
||||
#define CONFIG_GET_STRING_EXTERN(var, key) CONFIG_GET_STRING_BASE(conf, g_extern, var, key)
|
||||
|
||||
#ifdef SSNES_CONSOLE
|
||||
#define CONFIG_GET_BOOL_CONSOLE(var, key) CONFIG_GET_BOOL_BASE(conf, g_console, var, key)
|
||||
#define CONFIG_GET_INT_CONSOLE(var, key) CONFIG_GET_INT_BASE(conf, g_console, var, key)
|
||||
#define CONFIG_GET_FLOAT_CONSOLE(var, key) CONFIG_GET_FLOAT_BASE(conf, g_console, var, key)
|
||||
#define CONFIG_GET_STRING_CONSOLE(var, key) CONFIG_GET_STRING_BASE(conf, g_console, var, key)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
+119
-57
@@ -1,5 +1,5 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -22,7 +22,7 @@
|
||||
#ifndef __CONFIG_DEF_H
|
||||
#define __CONFIG_DEF_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "boolean.h"
|
||||
#include "libsnes.hpp"
|
||||
#include "driver.h"
|
||||
|
||||
@@ -37,6 +37,9 @@
|
||||
#define VIDEO_XVIDEO 11
|
||||
#define VIDEO_SDL 13
|
||||
#define VIDEO_EXT 14
|
||||
#define VIDEO_WII 24
|
||||
#define VIDEO_XENON360 25
|
||||
#define VIDEO_XDK360 28
|
||||
////////////////////////
|
||||
#define AUDIO_RSOUND 1
|
||||
#define AUDIO_OSS 2
|
||||
@@ -50,13 +53,27 @@
|
||||
#define AUDIO_EXT 15
|
||||
#define AUDIO_DSOUND 16
|
||||
#define AUDIO_COREAUDIO 17
|
||||
#define AUDIO_PS3 18
|
||||
#define AUDIO_XENON360 20
|
||||
#define AUDIO_WII 22
|
||||
#define AUDIO_XDK360 27
|
||||
////////////////////////
|
||||
#define INPUT_SDL 7
|
||||
#define INPUT_X 12
|
||||
#define INPUT_PS3 19
|
||||
#define INPUT_XENON360 21
|
||||
#define INPUT_WII 23
|
||||
#define INPUT_XDK360 26
|
||||
////////////////////////
|
||||
|
||||
#if defined(HAVE_OPENGL)
|
||||
#if defined(HAVE_OPENGL) || defined(__CELLOS_LV2__)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_GL
|
||||
#elif defined(GEKKO)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_WII
|
||||
#elif defined(XENON)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_XENON360
|
||||
#elif defined(_XBOX)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_XDK360
|
||||
#elif defined(HAVE_XVIDEO)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_XVIDEO
|
||||
#elif defined(HAVE_SDL)
|
||||
@@ -64,10 +81,18 @@
|
||||
#elif defined(HAVE_DYLIB)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_EXT
|
||||
#else
|
||||
#error "Need at least one video driver!"
|
||||
#error "Need at least one video driver."
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_ALSA)
|
||||
#if defined(__CELLOS_LV2__)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_PS3
|
||||
#elif defined(XENON)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_XENON360
|
||||
#elif defined(_XBOX)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_XDK360
|
||||
#elif defined(GEKKO)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_WII
|
||||
#elif defined(HAVE_ALSA)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_ALSA
|
||||
#elif defined(HAVE_PULSE)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_PULSE
|
||||
@@ -92,15 +117,23 @@
|
||||
#elif defined(HAVE_DYLIB)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_EXT
|
||||
#else
|
||||
#error "Need at least one audio driver!"
|
||||
#error "Need at least one audio driver."
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SDL)
|
||||
#if defined(XENON)
|
||||
#define INPUT_DEFAULT_DRIVER INPUT_XENON360
|
||||
#elif defined(_XBOX)
|
||||
#define INPUT_DEFAULT_DRIVER INPUT_XDK360
|
||||
#elif defined(HAVE_SDL)
|
||||
#define INPUT_DEFAULT_DRIVER INPUT_SDL
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
#define INPUT_DEFAULT_DRIVER INPUT_PS3
|
||||
#elif defined(GEKKO)
|
||||
#define INPUT_DEFAULT_DRIVER INPUT_WII
|
||||
#elif defined(HAVE_XVIDEO)
|
||||
#define INPUT_DEFAULT_DRIVER INPUT_X
|
||||
#else
|
||||
#error "Need at least one input driver!"
|
||||
#error "Need at least one input driver."
|
||||
#endif
|
||||
|
||||
|
||||
@@ -137,6 +170,9 @@ static const bool crop_overscan = true;
|
||||
|
||||
// Font size for on-screen messages.
|
||||
static const unsigned font_size = 48;
|
||||
// Attempt to scale the font size.
|
||||
// The scale factor will be window_size / desktop_size.
|
||||
static const bool font_scale = true;
|
||||
|
||||
// Offset for where messages will be placed on-screen. Values are in range [0.0, 1.0].
|
||||
static const float message_pos_offset_x = 0.05;
|
||||
@@ -153,6 +189,9 @@ static const bool second_pass_smooth = true;
|
||||
// Record video assuming game runs hi-res.
|
||||
static const bool hires_record = false;
|
||||
|
||||
// Enables lossless RGB H.264 recording if possible (if not, FFV1 is used).
|
||||
static const bool h264_record = true;
|
||||
|
||||
// Record post-filtered (CPU filter) video rather than raw SNES output.
|
||||
static const bool post_filter_record = false;
|
||||
|
||||
@@ -167,7 +206,15 @@ static const bool font_enable = true;
|
||||
// This value should stay close to 60Hz to avoid large pitch changes.
|
||||
// If your monitor does not run at 60Hz, or something close to it, disable VSync,
|
||||
// and leave this at its default.
|
||||
#if defined(__CELLOS_LV2__) || defined(XENON) || defined(GEKKO)
|
||||
static const float refresh_rate = 59.92;
|
||||
#else
|
||||
static const float refresh_rate = 59.95;
|
||||
#endif
|
||||
|
||||
// Allow games to set rotation. If false, rotation requests are honored, but ignored.
|
||||
// Used for setups where one manually rotates the monitor.
|
||||
static const bool allow_rotate = true;
|
||||
|
||||
////////////////
|
||||
// Audio
|
||||
@@ -191,6 +238,16 @@ static const int out_latency = 64;
|
||||
// Will sync audio. (recommended)
|
||||
static const bool audio_sync = true;
|
||||
|
||||
// Experimental rate control
|
||||
static const bool rate_control = false;
|
||||
|
||||
// Rate control delta. Defines how much rate_control is allowed to adjust input rate.
|
||||
static const float rate_control_delta = 0.005;
|
||||
|
||||
//////////////
|
||||
// Misc
|
||||
//////////////
|
||||
|
||||
// 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;
|
||||
@@ -218,6 +275,9 @@ static const bool block_sram_overwrite = false;
|
||||
// When the ROM is loaded, state index will be set to the highest existing value.
|
||||
static const bool savestate_auto_index = false;
|
||||
|
||||
// Slowmotion ratio.
|
||||
static const float slowmotion_ratio = 3.0;
|
||||
|
||||
|
||||
////////////////////
|
||||
// Keybinds, Joypad
|
||||
@@ -232,59 +292,61 @@ static const float axis_threshold = 0.5;
|
||||
|
||||
// Player 1
|
||||
static const struct snes_keybind snes_keybinds_1[] = {
|
||||
// SNES button | keyboard key | js btn | js axis |
|
||||
{ SNES_DEVICE_ID_JOYPAD_A, SK_x, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_B, SK_z, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_X, SK_s, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_Y, SK_a, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_L, SK_q, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_R, SK_w, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_LEFT, SK_LEFT, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SK_RIGHT, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_UP, SK_UP, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_DOWN, SK_DOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_START, SK_RETURN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_SELECT, SK_RSHIFT, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FAST_FORWARD_KEY, SK_SPACE, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FAST_FORWARD_HOLD_KEY, SK_l, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SAVE_STATE_KEY, SK_F2, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_LOAD_STATE_KEY, SK_F4, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FULLSCREEN_TOGGLE_KEY, SK_f, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_QUIT_KEY, SK_ESCAPE, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_STATE_SLOT_MINUS, SK_F6, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_STATE_SLOT_PLUS, SK_F7, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_AUDIO_INPUT_RATE_PLUS, SK_KP_PLUS, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_AUDIO_INPUT_RATE_MINUS, SK_KP_MINUS, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_REWIND, SK_r, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_MOVIE_RECORD_TOGGLE, SK_o, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_PAUSE_TOGGLE, SK_p, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FRAMEADVANCE, SK_k, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_RESET, SK_h, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SHADER_NEXT, SK_m, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SHADER_PREV, SK_n, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_CHEAT_INDEX_PLUS, SK_y, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_CHEAT_INDEX_MINUS, SK_t, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_CHEAT_TOGGLE, SK_u, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_SCREENSHOT, SK_PRINT, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_DSP_CONFIG, SK_c, NO_BTN, AXIS_NONE },
|
||||
{ -1 }
|
||||
// SNES button | keyboard key | js btn | js axis |
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_B, SK_z, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_Y, SK_a, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_SELECT, SK_RSHIFT, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_START, SK_RETURN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_UP, SK_UP, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_DOWN, SK_DOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_LEFT, SK_LEFT, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_RIGHT, SK_RIGHT, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_A, SK_x, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_X, SK_s, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_L, SK_q, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_R, SK_w, NO_BTN, AXIS_NONE },
|
||||
|
||||
{ true, SSNES_FAST_FORWARD_KEY, SK_SPACE, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_FAST_FORWARD_HOLD_KEY, SK_l, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_LOAD_STATE_KEY, SK_F4, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_SAVE_STATE_KEY, SK_F2, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_FULLSCREEN_TOGGLE_KEY, SK_f, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_QUIT_KEY, SK_ESCAPE, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_STATE_SLOT_PLUS, SK_F7, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_STATE_SLOT_MINUS, SK_F6, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_AUDIO_INPUT_RATE_PLUS, SK_KP_PLUS, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_AUDIO_INPUT_RATE_MINUS, SK_KP_MINUS, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_REWIND, SK_r, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_MOVIE_RECORD_TOGGLE, SK_o, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_PAUSE_TOGGLE, SK_p, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_FRAMEADVANCE, SK_k, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_RESET, SK_h, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_SHADER_NEXT, SK_m, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_SHADER_PREV, SK_n, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_CHEAT_INDEX_PLUS, SK_y, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_CHEAT_INDEX_MINUS, SK_t, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_CHEAT_TOGGLE, SK_u, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_SCREENSHOT, SK_PRINT, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_DSP_CONFIG, SK_c, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_MUTE, SK_F9, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_NETPLAY_FLIP, SK_i, NO_BTN, AXIS_NONE },
|
||||
{ true, SSNES_SLOWMOTION, SK_e, NO_BTN, AXIS_NONE },
|
||||
};
|
||||
|
||||
// Player 2-5
|
||||
static const struct snes_keybind snes_keybinds_rest[] = {
|
||||
{ SNES_DEVICE_ID_JOYPAD_A, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_B, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_X, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_Y, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_L, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_R, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_LEFT, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_UP, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_DOWN, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_START, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_SELECT, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ -1 }
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_B, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_Y, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_SELECT, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_START, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_UP, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_DOWN, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_LEFT, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_RIGHT, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_A, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_X, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_L, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
{ true, SNES_DEVICE_ID_JOYPAD_R, SK_UNKNOWN, NO_BTN, AXIS_NONE },
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+13
-1
@@ -2,7 +2,7 @@
|
||||
#define __SSNES_FEATURES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
#include "boolean.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
@@ -14,6 +14,12 @@ static const bool _sdl_supp = true;
|
||||
static const bool _sdl_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
static const bool _thread_supp = true;
|
||||
#else
|
||||
static const bool _thread_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
static const bool _opengl_supp = true;
|
||||
#else
|
||||
@@ -68,6 +74,12 @@ static const bool _pulse_supp = true;
|
||||
static const bool _pulse_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DSOUND
|
||||
static const bool _dsound_supp = true;
|
||||
#else
|
||||
static const bool _dsound_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XAUDIO
|
||||
static const bool _xaudio_supp = true;
|
||||
#else
|
||||
|
||||
@@ -0,0 +1,487 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "../boolean.h"
|
||||
#include "../compat/strl.h"
|
||||
#include "../libsnes.hpp"
|
||||
#include "../general.h"
|
||||
#include "../compat/strl.h"
|
||||
#include "main_wrap.h"
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
#include "szlib/zlib.h"
|
||||
#define WRITEBUFFERSIZE (1024 * 512)
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "../compat/posix_string.h"
|
||||
#endif
|
||||
|
||||
#define MAX_ARGS 32
|
||||
|
||||
/*============================================================
|
||||
ROM EXTENSIONS
|
||||
============================================================ */
|
||||
|
||||
static char g_rom_ext[1024];
|
||||
|
||||
void ssnes_console_set_rom_ext(const char *ext)
|
||||
{
|
||||
strlcpy(g_rom_ext, ext, sizeof(g_rom_ext));
|
||||
}
|
||||
|
||||
const char *ssnes_console_get_rom_ext(void)
|
||||
{
|
||||
const char *retval = NULL;
|
||||
|
||||
if (*g_rom_ext)
|
||||
retval = g_rom_ext;
|
||||
else
|
||||
retval = "ZIP|zip";
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void ssnes_console_name_from_id(char *name, size_t size)
|
||||
{
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
const char *id = snes_library_id();
|
||||
if (!id || strlen(id) >= size)
|
||||
{
|
||||
name[0] = '\0';
|
||||
return;
|
||||
}
|
||||
|
||||
name[strlen(id)] = '\0';
|
||||
|
||||
for (size_t i = 0; id[i] != '\0'; i++)
|
||||
{
|
||||
char c = id[i];
|
||||
if (isspace(c) || isblank(c))
|
||||
name[i] = '_';
|
||||
else
|
||||
name[i] = tolower(c);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
static int ssnes_extract_currentfile_in_zip(unzFile uf)
|
||||
{
|
||||
char filename_inzip[PATH_MAX];
|
||||
FILE *fout = NULL;
|
||||
|
||||
unz_file_info file_info;
|
||||
int err = unzGetCurrentFileInfo(uf,
|
||||
&file_info, filename_inzip, sizeof(filename_inzip),
|
||||
NULL, 0, NULL, 0);
|
||||
|
||||
if (err != UNZ_OK)
|
||||
{
|
||||
SSNES_ERR("Error %d with zipfile in unzGetCurrentFileInfo.\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
size_t size_buf = WRITEBUFFERSIZE;
|
||||
void *buf = malloc(size_buf);
|
||||
if (!buf)
|
||||
{
|
||||
SSNES_ERR("Error allocating memory\n");
|
||||
return UNZ_INTERNALERROR;
|
||||
}
|
||||
|
||||
char write_filename[PATH_MAX];
|
||||
|
||||
#if defined(__CELLOS_LV2__)
|
||||
snprintf(write_filename, sizeof(write_filename), "/dev_hdd1/%s", filename_inzip);
|
||||
#elif defined(_XBOX)
|
||||
snprintf(write_filename, sizeof(write_filename), "cache:\\%s", filename_inzip);
|
||||
#endif
|
||||
|
||||
err = unzOpenCurrentFile(uf);
|
||||
if (err != UNZ_OK)
|
||||
SSNES_ERR("Error %d with zipfile in unzOpenCurrentFile.\n", err);
|
||||
else
|
||||
{
|
||||
/* success */
|
||||
fout = fopen(write_filename, "wb");
|
||||
|
||||
if (!fout)
|
||||
SSNES_ERR("Error opening %s.\n", write_filename);
|
||||
}
|
||||
|
||||
if (fout)
|
||||
{
|
||||
SSNES_LOG("Extracting: %s\n", write_filename);
|
||||
|
||||
do
|
||||
{
|
||||
err = unzReadCurrentFile(uf, buf, size_buf);
|
||||
if (err < 0)
|
||||
{
|
||||
SSNES_ERR("error %d with zipfile in unzReadCurrentFile.\n", err);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err > 0)
|
||||
{
|
||||
if (fwrite(buf, err, 1, fout) != 1)
|
||||
{
|
||||
SSNES_ERR("Error in writing extracted file.\n");
|
||||
err = UNZ_ERRNO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (err > 0);
|
||||
|
||||
if (fout)
|
||||
fclose(fout);
|
||||
}
|
||||
|
||||
if (err == UNZ_OK)
|
||||
{
|
||||
err = unzCloseCurrentFile (uf);
|
||||
if (err != UNZ_OK)
|
||||
SSNES_ERR("Error %d with zipfile in unzCloseCurrentFile.\n", err);
|
||||
}
|
||||
else
|
||||
unzCloseCurrentFile(uf);
|
||||
|
||||
free(buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ssnes_extract_zipfile(const char *zip_path)
|
||||
{
|
||||
unzFile uf = unzOpen(zip_path);
|
||||
|
||||
unz_global_info gi;
|
||||
int err = unzGetGlobalInfo(uf, &gi);
|
||||
if (err != UNZ_OK)
|
||||
SSNES_ERR("error %d with zipfile in unzGetGlobalInfo \n",err);
|
||||
|
||||
for (unsigned i = 0; i < gi.number_entry; i++)
|
||||
{
|
||||
if (ssnes_extract_currentfile_in_zip(uf) != UNZ_OK)
|
||||
break;
|
||||
|
||||
if ((i + 1) < gi.number_entry)
|
||||
{
|
||||
err = unzGoToNextFile(uf);
|
||||
if (err != UNZ_OK)
|
||||
{
|
||||
SSNES_ERR("error %d with zipfile in unzGoToNextFile\n",err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int ssnes_main_init_wrap(const struct ssnes_main_wrap *args)
|
||||
{
|
||||
int argc = 0;
|
||||
char *argv[MAX_ARGS] = {NULL};
|
||||
|
||||
argv[argc++] = strdup("ssnes");
|
||||
|
||||
if (args->rom_path)
|
||||
argv[argc++] = strdup(args->rom_path);
|
||||
|
||||
if (args->sram_path)
|
||||
{
|
||||
argv[argc++] = strdup("-s");
|
||||
argv[argc++] = strdup(args->sram_path);
|
||||
}
|
||||
|
||||
if (args->state_path)
|
||||
{
|
||||
argv[argc++] = strdup("-S");
|
||||
argv[argc++] = strdup(args->state_path);
|
||||
}
|
||||
|
||||
if (args->config_path)
|
||||
{
|
||||
argv[argc++] = strdup("-c");
|
||||
argv[argc++] = strdup(args->config_path);
|
||||
}
|
||||
|
||||
if (args->verbose)
|
||||
argv[argc++] = strdup("-v");
|
||||
|
||||
int ret = ssnes_main_init(argc, argv);
|
||||
|
||||
char **tmp = argv;
|
||||
while (*tmp)
|
||||
{
|
||||
free(*tmp);
|
||||
tmp++;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*============================================================
|
||||
INPUT EXTENSIONS
|
||||
============================================================ */
|
||||
|
||||
#include "console_ext_input.h"
|
||||
|
||||
struct platform_bind
|
||||
{
|
||||
uint64_t joykey;
|
||||
const char *label;
|
||||
};
|
||||
|
||||
uint64_t ssnes_default_keybind_lut[SSNES_FIRST_META_KEY];
|
||||
|
||||
char ssnes_default_libsnes_keybind_name_lut[SSNES_FIRST_META_KEY][256] = {
|
||||
"B Button", /* SNES_DEVICE_ID_JOYPAD_B */
|
||||
"Y Button", /* SNES_DEVICE_ID_JOYPAD_Y */
|
||||
"Select button", /* SNES_DEVICE_ID_JOYPAD_SELECT */
|
||||
"Start button", /* SNES_DEVICE_ID_JOYPAD_START */
|
||||
"D-Pad Up", /* SNES_DEVICE_ID_JOYPAD_UP */
|
||||
"D-Pad Down", /* SNES_DEVICE_ID_JOYPAD_DOWN */
|
||||
"D-Pad Left", /* SNES_DEVICE_ID_JOYPAD_LEFT */
|
||||
"D-Pad Right", /* SNES_DEVICE_ID_JOYPAD_RIGHT */
|
||||
"A Button", /* SNES_DEVICE_ID_JOYPAD_A */
|
||||
"X Button", /* SNES_DEVICE_ID_JOYPAD_X */
|
||||
"L Button", /* SNES_DEVICE_ID_JOYPAD_L */
|
||||
"R Button", /* SNES_DEVICE_ID_JOYPAD_R */
|
||||
};
|
||||
|
||||
#if defined(__CELLOS_LV2__)
|
||||
uint64_t ssnes_platform_keybind_lut[SSNES_LAST_PLATFORM_KEY] = {
|
||||
CTRL_CIRCLE_MASK,
|
||||
CTRL_CROSS_MASK,
|
||||
CTRL_TRIANGLE_MASK,
|
||||
CTRL_SQUARE_MASK,
|
||||
CTRL_UP_MASK,
|
||||
CTRL_DOWN_MASK,
|
||||
CTRL_LEFT_MASK,
|
||||
CTRL_RIGHT_MASK,
|
||||
CTRL_SELECT_MASK,
|
||||
CTRL_START_MASK,
|
||||
CTRL_L1_MASK,
|
||||
CTRL_L2_MASK,
|
||||
CTRL_L3_MASK,
|
||||
CTRL_R1_MASK,
|
||||
CTRL_R2_MASK,
|
||||
CTRL_R3_MASK,
|
||||
CTRL_LSTICK_LEFT_MASK,
|
||||
CTRL_LSTICK_RIGHT_MASK,
|
||||
CTRL_LSTICK_UP_MASK,
|
||||
CTRL_LSTICK_DOWN_MASK,
|
||||
CTRL_LEFT_MASK | CTRL_LSTICK_LEFT_MASK,
|
||||
CTRL_RIGHT_MASK | CTRL_LSTICK_RIGHT_MASK,
|
||||
CTRL_UP_MASK | CTRL_LSTICK_UP_MASK,
|
||||
CTRL_DOWN_MASK | CTRL_LSTICK_DOWN_MASK,
|
||||
CTRL_RSTICK_LEFT_MASK,
|
||||
CTRL_RSTICK_RIGHT_MASK,
|
||||
CTRL_RSTICK_UP_MASK,
|
||||
CTRL_RSTICK_DOWN_MASK,
|
||||
CTRL_LEFT_MASK | CTRL_RSTICK_LEFT_MASK,
|
||||
CTRL_RIGHT_MASK | CTRL_RSTICK_RIGHT_MASK,
|
||||
CTRL_UP_MASK | CTRL_RSTICK_UP_MASK,
|
||||
CTRL_DOWN_MASK | CTRL_RSTICK_DOWN_MASK,
|
||||
};
|
||||
|
||||
static const struct platform_bind platform_keys[] = {
|
||||
{ CTRL_CIRCLE_MASK, "Circle button" },
|
||||
{ CTRL_CROSS_MASK, "Cross button" },
|
||||
{ CTRL_TRIANGLE_MASK, "Triangle button" },
|
||||
{ CTRL_SQUARE_MASK, "Square button" },
|
||||
{ CTRL_UP_MASK, "D-Pad Up" },
|
||||
{ CTRL_DOWN_MASK, "D-Pad Down" },
|
||||
{ CTRL_LEFT_MASK, "D-Pad Left" },
|
||||
{ CTRL_RIGHT_MASK, "D-Pad Right" },
|
||||
{ CTRL_SELECT_MASK, "Select button" },
|
||||
{ CTRL_START_MASK, "Start button" },
|
||||
{ CTRL_L1_MASK, "L1 button" },
|
||||
{ CTRL_L2_MASK, "L2 button" },
|
||||
{ CTRL_L3_MASK, "L3 button" },
|
||||
{ CTRL_R1_MASK, "R1 button" },
|
||||
{ CTRL_R2_MASK, "R2 button" },
|
||||
{ CTRL_R3_MASK, "R3 button" },
|
||||
{ CTRL_LSTICK_LEFT_MASK, "LStick Left" },
|
||||
{ CTRL_LSTICK_RIGHT_MASK, "LStick Right" },
|
||||
{ CTRL_LSTICK_UP_MASK, "LStick Up" },
|
||||
{ CTRL_LSTICK_DOWN_MASK, "LStick Down" },
|
||||
{ CTRL_LEFT_MASK | CTRL_LSTICK_LEFT_MASK, "LStick D-Pad Left" },
|
||||
{ CTRL_RIGHT_MASK | CTRL_LSTICK_RIGHT_MASK, "LStick D-Pad Right" },
|
||||
{ CTRL_UP_MASK | CTRL_LSTICK_UP_MASK, "LStick D-Pad Up" },
|
||||
{ CTRL_DOWN_MASK | CTRL_LSTICK_DOWN_MASK, "LStick D-Pad Down" },
|
||||
{ CTRL_RSTICK_LEFT_MASK, "RStick Left" },
|
||||
{ CTRL_RSTICK_RIGHT_MASK, "RStick Right" },
|
||||
{ CTRL_RSTICK_UP_MASK, "RStick Up" },
|
||||
{ CTRL_RSTICK_DOWN_MASK, "RStick Down" },
|
||||
{ CTRL_LEFT_MASK | CTRL_RSTICK_LEFT_MASK, "RStick D-Pad Left" },
|
||||
{ CTRL_RIGHT_MASK | CTRL_RSTICK_RIGHT_MASK, "RStick D-Pad Right" },
|
||||
{ CTRL_UP_MASK | CTRL_RSTICK_UP_MASK, "RStick D-Pad Up" },
|
||||
{ CTRL_DOWN_MASK | CTRL_RSTICK_DOWN_MASK, "RStick D-Pad Down" },
|
||||
};
|
||||
#elif defined(_XBOX)
|
||||
uint64_t ssnes_platform_keybind_lut[SSNES_LAST_PLATFORM_KEY] = {
|
||||
XINPUT_GAMEPAD_B,
|
||||
XINPUT_GAMEPAD_A,
|
||||
XINPUT_GAMEPAD_Y,
|
||||
XINPUT_GAMEPAD_X,
|
||||
XINPUT_GAMEPAD_DPAD_UP,
|
||||
XINPUT_GAMEPAD_DPAD_DOWN,
|
||||
XINPUT_GAMEPAD_DPAD_LEFT,
|
||||
XINPUT_GAMEPAD_DPAD_RIGHT,
|
||||
XINPUT_GAMEPAD_BACK,
|
||||
XINPUT_GAMEPAD_START,
|
||||
XINPUT_GAMEPAD_LEFT_SHOULDER,
|
||||
XINPUT_GAMEPAD_LEFT_TRIGGER,
|
||||
XINPUT_GAMEPAD_LEFT_THUMB,
|
||||
XINPUT_GAMEPAD_RIGHT_SHOULDER,
|
||||
XINPUT_GAMEPAD_RIGHT_TRIGGER,
|
||||
XINPUT_GAMEPAD_RIGHT_THUMB,
|
||||
XINPUT_GAMEPAD_LSTICK_LEFT_MASK,
|
||||
XINPUT_GAMEPAD_LSTICK_RIGHT_MASK,
|
||||
XINPUT_GAMEPAD_LSTICK_UP_MASK,
|
||||
XINPUT_GAMEPAD_LSTICK_DOWN_MASK,
|
||||
XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_LSTICK_LEFT_MASK,
|
||||
XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_LSTICK_RIGHT_MASK,
|
||||
XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_LSTICK_UP_MASK,
|
||||
XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_LSTICK_DOWN_MASK,
|
||||
XINPUT_GAMEPAD_RSTICK_LEFT_MASK,
|
||||
XINPUT_GAMEPAD_RSTICK_RIGHT_MASK,
|
||||
XINPUT_GAMEPAD_RSTICK_UP_MASK,
|
||||
XINPUT_GAMEPAD_RSTICK_DOWN_MASK,
|
||||
XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_RSTICK_LEFT_MASK,
|
||||
XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_RSTICK_RIGHT_MASK,
|
||||
XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_RSTICK_UP_MASK,
|
||||
XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_RSTICK_DOWN_MASK,
|
||||
};
|
||||
|
||||
static const struct platform_bind platform_keys[] = {
|
||||
{ XINPUT_GAMEPAD_B, "B button" },
|
||||
{ XINPUT_GAMEPAD_A, "A button" },
|
||||
{ XINPUT_GAMEPAD_Y, "Y button" },
|
||||
{ XINPUT_GAMEPAD_X, "X button" },
|
||||
{ XINPUT_GAMEPAD_DPAD_UP, "D-Pad Up" },
|
||||
{ XINPUT_GAMEPAD_DPAD_DOWN, "D-Pad Down" },
|
||||
{ XINPUT_GAMEPAD_DPAD_LEFT, "D-Pad Left" },
|
||||
{ XINPUT_GAMEPAD_DPAD_RIGHT, "D-Pad Right" },
|
||||
{ XINPUT_GAMEPAD_BACK, "Back button" },
|
||||
{ XINPUT_GAMEPAD_START, "Start button" },
|
||||
{ XINPUT_GAMEPAD_LEFT_SHOULDER, "Left Shoulder" },
|
||||
{ XINPUT_GAMEPAD_LEFT_TRIGGER, "Left Trigger" },
|
||||
{ XINPUT_GAMEPAD_LEFT_THUMB, "Left Thumb" },
|
||||
{ XINPUT_GAMEPAD_RIGHT_SHOULDER, "Right Shoulder" },
|
||||
{ XINPUT_GAMEPAD_RIGHT_TRIGGER, "Right Trigger" },
|
||||
{ XINPUT_GAMEPAD_RIGHT_THUMB, "Right Thumb" },
|
||||
{ XINPUT_GAMEPAD_LSTICK_LEFT_MASK, "LStick Left" },
|
||||
{ XINPUT_GAMEPAD_LSTICK_RIGHT_MASK, "LStick Right" },
|
||||
{ XINPUT_GAMEPAD_LSTICK_UP_MASK, "LStick Up" },
|
||||
{ XINPUT_GAMEPAD_LSTICK_DOWN_MASK, "LStick Down" },
|
||||
{ XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_LSTICK_LEFT_MASK, "LStick D-Pad Left" },
|
||||
{ XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_LSTICK_RIGHT_MASK, "LStick D-Pad Right" },
|
||||
{ XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_LSTICK_UP_MASK, "LStick D-Pad Up" },
|
||||
{ XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_LSTICK_DOWN_MASK, "LStick D-Pad Down" },
|
||||
{ XINPUT_GAMEPAD_RSTICK_LEFT_MASK, "RStick Left" },
|
||||
{ XINPUT_GAMEPAD_RSTICK_RIGHT_MASK, "RStick Right" },
|
||||
{ XINPUT_GAMEPAD_RSTICK_UP_MASK, "RStick Up" },
|
||||
{ XINPUT_GAMEPAD_RSTICK_DOWN_MASK, "RStick Down" },
|
||||
{ XINPUT_GAMEPAD_DPAD_LEFT | XINPUT_GAMEPAD_RSTICK_LEFT_MASK, "RStick D-Pad Left" },
|
||||
{ XINPUT_GAMEPAD_DPAD_RIGHT | XINPUT_GAMEPAD_RSTICK_RIGHT_MASK, "RStick D-Pad Right" },
|
||||
{ XINPUT_GAMEPAD_DPAD_UP | XINPUT_GAMEPAD_RSTICK_UP_MASK, "RStick D-Pad Up" },
|
||||
{ XINPUT_GAMEPAD_DPAD_DOWN | XINPUT_GAMEPAD_RSTICK_DOWN_MASK, "RStick D-Pad Down" },
|
||||
};
|
||||
#endif
|
||||
|
||||
uint64_t ssnes_input_find_previous_platform_key(uint64_t joykey)
|
||||
{
|
||||
size_t arr_size = sizeof(platform_keys) / sizeof(platform_keys[0]);
|
||||
|
||||
if (platform_keys[0].joykey == joykey)
|
||||
return joykey;
|
||||
|
||||
for (size_t i = 1; i < arr_size; i++)
|
||||
{
|
||||
if (platform_keys[i].joykey == joykey)
|
||||
return platform_keys[i - 1].joykey;
|
||||
}
|
||||
|
||||
return NO_BTN;
|
||||
}
|
||||
|
||||
uint64_t ssnes_input_find_next_platform_key(uint64_t joykey)
|
||||
{
|
||||
size_t arr_size = sizeof(platform_keys) / sizeof(platform_keys[0]);
|
||||
if (platform_keys[arr_size - 1].joykey == joykey)
|
||||
return joykey;
|
||||
|
||||
for (size_t i = 0; i < arr_size - 1; i++)
|
||||
{
|
||||
if (platform_keys[i].joykey == joykey)
|
||||
return platform_keys[i + 1].joykey;
|
||||
}
|
||||
|
||||
return NO_BTN;
|
||||
}
|
||||
|
||||
const char *ssnes_input_find_platform_key_label(uint64_t joykey)
|
||||
{
|
||||
if (joykey == NO_BTN)
|
||||
return "No button";
|
||||
|
||||
size_t arr_size = sizeof(platform_keys) / sizeof(platform_keys[0]);
|
||||
for (size_t i = 0; i < arr_size; i++)
|
||||
{
|
||||
if (platform_keys[i].joykey == joykey)
|
||||
return platform_keys[i].label;
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
void ssnes_input_set_default_keybind_names_for_emulator(void)
|
||||
{
|
||||
const char *id = snes_library_id();
|
||||
|
||||
// Genesis Plus GX/Next
|
||||
if (strstr(id, "Genesis Plus GX"))
|
||||
{
|
||||
strlcpy(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_B],
|
||||
"B button", sizeof(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_B]));
|
||||
strlcpy(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_A],
|
||||
"C button", sizeof(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_A]));
|
||||
strlcpy(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_X],
|
||||
"Y button", sizeof(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_X]));
|
||||
strlcpy(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_Y],
|
||||
"A button", sizeof(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_Y]));
|
||||
strlcpy(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_L],
|
||||
"X button", sizeof(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_L]));
|
||||
strlcpy(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_R],
|
||||
"Z button", sizeof(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_R]));
|
||||
strlcpy(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_SELECT],
|
||||
"Mode button", sizeof(ssnes_default_libsnes_keybind_name_lut[SNES_DEVICE_ID_JOYPAD_SELECT]));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ROM_EXT_H__
|
||||
#define ROM_EXT_H__
|
||||
|
||||
#include "console_ext_input.h"
|
||||
|
||||
/*============================================================
|
||||
ROM EXTENSIONS
|
||||
============================================================ */
|
||||
|
||||
void ssnes_console_set_rom_ext(const char *ext);
|
||||
|
||||
// Get rom extensions for current library.
|
||||
// Returns NULL if library doesn't have any preferences in particular.
|
||||
const char *ssnes_console_get_rom_ext(void);
|
||||
|
||||
// Transforms a library id to a name suitable as a pathname.
|
||||
void ssnes_console_name_from_id(char *name, size_t size);
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
int ssnes_extract_zipfile(const char *zip_path);
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
INPUT EXTENSIONS
|
||||
============================================================ */
|
||||
|
||||
const char *ssnes_input_find_platform_key_label(uint64_t joykey);
|
||||
uint64_t ssnes_input_find_previous_platform_key(uint64_t joykey);
|
||||
uint64_t ssnes_input_find_next_platform_key(uint64_t joykey);
|
||||
|
||||
// Sets custom default keybind names (some systems emulated by the emulator
|
||||
// will need different keybind names for buttons, etc.)
|
||||
void ssnes_input_set_default_keybind_names_for_emulator(void);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef ROM_EXT_INPUT_H__
|
||||
#define ROM_EXT_INPUT_H__
|
||||
|
||||
#include "../driver.h"
|
||||
|
||||
#if defined(__CELLOS_LV2__)
|
||||
#include "../ps3/ps3_input.h"
|
||||
enum ps3_device_id
|
||||
{
|
||||
PS3_DEVICE_ID_JOYPAD_CIRCLE = 0,
|
||||
PS3_DEVICE_ID_JOYPAD_CROSS,
|
||||
PS3_DEVICE_ID_JOYPAD_TRIANGLE,
|
||||
PS3_DEVICE_ID_JOYPAD_SQUARE,
|
||||
PS3_DEVICE_ID_JOYPAD_UP,
|
||||
PS3_DEVICE_ID_JOYPAD_DOWN,
|
||||
PS3_DEVICE_ID_JOYPAD_LEFT,
|
||||
PS3_DEVICE_ID_JOYPAD_RIGHT,
|
||||
PS3_DEVICE_ID_JOYPAD_SELECT,
|
||||
PS3_DEVICE_ID_JOYPAD_START,
|
||||
PS3_DEVICE_ID_JOYPAD_L1,
|
||||
PS3_DEVICE_ID_JOYPAD_L2,
|
||||
PS3_DEVICE_ID_JOYPAD_L3,
|
||||
PS3_DEVICE_ID_JOYPAD_R1,
|
||||
PS3_DEVICE_ID_JOYPAD_R2,
|
||||
PS3_DEVICE_ID_JOYPAD_R3,
|
||||
PS3_DEVICE_ID_LSTICK_LEFT,
|
||||
PS3_DEVICE_ID_LSTICK_RIGHT,
|
||||
PS3_DEVICE_ID_LSTICK_UP,
|
||||
PS3_DEVICE_ID_LSTICK_DOWN,
|
||||
PS3_DEVICE_ID_LSTICK_LEFT_DPAD,
|
||||
PS3_DEVICE_ID_LSTICK_RIGHT_DPAD,
|
||||
PS3_DEVICE_ID_LSTICK_UP_DPAD,
|
||||
PS3_DEVICE_ID_LSTICK_DOWN_DPAD,
|
||||
PS3_DEVICE_ID_RSTICK_LEFT,
|
||||
PS3_DEVICE_ID_RSTICK_RIGHT,
|
||||
PS3_DEVICE_ID_RSTICK_UP,
|
||||
PS3_DEVICE_ID_RSTICK_DOWN,
|
||||
PS3_DEVICE_ID_RSTICK_LEFT_DPAD,
|
||||
PS3_DEVICE_ID_RSTICK_RIGHT_DPAD,
|
||||
PS3_DEVICE_ID_RSTICK_UP_DPAD,
|
||||
PS3_DEVICE_ID_RSTICK_DOWN_DPAD,
|
||||
|
||||
SSNES_LAST_PLATFORM_KEY
|
||||
};
|
||||
|
||||
#elif defined(_XBOX)
|
||||
#include "../360/xdk360_input.h"
|
||||
|
||||
enum xdk360_device_id
|
||||
{
|
||||
XDK360_DEVICE_ID_JOYPAD_B = 0,
|
||||
XDK360_DEVICE_ID_JOYPAD_A,
|
||||
XDK360_DEVICE_ID_JOYPAD_Y,
|
||||
XDK360_DEVICE_ID_JOYPAD_X,
|
||||
XDK360_DEVICE_ID_JOYPAD_UP,
|
||||
XDK360_DEVICE_ID_JOYPAD_DOWN,
|
||||
XDK360_DEVICE_ID_JOYPAD_LEFT,
|
||||
XDK360_DEVICE_ID_JOYPAD_RIGHT,
|
||||
XDK360_DEVICE_ID_JOYPAD_BACK,
|
||||
XDK360_DEVICE_ID_JOYPAD_START,
|
||||
XDK360_DEVICE_ID_JOYPAD_LB,
|
||||
XDK360_DEVICE_ID_JOYPAD_LEFT_TRIGGER,
|
||||
XDK360_DEVICE_ID_LSTICK_THUMB,
|
||||
XDK360_DEVICE_ID_JOYPAD_RB,
|
||||
XDK360_DEVICE_ID_JOYPAD_RIGHT_TRIGGER,
|
||||
XDK360_DEVICE_ID_RSTICK_THUMB,
|
||||
XDK360_DEVICE_ID_LSTICK_LEFT,
|
||||
XDK360_DEVICE_ID_LSTICK_RIGHT,
|
||||
XDK360_DEVICE_ID_LSTICK_UP,
|
||||
XDK360_DEVICE_ID_LSTICK_DOWN,
|
||||
XDK360_DEVICE_ID_LSTICK_LEFT_DPAD,
|
||||
XDK360_DEVICE_ID_LSTICK_RIGHT_DPAD,
|
||||
XDK360_DEVICE_ID_LSTICK_UP_DPAD,
|
||||
XDK360_DEVICE_ID_LSTICK_DOWN_DPAD,
|
||||
XDK360_DEVICE_ID_RSTICK_LEFT,
|
||||
XDK360_DEVICE_ID_RSTICK_RIGHT,
|
||||
XDK360_DEVICE_ID_RSTICK_UP,
|
||||
XDK360_DEVICE_ID_RSTICK_DOWN,
|
||||
XDK360_DEVICE_ID_RSTICK_LEFT_DPAD,
|
||||
XDK360_DEVICE_ID_RSTICK_RIGHT_DPAD,
|
||||
XDK360_DEVICE_ID_RSTICK_UP_DPAD,
|
||||
XDK360_DEVICE_ID_RSTICK_DOWN_DPAD,
|
||||
|
||||
SSNES_LAST_PLATFORM_KEY
|
||||
};
|
||||
#endif
|
||||
|
||||
extern uint64_t ssnes_default_keybind_lut[SSNES_FIRST_META_KEY];
|
||||
extern uint64_t ssnes_platform_keybind_lut[SSNES_LAST_PLATFORM_KEY];
|
||||
extern char ssnes_default_libsnes_keybind_name_lut[SSNES_FIRST_META_KEY][256];
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,252 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef _XBOX
|
||||
#include <xtl.h>
|
||||
#endif
|
||||
#include "file_browser.h"
|
||||
|
||||
static int less_than_key(const void * a, const void * b)
|
||||
{
|
||||
#ifdef __CELLOS_LV2__
|
||||
DirectoryEntry * a_dir = (DirectoryEntry*)a;
|
||||
DirectoryEntry * b_dir = (DirectoryEntry*)b;
|
||||
|
||||
/* compare a directory to a file directory is always lesser than*/
|
||||
if ((a_dir->d_type == CELL_FS_TYPE_DIRECTORY && b_dir->d_type == CELL_FS_TYPE_REGULAR))
|
||||
return -1;
|
||||
else if (a_dir->d_type == CELL_FS_TYPE_REGULAR && b_dir->d_type == CELL_FS_TYPE_DIRECTORY)
|
||||
return 1;
|
||||
|
||||
return strcasecmp(a_dir->d_name, b_dir->d_name);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char * filebrowser_get_extension(const char * filename)
|
||||
{
|
||||
const char * ext = strrchr(filename, '.');
|
||||
|
||||
if (ext)
|
||||
return ext+1;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
static void filebrowser_clear_current_entries(filebrowser_t * filebrowser)
|
||||
{
|
||||
for(uint32_t i = 0; i < MAX_FILE_LIMIT; i++)
|
||||
{
|
||||
filebrowser->cur[filebrowser->file_count].d_type = 0;
|
||||
filebrowser->cur[filebrowser->file_count].d_namlen = 0;
|
||||
strcpy(filebrowser->cur[filebrowser->file_count].d_name, "\0");
|
||||
}
|
||||
}
|
||||
|
||||
static void filebrowser_parse_directory(filebrowser_t * filebrowser,
|
||||
const char * path, const char * extensions)
|
||||
{
|
||||
int error = 0;
|
||||
#if defined(_XBOX)
|
||||
filebrowser->file_count = 0;
|
||||
|
||||
WIN32_FIND_DATA ffd;
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
|
||||
char path_buf[PATH_MAX];
|
||||
|
||||
if (strlcpy(path_buf, path, sizeof(path_buf)) >= sizeof(path_buf))
|
||||
{
|
||||
error = 1;
|
||||
goto error;
|
||||
}
|
||||
if (strlcat(path_buf, "\\*", sizeof(path_buf)) >= sizeof(path_buf))
|
||||
{
|
||||
error = 1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
hFind = FindFirstFile(path_buf, &ffd);
|
||||
if (hFind == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
error = 1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
strcpy(filebrowser->dir[filebrowser->directory_stack_size], path);
|
||||
bool found_dir = false;
|
||||
if(!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
||||
{
|
||||
char tmp_extensions[512];
|
||||
strncpy(tmp_extensions, extensions, sizeof(tmp_extensions));
|
||||
const char * current_extension = filebrowser_get_extension(ffd.cFileName);
|
||||
bool found_rom = false;
|
||||
|
||||
if(current_extension)
|
||||
{
|
||||
char * pch = strtok(tmp_extensions, "|");
|
||||
while (pch != NULL)
|
||||
{
|
||||
if(strcmp(current_extension, pch) == 0)
|
||||
{
|
||||
found_rom = true;
|
||||
break;
|
||||
}
|
||||
pch = strtok(NULL, "|");
|
||||
}
|
||||
}
|
||||
|
||||
if(!found_rom)
|
||||
continue;
|
||||
}
|
||||
else if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
found_dir = true;
|
||||
|
||||
filebrowser->cur[filebrowser->file_count].d_type = found_dir ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL;
|
||||
sprintf(filebrowser->cur[filebrowser->file_count].d_name, ffd.cFileName);
|
||||
|
||||
filebrowser->file_count++;
|
||||
}while (FindNextFile(hFind, &ffd) != 0 && (filebrowser->file_count + 1) < MAX_FILE_LIMIT);
|
||||
#elif defined(__CELLOS_LV2__)
|
||||
int fd;
|
||||
|
||||
/* bad path*/
|
||||
if (strcmp(path,"") == 0)
|
||||
{
|
||||
error = 1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* delete old path*/
|
||||
filebrowser_clear_current_entries(filebrowser);
|
||||
|
||||
if (cellFsOpendir(path, &fd) == CELL_FS_SUCCEEDED)
|
||||
{
|
||||
uint64_t nread = 0;
|
||||
|
||||
strcpy(filebrowser->dir[filebrowser->directory_stack_size], path);
|
||||
|
||||
filebrowser->file_count = 0;
|
||||
filebrowser->currently_selected = 0;
|
||||
|
||||
CellFsDirent dirent;
|
||||
|
||||
while (cellFsReaddir(fd, &dirent, &nread) == CELL_FS_SUCCEEDED)
|
||||
{
|
||||
if (nread == 0)
|
||||
break;
|
||||
|
||||
if ((dirent.d_type != CELL_FS_TYPE_REGULAR) && (dirent.d_type != CELL_FS_TYPE_DIRECTORY))
|
||||
continue;
|
||||
|
||||
if (dirent.d_type == CELL_FS_TYPE_DIRECTORY && !(strcmp(dirent.d_name, ".")))
|
||||
continue;
|
||||
|
||||
if (dirent.d_type == CELL_FS_TYPE_REGULAR)
|
||||
{
|
||||
char tmp_extensions[512];
|
||||
strncpy(tmp_extensions, extensions, sizeof(tmp_extensions));
|
||||
const char * current_extension = filebrowser_get_extension(dirent.d_name);
|
||||
bool found_rom = false;
|
||||
|
||||
if(current_extension)
|
||||
{
|
||||
char * pch = strtok(tmp_extensions, "|");
|
||||
while (pch != NULL)
|
||||
{
|
||||
if(strcmp(current_extension, pch) == 0)
|
||||
{
|
||||
found_rom = true;
|
||||
break;
|
||||
}
|
||||
pch = strtok(NULL, "|");
|
||||
}
|
||||
}
|
||||
|
||||
if(!found_rom)
|
||||
continue;
|
||||
}
|
||||
|
||||
filebrowser->cur[filebrowser->file_count].d_type = dirent.d_type;
|
||||
filebrowser->cur[filebrowser->file_count].d_namlen = dirent.d_namlen;
|
||||
strcpy(filebrowser->cur[filebrowser->file_count].d_name, dirent.d_name);
|
||||
|
||||
++filebrowser->file_count;
|
||||
}
|
||||
|
||||
cellFsClosedir(fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
error = 1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
qsort(filebrowser->cur, filebrowser->file_count, sizeof(DirectoryEntry), less_than_key);
|
||||
#endif
|
||||
error:
|
||||
if(error)
|
||||
{
|
||||
SSNES_ERR("Failed to open directory: \"%s\"\n", path);
|
||||
}
|
||||
#ifdef _XBOX
|
||||
FindClose(hFind);
|
||||
#endif
|
||||
}
|
||||
|
||||
void filebrowser_new(filebrowser_t * filebrowser, const char * start_dir,
|
||||
const char * extensions)
|
||||
{
|
||||
filebrowser_clear_current_entries(filebrowser);
|
||||
filebrowser->directory_stack_size = 0;
|
||||
strlcpy(filebrowser->extensions, extensions, sizeof(filebrowser->extensions));
|
||||
|
||||
filebrowser_parse_directory(filebrowser, start_dir, filebrowser->extensions);
|
||||
}
|
||||
|
||||
|
||||
void filebrowser_reset_start_directory(filebrowser_t * filebrowser, const char * start_dir,
|
||||
const char * extensions)
|
||||
{
|
||||
filebrowser->directory_stack_size = 0;
|
||||
strlcpy(filebrowser->extensions, extensions, sizeof(filebrowser->extensions));
|
||||
|
||||
filebrowser_parse_directory(filebrowser, start_dir, filebrowser->extensions);
|
||||
}
|
||||
|
||||
void filebrowser_push_directory(filebrowser_t * filebrowser, const char * path,
|
||||
bool with_extension)
|
||||
{
|
||||
filebrowser->directory_stack_size++;
|
||||
if(with_extension)
|
||||
filebrowser_parse_directory(filebrowser, path, filebrowser->extensions);
|
||||
else
|
||||
filebrowser_parse_directory(filebrowser, path, "empty");
|
||||
}
|
||||
|
||||
void filebrowser_pop_directory (filebrowser_t * filebrowser)
|
||||
{
|
||||
if (filebrowser->directory_stack_size > 0)
|
||||
filebrowser->directory_stack_size--;
|
||||
|
||||
filebrowser_parse_directory(filebrowser, filebrowser->dir[filebrowser->directory_stack_size],
|
||||
filebrowser->extensions);
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef FILEBROWSER_H_
|
||||
#define FILEBROWSER_H_
|
||||
|
||||
#define MAXJOLIET 255
|
||||
#define MAX_PATH_LENGTH 1024
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef __CELLOS_LV2__
|
||||
#include <stdbool.h>
|
||||
#include <cell/cell_fs.h>
|
||||
#include <sys/types.h>
|
||||
#define FS_MAX_PATH 256
|
||||
#define FS_MAX_FS_PATH_LENGTH 255
|
||||
#define MAX_FILE_LIMIT 30000
|
||||
#elif defined(_XBOX)
|
||||
#define FS_MAX_PATH MAX_PATH
|
||||
#define FS_MAX_FS_PATH_LENGTH 2048
|
||||
#define MAX_FILE_LIMIT 4096
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint8_t d_type;
|
||||
uint8_t d_namlen;
|
||||
char d_name[FS_MAX_PATH];
|
||||
} DirectoryEntry;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint32_t file_count; /* amount of files in current dir*/
|
||||
uint32_t currently_selected; /* currently select browser entry*/
|
||||
uint32_t directory_stack_size;
|
||||
char dir[128][FS_MAX_FS_PATH_LENGTH]; /* info of the current directory*/
|
||||
DirectoryEntry cur[MAX_FILE_LIMIT]; /* current file listing*/
|
||||
char extensions[512]; /* allowed extensions*/
|
||||
} filebrowser_t;
|
||||
|
||||
void filebrowser_new(filebrowser_t * filebrowser, const char * start_dir, const char * extensions);
|
||||
void filebrowser_reset_start_directory(filebrowser_t * filebrowser, const char * start_dir, const char * extensions);
|
||||
void filebrowser_push_directory(filebrowser_t * filebrowser, const char * path, bool with_extension);
|
||||
void filebrowser_pop_directory (filebrowser_t * filebrowser);
|
||||
|
||||
#define FILEBROWSER_GET_CURRENT_DIRECTORY_NAME(filebrowser) (filebrowser.dir[filebrowser.directory_stack_size])
|
||||
#define FILEBROWSER_GET_CURRENT_DIRECTORY_FILE_COUNT(filebrowser) (filebrowser.file_count)
|
||||
#define FILEBROWSER_GOTO_ENTRY(filebrowser, i) filebrowser.currently_selected = i;
|
||||
|
||||
#define FILEBROWSER_INCREMENT_ENTRY(filebrowser) \
|
||||
{ \
|
||||
filebrowser.currently_selected++; \
|
||||
if (filebrowser.currently_selected >= filebrowser.file_count) \
|
||||
filebrowser.currently_selected = 0; \
|
||||
}
|
||||
|
||||
#define FILEBROWSER_INCREMENT_ENTRY_POINTER(filebrowser) \
|
||||
{ \
|
||||
filebrowser->currently_selected++; \
|
||||
if (filebrowser->currently_selected >= filebrowser->file_count) \
|
||||
filebrowser->currently_selected = 0; \
|
||||
}
|
||||
|
||||
#define FILEBROWSER_DECREMENT_ENTRY(filebrowser) \
|
||||
{ \
|
||||
filebrowser.currently_selected--; \
|
||||
if (filebrowser.currently_selected >= filebrowser.file_count) \
|
||||
filebrowser.currently_selected = filebrowser.file_count - 1; \
|
||||
}
|
||||
|
||||
#define FILEBROWSER_DECREMENT_ENTRY_POINTER(filebrowser) \
|
||||
{ \
|
||||
filebrowser->currently_selected--; \
|
||||
if (filebrowser->currently_selected >= filebrowser->file_count) \
|
||||
filebrowser->currently_selected = filebrowser->file_count - 1; \
|
||||
}
|
||||
|
||||
#define FILEBROWSER_GET_CURRENT_FILENAME(filebrowser) (filebrowser.cur[filebrowser.currently_selected].d_name)
|
||||
#define FILEBROWSER_GET_CURRENT_ENTRY_INDEX(filebrowser) (filebrowser.currently_selected)
|
||||
#define FILEBROWSER_IS_CURRENT_A_FILE(filebrowser) (filebrowser.cur[filebrowser.currently_selected].d_type == CELL_FS_TYPE_REGULAR)
|
||||
#define FILEBROWSER_IS_CURRENT_A_DIRECTORY(filebrowser) (filebrowser.cur[filebrowser.currently_selected].d_type == CELL_FS_TYPE_DIRECTORY)
|
||||
|
||||
#endif /* FILEBROWSER_H_ */
|
||||
@@ -0,0 +1,32 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#ifndef _FUNC_HOOKS_H
|
||||
#define _FUNC_HOOKS_H
|
||||
|
||||
/*============================================================
|
||||
GENERAL
|
||||
============================================================ */
|
||||
#if defined(__CELLOS_LV2__) || defined(_XBOX)
|
||||
#define HAVE_GRIFFIN_OVERRIDE_VIDEO_FRAME_FUNC 1
|
||||
#endif
|
||||
|
||||
#define ssnes_render_cached_frame() \
|
||||
const char *msg = msg_queue_pull(g_extern.msg_queue); \
|
||||
video_frame_func(g_extern.frame_cache.data, g_extern.frame_cache.width, g_extern.frame_cache.height, lines_to_pitch(g_extern.frame_cache.height), msg);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,173 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if defined(_XBOX)
|
||||
#include "../../msvc/msvc_compat.h"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
CONSOLE EXTENSIONS
|
||||
============================================================ */
|
||||
#include "../console_ext.c"
|
||||
|
||||
/*============================================================
|
||||
COMPATIBILITY
|
||||
============================================================ */
|
||||
#include "../../compat/compat.c"
|
||||
|
||||
/*============================================================
|
||||
CONFIG FILE
|
||||
============================================================ */
|
||||
#ifdef _XBOX
|
||||
#undef __SSNES_POSIX_STRING_H
|
||||
#undef __SSNES_MSVC_COMPAT_H
|
||||
#undef strcasecmp
|
||||
#endif
|
||||
#include "../../conf/config_file.c"
|
||||
|
||||
#include "func_hooks.h"
|
||||
|
||||
/*============================================================
|
||||
VIDEO
|
||||
============================================================ */
|
||||
#if defined(__CELLOS_LV2__)
|
||||
#include "../../ps3/ps3_video_psgl.c"
|
||||
#include "../../ps3/image.c"
|
||||
#elif defined(_XBOX)
|
||||
#include "../../360/xdk360_video.cpp"
|
||||
#include "../../360/fonts.cpp"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
INPUT
|
||||
============================================================ */
|
||||
#if defined(__CELLOS_LV2__)
|
||||
#include "../../ps3/ps3_input.c"
|
||||
#elif defined(_XBOX)
|
||||
#include "../../360/xdk360_input.c"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
SNES STATE
|
||||
============================================================ */
|
||||
#include "../../gfx/snes_state.c"
|
||||
|
||||
/*============================================================
|
||||
DRIVERS
|
||||
============================================================ */
|
||||
#include "../../driver.c"
|
||||
|
||||
/*============================================================
|
||||
FIFO BUFFER
|
||||
============================================================ */
|
||||
#include "../../fifo_buffer.c"
|
||||
|
||||
/*============================================================
|
||||
AUDIO HERMITE
|
||||
============================================================ */
|
||||
#include "../../audio/hermite.c"
|
||||
|
||||
/*============================================================
|
||||
RSOUND
|
||||
============================================================ */
|
||||
#ifdef __CELLOS_LV2__
|
||||
#include "../../console/librsound/librsound.c"
|
||||
#include "../../audio/rsound.c"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
AUDIO UTILS
|
||||
============================================================ */
|
||||
#include "../../audio/utils.c"
|
||||
|
||||
/*============================================================
|
||||
AUDIO
|
||||
============================================================ */
|
||||
#if defined(__CELLOS_LV2__)
|
||||
#include "../../ps3/ps3_audio.c"
|
||||
#elif defined(_XBOX)
|
||||
#include "../../360/xdk360_audio.cpp"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
DYNAMIC
|
||||
============================================================ */
|
||||
#include "../../dynamic.c"
|
||||
|
||||
/*============================================================
|
||||
FILE
|
||||
============================================================ */
|
||||
#if defined(__CELLOS_LV2__) || defined(_XBOX)
|
||||
#include "../fileio/file_browser.c"
|
||||
#endif
|
||||
#include "../../file.c"
|
||||
|
||||
/*============================================================
|
||||
MESSAGE
|
||||
============================================================ */
|
||||
#include "../../message.c"
|
||||
|
||||
/*============================================================
|
||||
PATCH
|
||||
============================================================ */
|
||||
#include "../../patch.c"
|
||||
|
||||
/*============================================================
|
||||
SETTINGS
|
||||
============================================================ */
|
||||
#include "../../settings.c"
|
||||
|
||||
/*============================================================
|
||||
REWIND
|
||||
============================================================ */
|
||||
#include "../../rewind.c"
|
||||
|
||||
|
||||
|
||||
/*============================================================
|
||||
MAIN
|
||||
============================================================ */
|
||||
#if defined(__CELLOS_LV2__)
|
||||
#include "../../ps3/main.c"
|
||||
#elif defined(_XBOX)
|
||||
#include "../../360/main.c"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
SSNES
|
||||
============================================================ */
|
||||
#include "../../ssnes.c"
|
||||
|
||||
/*============================================================
|
||||
THREAD
|
||||
============================================================ */
|
||||
#include "../../thread.c"
|
||||
|
||||
/*============================================================
|
||||
NETPLAY
|
||||
============================================================ */
|
||||
#include "../../netplay.c"
|
||||
|
||||
/*============================================================
|
||||
MENU
|
||||
============================================================ */
|
||||
#if defined(__CELLOS_LV2__)
|
||||
#include "../../ps3/menu.c"
|
||||
#elif defined(_XBOX)
|
||||
#include "../../360/menu.cpp"
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define audio_init_func(device, rate, latency) driver.audio->init(device, rate, latency)
|
||||
#define audio_write_func(buf, size) driver.audio->write(driver.audio_data, buf, size)
|
||||
#define audio_stop_func() driver.audio->stop(driver.audio_data)
|
||||
#define audio_start_func() driver.audio->start(driver.audio_data)
|
||||
#define audio_set_nonblock_state_func(state) driver.audio->set_nonblock_state(driver.audio_data, state)
|
||||
#define audio_free_func() driver.audio->free(driver.audio_data)
|
||||
#define audio_use_float_func() driver.audio->use_float(driver.audio_data)
|
||||
#define audio_write_avail_func() driver.audio->write_avail(driver.audio_data)
|
||||
#define audio_buffer_size_func() driver.audio->buffer_size(driver.audio_data)
|
||||
|
||||
/*============================================================
|
||||
PLAYSTATION3
|
||||
============================================================ */
|
||||
|
||||
#ifdef __CELLOS_LV2__
|
||||
#define video_init_func(video_info, input, input_data) \
|
||||
gl_init(video_info, input, input_data)
|
||||
#define video_frame_func(data, width, height, pitch, msg) \
|
||||
gl_frame(driver.video_data, data, width, height, pitch, msg)
|
||||
#define video_set_nonblock_state_func(state) driver.video->set_nonblock_state(driver.video_data, state)
|
||||
#define video_alive_func() gl_alive(driver.video_data)
|
||||
#define video_focus_func() gl_focus(driver.video_data)
|
||||
#define video_xml_shader_func(path) driver.video->xml_shader(driver.video_data, path)
|
||||
#define video_free_func() gl_free(driver.video_data)
|
||||
#define video_set_rotation_func(orientation) ps3graphics_set_orientation(driver.video_data, orientation)
|
||||
|
||||
#define input_init_func() ps3_input_initialize()
|
||||
#define input_poll_func() ps3_input_poll(driver.input_data)
|
||||
#define input_input_state_func(snes_keybinds, port, device, index, id) \
|
||||
ps3_input_state(driver.input_data, snes_keybinds, port, device, index, id)
|
||||
#define input_key_pressed_func(key) ps3_key_pressed(driver.input_data, key)
|
||||
#define input_free_func() ps3_free_input(driver.input_data)
|
||||
|
||||
/*============================================================
|
||||
XBOX 360
|
||||
============================================================ */
|
||||
|
||||
#elif defined(_XBOX)
|
||||
|
||||
#define video_init_func(video_info, input, input_data) \
|
||||
xdk360_gfx_init(video_info, input, input_data)
|
||||
#define video_frame_func(data, width, height, pitch, msg) \
|
||||
xdk360_gfx_frame(driver.video_data, data, width, height, pitch, msg)
|
||||
#define video_set_nonblock_state_func(state) driver.video->set_nonblock_state(driver.video_data, state)
|
||||
#define video_alive_func() xdk360_gfx_alive(driver.video_data)
|
||||
#define video_focus_func() xdk360_gfx_focus(driver.video_data)
|
||||
#define video_xml_shader_func(path) driver.video->xml_shader(driver.video_data, path)
|
||||
#define video_free_func() xdk360_gfx_free(driver.video_data)
|
||||
#define video_set_rotation_func(orientation) xdk360_set_orientation(driver.video_data, orientation)
|
||||
|
||||
#define input_init_func() xdk360_input_initialize()
|
||||
#define input_poll_func() xdk360_input_poll(driver.input_data)
|
||||
#define input_input_state_func(snes_keybinds, port, device, index, id) \
|
||||
xdk360_input_state(driver.input_data, snes_keybinds, port, device, index, id)
|
||||
#define input_key_pressed_func(key) xdk360_key_pressed(driver.input_data, key)
|
||||
#define input_free_func() xdk360_free_input(driver.input_data)
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _SSNES_FUNC_HOOKS_H
|
||||
#define _SSNES_FUNC_HOOKS_H
|
||||
|
||||
/*============================================================
|
||||
PLAYSTATION3
|
||||
============================================================ */
|
||||
|
||||
#ifdef __CELLOS_LV2__
|
||||
|
||||
#define HAVE_GRIFFIN_OVERRIDE_AUDIO_FLUSH_FUNC 1
|
||||
|
||||
static bool audio_flush(const int16_t *data, size_t samples)
|
||||
{
|
||||
const float *output_data = NULL;
|
||||
unsigned output_frames = 0;
|
||||
|
||||
audio_convert_s16_to_float(g_extern.audio_data.data, data, samples);
|
||||
|
||||
struct resampler_data src_data = {0};
|
||||
src_data.data_in = g_extern.audio_data.data;
|
||||
src_data.data_out = g_extern.audio_data.outsamples;
|
||||
src_data.input_frames = (samples / 2);
|
||||
|
||||
src_data.ratio = g_extern.audio_data.src_ratio;
|
||||
if (g_extern.is_slowmotion)
|
||||
src_data.ratio *= g_settings.slowmotion_ratio;
|
||||
|
||||
resampler_process(g_extern.audio_data.source, &src_data);
|
||||
|
||||
output_data = g_extern.audio_data.outsamples;
|
||||
output_frames = src_data.output_frames;
|
||||
|
||||
if (audio_write_func(output_data, output_frames * sizeof(float) * 2) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,339 @@
|
||||
/* RSound - A PCM audio client/server
|
||||
* Copyright (C) 2010 - Hans-Kristian Arntzen
|
||||
*
|
||||
* RSound 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.
|
||||
*
|
||||
* RSound 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 RSound.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __RSOUND_H
|
||||
#define __RSOUND_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef RSD_EXPOSE_STRUCT
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#else
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include "../../fifo_buffer.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define RSD_DEFAULT_HOST "127.0.0.1" // Stupid Windows.
|
||||
#else
|
||||
#define RSD_DEFAULT_HOST "localhost"
|
||||
#endif
|
||||
#define RSD_DEFAULT_PORT "12345"
|
||||
#define RSD_DEFAULT_UNIX_SOCK "/tmp/rsound"
|
||||
#define RSD_DEFAULT_OBJECT "rsound"
|
||||
|
||||
#ifndef RSD_VERSION
|
||||
#define RSD_VERSION "1.1"
|
||||
#endif
|
||||
|
||||
/* Feature tests */
|
||||
#define RSD_SAMPLERATE RSD_SAMPLERATE
|
||||
#define RSD_CHANNELS RSD_CHANNELS
|
||||
#define RSD_HOST RSD_HOST
|
||||
#define RSD_PORT RSD_PORT
|
||||
#define RSD_BUFSIZE RSD_BUFSIZE
|
||||
#define RSD_LATENCY RSD_LATENCY
|
||||
#define RSD_FORMAT RSD_FORMAT
|
||||
#define RSD_IDENTITY RSD_IDENTITY
|
||||
|
||||
#define RSD_S16_LE RSD_S16_LE
|
||||
#define RSD_S16_BE RSD_S16_BE
|
||||
#define RSD_U16_LE RSD_U16_LE
|
||||
#define RSD_U16_BE RSD_U16_BE
|
||||
#define RSD_U8 RSD_U8
|
||||
#define RSD_S8 RSD_S8
|
||||
#define RSD_S16_NE RSD_S16_NE
|
||||
#define RSD_U16_NE RSD_U16_NE
|
||||
#define RSD_ALAW RSD_ALAW
|
||||
#define RSD_MULAW RSD_MULAW
|
||||
|
||||
#define RSD_S32_LE RSD_S32_LE
|
||||
#define RSD_S32_BE RSD_S32_BE
|
||||
#define RSD_S32_NE RSD_S32_NE
|
||||
#define RSD_U32_LE RSD_U32_LE
|
||||
#define RSD_U32_BE RSD_U32_BE
|
||||
#define RSD_U32_NE RSD_U32_NE
|
||||
|
||||
#define RSD_DELAY_MS RSD_DELAY_MS
|
||||
#define RSD_SAMPLESIZE RSD_SAMPLESIZE
|
||||
#define RSD_EXEC RSD_EXEC
|
||||
#define RSD_SIMPLE_START RSD_SIMPLE_START
|
||||
|
||||
#define RSD_NO_FMT RSD_NO_FMT
|
||||
#define RSD_USES_OPAQUE_TYPE RSD_USES_OPAQUE_TYPE
|
||||
#define RSD_USES_SAMPLESIZE_MEMBER RSD_USES_SAMPLESIZE_MEMBER
|
||||
|
||||
#define RSD_AUDIO_CALLBACK_T RSD_AUDIO_CALLBACK_T
|
||||
#define RSD_ERROR_CALLBACK_T RSD_ERROR_CALLBACK_T
|
||||
#define RSD_SET_CALLBACK RSD_SET_CALLBACK
|
||||
#define RSD_CALLBACK_LOCK RSD_CALLBACK_LOCK
|
||||
#define RSD_CALLBACK_UNLOCK RSD_CALLBACK_UNLOCK
|
||||
/* End feature tests */
|
||||
|
||||
|
||||
|
||||
/* Defines sample formats available. Defaults to S16_LE should it never be set. */
|
||||
enum rsd_format
|
||||
{
|
||||
RSD_NO_FMT = 0x0000,
|
||||
RSD_S16_LE = 0x0001,
|
||||
RSD_S16_BE = 0x0002,
|
||||
RSD_U16_LE = 0x0004,
|
||||
RSD_U16_BE = 0x0008,
|
||||
RSD_U8 = 0x0010,
|
||||
RSD_S8 = 0x0020,
|
||||
RSD_S16_NE = 0x0040,
|
||||
RSD_U16_NE = 0x0080,
|
||||
RSD_ALAW = 0x0100,
|
||||
RSD_MULAW = 0x0200,
|
||||
RSD_S32_LE = 0x0400,
|
||||
RSD_S32_BE = 0x0800,
|
||||
RSD_S32_NE = 0x1000,
|
||||
RSD_U32_LE = 0x2000,
|
||||
RSD_U32_BE = 0x4000,
|
||||
RSD_U32_NE = 0x8000,
|
||||
};
|
||||
|
||||
/* Defines operations that can be used with rsd_set_param() */
|
||||
enum rsd_settings
|
||||
{
|
||||
RSD_SAMPLERATE = 0,
|
||||
RSD_CHANNELS,
|
||||
RSD_HOST,
|
||||
RSD_PORT,
|
||||
RSD_BUFSIZE,
|
||||
RSD_LATENCY,
|
||||
RSD_FORMAT,
|
||||
RSD_IDENTITY
|
||||
};
|
||||
|
||||
/* Audio callback for rsd_set_callback. Return -1 to trigger an error in the stream. */
|
||||
typedef ssize_t (*rsd_audio_callback_t)(void *data, size_t bytes, void *userdata);
|
||||
|
||||
/* Error callback. Signals caller that stream has been stopped, either by audio callback returning -1 or stream was hung up. */
|
||||
typedef void (*rsd_error_callback_t)(void *userdata);
|
||||
|
||||
|
||||
#ifdef RSD_EXPOSE_STRUCT
|
||||
|
||||
/* Defines the main structure for use with the API. */
|
||||
typedef struct rsound
|
||||
{
|
||||
struct {
|
||||
volatile int socket;
|
||||
volatile int ctl_socket;
|
||||
} conn;
|
||||
|
||||
char *host;
|
||||
char *port;
|
||||
char *buffer; /* Obsolete, but kept for backwards header compatibility. */
|
||||
int conn_type;
|
||||
|
||||
volatile int buffer_pointer; /* Obsolete, but kept for backwards header compatibility. */
|
||||
size_t buffer_size;
|
||||
fifo_buffer_t *fifo_buffer;
|
||||
|
||||
volatile int thread_active;
|
||||
|
||||
int64_t total_written;
|
||||
int64_t start_time;
|
||||
volatile int has_written;
|
||||
int bytes_in_buffer;
|
||||
int delay_offset;
|
||||
int max_latency;
|
||||
|
||||
struct {
|
||||
uint32_t latency;
|
||||
uint32_t chunk_size;
|
||||
} backend_info;
|
||||
|
||||
volatile int ready_for_data;
|
||||
|
||||
uint32_t rate;
|
||||
uint32_t channels;
|
||||
uint16_t format;
|
||||
int samplesize;
|
||||
|
||||
struct {
|
||||
pthread_t threadId;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_mutex_t cond_mutex;
|
||||
pthread_cond_t cond;
|
||||
} thread;
|
||||
|
||||
char identity[256];
|
||||
|
||||
rsd_audio_callback_t audio_callback;
|
||||
rsd_error_callback_t error_callback;
|
||||
size_t cb_max_size;
|
||||
void *cb_data;
|
||||
pthread_mutex_t cb_lock;
|
||||
} rsound_t;
|
||||
#else
|
||||
typedef struct rsound rsound_t;
|
||||
#endif
|
||||
|
||||
/* -- API --
|
||||
All functions (except for rsd_write() return 0 for success, and -1 for error. errno is currently not set. */
|
||||
|
||||
/* Initializes an rsound_t structure. To make sure no memory leaks occur, you need to rsd_free() it after use.
|
||||
A typical use of the API is as follows:
|
||||
rsound_t *rd;
|
||||
rsd_init(&rd);
|
||||
rsd_set_param(rd, RSD_HOST, "foohost");
|
||||
*sets more params*
|
||||
rsd_start(rd);
|
||||
rsd_write(rd, buf, size);
|
||||
rsd_stop(rd);
|
||||
rsd_free(rd);
|
||||
*/
|
||||
int rsd_init (rsound_t **rd);
|
||||
|
||||
|
||||
/* This is a simpler function that initializes an rsound struct, sets params as given,
|
||||
and starts the stream. Should this function fail, the structure will stay uninitialized.
|
||||
Should NULL be passed in either host, port or ident, defaults will be used. */
|
||||
|
||||
int rsd_simple_start (rsound_t **rd, const char* host, const char* port, const char* ident,
|
||||
int rate, int channels, enum rsd_format format);
|
||||
|
||||
|
||||
/* Sets params associated with an rsound_t. These options (int options) include:
|
||||
|
||||
RSD_HOST: Server to connect to. Expects (char *) in param.
|
||||
If not set, will default to environmental variable RSD_SERVER or "localhost".
|
||||
|
||||
RSD_PORT: Set port. Expects (char *) in param.
|
||||
If not set, will default to environmental variable RSD_PORT or "12345".
|
||||
|
||||
RSD_CHANNELS: Set number of audio channels. Expects (int *) in param. Mandatory.
|
||||
|
||||
RSD_SAMPLERATE: Set samplerate of audio stream. Expects (int *) in param. Mandatory.
|
||||
|
||||
RSD_BUFSIZE: Sets internal buffersize for the stream.
|
||||
Might be overridden if too small.
|
||||
Expects (int *) in param. Optional.
|
||||
|
||||
RSD_LATENCY: Sets maximum audio latency in milliseconds,
|
||||
(must be used with rsd_delay_wait() or this will have no effect).
|
||||
Most applications do not need this.
|
||||
Might be overridden if too small.
|
||||
Expects (int *) in param. Optional.
|
||||
|
||||
RSD_FORMAT: Sets sample format.
|
||||
It defaults to S16_LE, so you probably will not use this.
|
||||
Expects (int *) in param, with available values found in the format enum.
|
||||
If invalid format is given, param might be changed to reflect the sample format the library will use.
|
||||
|
||||
RSD_IDENTITY: Sets an identity string associated with the client.
|
||||
Takes a (char *) parameter with the stream name.
|
||||
Will be truncated if longer than 256 bytes.
|
||||
|
||||
*/
|
||||
|
||||
int rsd_set_param (rsound_t *rd, enum rsd_settings option, void* param);
|
||||
|
||||
/* Enables use of the callback interface. This must be set when stream is not active.
|
||||
When callback is active, use of the blocking interface is disabled.
|
||||
Only valid functions to call after rsd_start() is stopping the stream with either rsd_pause() or rsd_stop(). Calling any other function is undefined.
|
||||
The callback is called at regular intervals and is asynchronous, so thread safety must be ensured by the caller.
|
||||
If not enough data can be given to the callback, librsound will fill the rest of the callback data with silence.
|
||||
librsound will attempt to obey latency information given with RSD_LATENCY as given before calling rsd_start().
|
||||
max_size signifies the maximum size that will ever be requested by librsound. Set this to 0 to let librsound decide the maximum size.
|
||||
Should an error occur to the stream, err_callback will be called, and the stream will be stopped. The stream can be started again.
|
||||
|
||||
Callbacks can be disabled by setting callbacks to NULL. */
|
||||
|
||||
void rsd_set_callback (rsound_t *rd, rsd_audio_callback_t callback, rsd_error_callback_t err_callback, size_t max_size, void *userdata);
|
||||
|
||||
/* Lock and unlock the callback. When the callback lock is aquired, the callback is guaranteed to not be executing.
|
||||
The lock has to be unlocked afterwards.
|
||||
Attemping to call several rsd_callback_lock() in succession might cause a deadlock.
|
||||
The lock should be held for as short period as possible.
|
||||
Try to avoid calling code that may block when holding the lock. */
|
||||
void rsd_callback_lock (rsound_t *rd);
|
||||
void rsd_callback_unlock (rsound_t *rd);
|
||||
|
||||
/* Establishes connection to server. Might fail if connection can't be established or that one of
|
||||
the mandatory options isn't set in rsd_set_param(). This needs to be called after params have been set
|
||||
with rsd_set_param(), and before rsd_write(). */
|
||||
int rsd_start (rsound_t *rd);
|
||||
|
||||
/* Shuts down the rsound data structures, but returns the file descriptor associated with the connection.
|
||||
The control socket will be shut down. If this function returns a negative number, the exec failed,
|
||||
but the data structures will not be teared down.
|
||||
Should a valid file descriptor be returned, it will always be blocking.
|
||||
This call will block until all internal buffers have been sent to the network. */
|
||||
int rsd_exec (rsound_t *rd);
|
||||
|
||||
/* Disconnects from server. All audio data still in network buffer and other buffers will be dropped.
|
||||
To continue playing, you will need to rsd_start() again. */
|
||||
int rsd_stop (rsound_t *rd);
|
||||
|
||||
/* Writes from buf to the internal buffer. Might fail if no connection is established,
|
||||
or there was an unexpected error. This function will block until all data has
|
||||
been written to the buffer. This function will return the number of bytes written to the buffer,
|
||||
or 0 should it fail (disconnection from server). You will have to restart the stream again should this occur. */
|
||||
size_t rsd_write (rsound_t *rd, const void* buf, size_t size);
|
||||
|
||||
/* Gets the position of the buffer pointer.
|
||||
Not really interesting for normal applications.
|
||||
Might be useful for implementing rsound on top of other blocking APIs.
|
||||
*NOTE* This function is deprecated, it should not be used in new applications. */
|
||||
size_t rsd_pointer (rsound_t *rd);
|
||||
|
||||
/* Aquires how much data can be written to the buffer without blocking */
|
||||
size_t rsd_get_avail (rsound_t *rd);
|
||||
|
||||
/* Aquires the latency at the moment for the audio stream. It is measured in bytes. Useful for syncing video and audio. */
|
||||
size_t rsd_delay (rsound_t *rd);
|
||||
|
||||
/* Utility for returning latency in milliseconds. */
|
||||
size_t rsd_delay_ms (rsound_t *rd);
|
||||
|
||||
/* Returns bytes per sample */
|
||||
int rsd_samplesize(rsound_t *rd);
|
||||
|
||||
/* Will sleep until latency of stream reaches maximum allowed latency defined earlier by rsd_set_param - RSD_LATENCY
|
||||
Useful for hard headed blocking I/O design where user defined latency is needed. If rsd_set_param hasn't been set
|
||||
with RSD_LATENCY, this function will do nothing. */
|
||||
void rsd_delay_wait(rsound_t *rd);
|
||||
|
||||
|
||||
/* Pauses or unpauses a stream. pause -> enable = 1
|
||||
This function essentially calls on start() and stop(). This behavior might be changed later. */
|
||||
int rsd_pause (rsound_t *rd, int enable);
|
||||
|
||||
/* Frees an rsound_t struct. Make sure that the stream is properly closed down with rsd_stop() before calling rsd_free(). */
|
||||
int rsd_free (rsound_t *rd);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef __CELLOS_LV2__
|
||||
#include <netex/net.h>
|
||||
#include <cell/sysmodule.h>
|
||||
#include <netex/libnetctl.h>
|
||||
#include <sys/timer.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
#ifdef HW_RVL
|
||||
#include <network.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../../netplay_compat.h"
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
#if !defined(PC_DEVELOPMENT_IP_ADDRESS)
|
||||
#define PC_DEVELOPMENT_IP_ADDRESS "192.168.1.7"
|
||||
#endif
|
||||
|
||||
#if !defined(PC_DEVELOPMENT_UDP_PORT)
|
||||
#define PC_DEVELOPMENT_UDP_PORT 3490
|
||||
#endif
|
||||
|
||||
static int g_sid;
|
||||
static int sock;
|
||||
static struct sockaddr_in target;
|
||||
static char sendbuf[4096];
|
||||
|
||||
#ifdef HW_RVL
|
||||
#define sendto(s, msg, len, flags, addr, tolen) net_sendto(s, msg, len, flags, addr, tolen)
|
||||
#define socket(domain, type, protocol) net_socket(domain, type, protocol)
|
||||
|
||||
static int inet_pton(int af, const char *src, void *dst)
|
||||
{
|
||||
if (af != AF_INET)
|
||||
return -1;
|
||||
return inet_aton (src, dst);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int if_up_with(int index)
|
||||
{
|
||||
(void)index;
|
||||
#ifdef __CELLOS_LV2__
|
||||
int timeout_count = 10;
|
||||
int state;
|
||||
int ret;
|
||||
|
||||
ret = cellNetCtlInit();
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("cellNetCtlInit() failed(%x)\n", ret);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ret = cellNetCtlGetState(&state);
|
||||
if (ret < 0)
|
||||
{
|
||||
printf("cellNetCtlGetState() failed(%x)\n", ret);
|
||||
return (-1);
|
||||
}
|
||||
if (state == CELL_NET_CTL_STATE_IPObtained)
|
||||
break;
|
||||
sys_timer_usleep(500 * 1000);
|
||||
timeout_count--;
|
||||
if (index && timeout_count < 0)
|
||||
{
|
||||
printf("if_up_with(%d) timeout\n", index);
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
sock=socket(AF_INET,SOCK_DGRAM ,0);
|
||||
|
||||
target.sin_family = AF_INET;
|
||||
target.sin_port = htons(PC_DEVELOPMENT_UDP_PORT);
|
||||
inet_pton(AF_INET, PC_DEVELOPMENT_IP_ADDRESS, &target.sin_addr);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int if_down(int sid)
|
||||
{
|
||||
(void)sid;
|
||||
#ifdef __CELLOS_LV2__
|
||||
cellNetCtlTerm();
|
||||
#endif
|
||||
return (0);
|
||||
}
|
||||
|
||||
void logger_init (void)
|
||||
{
|
||||
g_sid = if_up_with(1);
|
||||
}
|
||||
|
||||
void logger_shutdown (void)
|
||||
{
|
||||
if_down(g_sid);
|
||||
}
|
||||
|
||||
void logger_send(const char *__format,...)
|
||||
{
|
||||
va_list args;
|
||||
|
||||
va_start(args,__format);
|
||||
vsnprintf(sendbuf,4000,__format, args);
|
||||
va_end(args);
|
||||
|
||||
int len=strlen(sendbuf);
|
||||
sendto(sock,sendbuf,len,MSG_DONTWAIT,(struct sockaddr*)&target,sizeof(target));
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _LOGGER_H_
|
||||
#define _LOGGER_H_
|
||||
|
||||
#define BUFSIZE (64 * 1024)
|
||||
#define TCPDUMP_STACKSIZE (16 * 1024)
|
||||
#define TCPDUMP_PRIO (2048)
|
||||
|
||||
void logger_init (void);
|
||||
void logger_shutdown (void);
|
||||
void logger_send (const char *__format,...);
|
||||
|
||||
#endif
|
||||
+13
-22
@@ -1,5 +1,6 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2011 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Daniel De Matteis
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
@@ -15,30 +16,20 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __UPS_H
|
||||
#define __UPS_H
|
||||
// Builds argc/argv and calls ssnes_main_init().
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#ifndef _SSNES_WRAP_H
|
||||
#define _SSNES_WRAP_H
|
||||
|
||||
// UPS implementation from bSNES.
|
||||
|
||||
typedef enum ups_error
|
||||
struct ssnes_main_wrap
|
||||
{
|
||||
UPS_UNKNOWN,
|
||||
UPS_SUCCESS,
|
||||
UPS_PATCH_INVALID,
|
||||
UPS_SOURCE_INVALID,
|
||||
UPS_TARGET_INVALID,
|
||||
UPS_TARGET_TOO_SMALL,
|
||||
UPS_PATCH_CHECKSUM_INVALID,
|
||||
UPS_SOURCE_CHECKSUM_INVALID,
|
||||
UPS_TARGET_CHECKSUM_INVALID
|
||||
} ups_error_t;
|
||||
const char *rom_path;
|
||||
const char *sram_path;
|
||||
const char *state_path;
|
||||
const char *config_path;
|
||||
bool verbose;
|
||||
};
|
||||
|
||||
ups_error_t ups_apply_patch(
|
||||
const uint8_t *patch_data, size_t patch_length,
|
||||
const uint8_t *source_data, size_t source_length,
|
||||
uint8_t *target_data, size_t *target_length);
|
||||
int ssnes_main_init_wrap(const struct ssnes_main_wrap *args);
|
||||
|
||||
#endif
|
||||
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 192 KiB |
@@ -0,0 +1,118 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - 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 "list.h"
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct sgui_file
|
||||
{
|
||||
char *path;
|
||||
sgui_file_type_t type;
|
||||
};
|
||||
|
||||
struct sgui_list
|
||||
{
|
||||
struct sgui_file *list;
|
||||
|
||||
size_t capacity;
|
||||
size_t ptr;
|
||||
};
|
||||
|
||||
sgui_list_t *sgui_list_new(void)
|
||||
{
|
||||
return (sgui_list_t*)calloc(1, sizeof(sgui_list_t));
|
||||
}
|
||||
|
||||
bool sgui_list_empty(const sgui_list_t *list)
|
||||
{
|
||||
return list->ptr == 0;
|
||||
}
|
||||
|
||||
void sgui_list_push(sgui_list_t *list, const char *path, sgui_file_type_t type)
|
||||
{
|
||||
if (list->ptr >= list->capacity)
|
||||
{
|
||||
list->capacity++;
|
||||
list->capacity *= 2;
|
||||
list->list = (struct sgui_file*)realloc(list->list, list->capacity * sizeof(struct sgui_file));
|
||||
}
|
||||
|
||||
list->list[list->ptr].path = strdup(path);
|
||||
list->list[list->ptr].type = type;
|
||||
list->ptr++;
|
||||
}
|
||||
|
||||
void sgui_list_pop(sgui_list_t *list)
|
||||
{
|
||||
if (!sgui_list_empty(list))
|
||||
free(list->list[--list->ptr].path);
|
||||
}
|
||||
|
||||
void sgui_list_free(sgui_list_t *list)
|
||||
{
|
||||
for (size_t i = 0; i < list->ptr; i++)
|
||||
free(list->list[i].path);
|
||||
free(list->list);
|
||||
free(list);
|
||||
}
|
||||
|
||||
void sgui_list_clear(sgui_list_t *list)
|
||||
{
|
||||
for (size_t i = 0; i < list->ptr; i++)
|
||||
free(list->list[i].path);
|
||||
list->ptr = 0;
|
||||
}
|
||||
|
||||
void sgui_list_back(const sgui_list_t *list,
|
||||
const char **path, sgui_file_type_t *file_type)
|
||||
{
|
||||
if (sgui_list_size(list) > 0)
|
||||
sgui_list_at(list, sgui_list_size(list) - 1, path, file_type);
|
||||
}
|
||||
|
||||
size_t sgui_list_size(const sgui_list_t *list)
|
||||
{
|
||||
return list->ptr;
|
||||
}
|
||||
|
||||
void sgui_list_at(const sgui_list_t *list, size_t index,
|
||||
const char **path, sgui_file_type_t *file_type)
|
||||
{
|
||||
if (path)
|
||||
*path = list->list[index].path;
|
||||
if (file_type)
|
||||
*file_type = list->list[index].type;
|
||||
}
|
||||
|
||||
static int list_comp(const void *a_, const void *b_)
|
||||
{
|
||||
const struct sgui_file *a = (const struct sgui_file*)a_;
|
||||
const struct sgui_file *b = (const struct sgui_file*)b_;
|
||||
|
||||
if (a->type != b->type)
|
||||
return a->type == SGUI_FILE_DIRECTORY ? -1 : 1;
|
||||
|
||||
return strcmp(a->path, b->path);
|
||||
}
|
||||
|
||||
void sgui_list_sort(sgui_list_t *list)
|
||||
{
|
||||
qsort(list->list, list->ptr, sizeof(struct sgui_file), list_comp);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - 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 SGUI_LIST_H__
|
||||
#define SGUI_LIST_H__
|
||||
|
||||
#include "sgui.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct sgui_list sgui_list_t;
|
||||
|
||||
sgui_list_t *sgui_list_new(void);
|
||||
void sgui_list_free(sgui_list_t *list);
|
||||
|
||||
void sgui_list_push(sgui_list_t *list, const char *path, sgui_file_type_t type);
|
||||
void sgui_list_pop(sgui_list_t *list);
|
||||
void sgui_list_clear(sgui_list_t *list);
|
||||
|
||||
bool sgui_list_empty(const sgui_list_t *list);
|
||||
void sgui_list_back(const sgui_list_t *list,
|
||||
const char **path, sgui_file_type_t *type);
|
||||
|
||||
size_t sgui_list_size(const sgui_list_t *list);
|
||||
void sgui_list_at(const sgui_list_t *list, size_t index,
|
||||
const char **path, sgui_file_type_t *type);
|
||||
|
||||
void sgui_list_sort(sgui_list_t *list);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - 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 "sgui.h"
|
||||
#include "list.h"
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define FONT_WIDTH 5
|
||||
#define FONT_HEIGHT 10
|
||||
#define FONT_WIDTH_STRIDE (FONT_WIDTH + 1)
|
||||
#define FONT_HEIGHT_STRIDE (FONT_HEIGHT + 1)
|
||||
|
||||
#define TERM_WIDTH (((SGUI_WIDTH - 30) / (FONT_WIDTH_STRIDE)))
|
||||
#define TERM_HEIGHT (((SGUI_HEIGHT - 30) / (FONT_HEIGHT_STRIDE)))
|
||||
#define TERM_START_X 15
|
||||
#define TERM_START_Y 15
|
||||
|
||||
struct sgui_handle
|
||||
{
|
||||
uint16_t *frame_buf;
|
||||
size_t frame_buf_pitch;
|
||||
const uint8_t *font_buf;
|
||||
|
||||
sgui_folder_enum_cb_t folder_cb;
|
||||
void *userdata;
|
||||
|
||||
sgui_list_t *path_stack;
|
||||
sgui_list_t *folder_buf;
|
||||
size_t directory_ptr;
|
||||
bool need_refresh;
|
||||
|
||||
char path_buf[PATH_MAX];
|
||||
|
||||
uint16_t font_white[256][FONT_HEIGHT][FONT_WIDTH];
|
||||
uint16_t font_green[256][FONT_HEIGHT][FONT_WIDTH];
|
||||
};
|
||||
|
||||
static void copy_glyph(uint16_t glyph_white[FONT_HEIGHT][FONT_WIDTH],
|
||||
uint16_t glyph_green[FONT_HEIGHT][FONT_WIDTH],
|
||||
const uint8_t *buf)
|
||||
{
|
||||
for (int y = 0; y < FONT_HEIGHT; y++)
|
||||
{
|
||||
for (int x = 0; x < FONT_WIDTH; x++)
|
||||
{
|
||||
uint32_t col =
|
||||
((uint32_t)buf[3 * (-y * 256 + x) + 0] << 0) |
|
||||
((uint32_t)buf[3 * (-y * 256 + x) + 1] << 8) |
|
||||
((uint32_t)buf[3 * (-y * 256 + x) + 2] << 16);
|
||||
|
||||
glyph_white[y][x] = col == 0xff ? 0 : 0x7fff;
|
||||
glyph_green[y][x] = col == 0xff ? 0 : (5 << 10) | (20 << 5) | (5 << 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_font(sgui_handle_t *sgui, const char *path)
|
||||
{
|
||||
for (unsigned i = 0; i < 256; i++)
|
||||
{
|
||||
unsigned y = i / 16;
|
||||
unsigned x = i % 16;
|
||||
copy_glyph(sgui->font_white[i],
|
||||
sgui->font_green[i],
|
||||
sgui->font_buf + 54 + 3 * (256 * (255 - 16 * y) + 16 * x));
|
||||
}
|
||||
}
|
||||
|
||||
sgui_handle_t *sgui_init(const char *base_path,
|
||||
uint16_t *buf, size_t buf_pitch,
|
||||
const uint8_t *font_buf,
|
||||
sgui_folder_enum_cb_t folder_cb, void *userdata)
|
||||
{
|
||||
sgui_handle_t *sgui = (sgui_handle_t*)calloc(1, sizeof(*sgui));
|
||||
|
||||
sgui->frame_buf = buf;
|
||||
sgui->frame_buf_pitch = buf_pitch;
|
||||
sgui->font_buf = font_buf;
|
||||
|
||||
sgui->folder_cb = folder_cb;
|
||||
sgui->userdata = userdata;
|
||||
|
||||
sgui->path_stack = sgui_list_new();
|
||||
sgui->folder_buf = sgui_list_new();
|
||||
sgui_list_push(sgui->path_stack, base_path, SGUI_FILE_DIRECTORY);
|
||||
|
||||
init_font(sgui, "font.bmp");
|
||||
|
||||
return sgui;
|
||||
}
|
||||
|
||||
void sgui_free(sgui_handle_t *sgui)
|
||||
{
|
||||
sgui_list_free(sgui->path_stack);
|
||||
sgui_list_free(sgui->folder_buf);
|
||||
free(sgui);
|
||||
}
|
||||
|
||||
static uint16_t gray_filler(unsigned x, unsigned y)
|
||||
{
|
||||
x >>= 1;
|
||||
y >>= 1;
|
||||
uint16_t col = ((x + y) & 1) + 1;
|
||||
col <<= 1;
|
||||
return (col << 0) | (col << 5) | (col << 10);
|
||||
}
|
||||
|
||||
static uint16_t green_filler(unsigned x, unsigned y)
|
||||
{
|
||||
x >>= 1;
|
||||
y >>= 1;
|
||||
uint16_t col = ((x + y) & 1) + 1;
|
||||
col <<= 1;
|
||||
return (col << 0) | (col << 6) | (col << 10);
|
||||
}
|
||||
|
||||
static void fill_rect(uint16_t *buf, unsigned pitch,
|
||||
unsigned x, unsigned y,
|
||||
unsigned width, unsigned height,
|
||||
uint16_t (*col)(unsigned x, unsigned y))
|
||||
{
|
||||
for (unsigned j = y; j < y + height; j++)
|
||||
for (unsigned i = x; i < x + width; i++)
|
||||
buf[j * (pitch >> 1) + i] = col(i, j);
|
||||
}
|
||||
|
||||
static void blit_line(sgui_handle_t *sgui,
|
||||
unsigned x, unsigned y, const char *message, bool green)
|
||||
{
|
||||
while (*message)
|
||||
{
|
||||
for (unsigned j = 0; j < FONT_HEIGHT; j++)
|
||||
{
|
||||
for (unsigned i = 0; i < FONT_WIDTH; i++)
|
||||
{
|
||||
uint16_t col = green ?
|
||||
sgui->font_green[(unsigned char)*message][j][i] :
|
||||
sgui->font_white[(unsigned char)*message][j][i];
|
||||
|
||||
if (col)
|
||||
sgui->frame_buf[(y + j) * (sgui->frame_buf_pitch >> 1) + (x + i)] = col;
|
||||
}
|
||||
}
|
||||
|
||||
x += FONT_WIDTH_STRIDE;
|
||||
message++;
|
||||
}
|
||||
}
|
||||
|
||||
static void render_text(sgui_handle_t *sgui, size_t begin, size_t end)
|
||||
{
|
||||
fill_rect(sgui->frame_buf, sgui->frame_buf_pitch,
|
||||
0, 0, SGUI_WIDTH, SGUI_HEIGHT, gray_filler);
|
||||
|
||||
fill_rect(sgui->frame_buf, sgui->frame_buf_pitch,
|
||||
5, 5, SGUI_WIDTH - 10, 5, green_filler);
|
||||
|
||||
fill_rect(sgui->frame_buf, sgui->frame_buf_pitch,
|
||||
5, SGUI_HEIGHT - 10, SGUI_WIDTH - 10, 5, green_filler);
|
||||
|
||||
fill_rect(sgui->frame_buf, sgui->frame_buf_pitch,
|
||||
5, 5, 5, SGUI_HEIGHT - 10, green_filler);
|
||||
|
||||
fill_rect(sgui->frame_buf, sgui->frame_buf_pitch,
|
||||
SGUI_WIDTH - 10, 5, 5, SGUI_HEIGHT - 10, green_filler);
|
||||
|
||||
unsigned x = TERM_START_X;
|
||||
unsigned y = TERM_START_Y;
|
||||
|
||||
for (size_t i = begin; i < end; i++, y += FONT_HEIGHT_STRIDE)
|
||||
{
|
||||
const char *path;
|
||||
sgui_file_type_t type;
|
||||
sgui_list_at(sgui->folder_buf, i, &path, &type);
|
||||
|
||||
char message[TERM_WIDTH + 1];
|
||||
snprintf(message, sizeof(message), "%c %-*s %6s\n",
|
||||
i == sgui->directory_ptr ? '>' : ' ',
|
||||
TERM_WIDTH - (6 + 1 + 2),
|
||||
path,
|
||||
type == SGUI_FILE_PLAIN ? "(FILE)" : "(DIR)");
|
||||
|
||||
blit_line(sgui, x, y, message, i == sgui->directory_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
const char *sgui_iterate(sgui_handle_t *sgui, sgui_action_t action)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case SGUI_ACTION_UP:
|
||||
if (sgui->directory_ptr > 0)
|
||||
sgui->directory_ptr--;
|
||||
break;
|
||||
|
||||
case SGUI_ACTION_DOWN:
|
||||
if (sgui->directory_ptr + 1 < sgui_list_size(sgui->folder_buf))
|
||||
sgui->directory_ptr++;
|
||||
break;
|
||||
|
||||
case SGUI_ACTION_LEFT:
|
||||
case SGUI_ACTION_CANCEL:
|
||||
if (sgui_list_size(sgui->path_stack) > 1)
|
||||
{
|
||||
sgui_list_pop(sgui->path_stack);
|
||||
sgui->need_refresh = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SGUI_ACTION_RIGHT:
|
||||
case SGUI_ACTION_OK:
|
||||
{
|
||||
if (sgui_list_size(sgui->folder_buf) == 0)
|
||||
return NULL;
|
||||
|
||||
const char *path = NULL;
|
||||
sgui_file_type_t type = SGUI_FILE_PLAIN;
|
||||
sgui_list_at(sgui->folder_buf, sgui->directory_ptr,
|
||||
&path, &type);
|
||||
|
||||
const char *dir;
|
||||
sgui_list_back(sgui->path_stack, &dir, NULL);
|
||||
|
||||
if (type == SGUI_FILE_DIRECTORY)
|
||||
{
|
||||
char cat_path[PATH_MAX];
|
||||
snprintf(cat_path, sizeof(cat_path), "%s/%s",
|
||||
strcmp(dir, "/") == 0 ? "" : dir, path);
|
||||
|
||||
sgui_list_push(sgui->path_stack, cat_path, SGUI_FILE_DIRECTORY);
|
||||
sgui->need_refresh = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(sgui->path_buf, sizeof(sgui->path_buf), "%s/%s",
|
||||
strcmp(dir, "/") == 0 ? "" : dir, path);
|
||||
return sgui->path_buf;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SGUI_ACTION_REFRESH:
|
||||
sgui->need_refresh = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sgui->need_refresh)
|
||||
{
|
||||
sgui->directory_ptr = 0;
|
||||
sgui_list_clear(sgui->folder_buf);
|
||||
|
||||
const char *path = NULL;
|
||||
sgui_list_back(sgui->path_stack, &path, NULL);
|
||||
|
||||
if (!sgui->folder_cb(path,
|
||||
(sgui_file_enum_cb_t)sgui_list_push,
|
||||
sgui->userdata, sgui->folder_buf))
|
||||
return NULL;
|
||||
|
||||
sgui_list_sort(sgui->folder_buf);
|
||||
|
||||
sgui->need_refresh = false;
|
||||
}
|
||||
|
||||
size_t begin = sgui->directory_ptr >= TERM_HEIGHT / 2 ?
|
||||
sgui->directory_ptr - TERM_HEIGHT / 2 : 0;
|
||||
size_t end = sgui->directory_ptr + TERM_HEIGHT <= sgui_list_size(sgui->folder_buf) ?
|
||||
sgui->directory_ptr + TERM_HEIGHT : sgui_list_size(sgui->folder_buf);
|
||||
|
||||
if (end - begin > TERM_HEIGHT)
|
||||
end = begin + TERM_HEIGHT;
|
||||
|
||||
#if 0
|
||||
printf("========================================\n");
|
||||
for (size_t i = begin; i < end; i++)
|
||||
{
|
||||
const char *path;
|
||||
sgui_file_type_t type;
|
||||
sgui_list_at(sgui->folder_buf, i, &path, &type);
|
||||
|
||||
printf("%c %-50s %s\n",
|
||||
i == sgui->directory_ptr ? '>' : ' ',
|
||||
path,
|
||||
type == SGUI_FILE_PLAIN ? "(FILE)" : "(DIR)");
|
||||
|
||||
}
|
||||
printf("========================================\n");
|
||||
#endif
|
||||
|
||||
render_text(sgui, begin, end);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - 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 SGUI_H__
|
||||
#define SGUI_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifndef __cplusplus
|
||||
#include <stdbool.h>
|
||||
#else
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SGUI_FILE_PLAIN,
|
||||
SGUI_FILE_DIRECTORY
|
||||
} sgui_file_type_t;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SGUI_ACTION_UP,
|
||||
SGUI_ACTION_DOWN,
|
||||
SGUI_ACTION_LEFT,
|
||||
SGUI_ACTION_RIGHT,
|
||||
SGUI_ACTION_OK,
|
||||
SGUI_ACTION_CANCEL,
|
||||
SGUI_ACTION_REFRESH,
|
||||
SGUI_ACTION_NOOP
|
||||
} sgui_action_t;
|
||||
|
||||
typedef struct sgui_handle sgui_handle_t;
|
||||
|
||||
typedef void (*sgui_file_enum_cb_t)(void *ctx, const char *path,
|
||||
sgui_file_type_t file_type);
|
||||
typedef bool (*sgui_folder_enum_cb_t)(const char *directory,
|
||||
sgui_file_enum_cb_t file_cb, void *userdata, void *ctx);
|
||||
|
||||
#define SGUI_WIDTH 320
|
||||
#define SGUI_HEIGHT 240
|
||||
|
||||
sgui_handle_t *sgui_init(const char *base_path,
|
||||
uint16_t *framebuf, size_t framebuf_pitch,
|
||||
const uint8_t *font_buf,
|
||||
sgui_folder_enum_cb_t folder_cb, void *userdata);
|
||||
|
||||
const char *sgui_iterate(sgui_handle_t *sgui, sgui_action_t action);
|
||||
|
||||
void sgui_free(sgui_handle_t *sgui);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Loaded 100 of 218 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user