mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-19 06:11:44 +02:00
Compare commits
827 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
55e78b03de | ||
|
47dcdc1b4f | ||
|
e13ab011eb | ||
|
892f202945 | ||
|
e575196abc | ||
|
404caeab50 | ||
|
dde9b4c2c7 | ||
|
793f2e5bf4 | ||
|
cc4ab9bc25 | ||
|
2551f20f3a | ||
|
5b29ddbcaa | ||
|
ac4d16c917 | ||
|
01c16dcf4d | ||
|
169c0871c7 | ||
|
ffee61a1b1 | ||
|
526df86ee6 | ||
|
748cf44f35 | ||
|
a4f96f0648 | ||
|
2ca1bab9ed | ||
|
1e6a745f19 | ||
|
90b1350110 | ||
|
357d054c19 | ||
|
d62e3f3362 | ||
|
4ec45a7453 | ||
|
f5d40bd1ee | ||
|
6aa7c944d5 | ||
|
dafd673177 | ||
|
a64c1adaa8 | ||
|
0d1d6f329d | ||
|
7cd897b53b | ||
|
011f470b07 | ||
|
6edad01fb8 | ||
|
3ecea80ecb | ||
|
b7b848eff5 | ||
|
da7350ac5c | ||
|
ba3fca27ad | ||
|
5775155714 | ||
|
f1108408a8 | ||
|
996358da66 | ||
|
c717a0e7bd | ||
|
2884cd87d2 | ||
|
454b90be24 | ||
|
2b9a22e1d8 | ||
|
1c1cfd086b | ||
|
f2978247c1 | ||
|
4f32551430 | ||
|
c61c3cabc6 | ||
|
819d6dbde4 | ||
|
f51bc06739 | ||
|
4f09a3873d | ||
|
55bfe402e7 | ||
|
30d7fa1923 | ||
|
9f86a3be26 | ||
|
6b7e6e01bb | ||
|
53f8de6ac3 | ||
|
cd18cdb1d6 | ||
|
6cb7d89d64 | ||
|
19f3cdfd5e | ||
|
a32b6fae74 | ||
|
03a6e1c7de | ||
|
6b34f134bf | ||
|
2de906ea46 | ||
|
95addddc46 | ||
|
45e9e0f0ea | ||
|
0aea7fd5c5 | ||
|
fb95d5b59f | ||
|
3d646aef73 | ||
|
3fb7ff6bfe | ||
|
d8bc2050be | ||
|
e71da4d8c8 | ||
|
e78aca34b9 | ||
|
0c82cc325e | ||
|
78c76962ec | ||
|
e22167cf82 | ||
|
1698533774 | ||
|
7b66e1c531 | ||
|
c6f92b782c | ||
|
e3f2e634c8 | ||
|
e598e81ab9 | ||
|
d37fb1c12e | ||
|
eaf33cb078 | ||
|
07427e4697 | ||
|
b5301b7ea8 | ||
|
57c53a86b4 | ||
|
f19f31938b | ||
|
4efee7e9f1 | ||
|
3701236ca0 | ||
|
2f684caa7c | ||
|
3a064fc5a3 | ||
|
5a4b667eae | ||
|
62729df2d1 | ||
|
1ef227f482 | ||
|
6e5542aa20 | ||
|
675662e739 | ||
|
409dd371b9 | ||
|
18d2ab6435 | ||
|
1e626e75ef | ||
|
c6d90d3ff1 | ||
|
29caf77751 | ||
|
29b13083d5 | ||
|
3883172a4e | ||
|
fa77fc6a8f | ||
|
56c9a5195e | ||
|
a6ebce428f | ||
|
0788627898 | ||
|
1195c46ac0 | ||
|
90f094b931 | ||
|
2bb1606552 | ||
|
08e5e81609 | ||
|
03aaaba889 | ||
|
556ab4c809 | ||
|
23467b5b1f | ||
|
5ae1bcd973 | ||
|
64e3658bcb | ||
|
ab515b59d4 | ||
|
bb7b2f2e60 | ||
|
639b9db961 | ||
|
f857f35e72 | ||
|
5dc27a9fb3 | ||
|
ce3dba130c | ||
|
f9ca7a4927 | ||
|
db1c37c799 | ||
|
7a98db84ac | ||
|
b73493b492 | ||
|
95831d3675 | ||
|
ab25877af4 | ||
|
5ba538ee39 | ||
|
04b85ade6b | ||
|
0b088b6b55 | ||
|
252f479b22 | ||
|
46dbe00f14 | ||
|
66ad62b79f | ||
|
27b2d11839 | ||
|
96c381f91f | ||
|
5757949023 | ||
|
bad27bb8f3 | ||
|
06ceb7d29e | ||
|
e030428054 | ||
|
24dce7dd92 | ||
|
b577e6d5d0 | ||
|
db988d9588 | ||
|
f6303518d5 | ||
|
9e8913cea0 | ||
|
0e56b27228 | ||
|
fe81130f54 | ||
|
216b472418 | ||
|
bc7456246c | ||
|
a7b30b069c | ||
|
454b39cb78 | ||
|
f65b7a8528 | ||
|
7e88bdde09 | ||
|
4291a0cae6 | ||
|
ee6498258f | ||
|
296f2c094d | ||
|
6b284bb247 | ||
|
e6d7df41da | ||
|
c77ecef3e0 | ||
|
a89acc2695 | ||
|
45df47f08b | ||
|
ff0fa9bb19 | ||
|
4d2244ed5f | ||
|
cdf4784468 | ||
|
3934be89ff | ||
|
2f67beeba9 | ||
|
c2a181dbc5 | ||
|
42e5bcc604 | ||
|
ea75d1bc7c | ||
|
73f0b7bb41 | ||
|
b637ae34fe | ||
|
30c606fdc8 | ||
|
b4c4b318e8 | ||
|
32e2abdd90 | ||
|
2e5f6c56c6 | ||
|
55799c4230 | ||
|
78a6a2e7d7 | ||
|
f3022fd907 | ||
|
e00fa027aa | ||
|
a0671bc8a9 | ||
|
32c781c531 | ||
|
10038ec76d | ||
|
0623d6ac2b | ||
|
14d87f6bf3 | ||
|
601b749e50 | ||
|
0aa13bd44b | ||
|
c343b0c08f | ||
|
9297e0a238 | ||
|
903d1e4012 | ||
|
382e192647 | ||
|
d19fe54c64 | ||
|
1979df20ad | ||
|
7c7421c951 | ||
|
244cecd8f4 | ||
|
c6465ae101 | ||
|
bd8e94a7c7 | ||
|
52f34ea470 | ||
|
a03d91882c | ||
|
d87a0f633d | ||
|
becbca47d4 | ||
|
7a548482ed | ||
|
30ed7f7e0b | ||
|
e05672183b | ||
|
922a0e420c | ||
|
c25d20a8d9 | ||
|
4d7bb510f2 | ||
|
7786206a4f | ||
|
fbc1571889 | ||
|
53843934c0 | ||
|
37b610da53 | ||
|
2d9ce59e99 | ||
|
559a6585ef | ||
|
25145f59cc | ||
|
17fc6d8d51 | ||
|
6871e0e32a | ||
|
bb1dd8c609 | ||
|
c9f7c6c4be | ||
|
95d0020297 | ||
|
41148b1024 | ||
|
dbee893408 | ||
|
cb86cd116c | ||
|
1a889ae232 | ||
|
79be6f2355 | ||
|
cac3858f65 | ||
|
1fd6d983da | ||
|
aaf094e7c4 | ||
|
3159285eaa | ||
|
90da691717 | ||
|
598076e400 | ||
|
075f540ec4 | ||
|
41eccf6ec4 | ||
|
4c4e79aa0e | ||
|
0b44399c0a | ||
|
23dd28952b | ||
|
03b06257d3 | ||
|
336d20123f | ||
|
c58169945c | ||
|
c2d0ed4ca8 | ||
|
3d34517f3e | ||
|
a3e0f6da25 | ||
|
bd814f0358 | ||
|
f9adb4d2c6 | ||
|
9a6ae6dacb | ||
|
1e4affe5f9 | ||
|
93a6a1ce7e | ||
|
3b4e8b6d75 | ||
|
b2b51d544f | ||
|
5da4532771 | ||
|
552d385031 | ||
|
0595e9e866 | ||
|
23da4e4e91 | ||
|
41e127a07c | ||
|
5d135b556d | ||
|
2335bb0df8 | ||
|
212da0a966 | ||
|
5deba5cbc1 | ||
|
716c95f279 | ||
|
876b4be1d2 | ||
|
22bd4b9277 | ||
|
f1a4576ac4 | ||
|
0aedb3430c | ||
|
35ff15f83e | ||
|
65a3e6c676 | ||
|
393c2395bb | ||
|
6090c63958 | ||
|
0c55796060 | ||
|
372e9ef42b | ||
|
40a5fbe605 | ||
|
ec960c5172 | ||
|
b14c6bf155 | ||
|
5b97fa2415 | ||
|
f70a20bc42 | ||
|
470e27323d | ||
|
5a8c814e25 | ||
|
91bb781b73 | ||
|
15b67922b3 | ||
|
173a5d67bc | ||
|
ec9729a9e1 | ||
|
77ac5f9e88 | ||
|
8c337d4ac6 | ||
|
73354923eb | ||
|
3aa90590ca | ||
|
52d0cd8dfb | ||
|
c67fb2c726 | ||
|
5d29700fa1 | ||
|
5e7fdbe2c0 | ||
|
51e3fcd3fa | ||
|
18852bcbe2 | ||
|
bcc2627793 | ||
|
685cec6583 | ||
|
6882bd98cf | ||
|
6c8e3c885d | ||
|
8f5bc80f01 | ||
|
2b8df2e70e | ||
|
ec4ab1dc11 | ||
|
5961ea9c03 | ||
|
3353efd3a1 | ||
|
a73a94f331 | ||
|
7ee1534093 | ||
|
87e2154ea1 | ||
|
d8bd1fca1f | ||
|
7acbf5c3dd | ||
|
ea11c6d098 | ||
|
f5b96e9e9e | ||
|
6078cdacbb | ||
|
c2648faeab | ||
|
2e14bd1c81 | ||
|
cd5dde0f62 | ||
|
e5f19e49d4 | ||
|
c6ed8bb4b1 | ||
|
c24eb6e592 | ||
|
d537eaa0fd | ||
|
8bbbc5e737 | ||
|
210306e661 | ||
|
6847058210 | ||
|
b7dca2f317 | ||
|
b69909be8d | ||
|
8617711ea2 | ||
|
540d960e30 | ||
|
8023d9cbe8 | ||
|
0ea17abfea | ||
|
8f61c267c5 | ||
|
eaa2c1f6c0 | ||
|
985610c167 | ||
|
72b824cf1a | ||
|
2dd35f984d | ||
|
e216912ca3 | ||
|
a4a3d611a6 | ||
|
5c55cc2c94 | ||
|
c49d3b2006 | ||
|
61091167b8 | ||
|
610d42d573 | ||
|
f8a6cc2cbd | ||
|
3a175ad2b0 | ||
|
5e330da4e8 | ||
|
c38a771f22 | ||
|
3d8be92550 | ||
|
38fbcd5277 | ||
|
0cb3529547 | ||
|
2f81b5a3e7 | ||
|
aef8d5e962 | ||
|
a5af5eab3c | ||
|
41efdba45a | ||
|
b55783c322 | ||
|
3ec08cebbe | ||
|
8d7d452534 | ||
|
56cb9c01a5 | ||
|
e9d2d56df9 | ||
|
6d487925d0 | ||
|
7aad868adb | ||
|
dd06dd0fed | ||
|
3c26736d4b | ||
|
f53cb33eb9 | ||
|
426de198b7 | ||
|
9e06857e4d | ||
|
e28aa32324 | ||
|
a9571ff5b8 | ||
|
3d21e9afe0 | ||
|
1cb37fc974 | ||
|
f92fc276af | ||
|
18afd41a80 | ||
|
3507c522f4 | ||
|
7a4bfca106 | ||
|
0db886f91d | ||
|
904d11a3f7 | ||
|
214b921388 | ||
|
b8b5aef165 | ||
|
f8e71b50d0 | ||
|
9a13863adb | ||
|
5dbaec85a7 | ||
|
92d86aef16 | ||
|
6524a7181d | ||
|
fbc58c70ae | ||
|
c63e6f2953 | ||
|
b899ee9a9c | ||
|
28fc75737e | ||
|
1ff315838e | ||
|
2e4cd09800 | ||
|
4fb8ce2821 | ||
|
3dce3aa3c8 | ||
|
28060d3a69 | ||
|
9dcbd12159 | ||
|
5352c5ab27 | ||
|
b47488ab09 | ||
|
96c45420d1 | ||
|
fd9194e4c2 | ||
|
593d32b885 | ||
|
fb31df6eb8 | ||
|
a81802422f | ||
|
a1eaec493a | ||
|
2956930d1c | ||
|
c557d68ec4 | ||
|
6b8c003ff8 | ||
|
25bda4f159 | ||
|
d8a8f06c35 | ||
|
fab830f84b | ||
|
f669c424c4 | ||
|
9c25f128f9 | ||
|
c273297577 | ||
|
afa8ea61c5 | ||
|
ea3c2dafda | ||
|
b38a657192 | ||
|
a8f2bfc533 | ||
|
d060904b8d | ||
|
56293c585b | ||
|
15b3dc8b0b | ||
|
d621136d69 | ||
|
c0934b826c | ||
|
86f3a8e670 | ||
|
b308661fa3 | ||
|
a4483339e5 | ||
|
1c13f0ad42 | ||
|
d416bb7231 | ||
|
4ba37e6cbe | ||
|
6c7a9adaad | ||
|
bafdb09e1b | ||
|
53c0002274 | ||
|
15c467b482 | ||
|
bfcd4e376b | ||
|
d13f1dd9ea | ||
|
8976438118 | ||
|
11357169a5 | ||
|
9be4e59a05 | ||
|
281b22e1c0 | ||
|
c9c931ecab | ||
|
366e9cebff | ||
|
58d70c7c9a | ||
|
d0b90d0b5c | ||
|
e0512b9100 | ||
|
08e1f93f71 | ||
|
6caad914ad | ||
|
c39ef91307 | ||
|
f0cf1df4af | ||
|
0034adab3b | ||
|
c4425b5fa8 | ||
|
722a2d797d | ||
|
60345fe8b5 | ||
|
f3af7f177b | ||
|
5f67b2a8fc | ||
|
1ae228663d | ||
|
59eb05226f | ||
|
fe9820481f | ||
|
1faa263a4a | ||
|
45c8f09330 | ||
|
09c9a55588 | ||
|
defb1eedd3 | ||
|
accffa8d1b | ||
|
4c4a1dcb67 | ||
|
c52b05a0de | ||
|
5af4f45a25 | ||
|
a350195c8c | ||
|
55bae99bb6 | ||
|
0ca6aaf6b7 | ||
|
bc9e8c8f4a | ||
|
b26db6c9b8 | ||
|
246887d65c | ||
|
0749009652 | ||
|
3a967430eb | ||
|
ce8f008894 | ||
|
f6fab1a502 | ||
|
a8363c41ba | ||
|
e888a607c1 | ||
|
fb52220b1b | ||
|
7760931ffc | ||
|
df2492c75e | ||
|
6e6c29138e | ||
|
5a44f249a4 | ||
|
3c3f1de317 | ||
|
a99e601c87 | ||
|
a62425745b | ||
|
232bcc0fa7 | ||
|
926d65cfe0 | ||
|
37a738a1a4 | ||
|
3e0adb1798 | ||
|
f5333c333c | ||
|
427ffd97b8 | ||
|
8df735e6e2 | ||
|
b7edf2abcf | ||
|
50d5beda53 | ||
|
65dadd1576 | ||
|
6f9839df62 | ||
|
781f1ff4c0 | ||
|
66ef548781 | ||
|
cabb865d5b | ||
|
54ffe85479 | ||
|
362d29c6a7 | ||
|
ecbc3f6693 | ||
|
1cdd539115 | ||
|
1870788cb8 | ||
|
1b77a4ec8d | ||
|
b02c35edae | ||
|
e721064fe9 | ||
|
f1e48b90ff | ||
|
6099424f0f | ||
|
43c5368a30 | ||
|
f4bf02ba02 | ||
|
8c3d076bf4 | ||
|
788bdb0ae2 | ||
|
39822bc123 | ||
|
6ba931987b | ||
|
4c4339d313 | ||
|
d166c280c7 | ||
|
73a37beae1 | ||
|
b61ca253de | ||
|
0ed9737dd4 | ||
|
adddc1dc0a | ||
|
b021f5186c | ||
|
1a68cb65eb | ||
|
82a64bb3ab | ||
|
75f842cdb2 | ||
|
9362726c65 | ||
|
a3e1983d50 | ||
|
20e3fdd1e3 | ||
|
0bb6a3b0bc | ||
|
752139e66c | ||
|
2f08bc3b1c | ||
|
f4c043ce50 | ||
|
1bbe78f94e | ||
|
ef5f0e9c21 | ||
|
83ae1579a0 | ||
|
e0b4fe6419 | ||
|
efbfe854d2 | ||
|
5f2fbd0d72 | ||
|
e0c021660a | ||
|
89192cee6c | ||
|
e8b9b6cd7b | ||
|
3efe3cdfbf | ||
|
224bbe03f4 | ||
|
aad87da68a | ||
|
444ec756bd | ||
|
96ebd87db4 | ||
|
8ef704c16b | ||
|
380130fbd8 | ||
|
859c5b587b | ||
|
7eceacf78e | ||
|
b3b3690948 | ||
|
331c53c896 | ||
|
fd2cdc261c | ||
|
d366c6ff43 | ||
|
eef90a8f14 | ||
|
00fecd96a4 | ||
|
f23e54f6b1 | ||
|
54e48aabbe | ||
|
0902869032 | ||
|
7d1685f7f5 | ||
|
775be26350 | ||
|
d38893008c | ||
|
d250096e48 | ||
|
68d57dc46c | ||
|
acbc52ab07 | ||
|
7406d903e1 | ||
|
d739e33243 | ||
|
1b62f18139 | ||
|
d0d7436d50 | ||
|
08fc1f659a | ||
|
e8b2f22623 | ||
|
cec9d91b39 | ||
|
11a3195e84 | ||
|
1fb37f087f | ||
|
8ece9ed16b | ||
|
302b369781 | ||
|
ba384a7c48 | ||
|
55f19c3e0d | ||
|
406b6a61a5 | ||
|
1067566834 | ||
|
559eeccc89 | ||
|
a72ff8b7fa | ||
|
0b6f1df987 | ||
|
020caa546d | ||
|
c2975e6898 | ||
|
571760c747 | ||
|
7022d1aa51 | ||
|
e1223366a7 | ||
|
80841deaa5 | ||
|
d5c09c9ab1 | ||
|
8be474b0ac | ||
|
284e4c043e | ||
|
0b4e7fb5a5 | ||
|
f87c6b7ecb | ||
|
4129630d97 | ||
|
17697317d4 | ||
|
ed5ec58595 | ||
|
434e303ffb | ||
|
ee982f098a | ||
|
cbbf5ec114 | ||
|
7af270aa59 | ||
|
191a71b291 | ||
|
d4876a831f | ||
|
16f736307e | ||
|
40802b0b9f | ||
|
ff3750de4f | ||
|
78f341489e | ||
|
3517d5c4a4 | ||
|
ecc7e899e0 | ||
|
f6d7922e62 | ||
|
a2baea248f | ||
|
561c6413a4 | ||
|
b7006822bf | ||
|
8476f35153 | ||
|
e7806dd6e8 | ||
|
50411a17d1 | ||
|
b73d918776 | ||
|
6e8406291c | ||
|
cea64b9991 | ||
|
8af3e4a6e2 | ||
|
a4629e1f64 | ||
|
3bcf3c24c9 | ||
|
2461293ff0 | ||
|
1ca4609079 | ||
|
82c58527c3 | ||
|
04072b278b | ||
|
7e7003fd29 | ||
|
89d47914b9 | ||
|
0bf2c9d4e1 | ||
|
4c3f9b93e7 | ||
|
1cab2dfeb8 | ||
|
68f04c3bb8 | ||
|
8071da4c6a | ||
|
d76c0c7e82 | ||
|
7c9b78b7bb | ||
|
fa6cbac251 | ||
|
bf70044edc | ||
|
ee7662a8be | ||
|
186f008574 | ||
|
bdc100e123 | ||
|
c40e9754bc | ||
|
ae5968cfeb | ||
|
b03563426f | ||
|
f500426158 | ||
|
8499c64756 | ||
|
26bd7590ad | ||
|
21ee597aae | ||
|
bf90bdfcc8 | ||
|
0ad70a30f8 | ||
|
79c83ade70 | ||
|
a3aea95e6b | ||
|
569f5abc28 | ||
|
5bdf55f08f | ||
|
e30780bb72 | ||
|
bab2ac812a | ||
|
1d7b674dd4 | ||
|
c2c957a9da | ||
|
8cf20dabbf | ||
|
2707c5316d | ||
|
f3e67da937 | ||
|
c6fc15f8d2 | ||
|
d6e9d94ec3 | ||
|
2fbbccf985 | ||
|
4c3f58150c | ||
|
d91f3999cc | ||
|
7c96826eb0 | ||
|
5df717ff2a | ||
|
f7ddbfc462 | ||
|
0b70a01b47 | ||
|
4d2e17f9c0 | ||
|
043f6a8b33 | ||
|
ffd150735b | ||
|
427bac3011 | ||
|
ac2d0ba1cf | ||
|
1df2549d18 | ||
|
9b8c3ff8c0 | ||
|
0a57cac70c | ||
|
8bdf8f2a55 | ||
|
e39987a3e3 | ||
|
f5e5bf1772 | ||
|
c50723ef61 | ||
|
ca277cd5e8 | ||
|
306cac2b54 | ||
|
f230d144b5 | ||
|
7ccfbe0206 | ||
|
4b897ba791 | ||
|
be3f6ac0d5 | ||
|
92fe5b0813 | ||
|
059347e575 | ||
|
0d6a09f9f8 | ||
|
b72f35a13e | ||
|
1c0ef793fe | ||
|
76a8ecd32a | ||
|
3dd1aa9c1b | ||
|
88c79e56a0 | ||
|
07995c05a5 | ||
|
13ad9644a2 | ||
|
8d5cc0c35e | ||
|
82293c95ae | ||
|
67457fade4 | ||
|
7a68059f78 | ||
|
3e807946b8 | ||
|
a816998122 | ||
|
3a9c7c6843 | ||
|
f48b332c83 | ||
|
ccd8878d75 | ||
|
875f031182 | ||
|
f04d9d58f5 | ||
|
40abcfc4a5 | ||
|
44a8c5a2b4 | ||
|
f1a80075fa | ||
|
ae5b4c3bb3 | ||
|
c074c6e064 | ||
|
50420e3dd2 | ||
|
b08449215a | ||
|
9b452c9f5f | ||
|
3681961ca5 | ||
|
20ac95ee49 | ||
|
fdc41611cf | ||
|
839813d0f1 | ||
|
7f3cfa17b9 | ||
|
ae5d380d06 | ||
|
3ebc77c148 | ||
|
6ae0abe3d3 | ||
|
0955295475 | ||
|
7cdae5195a | ||
|
e2ee6689a0 | ||
|
55e507d5df | ||
|
a2d3b8ba15 | ||
|
1929ad47d2 | ||
|
7403e69307 | ||
|
19e1d89f00 | ||
|
aff00506c5 | ||
|
e846c83d47 | ||
|
06d44b4878 | ||
|
25eaaa82f4 | ||
|
2d83300235 | ||
|
680d16561e | ||
|
379ab6991f | ||
|
d3413db04a | ||
|
a7f7985581 | ||
|
b586471562 | ||
|
c33065fbd1 | ||
|
79e7e6ab9e | ||
|
3d3ac8c1db | ||
|
b0d2f5033e | ||
|
570eb9c5f5 | ||
|
7dc62e3a69 | ||
|
fc7d5991ce | ||
|
29be18ce0c | ||
|
810cbdafb4 | ||
|
4b29f4bad7 | ||
|
ef65bb862a | ||
|
0d0af39b44 | ||
|
6c83329cae | ||
|
32a95a9761 | ||
|
a89a3da77a | ||
|
7a748e093e | ||
|
d158c8f293 | ||
|
71bda4144a | ||
|
ad51f1478e | ||
|
d0ddd87e9c | ||
|
605a8aa3e9 | ||
|
a8323d0d2b | ||
|
d7998b23ef | ||
|
344e63d928 | ||
|
f1ebef2ea8 | ||
|
1fdd0582fc | ||
|
12df278c5b | ||
|
cec33c1d0f | ||
|
3414c8c8df | ||
|
82ec876302 | ||
|
72b6a8b32e | ||
|
653bb378ee | ||
|
0b923489dd | ||
|
4d193d7d94 | ||
|
47d4bd4d81 | ||
|
27660505c8 | ||
|
702b657e75 | ||
|
0253db8685 | ||
|
2a4eb1cfc8 | ||
|
bd628de3cf | ||
|
2c53d5fbc0 | ||
|
f2a416aea9 | ||
|
78d49d3873 | ||
|
65a3306ad5 | ||
|
a219f9c121 | ||
|
6adfe71836 | ||
|
41c478ac4a | ||
|
40f4b91000 | ||
|
6d9f43a37b | ||
|
d1ffd59c29 | ||
|
0fe55e3f5b | ||
|
b42ab2fcb3 | ||
|
8476a12deb | ||
|
b0e862613b | ||
|
b113ecb5a3 | ||
|
bc5ad4a1cd | ||
|
1a90e206e0 | ||
|
483fc81356 | ||
|
0c87bdabed | ||
|
c45633550e | ||
|
7081f46e45 | ||
|
213879771e | ||
|
4344b916b6 | ||
|
0271d6a12b | ||
|
1b0b54a690 | ||
|
092cac9073 | ||
|
ecb35cac33 | ||
|
28a14198cb | ||
|
7ff7f64482 | ||
|
4c9266d18f | ||
|
169e400437 | ||
|
ea02f1e36a | ||
|
310ff4fa3b | ||
|
83f684c66c | ||
|
e0815b55b9 | ||
|
20cc6148cb | ||
|
a21ff570ee | ||
|
bb3c69a30d | ||
|
f0c17ffc0d | ||
|
314aee8c5c | ||
|
7bf4cff946 | ||
|
99b2b4b57c | ||
|
4e0223d590 | ||
|
458775a481 | ||
|
fc8eba133d | ||
|
39ca8a2fab | ||
|
c335ee9d80 | ||
|
2eb50fd70b | ||
|
89d578bc7f | ||
|
b4ba95242f | ||
|
a1b2fb0124 | ||
|
4a069761f9 | ||
|
80c1c9c2ef | ||
|
a512d14628 | ||
|
1a7bc6bb87 | ||
|
ecc651c88b | ||
|
3016e595f0 | ||
|
423a6c6bf8 | ||
|
10e2a6d497 | ||
|
187ba0eec6 | ||
|
c54be74832 | ||
|
04986d2bf7 |
84
.cirrus.yml
Normal file
84
.cirrus.yml
Normal file
@@ -0,0 +1,84 @@
|
||||
linux-x86_64-binaries_task:
|
||||
container:
|
||||
image: ubuntu:latest
|
||||
|
||||
setup_script:
|
||||
- apt-get update && apt-get -y install build-essential libgtk2.0-dev libpulse-dev mesa-common-dev libgtksourceview2.0-dev libcairo2-dev libsdl2-dev libxv-dev libao-dev libopenal-dev libudev-dev zip
|
||||
|
||||
compile_script:
|
||||
- make -C bsnes local=false
|
||||
|
||||
package_script:
|
||||
- mkdir bsnes-nightly
|
||||
- mkdir bsnes-nightly/Database
|
||||
- mkdir bsnes-nightly/Firmware
|
||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
||||
- cp -a bsnes/Database/* bsnes-nightly/Database
|
||||
- cp -a shaders bsnes-nightly/Shaders
|
||||
- cp -a GPLv3.txt bsnes-nightly
|
||||
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||
|
||||
bsnes-nightly_artifacts:
|
||||
path: "bsnes-nightly.zip"
|
||||
|
||||
freebsd-x86_64-binaries_task:
|
||||
freebsd_instance:
|
||||
image: freebsd-12-0-release-amd64
|
||||
|
||||
setup_script:
|
||||
- pkg install --yes gmake gdb gcc8 pkgconf sdl2 openal-soft gtksourceview2 libXv zip
|
||||
|
||||
compile_script:
|
||||
- gmake -C bsnes local=false
|
||||
|
||||
package_script:
|
||||
- mkdir bsnes-nightly
|
||||
- mkdir bsnes-nightly/Database
|
||||
- mkdir bsnes-nightly/Firmware
|
||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes
|
||||
- cp -a bsnes/Database/* bsnes-nightly/Database
|
||||
- cp -a shaders bsnes-nightly/Shaders
|
||||
- cp -a GPLv3.txt bsnes-nightly
|
||||
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||
|
||||
bsnes-nightly_artifacts:
|
||||
path: "bsnes-nightly.zip"
|
||||
|
||||
windows-x86_64-binaries_task:
|
||||
container:
|
||||
image: ubuntu:latest
|
||||
|
||||
setup_script:
|
||||
- apt-get update && apt-get -y install build-essential mingw-w64 zip
|
||||
|
||||
compile_script:
|
||||
- make -C bsnes local=false platform=windows compiler="x86_64-w64-mingw32-g++" windres="x86_64-w64-mingw32-windres"
|
||||
|
||||
package_script:
|
||||
- mkdir bsnes-nightly
|
||||
- mkdir bsnes-nightly/Database
|
||||
- mkdir bsnes-nightly/Firmware
|
||||
- cp -a bsnes/out/bsnes bsnes-nightly/bsnes.exe
|
||||
- cp -a bsnes/Database/* bsnes-nightly/Database
|
||||
- cp -a shaders bsnes-nightly/Shaders
|
||||
- cp -a GPLv3.txt bsnes-nightly
|
||||
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||
|
||||
bsnes-nightly_artifacts:
|
||||
path: "bsnes-nightly.zip"
|
||||
|
||||
macOS-x86_64-binaries_task:
|
||||
osx_instance:
|
||||
image: mojave-base
|
||||
|
||||
compile_script:
|
||||
- make -C bsnes local=false
|
||||
|
||||
package_script:
|
||||
- mkdir bsnes-nightly
|
||||
- cp -a bsnes/out/bsnes.app bsnes-nightly
|
||||
- cp -a GPLv3.txt bsnes-nightly
|
||||
- zip -r bsnes-nightly.zip bsnes-nightly
|
||||
|
||||
bsnes-nightly_artifacts:
|
||||
path: "bsnes-nightly.zip"
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.fs linguist-detectable=false
|
||||
*.vs linguist-detectable=false
|
1
.github/FUNDING.yml
vendored
Normal file
1
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
patreon: byuu
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +0,0 @@
|
||||
ananke/libananke.so
|
||||
|
89
CONTRIBUTING.md
Normal file
89
CONTRIBUTING.md
Normal file
@@ -0,0 +1,89 @@
|
||||
Contributing
|
||||
============
|
||||
|
||||
Code contributions are most welcome and highly appreciated!
|
||||
|
||||
But first, please note that although bsnes is licensed under the GPLv3 license,
|
||||
in order to be merged upstream, any code contributions must be provided under
|
||||
the ISC source code license.
|
||||
|
||||
This is *not* a CLA (community license agreement), no legal contract needs to be
|
||||
signed, and you will maintain full and exclusive copyright ownership over any
|
||||
contributed source code.
|
||||
|
||||
There are two reasons for this requirement:
|
||||
|
||||
GPLv4+
|
||||
------
|
||||
|
||||
bsnes is currently licensed under the GPLv3 license only. I do not license bsnes
|
||||
under the GPLv3 or later license, because there is no way of knowing what the
|
||||
GPLv4 and later licenses will change, and if they will be in the best interests
|
||||
of emulator development and video game preservation.
|
||||
|
||||
Although I put a good deal of trust into the FSF, no one is an oracle that can
|
||||
predict the future. Would *you* agree to a license before being able to read it?
|
||||
|
||||
However, the GPLv4 may prove beneficial, and close important holes in the GPLv3
|
||||
license, just as the GPLv3 license closed the GPLv2's TiVoization loophole. And
|
||||
so it is important that bsnes retains the option of relicensing to the GPLv4+ in
|
||||
the future.
|
||||
|
||||
As a point of interest, there have been projects with similar concerns about
|
||||
using a GPLv2 or later clause, that are now permanently stuck on the GPLv2
|
||||
license. There have also been projects that did use a GPLv2 or later clause,
|
||||
only to disagree with the changes introduced in the GPLv3.
|
||||
|
||||
ISC
|
||||
---
|
||||
|
||||
The more important reason for this requirement is that it is my intention to
|
||||
release the entirety of bsnes under the ISC license once official upstream
|
||||
development has ceased.
|
||||
|
||||
The reason I would want to relicense bsnes to the ISC license upon its official
|
||||
discontinuation is because once again, no one is an oracle, and I cannot predict
|
||||
what future issues bsnes permanently remaining under the GPLv3 license may
|
||||
cause.
|
||||
|
||||
For instance, imagine a world where a certain vendor took over the world, and
|
||||
the only way to distribute applications was with their approval, and their store
|
||||
rules forbade GPLv3 software. Or perhaps a world where the GPL was abandoned in
|
||||
favor of the new OSSv1 license. But GPLv3 software was incompatible with the
|
||||
OSSv1 license. Other open source developers would not be able to use bsnes in
|
||||
that scenario.
|
||||
|
||||
It would be very disappointing if all of our work ended up unusable 50+ years
|
||||
into the future because it was permanently bound to the GPLv3 license.
|
||||
|
||||
GPLv3
|
||||
-----
|
||||
|
||||
The reason I use the GPLv3 license currently is because it is a balance between
|
||||
altruism and self-interest. The GPLv3 allows other vendors to sell my own code
|
||||
without sharing revenue with me, and indeed this has already happened. But the
|
||||
GPLv3 also prevents other vendors from improving upon bsnes without sharing
|
||||
their work with everyone else as I have.
|
||||
|
||||
While I am actively developing bsnes, I do not wish to compete against myself.
|
||||
|
||||
As such, I believe the GPLv3 is the best license during active development, and
|
||||
the ISC is the best license once bsnes is officially discontinued.
|
||||
|
||||
Considerations
|
||||
--------------
|
||||
|
||||
This is the part that should concern you as a contributor: I am not requesting
|
||||
contributed source code to be released under the ISC so that I personally may
|
||||
sell GPLv3 commercial license exemptions to your work, but in the future when
|
||||
bsnes is released under the ISC license, that will open the door for anyone to
|
||||
sell the work commercially in a closed source form.
|
||||
|
||||
If this is not acceptable to you, I wholly understand and I welcome you to
|
||||
release your work under the GPLv3 in the form of a bsnes fork. And if your work
|
||||
is not an essential part of the core emulation -- that is to say, it may be
|
||||
optionally disabled -- then I am still willing to work with you in merging such
|
||||
work upstream anyway under the full GPLv3 license, but please reach out to me
|
||||
first before developing under the assumption your work will be merged upstream.
|
||||
|
||||
Thank you very much for reading and hopefully for your understanding.
|
674
GPLv3.txt
Normal file
674
GPLv3.txt
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Use with the GNU Affero General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU Affero General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU GPL, see
|
||||
<https://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
43
LICENSE.txt
Normal file
43
LICENSE.txt
Normal file
@@ -0,0 +1,43 @@
|
||||
----------------------------------------------------------------------
|
||||
bsnes - Super Nintendo emulator
|
||||
|
||||
Copyright © 2004-2019 byuu
|
||||
|
||||
https://byuu.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, specifically version 3 of the License
|
||||
and no other version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
----------------------------------------------------------------------
|
||||
libco - C cooperative threading library
|
||||
nall - C++ template library
|
||||
ruby - Hardware abstraction library
|
||||
hiro - User interface library
|
||||
|
||||
Copyright © 2006-2019 byuu
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for
|
||||
any purpose with or without fee is hereby granted, provided that the
|
||||
above copyright notice and this permission notice appear in all
|
||||
copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
|
||||
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
|
||||
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
----------------------------------------------------------------------
|
109
Makefile
109
Makefile
@@ -1,109 +0,0 @@
|
||||
include nall/Makefile
|
||||
|
||||
fc := fc
|
||||
sfc := sfc
|
||||
gb := gb
|
||||
gba := gba
|
||||
|
||||
profile := accuracy
|
||||
target := ethos
|
||||
|
||||
# options += debugger
|
||||
# arch := x86
|
||||
# console := true
|
||||
|
||||
# compiler
|
||||
flags += -I. -O3 -fomit-frame-pointer
|
||||
link +=
|
||||
objects := libco
|
||||
|
||||
# profile-guided optimization mode
|
||||
# pgo := instrument
|
||||
# pgo := optimize
|
||||
|
||||
ifeq ($(pgo),instrument)
|
||||
flags += -fprofile-generate
|
||||
link += -lgcov
|
||||
else ifeq ($(pgo),optimize)
|
||||
flags += -fprofile-use
|
||||
endif
|
||||
|
||||
# platform
|
||||
ifeq ($(platform),windows)
|
||||
ifeq ($(arch),x86)
|
||||
flags += -m32
|
||||
link += -m32
|
||||
endif
|
||||
ifeq ($(console),true)
|
||||
link += -mconsole
|
||||
else
|
||||
link += -mwindows
|
||||
endif
|
||||
link += -s -mthreads -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32 -lole32 -lws2_32
|
||||
link += -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc
|
||||
else ifeq ($(platform),macosx)
|
||||
flags += -march=native
|
||||
else ifeq ($(platform),linux)
|
||||
flags += -march=native
|
||||
link += -s -Wl,-export-dynamic -lX11 -lXext -ldl
|
||||
else ifeq ($(platform),bsd)
|
||||
flags += -march=native
|
||||
link += -s -Wl,-export-dynamic -lX11 -lXext
|
||||
else
|
||||
$(error unsupported platform.)
|
||||
endif
|
||||
|
||||
ui := target-$(target)
|
||||
|
||||
# implicit rules
|
||||
compile = \
|
||||
$(strip \
|
||||
$(if $(filter %.c,$<), \
|
||||
$(compiler) $(cflags) $(flags) $1 -c $< -o $@, \
|
||||
$(if $(filter %.cpp,$<), \
|
||||
$(compiler) $(cppflags) $(flags) $1 -c $< -o $@ \
|
||||
) \
|
||||
) \
|
||||
)
|
||||
|
||||
%.o: $<; $(call compile)
|
||||
|
||||
all: build;
|
||||
|
||||
obj/libco.o: libco/libco.c libco/*
|
||||
|
||||
include $(ui)/Makefile
|
||||
flags := $(flags) $(foreach o,$(call strupper,$(options)),-D$o)
|
||||
|
||||
# targets
|
||||
clean:
|
||||
-@$(call delete,obj/*.o)
|
||||
-@$(call delete,obj/*.a)
|
||||
-@$(call delete,obj/*.so)
|
||||
-@$(call delete,obj/*.dylib)
|
||||
-@$(call delete,obj/*.dll)
|
||||
-@$(call delete,*.res)
|
||||
-@$(call delete,*.manifest)
|
||||
|
||||
archive:
|
||||
if [ -f higan.tar.xz ]; then rm higan.tar.xz; fi
|
||||
tar -cJf higan.tar.xz `ls`
|
||||
|
||||
sync:
|
||||
ifeq ($(shell id -un),byuu)
|
||||
if [ -d ./libco ]; then rm -r ./libco; fi
|
||||
if [ -d ./nall ]; then rm -r ./nall; fi
|
||||
if [ -d ./ruby ]; then rm -r ./ruby; fi
|
||||
if [ -d ./phoenix ]; then rm -r ./phoenix; fi
|
||||
cp -r ../libco ./libco
|
||||
cp -r ../nall ./nall
|
||||
cp -r ../ruby ./ruby
|
||||
cp -r ../phoenix ./phoenix
|
||||
rm -r libco/doc
|
||||
rm -r libco/.test
|
||||
rm -r nall/.test
|
||||
rm -r ruby/.test
|
||||
rm -r phoenix/.test
|
||||
endif
|
||||
|
||||
help:;
|
99
README.md
Normal file
99
README.md
Normal file
@@ -0,0 +1,99 @@
|
||||
bsnes
|
||||
=====
|
||||
|
||||

|
||||
|
||||
bsnes is a multi-platform Super Nintendo (Super Famicom) emulator from
|
||||
[byuu](https://byuu.org) that focuses on performance, features, and ease of use.
|
||||
|
||||
bsnes currently enjoys 100% known, bug-free compatibility with the entire SNES
|
||||
library when configured to its most accurate settings, giving it the same
|
||||
accuracy level as higan. Accuracy can also optionally be traded for performance,
|
||||
allowing bsnes to operate more than 300% faster than higan while still remaining
|
||||
almost as accurate.
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
Git is used for the development of new releases, and represents a staging
|
||||
environment. As bsnes is rather mature, things should generally be quite stable.
|
||||
However, bugs will exist, regressions will occur, so proceed at your own risk.
|
||||
|
||||
If stability is required, please download the latest stable release from the
|
||||
[official website.](https://bsnes.byuu.org)
|
||||
|
||||
Unique Features
|
||||
---------------
|
||||
|
||||
- 100% (known) bug-free compatibility with the entire officially licensed SNES games library
|
||||
- True Super Game Boy emulation (using the SameBoy core by Lior Halphon)
|
||||
- HD mode 7 graphics with optional supersampling (by DerKoun)
|
||||
- Low-level emulation of all SNES coprocessors (DSP-n, ST-01n, Cx4)
|
||||
- Multi-threaded PPU graphics renderer
|
||||
- Speed mode settings which retain smooth audio output (50%, 75%, 100%, 150%, 200%)
|
||||
- Built-in games database with thousands of game entries
|
||||
- Built-in cheat code database for hundreds of popular games (by mightymo)
|
||||
- Built-in save state manager with screenshot previews and naming capabilities
|
||||
- Customizable per-byte game mappings to support any cartridges, including prototype games
|
||||
- 7-zip decompression support
|
||||
- Extensive Satellaview emulation, including BS Memory flash write and wear-leveling emulation
|
||||
- Optional higan game folder support (standard game ROM files are also fully supported!)
|
||||
- Advanced mapping system allowing multiple bindings to every emulated input
|
||||
|
||||
Standard Features
|
||||
-----------------
|
||||
|
||||
- MSU1 support
|
||||
- BPS and IPS soft-patching support
|
||||
- Save states with undo and redo support (for reverting accidental saves and loads)
|
||||
- OpenGL multi-pass pixel shaders
|
||||
- Several built-in software filters, including HQ2x (by MaxSt) and snes_ntsc (by blargg)
|
||||
- Adaptive sync and dynamic rate control for perfect audio/video synchronization
|
||||
- Just-in-time input polling for minimal input latency
|
||||
- Run-ahead support for removing internal game engine input latency
|
||||
- Support for Direct3D exclusive mode video
|
||||
- Support for WASAPI exclusive mode audio
|
||||
- Periodic auto-saving of game saves
|
||||
- Auto-saving of states when unloading games, and auto-resuming of states when reloading games
|
||||
- Sprite limit disable support
|
||||
- Cubic audio interpolation support
|
||||
- Optional high-level emulation of most SNES coprocessors
|
||||
- Optional emulation of flaws in older emulators for compatibility with older unofficial software
|
||||
- CPU, SA1, and SuperFX overclocking support
|
||||
- Frame advance support
|
||||
- Screenshot support
|
||||
- Cheat code search support
|
||||
- Movie recording and playback support
|
||||
- Rewind support
|
||||
- HiDPI support
|
||||
- Multi-monitor support
|
||||
- Turbo support for controller inputs
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
- [Official website](https://byuu.org/bsnes)
|
||||
- [Official git repository](https://github.com/byuu/bsnes)
|
||||
- [Developer resources](https://byuu.net)
|
||||
- [Donations](https://patreon.com/byuu)
|
||||
|
||||
Release Builds
|
||||
--------------
|
||||
|
||||
- [Windows binaries](https://byuu.itch.io/bsnes)
|
||||
|
||||
Nightly Builds
|
||||
--------------
|
||||
|
||||
- [Download](https://cirrus-ci.com/github/byuu/bsnes/master)
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
- 
|
||||
|
||||
Preview
|
||||
-------
|
||||
|
||||

|
||||

|
||||

|
@@ -1,49 +0,0 @@
|
||||
include ../nall/Makefile
|
||||
include ../phoenix/Makefile
|
||||
|
||||
path := /usr/local/lib
|
||||
flags := $(flags) -O3 -fomit-frame-pointer -I..
|
||||
|
||||
all:
|
||||
$(compiler) $(cppflags) $(flags) -fPIC -o obj/ananke.o -c ananke.cpp
|
||||
ifeq ($(platform),windows)
|
||||
$(compiler) $(phoenixflags) -fPIC -o obj/phoenix.o -c ../phoenix/phoenix.cpp
|
||||
$(compiler) $(link) -shared -o phoenix.dll obj/phoenix.o $(phoenixlink)
|
||||
$(compiler) $(link) -shared -o ananke.dll obj/ananke.o -L. -lphoenix
|
||||
else ifeq ($(platform),macosx)
|
||||
$(compiler) $(link) -shared -dynamiclib -undefined suppress -flat_namespace -o libananke.dylib obj/ananke.o
|
||||
else
|
||||
$(compiler) $(link) -shared -Wl,-soname,libananke.so.1 -o libananke.so obj/ananke.o
|
||||
endif
|
||||
|
||||
resource: force
|
||||
sourcery resource/resource.bml resource/resource.cpp resource/resource.hpp
|
||||
|
||||
clean:
|
||||
-@$(call delete,obj/*.o)
|
||||
-@$(call delete,*.dll)
|
||||
-@$(call delete,*.so)
|
||||
|
||||
install: uninstall
|
||||
ifeq ($(platform),windows)
|
||||
else ifeq ($(platform),macosx)
|
||||
if [ ! -d ~/Library/Application\ Support/ananke ]; then mkdir ~/Library/Application\ Support/ananke; fi
|
||||
sudo cp libananke.dylib $(path)/libananke.1.dylib
|
||||
sudo ln -s $(path)/libananke.1.dylib $(path)/libananke.dylib
|
||||
else
|
||||
if [ ! -d ~/.config/ananke ]; then mkdir ~/.config/ananke; fi
|
||||
sudo cp libananke.so $(path)/libananke.so.1
|
||||
sudo ln -s $(path)/libananke.so.1 $(path)/libananke.so
|
||||
endif
|
||||
|
||||
uninstall:
|
||||
ifeq ($(platform),windows)
|
||||
else ifeq ($(platform),macosx)
|
||||
if [ -f $(path)/libananke.dylib ]; then sudo rm $(path)/libananke.dylib; fi
|
||||
if [ -f $(path)/libananke.1.dylib ]; then sudo rm $(path)/libananke.1.dylib; fi
|
||||
else
|
||||
if [ -f $(path)/libananke.so ]; then sudo rm $(path)/libananke.so; fi
|
||||
if [ -f $(path)/libananke.so.1 ]; then sudo rm $(path)/libananke.so.1; fi
|
||||
endif
|
||||
|
||||
force:
|
@@ -1,179 +0,0 @@
|
||||
#include <nall/nall.hpp>
|
||||
#include <nall/beat/patch.hpp>
|
||||
#include "heuristics/famicom.hpp"
|
||||
#include "heuristics/super-famicom.hpp"
|
||||
#include "heuristics/game-boy.hpp"
|
||||
#include "heuristics/game-boy-advance.hpp"
|
||||
using namespace nall;
|
||||
|
||||
#include <phoenix/phoenix.hpp>
|
||||
using namespace phoenix;
|
||||
|
||||
namespace Database {
|
||||
#include "database/super-famicom.hpp"
|
||||
#include "database/sufami-turbo.hpp"
|
||||
#include "database/bsx-satellaview.hpp"
|
||||
};
|
||||
|
||||
struct Ananke {
|
||||
#include "configuration.cpp"
|
||||
string libraryPath;
|
||||
|
||||
Ananke();
|
||||
|
||||
struct Information {
|
||||
string path; //path to selected file
|
||||
string name; //name of selected file (inside of archive if .zip)
|
||||
string archive; //pathname of archive
|
||||
string manifest; //manifest from successfully applied patch
|
||||
} information;
|
||||
|
||||
//archive.cpp
|
||||
vector<uint8_t> extractROM();
|
||||
vector<uint8_t> extractFile(const string &filename);
|
||||
|
||||
//patch.cpp
|
||||
void applyBeatPatch(vector<uint8_t> &buffer);
|
||||
|
||||
//famicom.cpp
|
||||
void copyFamicomSaves(const string &pathname);
|
||||
string createFamicomHeuristic(vector<uint8_t> &buffer);
|
||||
string openFamicom(vector<uint8_t> &buffer);
|
||||
string syncFamicom(const string &pathname);
|
||||
|
||||
//super-famicom.cpp
|
||||
void copySuperFamicomSaves(const string &pathname);
|
||||
string createSuperFamicomDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
|
||||
string createSuperFamicomHeuristic(vector<uint8_t> &buffer);
|
||||
void createSuperFamicomHeuristicFirmware(vector<uint8_t> &buffer, const string &pathname, bool firmware_appended);
|
||||
string openSuperFamicom(vector<uint8_t> &buffer);
|
||||
string syncSuperFamicom(const string &pathname);
|
||||
|
||||
//sufami-turbo.cpp
|
||||
void copySufamiTurboSaves(const string &pathname);
|
||||
string createSufamiTurboDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
|
||||
string createSufamiTurboHeuristic(vector<uint8_t> &buffer);
|
||||
string openSufamiTurbo(vector<uint8_t> &buffer);
|
||||
string syncSufamiTurbo(const string &pathname);
|
||||
|
||||
//bsx-satellaview.cpp
|
||||
string createBsxSatellaviewDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest);
|
||||
string createBsxSatellaviewHeuristic(vector<uint8_t> &buffer);
|
||||
string openBsxSatellaview(vector<uint8_t> &buffer);
|
||||
string syncBsxSatellaview(const string &pathname);
|
||||
|
||||
//game-boy.cpp
|
||||
void copyGameBoySaves(const string &pathname);
|
||||
string createGameBoyHeuristic(vector<uint8_t> &buffer);
|
||||
string openGameBoy(vector<uint8_t> &buffer);
|
||||
string syncGameBoy(const string &pathname);
|
||||
|
||||
//game-boy-advance.cpp
|
||||
void copyGameBoyAdvanceSaves(const string &pathname);
|
||||
string createGameBoyAdvanceHeuristic(vector<uint8_t> &buffer);
|
||||
string openGameBoyAdvance(vector<uint8_t> &buffer);
|
||||
string syncGameBoyAdvance(const string &pathname);
|
||||
|
||||
static bool supported(const string &filename);
|
||||
string open(string filename = "");
|
||||
string sync(string pathname);
|
||||
};
|
||||
|
||||
#include "resource/resource.cpp"
|
||||
#include "file-dialog.cpp"
|
||||
#include "archive.cpp"
|
||||
#include "patch.cpp"
|
||||
#include "famicom.cpp"
|
||||
#include "super-famicom.cpp"
|
||||
#include "sufami-turbo.cpp"
|
||||
#include "bsx-satellaview.cpp"
|
||||
#include "game-boy.cpp"
|
||||
#include "game-boy-advance.cpp"
|
||||
|
||||
FileDialog *fileDialog = nullptr;
|
||||
|
||||
Ananke::Ananke() {
|
||||
libraryPath = string::read({configpath(), "higan/library.bml"}).strip().ltrim<1>("Path: ").replace("\\", "/");
|
||||
if(libraryPath.empty()) libraryPath = {userpath(), "Emulation/"};
|
||||
if(libraryPath.endsWith("/") == false) libraryPath.append("/");
|
||||
}
|
||||
|
||||
bool Ananke::supported(const string &filename) {
|
||||
string extension = nall::extension(filename);
|
||||
|
||||
if(extension == "fc" ) return true;
|
||||
if(extension == "nes") return true;
|
||||
if(extension == "sfc") return true;
|
||||
if(extension == "smc") return true;
|
||||
if(extension == "st" ) return true;
|
||||
if(extension == "bs" ) return true;
|
||||
if(extension == "gb" ) return true;
|
||||
if(extension == "gbc") return true;
|
||||
if(extension == "gba") return true;
|
||||
if(extension == "zip") return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
string Ananke::open(string filename) {
|
||||
if(filename.empty()) {
|
||||
if(!fileDialog) {
|
||||
fileDialog = new FileDialog;
|
||||
fileDialog->setGeometry(config.geometry);
|
||||
}
|
||||
fileDialog->setPath(config.path);
|
||||
filename = fileDialog->open();
|
||||
config.geometry = fileDialog->geometry().text();
|
||||
}
|
||||
|
||||
if(filename.empty()) return "";
|
||||
|
||||
information.path = dir(filename);
|
||||
information.name = notdir(filename);
|
||||
config.path = information.path; //remember last used directory
|
||||
|
||||
vector<uint8_t> buffer;
|
||||
if(filename.endsWith(".zip")) {
|
||||
information.archive = filename;
|
||||
buffer = extractROM();
|
||||
} else {
|
||||
buffer = file::read(filename);
|
||||
}
|
||||
if(buffer.size() == 0) return ""; //failed to read file
|
||||
|
||||
applyBeatPatch(buffer);
|
||||
|
||||
if(information.name.endsWith(".fc") || information.name.endsWith(".nes")) return openFamicom(buffer);
|
||||
if(information.name.endsWith(".sfc") || information.name.endsWith(".smc")) return openSuperFamicom(buffer);
|
||||
if(information.name.endsWith(".st")) return openSufamiTurbo(buffer);
|
||||
if(information.name.endsWith(".bs")) return openBsxSatellaview(buffer);
|
||||
if(information.name.endsWith(".gb") || information.name.endsWith(".gbc")) return openGameBoy(buffer);
|
||||
if(information.name.endsWith(".gba")) return openGameBoyAdvance(buffer);
|
||||
return "";
|
||||
}
|
||||
|
||||
string Ananke::sync(string pathname) {
|
||||
if(pathname.endsWith(".fc/")) return syncFamicom(pathname);
|
||||
if(pathname.endsWith(".sfc/")) return syncSuperFamicom(pathname);
|
||||
if(pathname.endsWith(".st/")) return syncSufamiTurbo(pathname);
|
||||
if(pathname.endsWith(".bs/")) return syncBsxSatellaview(pathname);
|
||||
if(pathname.endsWith(".gb/")) return syncGameBoy(pathname);
|
||||
if(pathname.endsWith(".gbc/")) return syncGameBoy(pathname);
|
||||
if(pathname.endsWith(".gba/")) return syncGameBoyAdvance(pathname);
|
||||
return "";
|
||||
}
|
||||
|
||||
extern "C" string ananke_browse(const string &filename) {
|
||||
Ananke ananke;
|
||||
return ananke.open();
|
||||
}
|
||||
|
||||
extern "C" string ananke_open(const string &filename) {
|
||||
Ananke ananke;
|
||||
return ananke.open(filename);
|
||||
}
|
||||
|
||||
extern "C" string ananke_sync(const string &pathname) {
|
||||
Ananke ananke;
|
||||
return ananke.sync(pathname);
|
||||
}
|
@@ -1,29 +0,0 @@
|
||||
vector<uint8_t> Ananke::extractROM() {
|
||||
unzip archive;
|
||||
if(archive.open(information.archive)) {
|
||||
for(auto& file : archive.file) {
|
||||
if(file.name.endsWith(".fc") || file.name.endsWith(".nes")
|
||||
|| file.name.endsWith(".sfc") || file.name.endsWith(".smc")
|
||||
|| file.name.endsWith(".st") || file.name.endsWith(".bs")
|
||||
|| file.name.endsWith(".gb") || file.name.endsWith(".gbc")
|
||||
|| file.name.endsWith(".gba")
|
||||
) {
|
||||
information.name = notdir(file.name);
|
||||
return archive.extract(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return vector<uint8_t>();
|
||||
}
|
||||
|
||||
vector<uint8_t> Ananke::extractFile(const string& filename) {
|
||||
unzip archive;
|
||||
if(archive.open(information.archive)) {
|
||||
for(auto& file : archive.file) {
|
||||
if(notdir(file.name) == filename) {
|
||||
return archive.extract(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
return vector<uint8_t>();
|
||||
}
|
@@ -1,72 +0,0 @@
|
||||
string Ananke::createBsxSatellaviewDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest) {
|
||||
string pathname = {
|
||||
libraryPath, "BS-X Satellaview/",
|
||||
document["release/information/name"].text(),
|
||||
" (", document["release/information/region"].text(), ")",
|
||||
" (", document["release/information/revision"].text(), ")",
|
||||
".bs/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
//strip "release" root node from database entry (since a single game manifest isn't part of a database)
|
||||
string markup = manifest;
|
||||
markup.replace("\n ", "\n");
|
||||
markup.replace("information", "\ninformation");
|
||||
markup.ltrim<1>("release\n");
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::createBsxSatellaviewHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
libraryPath, "BS-X Satellaview/",
|
||||
nall::basename(information.name),
|
||||
".bs/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
file::write({pathname, "manifest.bml"}, {
|
||||
"unverified\n",
|
||||
"\n",
|
||||
"cartridge\n",
|
||||
" rom name=program.rom size=0x", hex(buffer.size()), " type=FlashROM\n",
|
||||
"\n",
|
||||
"information\n",
|
||||
" title: ", nall::basename(information.name), "\n"
|
||||
});
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openBsxSatellaview(vector<uint8_t> &buffer) {
|
||||
string sha256 = nall::sha256(buffer.data(), buffer.size());
|
||||
|
||||
string databaseText = string::read({configpath(), "ananke/database/BS-X Satellaview.bml"}).strip();
|
||||
if(databaseText.empty()) databaseText = string{Database::BsxSatellaview}.strip();
|
||||
lstring databaseItem = databaseText.split("\n\n");
|
||||
|
||||
for(auto &item : databaseItem) {
|
||||
item.append("\n");
|
||||
auto document = Markup::Document(item);
|
||||
|
||||
if(document["release/information/sha256"].text() == sha256) {
|
||||
return createBsxSatellaviewDatabase(buffer, document, item);
|
||||
}
|
||||
}
|
||||
|
||||
return createBsxSatellaviewHeuristic(buffer);
|
||||
}
|
||||
|
||||
string Ananke::syncBsxSatellaview(const string &pathname) {
|
||||
auto buffer = file::read({pathname, "program.rom"});
|
||||
if(buffer.size() == 0) return "";
|
||||
|
||||
directory::remove(pathname);
|
||||
information.path = pathname;
|
||||
information.name = notdir(string{pathname}.rtrim<1>("/"));
|
||||
return openBsxSatellaview(buffer);
|
||||
}
|
@@ -1,17 +0,0 @@
|
||||
struct Settings : Configuration::Document {
|
||||
string path;
|
||||
string geometry;
|
||||
|
||||
Settings() {
|
||||
Configuration::Node node;
|
||||
node.append(path = userpath(), "Path");
|
||||
node.append(geometry = "64,64,480,600", "Geometry");
|
||||
append(node, "Settings");
|
||||
directory::create({configpath(), "ananke/"});
|
||||
load({configpath(), "ananke/settings.bml"});
|
||||
}
|
||||
|
||||
~Settings() {
|
||||
save({configpath(), "ananke/settings.bml"});
|
||||
}
|
||||
} config;
|
@@ -1,19 +0,0 @@
|
||||
string BsxSatellaview = R"(
|
||||
|
||||
database revision=2013-01-22
|
||||
|
||||
release
|
||||
cartridge
|
||||
rom name=program.rom size=0x80000 type=MaskROM
|
||||
information
|
||||
title: 鮫亀 キャラカセット
|
||||
name: Same Game - Character Cassette
|
||||
region: JP
|
||||
revision: 1.0
|
||||
board: BSMC-CR-01
|
||||
serial: BSMC-ZS5J-JPN
|
||||
sha256: 80c34b50817d58820bc8c88d2d9fa462550b4a76372e19c6467cbfbc8cf5d9ef
|
||||
configuration
|
||||
rom name=program.rom size=0x80000 type=MaskROM
|
||||
|
||||
)";
|
@@ -1,162 +0,0 @@
|
||||
string SufamiTurbo = R"(
|
||||
|
||||
database revision=2013-01-22
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDウルトラバトル ウルトラマン伝説
|
||||
name: SD Ultra Battle - Ultraman Densetsu
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0101-JPN
|
||||
sha256: 2bb55214fb668ca603d7b944b14f105dfb10b987a8902d420fe4ae1cb69c1d4a
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDウルトラバトル セブン伝説
|
||||
name: SD Ultra Battle - Seven Densetsu
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0102-JPN
|
||||
sha256: 2fec5f2bc7dee010af10569a3d2bc18715a79a126940800c3eade5abbd625e3f
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: ポイポイ忍者ワールド
|
||||
name: Poi Poi Ninja World
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0103-JPN
|
||||
sha256: 602b20b788640f5743487108a10f3f77bca5ce2d24208b25b1ca498a96eb0d69
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション 一年戦争記
|
||||
name: SD Gundam Generation - Ichinen Sensouki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0104-JPN
|
||||
sha256: 3e82215bed08274874b30d461fc4a965c6bca932229da5d46d56e36f484d65eb
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション グリプス戦記
|
||||
name: SD Gundam Generation - Grips Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0105-JPN
|
||||
sha256: 8547a08ed11fe408eac282a90ac46654bd2e5f49bda3aec8e5edf166a0a4b9af
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge
|
||||
rom name=program.rom size=0x80000
|
||||
information
|
||||
title: ゲゲゲの鬼太郎 妖怪ドンジャラ
|
||||
name: Gegege no Kitarou - Youkai Donjara
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0106-JPN
|
||||
sha256: d93b3a570e7cf343f680ab0768a50b77e3577f9c555007e2de3decd6bc4765c8
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション アクシズ戦記
|
||||
name: SD Gundam Generation - Axis Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0107-JPN
|
||||
sha256: 2a9d7c9a61318861028a73ca03e32a48cff162d76cba36fbaab8690b212efe9b
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション バビロニア建国戦記
|
||||
name: SD Gundam Generation - Babylonia Kenkoku Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0108-JPN
|
||||
sha256: 60ac017c18f534e8cf24ca7f38e22ce92db95ea6c30b2d59d76f13c4f1c8a6e4
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
information
|
||||
title: SDガンダムジェネレーション ザンスカール戦記
|
||||
name: SD Gundam Generation - Zanscar Senki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0110-JPN
|
||||
sha256: 5951a58a91d8e397d0a237ccc2b1248e17c7312cb9cc11cbc350200a97b4e021
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x2000
|
||||
linkable
|
||||
|
||||
release
|
||||
cartridge linkable
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
information
|
||||
title: SDガンダムジェネレーション コロニー格闘記
|
||||
name: SD Gundam Generation - Colony Kakutouki
|
||||
region: JP
|
||||
revision: 1.0
|
||||
serial: SFT-0111-JPN
|
||||
sha256: e639b5d5d722432b6809ccc6801dc584e1a3016379f34b335ed2dfa73b1ebf69
|
||||
configuration
|
||||
rom name=program.rom size=0x80000
|
||||
ram name=save.ram size=0x800
|
||||
linkable
|
||||
|
||||
)";
|
File diff suppressed because it is too large
Load Diff
@@ -1,39 +0,0 @@
|
||||
void Ananke::copyFamicomSaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".srm"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createFamicomHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
libraryPath, "Famicom/",
|
||||
nall::basename(information.name),
|
||||
".fc/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
FamicomCartridge info(buffer.data(), buffer.size());
|
||||
string markup = {"unverified\n\n", info.markup};
|
||||
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
|
||||
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer.data() + 16, info.prgrom);
|
||||
if(info.chrrom > 0) file::write({pathname, "character.rom"}, buffer.data() + 16 + info.prgrom, info.chrrom);
|
||||
|
||||
copyFamicomSaves(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openFamicom(vector<uint8_t> &buffer) {
|
||||
return createFamicomHeuristic(buffer);
|
||||
}
|
||||
|
||||
//this currently cannot work:
|
||||
//game folders discard iNES header required for heuristic detection
|
||||
//a games database of all commercial Famicom software will be required
|
||||
string Ananke::syncFamicom(const string &pathname) {
|
||||
return "";
|
||||
}
|
@@ -1,106 +0,0 @@
|
||||
struct FileDialog : Window {
|
||||
VerticalLayout layout;
|
||||
HorizontalLayout pathLayout;
|
||||
LineEdit pathEdit;
|
||||
Button homeButton;
|
||||
Button upButton;
|
||||
ListView fileList;
|
||||
HorizontalLayout controlLayout;
|
||||
Label filterLabel;
|
||||
Button openButton;
|
||||
|
||||
string open() {
|
||||
setVisible();
|
||||
fileList.setFocused();
|
||||
filename = "";
|
||||
|
||||
setModal();
|
||||
return filename;
|
||||
}
|
||||
|
||||
void setPath(const string &path) {
|
||||
pathname = string{path}.transform("\\", "/");
|
||||
if(pathname.empty()) pathname = userpath();
|
||||
if(pathname.endsWith("/") == false) pathname.append("/");
|
||||
pathEdit.setText(pathname);
|
||||
|
||||
fileList.reset();
|
||||
filenameList.reset();
|
||||
|
||||
lstring folders = directory::ifolders(pathname);
|
||||
for(auto &folder : folders) {
|
||||
fileList.append(string{folder}.rtrim<1>("/"));
|
||||
fileList.setImage(filenameList.size(), 0, {resource::folder, sizeof resource::folder});
|
||||
filenameList.append({pathname, folder});
|
||||
}
|
||||
|
||||
lstring files = directory::ifiles(pathname);
|
||||
for(auto &file : files) {
|
||||
if(Ananke::supported(file) == false) continue; //ignore unsupported extensions
|
||||
fileList.append(file);
|
||||
if(extension(file) == "zip") {
|
||||
fileList.setImage(filenameList.size(), 0, {resource::archive, sizeof resource::archive});
|
||||
} else {
|
||||
fileList.setImage(filenameList.size(), 0, {resource::file, sizeof resource::file});
|
||||
}
|
||||
filenameList.append({pathname, file});
|
||||
}
|
||||
|
||||
fileList.setSelection(0);
|
||||
fileList.setSelected();
|
||||
fileList.setFocused();
|
||||
}
|
||||
|
||||
FileDialog() {
|
||||
setTitle("Load Image");
|
||||
|
||||
layout.setMargin(5);
|
||||
homeButton.setImage({resource::home, sizeof resource::home});
|
||||
upButton.setImage({resource::up, sizeof resource::up});
|
||||
filterLabel.setText("Filter: *.fc, *.sfc, *.st, *.bs, *.gb, *.gbc, *.gba, *.nes, *.smc, *.zip");
|
||||
openButton.setText("Open");
|
||||
|
||||
append(layout);
|
||||
layout.append(pathLayout, {~0, 0}, 5);
|
||||
pathLayout.append(pathEdit, {~0, 0}, 5);
|
||||
pathLayout.append(homeButton, {28, 28}, 5);
|
||||
pathLayout.append(upButton, {28, 28});
|
||||
layout.append(fileList, {~0, ~0}, 5);
|
||||
layout.append(controlLayout, {~0, 0});
|
||||
controlLayout.append(filterLabel, {~0, 0}, 5);
|
||||
controlLayout.append(openButton, {80, 0});
|
||||
|
||||
pathEdit.onActivate = [&] {
|
||||
string path = pathEdit.text();
|
||||
setPath(path);
|
||||
};
|
||||
|
||||
homeButton.onActivate = [&] {
|
||||
setPath(userpath());
|
||||
};
|
||||
|
||||
upButton.onActivate = [&] {
|
||||
setPath(parentdir(pathname));
|
||||
};
|
||||
|
||||
fileList.onActivate = openButton.onActivate = [&] {
|
||||
if(fileList.selected() == false) return;
|
||||
string name = filenameList(fileList.selection());
|
||||
if(name.empty()) return;
|
||||
|
||||
if(name.endsWith("/")) return setPath(name);
|
||||
filename = name;
|
||||
onClose();
|
||||
};
|
||||
|
||||
onClose = [&] {
|
||||
setModal(false);
|
||||
setVisible(false);
|
||||
};
|
||||
}
|
||||
|
||||
private:
|
||||
string pathname;
|
||||
string filename;
|
||||
lstring filenameList;
|
||||
};
|
@@ -1,58 +0,0 @@
|
||||
void Ananke::copyGameBoyAdvanceSaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".sav"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
|
||||
if(!file::exists({pathname, "rtc.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".rtc"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".rtc"}, {pathname, "rtc.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createGameBoyAdvanceHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
libraryPath, "Game Boy Advance/",
|
||||
nall::basename(information.name),
|
||||
".gba/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
GameBoyAdvanceCartridge info(buffer.data(), buffer.size());
|
||||
string markup = {"unverified\n\n", info.markup};
|
||||
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
|
||||
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
copyGameBoyAdvanceSaves(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openGameBoyAdvance(vector<uint8_t> &buffer) {
|
||||
return createGameBoyAdvanceHeuristic(buffer);
|
||||
}
|
||||
|
||||
string Ananke::syncGameBoyAdvance(const string &pathname) {
|
||||
auto buffer = file::read({pathname, "program.rom"});
|
||||
if(buffer.size() == 0) return "";
|
||||
|
||||
auto save = file::read({pathname, "save.ram"});
|
||||
if(save.size() == 0) save = file::read({pathname, "save.rwm"});
|
||||
|
||||
auto rtc = file::read({pathname, "rtc.ram"});
|
||||
if(rtc.size() == 0) rtc = file::read({pathname, "rtc.rwm"});
|
||||
|
||||
directory::remove(pathname);
|
||||
information.path = pathname;
|
||||
information.name = notdir(string{pathname}.rtrim<1>("/"));
|
||||
string outputPath = openGameBoyAdvance(buffer);
|
||||
|
||||
if(save.size()) file::write({outputPath, "save.ram"}, save);
|
||||
if(rtc.size()) file::write({outputPath, "rtc.ram"}, rtc);
|
||||
|
||||
return outputPath;
|
||||
}
|
@@ -1,59 +0,0 @@
|
||||
void Ananke::copyGameBoySaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".sav"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".sav"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
|
||||
if(!file::exists({pathname, "rtc.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".rtc"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".rtc"}, {pathname, "rtc.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createGameBoyHeuristic(vector<uint8_t> &buffer) {
|
||||
GameBoyCartridge info(buffer.data(), buffer.size());
|
||||
|
||||
string pathname = {
|
||||
libraryPath, "Game Boy", (info.info.cgb ? " Color" : ""), "/",
|
||||
nall::basename(information.name),
|
||||
".", (info.info.cgb ? "gbc" : "gb"), "/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
string markup = {"unverified\n\n", info.markup};
|
||||
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
|
||||
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
|
||||
copyGameBoySaves(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openGameBoy(vector<uint8_t> &buffer) {
|
||||
return createGameBoyHeuristic(buffer);
|
||||
}
|
||||
|
||||
string Ananke::syncGameBoy(const string &pathname) {
|
||||
auto buffer = file::read({pathname, "program.rom"});
|
||||
if(buffer.size() == 0) return "";
|
||||
|
||||
auto save = file::read({pathname, "save.ram"});
|
||||
if(save.size() == 0) save = file::read({pathname, "save.rwm"});
|
||||
|
||||
auto rtc = file::read({pathname, "rtc.ram"});
|
||||
if(rtc.size() == 0) rtc = file::read({pathname, "rtc.rwm"});
|
||||
|
||||
directory::remove(pathname);
|
||||
information.path = pathname;
|
||||
information.name = notdir(string{pathname}.rtrim<1>("/"));
|
||||
string outputPath = openGameBoy(buffer);
|
||||
|
||||
if(save.size()) file::write({outputPath, "save.ram"}, save);
|
||||
if(rtc.size()) file::write({outputPath, "rtc.ram"}, rtc);
|
||||
|
||||
return outputPath;
|
||||
}
|
@@ -1,173 +0,0 @@
|
||||
#ifndef NALL_EMULATION_FAMICOM_HPP
|
||||
#define NALL_EMULATION_FAMICOM_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct FamicomCartridge {
|
||||
string markup;
|
||||
inline FamicomCartridge(const uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
unsigned mapper;
|
||||
unsigned mirror;
|
||||
unsigned prgrom;
|
||||
unsigned prgram;
|
||||
unsigned chrrom;
|
||||
unsigned chrram;
|
||||
};
|
||||
|
||||
FamicomCartridge::FamicomCartridge(const uint8_t *data, unsigned size) {
|
||||
markup = "";
|
||||
if(size < 16) return;
|
||||
if(data[0] != 'N') return;
|
||||
if(data[1] != 'E') return;
|
||||
if(data[2] != 'S') return;
|
||||
if(data[3] != 26) return;
|
||||
|
||||
mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
|
||||
mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
|
||||
prgrom = data[4] * 0x4000;
|
||||
chrrom = data[5] * 0x2000;
|
||||
prgram = 0u;
|
||||
chrram = chrrom == 0u ? 8192u : 0u;
|
||||
|
||||
markup.append("cartridge\n");
|
||||
|
||||
switch(mapper) {
|
||||
default:
|
||||
markup.append(" board type=NES-NROM-256\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 1:
|
||||
markup.append(" board type=NES-SXROM\n");
|
||||
markup.append(" chip type=MMC1B2\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
markup.append(" board type=NES-UOROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 3:
|
||||
markup.append(" board type=NES-CNROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 4:
|
||||
//MMC3
|
||||
markup.append(" board type=NES-TLROM\n");
|
||||
markup.append(" chip type=MMC3B\n");
|
||||
prgram = 8192;
|
||||
//MMC6
|
||||
//markup.append(" board type=NES-HKROM\n");
|
||||
//markup.append(" chip type=MMC6n");
|
||||
//prgram = 1024;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
markup.append(" board type=NES-ELROM\n");
|
||||
markup.append(" chip type=MMC5\n");
|
||||
prgram = 65536;
|
||||
break;
|
||||
|
||||
case 7:
|
||||
markup.append(" board type=NES-AOROM\n");
|
||||
break;
|
||||
|
||||
case 9:
|
||||
markup.append(" board type=NES-PNROM\n");
|
||||
markup.append(" chip type=MMC2\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 10:
|
||||
markup.append(" board type=NES-FKROM\n");
|
||||
markup.append(" chip type=MMC4\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
markup.append(" board type=BANDAI-FCG\n");
|
||||
markup.append(" chip type=LZ93D50\n");
|
||||
break;
|
||||
|
||||
case 21:
|
||||
case 23:
|
||||
case 25:
|
||||
//VRC4
|
||||
markup.append(" board type=KONAMI-VRC-4\n");
|
||||
markup.append(" chip type=VRC4\n");
|
||||
markup.append(" pinout a0=1 a1=0\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 22:
|
||||
//VRC2
|
||||
markup.append(" board type=KONAMI-VRC-2\n");
|
||||
markup.append(" chip type=VRC2\n");
|
||||
markup.append(" pinout a0=0 a1=1\n");
|
||||
break;
|
||||
|
||||
case 24:
|
||||
markup.append(" board type=KONAMI-VRC-6\n");
|
||||
markup.append(" chip type=VRC6\n");
|
||||
break;
|
||||
|
||||
case 26:
|
||||
markup.append(" board type=KONAMI-VRC-6\n");
|
||||
markup.append(" chip type=VRC6\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 34:
|
||||
markup.append(" board type=NES-BNROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 66:
|
||||
markup.append(" board type=NES-GNROM\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
break;
|
||||
|
||||
case 69:
|
||||
markup.append(" board type=SUNSOFT-5B\n");
|
||||
markup.append(" chip type=5B\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 73:
|
||||
markup.append(" board type=KONAMI-VRC-3\n");
|
||||
markup.append(" chip type=VRC3\n");
|
||||
markup.append(" mirror mode=", mirror == 0 ? "horizontal" : "vertical", "\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
|
||||
case 75:
|
||||
markup.append(" board type=KONAMI-VRC-1\n");
|
||||
markup.append(" chip type=VRC1\n");
|
||||
break;
|
||||
|
||||
case 85:
|
||||
markup.append(" board type=KONAMI-VRC-7\n");
|
||||
markup.append(" chip type=VRC7\n");
|
||||
prgram = 8192;
|
||||
break;
|
||||
}
|
||||
|
||||
markup.append(" prg\n");
|
||||
if(prgrom) markup.append(" rom name=program.rom size=0x", hex(prgrom), "\n");
|
||||
if(prgram) markup.append(" ram name=save.ram size=0x", hex(prgram), "\n");
|
||||
|
||||
markup.append(" chr\n");
|
||||
if(chrrom) markup.append(" rom name=character.rom size=0x", hex(chrrom), "\n");
|
||||
if(chrram) markup.append(" ram size=0x", hex(chrram), "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,63 +0,0 @@
|
||||
#ifndef NALL_EMULATION_GAME_BOY_ADVANCE_HPP
|
||||
#define NALL_EMULATION_GAME_BOY_ADVANCE_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct GameBoyAdvanceCartridge {
|
||||
string markup;
|
||||
string identifiers;
|
||||
inline GameBoyAdvanceCartridge(const uint8_t *data, unsigned size);
|
||||
};
|
||||
|
||||
GameBoyAdvanceCartridge::GameBoyAdvanceCartridge(const uint8_t *data, unsigned size) {
|
||||
struct Identifier {
|
||||
string name;
|
||||
unsigned size;
|
||||
};
|
||||
vector<Identifier> idlist;
|
||||
idlist.append({"SRAM_V", 6});
|
||||
idlist.append({"SRAM_F_V", 8});
|
||||
idlist.append({"EEPROM_V", 8});
|
||||
idlist.append({"FLASH_V", 7});
|
||||
idlist.append({"FLASH512_V", 10});
|
||||
idlist.append({"FLASH1M_V", 9});
|
||||
|
||||
lstring list;
|
||||
for(auto &id : idlist) {
|
||||
for(signed n = 0; n < size - 16; n++) {
|
||||
if(!memcmp(data + n, (const char*)id.name, id.size)) {
|
||||
const char *p = (const char*)data + n + id.size;
|
||||
if(p[0] >= '0' && p[0] <= '9'
|
||||
&& p[1] >= '0' && p[1] <= '9'
|
||||
&& p[2] >= '0' && p[2] <= '9'
|
||||
) {
|
||||
char text[16];
|
||||
memcpy(text, data + n, id.size + 3);
|
||||
text[id.size + 3] = 0;
|
||||
list.appendOnce(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
identifiers = list.merge(",");
|
||||
|
||||
markup = "";
|
||||
markup.append("cartridge\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(size), "\n");
|
||||
if(0);
|
||||
else if(identifiers.beginsWith("SRAM_V" )) markup.append(" ram name=save.ram type=SRAM size=0x8000\n");
|
||||
else if(identifiers.beginsWith("SRAM_F_V" )) markup.append(" ram name=save.ram type=FRAM size=0x8000\n");
|
||||
else if(identifiers.beginsWith("EEPROM_V" )) markup.append(" ram name=save.ram type=EEPROM size=0x0\n");
|
||||
else if(identifiers.beginsWith("FLASH_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
|
||||
else if(identifiers.beginsWith("FLASH512_V")) markup.append(" ram name=save.ram type=FlashROM size=0x10000\n");
|
||||
else if(identifiers.beginsWith("FLASH1M_V" )) markup.append(" ram name=save.ram type=FlashROM size=0x20000\n");
|
||||
//if(identifiers.empty() == false) markup.append(" #detected: ", identifiers, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,120 +0,0 @@
|
||||
#ifndef NALL_EMULATION_GAME_BOY_HPP
|
||||
#define NALL_EMULATION_GAME_BOY_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct GameBoyCartridge {
|
||||
string markup;
|
||||
inline GameBoyCartridge(uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
struct Information {
|
||||
string mapper;
|
||||
bool ram;
|
||||
bool battery;
|
||||
bool rtc;
|
||||
bool rumble;
|
||||
|
||||
unsigned romsize;
|
||||
unsigned ramsize;
|
||||
|
||||
bool cgb;
|
||||
bool cgbonly;
|
||||
} info;
|
||||
};
|
||||
|
||||
GameBoyCartridge::GameBoyCartridge(uint8_t *romdata, unsigned romsize) {
|
||||
markup = "";
|
||||
if(romsize < 0x4000) return;
|
||||
|
||||
info.mapper = "unknown";
|
||||
info.ram = false;
|
||||
info.battery = false;
|
||||
info.rtc = false;
|
||||
info.rumble = false;
|
||||
|
||||
info.romsize = 0;
|
||||
info.ramsize = 0;
|
||||
|
||||
unsigned base = romsize - 0x8000;
|
||||
if(romdata[base + 0x0104] == 0xce && romdata[base + 0x0105] == 0xed
|
||||
&& romdata[base + 0x0106] == 0x66 && romdata[base + 0x0107] == 0x66
|
||||
&& romdata[base + 0x0108] == 0xcc && romdata[base + 0x0109] == 0x0d
|
||||
&& romdata[base + 0x0147] >= 0x0b && romdata[base + 0x0147] <= 0x0d
|
||||
) {
|
||||
//MMM01 stores header at bottom of image
|
||||
//flip this around for consistency with all other mappers
|
||||
uint8_t header[0x8000];
|
||||
memcpy(header, romdata + base, 0x8000);
|
||||
memmove(romdata + 0x8000, romdata, romsize - 0x8000);
|
||||
memcpy(romdata, header, 0x8000);
|
||||
}
|
||||
|
||||
info.cgb = (romdata[0x0143] & 0x80) == 0x80;
|
||||
info.cgbonly = (romdata[0x0143] & 0xc0) == 0xc0;
|
||||
|
||||
switch(romdata[0x0147]) {
|
||||
case 0x00: info.mapper = "none"; break;
|
||||
case 0x01: info.mapper = "MBC1"; break;
|
||||
case 0x02: info.mapper = "MBC1"; info.ram = true; break;
|
||||
case 0x03: info.mapper = "MBC1"; info.ram = true; info.battery = true; break;
|
||||
case 0x05: info.mapper = "MBC2"; info.ram = true; break;
|
||||
case 0x06: info.mapper = "MBC2"; info.ram = true; info.battery = true; break;
|
||||
case 0x08: info.mapper = "none"; info.ram = true; break;
|
||||
case 0x09: info.mapper = "MBC0"; info.ram = true; info.battery = true; break;
|
||||
case 0x0b: info.mapper = "MMM01"; break;
|
||||
case 0x0c: info.mapper = "MMM01"; info.ram = true; break;
|
||||
case 0x0d: info.mapper = "MMM01"; info.ram = true; info.battery = true; break;
|
||||
case 0x0f: info.mapper = "MBC3"; info.rtc = true; info.battery = true; break;
|
||||
case 0x10: info.mapper = "MBC3"; info.rtc = true; info.ram = true; info.battery = true; break;
|
||||
case 0x11: info.mapper = "MBC3"; break;
|
||||
case 0x12: info.mapper = "MBC3"; info.ram = true; break;
|
||||
case 0x13: info.mapper = "MBC3"; info.ram = true; info.battery = true; break;
|
||||
case 0x19: info.mapper = "MBC5"; break;
|
||||
case 0x1a: info.mapper = "MBC5"; info.ram = true; break;
|
||||
case 0x1b: info.mapper = "MBC5"; info.ram = true; info.battery = true; break;
|
||||
case 0x1c: info.mapper = "MBC5"; info.rumble = true; break;
|
||||
case 0x1d: info.mapper = "MBC5"; info.rumble = true; info.ram = true; break;
|
||||
case 0x1e: info.mapper = "MBC5"; info.rumble = true; info.ram = true; info.battery = true; break;
|
||||
case 0xfc: break; //Pocket Camera
|
||||
case 0xfd: break; //Bandai TAMA5
|
||||
case 0xfe: info.mapper = "HuC3"; break;
|
||||
case 0xff: info.mapper = "HuC1"; info.ram = true; info.battery = true; break;
|
||||
}
|
||||
|
||||
switch(romdata[0x0148]) { default:
|
||||
case 0x00: info.romsize = 2 * 16 * 1024; break;
|
||||
case 0x01: info.romsize = 4 * 16 * 1024; break;
|
||||
case 0x02: info.romsize = 8 * 16 * 1024; break;
|
||||
case 0x03: info.romsize = 16 * 16 * 1024; break;
|
||||
case 0x04: info.romsize = 32 * 16 * 1024; break;
|
||||
case 0x05: info.romsize = 64 * 16 * 1024; break;
|
||||
case 0x06: info.romsize = 128 * 16 * 1024; break;
|
||||
case 0x07: info.romsize = 256 * 16 * 1024; break;
|
||||
case 0x52: info.romsize = 72 * 16 * 1024; break;
|
||||
case 0x53: info.romsize = 80 * 16 * 1024; break;
|
||||
case 0x54: info.romsize = 96 * 16 * 1024; break;
|
||||
}
|
||||
|
||||
switch(romdata[0x0149]) { default:
|
||||
case 0x00: info.ramsize = 0 * 1024; break;
|
||||
case 0x01: info.ramsize = 2 * 1024; break;
|
||||
case 0x02: info.ramsize = 8 * 1024; break;
|
||||
case 0x03: info.ramsize = 32 * 1024; break;
|
||||
}
|
||||
|
||||
if(info.mapper == "MBC2") info.ramsize = 512; //512 x 4-bit
|
||||
|
||||
markup = "";
|
||||
markup.append("cartridge\n");
|
||||
markup.append(" board type=", info.mapper, "\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(romsize), "\n");
|
||||
if(info.ramsize > 0) markup.append(" ram name=save.ram size=0x", hex(info.ramsize), "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,23 +0,0 @@
|
||||
#ifndef NALL_EMULATION_SATELLAVIEW_HPP
|
||||
#define NALL_EMULATION_SATELLAVIEW_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SatellaviewCartridge {
|
||||
string markup;
|
||||
inline SatellaviewCartridge(const uint8_t *data, unsigned size);
|
||||
};
|
||||
|
||||
SatellaviewCartridge::SatellaviewCartridge(const uint8_t *data, unsigned size) {
|
||||
markup = "";
|
||||
|
||||
markup.append("cartridge\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(size), " type=FlashROM\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,31 +0,0 @@
|
||||
#ifndef NALL_EMULATION_SUFAMI_TURBO_HPP
|
||||
#define NALL_EMULATION_SUFAMI_TURBO_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SufamiTurboCartridge {
|
||||
string markup;
|
||||
inline SufamiTurboCartridge(const uint8_t *data, unsigned size);
|
||||
};
|
||||
|
||||
SufamiTurboCartridge::SufamiTurboCartridge(const uint8_t *data, unsigned size) {
|
||||
markup = "";
|
||||
|
||||
if(size < 0x20000) return; //too small to be a valid game?
|
||||
if(memcmp(data, "BANDAI SFC-ADX", 14)) return; //missing required header?
|
||||
unsigned romsize = data[0x36] * 0x20000; //128KB
|
||||
unsigned ramsize = data[0x37] * 0x800; //2KB
|
||||
bool linkable = data[0x35] != 0x00; //TODO: unconfirmed
|
||||
|
||||
markup.append("cartridge", linkable ? " linkable" : "", "\n");
|
||||
markup.append(" rom name=program.rom size=0x", hex(romsize), "\n");
|
||||
if(ramsize)
|
||||
markup.append(" ram name=save.ram size=0x", hex(ramsize), "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@@ -1,814 +0,0 @@
|
||||
#ifndef NALL_EMULATION_SUPER_FAMICOM_HPP
|
||||
#define NALL_EMULATION_SUPER_FAMICOM_HPP
|
||||
|
||||
#include <nall/sha256.hpp>
|
||||
#include <nall/string.hpp>
|
||||
|
||||
namespace nall {
|
||||
|
||||
struct SuperFamicomCartridge {
|
||||
string markup;
|
||||
inline SuperFamicomCartridge(const uint8_t *data, unsigned size);
|
||||
|
||||
//private:
|
||||
inline void read_header(const uint8_t *data, unsigned size);
|
||||
inline unsigned find_header(const uint8_t *data, unsigned size);
|
||||
inline unsigned score_header(const uint8_t *data, unsigned size, unsigned addr);
|
||||
|
||||
enum HeaderField {
|
||||
CartName = 0x00,
|
||||
Mapper = 0x15,
|
||||
RomType = 0x16,
|
||||
RomSize = 0x17,
|
||||
RamSize = 0x18,
|
||||
CartRegion = 0x19,
|
||||
Company = 0x1a,
|
||||
Version = 0x1b,
|
||||
Complement = 0x1c, //inverse checksum
|
||||
Checksum = 0x1e,
|
||||
ResetVector = 0x3c,
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
ModeNormal,
|
||||
ModeBsxSlotted,
|
||||
ModeBsx,
|
||||
ModeSufamiTurbo,
|
||||
ModeSuperGameBoy,
|
||||
};
|
||||
|
||||
enum Type {
|
||||
TypeNormal,
|
||||
TypeBsxSlotted,
|
||||
TypeBsxBios,
|
||||
TypeBsx,
|
||||
TypeSufamiTurboBios,
|
||||
TypeSufamiTurbo,
|
||||
TypeSuperGameBoy1Bios,
|
||||
TypeSuperGameBoy2Bios,
|
||||
TypeGameBoy,
|
||||
TypeUnknown,
|
||||
};
|
||||
|
||||
enum Region {
|
||||
NTSC,
|
||||
PAL,
|
||||
};
|
||||
|
||||
enum MemoryMapper {
|
||||
LoROM,
|
||||
HiROM,
|
||||
ExLoROM,
|
||||
ExHiROM,
|
||||
SuperFXROM,
|
||||
SA1ROM,
|
||||
SPC7110ROM,
|
||||
BSCLoROM,
|
||||
BSCHiROM,
|
||||
BSXROM,
|
||||
STROM,
|
||||
};
|
||||
|
||||
enum DSP1MemoryMapper {
|
||||
DSP1Unmapped,
|
||||
DSP1LoROM1MB,
|
||||
DSP1LoROM2MB,
|
||||
DSP1HiROM,
|
||||
};
|
||||
|
||||
bool loaded; //is a base cartridge inserted?
|
||||
unsigned crc32; //crc32 of all cartridges (base+slot(s))
|
||||
unsigned rom_size;
|
||||
unsigned ram_size;
|
||||
bool firmware_appended; //true if firmware is appended to end of ROM data
|
||||
|
||||
Mode mode;
|
||||
Type type;
|
||||
Region region;
|
||||
MemoryMapper mapper;
|
||||
DSP1MemoryMapper dsp1_mapper;
|
||||
|
||||
bool has_bsx_slot;
|
||||
bool has_superfx;
|
||||
bool has_sa1;
|
||||
bool has_sharprtc;
|
||||
bool has_epsonrtc;
|
||||
bool has_sdd1;
|
||||
bool has_spc7110;
|
||||
bool has_cx4;
|
||||
bool has_dsp1;
|
||||
bool has_dsp2;
|
||||
bool has_dsp3;
|
||||
bool has_dsp4;
|
||||
bool has_obc1;
|
||||
bool has_st010;
|
||||
bool has_st011;
|
||||
bool has_st018;
|
||||
};
|
||||
|
||||
SuperFamicomCartridge::SuperFamicomCartridge(const uint8_t *data, unsigned size) {
|
||||
firmware_appended = false;
|
||||
|
||||
//skip copier header
|
||||
if((size & 0x7fff) == 512) data += 512, size -= 512;
|
||||
|
||||
markup = "";
|
||||
if(size < 0x8000) return;
|
||||
|
||||
read_header(data, size);
|
||||
|
||||
markup = "";
|
||||
if(type == TypeGameBoy) return;
|
||||
if(type == TypeBsx) return;
|
||||
if(type == TypeSufamiTurbo) return;
|
||||
|
||||
const char *range = (rom_size > 0x200000) || (ram_size > 32 * 1024) ? "0000-7fff" : "0000-ffff";
|
||||
markup.append("cartridge region=", region == NTSC ? "NTSC" : "PAL", "\n");
|
||||
|
||||
if(type == TypeSuperGameBoy1Bios || type == TypeSuperGameBoy2Bios) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" map id=rom address=00-7f,80-ff:8000-ffff mask=0x8000\n"
|
||||
" icd2 revision=1\n"
|
||||
" rom name=sgb.boot.rom size=0x100\n"
|
||||
" map id=io address=00-3f,80-bf:6000-7fff\n"
|
||||
);
|
||||
if((rom_size & 0x7fff) == 0x100) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x100;
|
||||
}
|
||||
}
|
||||
|
||||
else if(has_cx4) {
|
||||
markup.append(
|
||||
" hitachidsp model=HG51B169 frequency=20000000\n"
|
||||
" rom id=program name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" rom id=data name=cx4.data.rom size=0xc00\n"
|
||||
" ram id=data size=0xc00\n"
|
||||
" map id=io address=00-3f,80-bf:6000-7fff\n"
|
||||
" map id=rom address=00-7f,80-ff:8000-ffff mask=0x8000\n"
|
||||
" map id=ram address=70-77:0000-7fff\n"
|
||||
);
|
||||
if((rom_size & 0x7fff) == 0xc00) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0xc00;
|
||||
}
|
||||
}
|
||||
|
||||
else if(has_spc7110) {
|
||||
markup.append(
|
||||
" spc7110\n"
|
||||
" rom id=program name=program.rom size=0x100000\n"
|
||||
" rom id=data name=data.rom size=0x", hex(rom_size - 0x100000), "\n"
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" map id=io address=00-3f,80-bf:4800-483f\n"
|
||||
" map id=io address=50:0000-ffff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=c0-ff:0000-ffff\n"
|
||||
" map id=ram address=00-3f,80-bf:6000-7fff mask=0xe000\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(has_sdd1) {
|
||||
markup.append(
|
||||
" sdd1\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=io address=00-3f,80-bf:4800-4807\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=rom address=c0-ff:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff mask=0xe000\n"
|
||||
" map id=ram address=70-7f:0000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == LoROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-7f,80-ff:8000-ffff mask=0x8000\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=70-7f,f0-ff:", range, "\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == HiROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=40-7f,c0-ff:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=10-3f,90-bf:6000-7fff mask=0xe000\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == ExLoROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=rom address=40-7f:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff\n"
|
||||
" map id=ram address=70-7f:0000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == ExHiROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=rom address=00-3f:8000-ffff base=0x400000\n"
|
||||
" map id=rom address=40-7f:0000-ffff base=0x400000\n"
|
||||
" map id=rom address=80-bf:8000-ffff mask=0xc00000\n"
|
||||
" map id=rom address=c0-ff:0000-ffff mask=0xc00000\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff mask=0xe000\n"
|
||||
" map id=ram address=70-7f:", range, "\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == SuperFXROM) {
|
||||
markup.append(
|
||||
" superfx revision=3\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=io address=00-3f,80-bf:3000-32ff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=rom address=40-5f,c0-df:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=ram address=00-3f,80-bf:6000-7fff size=0x2000\n"
|
||||
" map id=ram address=70-71,f0-f1:0000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == SA1ROM) {
|
||||
markup.append(
|
||||
" sa1\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" ram id=bitmap name=save.ram size=0x", hex(ram_size), "\n"
|
||||
);
|
||||
markup.append(
|
||||
" ram id=internal size=0x800\n"
|
||||
" map id=io address=00-3f,80-bf:2200-23ff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=c0-ff:0000-ffff\n"
|
||||
);
|
||||
if(ram_size > 0) markup.append(
|
||||
" map id=bwram address=00-3f,80-bf:6000-7fff\n"
|
||||
" map id=bwram address=40-4f:0000-ffff\n"
|
||||
);
|
||||
markup.append(
|
||||
" map id=iram address=00-3f,80-bf:3000-37ff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == BSCLoROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" map id=rom address=00-1f:8000-ffff base=0x000000 mask=0x8000\n"
|
||||
" map id=rom address=20-3f:8000-ffff base=0x100000 mask=0x8000\n"
|
||||
" map id=rom address=80-9f:8000-ffff base=0x200000 mask=0x8000\n"
|
||||
" map id=rom address=a0-bf:8000-ffff base=0x100000 mask=0x8000\n"
|
||||
" map id=ram address=70-7f,f0-ff:0000-7fff\n"
|
||||
" bsxslot\n"
|
||||
" map id=rom address=c0-ef:0000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == BSCHiROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" ram name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" map id=rom address=00-1f,80-9f:8000-ffff\n"
|
||||
" map id=rom address=40-5f,c0-df:0000-ffff\n"
|
||||
" map id=ram address=20-3f,a0-bf:6000-7fff\n"
|
||||
" bsxslot\n"
|
||||
" map id=rom address=20-3f,a0-bf:8000-ffff\n"
|
||||
" map id=rom address=60-7f,e0-ff:0000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == BSXROM) {
|
||||
markup.append(
|
||||
" bsx\n"
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" ram id=save name=save.ram size=0x", hex(ram_size), "\n"
|
||||
" ram id=download name=bsx.ram size=0x40000\n"
|
||||
" map id=io address=00-3f,80-bf:5000-5fff\n"
|
||||
" map id=rom address=00-3f,80-bf:8000-ffff\n"
|
||||
" map id=rom address=40-7f,c0-ff:0000-ffff\n"
|
||||
" map id=ram address=20-3f:6000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
else if(mapper == STROM) {
|
||||
markup.append(
|
||||
" rom name=program.rom size=0x", hex(rom_size), "\n"
|
||||
" map id=rom address='00-1f,80-9f:8000-ffff mask=0x8000\n"
|
||||
" sufamiturbo\n"
|
||||
" slot id=A\n"
|
||||
" map id=rom address=20-3f,a0-bf:8000-ffff mask=0x8000\n"
|
||||
" map id=ram address=60-63,e0-e3:8000-ffff\n"
|
||||
" slot id=B\n"
|
||||
" map id=rom address=40-5f,c0-df:8000-ffff mask=0x8000\n"
|
||||
" map id=ram address=70-73,f0-f3:8000-ffff\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_sharprtc) {
|
||||
markup.append(
|
||||
" sharprtc\n"
|
||||
" ram name=rtc.ram size=0x10\n"
|
||||
" map id=io address=00-3f,80-bf:2800-2801\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_epsonrtc) {
|
||||
markup.append(
|
||||
" epsonrtc\n"
|
||||
" ram name=rtc.ram size=0x10\n"
|
||||
" map id=io address=00-3f,80-bf:4840-4842\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_obc1) {
|
||||
markup.append(
|
||||
" obc1\n"
|
||||
" ram name=save.ram size=0x2000\n"
|
||||
" map id=io address=00-3f,80-bf:6000-7fff\n"
|
||||
);
|
||||
}
|
||||
|
||||
if(has_dsp1) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp1b.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp1b.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
);
|
||||
if(dsp1_mapper == DSP1LoROM1MB) markup.append(
|
||||
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if(dsp1_mapper == DSP1LoROM2MB) markup.append(
|
||||
" map id=io address=60-6f,e0-ef:0000-7fff select=0x4000\n"
|
||||
);
|
||||
if(dsp1_mapper == DSP1HiROM) markup.append(
|
||||
" map id=io address=00-1f,80-9f:6000-7fff select=0x1000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_dsp2) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp2.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp2.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_dsp3) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp3.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp3.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
" map id=io address=20-3f,a0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_dsp4) {
|
||||
markup.append(
|
||||
" necdsp model=uPD7725 frequency=8000000\n"
|
||||
" rom id=program name=dsp4.program.rom size=0x1800\n"
|
||||
" rom id=data name=dsp4.data.rom size=0x800\n"
|
||||
" ram id=data size=0x200\n"
|
||||
" map id=io address=30-3f,b0-bf:8000-ffff select=0x4000\n"
|
||||
);
|
||||
if((size & 0x7fff) == 0x2000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x2000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_st010) {
|
||||
markup.append(
|
||||
" necdsp model=uPD96050 frequency=11000000\n"
|
||||
" rom id=program name=st010.program.rom size=0xc000\n"
|
||||
" rom id=data name=st010.data.rom size=0x1000\n"
|
||||
" ram id=data name=save.ram size=0x1000\n"
|
||||
" map id=io address=60-67,e0-e7:0000-3fff select=0x0001\n"
|
||||
" map id=ram address=68-6f,e8-ef:0000-7fff\n"
|
||||
);
|
||||
if((size & 0xffff) == 0xd000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0xd000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_st011) {
|
||||
markup.append(
|
||||
" necdsp model=uPD96050 frequency=15000000\n"
|
||||
" rom id=program name=st011.program.rom size=0xc000\n"
|
||||
" rom id=data name=st011.data.rom size=0x1000\n"
|
||||
" ram id=data name=save.ram size=0x1000\n"
|
||||
" map id=io address=60-67,e0-e7:0000-3fff select=0x0001\n"
|
||||
" map id=ram address=68-6f,e8-ef:0000-7fff\n"
|
||||
);
|
||||
if((size & 0xffff) == 0xd000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0xd000;
|
||||
}
|
||||
}
|
||||
|
||||
if(has_st018) {
|
||||
markup.append(
|
||||
" armdsp frequency=21477272\n"
|
||||
" rom id=program name=st018.program.rom size=0x20000\n"
|
||||
" rom id=data name=st018.data.rom size=0x8000\n"
|
||||
" ram name=save.ram size=0x4000\n"
|
||||
" map id=io address=00-3f,80-bf:3800-38ff\n"
|
||||
);
|
||||
if((size & 0x3ffff) == 0x28000) {
|
||||
firmware_appended = true;
|
||||
rom_size -= 0x28000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SuperFamicomCartridge::read_header(const uint8_t *data, unsigned size) {
|
||||
type = TypeUnknown;
|
||||
mapper = LoROM;
|
||||
dsp1_mapper = DSP1Unmapped;
|
||||
region = NTSC;
|
||||
rom_size = size;
|
||||
ram_size = 0;
|
||||
|
||||
has_bsx_slot = false;
|
||||
has_superfx = false;
|
||||
has_sa1 = false;
|
||||
has_sharprtc = false;
|
||||
has_epsonrtc = false;
|
||||
has_sdd1 = false;
|
||||
has_spc7110 = false;
|
||||
has_cx4 = false;
|
||||
has_dsp1 = false;
|
||||
has_dsp2 = false;
|
||||
has_dsp3 = false;
|
||||
has_dsp4 = false;
|
||||
has_obc1 = false;
|
||||
has_st010 = false;
|
||||
has_st011 = false;
|
||||
has_st018 = false;
|
||||
|
||||
//=====================
|
||||
//detect Game Boy carts
|
||||
//=====================
|
||||
|
||||
if(size >= 0x0140) {
|
||||
if(data[0x0104] == 0xce && data[0x0105] == 0xed && data[0x0106] == 0x66 && data[0x0107] == 0x66
|
||||
&& data[0x0108] == 0xcc && data[0x0109] == 0x0d && data[0x010a] == 0x00 && data[0x010b] == 0x0b) {
|
||||
type = TypeGameBoy;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(size < 32768) {
|
||||
type = TypeUnknown;
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned index = find_header(data, size);
|
||||
const uint8_t mapperid = data[index + Mapper];
|
||||
const uint8_t rom_type = data[index + RomType];
|
||||
const uint8_t rom_size = data[index + RomSize];
|
||||
const uint8_t company = data[index + Company];
|
||||
const uint8_t regionid = data[index + CartRegion] & 0x7f;
|
||||
|
||||
ram_size = 1024 << (data[index + RamSize] & 7);
|
||||
if(ram_size == 1024) ram_size = 0; //no RAM present
|
||||
if(rom_size == 0 && ram_size) ram_size = 0; //fix for Bazooka Blitzkrieg's malformed header (swapped ROM and RAM sizes)
|
||||
|
||||
//0, 1, 13 = NTSC; 2 - 12 = PAL
|
||||
region = (regionid <= 1 || regionid >= 13) ? NTSC : PAL;
|
||||
|
||||
//=======================
|
||||
//detect BS-X flash carts
|
||||
//=======================
|
||||
|
||||
if(data[index + 0x13] == 0x00 || data[index + 0x13] == 0xff) {
|
||||
if(data[index + 0x14] == 0x00) {
|
||||
const uint8_t n15 = data[index + 0x15];
|
||||
if(n15 == 0x00 || n15 == 0x80 || n15 == 0x84 || n15 == 0x9c || n15 == 0xbc || n15 == 0xfc) {
|
||||
if(data[index + 0x1a] == 0x33 || data[index + 0x1a] == 0xff) {
|
||||
type = TypeBsx;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================
|
||||
//detect Sufami Turbo carts
|
||||
//=========================
|
||||
|
||||
if(!memcmp(data, "BANDAI SFC-ADX", 14)) {
|
||||
if(!memcmp(data + 16, "SFC-ADX BACKUP", 14)) {
|
||||
type = TypeSufamiTurboBios;
|
||||
} else {
|
||||
type = TypeSufamiTurbo;
|
||||
}
|
||||
mapper = STROM;
|
||||
region = NTSC; //Sufami Turbo only released in Japan
|
||||
return; //RAM size handled outside this routine
|
||||
}
|
||||
|
||||
//==========================
|
||||
//detect Super Game Boy BIOS
|
||||
//==========================
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY2", 14)) {
|
||||
type = TypeSuperGameBoy2Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!memcmp(data + index, "Super GAMEBOY", 13)) {
|
||||
type = TypeSuperGameBoy1Bios;
|
||||
return;
|
||||
}
|
||||
|
||||
//=====================
|
||||
//detect standard carts
|
||||
//=====================
|
||||
|
||||
//detect presence of BS-X flash cartridge connector (reads extended header information)
|
||||
if(data[index - 14] == 'Z') {
|
||||
if(data[index - 11] == 'J') {
|
||||
uint8_t n13 = data[index - 13];
|
||||
if((n13 >= 'A' && n13 <= 'Z') || (n13 >= '0' && n13 <= '9')) {
|
||||
if(company == 0x33 || (data[index - 10] == 0x00 && data[index - 4] == 0x00)) {
|
||||
has_bsx_slot = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(has_bsx_slot) {
|
||||
if(!memcmp(data + index, "Satellaview BS-X ", 21)) {
|
||||
//BS-X base cart
|
||||
type = TypeBsxBios;
|
||||
mapper = BSXROM;
|
||||
region = NTSC; //BS-X only released in Japan
|
||||
return; //RAM size handled internally by load_cart_bsx() -> BSXCart class
|
||||
} else {
|
||||
type = TypeBsxSlotted;
|
||||
mapper = (index == 0x7fc0 ? BSCLoROM : BSCHiROM);
|
||||
region = NTSC; //BS-X slotted cartridges only released in Japan
|
||||
}
|
||||
} else {
|
||||
//standard cart
|
||||
type = TypeNormal;
|
||||
|
||||
if(index == 0x7fc0 && size >= 0x401000) {
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0 && mapperid == 0x32) {
|
||||
mapper = ExLoROM;
|
||||
} else if(index == 0x7fc0) {
|
||||
mapper = LoROM;
|
||||
} else if(index == 0xffc0) {
|
||||
mapper = HiROM;
|
||||
} else { //index == 0x40ffc0
|
||||
mapper = ExHiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && (rom_type == 0x13 || rom_type == 0x14 || rom_type == 0x15 || rom_type == 0x1a)) {
|
||||
has_superfx = true;
|
||||
mapper = SuperFXROM;
|
||||
ram_size = 1024 << (data[index - 3] & 7);
|
||||
if(ram_size == 1024) ram_size = 0;
|
||||
}
|
||||
|
||||
if(mapperid == 0x23 && (rom_type == 0x32 || rom_type == 0x34 || rom_type == 0x35)) {
|
||||
has_sa1 = true;
|
||||
mapper = SA1ROM;
|
||||
}
|
||||
|
||||
if(mapperid == 0x35 && rom_type == 0x55) {
|
||||
has_sharprtc = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x32 && (rom_type == 0x43 || rom_type == 0x45)) {
|
||||
has_sdd1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x3a && (rom_type == 0xf5 || rom_type == 0xf9)) {
|
||||
has_spc7110 = true;
|
||||
has_epsonrtc = (rom_type == 0xf9);
|
||||
mapper = SPC7110ROM;
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && rom_type == 0xf3) {
|
||||
has_cx4 = true;
|
||||
}
|
||||
|
||||
if((mapperid == 0x20 || mapperid == 0x21) && rom_type == 0x03) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company != 0xb2) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x31 && (rom_type == 0x03 || rom_type == 0x05)) {
|
||||
has_dsp1 = true;
|
||||
}
|
||||
|
||||
if(has_dsp1 == true) {
|
||||
if((mapperid & 0x2f) == 0x20 && size <= 0x100000) {
|
||||
dsp1_mapper = DSP1LoROM1MB;
|
||||
} else if((mapperid & 0x2f) == 0x20) {
|
||||
dsp1_mapper = DSP1LoROM2MB;
|
||||
} else if((mapperid & 0x2f) == 0x21) {
|
||||
dsp1_mapper = DSP1HiROM;
|
||||
}
|
||||
}
|
||||
|
||||
if(mapperid == 0x20 && rom_type == 0x05) {
|
||||
has_dsp2 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x05 && company == 0xb2) {
|
||||
has_dsp3 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x03) {
|
||||
has_dsp4 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0x25) {
|
||||
has_obc1 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size >= 10) {
|
||||
has_st010 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf6 && rom_size < 10) {
|
||||
has_st011 = true;
|
||||
}
|
||||
|
||||
if(mapperid == 0x30 && rom_type == 0xf5) {
|
||||
has_st018 = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SuperFamicomCartridge::find_header(const uint8_t *data, unsigned size) {
|
||||
unsigned score_lo = score_header(data, size, 0x007fc0);
|
||||
unsigned score_hi = score_header(data, size, 0x00ffc0);
|
||||
unsigned score_ex = score_header(data, size, 0x40ffc0);
|
||||
if(score_ex) score_ex += 4; //favor ExHiROM on images > 32mbits
|
||||
|
||||
if(score_lo >= score_hi && score_lo >= score_ex) {
|
||||
return 0x007fc0;
|
||||
} else if(score_hi >= score_ex) {
|
||||
return 0x00ffc0;
|
||||
} else {
|
||||
return 0x40ffc0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SuperFamicomCartridge::score_header(const uint8_t *data, unsigned size, unsigned addr) {
|
||||
if(size < addr + 64) return 0; //image too small to contain header at this location?
|
||||
int score = 0;
|
||||
|
||||
uint16_t resetvector = data[addr + ResetVector] | (data[addr + ResetVector + 1] << 8);
|
||||
uint16_t checksum = data[addr + Checksum ] | (data[addr + Checksum + 1] << 8);
|
||||
uint16_t complement = data[addr + Complement ] | (data[addr + Complement + 1] << 8);
|
||||
|
||||
uint8_t resetop = data[(addr & ~0x7fff) | (resetvector & 0x7fff)]; //first opcode executed upon reset
|
||||
uint8_t mapper = data[addr + Mapper] & ~0x10; //mask off irrelevent FastROM-capable bit
|
||||
|
||||
//$00:[000-7fff] contains uninitialized RAM and MMIO.
|
||||
//reset vector must point to ROM at $00:[8000-ffff] to be considered valid.
|
||||
if(resetvector < 0x8000) return 0;
|
||||
|
||||
//some images duplicate the header in multiple locations, and others have completely
|
||||
//invalid header information that cannot be relied upon.
|
||||
//below code will analyze the first opcode executed at the specified reset vector to
|
||||
//determine the probability that this is the correct header.
|
||||
|
||||
//most likely opcodes
|
||||
if(resetop == 0x78 //sei
|
||||
|| resetop == 0x18 //clc (clc; xce)
|
||||
|| resetop == 0x38 //sec (sec; xce)
|
||||
|| resetop == 0x9c //stz $nnnn (stz $4200)
|
||||
|| resetop == 0x4c //jmp $nnnn
|
||||
|| resetop == 0x5c //jml $nnnnnn
|
||||
) score += 8;
|
||||
|
||||
//plausible opcodes
|
||||
if(resetop == 0xc2 //rep #$nn
|
||||
|| resetop == 0xe2 //sep #$nn
|
||||
|| resetop == 0xad //lda $nnnn
|
||||
|| resetop == 0xae //ldx $nnnn
|
||||
|| resetop == 0xac //ldy $nnnn
|
||||
|| resetop == 0xaf //lda $nnnnnn
|
||||
|| resetop == 0xa9 //lda #$nn
|
||||
|| resetop == 0xa2 //ldx #$nn
|
||||
|| resetop == 0xa0 //ldy #$nn
|
||||
|| resetop == 0x20 //jsr $nnnn
|
||||
|| resetop == 0x22 //jsl $nnnnnn
|
||||
) score += 4;
|
||||
|
||||
//implausible opcodes
|
||||
if(resetop == 0x40 //rti
|
||||
|| resetop == 0x60 //rts
|
||||
|| resetop == 0x6b //rtl
|
||||
|| resetop == 0xcd //cmp $nnnn
|
||||
|| resetop == 0xec //cpx $nnnn
|
||||
|| resetop == 0xcc //cpy $nnnn
|
||||
) score -= 4;
|
||||
|
||||
//least likely opcodes
|
||||
if(resetop == 0x00 //brk #$nn
|
||||
|| resetop == 0x02 //cop #$nn
|
||||
|| resetop == 0xdb //stp
|
||||
|| resetop == 0x42 //wdm
|
||||
|| resetop == 0xff //sbc $nnnnnn,x
|
||||
) score -= 8;
|
||||
|
||||
//at times, both the header and reset vector's first opcode will match ...
|
||||
//fallback and rely on info validity in these cases to determine more likely header.
|
||||
|
||||
//a valid checksum is the biggest indicator of a valid header.
|
||||
if((checksum + complement) == 0xffff && (checksum != 0) && (complement != 0)) score += 4;
|
||||
|
||||
if(addr == 0x007fc0 && mapper == 0x20) score += 2; //0x20 is usually LoROM
|
||||
if(addr == 0x00ffc0 && mapper == 0x21) score += 2; //0x21 is usually HiROM
|
||||
if(addr == 0x007fc0 && mapper == 0x22) score += 2; //0x22 is usually ExLoROM
|
||||
if(addr == 0x40ffc0 && mapper == 0x25) score += 2; //0x25 is usually ExHiROM
|
||||
|
||||
if(data[addr + Company] == 0x33) score += 2; //0x33 indicates extended header
|
||||
if(data[addr + RomType] < 0x08) score++;
|
||||
if(data[addr + RomSize] < 0x10) score++;
|
||||
if(data[addr + RamSize] < 0x08) score++;
|
||||
if(data[addr + CartRegion] < 14) score++;
|
||||
|
||||
if(score < 0) score = 0;
|
||||
return score;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
1
ananke/obj/.gitignore
vendored
1
ananke/obj/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
*.o
|
@@ -1,15 +0,0 @@
|
||||
void Ananke::applyBeatPatch(vector<uint8_t> &buffer) {
|
||||
string name = {information.path, nall::basename(information.name), ".bps"};
|
||||
if(!file::exists(name)) return;
|
||||
|
||||
bpspatch patch;
|
||||
if(patch.modify(name) == false) return;
|
||||
patch.source(buffer.data(), buffer.size());
|
||||
vector<uint8_t> output;
|
||||
output.resize(patch.size());
|
||||
patch.target(output.data(), output.size());
|
||||
if(patch.apply() == bpspatch::result::success) {
|
||||
buffer = output;
|
||||
information.manifest = patch.metadata();
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
Before Width: | Height: | Size: 844 B |
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB |
@@ -1,6 +0,0 @@
|
||||
resource name=resource
|
||||
binary id=home name=home.png
|
||||
binary id=up name=up.png
|
||||
binary id=folder name=folder.png
|
||||
binary id=file name=file.png
|
||||
binary id=archive name=archive.png
|
@@ -1,156 +0,0 @@
|
||||
namespace resource {
|
||||
|
||||
const uint8_t home[606] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
|
||||
97,0,0,0,6,98,75,71,68,0,0,0,0,0,0,249,67,187,127,0,0,0,9,112,72,89,115,0,0,13,215,0,
|
||||
0,13,215,1,66,40,155,120,0,0,0,7,116,73,77,69,7,213,10,14,20,37,19,83,42,210,59,0,0,1,235,73,
|
||||
68,65,84,56,203,149,147,191,107,83,81,20,128,191,123,251,222,75,211,64,242,36,160,85,135,100,81,123,19,104,85,172,
|
||||
17,92,28,140,66,19,167,135,212,74,39,145,162,163,24,92,58,180,110,193,169,254,24,234,212,37,139,245,199,96,19,240,
|
||||
15,240,63,16,121,91,92,196,90,219,240,98,81,137,33,121,215,33,246,217,151,80,33,103,186,92,206,247,221,115,14,231,
|
||||
194,1,225,41,85,172,128,174,128,246,148,122,200,48,177,7,123,74,105,79,169,61,73,113,104,120,177,56,163,203,55,111,
|
||||
252,87,34,250,225,154,235,110,20,148,98,253,220,89,182,39,78,113,228,240,56,39,188,38,83,107,107,212,92,151,130,82,
|
||||
215,14,185,110,117,64,176,31,126,83,156,97,247,248,49,148,202,50,22,141,210,106,181,24,219,252,74,182,92,30,144,136,
|
||||
126,184,122,251,22,157,100,146,116,42,77,42,149,2,64,107,104,183,219,236,214,235,156,44,149,66,18,177,31,126,247,160,
|
||||
68,199,48,201,229,46,96,219,118,208,154,214,26,0,223,247,249,185,181,69,114,110,46,144,136,10,232,130,82,188,127,84,
|
||||
198,107,54,57,63,157,35,30,143,35,132,8,9,124,223,15,206,134,16,140,230,243,212,92,23,3,184,3,172,78,157,62,
|
||||
195,253,210,61,54,170,111,3,240,201,202,51,86,30,63,69,139,17,116,167,133,48,70,209,221,223,44,47,45,209,238,165,
|
||||
204,202,121,120,190,48,153,33,17,79,0,160,212,4,153,140,34,155,205,244,94,20,146,145,244,149,222,196,83,121,144,22,
|
||||
90,107,22,38,51,204,195,186,236,239,211,178,44,76,211,196,48,140,224,254,75,227,7,0,31,234,59,116,187,126,144,11,
|
||||
48,32,144,82,34,165,196,146,159,1,232,96,241,241,83,3,128,111,222,47,58,34,18,90,36,99,96,179,254,14,207,231,
|
||||
40,0,151,46,78,115,53,26,67,234,113,46,75,147,237,77,66,21,24,253,21,252,155,126,239,165,239,222,14,162,217,56,
|
||||
112,245,3,65,36,18,193,182,109,18,137,4,2,137,16,16,139,197,112,28,103,0,178,44,43,252,23,174,207,58,175,0,
|
||||
135,225,98,245,229,139,215,119,255,0,86,248,213,163,133,187,128,26,0,0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
|
||||
const uint8_t up[652] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,16,0,0,0,16,8,6,0,0,0,31,243,255,
|
||||
97,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,
|
||||
101,0,119,119,119,46,105,110,107,115,99,97,112,101,46,111,114,103,155,238,60,26,0,0,2,30,73,68,65,84,56,141,
|
||||
149,147,79,104,19,65,20,198,191,55,187,51,217,141,133,122,104,76,255,209,130,4,237,193,64,42,168,208,213,138,104,68,
|
||||
98,69,45,20,114,107,74,201,73,144,98,22,193,171,199,82,145,160,23,15,69,79,30,165,66,241,226,77,42,94,68,80,
|
||||
208,67,41,180,42,149,148,52,137,166,154,38,217,217,25,15,81,172,33,169,246,29,223,251,230,199,124,223,155,33,173,53,
|
||||
218,213,201,155,124,30,0,150,102,189,233,118,26,214,110,224,184,60,221,223,21,73,246,117,29,76,58,46,79,239,9,224,
|
||||
184,60,26,16,251,178,87,206,76,6,199,70,147,193,128,8,102,29,151,71,255,11,224,184,188,3,132,197,137,115,211,214,
|
||||
150,151,199,150,183,137,248,200,37,11,132,69,199,229,29,255,4,16,225,209,232,112,162,167,55,52,72,159,75,31,240,177,
|
||||
244,30,157,157,157,20,27,58,209,13,194,195,93,1,142,203,211,253,7,34,137,179,71,199,249,74,254,13,12,198,97,50,
|
||||
142,87,107,11,136,13,29,23,161,253,61,137,230,60,216,142,195,81,193,237,108,42,225,218,235,229,101,104,242,32,132,137,
|
||||
58,125,199,54,21,241,46,255,28,23,78,95,13,154,166,248,43,15,182,211,247,212,88,198,82,228,161,34,75,16,1,1,
|
||||
30,224,40,203,117,8,219,64,65,174,162,168,86,113,254,212,69,139,216,159,60,216,111,223,241,99,227,221,135,250,98,148,
|
||||
175,172,193,228,6,56,55,80,81,5,84,141,175,16,54,131,176,25,150,191,189,64,184,55,68,71,14,15,135,137,53,242,
|
||||
160,145,140,153,30,232,142,100,111,76,220,177,5,15,64,65,194,135,135,215,185,5,188,45,60,131,100,21,16,35,36,6,
|
||||
50,208,90,67,41,160,94,175,225,241,211,249,74,177,180,57,99,18,67,234,83,110,197,158,185,119,185,225,137,163,122,247,
|
||||
250,19,171,44,115,240,141,109,112,193,192,76,130,193,9,115,15,110,87,101,77,91,191,236,7,137,33,101,46,205,122,78,
|
||||
211,38,180,214,10,165,250,23,48,131,96,112,6,30,104,64,100,77,91,47,231,60,218,245,29,0,128,210,62,126,212,139,
|
||||
141,43,251,26,178,174,160,85,235,63,99,182,106,250,74,162,90,219,134,50,53,160,21,124,73,240,189,61,0,164,47,17,
|
||||
31,188,6,98,0,49,2,17,246,0,32,108,220,186,63,25,110,37,38,194,70,115,239,39,48,247,197,219,182,208,154,34,
|
||||
0,0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
|
||||
const uint8_t folder[1176] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,0,9,112,72,89,115,0,0,13,215,0,0,13,
|
||||
215,1,66,40,155,120,0,0,0,25,116,69,88,116,83,111,102,116,119,97,114,101,0,119,119,119,46,105,110,107,115,99,
|
||||
97,112,101,46,111,114,103,155,238,60,26,0,0,4,21,73,68,65,84,88,133,237,151,61,111,28,69,24,199,127,51,187,
|
||||
119,123,175,246,57,145,48,9,5,86,148,194,31,0,9,5,33,83,110,149,130,26,137,2,90,58,62,64,36,62,64,74,
|
||||
132,68,133,104,161,67,72,167,64,23,201,138,2,138,210,69,194,198,196,40,22,198,247,186,119,222,151,153,157,25,138,123,
|
||||
201,58,119,142,19,114,82,40,248,75,143,118,118,118,247,121,126,243,60,51,179,187,34,12,67,94,167,228,107,141,254,95,
|
||||
0,240,1,110,220,184,225,43,165,126,54,198,188,119,209,3,66,136,220,90,251,249,195,135,15,191,92,25,64,154,166,91,
|
||||
149,74,229,157,155,55,111,250,73,146,224,156,3,192,90,11,48,63,159,182,253,59,119,238,220,222,222,222,254,254,209,163,
|
||||
71,199,43,1,80,74,101,213,106,213,29,29,29,209,233,116,112,206,45,53,0,33,4,155,155,155,249,193,193,193,71,192,
|
||||
237,149,0,104,173,211,44,203,196,165,75,27,24,99,176,214,158,9,92,204,132,16,130,106,181,90,63,60,60,252,98,123,
|
||||
123,251,214,172,127,153,156,115,218,90,251,241,222,222,222,143,207,5,0,178,44,203,188,78,167,75,167,211,153,3,204,142,
|
||||
179,32,197,64,91,91,91,245,70,163,65,179,217,68,8,129,16,2,0,41,229,188,29,69,17,247,238,221,187,5,60,31,
|
||||
224,242,229,203,105,191,223,247,54,54,90,104,173,23,0,150,149,98,166,56,142,1,230,16,69,139,227,24,99,76,237,188,
|
||||
224,115,128,221,221,93,117,237,218,53,217,235,245,230,25,0,150,130,204,178,81,212,108,196,197,224,82,202,25,64,229,66,
|
||||
0,0,99,140,109,54,215,100,154,102,220,63,180,220,221,63,27,228,188,58,63,171,25,204,228,153,22,206,93,185,206,219,
|
||||
31,232,226,61,158,39,190,93,255,253,155,79,206,0,56,231,116,183,219,245,187,221,46,143,59,13,62,253,240,93,174,191,
|
||||
181,81,112,54,61,62,47,250,18,200,105,207,60,206,147,206,152,175,190,187,255,254,236,188,152,1,221,104,212,171,90,107,
|
||||
212,31,146,102,181,204,254,223,99,162,56,159,140,108,233,104,207,35,17,11,77,1,52,42,62,42,211,8,65,103,118,121,
|
||||
190,21,27,99,116,191,63,160,219,237,18,103,150,122,181,132,53,147,27,228,212,193,179,134,59,207,220,83,179,19,179,214,
|
||||
145,100,134,52,203,193,137,249,6,86,44,65,86,169,4,180,90,45,50,35,168,5,37,172,83,120,114,53,175,139,113,154,
|
||||
83,111,250,140,134,57,214,186,39,11,0,121,158,171,40,26,209,239,15,208,230,13,170,129,143,115,14,79,158,155,231,23,
|
||||
147,131,81,170,209,185,37,240,37,113,170,173,49,249,209,2,128,49,38,43,151,75,84,155,27,172,213,202,56,64,10,177,
|
||||
188,248,47,40,227,28,163,84,147,91,135,231,9,130,178,199,105,146,41,33,196,201,2,128,181,54,27,141,198,28,15,20,
|
||||
235,245,75,232,220,190,210,232,51,109,137,51,3,78,224,79,253,148,61,201,232,84,229,130,167,147,176,152,129,212,247,125,
|
||||
188,32,160,73,153,76,217,11,131,76,230,152,195,186,201,62,97,236,196,84,110,151,173,72,156,131,193,40,181,185,115,139,
|
||||
0,214,218,100,60,30,211,25,248,84,90,101,162,68,147,233,233,75,8,200,141,69,27,135,206,45,185,153,110,203,47,153,
|
||||
149,68,25,134,227,196,19,210,91,90,130,84,8,129,12,26,148,74,62,163,68,19,37,154,84,25,148,182,47,29,108,153,
|
||||
226,44,231,52,209,62,231,148,32,142,227,83,122,81,133,74,13,30,159,196,12,99,189,212,209,191,145,0,134,177,66,25,
|
||||
83,250,245,234,94,47,124,252,12,128,181,54,6,129,245,107,100,185,37,138,53,74,95,60,15,94,84,82,10,78,134,9,
|
||||
82,200,56,220,221,157,59,62,51,7,146,36,33,74,12,87,175,4,84,3,143,160,188,186,111,86,41,4,105,170,144,82,
|
||||
12,139,253,197,18,36,0,154,18,235,85,143,122,105,21,85,127,42,231,28,253,36,65,88,59,88,0,104,183,219,98,103,
|
||||
103,199,68,177,98,112,234,248,243,100,136,236,190,226,14,184,68,199,199,67,155,36,195,7,187,237,118,45,12,195,120,14,
|
||||
0,120,81,20,253,244,203,161,248,76,173,227,255,182,255,202,31,187,103,228,192,2,88,157,30,28,63,248,225,235,226,53,
|
||||
49,251,53,107,183,219,205,96,109,237,205,90,208,218,116,56,41,133,92,201,18,200,141,49,42,75,107,185,16,121,62,58,
|
||||
233,1,127,133,97,216,91,0,152,66,72,32,96,146,153,124,21,0,83,5,64,10,168,48,12,207,44,45,241,255,207,233,
|
||||
235,6,248,7,188,50,165,151,203,8,55,43,0,0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
|
||||
const uint8_t file[844] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,3,3,73,68,65,84,88,133,229,151,79,110,212,
|
||||
48,24,197,127,182,227,56,81,39,116,36,132,0,169,167,226,2,101,209,93,239,192,150,37,171,46,43,245,4,92,164,183,
|
||||
40,82,89,21,36,134,84,76,18,59,44,90,91,142,243,103,50,21,59,62,201,138,227,120,236,247,189,247,252,37,3,255,
|
||||
123,136,116,224,234,234,234,67,150,101,55,64,165,148,66,74,137,115,14,107,109,104,93,215,133,107,220,95,26,3,118,93,
|
||||
215,125,188,185,185,249,26,239,151,141,16,9,113,125,113,113,81,61,247,195,120,223,247,131,121,241,253,202,126,117,121,121,
|
||||
121,13,44,3,176,214,110,1,110,111,111,17,66,32,165,12,87,41,37,74,41,148,82,8,33,80,74,177,221,110,209,90,
|
||||
15,158,167,125,33,4,251,253,158,166,105,182,233,126,50,29,232,251,30,33,68,104,64,88,208,55,207,78,215,117,24,99,
|
||||
70,191,247,109,77,140,24,112,206,133,13,98,6,210,38,165,164,40,138,48,127,42,60,16,159,200,20,168,17,3,206,185,
|
||||
17,11,177,4,49,40,231,28,77,211,28,204,114,137,141,17,0,107,109,232,207,177,224,159,41,165,208,90,31,100,97,9,
|
||||
200,36,3,233,230,233,189,181,150,60,207,41,203,146,186,174,233,186,46,204,91,202,124,21,0,207,192,156,9,1,140,49,
|
||||
20,69,65,93,215,100,89,70,158,231,3,173,231,50,127,17,128,88,123,127,239,105,55,198,96,140,161,109,219,85,174,95,
|
||||
13,96,202,132,41,35,62,218,182,5,240,213,110,113,211,163,25,72,179,23,66,208,52,13,90,235,80,92,252,243,199,199,
|
||||
199,229,244,97,210,172,7,37,136,93,47,165,164,170,42,178,44,67,8,193,201,201,73,232,151,101,57,152,63,197,194,139,
|
||||
37,136,77,168,181,198,90,27,42,97,215,117,97,222,210,113,92,205,128,215,50,205,222,103,16,75,226,156,11,12,120,64,
|
||||
62,203,41,38,142,146,32,213,190,239,123,172,181,225,69,228,105,143,193,214,117,141,115,110,86,134,163,0,196,11,11,33,
|
||||
200,178,140,170,170,6,115,235,186,14,114,120,79,40,165,70,155,28,205,64,74,181,7,32,165,28,248,195,24,67,158,231,
|
||||
1,172,82,106,117,89,158,5,144,158,231,212,7,113,40,165,6,12,120,169,210,170,184,20,171,142,161,47,54,177,254,254,
|
||||
236,123,205,227,58,177,219,237,86,3,152,250,34,26,80,37,132,96,179,217,80,150,229,200,96,155,205,6,173,245,96,190,
|
||||
49,38,48,177,6,196,162,9,189,15,178,44,27,140,123,96,109,219,142,36,136,95,90,107,98,214,3,177,9,211,240,99,
|
||||
190,8,77,197,161,162,52,11,32,149,96,238,37,3,79,18,164,0,60,232,135,135,135,17,107,171,1,132,135,82,6,157,
|
||||
227,240,0,119,187,221,8,160,63,5,167,167,167,65,138,163,62,201,226,5,189,235,167,88,232,251,30,173,245,36,56,95,
|
||||
71,252,247,229,82,164,167,160,146,82,254,234,251,254,213,217,217,89,24,156,211,185,40,138,197,197,211,242,155,231,249,14,
|
||||
120,7,252,4,246,41,128,10,120,115,127,127,255,249,252,252,252,19,80,46,174,190,50,242,60,167,105,26,132,16,127,238,
|
||||
238,238,190,0,239,1,5,124,7,92,156,90,14,188,5,94,3,91,192,60,79,252,23,241,135,167,100,107,224,7,240,13,
|
||||
248,13,19,127,78,159,55,46,158,1,173,43,103,135,195,1,45,79,180,239,129,96,140,191,182,58,238,12,241,249,173,246,
|
||||
0,0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
|
||||
const uint8_t archive[1067] = {
|
||||
137,80,78,71,13,10,26,10,0,0,0,13,73,72,68,82,0,0,0,32,0,0,0,32,8,6,0,0,0,115,122,122,
|
||||
244,0,0,0,4,115,66,73,84,8,8,8,8,124,8,100,136,0,0,3,226,73,68,65,84,88,133,237,150,95,136,212,
|
||||
85,20,199,63,231,222,251,251,253,102,102,103,255,142,166,27,150,150,88,144,102,127,31,218,40,49,200,36,136,68,200,30,
|
||||
20,218,212,13,89,84,8,138,30,138,222,10,122,232,205,212,44,89,55,145,192,146,32,168,232,37,42,208,212,84,212,165,
|
||||
32,162,168,101,21,203,221,213,253,51,237,56,51,191,223,189,61,252,134,217,217,117,86,155,26,240,161,253,194,225,222,115,
|
||||
239,253,157,251,189,231,158,223,57,23,102,49,139,255,59,228,90,147,189,47,164,151,26,63,120,51,12,243,107,156,184,60,
|
||||
14,59,229,75,135,2,130,234,70,5,45,114,196,89,121,233,185,247,198,142,215,68,96,223,150,166,37,126,144,58,168,148,
|
||||
190,115,249,163,107,82,205,109,237,68,81,136,32,24,63,0,229,163,76,2,209,1,202,76,138,232,4,202,11,80,58,32,
|
||||
180,206,253,240,117,143,244,159,250,232,47,27,113,209,138,125,165,115,87,246,208,117,9,236,239,106,190,77,18,254,137,101,
|
||||
43,58,91,231,45,186,67,141,12,156,117,103,15,127,38,78,0,231,112,81,30,163,4,163,193,211,130,209,130,167,193,76,
|
||||
235,39,146,45,100,30,123,195,93,56,190,215,133,131,167,213,207,231,70,115,69,203,182,141,239,142,247,84,238,167,43,149,
|
||||
3,93,201,5,36,147,199,239,93,221,221,182,248,129,213,122,252,66,31,167,191,249,88,10,133,43,216,40,196,218,8,37,
|
||||
160,68,80,10,180,18,148,18,180,2,53,173,47,46,79,225,242,79,114,83,199,139,146,255,227,20,173,137,208,27,30,203,
|
||||
175,122,250,65,51,240,233,201,98,223,85,30,120,191,171,97,94,50,149,60,185,108,229,166,246,246,37,29,218,141,253,138,
|
||||
23,52,80,140,38,157,100,157,5,107,65,25,68,123,177,168,88,40,181,162,13,136,46,27,78,54,207,167,144,27,167,175,
|
||||
119,45,162,52,191,93,24,155,112,214,110,234,220,147,61,88,38,208,179,49,61,55,104,76,29,107,91,176,116,161,56,114,
|
||||
67,3,125,233,71,158,121,141,230,185,183,146,27,57,55,121,95,162,64,52,136,138,5,13,74,226,13,69,202,115,18,7,
|
||||
40,0,65,67,134,200,193,151,59,214,227,108,33,151,12,252,68,54,151,15,113,172,239,220,147,61,36,123,55,55,181,25,
|
||||
99,135,1,252,32,117,62,44,230,191,187,253,190,39,215,221,179,122,27,209,200,47,224,44,206,89,112,46,182,234,92,172,
|
||||
151,250,255,100,204,207,220,69,118,228,34,95,125,240,242,69,92,120,38,178,246,9,0,177,108,85,198,68,111,3,160,100,
|
||||
205,149,98,126,69,208,152,121,106,249,170,173,68,163,191,99,139,19,216,98,14,23,94,41,139,13,167,233,197,220,117,199,
|
||||
242,127,158,165,113,206,34,238,94,185,161,69,180,30,182,74,47,22,56,234,20,59,167,252,5,7,182,207,57,252,248,230,
|
||||
221,15,167,51,11,175,153,31,254,11,190,221,191,61,55,116,254,199,141,157,187,42,98,0,96,95,119,211,67,98,237,81,
|
||||
165,117,81,68,121,162,52,206,70,53,152,118,51,206,136,196,182,28,46,111,163,40,0,120,126,79,86,166,16,112,32,31,
|
||||
118,55,183,0,20,109,116,169,99,237,171,12,28,235,169,110,177,70,220,124,255,58,190,255,124,7,158,210,109,0,158,248,
|
||||
197,103,119,13,102,1,76,153,37,56,118,143,94,6,232,221,146,38,145,74,147,76,165,235,66,32,72,54,2,176,161,100,
|
||||
191,18,230,170,213,37,40,109,240,147,13,117,33,160,141,55,227,220,140,4,180,241,240,19,245,241,128,54,126,237,4,140,
|
||||
246,8,18,245,241,128,249,247,30,184,145,4,60,143,160,78,49,96,252,242,21,248,64,225,90,4,2,160,21,226,138,103,
|
||||
252,20,184,90,114,65,21,136,66,169,114,209,189,5,24,3,46,1,81,53,2,205,64,198,41,111,112,184,255,204,220,204,
|
||||
194,142,210,112,69,146,113,149,9,199,149,117,87,117,77,220,14,245,247,97,197,27,2,230,151,246,140,74,36,166,16,208,
|
||||
37,221,251,226,244,196,91,216,157,175,11,97,75,141,231,173,10,135,25,249,228,196,196,59,64,2,240,168,120,135,76,207,
|
||||
249,173,64,166,212,6,196,119,86,15,20,0,5,100,137,79,126,30,40,86,35,0,144,4,90,74,155,207,24,164,53,34,
|
||||
36,190,143,81,96,28,38,31,183,215,171,122,245,170,138,51,87,170,89,220,104,252,13,162,179,143,166,193,167,182,66,0,
|
||||
0,0,0,73,69,78,68,174,66,96,130,
|
||||
};
|
||||
|
||||
};
|
@@ -1,7 +0,0 @@
|
||||
namespace resource {
|
||||
extern const uint8_t home[606];
|
||||
extern const uint8_t up[652];
|
||||
extern const uint8_t folder[1176];
|
||||
extern const uint8_t file[844];
|
||||
extern const uint8_t archive[1067];
|
||||
};
|
@@ -1,90 +0,0 @@
|
||||
void Ananke::copySufamiTurboSaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".srm"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".srm"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createSufamiTurboDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest) {
|
||||
string pathname = {
|
||||
libraryPath, "Sufami Turbo/",
|
||||
document["release/information/name"].text(),
|
||||
" (", document["release/information/region"].text(), ")",
|
||||
" (", document["release/information/revision"].text(), ")",
|
||||
".st/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
//strip "release" root node from database entry (since a single game manifest isn't part of a database)
|
||||
string markup = manifest;
|
||||
markup.replace("\n ", "\n");
|
||||
markup.replace("information", "\ninformation");
|
||||
markup.ltrim<1>("release\n");
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
copySufamiTurboSaves(pathname);
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::createSufamiTurboHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
libraryPath, "Sufami Turbo/",
|
||||
nall::basename(information.name),
|
||||
".st/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
file::write({pathname, "manifest.bml"}, {
|
||||
"unverified\n",
|
||||
"\n",
|
||||
"cartridge\n",
|
||||
" rom name=program.rom size=0x", hex(buffer.size()), "\n",
|
||||
" ram name=save.ram size=0x2000\n",
|
||||
"\n",
|
||||
"information\n",
|
||||
" title: ", nall::basename(information.name), "\n"
|
||||
});
|
||||
file::write({pathname, "program.rom"}, buffer);
|
||||
copySufamiTurboSaves(pathname);
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::openSufamiTurbo(vector<uint8_t> &buffer) {
|
||||
string sha256 = nall::sha256(buffer.data(), buffer.size());
|
||||
|
||||
string databaseText = string::read({configpath(), "ananke/database/Sufami Turbo.bml"}).strip();
|
||||
if(databaseText.empty()) databaseText = string{Database::SufamiTurbo}.strip();
|
||||
lstring databaseItem = databaseText.split("\n\n");
|
||||
|
||||
for(auto &item : databaseItem) {
|
||||
item.append("\n");
|
||||
auto document = Markup::Document(item);
|
||||
|
||||
if(document["release/information/sha256"].text() == sha256) {
|
||||
return createSufamiTurboDatabase(buffer, document, item);
|
||||
}
|
||||
}
|
||||
|
||||
return createSufamiTurboHeuristic(buffer);
|
||||
}
|
||||
|
||||
string Ananke::syncSufamiTurbo(const string &pathname) {
|
||||
auto buffer = file::read({pathname, "program.rom"});
|
||||
if(buffer.size() == 0) return "";
|
||||
|
||||
auto save = file::read({pathname, "save.ram"});
|
||||
if(save.size() == 0) save = file::read({pathname, "save.rwm"});
|
||||
|
||||
directory::remove(pathname);
|
||||
information.path = pathname;
|
||||
information.name = notdir(string{pathname}.rtrim<1>("/"));
|
||||
string outputPath = openSufamiTurbo(buffer);
|
||||
|
||||
if(save.size()) file::write({outputPath, "save.ram"}, save);
|
||||
|
||||
return outputPath;
|
||||
}
|
@@ -1,215 +0,0 @@
|
||||
void Ananke::copySuperFamicomSaves(const string &pathname) {
|
||||
if(!file::exists({pathname, "save.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".srm"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".srm"}, {pathname, "save.ram"});
|
||||
}
|
||||
}
|
||||
|
||||
if(!file::exists({pathname, "rtc.ram"})) {
|
||||
if(file::exists({information.path, nall::basename(information.name), ".rtc"})) {
|
||||
file::copy({information.path, nall::basename(information.name), ".rtc"}, {pathname, "rtc.ram"});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string Ananke::createSuperFamicomDatabase(vector<uint8_t> &buffer, Markup::Node &document, const string &manifest) {
|
||||
string pathname = {
|
||||
libraryPath, "Super Famicom/",
|
||||
document["release/information/name"].text(),
|
||||
" (", document["release/information/region"].text(), ")",
|
||||
" (", document["release/information/revision"].text(), ")",
|
||||
".sfc/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
//strip "release" root node from database entry (since a single game manifest isn't part of a database)
|
||||
string markup = manifest;
|
||||
markup.replace("\n ", "\n");
|
||||
markup.replace("information", "\ninformation");
|
||||
markup.ltrim<1>("release\n");
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
|
||||
unsigned offset = 0;
|
||||
for(auto &node : document["release/information/configuration"]) {
|
||||
if(node.name != "rom") continue;
|
||||
string name = node["name"].text();
|
||||
unsigned size = node["size"].decimal();
|
||||
file::write({pathname, name}, buffer.data() + offset, size);
|
||||
offset += size;
|
||||
}
|
||||
|
||||
copySuperFamicomSaves(pathname);
|
||||
return pathname;
|
||||
}
|
||||
|
||||
string Ananke::createSuperFamicomHeuristic(vector<uint8_t> &buffer) {
|
||||
string pathname = {
|
||||
libraryPath, "Super Famicom/",
|
||||
nall::basename(information.name),
|
||||
".sfc/"
|
||||
};
|
||||
directory::create(pathname);
|
||||
|
||||
if((buffer.size() & 0x7fff) == 512) buffer.remove(0, 512); //strip copier header, if present
|
||||
|
||||
SuperFamicomCartridge info(buffer.data(), buffer.size());
|
||||
string markup = {"unverified\n\n", info.markup};
|
||||
markup.append("\ninformation\n title: ", nall::basename(information.name), "\n");
|
||||
if(!information.manifest.empty()) markup = information.manifest; //override with embedded beat manifest, if one exists
|
||||
information.manifest = markup; //save for use with firmware routine below
|
||||
|
||||
file::write({pathname, "manifest.bml"}, markup);
|
||||
|
||||
if(!markup.find("spc7110")) {
|
||||
file::write({pathname, "program.rom"}, buffer.data(), info.rom_size);
|
||||
} else {
|
||||
file::write({pathname, "program.rom"}, buffer.data(), 0x100000);
|
||||
file::write({pathname, "data.rom"}, buffer.data() + 0x100000, info.rom_size - 0x100000);
|
||||
}
|
||||
|
||||
createSuperFamicomHeuristicFirmware(buffer, pathname, info.firmware_appended);
|
||||
copySuperFamicomSaves(pathname);
|
||||
|
||||
return pathname;
|
||||
}
|
||||
|
||||
void Ananke::createSuperFamicomHeuristicFirmware(vector<uint8_t> &buffer, const string &pathname, bool firmware_appended) {
|
||||
auto copyFirmwareInternal = [&](const string &name, unsigned programSize, unsigned dataSize, unsigned bootSize) {
|
||||
//firmware appended directly onto .sfc file
|
||||
string basename = nall::basename(name);
|
||||
if(programSize) file::write({pathname, basename, ".program.rom"}, buffer.data() + buffer.size() - programSize - dataSize - bootSize, programSize);
|
||||
if(dataSize) file::write({pathname, basename, ".data.rom"}, buffer.data() + buffer.size() - dataSize - bootSize, dataSize);
|
||||
if(bootSize) file::write({pathname, basename, ".boot.rom"}, buffer.data() + buffer.size() - bootSize, bootSize);
|
||||
};
|
||||
|
||||
auto copyFirmwareExternal = [&](const string &name, unsigned programSize, unsigned dataSize, unsigned bootSize) {
|
||||
//firmware stored in external file
|
||||
auto buffer = file::read({information.path, name}); //try and read from the containing directory
|
||||
if(buffer.size() == 0) buffer = extractFile(name); //try and read from the containing archive, if one exists
|
||||
if(buffer.size() == 0) {
|
||||
if(thread::primary()) MessageWindow().setText({
|
||||
"Error: ", information.name, "\n\n",
|
||||
"Required firmware ", name, " not found. Game will not be playable!\n\n",
|
||||
"You must obtain this file, and place it in the same folder as this game."
|
||||
}).error();
|
||||
return;
|
||||
}
|
||||
|
||||
string basename = nall::basename(name);
|
||||
if(programSize) file::write({pathname, basename, ".program.rom"}, buffer.data(), programSize);
|
||||
if(dataSize) file::write({pathname, basename, ".data.rom"}, buffer.data() + programSize, dataSize);
|
||||
if(bootSize) file::write({pathname, basename, ".boot.rom"}, buffer.data() + programSize + dataSize, bootSize);
|
||||
};
|
||||
|
||||
auto copyFirmware = [&](const string &name, unsigned programSize, unsigned dataSize, unsigned bootSize = 0) {
|
||||
if(firmware_appended == 1) copyFirmwareInternal(name, programSize, dataSize, bootSize);
|
||||
if(firmware_appended == 0) copyFirmwareExternal(name, programSize, dataSize, bootSize);
|
||||
};
|
||||
|
||||
string markup = information.manifest;
|
||||
if(markup.find("dsp1.program.rom" )) copyFirmware("dsp1.rom", 0x001800, 0x000800);
|
||||
if(markup.find("dsp1b.program.rom")) copyFirmware("dsp1b.rom", 0x001800, 0x000800);
|
||||
if(markup.find("dsp2.program.rom" )) copyFirmware("dsp2.rom", 0x001800, 0x000800);
|
||||
if(markup.find("dsp3.program.rom" )) copyFirmware("dsp3.rom", 0x001800, 0x000800);
|
||||
if(markup.find("dsp4.program.rom" )) copyFirmware("dsp4.rom", 0x001800, 0x000800);
|
||||
if(markup.find("st010.program.rom")) copyFirmware("st010.rom", 0x00c000, 0x001000);
|
||||
if(markup.find("st011.program.rom")) copyFirmware("st011.rom", 0x00c000, 0x001000);
|
||||
if(markup.find("st018.program.rom")) copyFirmware("st018.rom", 0x020000, 0x008000);
|
||||
if(markup.find("cx4.data.rom" )) copyFirmware("cx4.rom", 0x000000, 0x000c00);
|
||||
if(markup.find("sgb.boot.rom" )) copyFirmware("sgb.rom", 0x000000, 0x000000, 0x000100);
|
||||
}
|
||||
|
||||
string Ananke::openSuperFamicom(vector<uint8_t> &buffer) {
|
||||
string sha256 = nall::sha256(buffer.data(), buffer.size());
|
||||
|
||||
string databaseText = string::read({configpath(), "ananke/database/Super Famicom.bml"}).strip();
|
||||
if(databaseText.empty()) databaseText = string{Database::SuperFamicom}.strip();
|
||||
lstring databaseItem = databaseText.split("\n\n");
|
||||
|
||||
for(auto &item : databaseItem) {
|
||||
item.append("\n");
|
||||
auto document = Markup::Document(item);
|
||||
|
||||
if(document["release/information/sha256"].text() == sha256) {
|
||||
return createSuperFamicomDatabase(buffer, document, item);
|
||||
}
|
||||
}
|
||||
|
||||
return createSuperFamicomHeuristic(buffer);
|
||||
}
|
||||
|
||||
string Ananke::syncSuperFamicom(const string &pathname) {
|
||||
if(file::exists({pathname, "msu1.rom"})) return ""; //cannot update MSU1 games
|
||||
|
||||
vector<uint8_t> buffer;
|
||||
|
||||
auto append = [&](string filename) {
|
||||
filename = {pathname, filename};
|
||||
auto data = file::read(filename);
|
||||
if(data.size() == 0) return; //file does not exist
|
||||
|
||||
unsigned position = buffer.size();
|
||||
buffer.resize(buffer.size() + data.size());
|
||||
memcpy(buffer.data() + position, data.data(), data.size());
|
||||
};
|
||||
|
||||
append("program.rom");
|
||||
append("data.rom");
|
||||
|
||||
append("dsp1.rom");
|
||||
append("dsp1.program.rom");
|
||||
append("dsp1.data.rom");
|
||||
|
||||
append("dsp1b.rom");
|
||||
append("dsp1b.program.rom");
|
||||
append("dsp1b.data.rom");
|
||||
|
||||
append("dsp2.rom");
|
||||
append("dsp2.program.rom");
|
||||
append("dsp2.data.rom");
|
||||
|
||||
append("dsp3.rom");
|
||||
append("dsp3.program.rom");
|
||||
append("dsp3.data.rom");
|
||||
|
||||
append("dsp4.rom");
|
||||
append("dsp4.program.rom");
|
||||
append("dsp4.data.rom");
|
||||
|
||||
append("st010.rom");
|
||||
append("st010.program.rom");
|
||||
append("st010.data.rom");
|
||||
|
||||
append("st011.rom");
|
||||
append("st011.program.rom");
|
||||
append("st011.data.rom");
|
||||
|
||||
append("st018.rom");
|
||||
append("st018.program.rom");
|
||||
append("st018.data.rom");
|
||||
|
||||
append("cx4.rom");
|
||||
append("cx4.data.rom");
|
||||
|
||||
append("sgb.rom");
|
||||
append("sgb.boot.rom");
|
||||
|
||||
if(buffer.size() == 0) return "";
|
||||
|
||||
auto save = file::read({pathname, "save.ram"});
|
||||
if(save.size() == 0) save = file::read({pathname, "save.rwm"});
|
||||
|
||||
auto rtc = file::read({pathname, "rtc.ram"});
|
||||
if(rtc.size() == 0) rtc= file::read({pathname, "rtc.rwm"});
|
||||
|
||||
directory::remove(pathname);
|
||||
information.path = pathname;
|
||||
information.name = notdir(string{pathname}.rtrim<1>("/"));
|
||||
string outputPath = openSuperFamicom(buffer);
|
||||
|
||||
if(save.size()) file::write({outputPath, "save.ram"}, save);
|
||||
if(rtc.size()) file::write({outputPath, "rtc.ram"}, save);
|
||||
|
||||
return outputPath;
|
||||
}
|
44
bsnes/Database/BS Memory.bml
Normal file
44
bsnes/Database/BS Memory.bml
Normal file
@@ -0,0 +1,44 @@
|
||||
database
|
||||
revision: 2020-01-01
|
||||
|
||||
//BS Memory (JPN)
|
||||
|
||||
database
|
||||
revision: 2018-04-14
|
||||
|
||||
game
|
||||
sha256: 80c34b50817d58820bc8c88d2d9fa462550b4a76372e19c6467cbfbc8cf5d9ef
|
||||
label: 鮫亀 キャラカセット
|
||||
name: Same Game - Chara Cassette
|
||||
region: BSMC-ZS5J-JPN
|
||||
revision: BSMC-ZS5J-0
|
||||
board: BSMC-CR-01
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: 859c7f7b4771d920a5bdb11f1d247ab6b43fb026594d1062f6f72d32cd340a0a
|
||||
label: 鮫亀 キャラデータ集
|
||||
name: Same Game - Chara Data Shuu
|
||||
region: BSMC-YS5J-JPN
|
||||
revision: BSMC-YS5J-0
|
||||
board: BSMC-CR-01
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
|
||||
game
|
||||
sha256: c92a15fdd9b0133f9ea69105d0230a3acd1cdeef98567462eca86ea02a959e4e
|
||||
label: SDガンダム ジーネクスト ユニット&マップコレクション
|
||||
name: SD Gundam G Next - Unit & Map Collection
|
||||
region: BSMC-ZX3J-JPN
|
||||
revision: BSMC-ZX3J-0
|
||||
board: BSMC-BR-01
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
|
87497
bsnes/Database/Cheat Codes.bml
Normal file
87497
bsnes/Database/Cheat Codes.bml
Normal file
File diff suppressed because it is too large
Load Diff
213
bsnes/Database/Sufami Turbo.bml
Normal file
213
bsnes/Database/Sufami Turbo.bml
Normal file
@@ -0,0 +1,213 @@
|
||||
database
|
||||
revision: 2020-01-01
|
||||
|
||||
//Sufami Turbo (JPN)
|
||||
|
||||
database
|
||||
revision: 2018-04-14
|
||||
|
||||
game
|
||||
sha256: f73bda08743565e0bd101632ebbac2d363d703a3ab39d23f49d95217cab29269
|
||||
label: 美少女戦士セーラームーン セーラースターズ ふわふわパニック2
|
||||
name: Bishoujo Senshi Sailor Moon Sailor Stars - Fuwafuwa Panic 2
|
||||
region: SFT-0112-JPN
|
||||
revision: SAILOR MOON
|
||||
board: PT-911
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x100000
|
||||
content: Program
|
||||
note: Unlinkable
|
||||
|
||||
game
|
||||
sha256: afb3f2a83b5bfcb1b8829b6995f108cc4d64ca322d1ba4a50b83af6e1f2e89bd
|
||||
label: クレヨンしんちゃん 長ぐつドボン!!
|
||||
name: Crayon Shin-chan - Nagagutsu Dobon!!
|
||||
region: SFT-0113-JPN
|
||||
revision: SHINCYAN
|
||||
board: PT-911
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
note: Unlinkable
|
||||
|
||||
game
|
||||
sha256: d93b3a570e7cf343f680ab0768a50b77e3577f9c555007e2de3decd6bc4765c8
|
||||
label: ゲゲゲの鬼太郎 妖怪ドンジャラ
|
||||
name: Gegege no Kitarou - Youkai Donjara
|
||||
region: SFT-0106-JPN
|
||||
revision: KITARO DONJYAR
|
||||
board: PT-911
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
note: Unlinkable
|
||||
|
||||
game
|
||||
sha256: 89aecd4e23d8219c8de3e71bb75db3dfe667d51c1eba4ea7239d2f772743d0cc
|
||||
label: 激走戦隊カーレンジャ 全開!レーサー戦士
|
||||
name: Gekisou Sentai Carranger - Zenkai! Racer Senshi
|
||||
region: SFT-0109-JPN
|
||||
revision: CAR RANGER
|
||||
board: PT-911
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
note: Unlinkable
|
||||
|
||||
game
|
||||
sha256: 602b20b788640f5743487108a10f3f77bca5ce2d24208b25b1ca498a96eb0d69
|
||||
label: ぽいぽい忍者ワールド
|
||||
name: Poi Poi Ninja World
|
||||
region: SFT-0103-JPN
|
||||
revision: POI POI NINJYA
|
||||
board: PT-911
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x800
|
||||
content: Save
|
||||
note: Linkable: SFT-0103-JPN
|
||||
|
||||
game
|
||||
sha256: 2a9d7c9a61318861028a73ca03e32a48cff162d76cba36fbaab8690b212efe9b
|
||||
label: SDガンダムジェネレーション アクシズ戦記
|
||||
name: SD Gundam Generation - Axis Senki
|
||||
region: SFT-0107-JPN
|
||||
revision: GUNDAM C
|
||||
board: PT-912
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
|
||||
|
||||
game
|
||||
sha256: 60ac017c18f534e8cf24ca7f38e22ce92db95ea6c30b2d59d76f13c4f1c8a6e4
|
||||
label: SDガンダムジェネレーション バビロニア建国戦記
|
||||
name: SD Gundam Generation - Babylonia Kenkoku Senki
|
||||
region: SFT-0108-JPN
|
||||
revision: GUNDAM D
|
||||
board: PT-912
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
|
||||
|
||||
game
|
||||
sha256: e639b5d5d722432b6809ccc6801dc584e1a3016379f34b335ed2dfa73b1ebf69
|
||||
label: SDガンダムジェネレーション コロニー格闘記
|
||||
name: SD Gundam Generation - Colony Kakutouki
|
||||
region: SFT-0111-JPN
|
||||
revision: GUNDAM F
|
||||
board: PT-912
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
|
||||
|
||||
game
|
||||
sha256: 8547a08ed11fe408eac282a90ac46654bd2e5f49bda3aec8e5edf166a0a4b9af
|
||||
label: SDガンダムジェネレーション グリプス戦記
|
||||
name: SD Gundam Generation - Gryps Senki
|
||||
region: SFT-0105-JPN
|
||||
revision: GUNDAM B
|
||||
board: PT-912
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
|
||||
|
||||
game
|
||||
sha256: 3e82215bed08274874b30d461fc4a965c6bca932229da5d46d56e36f484d65eb
|
||||
label: SDガンダムジェネレーション 一年戦争記
|
||||
name: SD Gundam Generation - Ichinen Sensouki
|
||||
region: SFT-0104-JPN
|
||||
revision: GUNDAM A
|
||||
board: PT-912
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
|
||||
|
||||
game
|
||||
sha256: 5951a58a91d8e397d0a237ccc2b1248e17c7312cb9cc11cbc350200a97b4e021
|
||||
label: SDガンダムジェネレーション ザンスカール戦記
|
||||
name: SD Gundam Generation - Zanscare Senki
|
||||
region: SFT-0110-JPN
|
||||
revision: GUNDAM E
|
||||
board: PT-912
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x2000
|
||||
content: Save
|
||||
note: Linkable: SFT-0104-JPN, SFT-0105-JPN, SFT-0107-JPN, SFT-0108-JPN, SFT-0110-JPN, SFT-0111-JPN
|
||||
|
||||
game
|
||||
sha256: 2fec5f2bc7dee010af10569a3d2bc18715a79a126940800c3eade5abbd625e3f
|
||||
label: SDウルトラバトル セブン伝説
|
||||
name: SD Ultra Battle - Seven Densetsu
|
||||
region: SFT-0102-JPN
|
||||
revision: ULTRA SEVEN 1
|
||||
board: PT-911
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x800
|
||||
content: Save
|
||||
note: Linkable: SFT-0101-JPN, SFT-0102-JPN
|
||||
|
||||
game
|
||||
sha256: 2bb55214fb668ca603d7b944b14f105dfb10b987a8902d420fe4ae1cb69c1d4a
|
||||
label: SDウルトラバトル ウルトラマン伝説
|
||||
name: SD Ultra Battle - Ultraman Densetsu
|
||||
region: SFT-0101-JPN
|
||||
revision: ULTRA MAN 1
|
||||
board: PT-911
|
||||
memory
|
||||
type: ROM
|
||||
size: 0x80000
|
||||
content: Program
|
||||
memory
|
||||
type: RAM
|
||||
size: 0x800
|
||||
content: Save
|
||||
note: Linkable: SFT-0101-JPN, SFT-0102-JPN
|
||||
|
16069
bsnes/Database/Super Famicom.bml
Normal file
16069
bsnes/Database/Super Famicom.bml
Normal file
File diff suppressed because it is too large
Load Diff
64
bsnes/GNUmakefile
Normal file
64
bsnes/GNUmakefile
Normal file
@@ -0,0 +1,64 @@
|
||||
target := bsnes
|
||||
binary := application
|
||||
build := performance
|
||||
openmp := true
|
||||
local := true
|
||||
flags += -I. -I..
|
||||
|
||||
# in order for this to work, obj/lzma.o must be omitted or bsnes will hang on startup.
|
||||
# further, only the X-Video driver works reliably. OpenGL 3.2, OpenGL 2.0, and XShm crash bsnes.
|
||||
ifeq ($(profile),true)
|
||||
flags += -pg
|
||||
options += -pg
|
||||
endif
|
||||
|
||||
# binaries built with this flag are faster, but are not portable to multiple machines.
|
||||
ifeq ($(local),true)
|
||||
flags += -march=native
|
||||
endif
|
||||
|
||||
nall.path := ../nall
|
||||
include $(nall.path)/GNUmakefile
|
||||
|
||||
ifeq ($(platform),windows)
|
||||
ifeq ($(binary),application)
|
||||
options += -luuid -lkernel32 -luser32 -lgdi32 -lcomctl32 -lcomdlg32 -lshell32
|
||||
options += -Wl,-enable-auto-import
|
||||
options += -Wl,-enable-runtime-pseudo-reloc
|
||||
else ifeq ($(binary),library)
|
||||
options += -shared
|
||||
endif
|
||||
else ifeq ($(platform),macos)
|
||||
ifeq ($(binary),application)
|
||||
else ifeq ($(binary),library)
|
||||
flags += -fPIC
|
||||
options += -dynamiclib
|
||||
endif
|
||||
else ifneq ($(filter $(platform),linux bsd),)
|
||||
ifeq ($(binary),application)
|
||||
options += -Wl,-export-dynamic
|
||||
options += -lX11 -lXext
|
||||
else ifeq ($(binary),library)
|
||||
flags += -fPIC
|
||||
options += -shared
|
||||
endif
|
||||
endif
|
||||
|
||||
objects := libco emulator filter lzma
|
||||
|
||||
obj/libco.o: ../libco/libco.c
|
||||
obj/emulator.o: emulator/emulator.cpp
|
||||
obj/filter.o: filter/filter.cpp
|
||||
obj/lzma.o: lzma/lzma.cpp
|
||||
|
||||
include sfc/GNUmakefile
|
||||
include gb/GNUmakefile
|
||||
include processor/GNUmakefile
|
||||
|
||||
ui := target-$(target)
|
||||
include $(ui)/GNUmakefile
|
||||
-include obj/*.d
|
||||
|
||||
clean:
|
||||
$(call delete,obj/*)
|
||||
$(call delete,out/*)
|
127
bsnes/Locale/Japanese.bml
Normal file
127
bsnes/Locale/Japanese.bml
Normal file
@@ -0,0 +1,127 @@
|
||||
locale
|
||||
language: 日本語
|
||||
|
||||
namespace: MessageDialog
|
||||
map
|
||||
input: Yes
|
||||
value: はい
|
||||
map
|
||||
input: No
|
||||
value: いいえ
|
||||
map
|
||||
input: Cancel
|
||||
value: キャンセル
|
||||
|
||||
namespace: BrowserDialog
|
||||
map
|
||||
input: Open
|
||||
value: 開く
|
||||
map
|
||||
input: Save
|
||||
value: 保存
|
||||
map
|
||||
input: Select
|
||||
value: 選択
|
||||
map
|
||||
input: Cancel
|
||||
value: キャンセル
|
||||
|
||||
namespace: Program
|
||||
map
|
||||
input: Unloaded
|
||||
value: アンロードされる
|
||||
map
|
||||
input: Paused
|
||||
value: ポーズ
|
||||
|
||||
namespace: Presentation
|
||||
map
|
||||
input: System
|
||||
value: システム
|
||||
map
|
||||
input: Load Game
|
||||
value: ゲームを読み込み
|
||||
map
|
||||
input: Load Recent Game
|
||||
value: 最新ゲームを読み込み
|
||||
map
|
||||
input: empty
|
||||
value: なし
|
||||
map
|
||||
input: Clear List
|
||||
value: 全部を消す
|
||||
map
|
||||
input: Reset System
|
||||
value: システムをリセット
|
||||
map
|
||||
input: Unload Game
|
||||
value: ゲームをアンロード
|
||||
map
|
||||
input: Controller Port 1
|
||||
value: コントローラポート1
|
||||
map
|
||||
input: Controller Port 2
|
||||
value: コントローラポート2
|
||||
map
|
||||
input: Expansion Port
|
||||
value: 拡張ポート
|
||||
map
|
||||
input: Gamepad
|
||||
value: ゲームパッド
|
||||
map
|
||||
input: Mouse
|
||||
value: マウス
|
||||
map
|
||||
input: Super Multitap
|
||||
value: スーパーマルチタップ
|
||||
map
|
||||
input: Super Scope
|
||||
value: スーパースコップ
|
||||
map
|
||||
input: Justifier
|
||||
value: 1挺のジャスティファイアー
|
||||
map
|
||||
input: Justifiers
|
||||
value: 2挺のジャスティファイアー
|
||||
map
|
||||
input: None
|
||||
value: なし
|
||||
map
|
||||
input: Satellaview
|
||||
value: サテラビュー
|
||||
map
|
||||
input: Quit
|
||||
value: 終了
|
||||
map
|
||||
input: Settings
|
||||
value: 設定
|
||||
map
|
||||
input: Tools
|
||||
value: ツール
|
||||
map
|
||||
input: Help
|
||||
value: ヘルプ
|
||||
map
|
||||
input: Documentation
|
||||
value: オンラインマニュアル
|
||||
map
|
||||
input: About
|
||||
value: 情報
|
||||
|
||||
namespace: AboutWindow
|
||||
map
|
||||
input: About {0}
|
||||
value: {0}について
|
||||
map
|
||||
input: Version
|
||||
value: バージョン
|
||||
map
|
||||
input: Author
|
||||
value: 作者
|
||||
map
|
||||
input: License
|
||||
value: ライセンス
|
||||
map
|
||||
input: Website
|
||||
value: 公式サイト
|
||||
|
75
bsnes/emulator/audio/audio.cpp
Normal file
75
bsnes/emulator/audio/audio.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#define double float
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
#include "stream.cpp"
|
||||
Audio audio;
|
||||
|
||||
Audio::~Audio() {
|
||||
reset(nullptr);
|
||||
}
|
||||
|
||||
auto Audio::reset(Interface* interface) -> void {
|
||||
_interface = interface;
|
||||
_streams.reset();
|
||||
_channels = 0;
|
||||
}
|
||||
|
||||
auto Audio::setFrequency(double frequency) -> void {
|
||||
_frequency = frequency;
|
||||
for(auto& stream : _streams) {
|
||||
stream->setFrequency(stream->inputFrequency, frequency);
|
||||
}
|
||||
}
|
||||
|
||||
auto Audio::setVolume(double volume) -> void {
|
||||
_volume = volume;
|
||||
}
|
||||
|
||||
auto Audio::setBalance(double balance) -> void {
|
||||
_balance = balance;
|
||||
}
|
||||
|
||||
auto Audio::createStream(uint channels, double frequency) -> shared_pointer<Stream> {
|
||||
_channels = max(_channels, channels);
|
||||
shared_pointer<Stream> stream = new Stream;
|
||||
stream->reset(channels, frequency, _frequency);
|
||||
_streams.append(stream);
|
||||
return stream;
|
||||
}
|
||||
|
||||
auto Audio::process() -> void {
|
||||
while(_streams) {
|
||||
for(auto& stream : _streams) {
|
||||
if(!stream->pending()) return;
|
||||
}
|
||||
|
||||
double samples[_channels];
|
||||
for(auto& sample : samples) sample = 0.0;
|
||||
|
||||
for(auto& stream : _streams) {
|
||||
double buffer[_channels];
|
||||
uint length = stream->read(buffer), offset = 0;
|
||||
|
||||
for(auto& sample : samples) {
|
||||
sample += buffer[offset];
|
||||
if(++offset >= length) offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto c : range(_channels)) {
|
||||
samples[c] = max(-1.0, min(+1.0, samples[c] * _volume));
|
||||
}
|
||||
|
||||
if(_channels == 2) {
|
||||
if(_balance < 0.0) samples[1] *= 1.0 + _balance;
|
||||
if(_balance > 0.0) samples[0] *= 1.0 - _balance;
|
||||
}
|
||||
|
||||
platform->audioFrame(samples, _channels);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#undef double
|
95
bsnes/emulator/audio/audio.hpp
Normal file
95
bsnes/emulator/audio/audio.hpp
Normal file
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
#define double float
|
||||
|
||||
#include <nall/dsp/iir/dc-removal.hpp>
|
||||
#include <nall/dsp/iir/one-pole.hpp>
|
||||
#include <nall/dsp/iir/biquad.hpp>
|
||||
#include <nall/dsp/resampler/cubic.hpp>
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Interface;
|
||||
struct Audio;
|
||||
struct Filter;
|
||||
struct Stream;
|
||||
|
||||
struct Audio {
|
||||
~Audio();
|
||||
auto reset(Interface* interface) -> void;
|
||||
|
||||
inline auto channels() const -> uint { return _channels; }
|
||||
inline auto frequency() const -> double { return _frequency; }
|
||||
inline auto volume() const -> double { return _volume; }
|
||||
inline auto balance() const -> double { return _balance; }
|
||||
|
||||
auto setFrequency(double frequency) -> void;
|
||||
auto setVolume(double volume) -> void;
|
||||
auto setBalance(double balance) -> void;
|
||||
|
||||
auto createStream(uint channels, double frequency) -> shared_pointer<Stream>;
|
||||
|
||||
private:
|
||||
auto process() -> void;
|
||||
|
||||
Interface* _interface = nullptr;
|
||||
vector<shared_pointer<Stream>> _streams;
|
||||
|
||||
uint _channels = 0;
|
||||
double _frequency = 48000.0;
|
||||
|
||||
double _volume = 1.0;
|
||||
double _balance = 0.0;
|
||||
|
||||
friend class Stream;
|
||||
};
|
||||
|
||||
struct Filter {
|
||||
enum class Mode : uint { DCRemoval, OnePole, Biquad } mode;
|
||||
enum class Type : uint { None, LowPass, HighPass } type;
|
||||
enum class Order : uint { None, First, Second } order;
|
||||
|
||||
DSP::IIR::DCRemoval dcRemoval;
|
||||
DSP::IIR::OnePole onePole;
|
||||
DSP::IIR::Biquad biquad;
|
||||
};
|
||||
|
||||
struct Stream {
|
||||
auto reset(uint channels, double inputFrequency, double outputFrequency) -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
auto frequency() const -> double;
|
||||
auto setFrequency(double inputFrequency, maybe<double> outputFrequency = nothing) -> void;
|
||||
|
||||
auto addDCRemovalFilter() -> void;
|
||||
auto addLowPassFilter(double cutoffFrequency, Filter::Order order, uint passes = 1) -> void;
|
||||
auto addHighPassFilter(double cutoffFrequency, Filter::Order order, uint passes = 1) -> void;
|
||||
|
||||
auto pending() const -> uint;
|
||||
auto read(double samples[]) -> uint;
|
||||
auto write(const double samples[]) -> void;
|
||||
|
||||
template<typename... P> auto sample(P&&... p) -> void {
|
||||
double samples[sizeof...(P)] = {forward<P>(p)...};
|
||||
write(samples);
|
||||
}
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
struct Channel {
|
||||
vector<Filter> filters;
|
||||
vector<DSP::IIR::Biquad> nyquist;
|
||||
DSP::Resampler::Cubic resampler;
|
||||
};
|
||||
vector<Channel> channels;
|
||||
double inputFrequency;
|
||||
double outputFrequency;
|
||||
|
||||
friend class Audio;
|
||||
};
|
||||
|
||||
extern Audio audio;
|
||||
|
||||
}
|
||||
|
||||
#undef double
|
125
bsnes/emulator/audio/stream.cpp
Normal file
125
bsnes/emulator/audio/stream.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
auto Stream::reset(uint channelCount, double inputFrequency, double outputFrequency) -> void {
|
||||
channels.reset();
|
||||
channels.resize(channelCount);
|
||||
|
||||
for(auto& channel : channels) {
|
||||
channel.filters.reset();
|
||||
}
|
||||
|
||||
setFrequency(inputFrequency, outputFrequency);
|
||||
}
|
||||
|
||||
auto Stream::reset() -> void {
|
||||
for(auto& channel : channels) {
|
||||
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
|
||||
}
|
||||
}
|
||||
|
||||
auto Stream::frequency() const -> double {
|
||||
return inputFrequency;
|
||||
}
|
||||
|
||||
auto Stream::setFrequency(double inputFrequency, maybe<double> outputFrequency) -> void {
|
||||
this->inputFrequency = inputFrequency;
|
||||
if(outputFrequency) this->outputFrequency = outputFrequency();
|
||||
|
||||
for(auto& channel : channels) {
|
||||
channel.nyquist.reset();
|
||||
channel.resampler.reset(this->inputFrequency, this->outputFrequency);
|
||||
}
|
||||
|
||||
if(this->inputFrequency >= this->outputFrequency * 2) {
|
||||
//add a low-pass filter to prevent aliasing during resampling
|
||||
double cutoffFrequency = min(25000.0, this->outputFrequency / 2.0 - 2000.0);
|
||||
for(auto& channel : channels) {
|
||||
uint passes = 3;
|
||||
for(uint pass : range(passes)) {
|
||||
DSP::IIR::Biquad filter;
|
||||
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||
filter.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, this->inputFrequency, q);
|
||||
channel.nyquist.append(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Stream::addDCRemovalFilter() -> void {
|
||||
return; //todo: test to ensure this is desirable before enabling
|
||||
for(auto& channel : channels) {
|
||||
Filter filter{Filter::Mode::DCRemoval, Filter::Type::None, Filter::Order::None};
|
||||
channel.filters.append(filter);
|
||||
}
|
||||
}
|
||||
|
||||
auto Stream::addLowPassFilter(double cutoffFrequency, Filter::Order order, uint passes) -> void {
|
||||
for(auto& channel : channels) {
|
||||
for(uint pass : range(passes)) {
|
||||
if(order == Filter::Order::First) {
|
||||
Filter filter{Filter::Mode::OnePole, Filter::Type::LowPass, Filter::Order::First};
|
||||
filter.onePole.reset(DSP::IIR::OnePole::Type::LowPass, cutoffFrequency, inputFrequency);
|
||||
channel.filters.append(filter);
|
||||
}
|
||||
if(order == Filter::Order::Second) {
|
||||
Filter filter{Filter::Mode::Biquad, Filter::Type::LowPass, Filter::Order::Second};
|
||||
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||
filter.biquad.reset(DSP::IIR::Biquad::Type::LowPass, cutoffFrequency, inputFrequency, q);
|
||||
channel.filters.append(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Stream::addHighPassFilter(double cutoffFrequency, Filter::Order order, uint passes) -> void {
|
||||
for(auto& channel : channels) {
|
||||
for(uint pass : range(passes)) {
|
||||
if(order == Filter::Order::First) {
|
||||
Filter filter{Filter::Mode::OnePole, Filter::Type::HighPass, Filter::Order::First};
|
||||
filter.onePole.reset(DSP::IIR::OnePole::Type::HighPass, cutoffFrequency, inputFrequency);
|
||||
channel.filters.append(filter);
|
||||
}
|
||||
if(order == Filter::Order::Second) {
|
||||
Filter filter{Filter::Mode::Biquad, Filter::Type::HighPass, Filter::Order::Second};
|
||||
double q = DSP::IIR::Biquad::butterworth(passes * 2, pass);
|
||||
filter.biquad.reset(DSP::IIR::Biquad::Type::HighPass, cutoffFrequency, inputFrequency, q);
|
||||
channel.filters.append(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Stream::pending() const -> uint {
|
||||
if(!channels) return 0;
|
||||
return channels[0].resampler.pending();
|
||||
}
|
||||
|
||||
auto Stream::read(double samples[]) -> uint {
|
||||
for(uint c : range(channels.size())) samples[c] = channels[c].resampler.read();
|
||||
return channels.size();
|
||||
}
|
||||
|
||||
auto Stream::write(const double samples[]) -> void {
|
||||
for(auto c : range(channels.size())) {
|
||||
double sample = samples[c] + 1e-25; //constant offset used to suppress denormals
|
||||
for(auto& filter : channels[c].filters) {
|
||||
switch(filter.mode) {
|
||||
case Filter::Mode::DCRemoval: sample = filter.dcRemoval.process(sample); break;
|
||||
case Filter::Mode::OnePole: sample = filter.onePole.process(sample); break;
|
||||
case Filter::Mode::Biquad: sample = filter.biquad.process(sample); break;
|
||||
}
|
||||
}
|
||||
for(auto& filter : channels[c].nyquist) {
|
||||
sample = filter.process(sample);
|
||||
}
|
||||
channels[c].resampler.write(sample);
|
||||
}
|
||||
|
||||
audio.process();
|
||||
}
|
||||
|
||||
auto Stream::serialize(serializer& s) -> void {
|
||||
for(auto& channel : channels) {
|
||||
channel.resampler.serialize(s);
|
||||
}
|
||||
s.real(inputFrequency);
|
||||
s.real(outputFrequency);
|
||||
}
|
57
bsnes/emulator/cheat.hpp
Normal file
57
bsnes/emulator/cheat.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Cheat {
|
||||
struct Code {
|
||||
auto operator==(const Code& code) const -> bool {
|
||||
if(address != code.address) return false;
|
||||
if(data != code.data) return false;
|
||||
if((bool)compare != (bool)code.compare) return false;
|
||||
if(compare && code.compare && compare() != code.compare()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint address;
|
||||
uint data;
|
||||
maybe<uint> compare;
|
||||
bool enable;
|
||||
uint restore;
|
||||
};
|
||||
|
||||
explicit operator bool() const {
|
||||
return codes.size() > 0;
|
||||
}
|
||||
|
||||
auto reset() -> void {
|
||||
codes.reset();
|
||||
}
|
||||
|
||||
auto append(uint address, uint data, maybe<uint> compare = {}) -> void {
|
||||
codes.append({address, data, compare});
|
||||
}
|
||||
|
||||
auto assign(const vector<string>& list) -> void {
|
||||
reset();
|
||||
for(auto& entry : list) {
|
||||
for(auto code : entry.split("+")) {
|
||||
auto part = code.transform("=?", "//").split("/");
|
||||
if(part.size() == 2) append(part[0].hex(), part[1].hex());
|
||||
if(part.size() == 3) append(part[0].hex(), part[2].hex(), part[1].hex());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto find(uint address, uint compare) -> maybe<uint> {
|
||||
for(auto& code : codes) {
|
||||
if(code.address == address && (!code.compare || code.compare() == compare)) {
|
||||
return code.data;
|
||||
}
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
vector<Code> codes;
|
||||
};
|
||||
|
||||
}
|
8
bsnes/emulator/emulator.cpp
Normal file
8
bsnes/emulator/emulator.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/audio/audio.cpp>
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
Platform* platform = nullptr;
|
||||
|
||||
}
|
58
bsnes/emulator/emulator.hpp
Normal file
58
bsnes/emulator/emulator.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/adaptive-array.hpp>
|
||||
#include <nall/any.hpp>
|
||||
#include <nall/chrono.hpp>
|
||||
#include <nall/dl.hpp>
|
||||
#include <nall/endian.hpp>
|
||||
#include <nall/image.hpp>
|
||||
#include <nall/literals.hpp>
|
||||
#include <nall/random.hpp>
|
||||
#include <nall/serializer.hpp>
|
||||
#include <nall/shared-pointer.hpp>
|
||||
#include <nall/string.hpp>
|
||||
#include <nall/traits.hpp>
|
||||
#include <nall/unique-pointer.hpp>
|
||||
#include <nall/vector.hpp>
|
||||
#include <nall/vfs.hpp>
|
||||
#include <nall/hash/crc32.hpp>
|
||||
#include <nall/hash/sha256.hpp>
|
||||
using namespace nall;
|
||||
|
||||
#include <emulator/types.hpp>
|
||||
#include <emulator/memory/readable.hpp>
|
||||
#include <emulator/memory/writable.hpp>
|
||||
#include <emulator/audio/audio.hpp>
|
||||
|
||||
namespace Emulator {
|
||||
static const string Name = "bsnes";
|
||||
static const string Version = "114";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "https://byuu.org";
|
||||
|
||||
//incremented only when serialization format changes
|
||||
static const string SerializerVersion = "112";
|
||||
|
||||
namespace Constants {
|
||||
namespace Colorburst {
|
||||
static constexpr double NTSC = 315.0 / 88.0 * 1'000'000.0;
|
||||
static constexpr double PAL = 283.75 * 15'625.0 + 25.0;
|
||||
}
|
||||
}
|
||||
|
||||
//nall/vfs shorthand constants for open(), load()
|
||||
namespace File {
|
||||
static const auto Read = vfs::file::mode::read;
|
||||
static const auto Write = vfs::file::mode::write;
|
||||
static const auto Optional = false;
|
||||
static const auto Required = true;
|
||||
};
|
||||
}
|
||||
|
||||
#include "platform.hpp"
|
||||
#include "interface.hpp"
|
||||
#include "game.hpp"
|
112
bsnes/emulator/game.hpp
Normal file
112
bsnes/emulator/game.hpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Game {
|
||||
struct Memory;
|
||||
struct Oscillator;
|
||||
|
||||
inline auto load(string_view) -> void;
|
||||
inline auto memory(Markup::Node) -> maybe<Memory>;
|
||||
inline auto oscillator(natural = 0) -> maybe<Oscillator>;
|
||||
|
||||
struct Memory {
|
||||
Memory() = default;
|
||||
inline Memory(Markup::Node);
|
||||
explicit operator bool() const { return (bool)type; }
|
||||
inline auto name() const -> string;
|
||||
|
||||
string type;
|
||||
natural size;
|
||||
string content;
|
||||
string manufacturer;
|
||||
string architecture;
|
||||
string identifier;
|
||||
boolean nonVolatile;
|
||||
};
|
||||
|
||||
struct Oscillator {
|
||||
Oscillator() = default;
|
||||
inline Oscillator(Markup::Node);
|
||||
explicit operator bool() const { return frequency; }
|
||||
|
||||
natural frequency;
|
||||
};
|
||||
|
||||
Markup::Node document;
|
||||
string sha256;
|
||||
string label;
|
||||
string name;
|
||||
string title;
|
||||
string region;
|
||||
string revision;
|
||||
string board;
|
||||
vector<Memory> memoryList;
|
||||
vector<Oscillator> oscillatorList;
|
||||
};
|
||||
|
||||
auto Game::load(string_view text) -> void {
|
||||
document = BML::unserialize(text);
|
||||
|
||||
sha256 = document["game/sha256"].text();
|
||||
label = document["game/label"].text();
|
||||
name = document["game/name"].text();
|
||||
title = document["game/title"].text();
|
||||
region = document["game/region"].text();
|
||||
revision = document["game/revision"].text();
|
||||
board = document["game/board"].text();
|
||||
|
||||
for(auto node : document.find("game/board/memory")) {
|
||||
memoryList.append(Memory{node});
|
||||
}
|
||||
|
||||
for(auto node : document.find("game/board/oscillator")) {
|
||||
oscillatorList.append(Oscillator{node});
|
||||
}
|
||||
}
|
||||
|
||||
auto Game::memory(Markup::Node node) -> maybe<Memory> {
|
||||
if(!node) return nothing;
|
||||
for(auto& memory : memoryList) {
|
||||
auto type = node["type"].text();
|
||||
auto size = node["size"].natural();
|
||||
auto content = node["content"].text();
|
||||
auto manufacturer = node["manufacturer"].text();
|
||||
auto architecture = node["architecture"].text();
|
||||
auto identifier = node["identifier"].text();
|
||||
if(type && type != memory.type) continue;
|
||||
if(size && size != memory.size) continue;
|
||||
if(content && content != memory.content) continue;
|
||||
if(manufacturer && manufacturer != memory.manufacturer) continue;
|
||||
if(architecture && architecture != memory.architecture) continue;
|
||||
if(identifier && identifier != memory.identifier) continue;
|
||||
return memory;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
auto Game::oscillator(natural index) -> maybe<Oscillator> {
|
||||
if(index < oscillatorList.size()) return oscillatorList[index];
|
||||
return nothing;
|
||||
}
|
||||
|
||||
Game::Memory::Memory(Markup::Node node) {
|
||||
type = node["type"].text();
|
||||
size = node["size"].natural();
|
||||
content = node["content"].text();
|
||||
manufacturer = node["manufacturer"].text();
|
||||
architecture = node["architecture"].text();
|
||||
identifier = node["identifier"].text();
|
||||
nonVolatile = !(bool)node["volatile"];
|
||||
}
|
||||
|
||||
auto Game::Memory::name() const -> string {
|
||||
if(architecture) return string{architecture, ".", content, ".", type}.downcase();
|
||||
return string{content, ".", type}.downcase();
|
||||
}
|
||||
|
||||
Game::Oscillator::Oscillator(Markup::Node node) {
|
||||
frequency = node["frequency"].natural();
|
||||
}
|
||||
|
||||
}
|
109
bsnes/emulator/interface.hpp
Normal file
109
bsnes/emulator/interface.hpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Interface {
|
||||
struct Information {
|
||||
string manufacturer;
|
||||
string name;
|
||||
string extension;
|
||||
bool resettable = false;
|
||||
};
|
||||
|
||||
struct Display {
|
||||
struct Type { enum : uint {
|
||||
CRT,
|
||||
LCD,
|
||||
};};
|
||||
uint id = 0;
|
||||
string name;
|
||||
uint type = 0;
|
||||
uint colors = 0;
|
||||
uint width = 0;
|
||||
uint height = 0;
|
||||
uint internalWidth = 0;
|
||||
uint internalHeight = 0;
|
||||
double aspectCorrection = 0;
|
||||
};
|
||||
|
||||
struct Port {
|
||||
uint id;
|
||||
string name;
|
||||
};
|
||||
|
||||
struct Device {
|
||||
uint id;
|
||||
string name;
|
||||
};
|
||||
|
||||
struct Input {
|
||||
struct Type { enum : uint {
|
||||
Hat,
|
||||
Button,
|
||||
Trigger,
|
||||
Control,
|
||||
Axis,
|
||||
Rumble,
|
||||
};};
|
||||
|
||||
uint type;
|
||||
string name;
|
||||
};
|
||||
|
||||
//information
|
||||
virtual auto information() -> Information { return {}; }
|
||||
|
||||
virtual auto display() -> Display { return {}; }
|
||||
virtual auto color(uint32 color) -> uint64 { return 0; }
|
||||
|
||||
//game interface
|
||||
virtual auto loaded() -> bool { return false; }
|
||||
virtual auto hashes() -> vector<string> { return {}; }
|
||||
virtual auto manifests() -> vector<string> { return {}; }
|
||||
virtual auto titles() -> vector<string> { return {}; }
|
||||
virtual auto title() -> string { return {}; }
|
||||
virtual auto load() -> bool { return false; }
|
||||
virtual auto save() -> void {}
|
||||
virtual auto unload() -> void {}
|
||||
|
||||
//system interface
|
||||
virtual auto ports() -> vector<Port> { return {}; }
|
||||
virtual auto devices(uint port) -> vector<Device> { return {}; }
|
||||
virtual auto inputs(uint device) -> vector<Input> { return {}; }
|
||||
virtual auto connected(uint port) -> uint { return 0; }
|
||||
virtual auto connect(uint port, uint device) -> void {}
|
||||
virtual auto power() -> void {}
|
||||
virtual auto reset() -> void {}
|
||||
virtual auto run() -> void {}
|
||||
|
||||
//time functions
|
||||
virtual auto rtc() -> bool { return false; }
|
||||
virtual auto synchronize(uint64 timestamp = 0) -> void {}
|
||||
|
||||
//state functions
|
||||
virtual auto serialize(bool synchronize = true) -> serializer { return {}; }
|
||||
virtual auto unserialize(serializer&) -> bool { return false; }
|
||||
|
||||
//cheat functions
|
||||
virtual auto read(uint24 address) -> uint8 { return 0; }
|
||||
virtual auto cheats(const vector<string>& = {}) -> void {}
|
||||
|
||||
//configuration
|
||||
virtual auto configuration() -> string { return {}; }
|
||||
virtual auto configuration(string name) -> string { return {}; }
|
||||
virtual auto configure(string configuration = "") -> bool { return false; }
|
||||
virtual auto configure(string name, string value) -> bool { return false; }
|
||||
|
||||
//settings
|
||||
virtual auto cap(const string& name) -> bool { return false; }
|
||||
virtual auto get(const string& name) -> any { return {}; }
|
||||
virtual auto set(const string& name, const any& value) -> bool { return false; }
|
||||
|
||||
virtual auto frameSkip() -> uint { return 0; }
|
||||
virtual auto setFrameSkip(uint frameSkip) -> void {}
|
||||
|
||||
virtual auto runAhead() -> bool { return false; }
|
||||
virtual auto setRunAhead(bool runAhead) -> void {}
|
||||
};
|
||||
|
||||
}
|
30
bsnes/emulator/memory/memory.hpp
Normal file
30
bsnes/emulator/memory/memory.hpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
namespace Emulator::Memory {
|
||||
|
||||
inline auto mirror(uint address, uint size) -> uint {
|
||||
if(size == 0) return 0;
|
||||
uint base = 0;
|
||||
uint mask = 1 << 31;
|
||||
while(address >= size) {
|
||||
while(!(address & mask)) mask >>= 1;
|
||||
address -= mask;
|
||||
if(size > mask) {
|
||||
size -= mask;
|
||||
base += mask;
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
return base + address;
|
||||
}
|
||||
|
||||
inline auto reduce(uint address, uint mask) -> uint {
|
||||
while(mask) {
|
||||
uint bits = (mask & -mask) - 1;
|
||||
address = address >> 1 & ~bits | address & bits;
|
||||
mask = (mask & mask - 1) >> 1;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
}
|
63
bsnes/emulator/memory/readable.hpp
Normal file
63
bsnes/emulator/memory/readable.hpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#pragma once
|
||||
|
||||
#include <emulator/memory/memory.hpp>
|
||||
|
||||
namespace Emulator::Memory {
|
||||
|
||||
template<typename T>
|
||||
struct Readable {
|
||||
~Readable() { reset(); }
|
||||
|
||||
inline auto reset() -> void {
|
||||
delete[] self.data;
|
||||
self.data = nullptr;
|
||||
self.size = 0;
|
||||
self.mask = 0;
|
||||
}
|
||||
|
||||
inline auto allocate(uint size, T fill = ~0ull) -> void {
|
||||
if(!size) return reset();
|
||||
delete[] self.data;
|
||||
self.size = size;
|
||||
self.mask = bit::round(self.size) - 1;
|
||||
self.data = new T[self.mask + 1];
|
||||
memory::fill<T>(self.data, self.mask + 1, fill);
|
||||
}
|
||||
|
||||
inline auto load(shared_pointer<vfs::file> fp) -> void {
|
||||
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
|
||||
for(uint address = self.size; address <= self.mask; address++) {
|
||||
self.data[address] = self.data[mirror(address, self.size)];
|
||||
}
|
||||
}
|
||||
|
||||
inline auto save(shared_pointer<vfs::file> fp) -> void {
|
||||
fp->write(self.data, self.size * sizeof(T));
|
||||
}
|
||||
|
||||
explicit operator bool() const { return (bool)self.data; }
|
||||
inline auto data() const -> const T* { return self.data; }
|
||||
inline auto size() const -> uint { return self.size; }
|
||||
inline auto mask() const -> uint { return self.mask; }
|
||||
|
||||
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
|
||||
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
|
||||
inline auto write(uint address, T data) const -> void {}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
const uint size = self.size;
|
||||
s.integer(self.size);
|
||||
s.integer(self.mask);
|
||||
if(self.size != size) allocate(self.size);
|
||||
s.array(self.data, self.size);
|
||||
}
|
||||
|
||||
private:
|
||||
struct {
|
||||
T* data = nullptr;
|
||||
uint size = 0;
|
||||
uint mask = 0;
|
||||
} self;
|
||||
};
|
||||
|
||||
}
|
65
bsnes/emulator/memory/writable.hpp
Normal file
65
bsnes/emulator/memory/writable.hpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#pragma once
|
||||
|
||||
#include <emulator/memory/memory.hpp>
|
||||
|
||||
namespace Emulator::Memory {
|
||||
|
||||
template<typename T>
|
||||
struct Writable {
|
||||
~Writable() { reset(); }
|
||||
|
||||
inline auto reset() -> void {
|
||||
delete[] self.data;
|
||||
self.data = nullptr;
|
||||
self.size = 0;
|
||||
self.mask = 0;
|
||||
}
|
||||
|
||||
inline auto allocate(uint size, T fill = ~0ull) -> void {
|
||||
if(!size) return reset();
|
||||
delete[] self.data;
|
||||
self.size = size;
|
||||
self.mask = bit::round(self.size) - 1;
|
||||
self.data = new T[self.mask + 1];
|
||||
memory::fill<T>(self.data, self.mask + 1, fill);
|
||||
}
|
||||
|
||||
inline auto load(shared_pointer<vfs::file> fp) -> void {
|
||||
fp->read(self.data, min(fp->size(), self.size * sizeof(T)));
|
||||
for(uint address = self.size; address <= self.mask; address++) {
|
||||
self.data[address] = self.data[mirror(address, self.size)];
|
||||
}
|
||||
}
|
||||
|
||||
inline auto save(shared_pointer<vfs::file> fp) -> void {
|
||||
fp->write(self.data, self.size * sizeof(T));
|
||||
}
|
||||
|
||||
explicit operator bool() const { return (bool)self.data; }
|
||||
inline auto data() -> T* { return self.data; }
|
||||
inline auto data() const -> const T* { return self.data; }
|
||||
inline auto size() const -> uint { return self.size; }
|
||||
inline auto mask() const -> uint { return self.mask; }
|
||||
|
||||
inline auto operator[](uint address) -> T& { return self.data[address & self.mask]; }
|
||||
inline auto operator[](uint address) const -> T { return self.data[address & self.mask]; }
|
||||
inline auto read(uint address) const -> T { return self.data[address & self.mask]; }
|
||||
inline auto write(uint address, T data) -> void { self.data[address & self.mask] = data; }
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
const uint size = self.size;
|
||||
s.integer(self.size);
|
||||
s.integer(self.mask);
|
||||
if(self.size != size) allocate(self.size);
|
||||
s.array(self.data, self.size);
|
||||
}
|
||||
|
||||
private:
|
||||
struct {
|
||||
T* data = nullptr;
|
||||
uint size = 0;
|
||||
uint mask = 0;
|
||||
} self;
|
||||
};
|
||||
|
||||
}
|
29
bsnes/emulator/platform.hpp
Normal file
29
bsnes/emulator/platform.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Platform {
|
||||
struct Load {
|
||||
Load() = default;
|
||||
Load(uint pathID, string option = "") : valid(true), pathID(pathID), option(option) {}
|
||||
explicit operator bool() const { return valid; }
|
||||
|
||||
bool valid = false;
|
||||
uint pathID = 0;
|
||||
string option;
|
||||
};
|
||||
|
||||
virtual auto path(uint id) -> string { return ""; }
|
||||
virtual auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> shared_pointer<vfs::file> { return {}; }
|
||||
virtual auto load(uint id, string name, string type, vector<string> options = {}) -> Load { return {}; }
|
||||
virtual auto videoFrame(const uint16* data, uint pitch, uint width, uint height, uint scale) -> void {}
|
||||
virtual auto audioFrame(const float* samples, uint channels) -> void {}
|
||||
virtual auto inputPoll(uint port, uint device, uint input) -> int16 { return 0; }
|
||||
virtual auto inputRumble(uint port, uint device, uint input, bool enable) -> void {}
|
||||
virtual auto dipSettings(Markup::Node node) -> uint { return 0; }
|
||||
virtual auto notify(string text) -> void {}
|
||||
};
|
||||
|
||||
extern Platform* platform;
|
||||
|
||||
}
|
96
bsnes/emulator/random.hpp
Normal file
96
bsnes/emulator/random.hpp
Normal file
@@ -0,0 +1,96 @@
|
||||
#pragma once
|
||||
|
||||
namespace Emulator {
|
||||
|
||||
struct Random {
|
||||
enum class Entropy : uint { None, Low, High };
|
||||
|
||||
auto operator()() -> uint64 {
|
||||
return random();
|
||||
}
|
||||
|
||||
auto entropy(Entropy entropy) -> void {
|
||||
_entropy = entropy;
|
||||
seed();
|
||||
}
|
||||
|
||||
auto seed(maybe<uint32> seed = nothing, maybe<uint32> sequence = nothing) -> void {
|
||||
if(!seed) seed = (uint32)clock();
|
||||
if(!sequence) sequence = 0;
|
||||
|
||||
_state = 0;
|
||||
_increment = sequence() << 1 | 1;
|
||||
step();
|
||||
_state += seed();
|
||||
step();
|
||||
}
|
||||
|
||||
auto random() -> uint64 {
|
||||
if(_entropy == Entropy::None) return 0;
|
||||
return (uint64)step() << 32 | (uint64)step() << 0;
|
||||
}
|
||||
|
||||
auto bias(uint64 bias) -> uint64 {
|
||||
if(_entropy == Entropy::None) return bias;
|
||||
return random();
|
||||
}
|
||||
|
||||
auto bound(uint64 bound) -> uint64 {
|
||||
uint64 threshold = -bound % bound;
|
||||
while(true) {
|
||||
uint64 result = random();
|
||||
if(result >= threshold) return result % bound;
|
||||
}
|
||||
}
|
||||
|
||||
auto array(uint8* data, uint32 size) -> void {
|
||||
if(_entropy == Entropy::None) {
|
||||
memory::fill(data, size);
|
||||
return;
|
||||
}
|
||||
|
||||
if(_entropy == Entropy::High) {
|
||||
for(uint32 address : range(size)) {
|
||||
data[address] = random();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//Entropy::Low
|
||||
uint lobit = random() & 3;
|
||||
uint hibit = (lobit + 8 + (random() & 3)) & 15;
|
||||
uint lovalue = random() & 255;
|
||||
uint hivalue = random() & 255;
|
||||
if((random() & 3) == 0) lovalue = 0;
|
||||
if((random() & 1) == 0) hivalue = ~lovalue;
|
||||
|
||||
for(uint32 address : range(size)) {
|
||||
uint8 value = (address & 1ull << lobit) ? lovalue : hivalue;
|
||||
if((address & 1ull << hibit)) value = ~value;
|
||||
if((random() & 511) == 0) value ^= 1 << (random() & 7);
|
||||
if((random() & 2047) == 0) value ^= 1 << (random() & 7);
|
||||
data[address] = value;
|
||||
}
|
||||
}
|
||||
|
||||
auto serialize(serializer& s) -> void {
|
||||
s.integer((uint&)_entropy);
|
||||
s.integer(_state);
|
||||
s.integer(_increment);
|
||||
}
|
||||
|
||||
private:
|
||||
auto step() -> uint32 {
|
||||
uint64 state = _state;
|
||||
_state = state * 6364136223846793005ull + _increment;
|
||||
uint32 xorshift = (state >> 18 ^ state) >> 27;
|
||||
uint32 rotate = state >> 59;
|
||||
return xorshift >> rotate | xorshift << (-rotate & 31);
|
||||
}
|
||||
|
||||
Entropy _entropy = Entropy::High;
|
||||
uint64 _state;
|
||||
uint64 _increment;
|
||||
};
|
||||
|
||||
}
|
72
bsnes/emulator/types.hpp
Normal file
72
bsnes/emulator/types.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
#pragma once
|
||||
|
||||
using int1 = nall::Integer< 1>;
|
||||
using int2 = nall::Integer< 2>;
|
||||
using int3 = nall::Integer< 3>;
|
||||
using int4 = nall::Integer< 4>;
|
||||
using int5 = nall::Integer< 5>;
|
||||
using int6 = nall::Integer< 6>;
|
||||
using int7 = nall::Integer< 7>;
|
||||
using int8 = int8_t;
|
||||
using int9 = nall::Integer< 9>;
|
||||
using int10 = nall::Integer<10>;
|
||||
using int11 = nall::Integer<11>;
|
||||
using int12 = nall::Integer<12>;
|
||||
using int13 = nall::Integer<13>;
|
||||
using int14 = nall::Integer<14>;
|
||||
using int15 = nall::Integer<15>;
|
||||
using int16 = int16_t;
|
||||
using int17 = nall::Integer<17>;
|
||||
using int18 = nall::Integer<18>;
|
||||
using int19 = nall::Integer<19>;
|
||||
using int20 = nall::Integer<20>;
|
||||
using int21 = nall::Integer<21>;
|
||||
using int22 = nall::Integer<22>;
|
||||
using int23 = nall::Integer<23>;
|
||||
using int24 = nall::Integer<24>;
|
||||
using int25 = nall::Integer<25>;
|
||||
using int26 = nall::Integer<26>;
|
||||
using int27 = nall::Integer<27>;
|
||||
using int28 = nall::Integer<28>;
|
||||
using int29 = nall::Integer<29>;
|
||||
using int30 = nall::Integer<30>;
|
||||
using int31 = nall::Integer<31>;
|
||||
using int32 = int32_t;
|
||||
using int48 = nall::Integer<48>; //Cx4
|
||||
using int64 = int64_t;
|
||||
|
||||
using uint1 = nall::Natural< 1>;
|
||||
using uint2 = nall::Natural< 2>;
|
||||
using uint3 = nall::Natural< 3>;
|
||||
using uint4 = nall::Natural< 4>;
|
||||
using uint5 = nall::Natural< 5>;
|
||||
using uint6 = nall::Natural< 6>;
|
||||
using uint7 = nall::Natural< 7>;
|
||||
using uint8 = uint8_t;
|
||||
using uint9 = nall::Natural< 9>;
|
||||
using uint10 = nall::Natural<10>;
|
||||
using uint11 = nall::Natural<11>;
|
||||
using uint12 = nall::Natural<12>;
|
||||
using uint13 = nall::Natural<13>;
|
||||
using uint14 = nall::Natural<14>;
|
||||
using uint15 = nall::Natural<15>;
|
||||
using uint16 = uint16_t;
|
||||
using uint17 = nall::Natural<17>;
|
||||
using uint18 = nall::Natural<18>;
|
||||
using uint19 = nall::Natural<19>;
|
||||
using uint20 = nall::Natural<20>;
|
||||
using uint21 = nall::Natural<21>;
|
||||
using uint22 = nall::Natural<22>;
|
||||
using uint23 = nall::Natural<23>;
|
||||
using uint24 = nall::Natural<24>;
|
||||
using uint25 = nall::Natural<25>;
|
||||
using uint26 = nall::Natural<26>;
|
||||
using uint27 = nall::Natural<27>;
|
||||
using uint28 = nall::Natural<28>;
|
||||
using uint29 = nall::Natural<29>;
|
||||
using uint30 = nall::Natural<30>;
|
||||
using uint31 = nall::Natural<31>;
|
||||
using uint32 = uint32_t;
|
||||
using uint40 = nall::Natural<40>; //SA1
|
||||
using uint48 = nall::Natural<48>; //Cx4
|
||||
using uint64 = uint64_t;
|
25
bsnes/filter/2xsai.cpp
Normal file
25
bsnes/filter/2xsai.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace Filter::_2xSaI {
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width *= 2;
|
||||
height *= 2;
|
||||
}
|
||||
|
||||
uint32_t temp[512 * 480];
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
const uint16_t *line_in = (const uint16_t*)(((const uint8_t*)input) + pitch * y);
|
||||
uint32_t *line_out = temp + y * width;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
line_out[x] = colortable[line_in[x]];
|
||||
}
|
||||
}
|
||||
|
||||
_2xSaI32((unsigned char*)temp, width * sizeof(uint32_t), 0, (unsigned char*)output, outpitch, width, height);
|
||||
}
|
||||
|
||||
}
|
25
bsnes/filter/filter.cpp
Normal file
25
bsnes/filter/filter.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <emulator/emulator.hpp>
|
||||
|
||||
#undef register
|
||||
#define register
|
||||
#include "sai/sai.cpp"
|
||||
|
||||
uint32_t* colortable;
|
||||
#include "snes_ntsc/snes_ntsc.h"
|
||||
#include "snes_ntsc/snes_ntsc.c"
|
||||
|
||||
#include "none.cpp"
|
||||
#include "scanlines-light.cpp"
|
||||
#include "scanlines-dark.cpp"
|
||||
#include "scanlines-black.cpp"
|
||||
#include "pixellate2x.cpp"
|
||||
#include "scale2x.cpp"
|
||||
#include "2xsai.cpp"
|
||||
#include "super-2xsai.cpp"
|
||||
#include "super-eagle.cpp"
|
||||
#include "lq2x.cpp"
|
||||
#include "hq2x.cpp"
|
||||
#include "ntsc-rf.cpp"
|
||||
#include "ntsc-composite.cpp"
|
||||
#include "ntsc-svideo.cpp"
|
||||
#include "ntsc-rgb.cpp"
|
129
bsnes/filter/filter.hpp
Normal file
129
bsnes/filter/filter.hpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include <emulator/emulator.hpp>
|
||||
|
||||
namespace Filter {
|
||||
using Size = auto (*)(uint& width, uint& height) -> void;
|
||||
using Render = auto (*)(uint32_t* palette, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::None {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::ScanlinesLight {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::ScanlinesDark {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::ScanlinesBlack {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::Pixellate2x {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::Scale2x {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::_2xSaI {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::Super2xSaI {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::SuperEagle {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::LQ2x {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::HQ2x {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::NTSC_RF {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::NTSC_Composite {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::NTSC_SVideo {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Filter::NTSC_RGB {
|
||||
auto size(uint& width, uint& height) -> void;
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void;
|
||||
}
|
200
bsnes/filter/hq2x.cpp
Normal file
200
bsnes/filter/hq2x.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
namespace Filter::HQ2x {
|
||||
|
||||
enum {
|
||||
diff_offset = (0x440 << 21) + (0x207 << 11) + 0x407,
|
||||
diff_mask = (0x380 << 21) + (0x1f0 << 11) + 0x3f0,
|
||||
};
|
||||
|
||||
uint32_t *yuvTable;
|
||||
uint8_t rotate[256];
|
||||
|
||||
const uint8_t hqTable[256] = {
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
|
||||
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 12, 12, 5, 3, 1, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
|
||||
4, 4, 6, 18, 4, 4, 6, 18, 5, 3, 16, 12, 5, 3, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 12, 12, 5, 19, 16, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 19, 1, 12, 5, 19, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 18, 5, 3, 16, 12, 5, 19, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 15, 12, 5, 3, 17, 13,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 17, 13, 5, 3, 16, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 13, 5, 3, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 16, 13,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 12,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 16, 12, 5, 3, 1, 14,
|
||||
4, 4, 6, 2, 4, 4, 6, 2, 5, 3, 1, 12, 5, 3, 1, 14,
|
||||
};
|
||||
|
||||
static void initialize() {
|
||||
static bool initialized = false;
|
||||
if(initialized == true) return;
|
||||
initialized = true;
|
||||
|
||||
yuvTable = new uint32_t[32768];
|
||||
|
||||
for(unsigned i = 0; i < 32768; i++) {
|
||||
uint8_t R = (i >> 0) & 31;
|
||||
uint8_t G = (i >> 5) & 31;
|
||||
uint8_t B = (i >> 10) & 31;
|
||||
|
||||
//bgr555->bgr888
|
||||
double r = (R << 3) | (R >> 2);
|
||||
double g = (G << 3) | (G >> 2);
|
||||
double b = (B << 3) | (B >> 2);
|
||||
|
||||
//bgr888->yuv
|
||||
double y = (r + g + b) * (0.25f * (63.5f / 48.0f));
|
||||
double u = ((r - b) * 0.25f + 128.0f) * (7.5f / 7.0f);
|
||||
double v = ((g * 2.0f - r - b) * 0.125f + 128.0f) * (7.5f / 6.0f);
|
||||
|
||||
yuvTable[i] = ((unsigned)y << 21) + ((unsigned)u << 11) + ((unsigned)v);
|
||||
}
|
||||
|
||||
//counter-clockwise rotation table; one revolution:
|
||||
//123 369 12346789
|
||||
//4.6 -> 2.8 =
|
||||
//789 147 36928147
|
||||
for(unsigned n = 0; n < 256; n++) {
|
||||
rotate[n] = ((n >> 2) & 0x11) | ((n << 2) & 0x88)
|
||||
| ((n & 0x01) << 5) | ((n & 0x08) << 3)
|
||||
| ((n & 0x10) >> 3) | ((n & 0x80) >> 5);
|
||||
}
|
||||
}
|
||||
|
||||
static void terminate() {
|
||||
delete[] yuvTable;
|
||||
}
|
||||
|
||||
static bool same(uint16_t x, uint16_t y) {
|
||||
return !((yuvTable[x] - yuvTable[y] + diff_offset) & diff_mask);
|
||||
}
|
||||
|
||||
static bool diff(uint32_t x, uint16_t y) {
|
||||
return ((x - yuvTable[y]) & diff_mask);
|
||||
}
|
||||
|
||||
static void grow(uint32_t &n) { n |= n << 16; n &= 0x03e07c1f; }
|
||||
static uint16_t pack(uint32_t n) { n &= 0x03e07c1f; return n | (n >> 16); }
|
||||
|
||||
static uint16_t blend1(uint32_t A, uint32_t B) {
|
||||
grow(A); grow(B);
|
||||
return pack((A * 3 + B) >> 2);
|
||||
}
|
||||
|
||||
static uint16_t blend2(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 2 + B + C) >> 2);
|
||||
}
|
||||
|
||||
static uint16_t blend3(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 5 + B * 2 + C) >> 3);
|
||||
}
|
||||
|
||||
static uint16_t blend4(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 6 + B + C) >> 3);
|
||||
}
|
||||
|
||||
static uint16_t blend5(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 2 + (B + C) * 3) >> 3);
|
||||
}
|
||||
|
||||
static uint16_t blend6(uint32_t A, uint32_t B, uint32_t C) {
|
||||
grow(A); grow(B); grow(C);
|
||||
return pack((A * 14 + B + C) >> 4);
|
||||
}
|
||||
|
||||
static uint16_t blend(unsigned rule, uint16_t E, uint16_t A, uint16_t B, uint16_t D, uint16_t F, uint16_t H) {
|
||||
switch(rule) { default:
|
||||
case 0: return E;
|
||||
case 1: return blend1(E, A);
|
||||
case 2: return blend1(E, D);
|
||||
case 3: return blend1(E, B);
|
||||
case 4: return blend2(E, D, B);
|
||||
case 5: return blend2(E, A, B);
|
||||
case 6: return blend2(E, A, D);
|
||||
case 7: return blend3(E, B, D);
|
||||
case 8: return blend3(E, D, B);
|
||||
case 9: return blend4(E, D, B);
|
||||
case 10: return blend5(E, D, B);
|
||||
case 11: return blend6(E, D, B);
|
||||
case 12: return same(B, D) ? blend2(E, D, B) : E;
|
||||
case 13: return same(B, D) ? blend5(E, D, B) : E;
|
||||
case 14: return same(B, D) ? blend6(E, D, B) : E;
|
||||
case 15: return same(B, D) ? blend2(E, D, B) : blend1(E, A);
|
||||
case 16: return same(B, D) ? blend4(E, D, B) : blend1(E, A);
|
||||
case 17: return same(B, D) ? blend5(E, D, B) : blend1(E, A);
|
||||
case 18: return same(B, F) ? blend3(E, B, D) : blend1(E, D);
|
||||
case 19: return same(D, H) ? blend3(E, D, B) : blend1(E, B);
|
||||
}
|
||||
}
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width *= 2;
|
||||
height *= 2;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
initialize();
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
for(uint y = 0; y < height; y++) {
|
||||
const uint16_t* in = input + y * pitch;
|
||||
uint32_t* out0 = output + y * outpitch * 2;
|
||||
uint32_t* out1 = output + y * outpitch * 2 + outpitch;
|
||||
|
||||
int prevline = (y == 0 ? 0 : pitch);
|
||||
int nextline = (y == height - 1 ? 0 : pitch);
|
||||
|
||||
in++;
|
||||
*out0++ = 0; *out0++ = 0;
|
||||
*out1++ = 0; *out1++ = 0;
|
||||
|
||||
for(unsigned x = 1; x < width - 1; x++) {
|
||||
uint16_t A = *(in - prevline - 1);
|
||||
uint16_t B = *(in - prevline + 0);
|
||||
uint16_t C = *(in - prevline + 1);
|
||||
uint16_t D = *(in - 1);
|
||||
uint16_t E = *(in + 0);
|
||||
uint16_t F = *(in + 1);
|
||||
uint16_t G = *(in + nextline - 1);
|
||||
uint16_t H = *(in + nextline + 0);
|
||||
uint16_t I = *(in + nextline + 1);
|
||||
uint32_t e = yuvTable[E] + diff_offset;
|
||||
|
||||
uint8_t pattern;
|
||||
pattern = diff(e, A) << 0;
|
||||
pattern |= diff(e, B) << 1;
|
||||
pattern |= diff(e, C) << 2;
|
||||
pattern |= diff(e, D) << 3;
|
||||
pattern |= diff(e, F) << 4;
|
||||
pattern |= diff(e, G) << 5;
|
||||
pattern |= diff(e, H) << 6;
|
||||
pattern |= diff(e, I) << 7;
|
||||
|
||||
*(out0 + 0) = colortable[blend(hqTable[pattern], E, A, B, D, F, H)]; pattern = rotate[pattern];
|
||||
*(out0 + 1) = colortable[blend(hqTable[pattern], E, C, F, B, H, D)]; pattern = rotate[pattern];
|
||||
*(out1 + 1) = colortable[blend(hqTable[pattern], E, I, H, F, D, B)]; pattern = rotate[pattern];
|
||||
*(out1 + 0) = colortable[blend(hqTable[pattern], E, G, D, H, B, F)];
|
||||
|
||||
in++;
|
||||
out0 += 2;
|
||||
out1 += 2;
|
||||
}
|
||||
|
||||
in++;
|
||||
*out0++ = 0; *out0++ = 0;
|
||||
*out1++ = 0; *out1++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
46
bsnes/filter/lq2x.cpp
Normal file
46
bsnes/filter/lq2x.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace Filter::LQ2x {
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width *= 2;
|
||||
height *= 2;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
for(uint y = 0; y < height; y++) {
|
||||
const uint16_t* in = input + y * pitch;
|
||||
uint32_t* out0 = output + y * outpitch * 2;
|
||||
uint32_t* out1 = output + y * outpitch * 2 + outpitch;
|
||||
|
||||
int prevline = (y == 0 ? 0 : pitch);
|
||||
int nextline = (y == height - 1 ? 0 : pitch);
|
||||
|
||||
for(uint x = 0; x < width; x++) {
|
||||
uint16_t A = *(in - prevline);
|
||||
uint16_t B = (x > 0) ? *(in - 1) : *in;
|
||||
uint16_t C = *in;
|
||||
uint16_t D = (x < width - 1) ? *(in + 1) : *in;
|
||||
uint16_t E = *(in++ + nextline);
|
||||
uint32_t c = colortable[C];
|
||||
|
||||
if(A != E && B != D) {
|
||||
*out0++ = (A == B ? colortable[C + A - ((C ^ A) & 0x0421) >> 1] : c);
|
||||
*out0++ = (A == D ? colortable[C + A - ((C ^ A) & 0x0421) >> 1] : c);
|
||||
*out1++ = (E == B ? colortable[C + E - ((C ^ E) & 0x0421) >> 1] : c);
|
||||
*out1++ = (E == D ? colortable[C + E - ((C ^ E) & 0x0421) >> 1] : c);
|
||||
} else {
|
||||
*out0++ = c;
|
||||
*out0++ = c;
|
||||
*out1++ = c;
|
||||
*out1++ = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
24
bsnes/filter/none.cpp
Normal file
24
bsnes/filter/none.cpp
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Filter::None {
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width = width;
|
||||
height = height;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
for(uint y = 0; y < height; y++) {
|
||||
const uint16_t* in = input + y * pitch;
|
||||
uint32_t* out = output + y * outpitch;
|
||||
for(uint x = 0; x < width; x++) {
|
||||
*out++ = colortable[*in++];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
50
bsnes/filter/ntsc-composite.cpp
Normal file
50
bsnes/filter/ntsc-composite.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
namespace Filter::NTSC_Composite {
|
||||
|
||||
struct snes_ntsc_t *ntsc;
|
||||
snes_ntsc_setup_t setup;
|
||||
int burst;
|
||||
int burst_toggle;
|
||||
|
||||
void initialize() {
|
||||
static bool initialized = false;
|
||||
if(initialized == true) return;
|
||||
initialized = true;
|
||||
|
||||
ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc);
|
||||
setup = snes_ntsc_composite;
|
||||
setup.merge_fields = 1;
|
||||
snes_ntsc_init(ntsc, &setup);
|
||||
|
||||
burst = 0;
|
||||
burst_toggle = (setup.merge_fields ? 0 : 1);
|
||||
}
|
||||
|
||||
void terminate() {
|
||||
if(ntsc) free(ntsc);
|
||||
}
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width = SNES_NTSC_OUT_WIDTH(256);
|
||||
height = height;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable_, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
initialize();
|
||||
colortable = colortable_;
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
if(width <= 256) {
|
||||
snes_ntsc_blit (ntsc, input, pitch, burst, width, height, output, outpitch << 2);
|
||||
} else {
|
||||
snes_ntsc_blit_hires(ntsc, input, pitch, burst, width, height, output, outpitch << 2);
|
||||
}
|
||||
|
||||
burst ^= burst_toggle;
|
||||
}
|
||||
|
||||
}
|
50
bsnes/filter/ntsc-rf.cpp
Normal file
50
bsnes/filter/ntsc-rf.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
namespace Filter::NTSC_RF {
|
||||
|
||||
struct snes_ntsc_t *ntsc;
|
||||
snes_ntsc_setup_t setup;
|
||||
int burst;
|
||||
int burst_toggle;
|
||||
|
||||
void initialize() {
|
||||
static bool initialized = false;
|
||||
if(initialized == true) return;
|
||||
initialized = true;
|
||||
|
||||
ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc);
|
||||
setup = snes_ntsc_composite;
|
||||
setup.merge_fields = 0;
|
||||
snes_ntsc_init(ntsc, &setup);
|
||||
|
||||
burst = 0;
|
||||
burst_toggle = (setup.merge_fields ? 0 : 1);
|
||||
}
|
||||
|
||||
void terminate() {
|
||||
if(ntsc) free(ntsc);
|
||||
}
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width = SNES_NTSC_OUT_WIDTH(256);
|
||||
height = height;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable_, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
initialize();
|
||||
colortable = colortable_;
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
if(width <= 256) {
|
||||
snes_ntsc_blit (ntsc, input, pitch, burst, width, height, output, outpitch << 2);
|
||||
} else {
|
||||
snes_ntsc_blit_hires(ntsc, input, pitch, burst, width, height, output, outpitch << 2);
|
||||
}
|
||||
|
||||
burst ^= burst_toggle;
|
||||
}
|
||||
|
||||
}
|
50
bsnes/filter/ntsc-rgb.cpp
Normal file
50
bsnes/filter/ntsc-rgb.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
namespace Filter::NTSC_RGB {
|
||||
|
||||
struct snes_ntsc_t *ntsc;
|
||||
snes_ntsc_setup_t setup;
|
||||
int burst;
|
||||
int burst_toggle;
|
||||
|
||||
void initialize() {
|
||||
static bool initialized = false;
|
||||
if(initialized == true) return;
|
||||
initialized = true;
|
||||
|
||||
ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc);
|
||||
setup = snes_ntsc_rgb;
|
||||
setup.merge_fields = 1;
|
||||
snes_ntsc_init(ntsc, &setup);
|
||||
|
||||
burst = 0;
|
||||
burst_toggle = (setup.merge_fields ? 0 : 1);
|
||||
}
|
||||
|
||||
void terminate() {
|
||||
if(ntsc) free(ntsc);
|
||||
}
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width = SNES_NTSC_OUT_WIDTH(256);
|
||||
height = height;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable_, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
initialize();
|
||||
colortable = colortable_;
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
if(width <= 256) {
|
||||
snes_ntsc_blit (ntsc, input, pitch, burst, width, height, output, outpitch << 2);
|
||||
} else {
|
||||
snes_ntsc_blit_hires(ntsc, input, pitch, burst, width, height, output, outpitch << 2);
|
||||
}
|
||||
|
||||
burst ^= burst_toggle;
|
||||
}
|
||||
|
||||
}
|
50
bsnes/filter/ntsc-svideo.cpp
Normal file
50
bsnes/filter/ntsc-svideo.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
namespace Filter::NTSC_SVideo {
|
||||
|
||||
struct snes_ntsc_t *ntsc;
|
||||
snes_ntsc_setup_t setup;
|
||||
int burst;
|
||||
int burst_toggle;
|
||||
|
||||
void initialize() {
|
||||
static bool initialized = false;
|
||||
if(initialized == true) return;
|
||||
initialized = true;
|
||||
|
||||
ntsc = (snes_ntsc_t*)malloc(sizeof *ntsc);
|
||||
setup = snes_ntsc_svideo;
|
||||
setup.merge_fields = 1;
|
||||
snes_ntsc_init(ntsc, &setup);
|
||||
|
||||
burst = 0;
|
||||
burst_toggle = (setup.merge_fields ? 0 : 1);
|
||||
}
|
||||
|
||||
void terminate() {
|
||||
if(ntsc) free(ntsc);
|
||||
}
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width = SNES_NTSC_OUT_WIDTH(256);
|
||||
height = height;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable_, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
initialize();
|
||||
colortable = colortable_;
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
if(width <= 256) {
|
||||
snes_ntsc_blit (ntsc, input, pitch, burst, width, height, output, outpitch << 2);
|
||||
} else {
|
||||
snes_ntsc_blit_hires(ntsc, input, pitch, burst, width, height, output, outpitch << 2);
|
||||
}
|
||||
|
||||
burst ^= burst_toggle;
|
||||
}
|
||||
|
||||
}
|
40
bsnes/filter/pixellate2x.cpp
Normal file
40
bsnes/filter/pixellate2x.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
namespace Filter::Pixellate2x {
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width = (width <= 256) ? width * 2 : width;
|
||||
height = (height <= 240) ? height * 2 : height;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
uint32_t *out0 = output;
|
||||
uint32_t *out1 = output + outpitch;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint32_t p = colortable[*input++];
|
||||
|
||||
*out0++ = p;
|
||||
if(height <= 240) *out1++ = p;
|
||||
if(width > 256) continue;
|
||||
|
||||
*out0++ = p;
|
||||
if(height <= 240) *out1++ = p;
|
||||
}
|
||||
|
||||
input += pitch - width;
|
||||
if(height <= 240) {
|
||||
out0 += outpitch + outpitch - 512;
|
||||
out1 += outpitch + outpitch - 512;
|
||||
} else {
|
||||
out0 += outpitch - 512;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
1175
bsnes/filter/sai/sai.cpp
Normal file
1175
bsnes/filter/sai/sai.cpp
Normal file
File diff suppressed because it is too large
Load Diff
46
bsnes/filter/scale2x.cpp
Normal file
46
bsnes/filter/scale2x.cpp
Normal file
@@ -0,0 +1,46 @@
|
||||
namespace Filter::Scale2x {
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width *= 2;
|
||||
height *= 2;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
for(uint y = 0; y < height; y++) {
|
||||
const uint16_t* in = input + y * pitch;
|
||||
uint32_t* out0 = output + y * outpitch * 2;
|
||||
uint32_t* out1 = output + y * outpitch * 2 + outpitch;
|
||||
|
||||
int prevline = (y == 0 ? 0 : pitch);
|
||||
int nextline = (y == height - 1 ? 0 : pitch);
|
||||
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint16_t A = *(in - prevline);
|
||||
uint16_t B = (x > 0) ? *(in - 1) : *in;
|
||||
uint16_t C = *in;
|
||||
uint16_t D = (x < width - 1) ? *(in + 1) : *in;
|
||||
uint16_t E = *(in++ + nextline);
|
||||
uint32_t c = colortable[C];
|
||||
|
||||
if(A != E && B != D) {
|
||||
*out0++ = (A == B ? colortable[A] : c);
|
||||
*out0++ = (A == D ? colortable[A] : c);
|
||||
*out1++ = (E == B ? colortable[E] : c);
|
||||
*out1++ = (E == D ? colortable[E] : c);
|
||||
} else {
|
||||
*out0++ = c;
|
||||
*out0++ = c;
|
||||
*out1++ = c;
|
||||
*out1++ = c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
28
bsnes/filter/scanlines-black.cpp
Normal file
28
bsnes/filter/scanlines-black.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace Filter::ScanlinesBlack {
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width = width;
|
||||
height = height * 2;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* palette, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
const uint16_t *in = input + y * pitch;
|
||||
uint32_t *out0 = output + y * outpitch * 2;
|
||||
uint32_t *out1 = output + y * outpitch * 2 + outpitch;
|
||||
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint16_t color = *in++;
|
||||
*out0++ = palette[color];
|
||||
*out1++ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
48
bsnes/filter/scanlines-dark.cpp
Normal file
48
bsnes/filter/scanlines-dark.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
namespace Filter::ScanlinesDark {
|
||||
|
||||
uint16_t adjust[32768];
|
||||
|
||||
void initialize() {
|
||||
static bool initialized = false;
|
||||
if(initialized == true) return;
|
||||
initialized = true;
|
||||
|
||||
for(unsigned i = 0; i < 32768; i++) {
|
||||
uint8_t r = (i >> 10) & 31;
|
||||
uint8_t g = (i >> 5) & 31;
|
||||
uint8_t b = (i >> 0) & 31;
|
||||
r *= 0.333;
|
||||
g *= 0.333;
|
||||
b *= 0.333;
|
||||
adjust[i] = (r << 10) + (g << 5) + (b << 0);
|
||||
}
|
||||
}
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width = width;
|
||||
height = height * 2;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* palette, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
initialize();
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
const uint16_t *in = input + y * pitch;
|
||||
uint32_t *out0 = output + y * outpitch * 2;
|
||||
uint32_t *out1 = output + y * outpitch * 2 + outpitch;
|
||||
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint16_t color = *in++;
|
||||
*out0++ = palette[color];
|
||||
*out1++ = palette[adjust[color]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
48
bsnes/filter/scanlines-light.cpp
Normal file
48
bsnes/filter/scanlines-light.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
namespace Filter::ScanlinesLight {
|
||||
|
||||
uint16_t adjust[32768];
|
||||
|
||||
void initialize() {
|
||||
static bool initialized = false;
|
||||
if(initialized == true) return;
|
||||
initialized = true;
|
||||
|
||||
for(unsigned i = 0; i < 32768; i++) {
|
||||
uint8_t r = (i >> 10) & 31;
|
||||
uint8_t g = (i >> 5) & 31;
|
||||
uint8_t b = (i >> 0) & 31;
|
||||
r *= 0.666;
|
||||
g *= 0.666;
|
||||
b *= 0.666;
|
||||
adjust[i] = (r << 10) + (g << 5) + (b << 0);
|
||||
}
|
||||
}
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width = width;
|
||||
height = height * 2;
|
||||
}
|
||||
|
||||
auto render(
|
||||
uint32_t* palette, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
initialize();
|
||||
|
||||
pitch >>= 1;
|
||||
outpitch >>= 2;
|
||||
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
const uint16_t *in = input + y * pitch;
|
||||
uint32_t *out0 = output + y * outpitch * 2;
|
||||
uint32_t *out1 = output + y * outpitch * 2 + outpitch;
|
||||
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
uint16_t color = *in++;
|
||||
*out0++ = palette[color];
|
||||
*out1++ = palette[adjust[color]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
251
bsnes/filter/snes_ntsc/snes_ntsc.c
Executable file
251
bsnes/filter/snes_ntsc/snes_ntsc.c
Executable file
@@ -0,0 +1,251 @@
|
||||
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
|
||||
|
||||
#include "snes_ntsc.h"
|
||||
|
||||
/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
snes_ntsc_setup_t const snes_ntsc_monochrome = { 0,-1, 0, 0,.2, 0,.2,-.2,-.2,-1, 1, 0, 0 };
|
||||
snes_ntsc_setup_t const snes_ntsc_composite = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 };
|
||||
snes_ntsc_setup_t const snes_ntsc_svideo = { 0, 0, 0, 0,.2, 0,.2, -1, -1, 0, 1, 0, 0 };
|
||||
snes_ntsc_setup_t const snes_ntsc_rgb = { 0, 0, 0, 0,.2, 0,.7, -1, -1,-1, 1, 0, 0 };
|
||||
|
||||
#define alignment_count 3
|
||||
#define burst_count 3
|
||||
#define rescale_in 8
|
||||
#define rescale_out 7
|
||||
|
||||
#define artifacts_mid 1.0f
|
||||
#define fringing_mid 1.0f
|
||||
#define std_decoder_hue 0
|
||||
|
||||
#define rgb_bits 7 /* half normal range to allow for doubled hires pixels */
|
||||
#define gamma_size 32
|
||||
|
||||
#include "snes_ntsc_impl.h"
|
||||
|
||||
/* 3 input pixels -> 8 composite samples */
|
||||
pixel_info_t const snes_ntsc_pixels [alignment_count] = {
|
||||
{ PIXEL_OFFSET( -4, -9 ), { 1, 1, .6667f, 0 } },
|
||||
{ PIXEL_OFFSET( -2, -7 ), { .3333f, 1, 1, .3333f } },
|
||||
{ PIXEL_OFFSET( 0, -5 ), { 0, .6667f, 1, 1 } },
|
||||
};
|
||||
|
||||
static void merge_kernel_fields( snes_ntsc_rgb_t* io )
|
||||
{
|
||||
int n;
|
||||
for ( n = burst_size; n; --n )
|
||||
{
|
||||
snes_ntsc_rgb_t p0 = io [burst_size * 0] + rgb_bias;
|
||||
snes_ntsc_rgb_t p1 = io [burst_size * 1] + rgb_bias;
|
||||
snes_ntsc_rgb_t p2 = io [burst_size * 2] + rgb_bias;
|
||||
/* merge colors without losing precision */
|
||||
io [burst_size * 0] =
|
||||
((p0 + p1 - ((p0 ^ p1) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
|
||||
io [burst_size * 1] =
|
||||
((p1 + p2 - ((p1 ^ p2) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
|
||||
io [burst_size * 2] =
|
||||
((p2 + p0 - ((p2 ^ p0) & snes_ntsc_rgb_builder)) >> 1) - rgb_bias;
|
||||
++io;
|
||||
}
|
||||
}
|
||||
|
||||
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out )
|
||||
{
|
||||
int n;
|
||||
for ( n = burst_count; n; --n )
|
||||
{
|
||||
unsigned i;
|
||||
for ( i = 0; i < rgb_kernel_size / 2; i++ )
|
||||
{
|
||||
snes_ntsc_rgb_t error = color -
|
||||
out [i ] - out [(i+12)%14+14] - out [(i+10)%14+28] -
|
||||
out [i + 7] - out [i + 5 +14] - out [i + 3 +28];
|
||||
DISTRIBUTE_ERROR( i+3+28, i+5+14, i+7 );
|
||||
}
|
||||
out += alignment_count * rgb_kernel_size;
|
||||
}
|
||||
}
|
||||
|
||||
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup )
|
||||
{
|
||||
int merge_fields;
|
||||
int entry;
|
||||
init_t impl;
|
||||
if ( !setup )
|
||||
setup = &snes_ntsc_composite;
|
||||
init( &impl, setup );
|
||||
|
||||
merge_fields = setup->merge_fields;
|
||||
if ( setup->artifacts <= -1 && setup->fringing <= -1 )
|
||||
merge_fields = 1;
|
||||
|
||||
for ( entry = 0; entry < snes_ntsc_palette_size; entry++ )
|
||||
{
|
||||
/* Reduce number of significant bits of source color. Clearing the
|
||||
low bits of R and B were least notictable. Modifying green was too
|
||||
noticeable. */
|
||||
int ir = entry >> 8 & 0x1E;
|
||||
int ig = entry >> 4 & 0x1F;
|
||||
int ib = entry << 1 & 0x1E;
|
||||
|
||||
#if SNES_NTSC_BSNES_COLORTBL
|
||||
if ( setup->bsnes_colortbl )
|
||||
{
|
||||
int bgr15 = (ib << 10) | (ig << 5) | ir;
|
||||
unsigned long rgb16 = setup->bsnes_colortbl [bgr15];
|
||||
ir = rgb16 >> 11 & 0x1E;
|
||||
ig = rgb16 >> 6 & 0x1F;
|
||||
ib = rgb16 & 0x1E;
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
float rr = impl.to_float [ir];
|
||||
float gg = impl.to_float [ig];
|
||||
float bb = impl.to_float [ib];
|
||||
|
||||
float y, i, q = RGB_TO_YIQ( rr, gg, bb, y, i );
|
||||
|
||||
int r, g, b = YIQ_TO_RGB( y, i, q, impl.to_rgb, int, r, g );
|
||||
snes_ntsc_rgb_t rgb = PACK_RGB( r, g, b );
|
||||
|
||||
snes_ntsc_rgb_t* out = ntsc->table [entry];
|
||||
gen_kernel( &impl, y, i, q, out );
|
||||
if ( merge_fields )
|
||||
merge_kernel_fields( out );
|
||||
correct_errors( rgb, out );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef SNES_NTSC_NO_BLITTERS
|
||||
|
||||
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
|
||||
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
|
||||
{
|
||||
int chunk_count = (in_width - 1) / snes_ntsc_in_chunk;
|
||||
for ( ; in_height; --in_height )
|
||||
{
|
||||
SNES_NTSC_IN_T const* line_in = input;
|
||||
SNES_NTSC_BEGIN_ROW( ntsc, burst_phase,
|
||||
snes_ntsc_black, snes_ntsc_black, SNES_NTSC_ADJ_IN( *line_in ) );
|
||||
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
|
||||
int n;
|
||||
++line_in;
|
||||
|
||||
for ( n = chunk_count; n; --n )
|
||||
{
|
||||
/* order of input and output pixels must not be altered */
|
||||
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
|
||||
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
|
||||
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
|
||||
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
line_in += 3;
|
||||
line_out += 7;
|
||||
}
|
||||
|
||||
/* finish final pixels */
|
||||
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
|
||||
SNES_NTSC_RGB_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
|
||||
SNES_NTSC_RGB_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
|
||||
SNES_NTSC_RGB_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_RGB_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
|
||||
input += in_row_width;
|
||||
rgb_out = (char*) rgb_out + out_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input, long in_row_width,
|
||||
int burst_phase, int in_width, int in_height, void* rgb_out, long out_pitch )
|
||||
{
|
||||
int chunk_count = (in_width - 2) / (snes_ntsc_in_chunk * 2);
|
||||
for ( ; in_height; --in_height )
|
||||
{
|
||||
SNES_NTSC_IN_T const* line_in = input;
|
||||
SNES_NTSC_HIRES_ROW( ntsc, burst_phase,
|
||||
snes_ntsc_black, snes_ntsc_black, snes_ntsc_black,
|
||||
SNES_NTSC_ADJ_IN( line_in [0] ),
|
||||
SNES_NTSC_ADJ_IN( line_in [1] ) );
|
||||
snes_ntsc_out_t* restrict line_out = (snes_ntsc_out_t*) rgb_out;
|
||||
int n;
|
||||
line_in += 2;
|
||||
|
||||
for ( n = chunk_count; n; --n )
|
||||
{
|
||||
/* twice as many input pixels per chunk */
|
||||
SNES_NTSC_COLOR_IN( 0, SNES_NTSC_ADJ_IN( line_in [0] ) );
|
||||
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 1, SNES_NTSC_ADJ_IN( line_in [1] ) );
|
||||
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 2, SNES_NTSC_ADJ_IN( line_in [2] ) );
|
||||
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 3, SNES_NTSC_ADJ_IN( line_in [3] ) );
|
||||
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 4, SNES_NTSC_ADJ_IN( line_in [4] ) );
|
||||
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 5, SNES_NTSC_ADJ_IN( line_in [5] ) );
|
||||
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
line_in += 6;
|
||||
line_out += 7;
|
||||
}
|
||||
|
||||
SNES_NTSC_COLOR_IN( 0, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 0, line_out [0], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 1, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 1, line_out [1], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 2, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 2, line_out [2], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 3, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 3, line_out [3], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 4, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 4, line_out [4], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
SNES_NTSC_COLOR_IN( 5, snes_ntsc_black );
|
||||
SNES_NTSC_HIRES_OUT( 5, line_out [5], SNES_NTSC_OUT_DEPTH );
|
||||
SNES_NTSC_HIRES_OUT( 6, line_out [6], SNES_NTSC_OUT_DEPTH );
|
||||
|
||||
burst_phase = (burst_phase + 1) % snes_ntsc_burst_count;
|
||||
input += in_row_width;
|
||||
rgb_out = (char*) rgb_out + out_pitch;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
228
bsnes/filter/snes_ntsc/snes_ntsc.h
Executable file
228
bsnes/filter/snes_ntsc/snes_ntsc.h
Executable file
@@ -0,0 +1,228 @@
|
||||
/* SNES NTSC video filter */
|
||||
|
||||
/* snes_ntsc 0.2.2 */
|
||||
#ifndef SNES_NTSC_H
|
||||
#define SNES_NTSC_H
|
||||
|
||||
#include "snes_ntsc_config.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Image parameters, ranging from -1.0 to 1.0. Actual internal values shown
|
||||
in parenthesis and should remain fairly stable in future versions. */
|
||||
typedef struct snes_ntsc_setup_t
|
||||
{
|
||||
/* Basic parameters */
|
||||
double hue; /* -1 = -180 degrees +1 = +180 degrees */
|
||||
double saturation; /* -1 = grayscale (0.0) +1 = oversaturated colors (2.0) */
|
||||
double contrast; /* -1 = dark (0.5) +1 = light (1.5) */
|
||||
double brightness; /* -1 = dark (0.5) +1 = light (1.5) */
|
||||
double sharpness; /* edge contrast enhancement/blurring */
|
||||
|
||||
/* Advanced parameters */
|
||||
double gamma; /* -1 = dark (1.5) +1 = light (0.5) */
|
||||
double resolution; /* image resolution */
|
||||
double artifacts; /* artifacts caused by color changes */
|
||||
double fringing; /* color artifacts caused by brightness changes */
|
||||
double bleed; /* color bleed (color resolution reduction) */
|
||||
int merge_fields; /* if 1, merges even and odd fields together to reduce flicker */
|
||||
float const* decoder_matrix; /* optional RGB decoder matrix, 6 elements */
|
||||
|
||||
unsigned long const* bsnes_colortbl; /* undocumented; set to 0 */
|
||||
} snes_ntsc_setup_t;
|
||||
|
||||
/* Video format presets */
|
||||
extern snes_ntsc_setup_t const snes_ntsc_composite; /* color bleeding + artifacts */
|
||||
extern snes_ntsc_setup_t const snes_ntsc_svideo; /* color bleeding only */
|
||||
extern snes_ntsc_setup_t const snes_ntsc_rgb; /* crisp image */
|
||||
extern snes_ntsc_setup_t const snes_ntsc_monochrome;/* desaturated + artifacts */
|
||||
|
||||
/* Initializes and adjusts parameters. Can be called multiple times on the same
|
||||
snes_ntsc_t object. Can pass NULL for either parameter. */
|
||||
typedef struct snes_ntsc_t snes_ntsc_t;
|
||||
void snes_ntsc_init( snes_ntsc_t* ntsc, snes_ntsc_setup_t const* setup );
|
||||
|
||||
/* Filters one or more rows of pixels. Input pixel format is set by SNES_NTSC_IN_FORMAT
|
||||
and output RGB depth is set by SNES_NTSC_OUT_DEPTH. Both default to 16-bit RGB.
|
||||
In_row_width is the number of pixels to get to the next input row. Out_pitch
|
||||
is the number of *bytes* to get to the next output row. */
|
||||
void snes_ntsc_blit( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
|
||||
long in_row_width, int burst_phase, int in_width, int in_height,
|
||||
void* rgb_out, long out_pitch );
|
||||
|
||||
void snes_ntsc_blit_hires( snes_ntsc_t const* ntsc, SNES_NTSC_IN_T const* input,
|
||||
long in_row_width, int burst_phase, int in_width, int in_height,
|
||||
void* rgb_out, long out_pitch );
|
||||
|
||||
/* Number of output pixels written by low-res blitter for given input width. Width
|
||||
might be rounded down slightly; use SNES_NTSC_IN_WIDTH() on result to find rounded
|
||||
value. Guaranteed not to round 256 down at all. */
|
||||
#define SNES_NTSC_OUT_WIDTH( in_width ) \
|
||||
((((in_width) - 1) / snes_ntsc_in_chunk + 1) * snes_ntsc_out_chunk)
|
||||
|
||||
/* Number of low-res input pixels that will fit within given output width. Might be
|
||||
rounded down slightly; use SNES_NTSC_OUT_WIDTH() on result to find rounded
|
||||
value. */
|
||||
#define SNES_NTSC_IN_WIDTH( out_width ) \
|
||||
(((out_width) / snes_ntsc_out_chunk - 1) * snes_ntsc_in_chunk + 1)
|
||||
|
||||
|
||||
/* Interface for user-defined custom blitters */
|
||||
|
||||
enum { snes_ntsc_in_chunk = 3 }; /* number of input pixels read per chunk */
|
||||
enum { snes_ntsc_out_chunk = 7 }; /* number of output pixels generated per chunk */
|
||||
enum { snes_ntsc_black = 0 }; /* palette index for black */
|
||||
enum { snes_ntsc_burst_count = 3 }; /* burst phase cycles through 0, 1, and 2 */
|
||||
|
||||
/* Begins outputting row and starts three pixels. First pixel will be cut off a bit.
|
||||
Use snes_ntsc_black for unused pixels. Declares variables, so must be before first
|
||||
statement in a block (unless you're using C++). */
|
||||
#define SNES_NTSC_BEGIN_ROW( ntsc, burst, pixel0, pixel1, pixel2 ) \
|
||||
char const* ktable = \
|
||||
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
|
||||
SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, SNES_NTSC_IN_FORMAT, ktable )
|
||||
|
||||
/* Begins input pixel */
|
||||
#define SNES_NTSC_COLOR_IN( index, color ) \
|
||||
SNES_NTSC_COLOR_IN_( index, color, SNES_NTSC_IN_FORMAT, ktable )
|
||||
|
||||
/* Generates output pixel. Bits can be 24, 16, 15, 14, 32 (treated as 24), or 0:
|
||||
24: RRRRRRRR GGGGGGGG BBBBBBBB (8-8-8 RGB)
|
||||
16: RRRRRGGG GGGBBBBB (5-6-5 RGB)
|
||||
15: RRRRRGG GGGBBBBB (5-5-5 RGB)
|
||||
14: BBBBBGG GGGRRRRR (5-5-5 BGR, native SNES format)
|
||||
0: xxxRRRRR RRRxxGGG GGGGGxxB BBBBBBBx (native internal format; x = junk bits) */
|
||||
#define SNES_NTSC_RGB_OUT( index, rgb_out, bits ) \
|
||||
SNES_NTSC_RGB_OUT_14_( index, rgb_out, bits, 1 )
|
||||
|
||||
/* Hires equivalents */
|
||||
#define SNES_NTSC_HIRES_ROW( ntsc, burst, pixel1, pixel2, pixel3, pixel4, pixel5 ) \
|
||||
char const* ktable = \
|
||||
(char const*) (ntsc)->table + burst * (snes_ntsc_burst_size * sizeof (snes_ntsc_rgb_t));\
|
||||
unsigned const snes_ntsc_pixel1_ = (pixel1);\
|
||||
snes_ntsc_rgb_t const* kernel1 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel1_ );\
|
||||
unsigned const snes_ntsc_pixel2_ = (pixel2);\
|
||||
snes_ntsc_rgb_t const* kernel2 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel2_ );\
|
||||
unsigned const snes_ntsc_pixel3_ = (pixel3);\
|
||||
snes_ntsc_rgb_t const* kernel3 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel3_ );\
|
||||
unsigned const snes_ntsc_pixel4_ = (pixel4);\
|
||||
snes_ntsc_rgb_t const* kernel4 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel4_ );\
|
||||
unsigned const snes_ntsc_pixel5_ = (pixel5);\
|
||||
snes_ntsc_rgb_t const* kernel5 = SNES_NTSC_IN_FORMAT( ktable, snes_ntsc_pixel5_ );\
|
||||
snes_ntsc_rgb_t const* kernel0 = kernel1;\
|
||||
snes_ntsc_rgb_t const* kernelx0;\
|
||||
snes_ntsc_rgb_t const* kernelx1 = kernel1;\
|
||||
snes_ntsc_rgb_t const* kernelx2 = kernel1;\
|
||||
snes_ntsc_rgb_t const* kernelx3 = kernel1;\
|
||||
snes_ntsc_rgb_t const* kernelx4 = kernel1;\
|
||||
snes_ntsc_rgb_t const* kernelx5 = kernel1
|
||||
|
||||
#define SNES_NTSC_HIRES_OUT( x, rgb_out, bits ) {\
|
||||
snes_ntsc_rgb_t raw_ =\
|
||||
kernel0 [ x ] + kernel2 [(x+5)%7+14] + kernel4 [(x+3)%7+28] +\
|
||||
kernelx0 [(x+7)%7+7] + kernelx2 [(x+5)%7+21] + kernelx4 [(x+3)%7+35] +\
|
||||
kernel1 [(x+6)%7 ] + kernel3 [(x+4)%7+14] + kernel5 [(x+2)%7+28] +\
|
||||
kernelx1 [(x+6)%7+7] + kernelx3 [(x+4)%7+21] + kernelx5 [(x+2)%7+35];\
|
||||
SNES_NTSC_CLAMP_( raw_, 0 );\
|
||||
SNES_NTSC_RGB_OUT_( rgb_out, (bits), 0 );\
|
||||
}
|
||||
|
||||
|
||||
/* private */
|
||||
enum { snes_ntsc_entry_size = 128 };
|
||||
enum { snes_ntsc_palette_size = 0x2000 };
|
||||
typedef unsigned long snes_ntsc_rgb_t;
|
||||
struct snes_ntsc_t {
|
||||
snes_ntsc_rgb_t table [snes_ntsc_palette_size] [snes_ntsc_entry_size];
|
||||
};
|
||||
enum { snes_ntsc_burst_size = snes_ntsc_entry_size / snes_ntsc_burst_count };
|
||||
|
||||
#define SNES_NTSC_RGB16( ktable, n ) \
|
||||
(snes_ntsc_rgb_t const*) (ktable + ((n & 0x001E) | (n >> 1 & 0x03E0) | (n >> 2 & 0x3C00)) * \
|
||||
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
|
||||
|
||||
#define SNES_NTSC_BGR15( ktable, n ) \
|
||||
(snes_ntsc_rgb_t const*) (ktable + ((n << 9 & 0x3C00) | (n & 0x03E0) | (n >> 10 & 0x001E)) * \
|
||||
(snes_ntsc_entry_size / 2 * sizeof (snes_ntsc_rgb_t)))
|
||||
|
||||
/* common 3->7 ntsc macros */
|
||||
#define SNES_NTSC_BEGIN_ROW_6_( pixel0, pixel1, pixel2, ENTRY, table ) \
|
||||
unsigned const snes_ntsc_pixel0_ = (pixel0);\
|
||||
snes_ntsc_rgb_t const* kernel0 = ENTRY( table, snes_ntsc_pixel0_ );\
|
||||
unsigned const snes_ntsc_pixel1_ = (pixel1);\
|
||||
snes_ntsc_rgb_t const* kernel1 = ENTRY( table, snes_ntsc_pixel1_ );\
|
||||
unsigned const snes_ntsc_pixel2_ = (pixel2);\
|
||||
snes_ntsc_rgb_t const* kernel2 = ENTRY( table, snes_ntsc_pixel2_ );\
|
||||
snes_ntsc_rgb_t const* kernelx0;\
|
||||
snes_ntsc_rgb_t const* kernelx1 = kernel0;\
|
||||
snes_ntsc_rgb_t const* kernelx2 = kernel0
|
||||
|
||||
#define SNES_NTSC_RGB_OUT_14_( x, rgb_out, bits, shift ) {\
|
||||
snes_ntsc_rgb_t raw_ =\
|
||||
kernel0 [x ] + kernel1 [(x+12)%7+14] + kernel2 [(x+10)%7+28] +\
|
||||
kernelx0 [(x+7)%14] + kernelx1 [(x+ 5)%7+21] + kernelx2 [(x+ 3)%7+35];\
|
||||
SNES_NTSC_CLAMP_( raw_, shift );\
|
||||
SNES_NTSC_RGB_OUT_( rgb_out, bits, shift );\
|
||||
}
|
||||
|
||||
/* common ntsc macros */
|
||||
#define snes_ntsc_rgb_builder ((1L << 21) | (1 << 11) | (1 << 1))
|
||||
#define snes_ntsc_clamp_mask (snes_ntsc_rgb_builder * 3 / 2)
|
||||
#define snes_ntsc_clamp_add (snes_ntsc_rgb_builder * 0x101)
|
||||
#define SNES_NTSC_CLAMP_( io, shift ) {\
|
||||
snes_ntsc_rgb_t sub = (io) >> (9-(shift)) & snes_ntsc_clamp_mask;\
|
||||
snes_ntsc_rgb_t clamp = snes_ntsc_clamp_add - sub;\
|
||||
io |= clamp;\
|
||||
clamp -= sub;\
|
||||
io &= clamp;\
|
||||
}
|
||||
|
||||
#define SNES_NTSC_COLOR_IN_( index, color, ENTRY, table ) {\
|
||||
unsigned color_;\
|
||||
kernelx##index = kernel##index;\
|
||||
kernel##index = (color_ = (color), ENTRY( table, color_ ));\
|
||||
}
|
||||
|
||||
/* x is always zero except in snes_ntsc library */
|
||||
/* original routine */
|
||||
/*
|
||||
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
|
||||
if ( bits == 16 )\
|
||||
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
|
||||
if ( bits == 24 || bits == 32 )\
|
||||
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
|
||||
if ( bits == 15 )\
|
||||
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
|
||||
if ( bits == 14 )\
|
||||
rgb_out = (raw_>>(24-x)& 0x001F)|(raw_>>(9-x)&0x03E0)|(raw_<<(6+x)&0x7C00);\
|
||||
if ( bits == 0 )\
|
||||
rgb_out = raw_ << x;\
|
||||
}
|
||||
*/
|
||||
|
||||
/* custom bsnes routine -- hooks into bsnes colortable */
|
||||
#define SNES_NTSC_RGB_OUT_( rgb_out, bits, x ) {\
|
||||
if ( bits == 16 ) {\
|
||||
rgb_out = (raw_>>(13-x)& 0xF800)|(raw_>>(8-x)&0x07E0)|(raw_>>(4-x)&0x001F);\
|
||||
rgb_out = ((rgb_out&0xf800)>>11)|((rgb_out&0x07c0)>>1)|((rgb_out&0x001f)<<10);\
|
||||
rgb_out = colortable[rgb_out];\
|
||||
} else if ( bits == 24 || bits == 32 ) {\
|
||||
rgb_out = (raw_>>(5-x)&0xFF0000)|(raw_>>(3-x)&0xFF00)|(raw_>>(1-x)&0xFF);\
|
||||
rgb_out = ((rgb_out&0xf80000)>>19)|((rgb_out&0x00f800)>>6)|((rgb_out&0x0000f8)<<7);\
|
||||
rgb_out = colortable[rgb_out];\
|
||||
} else if ( bits == 15 ) {\
|
||||
rgb_out = (raw_>>(14-x)& 0x7C00)|(raw_>>(9-x)&0x03E0)|(raw_>>(4-x)&0x001F);\
|
||||
rgb_out = ((rgb_out&0x7c00)>>10)|((rgb_out&0x03e0))|((rgb_out&0x001f)<<10);\
|
||||
rgb_out = colortable[rgb_out];\
|
||||
} else {\
|
||||
rgb_out = raw_ << x;\
|
||||
}\
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
26
bsnes/filter/snes_ntsc/snes_ntsc_config.h
Executable file
26
bsnes/filter/snes_ntsc/snes_ntsc_config.h
Executable file
@@ -0,0 +1,26 @@
|
||||
/* Configure library by modifying this file */
|
||||
|
||||
#ifndef SNES_NTSC_CONFIG_H
|
||||
#define SNES_NTSC_CONFIG_H
|
||||
|
||||
/* Format of source pixels */
|
||||
/* #define SNES_NTSC_IN_FORMAT SNES_NTSC_RGB16 */
|
||||
#define SNES_NTSC_IN_FORMAT SNES_NTSC_BGR15
|
||||
|
||||
/* The following affect the built-in blitter only; a custom blitter can
|
||||
handle things however it wants. */
|
||||
|
||||
/* Bits per pixel of output. Can be 15, 16, 32, or 24 (same as 32). */
|
||||
#define SNES_NTSC_OUT_DEPTH 32
|
||||
|
||||
/* Type of input pixel values */
|
||||
#define SNES_NTSC_IN_T unsigned short
|
||||
|
||||
/* Each raw pixel input value is passed through this. You might want to mask
|
||||
the pixel index if you use the high bits as flags, etc. */
|
||||
#define SNES_NTSC_ADJ_IN( in ) in
|
||||
|
||||
/* For each pixel, this is the basic operation:
|
||||
output_color = SNES_NTSC_ADJ_IN( SNES_NTSC_IN_T ) */
|
||||
|
||||
#endif
|
439
bsnes/filter/snes_ntsc/snes_ntsc_impl.h
Executable file
439
bsnes/filter/snes_ntsc/snes_ntsc_impl.h
Executable file
@@ -0,0 +1,439 @@
|
||||
/* snes_ntsc 0.2.2. http://www.slack.net/~ant/ */
|
||||
|
||||
/* Common implementation of NTSC filters */
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
/* Copyright (C) 2006 Shay Green. This module is free software; you
|
||||
can redistribute it and/or modify it under the terms of the GNU Lesser
|
||||
General Public License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version. This
|
||||
module is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
|
||||
details. You should have received a copy of the GNU Lesser General Public
|
||||
License along with this module; if not, write to the Free Software Foundation,
|
||||
Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
||||
|
||||
#define DISABLE_CORRECTION 0
|
||||
|
||||
#undef PI
|
||||
#define PI 3.14159265358979323846f
|
||||
|
||||
#ifndef LUMA_CUTOFF
|
||||
#define LUMA_CUTOFF 0.20
|
||||
#endif
|
||||
#ifndef gamma_size
|
||||
#define gamma_size 1
|
||||
#endif
|
||||
#ifndef rgb_bits
|
||||
#define rgb_bits 8
|
||||
#endif
|
||||
#ifndef artifacts_max
|
||||
#define artifacts_max (artifacts_mid * 1.5f)
|
||||
#endif
|
||||
#ifndef fringing_max
|
||||
#define fringing_max (fringing_mid * 2)
|
||||
#endif
|
||||
#ifndef STD_HUE_CONDITION
|
||||
#define STD_HUE_CONDITION( setup ) 1
|
||||
#endif
|
||||
|
||||
#define ext_decoder_hue (std_decoder_hue + 15)
|
||||
#define rgb_unit (1 << rgb_bits)
|
||||
#define rgb_offset (rgb_unit * 2 + 0.5f)
|
||||
|
||||
enum { burst_size = snes_ntsc_entry_size / burst_count };
|
||||
enum { kernel_half = 16 };
|
||||
enum { kernel_size = kernel_half * 2 + 1 };
|
||||
|
||||
typedef struct init_t
|
||||
{
|
||||
float to_rgb [burst_count * 6];
|
||||
float to_float [gamma_size];
|
||||
float contrast;
|
||||
float brightness;
|
||||
float artifacts;
|
||||
float fringing;
|
||||
float kernel [rescale_out * kernel_size * 2];
|
||||
} init_t;
|
||||
|
||||
#define ROTATE_IQ( i, q, sin_b, cos_b ) {\
|
||||
float t;\
|
||||
t = i * cos_b - q * sin_b;\
|
||||
q = i * sin_b + q * cos_b;\
|
||||
i = t;\
|
||||
}
|
||||
|
||||
static void init_filters( init_t* impl, snes_ntsc_setup_t const* setup )
|
||||
{
|
||||
#if rescale_out > 1
|
||||
float kernels [kernel_size * 2];
|
||||
#else
|
||||
float* const kernels = impl->kernel;
|
||||
#endif
|
||||
|
||||
/* generate luma (y) filter using sinc kernel */
|
||||
{
|
||||
/* sinc with rolloff (dsf) */
|
||||
float const rolloff = 1 + (float) setup->sharpness * (float) 0.032;
|
||||
float const maxh = 32;
|
||||
float const pow_a_n = (float) pow( rolloff, maxh );
|
||||
float sum;
|
||||
int i;
|
||||
/* quadratic mapping to reduce negative (blurring) range */
|
||||
float to_angle = (float) setup->resolution + 1;
|
||||
to_angle = PI / maxh * (float) LUMA_CUTOFF * (to_angle * to_angle + 1);
|
||||
|
||||
kernels [kernel_size * 3 / 2] = maxh; /* default center value */
|
||||
for ( i = 0; i < kernel_half * 2 + 1; i++ )
|
||||
{
|
||||
int x = i - kernel_half;
|
||||
float angle = x * to_angle;
|
||||
/* instability occurs at center point with rolloff very close to 1.0 */
|
||||
if ( x || pow_a_n > (float) 1.056 || pow_a_n < (float) 0.981 )
|
||||
{
|
||||
float rolloff_cos_a = rolloff * (float) cos( angle );
|
||||
float num = 1 - rolloff_cos_a -
|
||||
pow_a_n * (float) cos( maxh * angle ) +
|
||||
pow_a_n * rolloff * (float) cos( (maxh - 1) * angle );
|
||||
float den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
|
||||
float dsf = num / den;
|
||||
kernels [kernel_size * 3 / 2 - kernel_half + i] = dsf - (float) 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
/* apply blackman window and find sum */
|
||||
sum = 0;
|
||||
for ( i = 0; i < kernel_half * 2 + 1; i++ )
|
||||
{
|
||||
float x = PI * 2 / (kernel_half * 2) * i;
|
||||
float blackman = 0.42f - 0.5f * (float) cos( x ) + 0.08f * (float) cos( x * 2 );
|
||||
sum += (kernels [kernel_size * 3 / 2 - kernel_half + i] *= blackman);
|
||||
}
|
||||
|
||||
/* normalize kernel */
|
||||
sum = 1.0f / sum;
|
||||
for ( i = 0; i < kernel_half * 2 + 1; i++ )
|
||||
{
|
||||
int x = kernel_size * 3 / 2 - kernel_half + i;
|
||||
kernels [x] *= sum;
|
||||
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
|
||||
}
|
||||
}
|
||||
|
||||
/* generate chroma (iq) filter using gaussian kernel */
|
||||
{
|
||||
float const cutoff_factor = -0.03125f;
|
||||
float cutoff = (float) setup->bleed;
|
||||
int i;
|
||||
|
||||
if ( cutoff < 0 )
|
||||
{
|
||||
/* keep extreme value accessible only near upper end of scale (1.0) */
|
||||
cutoff *= cutoff;
|
||||
cutoff *= cutoff;
|
||||
cutoff *= cutoff;
|
||||
cutoff *= -30.0f / 0.65f;
|
||||
}
|
||||
cutoff = cutoff_factor - 0.65f * cutoff_factor * cutoff;
|
||||
|
||||
for ( i = -kernel_half; i <= kernel_half; i++ )
|
||||
kernels [kernel_size / 2 + i] = (float) exp( i * i * cutoff );
|
||||
|
||||
/* normalize even and odd phases separately */
|
||||
for ( i = 0; i < 2; i++ )
|
||||
{
|
||||
float sum = 0;
|
||||
int x;
|
||||
for ( x = i; x < kernel_size; x += 2 )
|
||||
sum += kernels [x];
|
||||
|
||||
sum = 1.0f / sum;
|
||||
for ( x = i; x < kernel_size; x += 2 )
|
||||
{
|
||||
kernels [x] *= sum;
|
||||
assert( kernels [x] == kernels [x] ); /* catch numerical instability */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
printf( "luma:\n" );
|
||||
for ( i = kernel_size; i < kernel_size * 2; i++ )
|
||||
printf( "%f\n", kernels [i] );
|
||||
printf( "chroma:\n" );
|
||||
for ( i = 0; i < kernel_size; i++ )
|
||||
printf( "%f\n", kernels [i] );
|
||||
*/
|
||||
|
||||
/* generate linear rescale kernels */
|
||||
#if rescale_out > 1
|
||||
{
|
||||
float weight = 1.0f;
|
||||
float* out = impl->kernel;
|
||||
int n = rescale_out;
|
||||
do
|
||||
{
|
||||
float remain = 0;
|
||||
int i;
|
||||
weight -= 1.0f / rescale_in;
|
||||
for ( i = 0; i < kernel_size * 2; i++ )
|
||||
{
|
||||
float cur = kernels [i];
|
||||
float m = cur * weight;
|
||||
*out++ = m + remain;
|
||||
remain = cur - m;
|
||||
}
|
||||
}
|
||||
while ( --n );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static float const default_decoder [6] =
|
||||
{ 0.956f, 0.621f, -0.272f, -0.647f, -1.105f, 1.702f };
|
||||
|
||||
static void init( init_t* impl, snes_ntsc_setup_t const* setup )
|
||||
{
|
||||
impl->brightness = (float) setup->brightness * (0.5f * rgb_unit) + rgb_offset;
|
||||
impl->contrast = (float) setup->contrast * (0.5f * rgb_unit) + rgb_unit;
|
||||
#ifdef default_palette_contrast
|
||||
if ( !setup->palette )
|
||||
impl->contrast *= default_palette_contrast;
|
||||
#endif
|
||||
|
||||
impl->artifacts = (float) setup->artifacts;
|
||||
if ( impl->artifacts > 0 )
|
||||
impl->artifacts *= artifacts_max - artifacts_mid;
|
||||
impl->artifacts = impl->artifacts * artifacts_mid + artifacts_mid;
|
||||
|
||||
impl->fringing = (float) setup->fringing;
|
||||
if ( impl->fringing > 0 )
|
||||
impl->fringing *= fringing_max - fringing_mid;
|
||||
impl->fringing = impl->fringing * fringing_mid + fringing_mid;
|
||||
|
||||
init_filters( impl, setup );
|
||||
|
||||
/* generate gamma table */
|
||||
if ( gamma_size > 1 )
|
||||
{
|
||||
float const to_float = 1.0f / (gamma_size - (gamma_size > 1));
|
||||
float const gamma = 1.1333f - (float) setup->gamma * 0.5f;
|
||||
/* match common PC's 2.2 gamma to TV's 2.65 gamma */
|
||||
int i;
|
||||
for ( i = 0; i < gamma_size; i++ )
|
||||
impl->to_float [i] =
|
||||
(float) pow( i * to_float, gamma ) * impl->contrast + impl->brightness;
|
||||
}
|
||||
|
||||
/* setup decoder matricies */
|
||||
{
|
||||
float hue = (float) setup->hue * PI + PI / 180 * ext_decoder_hue;
|
||||
float sat = (float) setup->saturation + 1;
|
||||
float const* decoder = setup->decoder_matrix;
|
||||
if ( !decoder )
|
||||
{
|
||||
decoder = default_decoder;
|
||||
if ( STD_HUE_CONDITION( setup ) )
|
||||
hue += PI / 180 * (std_decoder_hue - ext_decoder_hue);
|
||||
}
|
||||
|
||||
{
|
||||
float s = (float) sin( hue ) * sat;
|
||||
float c = (float) cos( hue ) * sat;
|
||||
float* out = impl->to_rgb;
|
||||
int n;
|
||||
|
||||
n = burst_count;
|
||||
do
|
||||
{
|
||||
float const* in = decoder;
|
||||
int n = 3;
|
||||
do
|
||||
{
|
||||
float i = *in++;
|
||||
float q = *in++;
|
||||
*out++ = i * c - q * s;
|
||||
*out++ = i * s + q * c;
|
||||
}
|
||||
while ( --n );
|
||||
if ( burst_count <= 1 )
|
||||
break;
|
||||
ROTATE_IQ( s, c, 0.866025f, -0.5f ); /* +120 degrees */
|
||||
}
|
||||
while ( --n );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* kernel generation */
|
||||
|
||||
#define RGB_TO_YIQ( r, g, b, y, i ) (\
|
||||
(y = (r) * 0.299f + (g) * 0.587f + (b) * 0.114f),\
|
||||
(i = (r) * 0.596f - (g) * 0.275f - (b) * 0.321f),\
|
||||
((r) * 0.212f - (g) * 0.523f + (b) * 0.311f)\
|
||||
)
|
||||
|
||||
#define YIQ_TO_RGB( y, i, q, to_rgb, type, r, g ) (\
|
||||
r = (type) (y + to_rgb [0] * i + to_rgb [1] * q),\
|
||||
g = (type) (y + to_rgb [2] * i + to_rgb [3] * q),\
|
||||
(type) (y + to_rgb [4] * i + to_rgb [5] * q)\
|
||||
)
|
||||
|
||||
#define PACK_RGB( r, g, b ) ((r) << 21 | (g) << 11 | (b) << 1)
|
||||
|
||||
enum { rgb_kernel_size = burst_size / alignment_count };
|
||||
enum { rgb_bias = rgb_unit * 2 * snes_ntsc_rgb_builder };
|
||||
|
||||
typedef struct pixel_info_t
|
||||
{
|
||||
int offset;
|
||||
float negate;
|
||||
float kernel [4];
|
||||
} pixel_info_t;
|
||||
|
||||
#if rescale_in > 1
|
||||
#define PIXEL_OFFSET_( ntsc, scaled ) \
|
||||
(kernel_size / 2 + ntsc + (scaled != 0) + (rescale_out - scaled) % rescale_out + \
|
||||
(kernel_size * 2 * scaled))
|
||||
|
||||
#define PIXEL_OFFSET( ntsc, scaled ) \
|
||||
PIXEL_OFFSET_( ((ntsc) - (scaled) / rescale_out * rescale_in),\
|
||||
(((scaled) + rescale_out * 10) % rescale_out) ),\
|
||||
(1.0f - (((ntsc) + 100) & 2))
|
||||
#else
|
||||
#define PIXEL_OFFSET( ntsc, scaled ) \
|
||||
(kernel_size / 2 + (ntsc) - (scaled)),\
|
||||
(1.0f - (((ntsc) + 100) & 2))
|
||||
#endif
|
||||
|
||||
extern pixel_info_t const snes_ntsc_pixels [alignment_count];
|
||||
|
||||
/* Generate pixel at all burst phases and column alignments */
|
||||
static void gen_kernel( init_t* impl, float y, float i, float q, snes_ntsc_rgb_t* out )
|
||||
{
|
||||
/* generate for each scanline burst phase */
|
||||
float const* to_rgb = impl->to_rgb;
|
||||
int burst_remain = burst_count;
|
||||
y -= rgb_offset;
|
||||
do
|
||||
{
|
||||
/* Encode yiq into *two* composite signals (to allow control over artifacting).
|
||||
Convolve these with kernels which: filter respective components, apply
|
||||
sharpening, and rescale horizontally. Convert resulting yiq to rgb and pack
|
||||
into integer. Based on algorithm by NewRisingSun. */
|
||||
pixel_info_t const* pixel = snes_ntsc_pixels;
|
||||
int alignment_remain = alignment_count;
|
||||
do
|
||||
{
|
||||
/* negate is -1 when composite starts at odd multiple of 2 */
|
||||
float const yy = y * impl->fringing * pixel->negate;
|
||||
float const ic0 = (i + yy) * pixel->kernel [0];
|
||||
float const qc1 = (q + yy) * pixel->kernel [1];
|
||||
float const ic2 = (i - yy) * pixel->kernel [2];
|
||||
float const qc3 = (q - yy) * pixel->kernel [3];
|
||||
|
||||
float const factor = impl->artifacts * pixel->negate;
|
||||
float const ii = i * factor;
|
||||
float const yc0 = (y + ii) * pixel->kernel [0];
|
||||
float const yc2 = (y - ii) * pixel->kernel [2];
|
||||
|
||||
float const qq = q * factor;
|
||||
float const yc1 = (y + qq) * pixel->kernel [1];
|
||||
float const yc3 = (y - qq) * pixel->kernel [3];
|
||||
|
||||
float const* k = &impl->kernel [pixel->offset];
|
||||
int n;
|
||||
++pixel;
|
||||
for ( n = rgb_kernel_size; n; --n )
|
||||
{
|
||||
float i = k[0]*ic0 + k[2]*ic2;
|
||||
float q = k[1]*qc1 + k[3]*qc3;
|
||||
float y = k[kernel_size+0]*yc0 + k[kernel_size+1]*yc1 +
|
||||
k[kernel_size+2]*yc2 + k[kernel_size+3]*yc3 + rgb_offset;
|
||||
if ( rescale_out <= 1 )
|
||||
k--;
|
||||
else if ( k < &impl->kernel [kernel_size * 2 * (rescale_out - 1)] )
|
||||
k += kernel_size * 2 - 1;
|
||||
else
|
||||
k -= kernel_size * 2 * (rescale_out - 1) + 2;
|
||||
{
|
||||
int r, g, b = YIQ_TO_RGB( y, i, q, to_rgb, int, r, g );
|
||||
*out++ = PACK_RGB( r, g, b ) - rgb_bias;
|
||||
}
|
||||
}
|
||||
}
|
||||
while ( alignment_count > 1 && --alignment_remain );
|
||||
|
||||
if ( burst_count <= 1 )
|
||||
break;
|
||||
|
||||
to_rgb += 6;
|
||||
|
||||
ROTATE_IQ( i, q, -0.866025f, -0.5f ); /* -120 degrees */
|
||||
}
|
||||
while ( --burst_remain );
|
||||
}
|
||||
|
||||
static void correct_errors( snes_ntsc_rgb_t color, snes_ntsc_rgb_t* out );
|
||||
|
||||
#if DISABLE_CORRECTION
|
||||
#define CORRECT_ERROR( a ) { out [i] += rgb_bias; }
|
||||
#define DISTRIBUTE_ERROR( a, b, c ) { out [i] += rgb_bias; }
|
||||
#else
|
||||
#define CORRECT_ERROR( a ) { out [a] += error; }
|
||||
#define DISTRIBUTE_ERROR( a, b, c ) {\
|
||||
snes_ntsc_rgb_t fourth = (error + 2 * snes_ntsc_rgb_builder) >> 2;\
|
||||
fourth &= (rgb_bias >> 1) - snes_ntsc_rgb_builder;\
|
||||
fourth -= rgb_bias >> 2;\
|
||||
out [a] += fourth;\
|
||||
out [b] += fourth;\
|
||||
out [c] += fourth;\
|
||||
out [i] += error - (fourth * 3);\
|
||||
}
|
||||
#endif
|
||||
|
||||
#define RGB_PALETTE_OUT( rgb, out_ )\
|
||||
{\
|
||||
unsigned char* out = (out_);\
|
||||
snes_ntsc_rgb_t clamped = (rgb);\
|
||||
SNES_NTSC_CLAMP_( clamped, (8 - rgb_bits) );\
|
||||
out [0] = (unsigned char) (clamped >> 21);\
|
||||
out [1] = (unsigned char) (clamped >> 11);\
|
||||
out [2] = (unsigned char) (clamped >> 1);\
|
||||
}
|
||||
|
||||
/* blitter related */
|
||||
|
||||
#ifndef restrict
|
||||
#if defined (__GNUC__)
|
||||
#define restrict __restrict__
|
||||
#elif defined (_MSC_VER) && _MSC_VER > 1300
|
||||
#define restrict __restrict
|
||||
#else
|
||||
/* no support for restricted pointers */
|
||||
#define restrict
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <limits.h>
|
||||
|
||||
#if SNES_NTSC_OUT_DEPTH <= 16
|
||||
#if USHRT_MAX == 0xFFFF
|
||||
typedef unsigned short snes_ntsc_out_t;
|
||||
#else
|
||||
#error "Need 16-bit int type"
|
||||
#endif
|
||||
|
||||
#else
|
||||
#if UINT_MAX == 0xFFFFFFFF
|
||||
typedef unsigned int snes_ntsc_out_t;
|
||||
#elif ULONG_MAX == 0xFFFFFFFF
|
||||
typedef unsigned long snes_ntsc_out_t;
|
||||
#else
|
||||
#error "Need 32-bit int type"
|
||||
#endif
|
||||
|
||||
#endif
|
25
bsnes/filter/super-2xsai.cpp
Normal file
25
bsnes/filter/super-2xsai.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace Filter::Super2xSaI {
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width *= 2;
|
||||
height *= 2;
|
||||
}
|
||||
|
||||
uint32_t temp[512 * 480];
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
const uint16_t *line_in = (const uint16_t*)(((const uint8_t*)input) + pitch * y);
|
||||
uint32_t *line_out = temp + y * width;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
line_out[x] = colortable[line_in[x]];
|
||||
}
|
||||
}
|
||||
|
||||
Super2xSaI32((unsigned char*)temp, width * sizeof(uint32_t), 0, (unsigned char*)output, outpitch, width, height);
|
||||
}
|
||||
|
||||
}
|
25
bsnes/filter/super-eagle.cpp
Normal file
25
bsnes/filter/super-eagle.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace Filter::SuperEagle {
|
||||
|
||||
auto size(uint& width, uint& height) -> void {
|
||||
width *= 2;
|
||||
height *= 2;
|
||||
}
|
||||
|
||||
uint32_t temp[512 * 480];
|
||||
|
||||
auto render(
|
||||
uint32_t* colortable, uint32_t* output, uint outpitch,
|
||||
const uint16_t* input, uint pitch, uint width, uint height
|
||||
) -> void {
|
||||
for(unsigned y = 0; y < height; y++) {
|
||||
const uint16_t *line_in = (const uint16_t*)(((const uint8_t*)input) + pitch * y);
|
||||
uint32_t *line_out = temp + y * width;
|
||||
for(unsigned x = 0; x < width; x++) {
|
||||
line_out[x] = colortable[line_in[x]];
|
||||
}
|
||||
}
|
||||
|
||||
SuperEagle32((unsigned char*)temp, width * sizeof(uint32_t), 0, (unsigned char*)output, outpitch, width, height);
|
||||
}
|
||||
|
||||
}
|
1040
bsnes/gb/Core/apu.c
Normal file
1040
bsnes/gb/Core/apu.c
Normal file
File diff suppressed because it is too large
Load Diff
164
bsnes/gb/Core/apu.h
Normal file
164
bsnes/gb/Core/apu.h
Normal file
@@ -0,0 +1,164 @@
|
||||
#ifndef apu_h
|
||||
#define apu_h
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "gb_struct_def.h"
|
||||
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
/* Speed = 1 / Length (in seconds) */
|
||||
#define DAC_DECAY_SPEED 20000
|
||||
#define DAC_ATTACK_SPEED 20000
|
||||
|
||||
|
||||
/* Divides nicely and never overflows with 4 channels and 8 (1-8) volume levels */
|
||||
#ifdef WIIU
|
||||
/* Todo: Remove this hack once https://github.com/libretro/RetroArch/issues/6252 is fixed*/
|
||||
#define MAX_CH_AMP (0xFF0 / 2)
|
||||
#else
|
||||
#define MAX_CH_AMP 0xFF0
|
||||
#endif
|
||||
#define CH_STEP (MAX_CH_AMP/0xF/8)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/* APU ticks are 2MHz, triggered by an internal APU clock. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int16_t left;
|
||||
int16_t right;
|
||||
} GB_sample_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
double left;
|
||||
double right;
|
||||
} GB_double_sample_t;
|
||||
|
||||
enum GB_CHANNELS {
|
||||
GB_SQUARE_1,
|
||||
GB_SQUARE_2,
|
||||
GB_WAVE,
|
||||
GB_NOISE,
|
||||
GB_N_CHANNELS
|
||||
};
|
||||
|
||||
typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t *sample);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bool global_enable;
|
||||
uint8_t apu_cycles;
|
||||
|
||||
uint8_t samples[GB_N_CHANNELS];
|
||||
bool is_active[GB_N_CHANNELS];
|
||||
|
||||
uint8_t div_divider; // The DIV register ticks the APU at 512Hz, but is then divided
|
||||
// once more to generate 128Hz and 64Hz clocks
|
||||
|
||||
uint8_t lf_div; // The APU runs in 2MHz, but channels 1, 2 and 4 run in 1MHZ so we divide
|
||||
// need to divide the signal.
|
||||
|
||||
uint8_t square_sweep_countdown; // In 128Hz
|
||||
uint8_t square_sweep_calculate_countdown; // In 2 MHz
|
||||
uint16_t new_sweep_sample_legnth;
|
||||
uint16_t shadow_sweep_sample_legnth;
|
||||
bool sweep_enabled;
|
||||
bool sweep_decreasing;
|
||||
|
||||
struct {
|
||||
uint16_t pulse_length; // Reloaded from NRX1 (xorred), in 256Hz DIV ticks
|
||||
uint8_t current_volume; // Reloaded from NRX2
|
||||
uint8_t volume_countdown; // Reloaded from NRX2
|
||||
uint8_t current_sample_index; /* For save state compatibility,
|
||||
highest bit is reused (See NR14/NR24's
|
||||
write code)*/
|
||||
|
||||
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
|
||||
uint16_t sample_length; // From NRX3, NRX4, in APU ticks
|
||||
bool length_enabled; // NRX4
|
||||
|
||||
} square_channels[2];
|
||||
|
||||
struct {
|
||||
bool enable; // NR30
|
||||
uint16_t pulse_length; // Reloaded from NR31 (xorred), in 256Hz DIV ticks
|
||||
uint8_t shift; // NR32
|
||||
uint16_t sample_length; // NR33, NR34, in APU ticks
|
||||
bool length_enabled; // NR34
|
||||
|
||||
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length, xorred $7FF)
|
||||
uint8_t current_sample_index;
|
||||
uint8_t current_sample; // Current sample before shifting.
|
||||
|
||||
int8_t wave_form[32];
|
||||
bool wave_form_just_read;
|
||||
} wave_channel;
|
||||
|
||||
struct {
|
||||
uint16_t pulse_length; // Reloaded from NR41 (xorred), in 256Hz DIV ticks
|
||||
uint8_t current_volume; // Reloaded from NR42
|
||||
uint8_t volume_countdown; // Reloaded from NR42
|
||||
uint16_t lfsr;
|
||||
bool narrow;
|
||||
|
||||
uint16_t sample_countdown; // in APU ticks (Reloaded from sample_length)
|
||||
uint16_t sample_length; // From NR43, in APU ticks
|
||||
bool length_enabled; // NR44
|
||||
|
||||
uint8_t alignment; // If (NR43 & 7) != 0, samples are aligned to 512KHz clock instead of
|
||||
// 1MHz. This variable keeps track of the alignment.
|
||||
|
||||
} noise_channel;
|
||||
|
||||
bool skip_div_event;
|
||||
bool current_lfsr_sample;
|
||||
} GB_apu_t;
|
||||
|
||||
typedef enum {
|
||||
GB_HIGHPASS_OFF, // Do not apply any filter, keep DC offset
|
||||
GB_HIGHPASS_ACCURATE, // Apply a highpass filter similar to the one used on hardware
|
||||
GB_HIGHPASS_REMOVE_DC_OFFSET, // Remove DC Offset without affecting the waveform
|
||||
GB_HIGHPASS_MAX
|
||||
} GB_highpass_mode_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned sample_rate;
|
||||
|
||||
double sample_cycles; // In 8 MHz units
|
||||
double cycles_per_sample;
|
||||
|
||||
// Samples are NOT normalized to MAX_CH_AMP * 4 at this stage!
|
||||
unsigned cycles_since_render;
|
||||
unsigned last_update[GB_N_CHANNELS];
|
||||
GB_sample_t current_sample[GB_N_CHANNELS];
|
||||
GB_sample_t summed_samples[GB_N_CHANNELS];
|
||||
double dac_discharge[GB_N_CHANNELS];
|
||||
|
||||
GB_highpass_mode_t highpass_mode;
|
||||
double highpass_rate;
|
||||
GB_double_sample_t highpass_diff;
|
||||
|
||||
GB_sample_callback_t sample_callback;
|
||||
|
||||
bool rate_set_in_clocks;
|
||||
} GB_apu_output_t;
|
||||
|
||||
void GB_set_sample_rate(GB_gameboy_t *gb, unsigned sample_rate);
|
||||
void GB_set_sample_rate_by_clocks(GB_gameboy_t *gb, double cycles_per_sample); /* Cycles are in 8MHz units */
|
||||
void GB_set_highpass_filter_mode(GB_gameboy_t *gb, GB_highpass_mode_t mode);
|
||||
void GB_apu_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback);
|
||||
#ifdef GB_INTERNAL
|
||||
bool GB_apu_is_DAC_enabled(GB_gameboy_t *gb, unsigned index);
|
||||
void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value);
|
||||
uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg);
|
||||
void GB_apu_div_event(GB_gameboy_t *gb);
|
||||
void GB_apu_init(GB_gameboy_t *gb);
|
||||
void GB_apu_run(GB_gameboy_t *gb);
|
||||
void GB_apu_update_cycles_per_sample(GB_gameboy_t *gb);
|
||||
#endif
|
||||
|
||||
#endif /* apu_h */
|
149
bsnes/gb/Core/camera.c
Normal file
149
bsnes/gb/Core/camera.c
Normal file
@@ -0,0 +1,149 @@
|
||||
#include "gb.h"
|
||||
|
||||
static int noise_seed = 0;
|
||||
|
||||
/* This is not a complete emulation of the camera chip. Only the features used by the GameBoy Camera ROMs are supported.
|
||||
We also do not emulate the timing of the real cart, as it might be actually faster than the webcam. */
|
||||
|
||||
static uint8_t generate_noise(uint8_t x, uint8_t y)
|
||||
{
|
||||
int value = (x + y * 128 + noise_seed);
|
||||
uint8_t *data = (uint8_t *) &value;
|
||||
unsigned hash = 0;
|
||||
|
||||
while ((int *) data != &value + 1) {
|
||||
hash ^= (*data << 8);
|
||||
if (hash & 0x8000) {
|
||||
hash ^= 0x8a00;
|
||||
hash ^= *data;
|
||||
}
|
||||
data++;
|
||||
hash <<= 1;
|
||||
}
|
||||
return (hash >> 8);
|
||||
}
|
||||
|
||||
static long get_processed_color(GB_gameboy_t *gb, uint8_t x, uint8_t y)
|
||||
{
|
||||
if (x >= 128) {
|
||||
x = 0;
|
||||
}
|
||||
if (y >= 112) {
|
||||
y = 0;
|
||||
}
|
||||
|
||||
long color = gb->camera_get_pixel_callback? gb->camera_get_pixel_callback(gb, x, y) : (generate_noise(x, y));
|
||||
|
||||
static const double gain_values[] =
|
||||
{0.8809390, 0.9149149, 0.9457498, 0.9739758,
|
||||
1.0000000, 1.0241412, 1.0466537, 1.0677433,
|
||||
1.0875793, 1.1240310, 1.1568911, 1.1868043,
|
||||
1.2142561, 1.2396208, 1.2743837, 1.3157323,
|
||||
1.3525190, 1.3856512, 1.4157897, 1.4434309,
|
||||
1.4689574, 1.4926697, 1.5148087, 1.5355703,
|
||||
1.5551159, 1.5735801, 1.5910762, 1.6077008,
|
||||
1.6235366, 1.6386550, 1.6531183, 1.6669808};
|
||||
/* Multiply color by gain value */
|
||||
color *= gain_values[gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0x1F];
|
||||
|
||||
|
||||
/* Color is multiplied by the exposure register to simulate exposure. */
|
||||
color = color * ((gb->camera_registers[GB_CAMERA_EXPOSURE_HIGH] << 8) + gb->camera_registers[GB_CAMERA_EXPOSURE_LOW]) / 0x1000;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr)
|
||||
{
|
||||
if (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) {
|
||||
/* Forbid reading the image while the camera is busy. */
|
||||
return 0xFF;
|
||||
}
|
||||
uint8_t tile_x = addr / 0x10 % 0x10;
|
||||
uint8_t tile_y = addr / 0x10 / 0x10;
|
||||
|
||||
uint8_t y = ((addr >> 1) & 0x7) + tile_y * 8;
|
||||
uint8_t bit = addr & 1;
|
||||
|
||||
uint8_t ret = 0;
|
||||
|
||||
for (uint8_t x = tile_x * 8; x < tile_x * 8 + 8; x++) {
|
||||
|
||||
long color = get_processed_color(gb, x, y);
|
||||
|
||||
static const double edge_enhancement_ratios[] = {0.5, 0.75, 1, 1.25, 2, 3, 4, 5};
|
||||
double edge_enhancement_ratio = edge_enhancement_ratios[(gb->camera_registers[GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE] >> 4) & 0x7];
|
||||
if ((gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0xE0) == 0xE0) {
|
||||
color += (color * 4) * edge_enhancement_ratio;
|
||||
color -= get_processed_color(gb, x - 1, y) * edge_enhancement_ratio;
|
||||
color -= get_processed_color(gb, x + 1, y) * edge_enhancement_ratio;
|
||||
color -= get_processed_color(gb, x, y - 1) * edge_enhancement_ratio;
|
||||
color -= get_processed_color(gb, x, y + 1) * edge_enhancement_ratio;
|
||||
}
|
||||
|
||||
|
||||
/* The camera's registers are used as a threshold pattern, which defines the dithering */
|
||||
uint8_t pattern_base = ((x & 3) + (y & 3) * 4) * 3 + GB_CAMERA_DITHERING_PATTERN_START;
|
||||
|
||||
if (color < gb->camera_registers[pattern_base]) {
|
||||
color = 3;
|
||||
}
|
||||
else if (color < gb->camera_registers[pattern_base + 1]) {
|
||||
color = 2;
|
||||
}
|
||||
else if (color < gb->camera_registers[pattern_base + 2]) {
|
||||
color = 1;
|
||||
}
|
||||
else {
|
||||
color = 0;
|
||||
}
|
||||
|
||||
ret <<= 1;
|
||||
ret |= (color >> bit) & 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback)
|
||||
{
|
||||
gb->camera_get_pixel_callback = callback;
|
||||
}
|
||||
|
||||
void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback)
|
||||
{
|
||||
gb->camera_update_request_callback = callback;
|
||||
}
|
||||
|
||||
void GB_camera_updated(GB_gameboy_t *gb)
|
||||
{
|
||||
gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] &= ~1;
|
||||
}
|
||||
|
||||
void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
{
|
||||
addr &= 0x7F;
|
||||
if (addr == GB_CAMERA_SHOOT_AND_1D_FLAGS) {
|
||||
value &= 0x7;
|
||||
noise_seed = rand();
|
||||
if ((value & 1) && !(gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) && gb->camera_update_request_callback) {
|
||||
/* If no callback is set, ignore the write as if the camera is instantly done */
|
||||
gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] |= 1;
|
||||
gb->camera_update_request_callback(gb);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (addr >= 0x36) {
|
||||
GB_log(gb, "Wrote invalid camera register %02x: %2x\n", addr, value);
|
||||
return;
|
||||
}
|
||||
gb->camera_registers[addr] = value;
|
||||
}
|
||||
}
|
||||
uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr)
|
||||
{
|
||||
if ((addr & 0x7F) == 0) {
|
||||
return gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS];
|
||||
}
|
||||
return 0;
|
||||
}
|
29
bsnes/gb/Core/camera.h
Normal file
29
bsnes/gb/Core/camera.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#ifndef camera_h
|
||||
#define camera_h
|
||||
#include <stdint.h>
|
||||
#include "gb_struct_def.h"
|
||||
|
||||
typedef uint8_t (*GB_camera_get_pixel_callback_t)(GB_gameboy_t *gb, uint8_t x, uint8_t y);
|
||||
typedef void (*GB_camera_update_request_callback_t)(GB_gameboy_t *gb);
|
||||
|
||||
enum {
|
||||
GB_CAMERA_SHOOT_AND_1D_FLAGS = 0,
|
||||
GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS = 1,
|
||||
GB_CAMERA_EXPOSURE_HIGH = 2,
|
||||
GB_CAMERA_EXPOSURE_LOW = 3,
|
||||
GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE = 4,
|
||||
GB_CAMERA_DITHERING_PATTERN_START = 6,
|
||||
GB_CAMERA_DITHERING_PATTERN_END = 0x35,
|
||||
};
|
||||
|
||||
uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr);
|
||||
|
||||
void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback);
|
||||
void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback);
|
||||
|
||||
void GB_camera_updated(GB_gameboy_t *gb);
|
||||
|
||||
void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
||||
uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr);
|
||||
|
||||
#endif
|
2459
bsnes/gb/Core/debugger.c
Normal file
2459
bsnes/gb/Core/debugger.c
Normal file
File diff suppressed because it is too large
Load Diff
43
bsnes/gb/Core/debugger.h
Normal file
43
bsnes/gb/Core/debugger.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef debugger_h
|
||||
#define debugger_h
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "gb_struct_def.h"
|
||||
#include "symbol_hash.h"
|
||||
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
#ifdef DISABLE_DEBUGGER
|
||||
#define GB_debugger_run(gb) (void)0
|
||||
#define GB_debugger_handle_async_commands(gb) (void)0
|
||||
#define GB_debugger_ret_hook(gb) (void)0
|
||||
#define GB_debugger_call_hook(gb, addr) (void)addr
|
||||
#define GB_debugger_test_write_watchpoint(gb, addr, value) ((void)addr, (void)value)
|
||||
#define GB_debugger_test_read_watchpoint(gb, addr) (void)addr
|
||||
#else
|
||||
void GB_debugger_run(GB_gameboy_t *gb);
|
||||
void GB_debugger_handle_async_commands(GB_gameboy_t *gb);
|
||||
void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr);
|
||||
void GB_debugger_ret_hook(GB_gameboy_t *gb);
|
||||
void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
||||
void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr);
|
||||
const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr);
|
||||
#endif /* DISABLE_DEBUGGER */
|
||||
#endif
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
bool /* Returns true if debugger waits for more commands. Not relevant for non-GB_INTERNAL */
|
||||
#else
|
||||
void
|
||||
#endif
|
||||
GB_debugger_execute_command(GB_gameboy_t *gb, char *input); /* Destroys input. */
|
||||
|
||||
|
||||
void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path);
|
||||
const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr);
|
||||
bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank); /* result_bank is -1 if unused. */
|
||||
void GB_debugger_break(GB_gameboy_t *gb);
|
||||
bool GB_debugger_is_stopped(GB_gameboy_t *gb);
|
||||
void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled);
|
||||
void GB_debugger_clear_symbols(GB_gameboy_t *gb);
|
||||
#endif /* debugger_h */
|
1227
bsnes/gb/Core/display.c
Normal file
1227
bsnes/gb/Core/display.c
Normal file
File diff suppressed because it is too large
Load Diff
54
bsnes/gb/Core/display.h
Normal file
54
bsnes/gb/Core/display.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#ifndef display_h
|
||||
#define display_h
|
||||
|
||||
#include "gb.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
void GB_display_run(GB_gameboy_t *gb, uint8_t cycles);
|
||||
void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index);
|
||||
void GB_window_related_write(GB_gameboy_t *gb, uint8_t addr, uint8_t value);
|
||||
void GB_STAT_update(GB_gameboy_t *gb);
|
||||
void GB_lcd_off(GB_gameboy_t *gb);
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
GB_PALETTE_NONE,
|
||||
GB_PALETTE_BACKGROUND,
|
||||
GB_PALETTE_OAM,
|
||||
GB_PALETTE_AUTO,
|
||||
} GB_palette_type_t;
|
||||
|
||||
typedef enum {
|
||||
GB_MAP_AUTO,
|
||||
GB_MAP_9800,
|
||||
GB_MAP_9C00,
|
||||
} GB_map_type_t;
|
||||
|
||||
typedef enum {
|
||||
GB_TILESET_AUTO,
|
||||
GB_TILESET_8800,
|
||||
GB_TILESET_8000,
|
||||
} GB_tileset_type_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t image[128];
|
||||
uint8_t x, y, tile, flags;
|
||||
uint16_t oam_addr;
|
||||
bool obscured_by_line_limit;
|
||||
} GB_oam_info_t;
|
||||
|
||||
typedef enum {
|
||||
GB_COLOR_CORRECTION_DISABLED,
|
||||
GB_COLOR_CORRECTION_CORRECT_CURVES,
|
||||
GB_COLOR_CORRECTION_EMULATE_HARDWARE,
|
||||
GB_COLOR_CORRECTION_PRESERVE_BRIGHTNESS,
|
||||
} GB_color_correction_mode_t;
|
||||
|
||||
void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index);
|
||||
void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type);
|
||||
uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height);
|
||||
uint32_t GB_convert_rgb15(GB_gameboy_t *gb, uint16_t color);
|
||||
void GB_set_color_correction_mode(GB_gameboy_t *gb, GB_color_correction_mode_t mode);
|
||||
#endif /* display_h */
|
1084
bsnes/gb/Core/gb.c
Normal file
1084
bsnes/gb/Core/gb.c
Normal file
File diff suppressed because it is too large
Load Diff
725
bsnes/gb/Core/gb.h
Normal file
725
bsnes/gb/Core/gb.h
Normal file
@@ -0,0 +1,725 @@
|
||||
#ifndef GB_h
|
||||
#define GB_h
|
||||
#define typeof __typeof__
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "gb_struct_def.h"
|
||||
#include "save_state.h"
|
||||
|
||||
#include "apu.h"
|
||||
#include "camera.h"
|
||||
#include "debugger.h"
|
||||
#include "display.h"
|
||||
#include "joypad.h"
|
||||
#include "mbc.h"
|
||||
#include "memory.h"
|
||||
#include "printer.h"
|
||||
#include "timing.h"
|
||||
#include "rewind.h"
|
||||
#include "sm83_cpu.h"
|
||||
#include "symbol_hash.h"
|
||||
#include "sgb.h"
|
||||
|
||||
#define GB_STRUCT_VERSION 13
|
||||
|
||||
#define GB_MODEL_FAMILY_MASK 0xF00
|
||||
#define GB_MODEL_DMG_FAMILY 0x000
|
||||
#define GB_MODEL_MGB_FAMILY 0x100
|
||||
#define GB_MODEL_CGB_FAMILY 0x200
|
||||
#define GB_MODEL_PAL_BIT 0x1000
|
||||
#define GB_MODEL_NO_SFC_BIT 0x2000
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
#if __clang__
|
||||
#define UNROLL _Pragma("unroll")
|
||||
#elif __GNUC__
|
||||
#define UNROLL _Pragma("GCC unroll 8")
|
||||
#else
|
||||
#define UNROLL
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||
#define GB_BIG_ENDIAN
|
||||
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
|
||||
#define GB_LITTLE_ENDIAN
|
||||
#else
|
||||
#error Unable to detect endianess
|
||||
#endif
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t seconds;
|
||||
uint8_t minutes;
|
||||
uint8_t hours;
|
||||
uint8_t days;
|
||||
uint8_t high;
|
||||
};
|
||||
uint8_t data[5];
|
||||
} GB_rtc_time_t;
|
||||
|
||||
|
||||
typedef enum {
|
||||
// GB_MODEL_DMG_0 = 0x000,
|
||||
// GB_MODEL_DMG_A = 0x001,
|
||||
GB_MODEL_DMG_B = 0x002,
|
||||
// GB_MODEL_DMG_C = 0x003,
|
||||
GB_MODEL_SGB = 0x004,
|
||||
GB_MODEL_SGB_NTSC = GB_MODEL_SGB,
|
||||
GB_MODEL_SGB_PAL = GB_MODEL_SGB | GB_MODEL_PAL_BIT,
|
||||
GB_MODEL_SGB_NTSC_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT,
|
||||
GB_MODEL_SGB_NO_SFC = GB_MODEL_SGB_NTSC_NO_SFC,
|
||||
GB_MODEL_SGB_PAL_NO_SFC = GB_MODEL_SGB | GB_MODEL_NO_SFC_BIT | GB_MODEL_PAL_BIT,
|
||||
// GB_MODEL_MGB = 0x100,
|
||||
GB_MODEL_SGB2 = 0x101,
|
||||
GB_MODEL_SGB2_NO_SFC = GB_MODEL_SGB2 | GB_MODEL_NO_SFC_BIT,
|
||||
// GB_MODEL_CGB_0 = 0x200,
|
||||
// GB_MODEL_CGB_A = 0x201,
|
||||
// GB_MODEL_CGB_B = 0x202,
|
||||
GB_MODEL_CGB_C = 0x203,
|
||||
// GB_MODEL_CGB_D = 0x204,
|
||||
GB_MODEL_CGB_E = 0x205,
|
||||
GB_MODEL_AGB = 0x206,
|
||||
} GB_model_t;
|
||||
|
||||
enum {
|
||||
GB_REGISTER_AF,
|
||||
GB_REGISTER_BC,
|
||||
GB_REGISTER_DE,
|
||||
GB_REGISTER_HL,
|
||||
GB_REGISTER_SP,
|
||||
GB_REGISTERS_16_BIT /* Count */
|
||||
};
|
||||
|
||||
/* Todo: Actually use these! */
|
||||
enum {
|
||||
GB_CARRY_FLAG = 16,
|
||||
GB_HALF_CARRY_FLAG = 32,
|
||||
GB_SUBSTRACT_FLAG = 64,
|
||||
GB_ZERO_FLAG = 128,
|
||||
};
|
||||
|
||||
#define GB_MAX_IR_QUEUE 256
|
||||
|
||||
enum {
|
||||
/* Joypad and Serial */
|
||||
GB_IO_JOYP = 0x00, // Joypad (R/W)
|
||||
GB_IO_SB = 0x01, // Serial transfer data (R/W)
|
||||
GB_IO_SC = 0x02, // Serial Transfer Control (R/W)
|
||||
|
||||
/* Missing */
|
||||
|
||||
/* Timers */
|
||||
GB_IO_DIV = 0x04, // Divider Register (R/W)
|
||||
GB_IO_TIMA = 0x05, // Timer counter (R/W)
|
||||
GB_IO_TMA = 0x06, // Timer Modulo (R/W)
|
||||
GB_IO_TAC = 0x07, // Timer Control (R/W)
|
||||
|
||||
/* Missing */
|
||||
|
||||
GB_IO_IF = 0x0f, // Interrupt Flag (R/W)
|
||||
|
||||
/* Sound */
|
||||
GB_IO_NR10 = 0x10, // Channel 1 Sweep register (R/W)
|
||||
GB_IO_NR11 = 0x11, // Channel 1 Sound length/Wave pattern duty (R/W)
|
||||
GB_IO_NR12 = 0x12, // Channel 1 Volume Envelope (R/W)
|
||||
GB_IO_NR13 = 0x13, // Channel 1 Frequency lo (Write Only)
|
||||
GB_IO_NR14 = 0x14, // Channel 1 Frequency hi (R/W)
|
||||
/* NR20 does not exist */
|
||||
GB_IO_NR21 = 0x16, // Channel 2 Sound Length/Wave Pattern Duty (R/W)
|
||||
GB_IO_NR22 = 0x17, // Channel 2 Volume Envelope (R/W)
|
||||
GB_IO_NR23 = 0x18, // Channel 2 Frequency lo data (W)
|
||||
GB_IO_NR24 = 0x19, // Channel 2 Frequency hi data (R/W)
|
||||
GB_IO_NR30 = 0x1a, // Channel 3 Sound on/off (R/W)
|
||||
GB_IO_NR31 = 0x1b, // Channel 3 Sound Length
|
||||
GB_IO_NR32 = 0x1c, // Channel 3 Select output level (R/W)
|
||||
GB_IO_NR33 = 0x1d, // Channel 3 Frequency's lower data (W)
|
||||
GB_IO_NR34 = 0x1e, // Channel 3 Frequency's higher data (R/W)
|
||||
/* NR40 does not exist */
|
||||
GB_IO_NR41 = 0x20, // Channel 4 Sound Length (R/W)
|
||||
GB_IO_NR42 = 0x21, // Channel 4 Volume Envelope (R/W)
|
||||
GB_IO_NR43 = 0x22, // Channel 4 Polynomial Counter (R/W)
|
||||
GB_IO_NR44 = 0x23, // Channel 4 Counter/consecutive, Inital (R/W)
|
||||
GB_IO_NR50 = 0x24, // Channel control / ON-OFF / Volume (R/W)
|
||||
GB_IO_NR51 = 0x25, // Selection of Sound output terminal (R/W)
|
||||
GB_IO_NR52 = 0x26, // Sound on/off
|
||||
|
||||
/* Missing */
|
||||
|
||||
GB_IO_WAV_START = 0x30, // Wave pattern start
|
||||
GB_IO_WAV_END = 0x3f, // Wave pattern end
|
||||
|
||||
/* Graphics */
|
||||
GB_IO_LCDC = 0x40, // LCD Control (R/W)
|
||||
GB_IO_STAT = 0x41, // LCDC Status (R/W)
|
||||
GB_IO_SCY = 0x42, // Scroll Y (R/W)
|
||||
GB_IO_SCX = 0x43, // Scroll X (R/W)
|
||||
GB_IO_LY = 0x44, // LCDC Y-Coordinate (R)
|
||||
GB_IO_LYC = 0x45, // LY Compare (R/W)
|
||||
GB_IO_DMA = 0x46, // DMA Transfer and Start Address (W)
|
||||
GB_IO_BGP = 0x47, // BG Palette Data (R/W) - Non CGB Mode Only
|
||||
GB_IO_OBP0 = 0x48, // Object Palette 0 Data (R/W) - Non CGB Mode Only
|
||||
GB_IO_OBP1 = 0x49, // Object Palette 1 Data (R/W) - Non CGB Mode Only
|
||||
GB_IO_WY = 0x4a, // Window Y Position (R/W)
|
||||
GB_IO_WX = 0x4b, // Window X Position minus 7 (R/W)
|
||||
// Has some undocumented compatibility flags written at boot.
|
||||
// Unfortunately it is not readable or writable after boot has finished, so research of this
|
||||
// register is quite limited. The value written to this register, however, can be controlled
|
||||
// in some cases.
|
||||
GB_IO_DMG_EMULATION = 0x4c,
|
||||
|
||||
/* General CGB features */
|
||||
GB_IO_KEY1 = 0x4d, // CGB Mode Only - Prepare Speed Switch
|
||||
|
||||
/* Missing */
|
||||
|
||||
GB_IO_VBK = 0x4f, // CGB Mode Only - VRAM Bank
|
||||
GB_IO_BIOS = 0x50, // Write to disable the BIOS mapping
|
||||
|
||||
/* CGB DMA */
|
||||
GB_IO_HDMA1 = 0x51, // CGB Mode Only - New DMA Source, High
|
||||
GB_IO_HDMA2 = 0x52, // CGB Mode Only - New DMA Source, Low
|
||||
GB_IO_HDMA3 = 0x53, // CGB Mode Only - New DMA Destination, High
|
||||
GB_IO_HDMA4 = 0x54, // CGB Mode Only - New DMA Destination, Low
|
||||
GB_IO_HDMA5 = 0x55, // CGB Mode Only - New DMA Length/Mode/Start
|
||||
|
||||
/* IR */
|
||||
GB_IO_RP = 0x56, // CGB Mode Only - Infrared Communications Port
|
||||
|
||||
/* Missing */
|
||||
|
||||
/* CGB Paletts */
|
||||
GB_IO_BGPI = 0x68, // CGB Mode Only - Background Palette Index
|
||||
GB_IO_BGPD = 0x69, // CGB Mode Only - Background Palette Data
|
||||
GB_IO_OBPI = 0x6a, // CGB Mode Only - Sprite Palette Index
|
||||
GB_IO_OBPD = 0x6b, // CGB Mode Only - Sprite Palette Data
|
||||
|
||||
// 1 is written for DMG ROMs on a CGB. Does not appear to have an effect.
|
||||
GB_IO_DMG_EMULATION_INDICATION = 0x6c, // (FEh) Bit 0 (Read/Write)
|
||||
|
||||
/* Missing */
|
||||
|
||||
GB_IO_SVBK = 0x70, // CGB Mode Only - WRAM Bank
|
||||
GB_IO_UNKNOWN2 = 0x72, // (00h) - Bit 0-7 (Read/Write)
|
||||
GB_IO_UNKNOWN3 = 0x73, // (00h) - Bit 0-7 (Read/Write)
|
||||
GB_IO_UNKNOWN4 = 0x74, // (00h) - Bit 0-7 (Read/Write) - CGB Mode Only
|
||||
GB_IO_UNKNOWN5 = 0x75, // (8Fh) - Bit 4-6 (Read/Write)
|
||||
GB_IO_PCM_12 = 0x76, // Channels 1 and 2 amplitudes
|
||||
GB_IO_PCM_34 = 0x77, // Channels 3 and 4 amplitudes
|
||||
GB_IO_UNKNOWN8 = 0x7F, // Unknown, write only
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
GB_LOG_BOLD = 1,
|
||||
GB_LOG_DASHED_UNDERLINE = 2,
|
||||
GB_LOG_UNDERLINE = 4,
|
||||
GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE
|
||||
} GB_log_attributes;
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
#define LCDC_PERIOD 70224
|
||||
#define CPU_FREQUENCY 0x400000
|
||||
#define SGB_NTSC_FREQUENCY (21477272 / 5)
|
||||
#define SGB_PAL_FREQUENCY (21281370 / 5)
|
||||
#define DIV_CYCLES (0x100)
|
||||
#define INTERNAL_DIV_CYCLES (0x40000)
|
||||
|
||||
#if !defined(MIN)
|
||||
#define MIN(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __a : __b; })
|
||||
#endif
|
||||
|
||||
#if !defined(MAX)
|
||||
#define MAX(A,B) ({ __typeof__(A) __a = (A); __typeof__(B) __b = (B); __a < __b ? __b : __a; })
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb);
|
||||
typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes);
|
||||
typedef char *(*GB_input_callback_t)(GB_gameboy_t *gb);
|
||||
typedef uint32_t (*GB_rgb_encode_callback_t)(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b);
|
||||
typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on, long cycles_since_last_update);
|
||||
typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, bool rumble_on);
|
||||
typedef void (*GB_serial_transfer_bit_start_callback_t)(GB_gameboy_t *gb, bool bit_to_send);
|
||||
typedef bool (*GB_serial_transfer_bit_end_callback_t)(GB_gameboy_t *gb);
|
||||
typedef void (*GB_update_input_hint_callback_t)(GB_gameboy_t *gb);
|
||||
typedef void (*GB_joyp_write_callback_t)(GB_gameboy_t *gb, uint8_t value);
|
||||
typedef void (*GB_icd_pixel_callback_t)(GB_gameboy_t *gb, uint8_t row);
|
||||
typedef void (*GB_icd_hreset_callback_t)(GB_gameboy_t *gb);
|
||||
typedef void (*GB_icd_vreset_callback_t)(GB_gameboy_t *gb);
|
||||
|
||||
typedef struct {
|
||||
bool state;
|
||||
long delay;
|
||||
} GB_ir_queue_item_t;
|
||||
|
||||
struct GB_breakpoint_s;
|
||||
struct GB_watchpoint_s;
|
||||
|
||||
typedef struct {
|
||||
uint8_t pixel; // Color, 0-3
|
||||
uint8_t palette; // Palette, 0 - 7 (CGB); 0-1 in DMG (or just 0 for BG)
|
||||
uint8_t priority; // Sprite priority – 0 in DMG, OAM index in CGB
|
||||
bool bg_priority; // For sprite FIFO – the BG priority bit. For the BG FIFO – the CGB attributes priority bit
|
||||
} GB_fifo_item_t;
|
||||
|
||||
#define GB_FIFO_LENGTH 16
|
||||
typedef struct {
|
||||
GB_fifo_item_t fifo[GB_FIFO_LENGTH];
|
||||
uint8_t read_end;
|
||||
uint8_t write_end;
|
||||
} GB_fifo_t;
|
||||
|
||||
/* When state saving, each section is dumped independently of other sections.
|
||||
This allows adding data to the end of the section without worrying about future compatibility.
|
||||
Some other changes might be "safe" as well.
|
||||
This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64
|
||||
bit platforms. */
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
struct GB_gameboy_s {
|
||||
#else
|
||||
struct GB_gameboy_internal_s {
|
||||
#endif
|
||||
GB_SECTION(header,
|
||||
/* The magic makes sure a state file is:
|
||||
- Indeed a SameBoy state file.
|
||||
- Has the same endianess has the current platform. */
|
||||
volatile uint32_t magic;
|
||||
/* The version field makes sure we don't load save state files with a completely different structure.
|
||||
This happens when struct fields are removed/resized in an backward incompatible manner. */
|
||||
uint32_t version;
|
||||
);
|
||||
|
||||
GB_SECTION(core_state,
|
||||
/* Registers */
|
||||
uint16_t pc;
|
||||
union {
|
||||
uint16_t registers[GB_REGISTERS_16_BIT];
|
||||
struct {
|
||||
uint16_t af,
|
||||
bc,
|
||||
de,
|
||||
hl,
|
||||
sp;
|
||||
};
|
||||
struct {
|
||||
#ifdef GB_BIG_ENDIAN
|
||||
uint8_t a, f,
|
||||
b, c,
|
||||
d, e,
|
||||
h, l;
|
||||
#else
|
||||
uint8_t f, a,
|
||||
c, b,
|
||||
e, d,
|
||||
l, h;
|
||||
#endif
|
||||
};
|
||||
|
||||
};
|
||||
uint8_t ime;
|
||||
uint8_t interrupt_enable;
|
||||
uint8_t cgb_ram_bank;
|
||||
|
||||
/* CPU and General Hardware Flags*/
|
||||
GB_model_t model;
|
||||
bool cgb_mode;
|
||||
bool cgb_double_speed;
|
||||
bool halted;
|
||||
bool stopped;
|
||||
bool boot_rom_finished;
|
||||
bool ime_toggle; /* ei has delayed a effect.*/
|
||||
bool halt_bug;
|
||||
bool just_halted;
|
||||
|
||||
/* Misc state */
|
||||
bool infrared_input;
|
||||
GB_printer_t printer;
|
||||
uint8_t extra_oam[0xff00 - 0xfea0];
|
||||
uint32_t ram_size; // Different between CGB and DMG
|
||||
);
|
||||
|
||||
/* DMA and HDMA */
|
||||
GB_SECTION(dma,
|
||||
bool hdma_on;
|
||||
bool hdma_on_hblank;
|
||||
uint8_t hdma_steps_left;
|
||||
int16_t hdma_cycles; // in 8MHz units
|
||||
uint16_t hdma_current_src, hdma_current_dest;
|
||||
|
||||
uint8_t dma_steps_left;
|
||||
uint8_t dma_current_dest;
|
||||
uint16_t dma_current_src;
|
||||
int16_t dma_cycles;
|
||||
bool is_dma_restarting;
|
||||
uint8_t last_opcode_read; /* Required to emulte HDMA reads from Exxx */
|
||||
bool hdma_starting;
|
||||
);
|
||||
|
||||
/* MBC */
|
||||
GB_SECTION(mbc,
|
||||
uint16_t mbc_rom_bank;
|
||||
uint8_t mbc_ram_bank;
|
||||
uint32_t mbc_ram_size;
|
||||
bool mbc_ram_enable;
|
||||
union {
|
||||
struct {
|
||||
uint8_t bank_low:5;
|
||||
uint8_t bank_high:2;
|
||||
uint8_t mode:1;
|
||||
} mbc1;
|
||||
|
||||
struct {
|
||||
uint8_t rom_bank:4;
|
||||
} mbc2;
|
||||
|
||||
struct {
|
||||
uint8_t rom_bank:7;
|
||||
uint8_t padding:1;
|
||||
uint8_t ram_bank:4;
|
||||
} mbc3;
|
||||
|
||||
struct {
|
||||
uint8_t rom_bank_low;
|
||||
uint8_t rom_bank_high:1;
|
||||
uint8_t ram_bank:4;
|
||||
} mbc5;
|
||||
|
||||
struct {
|
||||
uint8_t bank_low:6;
|
||||
uint8_t bank_high:3;
|
||||
uint8_t mode:1;
|
||||
} huc1;
|
||||
|
||||
struct {
|
||||
uint8_t rom_bank;
|
||||
uint8_t ram_bank;
|
||||
} huc3;
|
||||
};
|
||||
uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */
|
||||
bool camera_registers_mapped;
|
||||
uint8_t camera_registers[0x36];
|
||||
bool rumble_state;
|
||||
);
|
||||
|
||||
|
||||
/* HRAM and HW Registers */
|
||||
GB_SECTION(hram,
|
||||
uint8_t hram[0xFFFF - 0xFF80];
|
||||
uint8_t io_registers[0x80];
|
||||
);
|
||||
|
||||
/* Timing */
|
||||
GB_SECTION(timing,
|
||||
GB_UNIT(display);
|
||||
GB_UNIT(div);
|
||||
uint16_t div_counter;
|
||||
uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */
|
||||
uint16_t serial_cycles;
|
||||
uint16_t serial_length;
|
||||
uint8_t double_speed_alignment;
|
||||
uint8_t serial_count;
|
||||
);
|
||||
|
||||
/* APU */
|
||||
GB_SECTION(apu,
|
||||
GB_apu_t apu;
|
||||
);
|
||||
|
||||
/* RTC */
|
||||
GB_SECTION(rtc,
|
||||
GB_rtc_time_t rtc_real, rtc_latched;
|
||||
uint64_t last_rtc_second;
|
||||
bool rtc_latch;
|
||||
);
|
||||
|
||||
/* Video Display */
|
||||
GB_SECTION(video,
|
||||
uint32_t vram_size; // Different between CGB and DMG
|
||||
uint8_t cgb_vram_bank;
|
||||
uint8_t oam[0xA0];
|
||||
uint8_t background_palettes_data[0x40];
|
||||
uint8_t sprite_palettes_data[0x40];
|
||||
uint8_t position_in_line;
|
||||
bool stat_interrupt_line;
|
||||
uint8_t effective_scx;
|
||||
uint8_t wy_diff;
|
||||
/* The LCDC will skip the first frame it renders after turning it on.
|
||||
On the CGB, a frame is not skipped if the previous frame was skipped as well.
|
||||
See https://www.reddit.com/r/EmuDev/comments/6exyxu/ */
|
||||
|
||||
/* TODO: Drop this and properly emulate the dropped vreset signal*/
|
||||
enum {
|
||||
GB_FRAMESKIP_LCD_TURNED_ON, // On a DMG, the LCD renders a blank screen during this state,
|
||||
// on a CGB, the previous frame is repeated (which might be
|
||||
// blank if the LCD was off for more than a few cycles)
|
||||
GB_FRAMESKIP_FIRST_FRAME_SKIPPED, // This state is 'skipped' when emulating a DMG
|
||||
GB_FRAMESKIP_SECOND_FRAME_RENDERED,
|
||||
} frame_skip_state;
|
||||
bool oam_read_blocked;
|
||||
bool vram_read_blocked;
|
||||
bool oam_write_blocked;
|
||||
bool vram_write_blocked;
|
||||
bool window_disabled_while_active;
|
||||
uint8_t current_line;
|
||||
uint16_t ly_for_comparison;
|
||||
GB_fifo_t bg_fifo, oam_fifo;
|
||||
uint8_t fetcher_x;
|
||||
uint8_t fetcher_y;
|
||||
uint16_t cycles_for_line;
|
||||
uint8_t current_tile;
|
||||
uint8_t current_tile_attributes;
|
||||
uint8_t current_tile_data[2];
|
||||
uint8_t fetcher_state;
|
||||
bool bg_fifo_paused;
|
||||
bool oam_fifo_paused;
|
||||
bool in_window;
|
||||
uint8_t visible_objs[10];
|
||||
uint8_t obj_comparators[10];
|
||||
uint8_t n_visible_objs;
|
||||
uint8_t oam_search_index;
|
||||
uint8_t accessed_oam_row;
|
||||
uint8_t extra_penalty_for_sprite_at_0;
|
||||
uint8_t mode_for_interrupt;
|
||||
bool lyc_interrupt_line;
|
||||
bool cgb_palettes_blocked;
|
||||
uint8_t current_lcd_line; // The LCD can go out of sync since the vsync signal is skipped in some cases.
|
||||
uint32_t cycles_in_stop_mode;
|
||||
);
|
||||
|
||||
/* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */
|
||||
/* This data is reserved on reset and must come last in the struct */
|
||||
GB_SECTION(unsaved,
|
||||
/* ROM */
|
||||
uint8_t *rom;
|
||||
uint32_t rom_size;
|
||||
const GB_cartridge_t *cartridge_type;
|
||||
enum {
|
||||
GB_STANDARD_MBC1_WIRING,
|
||||
GB_MBC1M_WIRING,
|
||||
} mbc1_wiring;
|
||||
|
||||
unsigned pending_cycles;
|
||||
|
||||
/* Various RAMs */
|
||||
uint8_t *ram;
|
||||
uint8_t *vram;
|
||||
uint8_t *mbc_ram;
|
||||
|
||||
/* I/O */
|
||||
uint32_t *screen;
|
||||
uint32_t background_palettes_rgb[0x20];
|
||||
uint32_t sprite_palettes_rgb[0x20];
|
||||
GB_color_correction_mode_t color_correction_mode;
|
||||
bool keys[4][GB_KEY_MAX];
|
||||
|
||||
/* Timing */
|
||||
uint64_t last_sync;
|
||||
uint64_t cycles_since_last_sync; // In 8MHz units
|
||||
|
||||
/* Audio */
|
||||
GB_apu_output_t apu_output;
|
||||
|
||||
/* Callbacks */
|
||||
void *user_data;
|
||||
GB_log_callback_t log_callback;
|
||||
GB_input_callback_t input_callback;
|
||||
GB_input_callback_t async_input_callback;
|
||||
GB_rgb_encode_callback_t rgb_encode_callback;
|
||||
GB_vblank_callback_t vblank_callback;
|
||||
GB_infrared_callback_t infrared_callback;
|
||||
GB_camera_get_pixel_callback_t camera_get_pixel_callback;
|
||||
GB_camera_update_request_callback_t camera_update_request_callback;
|
||||
GB_rumble_callback_t rumble_callback;
|
||||
GB_serial_transfer_bit_start_callback_t serial_transfer_bit_start_callback;
|
||||
GB_serial_transfer_bit_end_callback_t serial_transfer_bit_end_callback;
|
||||
GB_update_input_hint_callback_t update_input_hint_callback;
|
||||
GB_joyp_write_callback_t joyp_write_callback;
|
||||
GB_icd_pixel_callback_t icd_pixel_callback;
|
||||
GB_icd_vreset_callback_t icd_hreset_callback;
|
||||
GB_icd_vreset_callback_t icd_vreset_callback;
|
||||
GB_read_memory_callback_t read_memory_callback;
|
||||
|
||||
/* IR */
|
||||
long cycles_since_ir_change; // In 8MHz units
|
||||
long cycles_since_input_ir_change; // In 8MHz units
|
||||
GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE];
|
||||
size_t ir_queue_length;
|
||||
|
||||
/*** Debugger ***/
|
||||
volatile bool debug_stopped, debug_disable;
|
||||
bool debug_fin_command, debug_next_command;
|
||||
|
||||
/* Breakpoints */
|
||||
uint16_t n_breakpoints;
|
||||
struct GB_breakpoint_s *breakpoints;
|
||||
bool has_jump_to_breakpoints;
|
||||
void *nontrivial_jump_state;
|
||||
bool non_trivial_jump_breakpoint_occured;
|
||||
|
||||
/* SLD (Todo: merge with backtrace) */
|
||||
bool stack_leak_detection;
|
||||
int debug_call_depth;
|
||||
uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */
|
||||
uint16_t addr_for_call_depth[0x200];
|
||||
|
||||
/* Backtrace */
|
||||
unsigned backtrace_size;
|
||||
uint16_t backtrace_sps[0x200];
|
||||
struct {
|
||||
uint16_t bank;
|
||||
uint16_t addr;
|
||||
} backtrace_returns[0x200];
|
||||
|
||||
/* Watchpoints */
|
||||
uint16_t n_watchpoints;
|
||||
struct GB_watchpoint_s *watchpoints;
|
||||
|
||||
/* Symbol tables */
|
||||
GB_symbol_map_t *bank_symbols[0x200];
|
||||
GB_reversed_symbol_map_t reversed_symbol_map;
|
||||
|
||||
/* Ticks command */
|
||||
unsigned long debugger_ticks;
|
||||
|
||||
/* Rewind */
|
||||
#define GB_REWIND_FRAMES_PER_KEY 255
|
||||
size_t rewind_buffer_length;
|
||||
struct {
|
||||
uint8_t *key_state;
|
||||
uint8_t *compressed_states[GB_REWIND_FRAMES_PER_KEY];
|
||||
unsigned pos;
|
||||
} *rewind_sequences; // lasts about 4 seconds
|
||||
size_t rewind_pos;
|
||||
|
||||
/* SGB - saved and allocated optionally */
|
||||
GB_sgb_t *sgb;
|
||||
|
||||
double sgb_intro_jingle_phases[7];
|
||||
double sgb_intro_sweep_phase;
|
||||
double sgb_intro_sweep_previous_sample;
|
||||
|
||||
/* Misc */
|
||||
bool turbo;
|
||||
bool turbo_dont_skip;
|
||||
bool disable_rendering;
|
||||
uint8_t boot_rom[0x900];
|
||||
bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank
|
||||
uint8_t cycles_since_run; // How many cycles have passed since the last call to GB_run(), in 8MHz units
|
||||
double clock_multiplier;
|
||||
);
|
||||
};
|
||||
|
||||
#ifndef GB_INTERNAL
|
||||
struct GB_gameboy_s {
|
||||
char __internal[sizeof(struct GB_gameboy_internal_s)];
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef __printflike
|
||||
/* Missing from Linux headers. */
|
||||
#define __printflike(fmtarg, firstvararg) \
|
||||
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
|
||||
#endif
|
||||
|
||||
void GB_init(GB_gameboy_t *gb, GB_model_t model);
|
||||
bool GB_is_inited(GB_gameboy_t *gb);
|
||||
bool GB_is_cgb(GB_gameboy_t *gb);
|
||||
bool GB_is_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2
|
||||
bool GB_is_hle_sgb(GB_gameboy_t *gb); // Returns true if the model is SGB or SGB2 and the SFC/SNES side is HLE'd
|
||||
GB_model_t GB_get_model(GB_gameboy_t *gb);
|
||||
void GB_free(GB_gameboy_t *gb);
|
||||
void GB_reset(GB_gameboy_t *gb);
|
||||
void GB_switch_model_and_reset(GB_gameboy_t *gb, GB_model_t model);
|
||||
|
||||
/* Returns the time passed, in 8MHz ticks. */
|
||||
uint8_t GB_run(GB_gameboy_t *gb);
|
||||
/* Returns the time passed since the last frame, in nanoseconds */
|
||||
uint64_t GB_run_frame(GB_gameboy_t *gb);
|
||||
|
||||
typedef enum {
|
||||
GB_DIRECT_ACCESS_ROM,
|
||||
GB_DIRECT_ACCESS_RAM,
|
||||
GB_DIRECT_ACCESS_CART_RAM,
|
||||
GB_DIRECT_ACCESS_VRAM,
|
||||
GB_DIRECT_ACCESS_HRAM,
|
||||
GB_DIRECT_ACCESS_IO, /* Warning: Some registers can only be read/written correctly via GB_memory_read/write. */
|
||||
GB_DIRECT_ACCESS_BOOTROM,
|
||||
GB_DIRECT_ACCESS_OAM,
|
||||
GB_DIRECT_ACCESS_BGP,
|
||||
GB_DIRECT_ACCESS_OBP,
|
||||
GB_DIRECT_ACCESS_IE,
|
||||
} GB_direct_access_t;
|
||||
|
||||
/* Returns a mutable pointer to various hardware memories. If that memory is banked, the current bank
|
||||
is returned at *bank, even if only a portion of the memory is banked. */
|
||||
void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank);
|
||||
|
||||
void *GB_get_user_data(GB_gameboy_t *gb);
|
||||
void GB_set_user_data(GB_gameboy_t *gb, void *data);
|
||||
|
||||
|
||||
|
||||
int GB_load_boot_rom(GB_gameboy_t *gb, const char *path);
|
||||
void GB_load_boot_rom_from_buffer(GB_gameboy_t *gb, const unsigned char *buffer, size_t size);
|
||||
int GB_load_rom(GB_gameboy_t *gb, const char *path);
|
||||
void GB_load_rom_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
|
||||
|
||||
int GB_save_battery_size(GB_gameboy_t *gb);
|
||||
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size);
|
||||
int GB_save_battery(GB_gameboy_t *gb, const char *path);
|
||||
|
||||
void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t size);
|
||||
void GB_load_battery(GB_gameboy_t *gb, const char *path);
|
||||
|
||||
void GB_set_turbo_mode(GB_gameboy_t *gb, bool on, bool no_frame_skip);
|
||||
void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled);
|
||||
|
||||
void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3);
|
||||
void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4);
|
||||
|
||||
void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output);
|
||||
|
||||
void GB_set_infrared_input(GB_gameboy_t *gb, bool state);
|
||||
void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, long cycles_after_previous_change); /* In 8MHz units*/
|
||||
|
||||
void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback);
|
||||
void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback);
|
||||
void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback);
|
||||
void GB_set_async_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback);
|
||||
void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback);
|
||||
void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback);
|
||||
void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback);
|
||||
void GB_set_update_input_hint_callback(GB_gameboy_t *gb, GB_update_input_hint_callback_t callback);
|
||||
|
||||
/* These APIs are used when using internal clock */
|
||||
void GB_set_serial_transfer_bit_start_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_start_callback_t callback);
|
||||
void GB_set_serial_transfer_bit_end_callback(GB_gameboy_t *gb, GB_serial_transfer_bit_end_callback_t callback);
|
||||
|
||||
/* These APIs are used when using external clock */
|
||||
bool GB_serial_get_data_bit(GB_gameboy_t *gb);
|
||||
void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data);
|
||||
|
||||
void GB_disconnect_serial(GB_gameboy_t *gb);
|
||||
|
||||
/* For integration with SFC/SNES emulators */
|
||||
void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback);
|
||||
void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback);
|
||||
void GB_set_icd_hreset_callback(GB_gameboy_t *gb, GB_icd_hreset_callback_t callback);
|
||||
void GB_set_icd_vreset_callback(GB_gameboy_t *gb, GB_icd_vreset_callback_t callback);
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
uint32_t GB_get_clock_rate(GB_gameboy_t *gb);
|
||||
#endif
|
||||
void GB_set_clock_multiplier(GB_gameboy_t *gb, double multiplier);
|
||||
|
||||
unsigned GB_get_screen_width(GB_gameboy_t *gb);
|
||||
unsigned GB_get_screen_height(GB_gameboy_t *gb);
|
||||
double GB_get_usual_frame_rate(GB_gameboy_t *gb);
|
||||
unsigned GB_get_player_count(GB_gameboy_t *gb);
|
||||
|
||||
#endif /* GB_h */
|
5
bsnes/gb/Core/gb_struct_def.h
Normal file
5
bsnes/gb/Core/gb_struct_def.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#ifndef gb_struct_def_h
|
||||
#define gb_struct_def_h
|
||||
struct GB_gameboy_s;
|
||||
typedef struct GB_gameboy_s GB_gameboy_t;
|
||||
#endif
|
92
bsnes/gb/Core/joypad.c
Normal file
92
bsnes/gb/Core/joypad.c
Normal file
@@ -0,0 +1,92 @@
|
||||
#include "gb.h"
|
||||
#include <assert.h>
|
||||
|
||||
void GB_update_joyp(GB_gameboy_t *gb)
|
||||
{
|
||||
if (gb->model & GB_MODEL_NO_SFC_BIT) return;
|
||||
|
||||
uint8_t key_selection = 0;
|
||||
uint8_t previous_state = 0;
|
||||
|
||||
/* Todo: add delay to key selection */
|
||||
previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
|
||||
key_selection = (gb->io_registers[GB_IO_JOYP] >> 4) & 3;
|
||||
gb->io_registers[GB_IO_JOYP] &= 0xF0;
|
||||
uint8_t current_player = gb->sgb? (gb->sgb->current_player & (gb->sgb->player_count - 1) & 3) : 0;
|
||||
switch (key_selection) {
|
||||
case 3:
|
||||
if (gb->sgb && gb->sgb->player_count > 1) {
|
||||
gb->io_registers[GB_IO_JOYP] |= 0xF - current_player;
|
||||
}
|
||||
else {
|
||||
/* Nothing is wired, all up */
|
||||
gb->io_registers[GB_IO_JOYP] |= 0x0F;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
/* Direction keys */
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i]) << i;
|
||||
}
|
||||
/* Forbid pressing two opposing keys, this breaks a lot of games; even if it's somewhat possible. */
|
||||
if (!(gb->io_registers[GB_IO_JOYP] & 1)) {
|
||||
gb->io_registers[GB_IO_JOYP] |= 2;
|
||||
}
|
||||
if (!(gb->io_registers[GB_IO_JOYP] & 4)) {
|
||||
gb->io_registers[GB_IO_JOYP] |= 8;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
/* Other keys */
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
gb->io_registers[GB_IO_JOYP] |= (!gb->keys[current_player][i + 4]) << i;
|
||||
}
|
||||
break;
|
||||
|
||||
case 0:
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
gb->io_registers[GB_IO_JOYP] |= (!(gb->keys[current_player][i] || gb->keys[current_player][i + 4])) << i;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Todo: This assumes the keys *always* bounce, which is incorrect when emulating an SGB */
|
||||
if (previous_state != (gb->io_registers[GB_IO_JOYP] & 0xF)) {
|
||||
/* The joypad interrupt DOES occur on CGB (Tested on CGB-E), unlike what some documents say. */
|
||||
gb->io_registers[GB_IO_IF] |= 0x10;
|
||||
}
|
||||
|
||||
gb->io_registers[GB_IO_JOYP] |= 0xC0;
|
||||
}
|
||||
|
||||
void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value)
|
||||
{
|
||||
uint8_t previous_state = gb->io_registers[GB_IO_JOYP] & 0xF;
|
||||
gb->io_registers[GB_IO_JOYP] &= 0xF0;
|
||||
gb->io_registers[GB_IO_JOYP] |= value & 0xF;
|
||||
|
||||
if (previous_state & ~(gb->io_registers[GB_IO_JOYP] & 0xF)) {
|
||||
gb->io_registers[GB_IO_IF] |= 0x10;
|
||||
}
|
||||
gb->io_registers[GB_IO_JOYP] |= 0xC0;
|
||||
}
|
||||
|
||||
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed)
|
||||
{
|
||||
assert(index >= 0 && index < GB_KEY_MAX);
|
||||
gb->keys[0][index] = pressed;
|
||||
GB_update_joyp(gb);
|
||||
}
|
||||
|
||||
void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed)
|
||||
{
|
||||
assert(index >= 0 && index < GB_KEY_MAX);
|
||||
assert(player < 4);
|
||||
gb->keys[player][index] = pressed;
|
||||
GB_update_joyp(gb);
|
||||
}
|
25
bsnes/gb/Core/joypad.h
Normal file
25
bsnes/gb/Core/joypad.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef joypad_h
|
||||
#define joypad_h
|
||||
#include "gb_struct_def.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef enum {
|
||||
GB_KEY_RIGHT,
|
||||
GB_KEY_LEFT,
|
||||
GB_KEY_UP,
|
||||
GB_KEY_DOWN,
|
||||
GB_KEY_A,
|
||||
GB_KEY_B,
|
||||
GB_KEY_SELECT,
|
||||
GB_KEY_START,
|
||||
GB_KEY_MAX
|
||||
} GB_key_t;
|
||||
|
||||
void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed);
|
||||
void GB_set_key_state_for_player(GB_gameboy_t *gb, GB_key_t index, unsigned player, bool pressed);
|
||||
void GB_icd_set_joyp(GB_gameboy_t *gb, uint8_t value);
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
void GB_update_joyp(GB_gameboy_t *gb);
|
||||
#endif
|
||||
#endif /* joypad_h */
|
154
bsnes/gb/Core/mbc.c
Normal file
154
bsnes/gb/Core/mbc.c
Normal file
@@ -0,0 +1,154 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "gb.h"
|
||||
|
||||
const GB_cartridge_t GB_cart_defs[256] = {
|
||||
// From http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header#0147_-_Cartridge_Type
|
||||
/* MBC SUBTYPE RAM BAT. RTC RUMB. */
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 00h ROM ONLY
|
||||
{ GB_MBC1 , GB_STANDARD_MBC, false, false, false, false}, // 01h MBC1
|
||||
{ GB_MBC1 , GB_STANDARD_MBC, true , false, false, false}, // 02h MBC1+RAM
|
||||
{ GB_MBC1 , GB_STANDARD_MBC, true , true , false, false}, // 03h MBC1+RAM+BATTERY
|
||||
[5] =
|
||||
{ GB_MBC2 , GB_STANDARD_MBC, true , false, false, false}, // 05h MBC2
|
||||
{ GB_MBC2 , GB_STANDARD_MBC, true , true , false, false}, // 06h MBC2+BATTERY
|
||||
[8] =
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, true , false, false, false}, // 08h ROM+RAM
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, true , true , false, false}, // 09h ROM+RAM+BATTERY
|
||||
[0xB] =
|
||||
/* Todo: Not supported yet */
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Bh MMM01
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Ch MMM01+RAM
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Dh MMM01+RAM+BATTERY
|
||||
[0xF] =
|
||||
{ GB_MBC3 , GB_STANDARD_MBC, false, true, true , false}, // 0Fh MBC3+TIMER+BATTERY
|
||||
{ GB_MBC3 , GB_STANDARD_MBC, true , true, true , false}, // 10h MBC3+TIMER+RAM+BATTERY
|
||||
{ GB_MBC3 , GB_STANDARD_MBC, false, false, false, false}, // 11h MBC3
|
||||
{ GB_MBC3 , GB_STANDARD_MBC, true , false, false, false}, // 12h MBC3+RAM
|
||||
{ GB_MBC3 , GB_STANDARD_MBC, true , true , false, false}, // 13h MBC3+RAM+BATTERY
|
||||
[0x19] =
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, false}, // 19h MBC5
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, false}, // 1Ah MBC5+RAM
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, false}, // 1Bh MBC5+RAM+BATTERY
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM
|
||||
{ GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY
|
||||
[0xFC] =
|
||||
{ GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA
|
||||
{ GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported)
|
||||
{ GB_HUC3 , GB_STANDARD_MBC, true , true , false, false}, // FEh HuC3 (Todo: Mapper support only)
|
||||
{ GB_HUC1 , GB_STANDARD_MBC, true , true , false, false}, // FFh HuC1+RAM+BATTERY (Todo: No IR bindings)
|
||||
};
|
||||
|
||||
void GB_update_mbc_mappings(GB_gameboy_t *gb)
|
||||
{
|
||||
switch (gb->cartridge_type->mbc_type) {
|
||||
case GB_NO_MBC: return;
|
||||
case GB_MBC1:
|
||||
switch (gb->mbc1_wiring) {
|
||||
case GB_STANDARD_MBC1_WIRING:
|
||||
gb->mbc_rom_bank = gb->mbc1.bank_low | (gb->mbc1.bank_high << 5);
|
||||
if (gb->mbc1.mode == 0) {
|
||||
gb->mbc_ram_bank = 0;
|
||||
gb->mbc_rom0_bank = 0;
|
||||
}
|
||||
else {
|
||||
gb->mbc_ram_bank = gb->mbc1.bank_high;
|
||||
gb->mbc_rom0_bank = gb->mbc1.bank_high << 5;
|
||||
}
|
||||
if ((gb->mbc_rom_bank & 0x1F) == 0) {
|
||||
gb->mbc_rom_bank++;
|
||||
}
|
||||
break;
|
||||
case GB_MBC1M_WIRING:
|
||||
gb->mbc_rom_bank = (gb->mbc1.bank_low & 0xF) | (gb->mbc1.bank_high << 4);
|
||||
if (gb->mbc1.mode == 0) {
|
||||
gb->mbc_ram_bank = 0;
|
||||
gb->mbc_rom0_bank = 0;
|
||||
}
|
||||
else {
|
||||
gb->mbc_rom0_bank = gb->mbc1.bank_high << 4;
|
||||
gb->mbc_ram_bank = 0;
|
||||
}
|
||||
if ((gb->mbc1.bank_low & 0x1F) == 0) {
|
||||
gb->mbc_rom_bank++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case GB_MBC2:
|
||||
gb->mbc_rom_bank = gb->mbc2.rom_bank;
|
||||
if ((gb->mbc_rom_bank & 0xF) == 0) {
|
||||
gb->mbc_rom_bank = 1;
|
||||
}
|
||||
break;
|
||||
case GB_MBC3:
|
||||
gb->mbc_rom_bank = gb->mbc3.rom_bank;
|
||||
gb->mbc_ram_bank = gb->mbc3.ram_bank;
|
||||
if (gb->mbc_rom_bank == 0) {
|
||||
gb->mbc_rom_bank = 1;
|
||||
}
|
||||
break;
|
||||
case GB_MBC5:
|
||||
gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8);
|
||||
gb->mbc_ram_bank = gb->mbc5.ram_bank;
|
||||
break;
|
||||
case GB_HUC1:
|
||||
if (gb->huc1.mode == 0) {
|
||||
gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6);
|
||||
gb->mbc_ram_bank = 0;
|
||||
}
|
||||
else {
|
||||
gb->mbc_rom_bank = gb->huc1.bank_low;
|
||||
gb->mbc_ram_bank = gb->huc1.bank_high;
|
||||
}
|
||||
break;
|
||||
case GB_HUC3:
|
||||
gb->mbc_rom_bank = gb->huc3.rom_bank;
|
||||
gb->mbc_ram_bank = gb->huc3.ram_bank;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GB_configure_cart(GB_gameboy_t *gb)
|
||||
{
|
||||
gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]];
|
||||
|
||||
if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) {
|
||||
GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n");
|
||||
gb->cartridge_type = &GB_cart_defs[0x11];
|
||||
}
|
||||
else if (gb->rom[0x147] != 0 && memcmp(gb->cartridge_type, &GB_cart_defs[0], sizeof(GB_cart_defs[0])) == 0) {
|
||||
GB_log(gb, "Cartridge type %02x is not yet supported.\n", gb->rom[0x147]);
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->has_ram) {
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC2) {
|
||||
gb->mbc_ram_size = 0x200;
|
||||
}
|
||||
else {
|
||||
static const int ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000};
|
||||
gb->mbc_ram_size = ram_sizes[gb->rom[0x149]];
|
||||
}
|
||||
gb->mbc_ram = malloc(gb->mbc_ram_size);
|
||||
|
||||
/* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridges types? */
|
||||
memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size);
|
||||
}
|
||||
|
||||
/* MBC1 has at least 3 types of wiring (We currently support two (Standard and 4bit-MBC1M) of these).
|
||||
See http://forums.nesdev.com/viewtopic.php?f=20&t=14099 */
|
||||
|
||||
/* Attempt to "guess" wiring */
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC1) {
|
||||
if (gb->rom_size >= 0x44000 && memcmp(gb->rom + 0x104, gb->rom + 0x40104, 0x30) == 0) {
|
||||
gb->mbc1_wiring = GB_MBC1M_WIRING;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set MBC5's bank to 1 correctly */
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC5) {
|
||||
gb->mbc5.rom_bank_low = 1;
|
||||
}
|
||||
}
|
32
bsnes/gb/Core/mbc.h
Normal file
32
bsnes/gb/Core/mbc.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#ifndef MBC_h
|
||||
#define MBC_h
|
||||
#include "gb_struct_def.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct {
|
||||
enum {
|
||||
GB_NO_MBC,
|
||||
GB_MBC1,
|
||||
GB_MBC2,
|
||||
GB_MBC3,
|
||||
GB_MBC5,
|
||||
GB_HUC1, /* Todo: HUC1 features are not emulated. Should be unified with the CGB IR sensor API. */
|
||||
GB_HUC3,
|
||||
} mbc_type;
|
||||
enum {
|
||||
GB_STANDARD_MBC,
|
||||
GB_CAMERA,
|
||||
} mbc_subtype;
|
||||
bool has_ram;
|
||||
bool has_battery;
|
||||
bool has_rtc;
|
||||
bool has_rumble;
|
||||
} GB_cartridge_t;
|
||||
|
||||
#ifdef GB_INTERNAL
|
||||
extern const GB_cartridge_t GB_cart_defs[256];
|
||||
void GB_update_mbc_mappings(GB_gameboy_t *gb);
|
||||
void GB_configure_cart(GB_gameboy_t *gb);
|
||||
#endif
|
||||
|
||||
#endif /* MBC_h */
|
1015
bsnes/gb/Core/memory.c
Normal file
1015
bsnes/gb/Core/memory.c
Normal file
File diff suppressed because it is too large
Load Diff
18
bsnes/gb/Core/memory.h
Normal file
18
bsnes/gb/Core/memory.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef memory_h
|
||||
#define memory_h
|
||||
#include "gb_struct_def.h"
|
||||
#include <stdint.h>
|
||||
|
||||
typedef uint8_t (*GB_read_memory_callback_t)(GB_gameboy_t *gb, uint16_t addr, uint8_t data);
|
||||
void GB_set_read_memory_callback(GB_gameboy_t *gb, GB_read_memory_callback_t callback);
|
||||
|
||||
uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr);
|
||||
void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value);
|
||||
#ifdef GB_INTERNAL
|
||||
void GB_dma_run(GB_gameboy_t *gb);
|
||||
void GB_hdma_run(GB_gameboy_t *gb);
|
||||
void GB_trigger_oam_bug(GB_gameboy_t *gb, uint16_t address);
|
||||
void GB_trigger_oam_bug_read_increase(GB_gameboy_t *gb, uint16_t address);
|
||||
#endif
|
||||
|
||||
#endif /* memory_h */
|
216
bsnes/gb/Core/printer.c
Normal file
216
bsnes/gb/Core/printer.c
Normal file
@@ -0,0 +1,216 @@
|
||||
#include "gb.h"
|
||||
|
||||
/* TODO: Emulation is VERY basic and assumes the ROM correctly uses the printer's interface.
|
||||
Incorrect usage is not correctly emulated, as it's not well documented, nor do I
|
||||
have my own GB Printer to figure it out myself.
|
||||
|
||||
It also does not currently emulate communication timeout, which means that a bug
|
||||
might prevent the printer operation until the GameBoy is restarted.
|
||||
|
||||
Also, field mask values are assumed. */
|
||||
|
||||
static void handle_command(GB_gameboy_t *gb)
|
||||
{
|
||||
|
||||
switch (gb->printer.command_id) {
|
||||
case GB_PRINTER_INIT_COMMAND:
|
||||
gb->printer.status = 0;
|
||||
gb->printer.image_offset = 0;
|
||||
break;
|
||||
|
||||
case GB_PRINTER_START_COMMAND:
|
||||
if (gb->printer.command_length == 4) {
|
||||
gb->printer.status = 6; /* Printing */
|
||||
uint32_t image[gb->printer.image_offset];
|
||||
uint8_t palette = gb->printer.command_data[2];
|
||||
uint32_t colors[4] = {gb->rgb_encode_callback(gb, 0xff, 0xff, 0xff),
|
||||
gb->rgb_encode_callback(gb, 0xaa, 0xaa, 0xaa),
|
||||
gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55),
|
||||
gb->rgb_encode_callback(gb, 0x00, 0x00, 0x00)};
|
||||
for (unsigned i = 0; i < gb->printer.image_offset; i++) {
|
||||
image[i] = colors[(palette >> (gb->printer.image[i] * 2)) & 3];
|
||||
}
|
||||
|
||||
if (gb->printer.callback) {
|
||||
gb->printer.callback(gb, image, gb->printer.image_offset / 160,
|
||||
gb->printer.command_data[1] >> 4, gb->printer.command_data[1] & 7,
|
||||
gb->printer.command_data[3] & 0x7F);
|
||||
}
|
||||
|
||||
gb->printer.image_offset = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case GB_PRINTER_DATA_COMMAND:
|
||||
if (gb->printer.command_length == GB_PRINTER_DATA_SIZE) {
|
||||
gb->printer.image_offset %= sizeof(gb->printer.image);
|
||||
gb->printer.status = 8; /* Received 0x280 bytes */
|
||||
|
||||
uint8_t *byte = gb->printer.command_data;
|
||||
|
||||
for (unsigned row = 2; row--; ) {
|
||||
for (unsigned tile_x = 0; tile_x < 160 / 8; tile_x++) {
|
||||
for (unsigned y = 0; y < 8; y++, byte += 2) {
|
||||
for (unsigned x_pixel = 0; x_pixel < 8; x_pixel++) {
|
||||
gb->printer.image[gb->printer.image_offset + tile_x * 8 + x_pixel + y * 160] =
|
||||
((*byte) >> 7) | (((*(byte + 1)) >> 7) << 1);
|
||||
(*byte) <<= 1;
|
||||
(*(byte + 1)) <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gb->printer.image_offset += 8 * 160;
|
||||
}
|
||||
}
|
||||
|
||||
case GB_PRINTER_NOP_COMMAND:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void byte_reieve_completed(GB_gameboy_t *gb, uint8_t byte_received)
|
||||
{
|
||||
gb->printer.byte_to_send = 0;
|
||||
switch (gb->printer.command_state) {
|
||||
case GB_PRINTER_COMMAND_MAGIC1:
|
||||
if (byte_received != 0x88) {
|
||||
return;
|
||||
}
|
||||
gb->printer.status &= ~1;
|
||||
gb->printer.command_length = 0;
|
||||
gb->printer.checksum = 0;
|
||||
break;
|
||||
|
||||
case GB_PRINTER_COMMAND_MAGIC2:
|
||||
if (byte_received != 0x33) {
|
||||
if (byte_received != 0x88) {
|
||||
gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case GB_PRINTER_COMMAND_ID:
|
||||
gb->printer.command_id = byte_received & 0xF;
|
||||
break;
|
||||
|
||||
case GB_PRINTER_COMMAND_COMPRESSION:
|
||||
gb->printer.compression = byte_received & 1;
|
||||
break;
|
||||
|
||||
case GB_PRINTER_COMMAND_LENGTH_LOW:
|
||||
gb->printer.length_left = byte_received;
|
||||
break;
|
||||
|
||||
case GB_PRINTER_COMMAND_LENGTH_HIGH:
|
||||
gb->printer.length_left |= (byte_received & 3) << 8;
|
||||
break;
|
||||
|
||||
case GB_PRINTER_COMMAND_DATA:
|
||||
if (gb->printer.command_length != GB_PRINTER_MAX_COMMAND_LENGTH) {
|
||||
if (gb->printer.compression) {
|
||||
if (!gb->printer.compression_run_lenth) {
|
||||
gb->printer.compression_run_is_compressed = byte_received & 0x80;
|
||||
gb->printer.compression_run_lenth = (byte_received & 0x7F) + 1 + gb->printer.compression_run_is_compressed;
|
||||
}
|
||||
else if (gb->printer.compression_run_is_compressed) {
|
||||
while (gb->printer.compression_run_lenth) {
|
||||
gb->printer.command_data[gb->printer.command_length++] = byte_received;
|
||||
gb->printer.compression_run_lenth--;
|
||||
if (gb->printer.command_length == GB_PRINTER_MAX_COMMAND_LENGTH) {
|
||||
gb->printer.compression_run_lenth = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
gb->printer.command_data[gb->printer.command_length++] = byte_received;
|
||||
gb->printer.compression_run_lenth--;
|
||||
}
|
||||
}
|
||||
else {
|
||||
gb->printer.command_data[gb->printer.command_length++] = byte_received;
|
||||
}
|
||||
}
|
||||
gb->printer.length_left--;
|
||||
break;
|
||||
|
||||
case GB_PRINTER_COMMAND_CHECKSUM_LOW:
|
||||
gb->printer.checksum ^= byte_received;
|
||||
break;
|
||||
|
||||
case GB_PRINTER_COMMAND_CHECKSUM_HIGH:
|
||||
gb->printer.checksum ^= byte_received << 8;
|
||||
if (gb->printer.checksum) {
|
||||
gb->printer.status |= 1; /* Checksum error*/
|
||||
gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
|
||||
return;
|
||||
}
|
||||
gb->printer.byte_to_send = 0x81;
|
||||
|
||||
break;
|
||||
case GB_PRINTER_COMMAND_ACTIVE:
|
||||
if ((gb->printer.command_id & 0xF) == GB_PRINTER_INIT_COMMAND) {
|
||||
/* Games expect INIT commands to return 0? */
|
||||
gb->printer.byte_to_send = 0;
|
||||
}
|
||||
else {
|
||||
gb->printer.byte_to_send = gb->printer.status;
|
||||
}
|
||||
break;
|
||||
case GB_PRINTER_COMMAND_STATUS:
|
||||
|
||||
/* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */
|
||||
if (gb->printer.status == 6) {
|
||||
gb->printer.status = 4; /* Done */
|
||||
}
|
||||
|
||||
gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1;
|
||||
handle_command(gb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gb->printer.command_state >= GB_PRINTER_COMMAND_ID && gb->printer.command_state < GB_PRINTER_COMMAND_CHECKSUM_LOW) {
|
||||
gb->printer.checksum += byte_received;
|
||||
}
|
||||
|
||||
if (gb->printer.command_state != GB_PRINTER_COMMAND_DATA) {
|
||||
gb->printer.command_state++;
|
||||
}
|
||||
|
||||
if (gb->printer.command_state == GB_PRINTER_COMMAND_DATA) {
|
||||
if (gb->printer.length_left == 0) {
|
||||
gb->printer.command_state++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void serial_start(GB_gameboy_t *gb, bool bit_received)
|
||||
{
|
||||
gb->printer.byte_being_recieved <<= 1;
|
||||
gb->printer.byte_being_recieved |= bit_received;
|
||||
gb->printer.bits_recieved++;
|
||||
if (gb->printer.bits_recieved == 8) {
|
||||
byte_reieve_completed(gb, gb->printer.byte_being_recieved);
|
||||
gb->printer.bits_recieved = 0;
|
||||
gb->printer.byte_being_recieved = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static bool serial_end(GB_gameboy_t *gb)
|
||||
{
|
||||
bool ret = gb->printer.bit_to_send;
|
||||
gb->printer.bit_to_send = gb->printer.byte_to_send & 0x80;
|
||||
gb->printer.byte_to_send <<= 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback)
|
||||
{
|
||||
memset(&gb->printer, 0, sizeof(gb->printer));
|
||||
GB_set_serial_transfer_bit_start_callback(gb, serial_start);
|
||||
GB_set_serial_transfer_bit_end_callback(gb, serial_end);
|
||||
gb->printer.callback = callback;
|
||||
}
|
63
bsnes/gb/Core/printer.h
Normal file
63
bsnes/gb/Core/printer.h
Normal file
@@ -0,0 +1,63 @@
|
||||
#ifndef printer_h
|
||||
#define printer_h
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "gb_struct_def.h"
|
||||
#define GB_PRINTER_MAX_COMMAND_LENGTH 0x280
|
||||
#define GB_PRINTER_DATA_SIZE 0x280
|
||||
|
||||
typedef void (*GB_print_image_callback_t)(GB_gameboy_t *gb,
|
||||
uint32_t *image,
|
||||
uint8_t height,
|
||||
uint8_t top_margin,
|
||||
uint8_t bottom_margin,
|
||||
uint8_t exposure);
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* Communication state machine */
|
||||
|
||||
enum {
|
||||
GB_PRINTER_COMMAND_MAGIC1,
|
||||
GB_PRINTER_COMMAND_MAGIC2,
|
||||
GB_PRINTER_COMMAND_ID,
|
||||
GB_PRINTER_COMMAND_COMPRESSION,
|
||||
GB_PRINTER_COMMAND_LENGTH_LOW,
|
||||
GB_PRINTER_COMMAND_LENGTH_HIGH,
|
||||
GB_PRINTER_COMMAND_DATA,
|
||||
GB_PRINTER_COMMAND_CHECKSUM_LOW,
|
||||
GB_PRINTER_COMMAND_CHECKSUM_HIGH,
|
||||
GB_PRINTER_COMMAND_ACTIVE,
|
||||
GB_PRINTER_COMMAND_STATUS,
|
||||
} command_state : 8;
|
||||
enum {
|
||||
GB_PRINTER_INIT_COMMAND = 1,
|
||||
GB_PRINTER_START_COMMAND = 2,
|
||||
GB_PRINTER_DATA_COMMAND = 4,
|
||||
GB_PRINTER_NOP_COMMAND = 0xF,
|
||||
} command_id : 8;
|
||||
bool compression;
|
||||
uint16_t length_left;
|
||||
uint8_t command_data[GB_PRINTER_MAX_COMMAND_LENGTH];
|
||||
uint16_t command_length;
|
||||
uint16_t checksum;
|
||||
uint8_t status;
|
||||
uint8_t byte_to_send;
|
||||
|
||||
uint8_t image[160 * 200];
|
||||
uint16_t image_offset;
|
||||
|
||||
GB_print_image_callback_t callback;
|
||||
|
||||
uint8_t compression_run_lenth;
|
||||
bool compression_run_is_compressed;
|
||||
|
||||
uint8_t bits_recieved;
|
||||
uint8_t byte_being_recieved;
|
||||
bool bit_to_send;
|
||||
} GB_printer_t;
|
||||
|
||||
|
||||
void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback);
|
||||
#endif
|
38
bsnes/gb/Core/random.c
Normal file
38
bsnes/gb/Core/random.c
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "random.h"
|
||||
#include <time.h>
|
||||
|
||||
static uint64_t seed;
|
||||
static bool enabled = true;
|
||||
|
||||
uint8_t GB_random(void)
|
||||
{
|
||||
if (!enabled) return 0;
|
||||
|
||||
seed *= 0x27BB2EE687B0B0FDL;
|
||||
seed += 0xB504F32D;
|
||||
return seed >> 56;
|
||||
}
|
||||
|
||||
uint32_t GB_random32(void)
|
||||
{
|
||||
GB_random();
|
||||
return seed >> 32;
|
||||
}
|
||||
|
||||
void GB_random_seed(uint64_t new_seed)
|
||||
{
|
||||
seed = new_seed;
|
||||
}
|
||||
|
||||
void GB_random_set_enabled(bool enable)
|
||||
{
|
||||
enabled = enable;
|
||||
}
|
||||
|
||||
static void __attribute__((constructor)) init_seed(void)
|
||||
{
|
||||
seed = time(NULL);
|
||||
for (unsigned i = 64; i--;) {
|
||||
GB_random();
|
||||
}
|
||||
}
|
12
bsnes/gb/Core/random.h
Normal file
12
bsnes/gb/Core/random.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#ifndef random_h
|
||||
#define random_h
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
uint8_t GB_random(void);
|
||||
uint32_t GB_random32(void);
|
||||
void GB_random_seed(uint64_t seed);
|
||||
void GB_random_set_enabled(bool enable);
|
||||
|
||||
#endif /* random_h */
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user