mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 17:28:49 -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 | ||
|
|
2476e3dee3 | ||
|
|
c05a5c9fdd | ||
|
|
503c51d633 | ||
|
|
46054de4f8 | ||
|
|
604eb9b973 | ||
|
|
0e72120bdc | ||
|
|
b8064f5d42 | ||
|
|
379b14a0f3 | ||
|
|
2a598c0b97 | ||
|
|
aa124b4c40 | ||
|
|
bb5e31df0d | ||
|
|
e49d216884 | ||
|
|
598d06fcb5 | ||
|
|
7f13ec88e6 | ||
|
|
c5c60c6f53 | ||
|
|
81b0e6d0a3 | ||
|
|
da97b29d0b | ||
|
|
69b7f92285 | ||
|
|
3a9dbbc8b7 | ||
|
|
bf5db10459 | ||
|
|
4fa27a5f0f | ||
|
|
3e7c412738 | ||
|
|
88c870dcfa | ||
|
|
66e1179503 | ||
|
|
4ecea5d0a4 | ||
|
|
57816edfd3 | ||
|
|
17cf0985c5 | ||
|
|
776b0a1e2d | ||
|
|
4a6392aefa | ||
|
|
29dd4727b8 | ||
|
|
face42a888 | ||
|
|
45fa6795c3 | ||
|
|
a9d339efb4 | ||
|
|
60841b01a5 | ||
|
|
ad67511483 | ||
|
|
104fb4fb16 | ||
|
|
816ef095ed | ||
|
|
48e8472f9c | ||
|
|
1755500b19 | ||
|
|
4bfc827eb2 | ||
|
|
ddfeacbd83 | ||
|
|
79353f861c | ||
|
|
f3f4efe304 | ||
|
|
51721b4cb6 | ||
|
|
477091ce8f | ||
|
|
2707b87767 | ||
|
|
b3d422b5cd | ||
|
|
d1dc65f202 | ||
|
|
7815940a97 | ||
|
|
4f841c208d | ||
|
|
b4f0d9b8f8 | ||
|
|
b96ab2982c | ||
|
|
825c1de7d1 | ||
|
|
6ada4d87d3 | ||
|
|
53abc92322 | ||
|
|
a816082ac3 | ||
|
|
47e63f1c6b | ||
|
|
cdf3d05c8e | ||
|
|
389e9ba0dc | ||
|
|
a6f7a2311f | ||
|
|
f19d99d523 | ||
|
|
e7082fd6ea | ||
|
|
4806d2fe88 | ||
|
|
f85ff7124b | ||
|
|
0f010cf3d2 | ||
|
|
95636d4f04 | ||
|
|
43905e410e | ||
|
|
d23a7e3ddf | ||
|
|
652f4c5856 | ||
|
|
90febc4ecf | ||
|
|
34b5994842 | ||
|
|
0c3f6ed66b | ||
|
|
f3e487557e | ||
|
|
ee12266a2b | ||
|
|
1981b14de9 | ||
|
|
65665f36cc | ||
|
|
a48dc55c41 | ||
|
|
202a458524 | ||
|
|
b4de704a09 | ||
|
|
fef5edfac8 | ||
|
|
a6e74775b1 | ||
|
|
ee66a28d05 | ||
|
|
5e28d82af2 | ||
|
|
c2a06bfa09 | ||
|
|
66510e21aa | ||
|
|
ec5e3355b9 | ||
|
|
0522a2d6a0 | ||
|
|
fad89f9d2d | ||
|
|
97f937ec6d | ||
|
|
9647006a2b | ||
|
|
418ba455f8 | ||
|
|
197d62860b | ||
|
|
402451626f | ||
|
|
3739cbfc70 | ||
|
|
235355ffc7 | ||
|
|
4856d0cac3 | ||
|
|
77a2723bb0 | ||
|
|
41cd6e21c3 | ||
|
|
1fc99c7123 | ||
|
|
fddcc3447c | ||
|
|
70e9f084b7 | ||
|
|
824790a80d | ||
|
|
23d23983bc | ||
|
|
c42655ed91 | ||
|
|
f46ee78c76 | ||
|
|
5d1d188231 | ||
|
|
c55ce543c2 | ||
|
|
de8bc62474 | ||
|
|
397da887a7 | ||
|
|
f44733d7f6 | ||
|
|
5faa28a1a2 | ||
|
|
efaa85ff9b | ||
|
|
8f5580be10 | ||
|
|
c7acd99548 | ||
|
|
64ee13e6f4 | ||
|
|
e134f8afc7 | ||
|
|
dd89e6fbe0 | ||
|
|
a79a1b46f7 | ||
|
|
6467f51ea8 | ||
|
|
141eefb03b | ||
|
|
068c224d83 | ||
|
|
5bc8090ea7 | ||
|
|
466ab1ef8d | ||
|
|
a1c19ed1f5 | ||
|
|
cce4b7d20c | ||
|
|
80925ffc11 | ||
|
|
9eb14e328d | ||
|
|
5ae0cb1821 | ||
|
|
fc594f7f0f | ||
|
|
0c4e406ee7 | ||
|
|
a082ec5251 | ||
|
|
953b5daf0a | ||
|
|
b4ff622f32 | ||
|
|
f26b54c998 | ||
|
|
d7b3f39136 | ||
|
|
5bcf601a40 | ||
|
|
e91ebdac97 | ||
|
|
b2dd12d186 | ||
|
|
9c79bf0ba2 | ||
|
|
f48247a18b | ||
|
|
48e015558e | ||
|
|
0ff3cbc93b | ||
|
|
25f0e162a4 | ||
|
|
0810812cd9 | ||
|
|
15e0b721e6 | ||
|
|
9d12c3ac87 | ||
|
|
a7c3d8d17a | ||
|
|
8fbfdee011 | ||
|
|
bc999e72fa | ||
|
|
c7f8110670 | ||
|
|
b0dbc67281 | ||
|
|
4916f7b08b | ||
|
|
1058d94ec1 | ||
|
|
340dca3c6d | ||
|
|
4ae0f3da22 | ||
|
|
7d2d1ce49e | ||
|
|
fa26f6a10c | ||
|
|
db71a5cdba | ||
|
|
5058fdcd1a | ||
|
|
ef04f199d5 | ||
|
|
d26dd9d9e7 | ||
|
|
d2bc4096de | ||
|
|
08ca1a54d3 | ||
|
|
869eddfc1a | ||
|
|
fbced43f11 | ||
|
|
c996f816b8 | ||
|
|
33ecedb462 | ||
|
|
a58676c9e1 | ||
|
|
1ee9722e5f | ||
|
|
81250657f0 | ||
|
|
7d33cccc8e | ||
|
|
3db79ab2f0 | ||
|
|
ea0f6813d2 | ||
|
|
f6d4d32c03 | ||
|
|
892e82141f | ||
|
|
65ec7fae9c | ||
|
|
a0acb75a19 | ||
|
|
bcae973d82 | ||
|
|
d01a4fe61b | ||
|
|
8d4620b680 | ||
|
|
c2cedb1b6f | ||
|
|
157dcad87b | ||
|
|
8ea5ba171a | ||
|
|
fa876b10a3 | ||
|
|
3c58d10eae | ||
|
|
a3ee8c8849 | ||
|
|
5e14b302bc | ||
|
|
efe3f943d5 | ||
|
|
38e7fc6659 | ||
|
|
a1ceb534de | ||
|
|
0d897dd65d | ||
|
|
721d73f451 | ||
|
|
e8bd64e898 | ||
|
|
7da7ab2ce7 | ||
|
|
3f6183ff15 | ||
|
|
642316a1e2 | ||
|
|
21c6b75839 | ||
|
|
87d34deb0c | ||
|
|
1b3408919c | ||
|
|
23516572b7 | ||
|
|
924848fdad | ||
|
|
4c40e56060 | ||
|
|
cf34444505 | ||
|
|
9ca5e4e470 | ||
|
|
32f2689b6a | ||
|
|
13ae61031f | ||
|
|
1ced96c5e9 | ||
|
|
0d905eb3f6 | ||
|
|
32a5460ea7 | ||
|
|
be5acff69c | ||
|
|
1b0ef5793e | ||
|
|
2bd0caae2e | ||
|
|
3045f2c478 | ||
|
|
1282a142d6 | ||
|
|
ac1a539d22 | ||
|
|
6f47775239 | ||
|
|
0823d72dee | ||
|
|
407ad94f58 | ||
|
|
b2b103362d | ||
|
|
231680944f | ||
|
|
ec7dab05e8 | ||
|
|
9588f6061f | ||
|
|
57c25dcb0b | ||
|
|
38763c4554 | ||
|
|
fac2ea00dd | ||
|
|
74782a1bea | ||
|
|
c1265e7227 | ||
|
|
f0841256e8 | ||
|
|
600efab1bd | ||
|
|
dc9dd2e6bf | ||
|
|
de8fe0e88c | ||
|
|
338432c35f | ||
|
|
11aeb05a6e | ||
|
|
e28a32d922 | ||
|
|
edac39cb2b | ||
|
|
c1954aa4a6 | ||
|
|
ef048fcfd5 | ||
|
|
48a1531d7c | ||
|
|
4b4686ae04 | ||
|
|
cb4a54fefe | ||
|
|
131b2386cd | ||
|
|
4d6d01228c | ||
|
|
a050955113 | ||
|
|
b02789d280 | ||
|
|
6f92d0588d | ||
|
|
45df48ab67 | ||
|
|
aba5c7722e | ||
|
|
07a1a1ee2f | ||
|
|
3b952973de | ||
|
|
ac4adebdf6 | ||
|
|
1bfd9716c5 | ||
|
|
702cf9c804 | ||
|
|
5993fffb64 | ||
|
|
58758d2818 | ||
|
|
21f88fcf39 | ||
|
|
e3f029a446 | ||
|
|
0694d8884f | ||
|
|
0730a34a5b | ||
|
|
f827bb4cbd | ||
|
|
eafd8d91d7 | ||
|
|
56a4d1ddac | ||
|
|
8d3f15602c | ||
|
|
7877c7939d | ||
|
|
c55263b956 | ||
|
|
96ce288120 | ||
|
|
e5326467a1 | ||
|
|
596d8d9985 | ||
|
|
b4b0be9118 | ||
|
|
00e5f0fe97 | ||
|
|
cb140c3691 | ||
|
|
ad85ccb42f | ||
|
|
97e50f15de | ||
|
|
071c03e194 | ||
|
|
6f47c21ce9 | ||
|
|
68bfda24ff | ||
|
|
4f2fbcb9bc | ||
|
|
90ab5dfc2f | ||
|
|
362cbe5deb | ||
|
|
e133662346 | ||
|
|
1fe83ab25a | ||
|
|
1d95b5ee22 | ||
|
|
9dc603d405 | ||
|
|
5cb2863e6e | ||
|
|
98cf4223ab | ||
|
|
ff7843cbe9 | ||
|
|
c4d6b0b7c1 | ||
|
|
fb739621ac | ||
|
|
24e8ca6856 | ||
|
|
1ff9b6a375 | ||
|
|
cb6086635c | ||
|
|
bce679df78 | ||
|
|
22e8c3d895 | ||
|
|
d2b06a92c5 | ||
|
|
3901934d97 | ||
|
|
a76b25aff9 | ||
|
|
343852af26 | ||
|
|
4395f124ca | ||
|
|
b16ff3024c | ||
|
|
bc23b421e1 | ||
|
|
d9cb85a8d5 | ||
|
|
7d1cc23d16 | ||
|
|
4496ed3c13 | ||
|
|
3f5b6a450e | ||
|
|
9e24474047 | ||
|
|
7c17ede1ef | ||
|
|
5d9805c3e5 | ||
|
|
b17617bed5 | ||
|
|
a405708f1c | ||
|
|
594e66704d | ||
|
|
50579aaeea | ||
|
|
38eb855b48 | ||
|
|
12cc85c8a5 | ||
|
|
5b12595c85 | ||
|
|
3753f0bfca | ||
|
|
14ae5e397a | ||
|
|
657b4ce8c7 | ||
|
|
777e6a5334 | ||
|
|
a2ec78320b | ||
|
|
1fb53627ac | ||
|
|
fc2a39cd58 | ||
|
|
7425e1f879 | ||
|
|
6a260a57dd | ||
|
|
dadd794ece | ||
|
|
706a3738b5 | ||
|
|
9aef33aabd | ||
|
|
ac4219bcde | ||
|
|
b337856f80 | ||
|
|
079a0100c0 | ||
|
|
014c3c90e0 | ||
|
|
4759e3411f | ||
|
|
4a05e50636 | ||
|
|
72c070f338 | ||
|
|
776291a137 | ||
|
|
f3cdb95b04 | ||
|
|
033860ceb9 | ||
|
|
ab601ee3dc | ||
|
|
783ab7acd5 | ||
|
|
19a46b1f8b | ||
|
|
508cfdbda7 | ||
|
|
d81b9ee0a7 | ||
|
|
36c6c21194 | ||
|
|
1631e940f6 | ||
|
|
2eeb1fa6d1 | ||
|
|
26ac00ad76 | ||
|
|
bafc4f596f | ||
|
|
eee89dddb1 | ||
|
|
089b4fbba0 | ||
|
|
80727cef5a | ||
|
|
346a9f2b4e | ||
|
|
363d8ca5b4 | ||
|
|
06a574225d | ||
|
|
fa777b8356 | ||
|
|
7cf01318cb | ||
|
|
7c538274b4 | ||
|
|
72fb694e97 | ||
|
|
59c92dddc3 | ||
|
|
3f60fdd106 | ||
|
|
d221c480e6 | ||
|
|
59d9ff0d29 | ||
|
|
d279ea79e8 | ||
|
|
77cf9423cc | ||
|
|
60b94bbe43 | ||
|
|
b6272f9653 | ||
|
|
25fce1a6c6 | ||
|
|
59229f99c5 | ||
|
|
db3e1ae053 | ||
|
|
ec1401e657 | ||
|
|
f85fd50fa1 | ||
|
|
c39b0ab09b | ||
|
|
4b5ed0fa41 | ||
|
|
75d1155a23 | ||
|
|
753d2057bb | ||
|
|
b06d814d9c | ||
|
|
eb085a8068 | ||
|
|
45904ccd73 | ||
|
|
3b0606bbd6 | ||
|
|
0f1a76f932 | ||
|
|
d3e0140f01 | ||
|
|
ea7dded915 | ||
|
|
f187f3db29 | ||
|
|
3099a79bac | ||
|
|
7cdea39c95 | ||
|
|
95f6a5116a | ||
|
|
da94877155 | ||
|
|
b1feed9d2b | ||
|
|
a3816bffa1 | ||
|
|
612270ec9b | ||
|
|
54bc0a8d96 | ||
|
|
f761811189 | ||
|
|
0e964c5cc2 | ||
|
|
c34a944fcf | ||
|
|
3feade6b6e | ||
|
|
f79eafe449 | ||
|
|
111b159373 | ||
|
|
d744167865 | ||
|
|
a2fea86c92 | ||
|
|
5385806e74 | ||
|
|
99e85684db | ||
|
|
45727bf9f5 | ||
|
|
2a0abb0618 | ||
|
|
192e3303a1 | ||
|
|
b6051add6a | ||
|
|
24d0cda5fd | ||
|
|
d0ba59f8b5 | ||
|
|
99a1784aaf | ||
|
|
b6ff98cf18 | ||
|
|
2dcccc7cda | ||
|
|
d03855c655 | ||
|
|
69227bc1ca | ||
|
|
851debf1e4 | ||
|
|
8c1db1c407 | ||
|
|
576894e0e8 | ||
|
|
631c3ca393 | ||
|
|
fd70a1c875 | ||
|
|
30cb2e4c07 | ||
|
|
7d87cc2d24 | ||
|
|
0342dcb169 | ||
|
|
f339560550 | ||
|
|
11b58d8c69 | ||
|
|
3717f7442c | ||
|
|
e02d9f41e8 | ||
|
|
f62120861a | ||
|
|
884e2881a0 | ||
|
|
a605628dff | ||
|
|
4d96fbba6a | ||
|
|
9bfeb88e24 | ||
|
|
712f463022 | ||
|
|
713e8624a0 | ||
|
|
5c8a549125 | ||
|
|
c3e7a35b62 | ||
|
|
5bc9ca216e | ||
|
|
838a85a719 | ||
|
|
b880039541 | ||
|
|
c646166fe1 | ||
|
|
44c52ca4f8 | ||
|
|
9c9eee5d01 | ||
|
|
7bbaa6ee31 | ||
|
|
f24aed9358 | ||
|
|
02344f9048 | ||
|
|
200d2b598b | ||
|
|
3213bb13ea | ||
|
|
53fd0b3751 | ||
|
|
9b6e6a1215 | ||
|
|
e5a9d78b70 | ||
|
|
f22e964cc0 | ||
|
|
9fb827d238 | ||
|
|
24f802023a | ||
|
|
ef2b755e17 | ||
|
|
7cc8c01a1e | ||
|
|
ca509c5b9f | ||
|
|
12a39e160b | ||
|
|
7c5f36be61 | ||
|
|
aec44b093e | ||
|
|
ee83c31183 | ||
|
|
f2ad94f1c3 | ||
|
|
3aed471d57 | ||
|
|
3ee19ec75f | ||
|
|
b0d1a98cd1 | ||
|
|
94ff67ee15 | ||
|
|
8d2a7e4593 | ||
|
|
4e7a003497 | ||
|
|
380384654c | ||
|
|
2144ef84fd | ||
|
|
83d913b6d2 | ||
|
|
2782c9da91 | ||
|
|
2f539cd326 | ||
|
|
f4af518fb4 | ||
|
|
eebe0ae5d0 | ||
|
|
f9c5c7c8a5 | ||
|
|
8bc3d3ac5c | ||
|
|
6a3386ad1a | ||
|
|
5dced40c98 | ||
|
|
3f58932c2b | ||
|
|
373b9dcc37 | ||
|
|
993bdfb75f | ||
|
|
70000607a2 | ||
|
|
1b64e4b902 | ||
|
|
2166db91e2 | ||
|
|
4f50b6a749 | ||
|
|
676f659306 | ||
|
|
2f858b7948 | ||
|
|
36d9adbc0f | ||
|
|
9473b392df | ||
|
|
3bf050d5aa | ||
|
|
8a0ccc99df | ||
|
|
c938f3d8de | ||
|
|
d1c7b774f0 | ||
|
|
6b6877afc1 | ||
|
|
c370cba5b5 | ||
|
|
05d62a93c9 | ||
|
|
24cb2e6844 | ||
|
|
7f1cd62c8a | ||
|
|
5f5013871a | ||
|
|
aebe6bcd23 | ||
|
|
ac21fb81e2 | ||
|
|
74afe7f95b | ||
|
|
c8161a31d6 | ||
|
|
628217c6ae | ||
|
|
efed98d5b6 | ||
|
|
29addd605e | ||
|
|
0c121571b3 | ||
|
|
6244671b04 | ||
|
|
21b046cdd1 | ||
|
|
b32224d406 | ||
|
|
5d5865cfd8 | ||
|
|
4a78f2fef1 | ||
|
|
b521becd3c | ||
|
|
f10ed12b74 | ||
|
|
8d051ab413 | ||
|
|
4cc17a74f7 | ||
|
|
5ae4235545 | ||
|
|
8ceb8225ad | ||
|
|
20a3087d1c | ||
|
|
1f45dc512b | ||
|
|
13dee59fc8 | ||
|
|
5f78063449 | ||
|
|
ea6bd8c5c5 | ||
|
|
bdc85f2def | ||
|
|
efa731fc6d | ||
|
|
80d50b046d | ||
|
|
1855a7ea0d | ||
|
|
f5c11071be | ||
|
|
4b9e46ff06 | ||
|
|
75d618140b | ||
|
|
59c6de69b0 | ||
|
|
dc20c58189 | ||
|
|
c6eecf706a | ||
|
|
f2ee22d259 | ||
|
|
b48322222a | ||
|
|
bbf5a1ee72 | ||
|
|
c0851877bf | ||
|
|
51d96676af | ||
|
|
9f3b86e893 | ||
|
|
ac85037fc1 | ||
|
|
fa8e948e3e | ||
|
|
252cce07a6 | ||
|
|
17581106ba | ||
|
|
1bd8424fb4 | ||
|
|
23db525296 | ||
|
|
d679f8b24f | ||
|
|
d2f2ab6a9d | ||
|
|
3e1e08ca9a | ||
|
|
bb93f20be9 | ||
|
|
54dc289cde | ||
|
|
fcebad2589 | ||
|
|
4b60f98603 | ||
|
|
d304960c21 | ||
|
|
8da13b716a | ||
|
|
a6c08a24d7 | ||
|
|
6713272809 | ||
|
|
8d7cf23b40 | ||
|
|
d317a9773f | ||
|
|
e8806c48c9 | ||
|
|
abd7bab43a | ||
|
|
66464c2cce | ||
|
|
092fa9d531 | ||
|
|
1005d7fa9b | ||
|
|
079549cbd2 | ||
|
|
0f0a4d0a5e | ||
|
|
f0bbeb901d | ||
|
|
8027a25016 | ||
|
|
a1426c96f8 | ||
|
|
a4c901df57 | ||
|
|
f2cc19d1a7 | ||
|
|
ad55829890 | ||
|
|
6b8eea6363 | ||
|
|
b2d214ee79 | ||
|
|
f5fb113563 | ||
|
|
844a523706 | ||
|
|
d05379b340 | ||
|
|
006948b973 | ||
|
|
e5196026a1 | ||
|
|
66d18e4ad7 | ||
|
|
b539df1ccd | ||
|
|
c0117bfbb6 | ||
|
|
074234f182 | ||
|
|
bab03ba8c5 | ||
|
|
4878a283ed | ||
|
|
d3eed4f8c4 | ||
|
|
647db4d5de | ||
|
|
3455f83fa7 | ||
|
|
f730ba4043 | ||
|
|
caf87b1198 | ||
|
|
1706763a4c | ||
|
|
c1b8cdb35b | ||
|
|
936b7f5395 | ||
|
|
a01e3347f7 | ||
|
|
142411c08c | ||
|
|
29808c4421 | ||
|
|
48dcd174a7 | ||
|
|
6c85e6e702 | ||
|
|
e484e22e2f | ||
|
|
8ef54de812 | ||
|
|
c1d5a6e140 | ||
|
|
ea153f9df2 | ||
|
|
1ba9854070 | ||
|
|
012e955a05 | ||
|
|
37c0b49a99 | ||
|
|
de30ce9ef5 | ||
|
|
027a21278e | ||
|
|
f77fe62ab0 | ||
|
|
d42c35e1aa | ||
|
|
d5761b6385 | ||
|
|
bb3ab3b49c | ||
|
|
0441725045 | ||
|
|
ac4e6e89a4 | ||
|
|
12d21cf7a6 | ||
|
|
3fda3effda | ||
|
|
7e07b821aa | ||
|
|
e502a8b566 | ||
|
|
ec51ceb010 | ||
|
|
3fa037daee | ||
|
|
46c6955cfe | ||
|
|
252425c35c | ||
|
|
d309d472e1 | ||
|
|
898c4e8c6c | ||
|
|
56ac7b4649 | ||
|
|
9dd05b7101 | ||
|
|
e2daab9ce9 | ||
|
|
ef19ea7a85 | ||
|
|
01cf24f15f | ||
|
|
f870080497 | ||
|
|
c5942ec5a2 | ||
|
|
50d78fb804 | ||
|
|
8053aca3c3 | ||
|
|
0f3171edea | ||
|
|
6b478b9dda | ||
|
|
caaa9cea40 | ||
|
|
ea7f8f5552 | ||
|
|
69caca6017 | ||
|
|
703cfadf63 | ||
|
|
adfd461c3e | ||
|
|
bb79dbaa98 | ||
|
|
394de09907 | ||
|
|
560cafccaf | ||
|
|
0b09032164 | ||
|
|
1ee7162784 | ||
|
|
4949a131f8 | ||
|
|
8b241c3deb | ||
|
|
78c457b996 | ||
|
|
0be1b0b88b | ||
|
|
3ef0276b2a | ||
|
|
42d19f1a35 | ||
|
|
1cf9381cb0 | ||
|
|
f94dc125e5 | ||
|
|
96b978d55a | ||
|
|
ec367d333d | ||
|
|
9535097894 | ||
|
|
b541c27226 | ||
|
|
41b2af66d5 | ||
|
|
84b2b9c7a5 | ||
|
|
52c8d96dca | ||
|
|
8c6519a53b | ||
|
|
336adb9f85 | ||
|
|
7053006e77 | ||
|
|
bbf4804d12 | ||
|
|
7c16424ba3 | ||
|
|
de9dc686ac | ||
|
|
069e9b574f | ||
|
|
d5319a879a | ||
|
|
f64880aee1 | ||
|
|
fed1857918 | ||
|
|
9aeb40155e | ||
|
|
e49aeb1071 | ||
|
|
13f8ec6f3b | ||
|
|
f75c6df270 | ||
|
|
60b0afce95 | ||
|
|
f24653768f | ||
|
|
f135972a6b | ||
|
|
42c68e21a2 | ||
|
|
e8865060ca | ||
|
|
b07827a856 | ||
|
|
0735398e64 | ||
|
|
ec2d10e069 | ||
|
|
4cdb6151e5 | ||
|
|
e482b1bd18 | ||
|
|
9be3c13a07 | ||
|
|
8a4bcacf88 | ||
|
|
6b9b37b528 | ||
|
|
8e87c1ff19 | ||
|
|
aa648c1a49 | ||
|
|
056a590671 | ||
|
|
97f2503794 | ||
|
|
143079efbe | ||
|
|
7bcc173c5f | ||
|
|
0485f783b3 | ||
|
|
f3c07325bd | ||
|
|
e8eabb0102 | ||
|
|
b73f5e77ff | ||
|
|
d07712b308 | ||
|
|
89ba64d27f | ||
|
|
1a6f112d8a | ||
|
|
70e8681487 | ||
|
|
17ae0f72df | ||
|
|
466485e62e | ||
|
|
f6ee0845f2 | ||
|
|
df1eee1a74 | ||
|
|
46b46ba0b1 | ||
|
|
a763848644 | ||
|
|
9ba2d6c9fa | ||
|
|
fd1367e257 | ||
|
|
85dbc05ded | ||
|
|
1eea7786bc | ||
|
|
d039bedad4 | ||
|
|
ad6719426c | ||
|
|
719e4942ce | ||
|
|
ad9a08aea6 | ||
|
|
604fe709d5 | ||
|
|
81e8368099 | ||
|
|
2137ca8e3f | ||
|
|
672824539c | ||
|
|
1915be5aea | ||
|
|
bdea56370d | ||
|
|
e5e0c33ed6 | ||
|
|
d0c10e7523 | ||
|
|
91d2cea259 | ||
|
|
d9e2072733 | ||
|
|
820b124165 | ||
|
|
caf3eb7b05 | ||
|
|
1b5e63f024 | ||
|
|
93202f191c | ||
|
|
3033fb6791 | ||
|
|
87589609bf | ||
|
|
757514242d | ||
|
|
359d228632 | ||
|
|
24dda40863 | ||
|
|
6c0eebd4bf | ||
|
|
31db2a30bb | ||
|
|
5e241d79c8 | ||
|
|
96389ef83b | ||
|
|
af010712f3 | ||
|
|
c2896db558 | ||
|
|
57eebebcdd | ||
|
|
9ad9cbd7de | ||
|
|
1b5adde885 | ||
|
|
ca35dd79d2 | ||
|
|
491f2e3e76 | ||
|
|
b6591e21cd | ||
|
|
67ed89729d | ||
|
|
6033e75dc4 | ||
|
|
5f8993cb83 | ||
|
|
2446300dd9 | ||
|
|
b97c45df78 | ||
|
|
9034e82298 | ||
|
|
ae395f05e1 | ||
|
|
0c1d359091 | ||
|
|
dd8a9c67c9 | ||
|
|
de331bcd9e | ||
|
|
a81d6bc247 | ||
|
|
47d0b12d40 | ||
|
|
849e9aa734 | ||
|
|
351e098da1 | ||
|
|
c5f825eaa5 | ||
|
|
d0a4102efa | ||
|
|
3aa4181380 | ||
|
|
43dc30c3a5 | ||
|
|
98e6613df0 | ||
|
|
3ce07891e4 | ||
|
|
2ff47ab7c4 | ||
|
|
8ba639c7d5 | ||
|
|
e0403e4dae | ||
|
|
7776525c96 | ||
|
|
25fe9b7f58 | ||
|
|
fff7aba8ec | ||
|
|
e83f3ed444 | ||
|
|
334f11d5bc | ||
|
|
5bcde44684 | ||
|
|
2459793f9c | ||
|
|
cadd7b9728 | ||
|
|
644f247026 | ||
|
|
8c1ef3051d | ||
|
|
d296e15f26 | ||
|
|
5a5cd46f9b | ||
|
|
20d9345a4e |
No files matched your search
+13
-1
@@ -1,3 +1,15 @@
|
||||
*.o
|
||||
ssnes
|
||||
*.bmpobj
|
||||
*.so
|
||||
*.dll
|
||||
/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
|
||||
@@ -3,3 +3,11 @@ Hans-Kristian Arntzen - <[email protected]>
|
||||
|
||||
Devin J. Pohly - <[email protected]>
|
||||
- Joypad axis support
|
||||
|
||||
Chris Moeller - <[email protected]>
|
||||
- CoreAudio audio driver
|
||||
|
||||
Daniel De Matteis - <[email protected]>
|
||||
- PlayStation 3 port
|
||||
- XBox 360 XDK port
|
||||
|
||||
@@ -2,51 +2,122 @@ include config.mk
|
||||
|
||||
TARGET = ssnes tools/ssnes-joyconfig
|
||||
|
||||
OBJ = ssnes.o file.o driver.o settings.o dynamic.o message.o
|
||||
JOYCONFIG_OBJ = tools/ssnes-joyconfig.o conf/config_file.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 =
|
||||
DEFINES = -DHAVE_CONFIG_H
|
||||
LIBS = -lm
|
||||
DEFINES = -DHAVE_CONFIG_H -DHAVE_SCREENSHOTS
|
||||
|
||||
ifeq ($(HAVE_SRC), 1)
|
||||
LIBS += $(SRC_LIBS)
|
||||
DEFINES += $(SRC_CFLAGS)
|
||||
ifeq ($(REENTRANT_TEST), 1)
|
||||
DEFINES += -Dmain=ssnes_main
|
||||
OBJ += console/test.o
|
||||
endif
|
||||
|
||||
ifneq ($(findstring Darwin,$(OS)),)
|
||||
OSX := 1
|
||||
LIBS += -framework AppKit
|
||||
else
|
||||
OSX := 0
|
||||
endif
|
||||
|
||||
BSD_LOCAL_INC =
|
||||
DYLIB_LIB = -ldl
|
||||
ifneq ($(findstring BSD,$(OS)),)
|
||||
BSD_LOCAL_INC = -I/usr/local/include
|
||||
DYLIB_LIB = -lc
|
||||
endif
|
||||
|
||||
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
|
||||
|
||||
ifeq ($(HAVE_RSOUND), 1)
|
||||
OBJ += audio/rsound.o
|
||||
LIBS += -lrsound
|
||||
LIBS += $(RSOUND_LIBS)
|
||||
DEFINES += $(RSOUND_CFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_OSS), 1)
|
||||
OBJ += audio/oss.o
|
||||
endif
|
||||
ifeq ($(HAVE_OSS_BSD), 1)
|
||||
OBJ += audio/oss.o
|
||||
endif
|
||||
ifeq ($(HAVE_OSS_LIB), 1)
|
||||
LIBS += -lossaudio
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_ALSA), 1)
|
||||
OBJ += audio/alsa.o
|
||||
LIBS += -lasound
|
||||
LIBS += $(ALSA_LIBS)
|
||||
DEFINES += $(ALSA_CFLAGS)
|
||||
endif
|
||||
ifeq ($(HAVE_ROAR), 1)
|
||||
OBJ += audio/roar.o
|
||||
LIBS += -lroar
|
||||
LIBS += $(ROAR_LIBS)
|
||||
DEFINES += $(ROAR_CFLAGS)
|
||||
endif
|
||||
ifeq ($(HAVE_AL), 1)
|
||||
OBJ += audio/openal.o
|
||||
ifeq ($(OSX),1)
|
||||
LIBS += -framework OpenAL
|
||||
else
|
||||
LIBS += -lopenal
|
||||
endif
|
||||
endif
|
||||
ifeq ($(HAVE_JACK),1)
|
||||
OBJ += audio/jack.o
|
||||
LIBS += -ljack
|
||||
LIBS += $(JACK_LIBS)
|
||||
DEFINES += $(JACK_CFLAGS)
|
||||
endif
|
||||
ifeq ($(HAVE_PULSE), 1)
|
||||
OBJ += audio/pulse.o
|
||||
LIBS += $(PULSE_LIBS)
|
||||
DEFINES += $(PULSE_CFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_COREAUDIO), 1)
|
||||
OBJ += audio/coreaudio.o
|
||||
LIBS += -framework CoreServices -framework CoreAudio -framework AudioUnit
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL), 1)
|
||||
OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
|
||||
LIBS += $(SDL_LIBS) -lGL
|
||||
DEFINES += $(SDL_CFLAGS)
|
||||
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)
|
||||
|
||||
ifeq ($(HAVE_OPENGL), 1)
|
||||
OBJ += gfx/gl.o
|
||||
ifeq ($(OSX),1)
|
||||
LIBS += -framework OpenGL
|
||||
else
|
||||
LIBS += -lGL
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XVIDEO), 1)
|
||||
OBJ += gfx/xvideo.o input/x11_input.o
|
||||
LIBS += $(XVIDEO_LIBS) $(X11_LIBS) $(XEXT_LIBS)
|
||||
DEFINES += $(XVIDEO_CFLAGS) $(X11_CFLAGS) $(XEXT_CFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CG), 1)
|
||||
@@ -55,17 +126,24 @@ ifeq ($(HAVE_CG), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XML), 1)
|
||||
OBJ += gfx/shader_glsl.o
|
||||
OBJ += cheats.o
|
||||
LIBS += $(XML_LIBS)
|
||||
DEFINES += $(XML_CFLAGS)
|
||||
|
||||
ifeq ($(HAVE_OPENGL), 1)
|
||||
OBJ += gfx/shader_glsl.o
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FILTER), 1)
|
||||
OBJ += hqflt/hq.o
|
||||
OBJ += hqflt/grayscale.o
|
||||
OBJ += hqflt/bleed.o
|
||||
OBJ += hqflt/ntsc.o
|
||||
OBJ += hqflt/snes_ntsc/snes_ntsc.o
|
||||
ifeq ($(HAVE_XML), 1)
|
||||
OBJ += gfx/snes_state.o gfx/image.o
|
||||
else ifeq ($(HAVE_CG), 1)
|
||||
OBJ += gfx/snes_state.o gfx/image.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
OBJ += gfx/ext_gfx.o audio/ext_audio.o
|
||||
LIBS += $(DYLIB_LIB)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FREETYPE), 1)
|
||||
@@ -74,23 +152,54 @@ ifeq ($(HAVE_FREETYPE), 1)
|
||||
DEFINES += $(FREETYPE_CFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL_IMAGE), 1)
|
||||
LIBS += $(SDL_IMAGE_LIBS)
|
||||
DEFINES += $(SDL_IMAGE_CFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FFMPEG), 1)
|
||||
OBJ += record/ffemu.o
|
||||
LIBS += $(AVCODEC_LIBS) $(AVCORE_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS)
|
||||
DEFINES += $(AVCODEC_CFLAGS) $(AVCORE_CFLAGS) $(AVFORMAT_CFLAGS) $(AVUTIL_CFLAGS) $(SWSCALE_CFLAGS)
|
||||
LIBS += $(AVCODEC_LIBS) $(AVFORMAT_LIBS) $(AVUTIL_LIBS) $(SWSCALE_LIBS)
|
||||
DEFINES += $(AVCODEC_CFLAGS) $(AVFORMAT_CFLAGS) $(AVUTIL_CFLAGS) $(SWSCALE_CFLAGS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYNAMIC), 1)
|
||||
LIBS += -ldl
|
||||
LIBS += $(DYLIB_LIB)
|
||||
else
|
||||
LIBS += $(libsnes)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_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
|
||||
|
||||
CFLAGS = -Wall -O3 -g -std=gnu99 -I.
|
||||
OPTIMIZE_FLAG = -O3
|
||||
ifeq ($(DEBUG), 1)
|
||||
OPTIMIZE_FLAG = -O0
|
||||
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
|
||||
|
||||
@@ -99,11 +208,15 @@ config.mk: configure qb/*
|
||||
@exit 1
|
||||
|
||||
ssnes: $(OBJ)
|
||||
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS)
|
||||
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) $(LIBRARY_DIRS)
|
||||
@$(if $(Q), $(shell echo echo LD $@),)
|
||||
|
||||
tools/ssnes-joyconfig: $(JOYCONFIG_OBJ)
|
||||
$(Q)$(CC) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS)
|
||||
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)
|
||||
@@ -111,26 +224,30 @@ tools/ssnes-joyconfig: $(JOYCONFIG_OBJ)
|
||||
@$(if $(Q), $(shell echo echo CC $<),)
|
||||
|
||||
install: $(TARGET)
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/bin
|
||||
mkdir -p $(DESTDIR)/etc
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/bin 2>/dev/null || /bin/true
|
||||
mkdir -p $(DESTDIR)/etc 2>/dev/null || /bin/true
|
||||
mkdir -p $(DESTDIR)$(PREFIX)/share/man/man1 2>/dev/null || /bin/true
|
||||
install -m755 $(TARGET) $(DESTDIR)$(PREFIX)/bin
|
||||
install -m644 ssnes.cfg $(DESTDIR)/etc/ssnes.cfg
|
||||
install -m644 docs/{ssnes,ssnes-joyconfig}.1 $(DESTDIR)$(PREFIX)/share/man/man1
|
||||
install -m644 docs/ssnes.1 $(DESTDIR)$(PREFIX)/share/man/man1
|
||||
install -m644 docs/ssnes-joyconfig.1 $(DESTDIR)$(PREFIX)/share/man/man1
|
||||
install -m755 ssnes-zip $(DESTDIR)$(PREFIX)/bin
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/{ssnes,ssnes-joyconfig}
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/ssnes
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/ssnes-joyconfig
|
||||
rm -f $(DESTDIR)$(PREFIX)/bin/ssnes-zip
|
||||
rm -f $(DESTDIR)/etc/ssnes.cfg
|
||||
rm -f $(DESTDIR)$(PREFIX)/share/man/man1/{ssnes,ssnes-joyconfig}.1
|
||||
rm -f $(DESTDIR)$(PREFIX)/share/man/man1/ssnes.1
|
||||
rm -f $(DESTDIR)$(PREFIX)/share/man/man1/ssnes-joyconfig.1
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f audio/*.o
|
||||
rm -f conf/*.o
|
||||
rm -f gfx/*.o
|
||||
rm -f gfx/py_state/*.o
|
||||
rm -f record/*.o
|
||||
rm -f hqflt/*.o
|
||||
rm -f hqflt/snes_ntsc/*.o
|
||||
rm -f input/*.o
|
||||
rm -f tools/*.o
|
||||
rm -f $(TARGET)
|
||||
|
||||
+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
|
||||
|
||||
+212
@@ -0,0 +1,212 @@
|
||||
TARGET = ssnes.exe
|
||||
JTARGET = ssnes-joyconfig.exe
|
||||
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++
|
||||
|
||||
HAVE_DINPUT = 1
|
||||
HAVE_XAUDIO = 1
|
||||
HAVE_DSOUND = 1
|
||||
HAVE_SDL = 1
|
||||
HAVE_OPENGL = 1
|
||||
HAVE_DYLIB = 1
|
||||
HAVE_NETPLAY = 1
|
||||
HAVE_THREADS = 1
|
||||
DYNAMIC = 1
|
||||
|
||||
ifeq ($(SLIM),)
|
||||
HAVE_SDL_IMAGE = 1
|
||||
HAVE_XML = 1
|
||||
HAVE_FREETYPE = 1
|
||||
HAVE_RSOUND = 1
|
||||
HAVE_FBO = 1
|
||||
HAVE_CG = 1
|
||||
HAVE_PYTHON = 1
|
||||
HAVE_FFMPEG = 1
|
||||
endif
|
||||
|
||||
libsnes ?= -lsnes
|
||||
|
||||
LIBS = -lm
|
||||
DEFINES = -I. -DHAVE_CONFIGFILE -DHAVE_SCREENSHOTS -DHAVE_BSV_MOVIE -DPACKAGE_VERSION=\"0.9.5\"
|
||||
LDFLAGS = -L. -static-libgcc
|
||||
LDCXXFLAGS = -s
|
||||
|
||||
ifeq ($(TDM_GCC),)
|
||||
LDCXXFLAGS += -static-libstdc++
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL), 1)
|
||||
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
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL_IMAGE), 1)
|
||||
LIBS += -lSDL_image
|
||||
DEFINES += -DHAVE_SDL_IMAGE
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CG), 1)
|
||||
OBJ += gfx/shader_cg.o
|
||||
LIBS += -lcg -lcgGL
|
||||
DEFINES += -DHAVE_CG
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XAUDIO), 1)
|
||||
OBJ += audio/xaudio.o audio/xaudio-c/xaudio-c.o
|
||||
DEFINES += -DHAVE_XAUDIO
|
||||
LIBS += -lole32
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DSOUND), 1)
|
||||
OBJ += audio/dsound.o
|
||||
DEFINES += -DHAVE_DSOUND
|
||||
LIBS += -ldxguid -ldsound
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_RSOUND), 1)
|
||||
OBJ += audio/rsound.o
|
||||
DEFINES += -DHAVE_RSOUND
|
||||
LIBS += -lrsound
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XML), 1)
|
||||
OBJ += gfx/shader_glsl.o gfx/image.o gfx/snes_state.o cheats.o
|
||||
DEFINES += -Ilibxml2 -DHAVE_XML
|
||||
LIBS += -lxml2 -liconv
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
DEFINES += -DHAVE_DYLIB
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_NETPLAY), 1)
|
||||
DEFINES += -DHAVE_NETPLAY
|
||||
OBJ += netplay.o
|
||||
LIBS += -lws2_32
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FREETYPE), 1)
|
||||
OBJ += gfx/fonts.o
|
||||
DEFINES += -DHAVE_FREETYPE -Ifreetype2
|
||||
LIBS += -lfreetype -lz
|
||||
endif
|
||||
|
||||
ifeq ($(DYNAMIC), 1)
|
||||
DEFINES += -DHAVE_DYNAMIC
|
||||
else
|
||||
LIBS += $(libsnes)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FBO), 1)
|
||||
DEFINES += -DHAVE_FBO
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DYLIB), 1)
|
||||
OBJ += gfx/ext_gfx.o audio/ext_audio.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_PYTHON), 1)
|
||||
LIBS += -lpython32
|
||||
DEFINES += -DHAVE_PYTHON -Ipython
|
||||
OBJ += gfx/py_state/py_state.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_DINPUT), 1)
|
||||
LIBS += -ldinput8 -ldxguid -lole32
|
||||
DEFINES += -DHAVE_DINPUT
|
||||
OBJ += input/dinput.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FFMPEG), 1)
|
||||
LIBS += -lavformat -lavcodec -lavutil -lswscale -lws2_32 -lz
|
||||
DEFINES += -DHAVE_FFMPEG -Iffmpeg
|
||||
DEFINES += -DHAVE_FFMPEG_ALLOC_CONTEXT3
|
||||
DEFINES += -DHAVE_FFMPEG_AVCODEC_OPEN2
|
||||
DEFINES += -DHAVE_FFMPEG_AVIO_OPEN
|
||||
DEFINES += -DHAVE_FFMPEG_AVFORMAT_WRITE_HEADER
|
||||
DEFINES += -DHAVE_FFMPEG_AVFORMAT_NEW_STREAM
|
||||
DEFINES += -DHAVE_X264RGB
|
||||
OBJ += record/ffemu.o
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SINC), 1)
|
||||
OBJ += audio/sinc.o
|
||||
else
|
||||
OBJ += audio/hermite.o
|
||||
endif
|
||||
|
||||
ifneq ($(V), 1)
|
||||
Q := @
|
||||
endif
|
||||
|
||||
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)
|
||||
|
||||
$(TARGET): $(OBJ)
|
||||
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS) $(LDCXXFLAGS)
|
||||
@$(if $(Q), $(shell echo echo LD $@),)
|
||||
|
||||
%.o: %.c
|
||||
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
@$(if $(Q), $(shell echo echo CC $<),)
|
||||
|
||||
$(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:
|
||||
rm -f *.o
|
||||
rm -f audio/*.o
|
||||
rm -f audio/xaudio-c/*.o
|
||||
rm -f conf/*.o
|
||||
rm -f gfx/*.o
|
||||
rm -f gfx/py_state/*.o
|
||||
rm -f record/*.o
|
||||
rm -f input/*.o
|
||||
rm -f $(TARGET)
|
||||
rm -f ssnes-joyconfig.exe
|
||||
rm -f tools/*.o
|
||||
|
||||
dist_x86: all
|
||||
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.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
|
||||
unzip SSNES-win32-libs.zip
|
||||
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-headers.zip --no-check-certificate
|
||||
unzip SSNES-win32-headers.zip
|
||||
|
||||
libs_x86_64:
|
||||
wget https://github.com/downloads/Themaister/SSNES/SSNES-win64-libs.zip --no-check-certificate
|
||||
unzip SSNES-win64-libs.zip
|
||||
wget https://github.com/downloads/Themaister/SSNES/SSNES-win64-headers.zip --no-check-certificate
|
||||
unzip SSNES-win64-headers.zip
|
||||
|
||||
.PHONY: all install uninstall clean dist_x86 dist_x86_64 libs_x86 libs_x86_64
|
||||
@@ -1,93 +0,0 @@
|
||||
TARGET = ssnes.exe
|
||||
JTARGET = ssnes-joyconfig.exe
|
||||
OBJ = ssnes.o file.o driver.o conf/config_file.o settings.o dynamic.o message.o
|
||||
JOBJ = conf/config_file.o tools/main-stub.o tools/ssnes-joyconfig.o
|
||||
|
||||
CC = gcc
|
||||
CXX = g++
|
||||
|
||||
HAVE_SRC = 1
|
||||
HAVE_SDL = 1
|
||||
HAVE_XML = 1
|
||||
HAVE_FREETYPE = 1
|
||||
libsnes ?= -lsnes
|
||||
|
||||
LIBS =
|
||||
DEFINES = -I. -DHAVE_CONFIGFILE
|
||||
LDFLAGS = -L. -static-libgcc -s
|
||||
|
||||
SRC_LIBS = -lsamplerate-0
|
||||
SDL_LIBS = -lSDL
|
||||
SDL_CFLAGS = -ISDL
|
||||
|
||||
ifeq ($(HAVE_SRC), 1)
|
||||
LIBS += $(SRC_LIBS)
|
||||
DEFINES += $(SRC_CFLAGS) -DHAVE_SRC
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_SDL), 1)
|
||||
OBJ += gfx/gl.o input/sdl.o audio/sdl.o audio/buffer.o
|
||||
LIBS += $(SDL_LIBS) -lopengl32
|
||||
DEFINES += $(SDL_CFLAGS) -DHAVE_SDL
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_XML), 1)
|
||||
OBJ += gfx/shader_glsl.o
|
||||
DEFINES += $(XML_CFLAGS) -DHAVE_XML
|
||||
LIBS += -lxml2
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_FREETYPE), 1)
|
||||
OBJ += gfx/fonts.o
|
||||
DEFINES += -DHAVE_FREETYPE -Ifreetype2
|
||||
LIBS += -lz -lfreetype6
|
||||
endif
|
||||
|
||||
ifeq ($(DYNAMIC), 1)
|
||||
DEFINES += -DHAVE_DYNAMIC
|
||||
else
|
||||
LIBS += $(libsnes)
|
||||
endif
|
||||
|
||||
ifneq ($(V),1)
|
||||
Q := @
|
||||
endif
|
||||
|
||||
|
||||
CFLAGS = -Wall -O3 -std=gnu99 -I.
|
||||
|
||||
all: $(TARGET) $(JTARGET)
|
||||
|
||||
$(TARGET): $(OBJ)
|
||||
$(Q)$(CXX) -o $@ $(OBJ) $(LIBS) $(LDFLAGS)
|
||||
@$(if $(Q), $(shell echo echo LD $@),)
|
||||
|
||||
%.o: %.c
|
||||
$(Q)$(CC) $(CFLAGS) $(DEFINES) -c -o $@ $<
|
||||
@$(if $(Q), $(shell echo echo CC $<),)
|
||||
|
||||
$(JTARGET): $(JOBJ)
|
||||
$(Q)$(CC) -o $@ $(JOBJ) $(SDL_LIBS) $(LDFLAGS)
|
||||
@$(if $(Q), $(shell echo echo LD $@),)
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f audio/*.o
|
||||
rm -f conf/*.o
|
||||
rm -f gfx/*.o
|
||||
rm -f record/*.o
|
||||
rm -f hqflt/*.o
|
||||
rm -f input/*.o
|
||||
rm -f hqflt/snes_ntsc/*.o
|
||||
rm -f $(TARGET)
|
||||
rm -f ssnes-joyconfig.exe
|
||||
rm -f tools/*.o
|
||||
|
||||
dist: all
|
||||
zip -r ssnes-win32-0.2.zip $(TARGET) ssnes.cfg snes.dll libxml2.dll iconv.dll zlib1.dll SDL.dll libsamplerate-0.dll freetype6.dll $(JTARGET)
|
||||
|
||||
libs:
|
||||
wget https://github.com/downloads/Themaister/SSNES/SSNES-win32-libs.zip --no-check-certificate
|
||||
unzip SSNES-win32-libs.zip
|
||||
|
||||
.PHONY: all install uninstall clean dist libs
|
||||
@@ -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
|
||||
|
||||
@@ -7,26 +7,38 @@ SSNES is a simple frontend for the libsnes library.
|
||||
libsnes is the emulator core of [bSNES](http://www.byuu.org), the most accurate SNES emulator to date, in library form. The libsnes API has been implemented by SNES9x as well, allowing multiple different cores to be used for an emulator that supports libsnes.
|
||||
This enables the possibility of custom front-ends for the emulator.
|
||||
|
||||
# Binaries
|
||||
|
||||
Latest Windows binaries are currently hosted on my [homepage](http://themaister.net/ssnes.html).
|
||||
|
||||
# Philosophy
|
||||
|
||||
SSNES attempts to be very small and lean, while still having all the useful core features expected from an emulator.
|
||||
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:
|
||||
|
||||
- [libsnes](http://byuu.org/bsnes/)
|
||||
- SDL
|
||||
- libsamplerate
|
||||
|
||||
SSNES can utilize these libraries if enabled:
|
||||
|
||||
- nvidia-cg-toolkit
|
||||
- libxml2 (bSNES XML shaders)
|
||||
- libfreetype2 (TTF font rendering on screen)
|
||||
- libsamplerate
|
||||
|
||||
SSNES needs one of these audio driver libraries:
|
||||
SSNES needs at least one of these audio driver libraries:
|
||||
|
||||
- ALSA
|
||||
- OSS
|
||||
@@ -35,6 +47,8 @@ SSNES needs one of these audio driver libraries:
|
||||
- OpenAL
|
||||
- JACK
|
||||
- SDL
|
||||
- XAudio2 (Win32)
|
||||
- PulseAudio
|
||||
|
||||
# Building libsnes
|
||||
|
||||
@@ -80,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
|
||||
|
||||
|
||||
+58
-45
@@ -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 "driver.h"
|
||||
#include "../driver.h"
|
||||
#include <stdlib.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "general.h"
|
||||
#include <asoundlib.h>
|
||||
#include "../general.h"
|
||||
|
||||
#define TRY_ALSA(x) if ( x < 0 ) { \
|
||||
#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)
|
||||
static bool alsa_use_float(void *data)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
alsa_t *alsa = (alsa_t*)data;
|
||||
return alsa->has_float;
|
||||
}
|
||||
|
||||
@@ -49,27 +51,30 @@ static bool find_float_format(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void* __alsa_init(const char* device, int rate, int latency)
|
||||
static void *alsa_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
alsa_t *alsa = calloc(1, sizeof(alsa_t));
|
||||
if ( alsa == NULL )
|
||||
alsa_t *alsa = (alsa_t*)calloc(1, sizeof(alsa_t));
|
||||
if (!alsa)
|
||||
return NULL;
|
||||
|
||||
snd_pcm_hw_params_t *params = NULL;
|
||||
snd_pcm_sw_params_t *sw_params = NULL;
|
||||
|
||||
const char *alsa_dev = "default";
|
||||
if ( device != NULL )
|
||||
if (device)
|
||||
alsa_dev = device;
|
||||
|
||||
unsigned latency_usec = latency * 1000;
|
||||
unsigned channels = 2;
|
||||
unsigned periods = 4;
|
||||
snd_pcm_uframes_t buffer_size;
|
||||
snd_pcm_format_t format;
|
||||
|
||||
TRY_ALSA(snd_pcm_open(&alsa->pcm, alsa_dev, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK));
|
||||
|
||||
unsigned int latency_usec = latency * 1000;
|
||||
|
||||
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, int rate, int 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));
|
||||
@@ -117,16 +121,16 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t __alsa_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t alsa_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
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)
|
||||
{
|
||||
@@ -141,16 +145,16 @@ static ssize_t __alsa_write(void* data, const void* buf, size_t size)
|
||||
|
||||
frames = snd_pcm_writei(alsa->pcm, (const char*)buf + written * 2 * (alsa->has_float ? sizeof(float) : sizeof(int16_t)), size - written);
|
||||
|
||||
if ( frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE )
|
||||
if (frames == -EPIPE || frames == -EINTR || frames == -ESTRPIPE)
|
||||
{
|
||||
if ( snd_pcm_recover(alsa->pcm, frames, 1) < 0 )
|
||||
if (snd_pcm_recover(alsa->pcm, frames, 1) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
else if ( frames == -EAGAIN && alsa->nonblock )
|
||||
else if (frames == -EAGAIN && alsa->nonblock)
|
||||
return 0;
|
||||
else if ( frames < 0 )
|
||||
else if (frames < 0)
|
||||
return -1;
|
||||
|
||||
written += frames;
|
||||
@@ -159,28 +163,28 @@ static ssize_t __alsa_write(void* data, const void* buf, size_t size)
|
||||
return snd_pcm_frames_to_bytes(alsa->pcm, size);
|
||||
}
|
||||
|
||||
static bool __alsa_stop(void *data)
|
||||
static bool alsa_stop(void *data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __alsa_set_nonblock_state(void *data, bool state)
|
||||
static void alsa_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
alsa_t *alsa = (alsa_t*)data;
|
||||
alsa->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __alsa_start(void *data)
|
||||
static bool alsa_start(void *data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __alsa_free(void *data)
|
||||
static void alsa_free(void *data)
|
||||
{
|
||||
alsa_t *alsa = data;
|
||||
if ( alsa )
|
||||
alsa_t *alsa = (alsa_t*)data;
|
||||
if (alsa)
|
||||
{
|
||||
if ( alsa->pcm )
|
||||
if (alsa->pcm)
|
||||
{
|
||||
snd_pcm_drop(alsa->pcm);
|
||||
snd_pcm_close(alsa->pcm);
|
||||
@@ -189,19 +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,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,302 @@
|
||||
/* SSNES - A Super Nintendo Entertainment System (SNES) Emulator frontend for libsnes.
|
||||
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
||||
* Copyright (C) 2011-2012 - Chris Moeller
|
||||
*
|
||||
* Some code herein may be based on code found in BSNES.
|
||||
*
|
||||
* SSNES is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* SSNES is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with SSNES.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
#include "driver.h"
|
||||
#include "general.h"
|
||||
#include "fifo_buffer.h"
|
||||
#include <stdlib.h>
|
||||
#include "../boolean.h"
|
||||
#include <pthread.h>
|
||||
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AudioUnit/AUComponent.h>
|
||||
|
||||
typedef struct coreaudio
|
||||
{
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t cond;
|
||||
|
||||
ComponentInstance dev;
|
||||
bool dev_alive;
|
||||
|
||||
fifo_buffer_t *buffer;
|
||||
bool nonblock;
|
||||
size_t buffer_size;
|
||||
} coreaudio_t;
|
||||
|
||||
static void coreaudio_free(void *data)
|
||||
{
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
if (!dev)
|
||||
return;
|
||||
|
||||
if (dev->dev_alive)
|
||||
{
|
||||
AudioOutputUnitStop(dev->dev);
|
||||
CloseComponent(dev->dev);
|
||||
}
|
||||
|
||||
if (dev->buffer)
|
||||
fifo_free(dev->buffer);
|
||||
|
||||
pthread_mutex_destroy(&dev->lock);
|
||||
pthread_cond_destroy(&dev->cond);
|
||||
|
||||
free(dev);
|
||||
}
|
||||
|
||||
static OSStatus audio_cb(void *userdata, AudioUnitRenderActionFlags *action_flags,
|
||||
const AudioTimeStamp *time_stamp, UInt32 bus_number,
|
||||
UInt32 number_frames, AudioBufferList *io_data)
|
||||
{
|
||||
coreaudio_t *dev = (coreaudio_t*)userdata;
|
||||
(void)time_stamp;
|
||||
(void)bus_number;
|
||||
(void)number_frames;
|
||||
|
||||
if (!io_data)
|
||||
return noErr;
|
||||
if (io_data->mNumberBuffers != 1)
|
||||
return noErr;
|
||||
|
||||
unsigned write_avail = io_data->mBuffers[0].mDataByteSize;
|
||||
void *outbuf = io_data->mBuffers[0].mData;
|
||||
|
||||
pthread_mutex_lock(&dev->lock);
|
||||
if (fifo_read_avail(dev->buffer) < write_avail)
|
||||
{
|
||||
*action_flags = kAudioUnitRenderAction_OutputIsSilence;
|
||||
memset(outbuf, 0, write_avail); // Seems to be needed.
|
||||
pthread_mutex_unlock(&dev->lock);
|
||||
pthread_cond_signal(&dev->cond); // Technically possible to deadlock without.
|
||||
return noErr;
|
||||
}
|
||||
|
||||
fifo_read(dev->buffer, outbuf, write_avail);
|
||||
pthread_mutex_unlock(&dev->lock);
|
||||
pthread_cond_signal(&dev->cond);
|
||||
return noErr;
|
||||
}
|
||||
|
||||
static void *coreaudio_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
|
||||
coreaudio_t *dev = (coreaudio_t*)calloc(1, sizeof(*dev));
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
CFRunLoopRef run_loop = NULL;
|
||||
AudioObjectPropertyAddress addr = {
|
||||
kAudioHardwarePropertyRunLoop,
|
||||
kAudioObjectPropertyScopeGlobal,
|
||||
kAudioObjectPropertyElementMaster
|
||||
};
|
||||
|
||||
pthread_mutex_init(&dev->lock, NULL);
|
||||
pthread_cond_init(&dev->cond, NULL);
|
||||
AudioObjectSetPropertyData(kAudioObjectSystemObject, &addr, 0, NULL,
|
||||
sizeof(CFRunLoopRef), &run_loop);
|
||||
|
||||
ComponentDescription desc = {0};
|
||||
desc.componentType = kAudioUnitType_Output;
|
||||
desc.componentSubType = kAudioUnitSubType_HALOutput;
|
||||
desc.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
|
||||
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;
|
||||
|
||||
res = OpenAComponent(comp, &dev->dev);
|
||||
if (res != noErr)
|
||||
goto error;
|
||||
|
||||
dev->dev_alive = true;
|
||||
|
||||
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;
|
||||
|
||||
i_size = sizeof(real_desc);
|
||||
res = AudioUnitGetProperty(dev->dev, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &real_desc, &i_size);
|
||||
if (res != noErr)
|
||||
goto error;
|
||||
|
||||
SSNES_LOG("[CoreAudio]: Using output sample rate of %.1f Hz\n", (float)real_desc.mSampleRate);
|
||||
g_settings.audio.out_rate = real_desc.mSampleRate;
|
||||
|
||||
if (real_desc.mChannelsPerFrame != stream_desc.mChannelsPerFrame)
|
||||
goto error;
|
||||
if (real_desc.mBitsPerChannel != stream_desc.mBitsPerChannel)
|
||||
goto error;
|
||||
if (real_desc.mFormatFlags != stream_desc.mFormatFlags)
|
||||
goto error;
|
||||
if (real_desc.mFormatID != stream_desc.mFormatID)
|
||||
goto error;
|
||||
|
||||
layout.mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelBitmap;
|
||||
layout.mChannelBitmap = 0x03;
|
||||
|
||||
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_AudioChannelLayout,
|
||||
kAudioUnitScope_Input, 0, &layout, sizeof(layout));
|
||||
if (res != noErr)
|
||||
goto error;
|
||||
|
||||
cb.inputProc = audio_cb;
|
||||
cb.inputProcRefCon = dev;
|
||||
|
||||
res = AudioUnitSetProperty(dev->dev, kAudioUnitProperty_SetRenderCallback,
|
||||
kAudioUnitScope_Input, 0, &cb, sizeof(cb));
|
||||
if (res != noErr)
|
||||
goto error;
|
||||
|
||||
res = AudioUnitInitialize(dev->dev);
|
||||
if (res != noErr)
|
||||
goto error;
|
||||
|
||||
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)
|
||||
goto error;
|
||||
|
||||
SSNES_LOG("[CoreAudio]: Using buffer size of %u bytes: (latency = %u ms)\n", (unsigned)fifo_size, latency);
|
||||
|
||||
res = AudioOutputUnitStart(dev->dev);
|
||||
if (res != noErr)
|
||||
goto error;
|
||||
|
||||
return dev;
|
||||
|
||||
error:
|
||||
SSNES_ERR("[CoreAudio]: Failed to initialize driver ...\n");
|
||||
coreaudio_free(dev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t coreaudio_write(void *data, const void *buf_, size_t size)
|
||||
{
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
|
||||
const uint8_t *buf = (const uint8_t*)buf_;
|
||||
size_t written = 0;
|
||||
|
||||
while (size > 0)
|
||||
{
|
||||
pthread_mutex_lock(&dev->lock);
|
||||
|
||||
size_t write_avail = fifo_write_avail(dev->buffer);
|
||||
if (write_avail > size)
|
||||
write_avail = size;
|
||||
|
||||
fifo_write(dev->buffer, buf, write_avail);
|
||||
buf += write_avail;
|
||||
written += write_avail;
|
||||
size -= write_avail;
|
||||
|
||||
if (dev->nonblock)
|
||||
{
|
||||
pthread_mutex_unlock(&dev->lock);
|
||||
break;
|
||||
}
|
||||
|
||||
if (write_avail == 0)
|
||||
pthread_cond_wait(&dev->cond, &dev->lock);
|
||||
pthread_mutex_unlock(&dev->lock);
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
static bool coreaudio_stop(void *data)
|
||||
{
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
return AudioOutputUnitStop(dev->dev) == noErr;
|
||||
}
|
||||
|
||||
static void coreaudio_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
dev->nonblock = state;
|
||||
}
|
||||
|
||||
static bool coreaudio_start(void *data)
|
||||
{
|
||||
coreaudio_t *dev = (coreaudio_t*)data;
|
||||
return AudioOutputUnitStart(dev->dev) == noErr;
|
||||
}
|
||||
|
||||
static bool coreaudio_use_float(void *data)
|
||||
{
|
||||
(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 = {
|
||||
coreaudio_init,
|
||||
coreaudio_write,
|
||||
coreaudio_stop,
|
||||
coreaudio_start,
|
||||
coreaudio_set_nonblock_state,
|
||||
coreaudio_free,
|
||||
coreaudio_use_float,
|
||||
"coreaudio",
|
||||
coreaudio_write_avail,
|
||||
coreaudio_buffer_size,
|
||||
};
|
||||
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
/* 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 "../driver.h"
|
||||
#include <stdlib.h>
|
||||
#include "../boolean.h"
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <dsound.h>
|
||||
#include "../fifo_buffer.h"
|
||||
#include "../general.h"
|
||||
|
||||
typedef struct dsound
|
||||
{
|
||||
LPDIRECTSOUND ds;
|
||||
LPDIRECTSOUNDBUFFER dsb;
|
||||
HANDLE event;
|
||||
bool nonblock;
|
||||
|
||||
fifo_buffer_t *buffer;
|
||||
CRITICAL_SECTION crit;
|
||||
|
||||
volatile bool thread_alive;
|
||||
HANDLE thread;
|
||||
unsigned buffer_size;
|
||||
} dsound_t;
|
||||
|
||||
static inline unsigned write_avail(unsigned read_ptr, unsigned write_ptr, unsigned buffer_size)
|
||||
{
|
||||
return (read_ptr + buffer_size - write_ptr) % buffer_size;
|
||||
}
|
||||
|
||||
static inline void get_positions(dsound_t *ds, DWORD *read_ptr, DWORD *write_ptr)
|
||||
{
|
||||
IDirectSoundBuffer_GetCurrentPosition(ds->dsb, read_ptr, write_ptr);
|
||||
}
|
||||
|
||||
#define CHUNK_SIZE 256
|
||||
|
||||
struct audio_lock
|
||||
{
|
||||
void *chunk1;
|
||||
DWORD size1;
|
||||
void *chunk2;
|
||||
DWORD size2;
|
||||
};
|
||||
|
||||
static inline bool grab_region(dsound_t *ds, DWORD write_ptr, struct audio_lock *region)
|
||||
{
|
||||
HRESULT res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, ®ion->chunk1, ®ion->size1, ®ion->chunk2, ®ion->size2, 0);
|
||||
if (res == DSERR_BUFFERLOST)
|
||||
{
|
||||
res = IDirectSoundBuffer_Restore(ds->dsb);
|
||||
if (res != DS_OK)
|
||||
return false;
|
||||
|
||||
res = IDirectSoundBuffer_Lock(ds->dsb, write_ptr, CHUNK_SIZE, ®ion->chunk1, ®ion->size1, ®ion->chunk2, ®ion->size2, 0);
|
||||
if (res != DS_OK)
|
||||
return false;
|
||||
}
|
||||
|
||||
const char *err;
|
||||
switch (res)
|
||||
{
|
||||
case DSERR_BUFFERLOST:
|
||||
err = "DSERR_BUFFERLOST";
|
||||
break;
|
||||
case DSERR_INVALIDCALL:
|
||||
err = "DSERR_INVALIDCALL";
|
||||
break;
|
||||
case DSERR_INVALIDPARAM:
|
||||
err = "DSERR_INVALIDPARAM";
|
||||
break;
|
||||
case DSERR_PRIOLEVELNEEDED:
|
||||
err = "DSERR_PRIOLEVELNEEDED";
|
||||
break;
|
||||
|
||||
default:
|
||||
err = NULL;
|
||||
}
|
||||
|
||||
if (err)
|
||||
{
|
||||
SSNES_WARN("[DirectSound error]: %s\n", err);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void release_region(dsound_t *ds, const struct audio_lock *region)
|
||||
{
|
||||
IDirectSoundBuffer_Unlock(ds->dsb, region->chunk1, region->size1, region->chunk2, region->size2);
|
||||
}
|
||||
|
||||
static DWORD CALLBACK dsound_thread(PVOID data)
|
||||
{
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
||||
|
||||
DWORD write_ptr;
|
||||
get_positions(ds, NULL, &write_ptr);
|
||||
write_ptr = (write_ptr + ds->buffer_size / 2) % ds->buffer_size;
|
||||
|
||||
while (ds->thread_alive)
|
||||
{
|
||||
DWORD read_ptr;
|
||||
get_positions(ds, &read_ptr, NULL);
|
||||
|
||||
DWORD avail = write_avail(read_ptr, write_ptr, ds->buffer_size);
|
||||
|
||||
EnterCriticalSection(&ds->crit);
|
||||
DWORD fifo_avail = fifo_read_avail(ds->buffer);
|
||||
LeaveCriticalSection(&ds->crit);
|
||||
|
||||
// No space to write, or we don't have data in our fifo, but we can wait some time before it underruns ...
|
||||
if (avail < CHUNK_SIZE || ((fifo_avail < CHUNK_SIZE) && (avail < ds->buffer_size / 2)))
|
||||
{
|
||||
Sleep(1);
|
||||
// We could opt for using the notification interface,
|
||||
// but it is not guaranteed to work, so use high priority sleeping patterns. :(
|
||||
}
|
||||
else if (fifo_avail < CHUNK_SIZE) // Got space to write, but nothing in FIFO (underrun), fill block with silence.
|
||||
{
|
||||
struct audio_lock region;
|
||||
if (!grab_region(ds, write_ptr, ®ion))
|
||||
{
|
||||
ds->thread_alive = false;
|
||||
SetEvent(ds->event);
|
||||
break;
|
||||
}
|
||||
|
||||
memset(region.chunk1, 0, region.size1);
|
||||
memset(region.chunk2, 0, region.size2);
|
||||
|
||||
release_region(ds, ®ion);
|
||||
write_ptr = (write_ptr + region.size1 + region.size2) % ds->buffer_size;
|
||||
}
|
||||
else // All is good. Pull from it and notify FIFO :D
|
||||
{
|
||||
struct audio_lock region;
|
||||
if (!grab_region(ds, write_ptr, ®ion))
|
||||
{
|
||||
ds->thread_alive = false;
|
||||
SetEvent(ds->event);
|
||||
break;
|
||||
}
|
||||
|
||||
EnterCriticalSection(&ds->crit);
|
||||
if (region.chunk1)
|
||||
fifo_read(ds->buffer, region.chunk1, region.size1);
|
||||
if (region.chunk2)
|
||||
fifo_read(ds->buffer, region.chunk2, region.size2);
|
||||
LeaveCriticalSection(&ds->crit);
|
||||
|
||||
release_region(ds, ®ion);
|
||||
write_ptr = (write_ptr + region.size1 + region.size2) % ds->buffer_size;
|
||||
|
||||
SetEvent(ds->event);
|
||||
}
|
||||
}
|
||||
|
||||
ExitThread(0);
|
||||
}
|
||||
|
||||
static void dsound_stop_thread(dsound_t *ds)
|
||||
{
|
||||
if (ds->thread)
|
||||
{
|
||||
ds->thread_alive = false;
|
||||
WaitForSingleObject(ds->thread, INFINITE);
|
||||
CloseHandle(ds->thread);
|
||||
ds->thread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static bool dsound_start_thread(dsound_t *ds)
|
||||
{
|
||||
if (!ds->thread)
|
||||
{
|
||||
ds->thread_alive = true;
|
||||
ds->thread = CreateThread(NULL, 0, dsound_thread, ds, 0, NULL);
|
||||
if (ds->thread == NULL)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void dsound_clear_buffer(dsound_t *ds)
|
||||
{
|
||||
IDirectSoundBuffer_SetCurrentPosition(ds->dsb, 0);
|
||||
void *ptr;
|
||||
DWORD size;
|
||||
|
||||
if (IDirectSoundBuffer_Lock(ds->dsb, 0, 0, &ptr, &size, NULL, NULL, DSBLOCK_ENTIREBUFFER) == DS_OK)
|
||||
{
|
||||
memset(ptr, 0, size);
|
||||
IDirectSoundBuffer_Unlock(ds->dsb, ptr, size, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void dsound_free(void *data)
|
||||
{
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
if (ds)
|
||||
{
|
||||
if (ds->thread)
|
||||
{
|
||||
ds->thread_alive = false;
|
||||
WaitForSingleObject(ds->thread, INFINITE);
|
||||
CloseHandle(ds->thread);
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&ds->crit);
|
||||
|
||||
if (ds->dsb)
|
||||
{
|
||||
IDirectSoundBuffer_Stop(ds->dsb);
|
||||
IDirectSoundBuffer_Release(ds->dsb);
|
||||
}
|
||||
|
||||
if (ds)
|
||||
IDirectSound_Release(ds->ds);
|
||||
|
||||
if (ds->event)
|
||||
CloseHandle(ds->event);
|
||||
|
||||
if (ds->buffer)
|
||||
fifo_free(ds->buffer);
|
||||
|
||||
free(ds);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 (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;
|
||||
|
||||
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;
|
||||
ds->buffer_size *= CHUNK_SIZE;
|
||||
if (ds->buffer_size < 4 * CHUNK_SIZE)
|
||||
ds->buffer_size = 4 * CHUNK_SIZE;
|
||||
|
||||
SSNES_LOG("[DirectSound]: Setting buffer size of %u bytes\n", ds->buffer_size);
|
||||
SSNES_LOG("[DirectSound]: Latency = %u ms\n", (unsigned)((1000 * ds->buffer_size) / wfx.nAvgBytesPerSec));
|
||||
|
||||
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)
|
||||
goto error;
|
||||
|
||||
ds->buffer = fifo_new(4 * 1024);
|
||||
if (!ds->buffer)
|
||||
goto error;
|
||||
|
||||
if (IDirectSound_CreateSoundBuffer(ds->ds, &bufdesc, &ds->dsb, 0) != DS_OK)
|
||||
goto error;
|
||||
|
||||
IDirectSoundBuffer_SetCurrentPosition(ds->dsb, 0);
|
||||
|
||||
dsound_clear_buffer(ds);
|
||||
|
||||
if (IDirectSoundBuffer_Play(ds->dsb, 0, 0, DSBPLAY_LOOPING) != DS_OK)
|
||||
goto error;
|
||||
|
||||
if (!dsound_start_thread(ds))
|
||||
goto error;
|
||||
|
||||
return ds;
|
||||
|
||||
error:
|
||||
SSNES_ERR("[DirectSound] Error occured in init.\n");
|
||||
dsound_free(ds);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool dsound_stop(void *data)
|
||||
{
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
dsound_stop_thread(ds);
|
||||
return IDirectSoundBuffer_Stop(ds->dsb) == DS_OK;
|
||||
}
|
||||
|
||||
static bool dsound_start(void *data)
|
||||
{
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
dsound_clear_buffer(ds);
|
||||
|
||||
if (!dsound_start_thread(ds))
|
||||
return false;
|
||||
|
||||
return IDirectSoundBuffer_Play(ds->dsb, 0, 0, DSBPLAY_LOOPING) == DS_OK;
|
||||
}
|
||||
|
||||
static void dsound_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
ds->nonblock = state;
|
||||
}
|
||||
|
||||
static ssize_t dsound_write(void *data, const void *buf_, size_t size)
|
||||
{
|
||||
dsound_t *ds = (dsound_t*)data;
|
||||
const uint8_t *buf = (const uint8_t*)buf_;
|
||||
|
||||
if (!ds->thread_alive)
|
||||
return -1;
|
||||
|
||||
size_t written = 0;
|
||||
while (size > 0)
|
||||
{
|
||||
EnterCriticalSection(&ds->crit);
|
||||
size_t avail = fifo_write_avail(ds->buffer);
|
||||
if (avail > size)
|
||||
avail = size;
|
||||
|
||||
fifo_write(ds->buffer, buf, avail);
|
||||
LeaveCriticalSection(&ds->crit);
|
||||
|
||||
buf += avail;
|
||||
size -= avail;
|
||||
written += avail;
|
||||
|
||||
if (ds->nonblock || !ds->thread_alive)
|
||||
break;
|
||||
|
||||
if (avail == 0)
|
||||
WaitForSingleObject(ds->event, INFINITE);
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
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 = {
|
||||
dsound_init,
|
||||
dsound_write,
|
||||
dsound_stop,
|
||||
dsound_start,
|
||||
dsound_set_nonblock_state,
|
||||
dsound_free,
|
||||
NULL,
|
||||
"dsound",
|
||||
dsound_write_avail,
|
||||
dsound_buffer_size,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/////
|
||||
// API header for external SSNES audio driver plugins.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __SSNES_AUDIO_DRIVER_PLUGIN_H
|
||||
#define __SSNES_AUDIO_DRIVER_PLUGIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef SSNES_DLL_IMPORT
|
||||
#define SSNES_API_EXPORT __declspec(dllimport)
|
||||
#else
|
||||
#define SSNES_API_EXPORT __declspec(dllexport)
|
||||
#endif
|
||||
#define SSNES_API_CALLTYPE __cdecl
|
||||
#else
|
||||
#define SSNES_API_EXPORT
|
||||
#define SSNES_API_CALLTYPE
|
||||
#endif
|
||||
|
||||
#ifndef SSNES_TRUE
|
||||
#define SSNES_TRUE 1
|
||||
#endif
|
||||
|
||||
#ifndef SSNES_FALSE
|
||||
#define SSNES_FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef SSNES_OK
|
||||
#define SSNES_OK 1
|
||||
#endif
|
||||
|
||||
#ifndef SSNES_ERROR
|
||||
#define SSNES_ERROR 0
|
||||
#endif
|
||||
|
||||
#define SSNES_AUDIO_API_VERSION 2
|
||||
|
||||
typedef struct ssnes_audio_driver_info
|
||||
{
|
||||
// A hint for a subdevice of the audio driver.
|
||||
// This is driver independent, and not relevant for all
|
||||
// audio drivers. I.e. ALSA driver might use "hw:0",
|
||||
// OSS "/dev/audio", etc.
|
||||
const char *device;
|
||||
|
||||
// Audio sample rate.
|
||||
unsigned sample_rate;
|
||||
|
||||
// Maximum audio latency requested for output,
|
||||
// measured in milliseconds.
|
||||
// If driver is not able to provide this latency, it can
|
||||
// be disregarded.
|
||||
unsigned latency;
|
||||
} ssnes_audio_driver_info_t;
|
||||
|
||||
typedef struct ssnes_audio_driver
|
||||
{
|
||||
// Initializes the device.
|
||||
void *(*init)(const ssnes_audio_driver_info_t *info);
|
||||
|
||||
// Write data in buffer to audio driver.
|
||||
// A frame here is defined as one combined sample of left and right
|
||||
// channels. (I.e. 44.1kHz, 16-bit stereo has 88.2k samples/s, and
|
||||
// 44.1k frames/s.)
|
||||
//
|
||||
// Samples are interleaved in format LRLRLRLRLR ...
|
||||
// If the driver returns true in use_float(), a floating point
|
||||
// format will be used, with range [-1.0, 1.0].
|
||||
// If not, signed 16-bit samples in native byte ordering will be used.
|
||||
//
|
||||
// This function returns the number of frames successfully written.
|
||||
// If an error occurs, -1 should be returned.
|
||||
// Note that non-blocking behavior that cannot write at this time
|
||||
// should return 0 as returning -1 will terminate the driver.
|
||||
//
|
||||
// Unless said otherwise with set_nonblock_state(), all writes
|
||||
// are blocking, and it should block till it has written all frames.
|
||||
int (*write)(void *data, const void *buffer, unsigned frames);
|
||||
|
||||
// Temporarily pauses the audio driver.
|
||||
int (*stop)(void *data);
|
||||
|
||||
// Resumes audio driver from the paused state.
|
||||
int (*start)(void *data);
|
||||
|
||||
// If state is true, nonblocking operation is assumed.
|
||||
// This is typically used for fast-forwarding. If driver cannot
|
||||
// implement nonblocking writes, this can be disregarded, but should
|
||||
// log a message to stderr.
|
||||
void (*set_nonblock_state)(void *data, int state);
|
||||
|
||||
// Stops and frees the audio driver.
|
||||
void (*free)(void *data);
|
||||
|
||||
// If true is returned, the audio driver is capable of using
|
||||
// floating point data. This will likely increase performance as the
|
||||
// resampler unit uses floating point. The sample range is
|
||||
// [-1.0, 1.0].
|
||||
int (*use_float)(void *data);
|
||||
|
||||
// The driver might be forced to use a certain output frequency
|
||||
// (i.e. Jack), and thus to avoid double resampling, the driver
|
||||
// can request SSNES to resample to a different sample rate.
|
||||
// This function can be set to NULL if the driver does not
|
||||
// desire to override the sample rate.
|
||||
unsigned (*sample_rate)(void *data);
|
||||
|
||||
// Human readable identification string for the driver.
|
||||
const char *ident;
|
||||
|
||||
// Must be set to SSNES_AUDIO_API_VERSION.
|
||||
// Used for detecting API mismatch.
|
||||
int api_version;
|
||||
} ssnes_audio_driver_t;
|
||||
|
||||
SSNES_API_EXPORT const ssnes_audio_driver_t* SSNES_API_CALLTYPE
|
||||
ssnes_audio_driver_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,129 @@
|
||||
/////
|
||||
// API header for external SSNES DSP plugins.
|
||||
//
|
||||
//
|
||||
|
||||
#ifndef __SSNES_DSP_PLUGIN_H
|
||||
#define __SSNES_DSP_PLUGIN_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifdef SSNES_DLL_IMPORT
|
||||
#define SSNES_API_EXPORT __declspec(dllimport)
|
||||
#else
|
||||
#define SSNES_API_EXPORT __declspec(dllexport)
|
||||
#endif
|
||||
#define SSNES_API_CALLTYPE __cdecl
|
||||
#else
|
||||
#define SSNES_API_EXPORT
|
||||
#define SSNES_API_CALLTYPE
|
||||
#endif
|
||||
|
||||
#ifndef SSNES_FALSE
|
||||
#define SSNES_FALSE 0
|
||||
#endif
|
||||
|
||||
#ifndef SSNES_TRUE
|
||||
#define SSNES_TRUE 1
|
||||
#endif
|
||||
|
||||
#define SSNES_DSP_API_VERSION 3
|
||||
|
||||
typedef struct ssnes_dsp_info
|
||||
{
|
||||
// Input sample rate that the DSP plugin receives. This is generally ~32kHz.
|
||||
// Some small variance is allowed due to syncing behavior.
|
||||
float input_rate;
|
||||
|
||||
// SSNES requests that the DSP plugin resamples the
|
||||
// input to a certain frequency.
|
||||
//
|
||||
// However, the plugin might ignore this
|
||||
// using the resample field in ssnes_dsp_output_t (see below).
|
||||
float output_rate;
|
||||
} ssnes_dsp_info_t;
|
||||
|
||||
typedef struct ssnes_dsp_output
|
||||
{
|
||||
// The DSP plugin has to provide the buffering for the output samples.
|
||||
// This is for performance reasons to avoid redundant copying of data.
|
||||
// The samples are laid out in interleaving order: LRLRLRLR
|
||||
// The range of the samples are [-1.0, 1.0].
|
||||
// This range cannot be exceeded without horrible audio glitches.
|
||||
const float *samples;
|
||||
|
||||
// Frames which the DSP plugin outputted for the current process.
|
||||
// One frame is here defined as a combined sample of
|
||||
// left and right channels.
|
||||
// (I.e. 44.1kHz, 16bit stereo will have
|
||||
// 88.2k samples/sec and 44.1k frames/sec.)
|
||||
unsigned frames;
|
||||
|
||||
// If true, the DSP plugin did not resample the input audio,
|
||||
// and requests resampling to the proper frequency to be
|
||||
// performed outside the plugin.
|
||||
// If false,
|
||||
// it is assumed that the output has the same sample rate as given
|
||||
// in output_rate.
|
||||
int should_resample;
|
||||
} ssnes_dsp_output_t;
|
||||
|
||||
typedef struct ssnes_dsp_input
|
||||
{
|
||||
// Input data for the DSP. The samples are interleaved in order: LRLRLRLR
|
||||
const float *samples;
|
||||
|
||||
// Number of frames for input data.
|
||||
// One frame is here defined as a combined sample of
|
||||
// left and right channels.
|
||||
// (I.e. 44.1kHz, 16bit stereo will have
|
||||
// 88.2k samples/sec and 44.1k frames/sec.)
|
||||
unsigned frames;
|
||||
} ssnes_dsp_input_t;
|
||||
|
||||
typedef struct ssnes_dsp_plugin
|
||||
{
|
||||
// Creates a handle of the plugin. Returns NULL if failed.
|
||||
void *(*init)(const ssnes_dsp_info_t *info);
|
||||
|
||||
// Processes input data.
|
||||
// The plugin is allowed to return variable sizes for output data.
|
||||
void (*process)(void *data, ssnes_dsp_output_t *output,
|
||||
const ssnes_dsp_input_t *input);
|
||||
|
||||
// Frees the handle.
|
||||
void (*free)(void *data);
|
||||
|
||||
// API version used to compile the plugin.
|
||||
// Used to detect mismatches in API.
|
||||
// Must be set to SSNES_DSP_API_VERSION on compile.
|
||||
int api_version;
|
||||
|
||||
// Signal plugin that it may open a configuring window or
|
||||
// something similiar. The behavior of this function
|
||||
// is thus plugin dependent. Implementing this is optional,
|
||||
// and can be set to NULL.
|
||||
void (*config)(void *data);
|
||||
|
||||
// Human readable identification string.
|
||||
const char *ident;
|
||||
|
||||
// Called every frame, allows creating a GUI main loop in the main thread.
|
||||
// GUI events can be processed here in a non-blocking fashion.
|
||||
// Can be set to NULL to ignore it.
|
||||
void (*events)(void *data);
|
||||
} ssnes_dsp_plugin_t;
|
||||
|
||||
// Called by SSNES at startup to get the callback struct.
|
||||
// This is NOT dynamically allocated!
|
||||
SSNES_API_EXPORT const ssnes_dsp_plugin_t* SSNES_API_CALLTYPE
|
||||
ssnes_dsp_plugin_init(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,164 @@
|
||||
/* 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 "ext/ssnes_audio.h"
|
||||
#include "../boolean.h"
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "../driver.h"
|
||||
#include "../dynamic.h"
|
||||
#include "../general.h"
|
||||
#include <sys/types.h>
|
||||
|
||||
typedef struct audio_ext
|
||||
{
|
||||
dylib_t lib;
|
||||
const ssnes_audio_driver_t *driver;
|
||||
void *handle;
|
||||
bool is_float;
|
||||
} audio_ext_t;
|
||||
|
||||
static void audio_ext_free(void *data)
|
||||
{
|
||||
audio_ext_t *ext = (audio_ext_t*)data;
|
||||
if (ext)
|
||||
{
|
||||
if (ext->driver && ext->handle)
|
||||
ext->driver->free(ext->handle);
|
||||
if (ext->lib)
|
||||
dylib_close(ext->lib);
|
||||
free(ext);
|
||||
}
|
||||
}
|
||||
|
||||
static void *audio_ext_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
if (!(*g_settings.audio.external_driver))
|
||||
{
|
||||
SSNES_ERR("Please define an external audio driver.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
audio_ext_t *ext = (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)
|
||||
{
|
||||
SSNES_ERR("Failed to load external library \"%s\"\n", g_settings.audio.external_driver);
|
||||
goto error;
|
||||
}
|
||||
|
||||
plugin_load = (const ssnes_audio_driver_t *(*)(void))dylib_proc(ext->lib, "ssnes_audio_driver_init");
|
||||
|
||||
if (!plugin_load)
|
||||
{
|
||||
SSNES_ERR("Failed to find symbol \"ssnes_audio_driver_init\" in plugin.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ext->driver = plugin_load();
|
||||
if (!ext->driver)
|
||||
{
|
||||
SSNES_ERR("Received invalid driver from plugin.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
SSNES_LOG("Loaded external audio driver: \"%s\"\n", ext->driver->ident ? ext->driver->ident : "Unknown");
|
||||
|
||||
if (ext->driver->api_version != SSNES_AUDIO_API_VERSION)
|
||||
{
|
||||
SSNES_ERR("API mismatch in external audio plugin. SSNES: %d, Plugin: %d ...\n", SSNES_AUDIO_API_VERSION, ext->driver->api_version);
|
||||
goto error;
|
||||
}
|
||||
|
||||
info.device = device;
|
||||
info.sample_rate = rate;
|
||||
info.latency = latency;
|
||||
|
||||
ext->handle = ext->driver->init(&info);
|
||||
if (!ext->handle)
|
||||
{
|
||||
SSNES_ERR("Failed to init audio driver.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ext->driver->sample_rate)
|
||||
g_settings.audio.out_rate = ext->driver->sample_rate(ext->handle);
|
||||
|
||||
if (!g_settings.audio.sync)
|
||||
ext->driver->set_nonblock_state(ext->handle, SSNES_TRUE);
|
||||
|
||||
return ext;
|
||||
|
||||
error:
|
||||
audio_ext_free(ext);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t audio_ext_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
audio_ext_t *ext = (audio_ext_t*)data;
|
||||
unsigned frame_size = ext->is_float ? (2 * sizeof(float)) : (2 * sizeof(int16_t));
|
||||
size /= frame_size;
|
||||
|
||||
int ret = ext->driver->write(ext->handle, buf, size);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
return ret * frame_size;
|
||||
}
|
||||
|
||||
static bool audio_ext_start(void *data)
|
||||
{
|
||||
audio_ext_t *ext = (audio_ext_t*)data;
|
||||
return ext->driver->start(ext->handle);
|
||||
}
|
||||
|
||||
static bool audio_ext_stop(void *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 = (audio_ext_t*)data;
|
||||
ext->driver->set_nonblock_state(ext->handle, toggle);
|
||||
}
|
||||
|
||||
static bool audio_ext_use_float(void *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 = {
|
||||
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"
|
||||
};
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
// Hermite resampler based on bsnes' audio library.
|
||||
|
||||
#include "resampler.h"
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include "../boolean.h"
|
||||
|
||||
#define CHANNELS 2
|
||||
|
||||
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;
|
||||
|
||||
mu2 = mu1 * mu1;
|
||||
mu3 = mu2 * mu1;
|
||||
|
||||
m0 = (c - a) * 0.5f;
|
||||
m1 = (d - b) * 0.5f;
|
||||
|
||||
a0 = +2 * mu3 - 3 * mu2 + 1;
|
||||
a1 = mu3 - 2 * mu2 + mu1;
|
||||
a2 = mu3 - mu2;
|
||||
a3 = -2 * mu3 + 3 * mu2;
|
||||
|
||||
return (a0 * b) + (a1 * m0) + (a2 * m1) + (a3 * c);
|
||||
}
|
||||
|
||||
ssnes_resampler_t *resampler_new(void)
|
||||
{
|
||||
return (ssnes_resampler_t*)calloc(1, sizeof(ssnes_resampler_t));
|
||||
}
|
||||
|
||||
void resampler_process(ssnes_resampler_t *re, struct resampler_data *data)
|
||||
{
|
||||
double r_step = 1.0 / data->ratio;
|
||||
size_t processed_out = 0;
|
||||
|
||||
size_t in_frames = data->input_frames;
|
||||
const float *in_data = data->data_in;
|
||||
float *out_data = data->data_out;
|
||||
|
||||
for (size_t i = 0; i < in_frames; i++)
|
||||
{
|
||||
while (re->r_frac <= 1.0)
|
||||
{
|
||||
re->r_frac += r_step;
|
||||
for (unsigned i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
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;
|
||||
}
|
||||
processed_out++;
|
||||
}
|
||||
|
||||
re->r_frac -= 1.0;
|
||||
for (unsigned i = 0; i < CHANNELS; i++)
|
||||
{
|
||||
re->chan_data[i][0] = re->chan_data[i][1];
|
||||
re->chan_data[i][1] = re->chan_data[i][2];
|
||||
re->chan_data[i][2] = re->chan_data[i][3];
|
||||
re->chan_data[i][3] = *in_data++;
|
||||
}
|
||||
}
|
||||
|
||||
data->output_frames = processed_out;
|
||||
}
|
||||
|
||||
void resampler_free(ssnes_resampler_t *re)
|
||||
{
|
||||
free(re);
|
||||
}
|
||||
|
||||
+84
-59
@@ -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);
|
||||
}
|
||||
@@ -103,7 +104,15 @@ static size_t find_buffersize(jack_t *jd, int latency)
|
||||
{
|
||||
int frames = latency * g_settings.audio.out_rate / 1000;
|
||||
|
||||
int jack_latency = jack_port_get_total_latency(jd->client, jd->ports[0]);
|
||||
jack_latency_range_t range;
|
||||
int jack_latency = 0;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
jack_port_get_latency_range(jd->ports[i], JackPlaybackLatency, &range);
|
||||
if ((int)range.max > jack_latency)
|
||||
jack_latency = range.max;
|
||||
}
|
||||
|
||||
SSNES_LOG("JACK: Jack latency is %d frames.\n", jack_latency);
|
||||
|
||||
int buffer_frames = frames - jack_latency;
|
||||
@@ -116,13 +125,19 @@ static size_t find_buffersize(jack_t *jd, int latency)
|
||||
return buffer_frames * sizeof(jack_default_audio_sample_t);
|
||||
}
|
||||
|
||||
static void* __jack_init(const char* device, int rate, int latency)
|
||||
static void *ja_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
jack_t *jd = calloc(1, sizeof(jack_t));
|
||||
if ( jd == NULL )
|
||||
jack_t *jd = (jack_t*)calloc(1, sizeof(jack_t));
|
||||
if (!jd)
|
||||
return NULL;
|
||||
|
||||
pthread_cond_init(&jd->cond, NULL);
|
||||
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)
|
||||
@@ -140,9 +155,7 @@ static void* __jack_init(const char* device, int rate, int latency)
|
||||
SSNES_ERR("Failed to register ports.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
|
||||
char *dest_ports[2];
|
||||
jports = jack_get_ports(jd->client, NULL, NULL, JackPortIsPhysical | JackPortIsInput);
|
||||
if (jports == NULL)
|
||||
{
|
||||
@@ -150,7 +163,21 @@ static void* __jack_init(const char* device, int rate, int latency)
|
||||
goto error;
|
||||
}
|
||||
|
||||
int parsed = parse_ports(dest_ports, jports);
|
||||
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++)
|
||||
{
|
||||
jd->buffer[i] = jack_ringbuffer_create(bufsize);
|
||||
if (jd->buffer[i] == NULL)
|
||||
{
|
||||
SSNES_ERR("Failed to create buffers.\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
parsed = parse_ports(dest_ports, jports);
|
||||
|
||||
if (jack_activate(jd->client) < 0)
|
||||
{
|
||||
@@ -169,23 +196,7 @@ static void* __jack_init(const char* device, int rate, int latency)
|
||||
|
||||
for (int i = 0; i < parsed; i++)
|
||||
free(dest_ports[i]);
|
||||
|
||||
size_t bufsize = find_buffersize(jd, latency);
|
||||
|
||||
SSNES_LOG("JACK: Internal buffer size: %d frames.\n", (int)(bufsize / sizeof(jack_default_audio_sample_t)));
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
jd->buffer[i] = jack_ringbuffer_create(bufsize);
|
||||
if (jd->buffer[i] == NULL)
|
||||
{
|
||||
SSNES_ERR("Failed to create buffers.\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_cond_init(&jd->cond, NULL);
|
||||
pthread_mutex_init(&jd->cond_lock, NULL);
|
||||
|
||||
|
||||
jack_free(jports);
|
||||
return jd;
|
||||
|
||||
@@ -197,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++)
|
||||
@@ -211,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);
|
||||
|
||||
@@ -222,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
|
||||
@@ -239,34 +255,34 @@ static size_t write_buffer(jack_t *jd, const float *buf, size_t size)
|
||||
return written * sizeof(float) * 2;
|
||||
}
|
||||
|
||||
static ssize_t __jack_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t ja_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jack_t *jd = (jack_t*)data;
|
||||
|
||||
return write_buffer(jd, buf, size);
|
||||
return write_buffer(jd, (const float*)buf, size);
|
||||
}
|
||||
|
||||
static bool __jack_stop(void *data)
|
||||
static bool ja_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __jack_set_nonblock_state(void *data, bool state)
|
||||
static void ja_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jack_t *jd = (jack_t*)data;
|
||||
jd->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __jack_start(void *data)
|
||||
static bool ja_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __jack_free(void *data)
|
||||
static void ja_free(void *data)
|
||||
{
|
||||
jack_t *jd = data;
|
||||
jack_t *jd = (jack_t*)data;
|
||||
|
||||
jd->shutdown = true;
|
||||
|
||||
@@ -285,25 +301,34 @@ static void __jack_free(void *data)
|
||||
free(jd);
|
||||
}
|
||||
|
||||
static bool __jack_use_float(void *data)
|
||||
static bool ja_use_float(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
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 = __jack_init,
|
||||
.write = __jack_write,
|
||||
.stop = __jack_stop,
|
||||
.start = __jack_start,
|
||||
.set_nonblock_state = __jack_set_nonblock_state,
|
||||
.free = __jack_free,
|
||||
.use_float = __jack_use_float,
|
||||
.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,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+36
-31
@@ -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,10 +15,16 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "../driver.h"
|
||||
|
||||
#include "driver.h"
|
||||
#ifdef __APPLE__
|
||||
#include <OpenAL/al.h>
|
||||
#include <OpenAL/alc.h>
|
||||
#else
|
||||
#include <AL/al.h>
|
||||
#include <AL/alc.h>
|
||||
#endif
|
||||
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -49,11 +55,11 @@ typedef struct al
|
||||
|
||||
} al_t;
|
||||
|
||||
static void* __al_init(const char* device, int rate, int latency)
|
||||
static void *al_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
al_t *al = calloc(1, sizeof(al_t));
|
||||
if ( al == NULL )
|
||||
al_t *al = (al_t*)calloc(1, sizeof(al_t));
|
||||
if (!al)
|
||||
return NULL;
|
||||
|
||||
al->handle = alcOpenDevice(NULL);
|
||||
@@ -69,8 +75,8 @@ static void* __al_init(const char* device, int rate, int 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;
|
||||
|
||||
@@ -120,13 +126,12 @@ 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(;;)
|
||||
for (;;)
|
||||
{
|
||||
if (al_unqueue_buffers(al))
|
||||
break;
|
||||
@@ -146,17 +151,17 @@ static bool al_get_buffer(al_t *al, ALuint *buffer)
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t al_fill_internal_buf(al_t *al, const void* buf, size_t size)
|
||||
static size_t al_fill_internal_buf(al_t *al, const void *buf, size_t size)
|
||||
{
|
||||
size_t read_size = (BUFSIZE - al->tmpbuf_ptr > size) ? size : (BUFSIZE - al->tmpbuf_ptr);
|
||||
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;
|
||||
}
|
||||
|
||||
static ssize_t __al_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t al_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
al_t *al = data;
|
||||
al_t *al = (al_t*)data;
|
||||
|
||||
size_t written = 0;
|
||||
while (written < size)
|
||||
@@ -189,32 +194,32 @@ static ssize_t __al_write(void* data, const void* buf, size_t size)
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool __al_stop(void *data)
|
||||
static bool al_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __al_set_nonblock_state(void *data, bool state)
|
||||
static void al_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
al_t *al = data;
|
||||
al_t *al = (al_t*)data;
|
||||
al->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __al_start(void *data)
|
||||
static bool al_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __al_free(void *data)
|
||||
static void al_free(void *data)
|
||||
{
|
||||
al_t *al= data;
|
||||
al_t *al = (al_t*)data;
|
||||
if (al)
|
||||
{
|
||||
alSourceStop(al->source);
|
||||
alDeleteSources(1, &al->source);
|
||||
if ( al->buffers )
|
||||
if (al->buffers)
|
||||
{
|
||||
alDeleteBuffers(al->num_buffers, al->buffers);
|
||||
free(al->buffers);
|
||||
@@ -228,13 +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"
|
||||
};
|
||||
|
||||
|
||||
+94
-44
@@ -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,81 +15,105 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "driver.h"
|
||||
#include "general.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef HAVE_OSS_BSD
|
||||
#include <soundcard.h>
|
||||
#else
|
||||
#include <sys/soundcard.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
|
||||
static void* __oss_init(const char* device, int rate, int latency)
|
||||
#ifdef HAVE_OSS_BSD
|
||||
#define DEFAULT_OSS_DEV "/dev/audio"
|
||||
#else
|
||||
#define DEFAULT_OSS_DEV "/dev/dsp"
|
||||
#endif
|
||||
|
||||
static void *oss_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
int *fd = calloc(1, sizeof(int));
|
||||
if ( fd == NULL )
|
||||
int *fd = (int*)calloc(1, sizeof(int));
|
||||
if (fd == NULL)
|
||||
return NULL;
|
||||
|
||||
const char *oss_device = "/dev/dsp";
|
||||
const char *oss_device = DEFAULT_OSS_DEV;
|
||||
|
||||
if ( device != NULL )
|
||||
if (device != NULL)
|
||||
oss_device = device;
|
||||
|
||||
if ( (*fd = open(oss_device, O_WRONLY)) < 0 )
|
||||
if ((*fd = open(oss_device, O_WRONLY)) < 0)
|
||||
{
|
||||
free(fd);
|
||||
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;
|
||||
}
|
||||
if (ioctl(*fd, SNDCTL_DSP_SETFRAGMENT, &frag) < 0)
|
||||
SSNES_WARN("Cannot set fragment sizes. Latency might not be as expected ...\n");
|
||||
|
||||
int channels = 2;
|
||||
int format = AFMT_S16_LE;
|
||||
int format = is_little_endian() ?
|
||||
AFMT_S16_LE : AFMT_S16_BE;
|
||||
|
||||
if ( ioctl(*fd, SNDCTL_DSP_CHANNELS, &channels) < 0 )
|
||||
if (ioctl(*fd, SNDCTL_DSP_CHANNELS, &channels) < 0)
|
||||
{
|
||||
close(*fd);
|
||||
free(fd);
|
||||
perror("ioctl");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ( ioctl(*fd, SNDCTL_DSP_SETFMT, &format) < 0 )
|
||||
if (ioctl(*fd, SNDCTL_DSP_SETFMT, &format) < 0)
|
||||
{
|
||||
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)
|
||||
static ssize_t oss_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
int *fd = data;
|
||||
int *fd = (int*)data;
|
||||
|
||||
if ( size == 0 )
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
ssize_t ret;
|
||||
if ( (ret = write(*fd, buf, size)) <= 0 )
|
||||
if ((ret = write(*fd, buf, size)) <= 0)
|
||||
{
|
||||
if ( (fcntl(*fd, F_GETFL) & O_NONBLOCK) && errno == EAGAIN )
|
||||
if ((fcntl(*fd, F_GETFL) & O_NONBLOCK) && errno == EAGAIN)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
@@ -97,51 +121,77 @@ static ssize_t __oss_write(void* data, const void* buf, size_t size)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool __oss_stop(void *data)
|
||||
static bool oss_stop(void *data)
|
||||
{
|
||||
int *fd = data;
|
||||
int *fd = (int*)data;
|
||||
ioctl(*fd, SNDCTL_DSP_RESET, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool __oss_start(void *data)
|
||||
static bool oss_start(void *data)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __oss_set_nonblock_state(void *data, bool state)
|
||||
static void oss_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
int *fd = data;
|
||||
int *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)
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
/* 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 "driver.h"
|
||||
#include "general.h"
|
||||
#include <pulse/pulseaudio.h>
|
||||
#include "../boolean.h"
|
||||
#include <string.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
pa_threaded_mainloop *mainloop;
|
||||
pa_context *context;
|
||||
pa_stream *stream;
|
||||
bool nonblock;
|
||||
size_t buffer_size;
|
||||
} pa_t;
|
||||
|
||||
static void pulse_free(void *data)
|
||||
{
|
||||
pa_t *pa = (pa_t*)data;
|
||||
if (pa)
|
||||
{
|
||||
if (pa->mainloop)
|
||||
pa_threaded_mainloop_stop(pa->mainloop);
|
||||
|
||||
if (pa->stream)
|
||||
{
|
||||
pa_stream_disconnect(pa->stream);
|
||||
pa_stream_unref(pa->stream);
|
||||
}
|
||||
|
||||
if (pa->context)
|
||||
{
|
||||
pa_context_disconnect(pa->context);
|
||||
pa_context_unref(pa->context);
|
||||
}
|
||||
|
||||
if (pa->mainloop)
|
||||
pa_threaded_mainloop_free(pa->mainloop);
|
||||
|
||||
free(pa);
|
||||
}
|
||||
}
|
||||
|
||||
static void context_state_cb(pa_context *c, void *data)
|
||||
{
|
||||
pa_t *pa = (pa_t*)data;
|
||||
switch (pa_context_get_state(c))
|
||||
{
|
||||
case PA_CONTEXT_READY:
|
||||
case PA_CONTEXT_TERMINATED:
|
||||
case PA_CONTEXT_FAILED:
|
||||
pa_threaded_mainloop_signal(pa->mainloop, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void stream_state_cb(pa_stream *s, void *data)
|
||||
{
|
||||
pa_t *pa = (pa_t*)data;
|
||||
switch (pa_stream_get_state(s)) {
|
||||
case PA_STREAM_READY:
|
||||
case PA_STREAM_FAILED:
|
||||
case PA_STREAM_TERMINATED:
|
||||
pa_threaded_mainloop_signal(pa->mainloop, 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void stream_request_cb(pa_stream *s, size_t length, void *data)
|
||||
{
|
||||
(void)length;
|
||||
(void)s;
|
||||
pa_t *pa = (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 = (pa_t*)data;
|
||||
pa_threaded_mainloop_signal(pa->mainloop, 0);
|
||||
}
|
||||
|
||||
static void *pulse_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
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;
|
||||
|
||||
pa->mainloop = pa_threaded_mainloop_new();
|
||||
if (!pa->mainloop)
|
||||
goto error;
|
||||
|
||||
pa->context = pa_context_new(pa_threaded_mainloop_get_api(pa->mainloop), "SSNES");
|
||||
if (!pa->context)
|
||||
goto error;
|
||||
|
||||
pa_context_set_state_callback(pa->context, context_state_cb, pa);
|
||||
|
||||
if (pa_context_connect(pa->context, device, PA_CONTEXT_NOFLAGS, NULL) < 0)
|
||||
goto error;
|
||||
|
||||
pa_threaded_mainloop_lock(pa->mainloop);
|
||||
if (pa_threaded_mainloop_start(pa->mainloop) < 0)
|
||||
goto error;
|
||||
|
||||
pa_threaded_mainloop_wait(pa->mainloop);
|
||||
|
||||
if (pa_context_get_state(pa->context) != PA_CONTEXT_READY)
|
||||
goto unlock_error;
|
||||
|
||||
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)
|
||||
goto unlock_error;
|
||||
|
||||
pa_stream_set_state_callback(pa->stream, stream_state_cb, pa);
|
||||
pa_stream_set_write_callback(pa->stream, stream_request_cb, pa);
|
||||
pa_stream_set_latency_update_callback(pa->stream, stream_latency_update_cb, pa);
|
||||
|
||||
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;
|
||||
|
||||
pa_threaded_mainloop_wait(pa->mainloop);
|
||||
|
||||
if (pa_stream_get_state(pa->stream) != PA_STREAM_READY)
|
||||
goto unlock_error;
|
||||
|
||||
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||
|
||||
return pa;
|
||||
|
||||
unlock_error:
|
||||
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||
error:
|
||||
pulse_free(pa);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static ssize_t pulse_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
pa_t *pa = (pa_t*)data;
|
||||
|
||||
pa_threaded_mainloop_lock(pa->mainloop);
|
||||
size_t length = pa_stream_writable_size(pa->stream);
|
||||
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||
|
||||
while (length < size)
|
||||
{
|
||||
pa_threaded_mainloop_wait(pa->mainloop);
|
||||
pa_threaded_mainloop_lock(pa->mainloop);
|
||||
length = pa_stream_writable_size(pa->stream);
|
||||
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||
|
||||
if (pa->nonblock)
|
||||
break;
|
||||
}
|
||||
|
||||
size_t write_size = length < size ? length : size;
|
||||
|
||||
pa_threaded_mainloop_lock(pa->mainloop);
|
||||
pa_stream_write(pa->stream, buf, write_size, NULL, 0LL, PA_SEEK_RELATIVE);
|
||||
pa_threaded_mainloop_unlock(pa->mainloop);
|
||||
return write_size;
|
||||
}
|
||||
|
||||
static bool pulse_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pulse_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void pulse_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
pa_t *pa = (pa_t*)data;
|
||||
pa->nonblock = state;
|
||||
}
|
||||
|
||||
static bool pulse_use_float(void *data)
|
||||
{
|
||||
(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 = {
|
||||
pulse_init,
|
||||
pulse_write,
|
||||
pulse_stop,
|
||||
pulse_start,
|
||||
pulse_set_nonblock_state,
|
||||
pulse_free,
|
||||
pulse_use_float,
|
||||
"pulse",
|
||||
pulse_write_avail,
|
||||
pulse_buffer_size,
|
||||
};
|
||||
|
||||
@@ -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 __SSNES_RESAMPLER_H
|
||||
#define __SSNES_RESAMPLER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <math.h>
|
||||
|
||||
// M_PI is left out of ISO C99 :(
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846264338327
|
||||
#endif
|
||||
|
||||
typedef struct ssnes_resampler ssnes_resampler_t;
|
||||
|
||||
struct resampler_data
|
||||
{
|
||||
const float *data_in;
|
||||
float *data_out;
|
||||
|
||||
size_t input_frames;
|
||||
size_t output_frames;
|
||||
|
||||
double ratio;
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
+31
-31
@@ -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,8 @@
|
||||
#include <roaraudio.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include "../boolean.h"
|
||||
#include "general.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -29,44 +30,47 @@ typedef struct
|
||||
bool nonblocking;
|
||||
} roar_t;
|
||||
|
||||
static void* __roar_init(const char* device, int rate, int latency)
|
||||
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;
|
||||
|
||||
roar_vs_t *vss;
|
||||
if ( (vss = roar_vs_new_simple(NULL, NULL, rate, 2, ROAR_CODEC_PCM_S_LE, 16, ROAR_DIR_PLAY, &err)) == NULL )
|
||||
if ((vss = roar_vs_new_simple(device, "SSNES", rate, 2, ROAR_CODEC_PCM_S, 16, ROAR_DIR_PLAY, &err)) == NULL)
|
||||
{
|
||||
fprintf(stderr, "roar_vs: \"%s\"\n", roar_vs_strerr(err));
|
||||
SSNES_ERR("RoarAudio: \"%s\"\n", roar_vs_strerr(err));
|
||||
free(roar);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
roar_vs_role(vss, ROAR_ROLE_GAME, NULL);
|
||||
roar->vss = vss;
|
||||
|
||||
return roar;
|
||||
}
|
||||
|
||||
static ssize_t __roar_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t ra_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
roar_t *roar = (roar_t*)data;
|
||||
ssize_t rc;
|
||||
|
||||
if ( size == 0 )
|
||||
if (size == 0)
|
||||
return 0;
|
||||
|
||||
int err;
|
||||
size_t written = 0;
|
||||
while (written < size)
|
||||
{
|
||||
if ((rc = roar_vs_write(roar->vss, (const char*)buf + written, size - written, &err)) < size)
|
||||
size_t write_amt = size - written;
|
||||
if ((rc = roar_vs_write(roar->vss, (const char*)buf + written, write_amt, &err)) < write_amt)
|
||||
{
|
||||
if (rc < 0)
|
||||
if (roar->nonblocking)
|
||||
return rc;
|
||||
else if (rc < 0)
|
||||
return -1;
|
||||
else if (roar->nonblocking)
|
||||
return 0;
|
||||
}
|
||||
written += rc;
|
||||
}
|
||||
@@ -74,45 +78,41 @@ static ssize_t __roar_write(void* data, const void* buf, size_t size)
|
||||
return size;
|
||||
}
|
||||
|
||||
static bool __roar_stop(void *data)
|
||||
static bool ra_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __roar_set_nonblock_state(void *data, bool state)
|
||||
static void ra_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
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;
|
||||
}
|
||||
|
||||
static bool __roar_start(void *data)
|
||||
static bool ra_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __roar_free(void *data)
|
||||
static void ra_free(void *data)
|
||||
{
|
||||
roar_t *roar = data;
|
||||
roar_t *roar = (roar_t*)data;
|
||||
roar_vs_close(roar->vss, ROAR_VS_TRUE, NULL);
|
||||
free(data);
|
||||
}
|
||||
|
||||
const audio_driver_t audio_roar = {
|
||||
.init = __roar_init,
|
||||
.write = __roar_write,
|
||||
.stop = __roar_stop,
|
||||
.start = __roar_start,
|
||||
.set_nonblock_state = __roar_set_nonblock_state,
|
||||
.free = __roar_free,
|
||||
.ident = "roar"
|
||||
ra_init,
|
||||
ra_write,
|
||||
ra_stop,
|
||||
ra_start,
|
||||
ra_set_nonblock_state,
|
||||
ra_free,
|
||||
NULL,
|
||||
"roar"
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+128
-56
@@ -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,131 +16,203 @@
|
||||
*/
|
||||
|
||||
|
||||
#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 "../boolean.h"
|
||||
#include "../thread.h"
|
||||
|
||||
typedef struct rsd
|
||||
{
|
||||
rsound_t *rd;
|
||||
int latency;
|
||||
int rate;
|
||||
int nonblock;
|
||||
bool nonblock;
|
||||
volatile bool has_error;
|
||||
|
||||
fifo_buffer_t *buffer;
|
||||
|
||||
slock_t *cond_lock;
|
||||
scond_t *cond;
|
||||
} rsd_t;
|
||||
|
||||
static void* __rsd_init(const char* device, int rate, int latency)
|
||||
static ssize_t audio_cb(void *data, size_t bytes, void *userdata)
|
||||
{
|
||||
rsd_t *rsd = calloc(1, sizeof(rsd_t));
|
||||
if ( rsd == NULL )
|
||||
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);
|
||||
scond_signal(rsd->cond);
|
||||
|
||||
return write_size;
|
||||
}
|
||||
|
||||
static void err_cb(void *userdata)
|
||||
{
|
||||
rsd_t *rsd = (rsd_t*)userdata;
|
||||
rsd->has_error = true;
|
||||
scond_signal(rsd->cond);
|
||||
}
|
||||
|
||||
static void *rs_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
rsd_t *rsd = (rsd_t*)calloc(1, sizeof(rsd_t));
|
||||
if (!rsd)
|
||||
return NULL;
|
||||
|
||||
rsound_t *rd;
|
||||
|
||||
if ( rsd_init(&rd) < 0 )
|
||||
if (rsd_init(&rd) < 0)
|
||||
{
|
||||
free(rsd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rsd->cond_lock = slock_new();
|
||||
rsd->cond = scond_new();
|
||||
|
||||
rsd->buffer = fifo_new(1024 * 4);
|
||||
|
||||
int channels = 2;
|
||||
int format = RSD_S16_NE;
|
||||
|
||||
rsd_set_param(rd, RSD_CHANNELS, &channels);
|
||||
rsd_set_param(rd, RSD_SAMPLERATE, &rate);
|
||||
rsd_set_param(rd, RSD_LATENCY, &latency);
|
||||
|
||||
if ( device != NULL )
|
||||
if (device)
|
||||
rsd_set_param(rd, RSD_HOST, (void*)device);
|
||||
|
||||
rsd_set_param(rd, RSD_FORMAT, &format);
|
||||
|
||||
if ( rsd_start(rd) < 0 )
|
||||
rsd_set_callback(rd, audio_cb, err_cb, 256, rsd);
|
||||
|
||||
if (rsd_start(rd) < 0)
|
||||
{
|
||||
free(rsd);
|
||||
rsd_free(rd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int min_latency = (rsd_delay_ms(rd) > latency) ? (rsd_delay_ms(rd) * 3 / 2) : latency;
|
||||
|
||||
rsd_set_param(rd, RSD_LATENCY, &min_latency);
|
||||
|
||||
rsd->rd = rd;
|
||||
rsd->latency = min_latency;
|
||||
rsd->rate = rate;
|
||||
|
||||
return rsd;
|
||||
}
|
||||
|
||||
static ssize_t __rsd_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t rs_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd_t *rsd = (rsd_t*)data;
|
||||
|
||||
if ( (rsd_delay_ms(rsd->rd) > rsd->latency || rsd_get_avail(rsd->rd) < size) && rsd->nonblock )
|
||||
return 0;
|
||||
|
||||
if ( size == 0 )
|
||||
return 0;
|
||||
|
||||
rsd_delay_wait(rsd->rd);
|
||||
if ( rsd_write(rsd->rd, buf, size) == 0 )
|
||||
if (rsd->has_error)
|
||||
return -1;
|
||||
|
||||
if ( rsd_delay_ms(rsd->rd) < rsd->latency/2 )
|
||||
if (rsd->nonblock)
|
||||
{
|
||||
int ms = rsd->latency/2;
|
||||
size_t size = (ms * rsd->rate * 4) / 1000;
|
||||
void *temp = calloc(1, size);
|
||||
rsd_write(rsd->rd, temp, size);
|
||||
free(temp);
|
||||
rsd_callback_lock(rsd->rd);
|
||||
size_t avail = fifo_write_avail(rsd->buffer);
|
||||
size_t write_amt = avail > size ? size : avail;
|
||||
fifo_write(rsd->buffer, buf, write_amt);
|
||||
rsd_callback_unlock(rsd->rd);
|
||||
return write_amt;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t written = 0;
|
||||
while (written < size && !rsd->has_error)
|
||||
{
|
||||
rsd_callback_lock(rsd->rd);
|
||||
size_t avail = fifo_write_avail(rsd->buffer);
|
||||
|
||||
return size;
|
||||
if (avail == 0)
|
||||
{
|
||||
rsd_callback_unlock(rsd->rd);
|
||||
if (!rsd->has_error)
|
||||
{
|
||||
slock_lock(rsd->cond_lock);
|
||||
scond_wait(rsd->cond, rsd->cond_lock);
|
||||
slock_unlock(rsd->cond_lock);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t write_amt = size - written > avail ? avail : size - written;
|
||||
fifo_write(rsd->buffer, (const char*)buf + written, write_amt);
|
||||
rsd_callback_unlock(rsd->rd);
|
||||
written += write_amt;
|
||||
}
|
||||
}
|
||||
return written;
|
||||
}
|
||||
}
|
||||
|
||||
static bool __rsd_stop(void *data)
|
||||
static bool rs_stop(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd_t *rsd = (rsd_t*)data;
|
||||
rsd_stop(rsd->rd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __rsd_set_nonblock_state(void *data, bool state)
|
||||
static void rs_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd_t *rsd = (rsd_t*)data;
|
||||
rsd->nonblock = state;
|
||||
}
|
||||
|
||||
static bool __rsd_start(void *data)
|
||||
static bool rs_start(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
if ( rsd_start(rsd->rd) < 0)
|
||||
rsd_t *rsd = (rsd_t*)data;
|
||||
if (rsd_start(rsd->rd) < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void __rsd_free(void *data)
|
||||
static void rs_free(void *data)
|
||||
{
|
||||
rsd_t *rsd = data;
|
||||
rsd_t *rsd = (rsd_t*)data;
|
||||
|
||||
rsd_stop(rsd->rd);
|
||||
rsd_free(rsd->rd);
|
||||
|
||||
fifo_free(rsd->buffer);
|
||||
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 = __rsd_init,
|
||||
.write = __rsd_write,
|
||||
.stop = __rsd_stop,
|
||||
.start = __rsd_start,
|
||||
.set_nonblock_state = __rsd_set_nonblock_state,
|
||||
.free = __rsd_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,54 +16,42 @@
|
||||
*/
|
||||
|
||||
|
||||
#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 "buffer.h"
|
||||
#include "../thread.h"
|
||||
|
||||
#include "../general.h"
|
||||
#include "../fifo_buffer.h"
|
||||
|
||||
typedef struct sdl_audio
|
||||
{
|
||||
bool nonblock;
|
||||
|
||||
SDL_mutex *lock;
|
||||
SDL_cond *cond;
|
||||
rsound_fifo_buffer_t *buffer;
|
||||
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 = rsnd_fifo_read_avail(sdl->buffer);
|
||||
size_t write_size = len > avail ? avail : len;
|
||||
rsnd_fifo_read(sdl->buffer, stream, write_size);
|
||||
SDL_CondSignal(sdl->cond);
|
||||
size_t avail = fifo_read_avail(sdl->buffer);
|
||||
size_t write_size = len > (int)avail ? avail : len;
|
||||
fifo_read(sdl->buffer, stream, write_size);
|
||||
scond_signal(sdl->cond);
|
||||
|
||||
// If underrun, fill rest with silence.
|
||||
memset(stream + write_size, 0, len - write_size);
|
||||
}
|
||||
|
||||
// Interesting hack from http://www-graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
static inline uint32_t next_pow2(uint32_t v)
|
||||
{
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
return v;
|
||||
}
|
||||
|
||||
static inline int find_num_frames(int rate, int latency)
|
||||
{
|
||||
int frames = (rate * latency) / 1000;
|
||||
@@ -71,27 +59,26 @@ static inline int find_num_frames(int rate, int latency)
|
||||
return next_pow2(frames);
|
||||
}
|
||||
|
||||
static void* sdl_audio_init(const char* device, int rate, int latency)
|
||||
static void *sdl_audio_init(const char *device, unsigned rate, unsigned latency)
|
||||
{
|
||||
(void)device;
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
|
||||
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;
|
||||
|
||||
@@ -103,18 +90,18 @@ static void* sdl_audio_init(const char* device, int rate, int 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));
|
||||
|
||||
// Create a buffer twice as big as needed and prefill the buffer.
|
||||
size_t bufsize = out.samples * 4 * sizeof(int16_t);
|
||||
void *tmp = calloc(1, bufsize);
|
||||
sdl->buffer = rsnd_fifo_new(bufsize);
|
||||
sdl->buffer = fifo_new(bufsize);
|
||||
if (tmp)
|
||||
{
|
||||
rsnd_fifo_write(sdl->buffer, tmp, bufsize);
|
||||
fifo_write(sdl->buffer, tmp, bufsize);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
@@ -122,17 +109,17 @@ static void* sdl_audio_init(const char* device, int rate, int latency)
|
||||
return sdl;
|
||||
}
|
||||
|
||||
static ssize_t sdl_audio_write(void* data, const void* buf, size_t size)
|
||||
static ssize_t sdl_audio_write(void *data, const void *buf, size_t size)
|
||||
{
|
||||
sdl_audio_t *sdl = data;
|
||||
sdl_audio_t *sdl = (sdl_audio_t*)data;
|
||||
|
||||
ssize_t ret = 0;
|
||||
if (sdl->nonblock)
|
||||
{
|
||||
SDL_LockAudio();
|
||||
size_t avail = rsnd_fifo_write_avail(sdl->buffer);
|
||||
size_t avail = fifo_write_avail(sdl->buffer);
|
||||
size_t write_amt = avail > size ? size : avail;
|
||||
rsnd_fifo_write(sdl->buffer, buf, write_amt);
|
||||
fifo_write(sdl->buffer, buf, write_amt);
|
||||
SDL_UnlockAudio();
|
||||
ret = write_amt;
|
||||
}
|
||||
@@ -142,19 +129,19 @@ static ssize_t sdl_audio_write(void* data, const void* buf, size_t size)
|
||||
while (written < size)
|
||||
{
|
||||
SDL_LockAudio();
|
||||
size_t avail = rsnd_fifo_write_avail(sdl->buffer);
|
||||
size_t avail = fifo_write_avail(sdl->buffer);
|
||||
|
||||
if (avail == 0)
|
||||
{
|
||||
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
|
||||
{
|
||||
size_t write_amt = size - written > avail ? avail : size - written;
|
||||
rsnd_fifo_write(sdl->buffer, (const char*)buf + written, write_amt);
|
||||
fifo_write(sdl->buffer, (const char*)buf + written, write_amt);
|
||||
SDL_UnlockAudio();
|
||||
written += write_amt;
|
||||
}
|
||||
@@ -181,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;
|
||||
}
|
||||
|
||||
@@ -190,28 +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)
|
||||
{
|
||||
rsnd_fifo_free(sdl->buffer);
|
||||
SDL_DestroyMutex(sdl->lock);
|
||||
SDL_DestroyCond(sdl->cond);
|
||||
fifo_free(sdl->buffer);
|
||||
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
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
#ifndef AUDIO_UTILS_H
|
||||
#define AUDIO_UTILS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if __SSE2__
|
||||
#define audio_convert_s16_to_float audio_convert_s16_to_float_SSE2
|
||||
#define audio_convert_float_to_s16 audio_convert_float_to_s16_SSE2
|
||||
|
||||
void audio_convert_s16_to_float_SSE2(float *out,
|
||||
const int16_t *in, size_t samples);
|
||||
|
||||
void audio_convert_float_to_s16_SSE2(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
|
||||
#elif __ALTIVEC__
|
||||
#define audio_convert_s16_to_float audio_convert_s16_to_float_altivec
|
||||
#define audio_convert_float_to_s16 audio_convert_float_to_s16_altivec
|
||||
|
||||
void audio_convert_s16_to_float_altivec(float *out,
|
||||
const int16_t *in, size_t samples);
|
||||
|
||||
void audio_convert_float_to_s16_altivec(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
|
||||
#else
|
||||
#define audio_convert_s16_to_float audio_convert_s16_to_float_C
|
||||
#define audio_convert_float_to_s16 audio_convert_float_to_s16_C
|
||||
#endif
|
||||
|
||||
void audio_convert_s16_to_float_C(float *out,
|
||||
const int16_t *in, size_t samples);
|
||||
void audio_convert_float_to_s16_C(int16_t *out,
|
||||
const float *in, size_t samples);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
Simple C interface for XAudio2
|
||||
Author: Hans-Kristian Arntzen
|
||||
License: Public Domain
|
||||
*/
|
||||
|
||||
#ifndef XAUDIO_C_H
|
||||
#define XAUDIO_C_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct xaudio2 xaudio2_t;
|
||||
|
||||
xaudio2_t* xaudio2_new(unsigned samplerate, unsigned channels, size_t bufsize, 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
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
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>
|
||||
#include <basetyps.h>
|
||||
#include <objbase.h>
|
||||
#include <mmreg.h>
|
||||
|
||||
#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)
|
||||
|
||||
DEFINE_CLSID_X(XAudio2, e21a7345, eb21, 468e, be, 50, 80, 4d, b9, 7c, f7, 08);
|
||||
DEFINE_IID_X(IXAudio2, 8bcf1f58, 9fe7, 4583, 8a, c6, e2, ad, c4, 65, c8, bb);
|
||||
|
||||
#ifndef INTERFACE
|
||||
#define INTERFACE void
|
||||
#endif
|
||||
|
||||
#define XAUDIO2_COMMIT_NOW 0
|
||||
#define XAUDIO2_DEFAULT_CHANNELS 0
|
||||
#define XAUDIO2_DEFAULT_SAMPLERATE 0
|
||||
#define XAUDIO2_DEFAULT_FREQ_RATIO 4.0f
|
||||
#define XAUDIO2_DEBUG_ENGINE 0x0001
|
||||
#define XAUDIO2_VOICE_NOSRC 0x0004
|
||||
|
||||
typedef enum XAUDIO2_DEVICE_ROLE
|
||||
{
|
||||
NotDefaultDevice = 0x0,
|
||||
DefaultConsoleDevice = 0x1,
|
||||
DefaultMultimediaDevice = 0x2,
|
||||
DefaultCommunicationsDevice = 0x4,
|
||||
DefaultGameDevice = 0x8,
|
||||
GlobalDefaultDevice = 0xf,
|
||||
InvalidDeviceRole = ~GlobalDefaultDevice
|
||||
} XAUDIO2_DEVICE_ROLE;
|
||||
|
||||
typedef 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];
|
||||
WCHAR DisplayName[256];
|
||||
XAUDIO2_DEVICE_ROLE Role;
|
||||
WAVEFORMATEXTENSIBLE OutputFormat;
|
||||
} XAUDIO2_DEVICE_DETAILS;
|
||||
|
||||
typedef struct XAUDIO2_VOICE_DETAILS XAUDIO2_VOICE_DETAILS;
|
||||
typedef struct XAUDIO2_VOICE_SENDS XAUDIO2_VOICE_SENDS;
|
||||
typedef struct XAUDIO2_EFFECT_DESCRIPTOR XAUDIO2_EFFECT_DESCRIPTOR;
|
||||
typedef struct XAUDIO2_EFFECT_CHAIN XAUDIO2_EFFECT_CHAIN;
|
||||
typedef struct XAUDIO2_FILTER_PARAMETERS XAUDIO2_FILTER_PARAMETERS;
|
||||
typedef struct XAUDIO2_BUFFER_WMA XAUDIO2_BUFFER_WMA;
|
||||
typedef struct XAUDIO2_VOICE_STATE XAUDIO2_VOICE_STATE;
|
||||
typedef struct XAUDIO2_PERFORMANCE_DATA XAUDIO2_PERFORMANCE_DATA;
|
||||
typedef struct XAUDIO2_DEBUG_CONFIGURATION XAUDIO2_DEBUG_CONFIGURATION;
|
||||
typedef struct IXAudio2EngineCallback IXAudio2EngineCallback;
|
||||
typedef struct IXAudio2SubmixVoice IXAudio2SubmixVoice;
|
||||
|
||||
typedef struct XAUDIO2_BUFFER
|
||||
{
|
||||
UINT32 Flags;
|
||||
UINT32 AudioBytes;
|
||||
const BYTE* pAudioData;
|
||||
UINT32 PlayBegin;
|
||||
UINT32 PlayLength;
|
||||
UINT32 LoopBegin;
|
||||
UINT32 LoopLength;
|
||||
UINT32 LoopCount;
|
||||
void *pContext;
|
||||
} XAUDIO2_BUFFER;
|
||||
|
||||
DECLARE_INTERFACE(IXAudio2VoiceCallback)
|
||||
{
|
||||
STDMETHOD_(void, OnVoiceProcessingPassStart) (THIS_ UINT32 BytesRequired) PURE;
|
||||
STDMETHOD_(void, OnVoiceProcessingPassEnd) (THIS) PURE;
|
||||
STDMETHOD_(void, OnStreamEnd) (THIS) PURE;
|
||||
STDMETHOD_(void, OnBufferStart) (THIS_ void *pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnBufferEnd) (THIS_ void *pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnLoopEnd) (THIS_ void *pBufferContext) PURE;
|
||||
STDMETHOD_(void, OnVoiceError) (THIS_ void *pBufferContext, HRESULT Error) PURE;
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE(IXAudio2Voice)
|
||||
{
|
||||
#define Declare_IXAudio2Voice_Methods() \
|
||||
STDMETHOD_(void, GetVoiceDetails) (THIS_ XAUDIO2_VOICE_DETAILS* pVoiceDetails) PURE; \
|
||||
STDMETHOD(SetOutputVoices) (THIS_ const XAUDIO2_VOICE_SENDS* pSendList) PURE; \
|
||||
STDMETHOD(SetEffectChain) (THIS_ const XAUDIO2_EFFECT_CHAIN* pEffectChain) PURE; \
|
||||
STDMETHOD(EnableEffect) (THIS_ UINT32 EffectIndex, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD(DisableEffect) (THIS_ UINT32 EffectIndex, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetEffectState) (THIS_ UINT32 EffectIndex, BOOL* pEnabled) PURE; \
|
||||
STDMETHOD(SetEffectParameters) (THIS_ UINT32 EffectIndex, \
|
||||
const void *pParameters, \
|
||||
UINT32 ParametersByteSize, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD(GetEffectParameters) (THIS_ UINT32 EffectIndex, void *pParameters, \
|
||||
UINT32 ParametersByteSize) PURE; \
|
||||
STDMETHOD(SetFilterParameters) (THIS_ const XAUDIO2_FILTER_PARAMETERS* pParameters, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetFilterParameters) (THIS_ XAUDIO2_FILTER_PARAMETERS* pParameters) PURE; \
|
||||
STDMETHOD(SetVolume) (THIS_ float Volume, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetVolume) (THIS_ float* pVolume) PURE; \
|
||||
STDMETHOD(SetChannelVolumes) (THIS_ UINT32 Channels, const float* pVolumes, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetChannelVolumes) (THIS_ UINT32 Channels, float* pVolumes) PURE; \
|
||||
STDMETHOD(SetOutputMatrix) (THIS_ IXAudio2Voice* pDestinationVoice, \
|
||||
UINT32 SourceChannels, UINT32 DestinationChannels, \
|
||||
const float* pLevelMatrix, \
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE; \
|
||||
STDMETHOD_(void, GetOutputMatrix) (THIS_ IXAudio2Voice* pDestinationVoice, \
|
||||
UINT32 SourceChannels, UINT32 DestinationChannels, \
|
||||
float* pLevelMatrix) PURE; \
|
||||
STDMETHOD_(void, DestroyVoice) (THIS) PURE
|
||||
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2MasteringVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2SourceVoice, IXAudio2Voice)
|
||||
{
|
||||
Declare_IXAudio2Voice_Methods();
|
||||
STDMETHOD(Start) (THIS_ UINT32 Flags, UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD(Stop) (THIS_ UINT32 Flags, UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD(SubmitSourceBuffer) (THIS_ const XAUDIO2_BUFFER* pBuffer, const XAUDIO2_BUFFER_WMA* pBufferWMA X2DEFAULT(NULL)) PURE;
|
||||
STDMETHOD(FlushSourceBuffers) (THIS) PURE;
|
||||
STDMETHOD(Discontinuity) (THIS) PURE;
|
||||
STDMETHOD(ExitLoop) (THIS_ UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD_(void, GetState) (THIS_ XAUDIO2_VOICE_STATE* pVoiceState) PURE;
|
||||
STDMETHOD(SetFrequencyRatio) (THIS_ float Ratio,
|
||||
UINT32 OperationSet X2DEFAULT(XAUDIO2_COMMIT_NOW)) PURE;
|
||||
STDMETHOD_(void, GetFrequencyRatio) (THIS_ float* pRatio) PURE;
|
||||
};
|
||||
|
||||
DECLARE_INTERFACE_(IXAudio2, IUnknown)
|
||||
{
|
||||
STDMETHOD(QueryInterface) (THIS_ REFIID riid, void** ppvInterface) PURE;
|
||||
STDMETHOD_(ULONG, AddRef) (THIS) PURE;
|
||||
STDMETHOD_(ULONG, Release) (THIS) PURE;
|
||||
STDMETHOD(GetDeviceCount) (THIS_ UINT32* pCount) PURE;
|
||||
STDMETHOD(GetDeviceDetails) (THIS_ UINT32 Index, XAUDIO2_DEVICE_DETAILS* pDeviceDetails) PURE;
|
||||
STDMETHOD(Initialize) (THIS_ UINT32 Flags X2DEFAULT(0),
|
||||
XAUDIO2_PROCESSOR XAudio2Processor X2DEFAULT(XAUDIO2_DEFAULT_PROCESSOR)) PURE;
|
||||
STDMETHOD(RegisterForCallbacks) (IXAudio2EngineCallback* pCallback) PURE;
|
||||
STDMETHOD_(void, UnregisterForCallbacks) (IXAudio2EngineCallback* pCallback) PURE;
|
||||
STDMETHOD(CreateSourceVoice) (THIS_ IXAudio2SourceVoice** ppSourceVoice,
|
||||
const WAVEFORMATEX* pSourceFormat,
|
||||
UINT32 Flags X2DEFAULT(0),
|
||||
float MaxFrequencyRatio X2DEFAULT(XAUDIO2_DEFAULT_FREQ_RATIO),
|
||||
IXAudio2VoiceCallback* pCallback X2DEFAULT(NULL),
|
||||
const XAUDIO2_VOICE_SENDS* pSendList X2DEFAULT(NULL),
|
||||
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
|
||||
STDMETHOD(CreateSubmixVoice) (THIS_ IXAudio2SubmixVoice** ppSubmixVoice,
|
||||
UINT32 InputChannels, UINT32 InputSampleRate,
|
||||
UINT32 Flags X2DEFAULT(0), UINT32 ProcessingStage X2DEFAULT(0),
|
||||
const XAUDIO2_VOICE_SENDS* pSendList X2DEFAULT(NULL),
|
||||
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
|
||||
STDMETHOD(CreateMasteringVoice) (THIS_ IXAudio2MasteringVoice** ppMasteringVoice,
|
||||
UINT32 InputChannels X2DEFAULT(XAUDIO2_DEFAULT_CHANNELS),
|
||||
UINT32 InputSampleRate X2DEFAULT(XAUDIO2_DEFAULT_SAMPLERATE),
|
||||
UINT32 Flags X2DEFAULT(0), UINT32 DeviceIndex X2DEFAULT(0),
|
||||
const XAUDIO2_EFFECT_CHAIN* pEffectChain X2DEFAULT(NULL)) PURE;
|
||||
STDMETHOD(StartEngine) (THIS) PURE;
|
||||
STDMETHOD_(void, StopEngine) (THIS) PURE;
|
||||
STDMETHOD(CommitChanges) (THIS_ UINT32 OperationSet) PURE;
|
||||
STDMETHOD_(void, GetPerformanceData) (THIS_ XAUDIO2_PERFORMANCE_DATA* pPerfData) PURE;
|
||||
STDMETHOD_(void, SetDebugConfiguration) (THIS_ const XAUDIO2_DEBUG_CONFIGURATION* pDebugConfiguration,
|
||||
void *pReserved X2DEFAULT(NULL)) PURE;
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
#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 = IXAudio2_Initialize(pXAudio2, 0, XAUDIO2_DEFAULT_PROCESSOR);
|
||||
if (SUCCEEDED(hr))
|
||||
*ppXAudio2 = pXAudio2;
|
||||
else
|
||||
IXAudio2_Release(pXAudio2);
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
/* 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 "../driver.h"
|
||||
#include <stdlib.h>
|
||||
#include "xaudio-c/xaudio-c.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)
|
||||
{
|
||||
if (latency < 8)
|
||||
latency = 8; // Do not allow shenanigans.
|
||||
|
||||
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->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 = (xa_t*)data;
|
||||
if (xa->nonblock)
|
||||
{
|
||||
size_t avail = xaudio2_write_avail(xa->xa);
|
||||
if (avail == 0)
|
||||
return 0;
|
||||
if (avail < size)
|
||||
size = avail;
|
||||
}
|
||||
|
||||
size_t ret = xaudio2_write(xa->xa, buf, size);
|
||||
if (ret == 0 && size > 0)
|
||||
return -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool xa_stop(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void xa_set_nonblock_state(void *data, bool state)
|
||||
{
|
||||
xa_t *xa = (xa_t*)data;
|
||||
xa->nonblock = state;
|
||||
}
|
||||
|
||||
static bool xa_start(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool xa_use_float(void *data)
|
||||
{
|
||||
(void)data;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void xa_free(void *data)
|
||||
{
|
||||
xa_t *xa = (xa_t*)data;
|
||||
if (xa)
|
||||
{
|
||||
if (xa->xa)
|
||||
xaudio2_free(xa->xa);
|
||||
free(xa);
|
||||
}
|
||||
}
|
||||
|
||||
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 = {
|
||||
xa_init,
|
||||
xa_write,
|
||||
xa_stop,
|
||||
xa_start,
|
||||
xa_set_nonblock_state,
|
||||
xa_free,
|
||||
xa_use_float,
|
||||
"xaudio",
|
||||
xa_write_avail,
|
||||
xa_buffer_size,
|
||||
};
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/* 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 "autosave.h"
|
||||
#include "thread.h"
|
||||
#include <stdlib.h>
|
||||
#include "boolean.h"
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "general.h"
|
||||
|
||||
struct autosave
|
||||
{
|
||||
volatile bool quit;
|
||||
slock_t *lock;
|
||||
|
||||
slock_t *cond_lock;
|
||||
scond_t *cond;
|
||||
sthread_t *thread;
|
||||
|
||||
void *buffer;
|
||||
const void *snes_buffer;
|
||||
const char *path;
|
||||
size_t bufsize;
|
||||
unsigned interval;
|
||||
};
|
||||
|
||||
static void autosave_thread(void *data)
|
||||
{
|
||||
autosave_t *save = (autosave_t*)data;
|
||||
|
||||
bool first_log = true;
|
||||
|
||||
while (!save->quit)
|
||||
{
|
||||
autosave_lock(save);
|
||||
bool differ = memcmp(save->buffer, save->snes_buffer, save->bufsize) != 0;
|
||||
if (differ)
|
||||
memcpy(save->buffer, save->snes_buffer, save->bufsize);
|
||||
autosave_unlock(save);
|
||||
|
||||
if (differ)
|
||||
{
|
||||
// Should probably deal with this more elegantly.
|
||||
FILE *file = fopen(save->path, "wb");
|
||||
if (file)
|
||||
{
|
||||
// Avoid spamming down stderr ... :)
|
||||
if (first_log)
|
||||
{
|
||||
SSNES_LOG("Autosaving SRAM to \"%s\", will continue to check every %u seconds ...\n", save->path, save->interval);
|
||||
first_log = false;
|
||||
}
|
||||
else
|
||||
SSNES_LOG("SRAM changed ... autosaving ...\n");
|
||||
|
||||
bool failed = false;
|
||||
failed |= fwrite(save->buffer, 1, save->bufsize, file) != save->bufsize;
|
||||
failed |= fflush(file) != 0;
|
||||
failed |= fclose(file) != 0;
|
||||
if (failed)
|
||||
SSNES_WARN("Failed to autosave SRAM. Disk might be full.\n");
|
||||
}
|
||||
}
|
||||
|
||||
slock_lock(save->cond_lock);
|
||||
if (!save->quit)
|
||||
scond_wait_timeout(save->cond, save->cond_lock, save->interval * 1000);
|
||||
slock_unlock(save->cond_lock);
|
||||
}
|
||||
}
|
||||
|
||||
autosave_t *autosave_new(const char *path, const void *data, size_t size, unsigned interval)
|
||||
{
|
||||
autosave_t *handle = (autosave_t*)calloc(1, sizeof(*handle));
|
||||
if (!handle)
|
||||
return NULL;
|
||||
|
||||
handle->bufsize = size;
|
||||
handle->interval = interval;
|
||||
handle->path = path;
|
||||
handle->buffer = malloc(size);
|
||||
handle->snes_buffer = data;
|
||||
|
||||
if (!handle->buffer)
|
||||
{
|
||||
free(handle);
|
||||
return NULL;
|
||||
}
|
||||
memcpy(handle->buffer, handle->snes_buffer, handle->bufsize);
|
||||
|
||||
handle->lock = slock_new();
|
||||
handle->cond_lock = slock_new();
|
||||
handle->cond = scond_new();
|
||||
|
||||
handle->thread = sthread_create(autosave_thread, handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void autosave_lock(autosave_t *handle)
|
||||
{
|
||||
slock_lock(handle->lock);
|
||||
}
|
||||
|
||||
void autosave_unlock(autosave_t *handle)
|
||||
{
|
||||
slock_unlock(handle->lock);
|
||||
}
|
||||
|
||||
void autosave_free(autosave_t *handle)
|
||||
{
|
||||
slock_lock(handle->cond_lock);
|
||||
handle->quit = true;
|
||||
slock_unlock(handle->cond_lock);
|
||||
scond_signal(handle->cond);
|
||||
sthread_join(handle->thread);
|
||||
|
||||
slock_free(handle->lock);
|
||||
slock_free(handle->cond_lock);
|
||||
scond_free(handle->cond);
|
||||
|
||||
free(handle->buffer);
|
||||
free(handle);
|
||||
}
|
||||
|
||||
void lock_autosave(void)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof(g_extern.autosave)/sizeof(g_extern.autosave[0]); i++)
|
||||
{
|
||||
if (g_extern.autosave[i])
|
||||
autosave_lock(g_extern.autosave[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void unlock_autosave(void)
|
||||
{
|
||||
for (unsigned i = 0; i < sizeof(g_extern.autosave)/sizeof(g_extern.autosave[0]); i++)
|
||||
{
|
||||
if (g_extern.autosave[i])
|
||||
autosave_unlock(g_extern.autosave[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/* 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_AUTOSAVE_H
|
||||
#define __SSNES_AUTOSAVE_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct autosave autosave_t;
|
||||
|
||||
autosave_t *autosave_new(const char *path, const void *data, size_t size, unsigned interval);
|
||||
void autosave_lock(autosave_t *handle);
|
||||
void autosave_unlock(autosave_t *handle);
|
||||
void autosave_free(autosave_t *handle);
|
||||
|
||||
void lock_autosave(void);
|
||||
void unlock_autosave(void);
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,390 @@
|
||||
/* 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 "cheats.h"
|
||||
#include "hash.h"
|
||||
#include "dynamic.h"
|
||||
#include "general.h"
|
||||
#include "compat/strl.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
#include "conf/config_file.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/tree.h>
|
||||
|
||||
struct cheat
|
||||
{
|
||||
char *desc;
|
||||
bool state;
|
||||
char *code;
|
||||
};
|
||||
|
||||
struct cheat_manager
|
||||
{
|
||||
struct cheat *cheats;
|
||||
unsigned ptr;
|
||||
unsigned size;
|
||||
unsigned buf_size;
|
||||
};
|
||||
|
||||
static char *strcat_alloc(char *dest, const char *input)
|
||||
{
|
||||
size_t dest_len = dest ? strlen(dest) : 0;
|
||||
size_t input_len = strlen(input);
|
||||
size_t required_len = dest_len + input_len + 1;
|
||||
|
||||
char *output = (char*)realloc(dest, required_len);
|
||||
if (!output)
|
||||
return NULL;
|
||||
|
||||
if (dest)
|
||||
strlcat(output, input, required_len);
|
||||
else
|
||||
strlcpy(output, input, required_len);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static bool xml_grab_cheat(struct cheat *cht, xmlNodePtr ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return false;
|
||||
|
||||
memset(cht, 0, sizeof(struct cheat));
|
||||
bool first = true;
|
||||
|
||||
for (; ptr; ptr = ptr->next)
|
||||
{
|
||||
if (strcmp((const char*)ptr->name, "description") == 0)
|
||||
{
|
||||
cht->desc = (char*)xmlNodeGetContent(ptr);
|
||||
}
|
||||
else if (strcmp((const char*)ptr->name, "code") == 0)
|
||||
{
|
||||
if (!first)
|
||||
{
|
||||
cht->code = strcat_alloc(cht->code, "+");
|
||||
if (!cht->code)
|
||||
return false;
|
||||
}
|
||||
|
||||
xmlChar *code = xmlNodeGetContent(ptr);
|
||||
if (!code)
|
||||
return false;
|
||||
|
||||
cht->code = strcat_alloc(cht->code, (const char*)code);
|
||||
xmlFree(code);
|
||||
if (!cht->code)
|
||||
return false;
|
||||
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool xml_grab_cheats(cheat_manager_t *handle, xmlNodePtr ptr)
|
||||
{
|
||||
for (; ptr; ptr = ptr->next)
|
||||
{
|
||||
if (strcmp((const char*)ptr->name, "name") == 0)
|
||||
{
|
||||
xmlChar *name = xmlNodeGetContent(ptr);
|
||||
if (name)
|
||||
{
|
||||
SSNES_LOG("Found cheat for game: \"%s\"\n", name);
|
||||
xmlFree(name);
|
||||
}
|
||||
}
|
||||
else if (strcmp((const char*)ptr->name, "cheat") == 0)
|
||||
{
|
||||
if (handle->size == handle->buf_size)
|
||||
{
|
||||
handle->buf_size *= 2;
|
||||
handle->cheats = (struct cheat*)realloc(handle->cheats, handle->buf_size * sizeof(struct cheat));
|
||||
if (!handle->cheats)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (xml_grab_cheat(&handle->cheats[handle->size], ptr->children))
|
||||
handle->size++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cheat_manager_apply_cheats(cheat_manager_t *handle)
|
||||
{
|
||||
unsigned index = 0;
|
||||
psnes_cheat_reset();
|
||||
for (unsigned i = 0; i < handle->size; i++)
|
||||
{
|
||||
if (handle->cheats[i].state)
|
||||
psnes_cheat_set(index++, true, handle->cheats[i].code);
|
||||
}
|
||||
}
|
||||
|
||||
static void cheat_manager_load_config(cheat_manager_t *handle, const char *path, const char *sha256)
|
||||
{
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
if (!(*path))
|
||||
return;
|
||||
|
||||
config_file_t *conf = config_file_new(path);
|
||||
if (!conf)
|
||||
return;
|
||||
|
||||
char *str;
|
||||
if (!config_get_string(conf, sha256, &str))
|
||||
{
|
||||
config_file_free(conf);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *num = strtok(str, ";");
|
||||
while (num)
|
||||
{
|
||||
unsigned index = strtoul(num, NULL, 0);
|
||||
if (index < handle->size)
|
||||
handle->cheats[index].state = true;
|
||||
|
||||
num = strtok(NULL, ";");
|
||||
}
|
||||
|
||||
free(str);
|
||||
config_file_free(conf);
|
||||
|
||||
cheat_manager_apply_cheats(handle);
|
||||
|
||||
#else
|
||||
(void)handle;
|
||||
(void)path;
|
||||
(void)sha256;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void cheat_manager_save_config(cheat_manager_t *handle, const char *path, const char *sha256)
|
||||
{
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
if (!(*path))
|
||||
return;
|
||||
|
||||
config_file_t *conf = config_file_new(path);
|
||||
if (!conf)
|
||||
conf = config_file_new(NULL);
|
||||
|
||||
if (!conf)
|
||||
{
|
||||
SSNES_ERR("Cannot save XML cheat settings.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char conf_str[512] = {0};
|
||||
char tmp[32] = {0};
|
||||
|
||||
for (unsigned i = 0; i < handle->size; i++)
|
||||
{
|
||||
if (handle->cheats[i].state)
|
||||
{
|
||||
snprintf(tmp, sizeof(tmp), "%u;", i);
|
||||
strlcat(conf_str, tmp, sizeof(conf_str));
|
||||
}
|
||||
}
|
||||
|
||||
if (*conf_str)
|
||||
conf_str[strlen(conf_str) - 1] = '\0'; // Remove the trailing ';'
|
||||
|
||||
config_set_string(conf, sha256, conf_str);
|
||||
|
||||
if (!config_file_write(conf, path))
|
||||
SSNES_ERR("Failed to write XML cheat settings to \"%s\". Check permissions.\n", path);
|
||||
|
||||
config_file_free(conf);
|
||||
|
||||
#else
|
||||
(void)handle;
|
||||
(void)path;
|
||||
(void)sha256;
|
||||
#endif
|
||||
}
|
||||
|
||||
cheat_manager_t* cheat_manager_new(const char *path)
|
||||
{
|
||||
LIBXML_TEST_VERSION;
|
||||
|
||||
psnes_cheat_reset();
|
||||
|
||||
xmlParserCtxtPtr ctx = NULL;
|
||||
xmlDocPtr doc = NULL;
|
||||
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 = (struct cheat*)calloc(handle->buf_size, sizeof(struct cheat));
|
||||
if (!handle->cheats)
|
||||
{
|
||||
handle->buf_size = 0;
|
||||
goto error;
|
||||
}
|
||||
|
||||
ctx = xmlNewParserCtxt();
|
||||
if (!ctx)
|
||||
goto error;
|
||||
|
||||
doc = xmlCtxtReadFile(ctx, path, NULL, 0);
|
||||
if (!doc)
|
||||
{
|
||||
SSNES_ERR("Failed to parse XML file: %s\n", path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (ctx->valid == 0)
|
||||
{
|
||||
SSNES_ERR("Cannot validate XML file: %s\n", path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
head = xmlDocGetRootElement(doc);
|
||||
for (cur = head; cur; cur = cur->next)
|
||||
{
|
||||
if (cur->type == XML_ELEMENT_NODE && strcmp((const char*)cur->name, "database") == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!cur)
|
||||
goto error;
|
||||
|
||||
for (cur = cur->children; cur; cur = cur->next)
|
||||
{
|
||||
if (cur->type != XML_ELEMENT_NODE)
|
||||
continue;
|
||||
|
||||
if (strcmp((const char*)cur->name, "cartridge") == 0)
|
||||
{
|
||||
xmlChar *sha256 = xmlGetProp(cur, (const xmlChar*)"sha256");
|
||||
if (!sha256)
|
||||
continue;
|
||||
|
||||
if (strcmp((const char*)sha256, g_extern.sha256) == 0)
|
||||
{
|
||||
xmlFree(sha256);
|
||||
break;
|
||||
}
|
||||
|
||||
xmlFree(sha256);
|
||||
}
|
||||
}
|
||||
|
||||
if (!cur)
|
||||
goto error;
|
||||
|
||||
if (!xml_grab_cheats(handle, cur->children))
|
||||
{
|
||||
SSNES_ERR("Failed to grab cheats. This should not happen.\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (handle->size == 0)
|
||||
{
|
||||
SSNES_ERR("Did not find any cheats in XML file: %s\n", path);
|
||||
goto error;
|
||||
}
|
||||
|
||||
cheat_manager_load_config(handle, g_settings.cheat_settings_path, g_extern.sha256);
|
||||
|
||||
xmlFreeDoc(doc);
|
||||
xmlFreeParserCtxt(ctx);
|
||||
return handle;
|
||||
|
||||
error:
|
||||
cheat_manager_free(handle);
|
||||
if (doc)
|
||||
xmlFreeDoc(doc);
|
||||
if (ctx)
|
||||
xmlFreeParserCtxt(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void cheat_manager_free(cheat_manager_t *handle)
|
||||
{
|
||||
if (!handle)
|
||||
return;
|
||||
|
||||
if (handle->cheats)
|
||||
{
|
||||
cheat_manager_save_config(handle, g_settings.cheat_settings_path, g_extern.sha256);
|
||||
for (unsigned i = 0; i < handle->size; i++)
|
||||
{
|
||||
xmlFree(handle->cheats[i].desc);
|
||||
free(handle->cheats[i].code);
|
||||
}
|
||||
|
||||
free(handle->cheats);
|
||||
}
|
||||
|
||||
free(handle);
|
||||
}
|
||||
|
||||
static void cheat_manager_update(cheat_manager_t *handle)
|
||||
{
|
||||
msg_queue_clear(g_extern.msg_queue);
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof(msg), "Cheat: #%u [%s]: %s", handle->ptr, handle->cheats[handle->ptr].state ? "ON" : "OFF", handle->cheats[handle->ptr].desc);
|
||||
msg_queue_push(g_extern.msg_queue, msg, 1, 180);
|
||||
SSNES_LOG("%s\n", msg);
|
||||
}
|
||||
|
||||
|
||||
void cheat_manager_toggle(cheat_manager_t *handle)
|
||||
{
|
||||
handle->cheats[handle->ptr].state ^= true;
|
||||
cheat_manager_apply_cheats(handle);
|
||||
cheat_manager_update(handle);
|
||||
}
|
||||
|
||||
void cheat_manager_index_next(cheat_manager_t *handle)
|
||||
{
|
||||
handle->ptr = (handle->ptr + 1) % handle->size;
|
||||
cheat_manager_update(handle);
|
||||
}
|
||||
|
||||
void cheat_manager_index_prev(cheat_manager_t *handle)
|
||||
{
|
||||
if (handle->ptr == 0)
|
||||
handle->ptr = handle->size - 1;
|
||||
else
|
||||
handle->ptr--;
|
||||
|
||||
cheat_manager_update(handle);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/* 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_CHEATS_H
|
||||
#define __SSNES_CHEATS_H
|
||||
|
||||
typedef struct cheat_manager cheat_manager_t;
|
||||
|
||||
cheat_manager_t* cheat_manager_new(const char *path);
|
||||
void cheat_manager_free(cheat_manager_t *handle);
|
||||
|
||||
void cheat_manager_index_next(cheat_manager_t *handle);
|
||||
void cheat_manager_index_prev(cheat_manager_t *handle);
|
||||
void cheat_manager_toggle(cheat_manager_t *handle);
|
||||
|
||||
#endif
|
||||
+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
|
||||
|
||||
@@ -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,32 +15,30 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __SSNES_STRL_H
|
||||
#define __SSNES_STRL_H
|
||||
|
||||
#ifndef __FILTERS_H
|
||||
#define __FILTERS_H
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FILTER
|
||||
#ifndef HAVE_STRL
|
||||
|
||||
#include "pastlib.h"
|
||||
#include "grayscale.h"
|
||||
#include "bleed.h"
|
||||
#include "ntsc.h"
|
||||
#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)
|
||||
|
||||
#define FILTER_NONE 0
|
||||
#define FILTER_HQ2X 1
|
||||
#define FILTER_HQ4X 2
|
||||
#define FILTER_GRAYSCALE 3
|
||||
#define FILTER_BLEED 4
|
||||
#define FILTER_NTSC 5
|
||||
#define FILTER_HQ2X_STR "hq2x"
|
||||
#define FILTER_HQ4X_STR "hq4x"
|
||||
#define FILTER_GRAYSCALE_STR "grayscale"
|
||||
#define FILTER_BLEED_STR "bleed"
|
||||
#define FILTER_NTSC_STR "ntsc"
|
||||
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
|
||||
|
||||
#endif
|
||||
+465
-81
@@ -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,22 +21,61 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include "../compat/strl.h"
|
||||
#include "../compat/posix_string.h"
|
||||
#include "../msvc/msvc_compat.h"
|
||||
|
||||
#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 PATH_MAX
|
||||
#ifdef PATH_MAX
|
||||
#define PATH_MAX PATH_MAX
|
||||
#else
|
||||
#define PATH_MAX 512
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define MAX_INCLUDE_DEPTH 16
|
||||
|
||||
struct entry_list
|
||||
{
|
||||
bool readonly; // If we got this from an #include, do not allow write.
|
||||
char *key;
|
||||
char *value;
|
||||
struct entry_list *next;
|
||||
};
|
||||
|
||||
struct include_list
|
||||
{
|
||||
char *path;
|
||||
struct include_list *next;
|
||||
};
|
||||
|
||||
struct config_file
|
||||
{
|
||||
char *path;
|
||||
struct entry_list *entries;
|
||||
struct entry_list *tail;
|
||||
unsigned include_depth;
|
||||
|
||||
struct include_list *includes;
|
||||
};
|
||||
|
||||
static config_file_t *config_file_new_internal(const char *path, unsigned depth);
|
||||
|
||||
static char *getaline(FILE *file)
|
||||
{
|
||||
char *newline = malloc(9);
|
||||
char *newline = (char*)malloc(9);
|
||||
size_t cur_size = 8;
|
||||
size_t index = 0;
|
||||
|
||||
@@ -46,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;
|
||||
@@ -56,18 +95,198 @@ static char *getaline(FILE *file)
|
||||
return newline;
|
||||
}
|
||||
|
||||
static bool parse_line(struct entry_list *list, char *line)
|
||||
static char *extract_value(char *line, bool is_value)
|
||||
{
|
||||
if (is_value)
|
||||
{
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// If we don't have an equal sign here, we've got an invalid string...
|
||||
if (*line != '=')
|
||||
return NULL;
|
||||
|
||||
line++;
|
||||
}
|
||||
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// We have a full string. Read until next ".
|
||||
if (*line == '"')
|
||||
{
|
||||
line++;
|
||||
char *tok = strtok(line, "\"");
|
||||
if (!tok)
|
||||
return NULL;
|
||||
return strdup(tok);
|
||||
}
|
||||
else if (*line == '\0') // Nothing :(
|
||||
return NULL;
|
||||
else // We don't have that... Read till next space.
|
||||
{
|
||||
char *tok = strtok(line, " \n\t\f\r\v");
|
||||
if (tok)
|
||||
return strdup(tok);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void set_list_readonly(struct entry_list *list)
|
||||
{
|
||||
while (list)
|
||||
{
|
||||
list->readonly = true;
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
// Move semantics? :)
|
||||
static void add_child_list(config_file_t *parent, config_file_t *child)
|
||||
{
|
||||
if (parent->entries)
|
||||
{
|
||||
struct entry_list *head = parent->entries;
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
|
||||
set_list_readonly(child->entries);
|
||||
head->next = child->entries;
|
||||
}
|
||||
else
|
||||
{
|
||||
set_list_readonly(child->entries);
|
||||
parent->entries = child->entries;
|
||||
}
|
||||
|
||||
child->entries = NULL;
|
||||
|
||||
// Rebase tail.
|
||||
if (parent->entries)
|
||||
{
|
||||
struct entry_list *head = parent->entries;
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
parent->tail = head;
|
||||
}
|
||||
else
|
||||
parent->tail = NULL;
|
||||
}
|
||||
|
||||
static void add_include_list(config_file_t *conf, const char *path)
|
||||
{
|
||||
struct include_list *head = conf->includes;
|
||||
struct include_list *node = (struct include_list*)calloc(1, sizeof(*node));
|
||||
node->path = strdup(path);
|
||||
|
||||
if (head)
|
||||
{
|
||||
while (head->next)
|
||||
head = head->next;
|
||||
|
||||
head->next = node;
|
||||
}
|
||||
else
|
||||
conf->includes = node;
|
||||
}
|
||||
|
||||
static void add_sub_conf(config_file_t *conf, char *line)
|
||||
{
|
||||
char *path = extract_value(line, false);
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
add_include_list(conf, path);
|
||||
|
||||
char real_path[PATH_MAX];
|
||||
|
||||
#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));
|
||||
char *split = strrchr(real_path, '/');
|
||||
if (split)
|
||||
{
|
||||
split[1] = '\0';
|
||||
strlcat(real_path, path, sizeof(real_path));
|
||||
}
|
||||
else
|
||||
strlcpy(real_path, path, sizeof(real_path));
|
||||
}
|
||||
#endif
|
||||
|
||||
config_file_t *sub_conf = config_file_new_internal(real_path, conf->include_depth + 1);
|
||||
if (!sub_conf)
|
||||
{
|
||||
free(path);
|
||||
return;
|
||||
}
|
||||
|
||||
// Pilfer internal list. :D
|
||||
add_child_list(conf, sub_conf);
|
||||
config_file_free(sub_conf);
|
||||
free(path);
|
||||
}
|
||||
|
||||
static bool parse_line(config_file_t *conf, struct entry_list *list, char *line)
|
||||
{
|
||||
// Remove everything after comment.
|
||||
char *comment = strchr(line, '#');
|
||||
if (comment)
|
||||
*comment = '\0';
|
||||
|
||||
// Starting line with # and include includes config files. :)
|
||||
if ((comment == line) && (conf->include_depth < MAX_INCLUDE_DEPTH))
|
||||
{
|
||||
comment++;
|
||||
if (strstr(comment, "include ") == comment)
|
||||
{
|
||||
add_sub_conf(conf, comment + strlen("include "));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (conf->include_depth >= MAX_INCLUDE_DEPTH)
|
||||
fprintf(stderr, "!!! #include depth exceeded for config. Might be a cycle.\n");
|
||||
|
||||
// Skips to first character.
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
char *key = malloc(9);
|
||||
char *key = (char*)malloc(9);
|
||||
size_t cur_size = 8;
|
||||
size_t index = 0;
|
||||
|
||||
@@ -76,7 +295,7 @@ static bool parse_line(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++;
|
||||
@@ -84,118 +303,113 @@ static bool parse_line(struct entry_list *list, char *line)
|
||||
key[index] = '\0';
|
||||
list->key = key;
|
||||
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// If we don't have an equal sign here, we've got an invalid string...
|
||||
if (*line != '=')
|
||||
list->value = extract_value(line, true);
|
||||
if (!list->value)
|
||||
{
|
||||
list->key = NULL;
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
line++;
|
||||
|
||||
while (isspace(*line))
|
||||
line++;
|
||||
|
||||
// We have a full string. Read until next ".
|
||||
if (*line == '"')
|
||||
{
|
||||
char *tok = strtok(line + 1, "\"");
|
||||
if (tok == NULL)
|
||||
{
|
||||
list->key = NULL;
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
list->value = strdup(tok);
|
||||
}
|
||||
else // We don't have that... Read till next space.
|
||||
{
|
||||
char *tok = strtok(line, " \t\f");
|
||||
if (tok == NULL)
|
||||
{
|
||||
list->key = NULL;
|
||||
free(key);
|
||||
return false;
|
||||
}
|
||||
list->value = strdup(tok);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
config_file_t *config_file_new(const char *path)
|
||||
static config_file_t *config_file_new_internal(const char *path, unsigned depth)
|
||||
{
|
||||
|
||||
struct config_file *conf = calloc(1, sizeof(*conf));
|
||||
if (conf == NULL)
|
||||
struct config_file *conf = (struct config_file*)calloc(1, sizeof(*conf));
|
||||
if (!conf)
|
||||
return NULL;
|
||||
|
||||
if (path == NULL)
|
||||
if (!path)
|
||||
return conf;
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
if (!file)
|
||||
conf->path = strdup(path);
|
||||
if (!conf->path)
|
||||
{
|
||||
free(conf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct entry_list *tail = conf->entries;
|
||||
conf->include_depth = depth;
|
||||
|
||||
FILE *file = fopen(path, "r");
|
||||
if (!file)
|
||||
{
|
||||
free(conf->path);
|
||||
free(conf);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (!feof(file))
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (parse_line(list, line))
|
||||
if (parse_line(conf, list, line))
|
||||
{
|
||||
if (conf->entries == NULL)
|
||||
if (conf->entries)
|
||||
{
|
||||
conf->entries = list;
|
||||
tail = list;
|
||||
conf->tail->next = list;
|
||||
conf->tail = list;
|
||||
}
|
||||
else
|
||||
{
|
||||
tail->next = list;
|
||||
tail = list;
|
||||
conf->entries = list;
|
||||
conf->tail = list;
|
||||
}
|
||||
}
|
||||
|
||||
free(line);
|
||||
}
|
||||
|
||||
if (list != conf->tail)
|
||||
free(list);
|
||||
}
|
||||
fclose(file);
|
||||
|
||||
return conf;
|
||||
}
|
||||
|
||||
config_file_t *config_file_new(const char *path)
|
||||
{
|
||||
return config_file_new_internal(path, 0);
|
||||
}
|
||||
|
||||
void config_file_free(config_file_t *conf)
|
||||
{
|
||||
if (conf != NULL)
|
||||
if (!conf)
|
||||
return;
|
||||
|
||||
struct entry_list *tmp = conf->entries;
|
||||
while (tmp)
|
||||
{
|
||||
struct entry_list *tmp = conf->entries;
|
||||
struct entry_list *old = tmp;
|
||||
while (tmp != NULL)
|
||||
{
|
||||
free(tmp->key);
|
||||
free(tmp->value);
|
||||
old = tmp;
|
||||
tmp = tmp->next;
|
||||
free(old);
|
||||
}
|
||||
free(conf);
|
||||
free(tmp->key);
|
||||
free(tmp->value);
|
||||
struct entry_list *hold = tmp;
|
||||
tmp = tmp->next;
|
||||
free(hold);
|
||||
}
|
||||
|
||||
struct include_list *inc_tmp = conf->includes;
|
||||
while (inc_tmp)
|
||||
{
|
||||
free(inc_tmp->path);
|
||||
struct include_list *hold = inc_tmp;
|
||||
inc_tmp = inc_tmp->next;
|
||||
free(hold);
|
||||
}
|
||||
|
||||
free(conf->path);
|
||||
free(conf);
|
||||
}
|
||||
|
||||
bool config_get_double(config_file_t *conf, const char *key, double *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -207,16 +421,108 @@ 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;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
*in = strtol(list->value, NULL, 0);
|
||||
return true;
|
||||
errno = 0;
|
||||
int val = strtol(list->value, NULL, 0);
|
||||
if (errno == 0)
|
||||
{
|
||||
*in = val;
|
||||
return true;
|
||||
}
|
||||
return
|
||||
false;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
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;
|
||||
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
errno = 0;
|
||||
unsigned val = strtoul(list->value, NULL, 16);
|
||||
if (errno == 0)
|
||||
{
|
||||
*in = val;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
@@ -227,11 +533,11 @@ bool config_get_char(config_file_t *conf, const char *key, char *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
if (strlen(list->value) > 1)
|
||||
if (list->value[0] && list->value[1])
|
||||
return false;
|
||||
*in = *list->value;
|
||||
return true;
|
||||
@@ -245,7 +551,7 @@ bool config_get_string(config_file_t *conf, const char *key, char **str)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -257,11 +563,27 @@ bool config_get_string(config_file_t *conf, const char *key, char **str)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_get_array(config_file_t *conf, const char *key, char *buf, size_t size)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
strlcpy(buf, list->value, size);
|
||||
return true;
|
||||
}
|
||||
list = list->next;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool config_get_bool(config_file_t *conf, const char *key, bool *in)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
{
|
||||
@@ -287,19 +609,20 @@ void config_set_string(config_file_t *conf, const char *key, const char *val)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
struct entry_list *last = list;
|
||||
while (list != NULL)
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(key, list->key) == 0)
|
||||
if (!list->readonly && (strcmp(key, list->key) == 0))
|
||||
{
|
||||
free(list->value);
|
||||
list->value = strdup(val);
|
||||
return;
|
||||
}
|
||||
|
||||
last = list;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -312,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);
|
||||
}
|
||||
|
||||
@@ -323,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];
|
||||
@@ -358,11 +703,50 @@ bool config_file_write(config_file_t *conf, const char *path)
|
||||
|
||||
void config_file_dump(config_file_t *conf, FILE *file)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list != NULL)
|
||||
struct include_list *includes = conf->includes;
|
||||
while (includes)
|
||||
{
|
||||
fprintf(file, "%s = \"%s\"\n", list->key, list->value);
|
||||
fprintf(file, "#include \"%s\"\n", includes->path);
|
||||
includes = includes->next;
|
||||
}
|
||||
|
||||
struct entry_list *list = conf->entries;
|
||||
while (list)
|
||||
{
|
||||
if (!list->readonly)
|
||||
fprintf(file, "%s = \"%s\"\n", list->key, list->value);
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
void config_file_dump_all(config_file_t *conf, FILE *file)
|
||||
{
|
||||
struct include_list *includes = conf->includes;
|
||||
while (includes)
|
||||
{
|
||||
fprintf(file, "#include \"%s\"\n", includes->path);
|
||||
includes = includes->next;
|
||||
}
|
||||
|
||||
struct entry_list *list = conf->entries;
|
||||
while (list)
|
||||
{
|
||||
fprintf(file, "%s = \"%s\" %s\n", list->key, list->value, list->readonly ? "(included)" : "");
|
||||
list = list->next;
|
||||
}
|
||||
}
|
||||
|
||||
bool config_entry_exists(config_file_t *conf, const char *entry)
|
||||
{
|
||||
struct entry_list *list = conf->entries;
|
||||
|
||||
while (list)
|
||||
{
|
||||
if (strcmp(entry, list->key) == 0)
|
||||
return true;
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+30
-7
@@ -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,8 +20,9 @@
|
||||
#define __CONFIG_FILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "../boolean.h"
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct config_file config_file_t;
|
||||
|
||||
@@ -30,28 +31,49 @@ typedef struct config_file config_file_t;
|
||||
// - # are treated as comments. Rest of the line is ignored.
|
||||
// - Format is: key = value. There can be as many spaces as you like in-between.
|
||||
// - Value can be wrapped inside "" for multiword strings. (foo = "hai u")
|
||||
//
|
||||
// - #include includes a config file in-place.
|
||||
// Path is relative to where config file was loaded unless an absolute path is chosen.
|
||||
// Key/value pairs from an #include are read-only, and cannot be modified.
|
||||
|
||||
// Loads a config file. Returns NULL if file doesn't exist. NULL path will create an empty config file.
|
||||
// Loads a config file. Returns NULL if file doesn't exist.
|
||||
// NULL path will create an empty config file.
|
||||
config_file_t *config_file_new(const char *path);
|
||||
// Frees config file.
|
||||
void config_file_free(config_file_t *conf);
|
||||
|
||||
// All extract functions return true when value is valid and exists. Returns false otherwise.
|
||||
// All extract functions return true when value is valid and exists.
|
||||
// Returns false otherwise.
|
||||
|
||||
bool config_entry_exists(config_file_t *conf, const char *entry);
|
||||
|
||||
// Extracts a double from config file.
|
||||
bool config_get_double(config_file_t *conf, const char *entry, double *in);
|
||||
// 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 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);
|
||||
// Extracts an allocated string in *in. This must be free()-d if this function succeeds.
|
||||
bool config_get_string(config_file_t *conf, const char *entry, char **in);
|
||||
// Extracts a string to a preallocated buffer. Avoid memory allocation.
|
||||
bool config_get_array(config_file_t *conf, const char *entry, char *in, size_t size);
|
||||
// Extracts a boolean from config. Valid boolean true are "true" and "1". Valid false are "false" and "0". Other values will be treated as an error.
|
||||
bool config_get_bool(config_file_t *conf, const char *entry, bool *in);
|
||||
|
||||
// Setters. Similiar to the getters.
|
||||
// Setters. Similar to the getters. Will not write to entry if the entry
|
||||
// was obtained from an #include.
|
||||
void config_set_double(config_file_t *conf, const char *entry, double value);
|
||||
void config_set_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);
|
||||
@@ -61,7 +83,8 @@ bool config_file_write(config_file_t *conf, const char *path);
|
||||
|
||||
// Dump the current config to an already opened file. Does not close the file.
|
||||
void config_file_dump(config_file_t *conf, FILE *file);
|
||||
|
||||
|
||||
// Also dumps inherited values, useful for logging.
|
||||
void config_file_dump_all(config_file_t *conf, FILE *file);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
|
||||
+235
-132
@@ -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"
|
||||
|
||||
@@ -30,21 +30,16 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
#include <SDL/SDL.h>
|
||||
#else
|
||||
#error HAVE_SDL is not defined!
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
#include <samplerate.h>
|
||||
#else
|
||||
#error HAVE_SRC is not defined!
|
||||
#endif
|
||||
|
||||
#include "input/keysym.h"
|
||||
|
||||
///////////////// Drivers
|
||||
#define VIDEO_GL 0
|
||||
#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
|
||||
@@ -53,29 +48,93 @@
|
||||
#define AUDIO_AL 5
|
||||
#define AUDIO_JACK 6
|
||||
#define AUDIO_SDL 8
|
||||
#define AUDIO_XAUDIO 9
|
||||
#define AUDIO_PULSE 10
|
||||
#define AUDIO_EXT 15
|
||||
#define 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) || defined(__CELLOS_LV2__)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_GL
|
||||
|
||||
#if HAVE_ALSA
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_ALSA
|
||||
#elif HAVE_OSS
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_OSS
|
||||
#elif HAVE_JACK
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_JACK
|
||||
#elif HAVE_RSOUND
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_RSOUND
|
||||
#elif HAVE_ROAR
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_ROAR
|
||||
#elif HAVE_AL
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_AL
|
||||
#elif HAVE_SDL
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_SDL
|
||||
#elif defined(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)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_SDL
|
||||
#elif defined(HAVE_DYLIB)
|
||||
#define VIDEO_DEFAULT_DRIVER VIDEO_EXT
|
||||
#else
|
||||
#error "Need at least one video driver."
|
||||
#endif
|
||||
|
||||
#if defined(__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
|
||||
#elif defined(HAVE_OSS)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_OSS
|
||||
#elif defined(HAVE_JACK)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_JACK
|
||||
#elif defined(HAVE_COREAUDIO)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_COREAUDIO
|
||||
#elif defined(HAVE_AL)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_AL
|
||||
#elif defined(HAVE_DSOUND)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND
|
||||
#elif defined(HAVE_SDL)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_SDL
|
||||
#elif defined(HAVE_XAUDIO)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_XAUDIO
|
||||
#elif defined(HAVE_RSOUND)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_RSOUND
|
||||
#elif defined(HAVE_ROAR)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_ROAR
|
||||
#elif defined(HAVE_DYLIB)
|
||||
#define AUDIO_DEFAULT_DRIVER AUDIO_EXT
|
||||
#else
|
||||
#error "Need at least one audio driver."
|
||||
#endif
|
||||
|
||||
#if defined(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."
|
||||
#endif
|
||||
|
||||
|
||||
////////////////
|
||||
@@ -83,13 +142,19 @@
|
||||
////////////////
|
||||
|
||||
// Windowed
|
||||
static const float xscale = 3.0; // Real x res = 296 * xscale
|
||||
static const float yscale = 3.0; // Real y res = 224 * yscale
|
||||
static const float xscale = 3.0; // Real x res = aspect * base_size * xscale
|
||||
static const float yscale = 3.0; // Real y res = base_size * yscale
|
||||
|
||||
// Fullscreen
|
||||
static const bool fullscreen = false; // To start in Fullscreen or not
|
||||
static const unsigned fullscreen_x = 1280;
|
||||
static const unsigned fullscreen_y = 720;
|
||||
static const unsigned fullscreen_x = 0; // Fullscreen resolution. A value of 0 uses the desktop resolution.
|
||||
static const unsigned fullscreen_y = 0;
|
||||
|
||||
// Force 16-bit colors.
|
||||
static const bool force_16bit = false;
|
||||
|
||||
// Forcibly disable composition. Only valid on Windows Vista/7 for now.
|
||||
static const bool disable_composition = false;
|
||||
|
||||
// Video VSYNC (recommended)
|
||||
static const bool vsync = true;
|
||||
@@ -100,14 +165,56 @@ static const bool video_smooth = true;
|
||||
// On resize and fullscreen, rendering area will stay 4:3
|
||||
static const bool force_aspect = true;
|
||||
|
||||
// Crop overscanned frames (7/8 or 15/15 for interlaced frames).
|
||||
static const bool crop_overscan = 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;
|
||||
static const float message_pos_offset_y = 0.05;
|
||||
// Color of the message.
|
||||
static const uint32_t message_color = 0xffff00; // RGB hex value.
|
||||
|
||||
#define SNES_ASPECT_RATIO (4.0/3)
|
||||
// Render-to-texture before rendering to screen (multi-pass shaders)
|
||||
static const bool render_to_texture = false;
|
||||
static const float fbo_scale_x = 2.0;
|
||||
static const float fbo_scale_y = 2.0;
|
||||
static const bool second_pass_smooth = true;
|
||||
|
||||
// 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;
|
||||
|
||||
// OSD-messages
|
||||
static const bool font_enable = true;
|
||||
|
||||
// The accurate refresh rate of your monitor (Hz).
|
||||
// This is used to calculate audio input rate with the formula:
|
||||
// audio_input_rate = game_input_rate * display_refresh_rate / game_refresh_rate.
|
||||
// If the implementation does not report any values,
|
||||
// SNES NTSC defaults will be assumed for compatibility.
|
||||
// This value should stay close to 60Hz to avoid large pitch changes.
|
||||
// If your monitor does not run at 60Hz, or something close to it, disable VSync,
|
||||
// and leave this at its default.
|
||||
#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
|
||||
@@ -119,12 +226,11 @@ static const bool audio_enable = true;
|
||||
// Output samplerate
|
||||
static const unsigned out_rate = 48000;
|
||||
|
||||
// Input samplerate from libSNES.
|
||||
// Lower this (slightly) if you are experiencing frequent audio dropouts while vsync is enabled.
|
||||
static const unsigned in_rate = 31980;
|
||||
// When changing input rate on-the-fly
|
||||
static const float audio_rate_step = 0.25;
|
||||
|
||||
// Audio device (e.g. hw:0,0 or /dev/audio). If NULL, will use defaults.
|
||||
static const char* audio_device = NULL;
|
||||
static const char *audio_device = NULL;
|
||||
|
||||
// Desired audio latency in milliseconds. Might not be honored if driver can't provide given latency.
|
||||
static const int out_latency = 64;
|
||||
@@ -132,10 +238,45 @@ static const int out_latency = 64;
|
||||
// Will sync audio. (recommended)
|
||||
static const bool audio_sync = true;
|
||||
|
||||
// Defines the quality (and cpu reqirements) of samplerate conversion.
|
||||
#define SAMPLERATE_QUALITY SRC_LINEAR
|
||||
// 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;
|
||||
|
||||
// The buffer size for the rewind buffer. This needs to be about 15-20MB per minute. Very game dependant.
|
||||
static const unsigned rewind_buffer_size = 20 << 20; // 20MiB
|
||||
|
||||
// How many frames to rewind at a time.
|
||||
static const unsigned rewind_granularity = 1;
|
||||
|
||||
// Pause gameplay when gameplay loses focus.
|
||||
static const bool pause_nonactive = false;
|
||||
|
||||
// Saves non-volatile SRAM at a regular interval. It is measured in seconds. A value of 0 disables autosave.
|
||||
static const unsigned autosave_interval = 0;
|
||||
|
||||
// When being client over netplay, use keybinds for player 1 rather than player 2.
|
||||
static const bool netplay_client_swap_input = true;
|
||||
|
||||
// On save state load, block SRAM from being overwritten.
|
||||
// This could potentially lead to buggy games.
|
||||
static const bool block_sram_overwrite = false;
|
||||
|
||||
// When saving savestates, state index is automatically incremented before saving.
|
||||
// When the ROM is loaded, state index will be set to the highest existing value.
|
||||
static const bool savestate_auto_index = false;
|
||||
|
||||
// Slowmotion ratio.
|
||||
static const float slowmotion_ratio = 3.0;
|
||||
|
||||
|
||||
////////////////////
|
||||
@@ -144,106 +285,68 @@ static const bool audio_sync = true;
|
||||
|
||||
// Axis threshold (between 0.0 and 1.0)
|
||||
// How far an axis must be tilted to result in a button press
|
||||
#define AXIS_THRESHOLD 0.5
|
||||
static const float axis_threshold = 0.5;
|
||||
|
||||
// To figure out which joypad buttons to use, check jstest or similar.
|
||||
// SDL sometimes reverses the axes for some odd reason, but hey. :D
|
||||
|
||||
// Player 1
|
||||
static const struct snes_keybind snes_keybinds_1[] = {
|
||||
// SNES button | keyboard key | js btn | js axis |
|
||||
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_x, 1, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_z, 0, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_s, 3, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_a, 2, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_q, 4, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_w, 5, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_LEFT, 11, AXIS_NEG(0) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_RIGHT, 12, AXIS_POS(0) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_UP, 13, AXIS_NEG(1) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_DOWN, 14, AXIS_POS(1) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_RETURN, 7, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_RSHIFT, 6, AXIS_NONE },
|
||||
{ SSNES_FAST_FORWARD_KEY, SDLK_SPACE, 10, AXIS_NONE },
|
||||
{ SSNES_SAVE_STATE_KEY, SDLK_F2, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_LOAD_STATE_KEY, SDLK_F4, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_FULLSCREEN_TOGGLE_KEY, SDLK_f, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_QUIT_KEY, SDLK_ESCAPE, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_STATE_SLOT_MINUS, SDLK_F6, NO_BTN, AXIS_NONE },
|
||||
{ SSNES_STATE_SLOT_PLUS, SDLK_F7, NO_BTN, AXIS_NONE },
|
||||
{ -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
|
||||
static const struct snes_keybind snes_keybinds_2[] = {
|
||||
// SNES button | keyboard key | js btn | js axis |
|
||||
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
|
||||
{ -1 }
|
||||
};
|
||||
|
||||
// Player 3
|
||||
static const struct snes_keybind snes_keybinds_3[] = {
|
||||
// SNES button | keyboard key | js btn | js axis |
|
||||
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
|
||||
{ -1 }
|
||||
};
|
||||
|
||||
// Player 4
|
||||
static const struct snes_keybind snes_keybinds_4[] = {
|
||||
// SNES button | keyboard key | js btn | js axis |
|
||||
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
|
||||
{ -1 }
|
||||
};
|
||||
|
||||
// Player 5
|
||||
static const struct snes_keybind snes_keybinds_5[] = {
|
||||
// SNES button | keyboard key | js btn | js axis |
|
||||
{ SNES_DEVICE_ID_JOYPAD_A, SDLK_b, 1, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_B, SDLK_v, 0, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_X, SDLK_g, 3, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_Y, SDLK_f, 2, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_L, SDLK_r, 4, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_R, SDLK_t, 5, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_LEFT, SDLK_j, 11, AXIS_NEG(0) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_RIGHT, SDLK_l, 12, AXIS_POS(0) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_UP, SDLK_i, 13, AXIS_NEG(1) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_DOWN, SDLK_k, 14, AXIS_POS(1) },
|
||||
{ SNES_DEVICE_ID_JOYPAD_START, SDLK_p, 6, AXIS_NONE },
|
||||
{ SNES_DEVICE_ID_JOYPAD_SELECT, SDLK_o, 7, AXIS_NONE },
|
||||
{ -1 }
|
||||
// Player 2-5
|
||||
static const struct snes_keybind snes_keybinds_rest[] = {
|
||||
{ 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
|
||||
|
||||
+68
-8
@@ -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,13 +14,31 @@ static const bool _sdl_supp = true;
|
||||
static const bool _sdl_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_THREADS
|
||||
static const bool _thread_supp = true;
|
||||
#else
|
||||
static const bool _thread_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENGL
|
||||
static const bool _opengl_supp = true;
|
||||
#else
|
||||
static const bool _opengl_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_XVIDEO
|
||||
static const bool _xvideo_supp = true;
|
||||
#else
|
||||
static const bool _xvideo_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_ALSA
|
||||
static const bool _alsa_supp = true;
|
||||
#else
|
||||
static const bool _alsa_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OSS
|
||||
#if defined(HAVE_OSS) || defined(HAVE_OSS_BSD)
|
||||
static const bool _oss_supp = true;
|
||||
#else
|
||||
static const bool _oss_supp = false;
|
||||
@@ -50,10 +68,28 @@ static const bool _jack_supp = true;
|
||||
static const bool _jack_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FILTER
|
||||
static const bool _filter_supp = true;
|
||||
#ifdef HAVE_PULSE
|
||||
static const bool _pulse_supp = true;
|
||||
#else
|
||||
static const bool _filter_supp = false;
|
||||
static const bool _pulse_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_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
|
||||
static const bool _xaudio_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DYLIB
|
||||
static const bool _dylib_supp = true;
|
||||
#else
|
||||
static const bool _dylib_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CG
|
||||
@@ -68,6 +104,18 @@ static const bool _xml_supp = true;
|
||||
static const bool _xml_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SDL_IMAGE
|
||||
static const bool _sdl_image_supp = true;
|
||||
#else
|
||||
static const bool _sdl_image_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_FBO
|
||||
static const bool _fbo_supp = true;
|
||||
#else
|
||||
static const bool _fbo_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_DYNAMIC
|
||||
static const bool _dynamic_supp = true;
|
||||
#else
|
||||
@@ -80,10 +128,10 @@ static const bool _ffmpeg_supp = true;
|
||||
static const bool _ffmpeg_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SRC
|
||||
static const bool _src_supp = true;
|
||||
#ifdef HAVE_X264RGB
|
||||
static const bool _x264rgb_supp = true;
|
||||
#else
|
||||
static const bool _src_supp = false;
|
||||
static const bool _x264rgb_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CONFIGFILE
|
||||
@@ -98,4 +146,16 @@ static const bool _freetype_supp = true;
|
||||
static const bool _freetype_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETPLAY
|
||||
static const bool _netplay_supp = true;
|
||||
#else
|
||||
static const bool _netplay_supp = false;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PYTHON
|
||||
static const bool _python_supp = true;
|
||||
#else
|
||||
static const bool _python_supp = false;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
@@ -0,0 +1,35 @@
|
||||
/* 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/>.
|
||||
*/
|
||||
|
||||
// Builds argc/argv and calls ssnes_main_init().
|
||||
|
||||
#ifndef _SSNES_WRAP_H
|
||||
#define _SSNES_WRAP_H
|
||||
|
||||
struct ssnes_main_wrap
|
||||
{
|
||||
const char *rom_path;
|
||||
const char *sram_path;
|
||||
const char *state_path;
|
||||
const char *config_path;
|
||||
bool verbose;
|
||||
};
|
||||
|
||||
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
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,71 @@
|
||||
/* zconf.h -- configuration of the zlib compression library
|
||||
* Copyright (C) 1995-2002 Jean-loup Gailly.
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
/* This source as presented is a modified version of original zlib for use with SSNES,
|
||||
* and must not be confused with the original software. */
|
||||
|
||||
|
||||
#ifndef _ZCONF_H
|
||||
#define _ZCONF_H
|
||||
|
||||
#if (defined(_WIN32) || defined(__WIN32__)) && !defined(WIN32)
|
||||
# define WIN32
|
||||
#endif
|
||||
|
||||
/* Some Mac compilers merge all .h files incorrectly: */
|
||||
#if defined(__MWERKS__) || defined(applec) ||defined(THINK_C) ||defined(__SC__)
|
||||
# define NO_DUMMY_DECL
|
||||
#endif
|
||||
|
||||
#ifndef MAX_MEM_LEVEL
|
||||
# ifdef MAXSEG_64K
|
||||
# define MAX_MEM_LEVEL 8
|
||||
# else
|
||||
# define MAX_MEM_LEVEL 9
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef MAX_WBITS
|
||||
# define MAX_WBITS 15 /* 32K LZ77 window */
|
||||
#endif
|
||||
|
||||
/* The memory requirements for deflate are (in bytes):
|
||||
(1 << (windowBits+2)) + (1 << (memLevel+9))
|
||||
that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
|
||||
plus a few kilobytes for small objects. For example, if you want to reduce
|
||||
the default memory requirements from 256K to 128K, compile with
|
||||
make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
|
||||
Of course this will generally degrade compression (there's no free lunch).
|
||||
|
||||
The memory requirements for inflate are (in bytes) 1 << windowBits
|
||||
that is, 32K for windowBits=15 (default value) plus a few kilobytes
|
||||
for small objects.
|
||||
*/
|
||||
|
||||
/* Type declarations */
|
||||
|
||||
#if !defined(MACOS) && !defined(TARGET_OS_MAC)
|
||||
typedef unsigned char Byte; /* 8 bits */
|
||||
#endif
|
||||
|
||||
typedef Byte Bytef;
|
||||
typedef void *voidpf;
|
||||
typedef void *voidp;
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <sys/types.h> /* for off_t */
|
||||
# include <unistd.h> /* for SEEK_* and off_t */
|
||||
# define z_off_t off_t
|
||||
#endif
|
||||
#ifndef SEEK_SET
|
||||
# define SEEK_SET 0 /* Seek from beginning of file. */
|
||||
# define SEEK_CUR 1 /* Seek from current position. */
|
||||
# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
|
||||
#endif
|
||||
#ifndef z_off_t
|
||||
# define z_off_t long
|
||||
#endif
|
||||
|
||||
#endif /* _ZCONF_H */
|
||||
Loaded 100 of 243 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user