mirror of
https://github.com/stenzek/duckstation.git
synced 2026-02-16 11:24:36 +00:00
Compare commits
1192 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9413f6d94 | ||
|
|
0110295f2c | ||
|
|
d8ce98a1e2 | ||
|
|
2c1999e622 | ||
|
|
0d13e1013d | ||
|
|
f7426b0988 | ||
|
|
f5d7fec914 | ||
|
|
d4143399eb | ||
|
|
748e2e9a70 | ||
|
|
0c71a97e02 | ||
|
|
5a66639d78 | ||
|
|
e7fc904cf4 | ||
|
|
e8832bf552 | ||
|
|
b811b78c09 | ||
|
|
935c73fb6c | ||
|
|
8b8dbc6812 | ||
|
|
d07d0b7729 | ||
|
|
dfaf0797f0 | ||
|
|
63ef510a0c | ||
|
|
63b04f6479 | ||
|
|
092aaf1597 | ||
|
|
eabda2979c | ||
|
|
4e10d100db | ||
|
|
c6c67b7b27 | ||
|
|
c8efade20c | ||
|
|
9f73791343 | ||
|
|
de688615ff | ||
|
|
e75f1c1b6d | ||
|
|
32ee362228 | ||
|
|
b9695eb547 | ||
|
|
22963ecbf3 | ||
|
|
bfa2b72e08 | ||
|
|
6ab926ab28 | ||
|
|
5f6fce9d98 | ||
|
|
a03eb9f352 | ||
|
|
2446e945a7 | ||
|
|
3b5739eca8 | ||
|
|
6497aa9f4a | ||
|
|
d1536fdc62 | ||
|
|
ef97131c9d | ||
|
|
556913dad3 | ||
|
|
3857d2426d | ||
|
|
537f833658 | ||
|
|
4f75f2d266 | ||
|
|
53fb55ff15 | ||
|
|
ee3aa0dc4d | ||
|
|
d356b95819 | ||
|
|
515b85fa36 | ||
|
|
1b1a5996ec | ||
|
|
eb993ae330 | ||
|
|
18114806f8 | ||
|
|
6f22ea0ffa | ||
|
|
590275a20e | ||
|
|
8b11511a9b | ||
|
|
bb5d4017cf | ||
|
|
c9b874eeec | ||
|
|
8c77c4a493 | ||
|
|
0d5c0308db | ||
|
|
c043643c4d | ||
|
|
4a240958ff | ||
|
|
e7238c236e | ||
|
|
a47a8daa62 | ||
|
|
d17833d6c6 | ||
|
|
482fdc8f35 | ||
|
|
c700e29c51 | ||
|
|
3a5b24b37e | ||
|
|
275fedbaf2 | ||
|
|
cc0cc6fbb9 | ||
|
|
a77edc6436 | ||
|
|
fc578b7ec7 | ||
|
|
3bc34d40a1 | ||
|
|
1a63edec27 | ||
|
|
6ad2b72c2e | ||
|
|
b560142015 | ||
|
|
c0dff8ab0b | ||
|
|
ccc25fc131 | ||
|
|
b20b1a2fa8 | ||
|
|
837fb6128b | ||
|
|
011df33fc4 | ||
|
|
d416dbb461 | ||
|
|
c03c4cb95a | ||
|
|
5a9abd55a1 | ||
|
|
c6ed19cc7b | ||
|
|
701edb335a | ||
|
|
3c910782ca | ||
|
|
105376b1b3 | ||
|
|
d7962fdac7 | ||
|
|
bfdf33868f | ||
|
|
8582e2770d | ||
|
|
76bf5b5aa2 | ||
|
|
f943aa0489 | ||
|
|
10daea9faf | ||
|
|
8b79e2c006 | ||
|
|
4591e75f5b | ||
|
|
662d6e9711 | ||
|
|
01b3b5066d | ||
|
|
2c2af6c751 | ||
|
|
05545566e7 | ||
|
|
39a4aafee5 | ||
|
|
43d1495a63 | ||
|
|
d455b61d5e | ||
|
|
7b4650700f | ||
|
|
d4997c6fb9 | ||
|
|
d0667ba32a | ||
|
|
3e9fdf22bf | ||
|
|
8f9bbb0bba | ||
|
|
469010868e | ||
|
|
929863e7d6 | ||
|
|
2ae6163523 | ||
|
|
0d911f1ccd | ||
|
|
2029702eda | ||
|
|
8647423fe9 | ||
|
|
4c0d3c5c9d | ||
|
|
26f942cb2e | ||
|
|
b9f72cefc4 | ||
|
|
281af1d23a | ||
|
|
fd269ec9dd | ||
|
|
55b61e9030 | ||
|
|
a50052bd92 | ||
|
|
62fe03782e | ||
|
|
3fec066a92 | ||
|
|
adb1857d8c | ||
|
|
3b85057728 | ||
|
|
3f981f0ceb | ||
|
|
e0dd45c35e | ||
|
|
1e8752759d | ||
|
|
c54842830f | ||
|
|
626b2ecec1 | ||
|
|
43bb69fb6b | ||
|
|
904680f0df | ||
|
|
b7af4f8d62 | ||
|
|
c7e08ec420 | ||
|
|
ad2fd0d682 | ||
|
|
33e8b8230f | ||
|
|
276f2e2ceb | ||
|
|
2a6ac1115a | ||
|
|
91092847f8 | ||
|
|
cbdf722dd6 | ||
|
|
dac9cdd04c | ||
|
|
1a9120135f | ||
|
|
bd43241f3e | ||
|
|
260e39a516 | ||
|
|
590513350c | ||
|
|
23e102b90a | ||
|
|
d1a5b89f0a | ||
|
|
18405a713c | ||
|
|
51babf4009 | ||
|
|
55415d3ce0 | ||
|
|
cc582912b6 | ||
|
|
c17fb88426 | ||
|
|
bb2e35b732 | ||
|
|
2dabccffc6 | ||
|
|
85b4a02f20 | ||
|
|
98bad30af8 | ||
|
|
b09da307b5 | ||
|
|
b267020d07 | ||
|
|
dc0eb4cb04 | ||
|
|
cae5bc1712 | ||
|
|
61e4e6f140 | ||
|
|
0cdd1a70c0 | ||
|
|
3a7d9f1725 | ||
|
|
61d0af30a2 | ||
|
|
41be96ef93 | ||
|
|
ec60fa3c9d | ||
|
|
2dbb3325ff | ||
|
|
e1bf362d13 | ||
|
|
bed1abf0b8 | ||
|
|
4540a190c1 | ||
|
|
556cd6d168 | ||
|
|
96a36f4850 | ||
|
|
48f78a9c41 | ||
|
|
55a1d0437d | ||
|
|
98b4101cd7 | ||
|
|
9d08f2e277 | ||
|
|
5875b738dc | ||
|
|
7a48bcc585 | ||
|
|
e697d9aa33 | ||
|
|
e132cac0e5 | ||
|
|
e614522de5 | ||
|
|
d8ea9c2983 | ||
|
|
582c77b604 | ||
|
|
a563f99191 | ||
|
|
6f60960a1d | ||
|
|
7d036d6fee | ||
|
|
5aeabf028b | ||
|
|
122cf67bb3 | ||
|
|
35f8ea13d9 | ||
|
|
ef7d135492 | ||
|
|
9d80c48d6f | ||
|
|
99bedb0f0b | ||
|
|
b43b24ce8d | ||
|
|
84dd24902c | ||
|
|
678ef7d7b6 | ||
|
|
c6ced52bc7 | ||
|
|
fa2cca1429 | ||
|
|
c4386d6e71 | ||
|
|
9abfbdd86f | ||
|
|
c61dc0dcbe | ||
|
|
c10e62e729 | ||
|
|
f98f8ed726 | ||
|
|
2938c946f9 | ||
|
|
c9c2f1bd21 | ||
|
|
c03f48c907 | ||
|
|
e8515322f4 | ||
|
|
24c7021d0c | ||
|
|
3c2b11fccb | ||
|
|
43a682b358 | ||
|
|
84c30703b2 | ||
|
|
9ada3c8e6f | ||
|
|
4bb3fb48f9 | ||
|
|
4e583890ea | ||
|
|
9316ce532b | ||
|
|
ed4ed259e3 | ||
|
|
d23474f552 | ||
|
|
061268e76f | ||
|
|
35dc530b9a | ||
|
|
700f916a34 | ||
|
|
b96136a9ec | ||
|
|
0b4363679c | ||
|
|
368820d6e9 | ||
|
|
1320e4fdab | ||
|
|
c119ccffc8 | ||
|
|
b776541164 | ||
|
|
c5dd0ef2ae | ||
|
|
9e394c31bf | ||
|
|
1b6780386f | ||
|
|
a5dfc68ac9 | ||
|
|
16a32bf696 | ||
|
|
1b16ba3d98 | ||
|
|
e79dff1731 | ||
|
|
52f5ca7e28 | ||
|
|
0d473e8681 | ||
|
|
8cafe856f0 | ||
|
|
24dd49e05d | ||
|
|
31727b51a7 | ||
|
|
8ceadf802c | ||
|
|
791012d151 | ||
|
|
12dd62a738 | ||
|
|
bcfdb19c36 | ||
|
|
557f3c9ca1 | ||
|
|
b7752ff30b | ||
|
|
f47784cb41 | ||
|
|
6eeca57a06 | ||
|
|
b3ce2b21c0 | ||
|
|
3f96473fb5 | ||
|
|
e01d66d18e | ||
|
|
689b62e065 | ||
|
|
516d685dd0 | ||
|
|
6c6fdeb15e | ||
|
|
e54ba23c4f | ||
|
|
ddcc29c8a6 | ||
|
|
a84bf0d8cf | ||
|
|
4a60cc4c1e | ||
|
|
b1d4d5db5e | ||
|
|
b107cdee53 | ||
|
|
59810bf8db | ||
|
|
6a122623fa | ||
|
|
d62e6ffbb3 | ||
|
|
f61427017d | ||
|
|
76ec38ee47 | ||
|
|
41a9231a87 | ||
|
|
b62f31fd96 | ||
|
|
253b115b11 | ||
|
|
10135e08a2 | ||
|
|
baf8b1af43 | ||
|
|
2782172b7e | ||
|
|
c31ec6d428 | ||
|
|
e8e8fd9ffa | ||
|
|
3c3bfdea93 | ||
|
|
4433197665 | ||
|
|
0dee202095 | ||
|
|
a0ca20a821 | ||
|
|
5b4aff5edf | ||
|
|
63562e4527 | ||
|
|
f5188c7fef | ||
|
|
fb48e8f093 | ||
|
|
4e802e90ca | ||
|
|
8c8f29c047 | ||
|
|
3c012ec6ef | ||
|
|
a927bfbe50 | ||
|
|
cbee8fab66 | ||
|
|
d4f52f1ec7 | ||
|
|
914b9bf738 | ||
|
|
699d599d00 | ||
|
|
9da35be0a8 | ||
|
|
29690c4635 | ||
|
|
00512f6cea | ||
|
|
f74a4ad6ee | ||
|
|
2cd4357a5a | ||
|
|
6fbd970b55 | ||
|
|
4c9e0299ed | ||
|
|
e81df6bddd | ||
|
|
9c30990f8f | ||
|
|
eeca8305f4 | ||
|
|
e66d353549 | ||
|
|
0decadd86d | ||
|
|
901f862737 | ||
|
|
02948f988d | ||
|
|
ffa3744b3b | ||
|
|
d44de3a9dc | ||
|
|
3b433d8d3e | ||
|
|
93959a9d88 | ||
|
|
6ad8a6666c | ||
|
|
434f75658c | ||
|
|
a27f220f79 | ||
|
|
37f9f118c4 | ||
|
|
60232c390a | ||
|
|
1ae4b94eb2 | ||
|
|
2b5cfb272c | ||
|
|
5746dcdbd4 | ||
|
|
b5ffbfe826 | ||
|
|
b3fd07e1b5 | ||
|
|
220f2f4845 | ||
|
|
d101f7cf9d | ||
|
|
fc58d8e8cc | ||
|
|
a0a24c7752 | ||
|
|
16ad54143b | ||
|
|
0884954d10 | ||
|
|
6712f6b649 | ||
|
|
c18fa06f0c | ||
|
|
873b9f622a | ||
|
|
e5915846a4 | ||
|
|
13cba122ef | ||
|
|
fd166a4485 | ||
|
|
97971464d1 | ||
|
|
29331ef679 | ||
|
|
69f14feec1 | ||
|
|
ba2710fb4c | ||
|
|
bc5fb850d4 | ||
|
|
43d01776dc | ||
|
|
bb572741ae | ||
|
|
5769830b72 | ||
|
|
d23645e02f | ||
|
|
ce965e89ca | ||
|
|
fdeef65676 | ||
|
|
54f5563321 | ||
|
|
d73fedcef1 | ||
|
|
692eb035ec | ||
|
|
25847494d5 | ||
|
|
5996945b37 | ||
|
|
a5f9aa11e1 | ||
|
|
54c7fc6b08 | ||
|
|
1eab296ec3 | ||
|
|
bc6444b6ff | ||
|
|
5d7d848fe6 | ||
|
|
19032750b4 | ||
|
|
2c2195b116 | ||
|
|
11992bde4e | ||
|
|
8c241ed8de | ||
|
|
b4d1dcfe4b | ||
|
|
15652b4c1f | ||
|
|
df98a0b04e | ||
|
|
4fafb34251 | ||
|
|
ad0d16e243 | ||
|
|
64fc560314 | ||
|
|
9d7f4523b0 | ||
|
|
693b070889 | ||
|
|
9951921a6d | ||
|
|
b0398f5aa7 | ||
|
|
6a04803502 | ||
|
|
5add87c8ad | ||
|
|
c6da182232 | ||
|
|
8d56a3d186 | ||
|
|
bfd4273f2c | ||
|
|
d860c14267 | ||
|
|
5ff3299384 | ||
|
|
4cc2883224 | ||
|
|
419726f4cc | ||
|
|
573c8370d7 | ||
|
|
f26b094cf7 | ||
|
|
24c373245e | ||
|
|
d86fc03ecb | ||
|
|
ee60c02e0a | ||
|
|
5eca471624 | ||
|
|
e80cd69811 | ||
|
|
3f11cb1de1 | ||
|
|
7de3fafff6 | ||
|
|
de075fffc3 | ||
|
|
c9b8dc13fd | ||
|
|
f832dca975 | ||
|
|
f3cdfe97a7 | ||
|
|
7f22dc4199 | ||
|
|
9afd122f85 | ||
|
|
845cd37835 | ||
|
|
98a4e59f52 | ||
|
|
b83a9cfe9a | ||
|
|
a5078d7e35 | ||
|
|
25b0b72566 | ||
|
|
a6e1b3c4ad | ||
|
|
cdefcf7359 | ||
|
|
79012d5288 | ||
|
|
0534f36b8d | ||
|
|
e3de44d127 | ||
|
|
8f5c20bdf5 | ||
|
|
5cd261a5b2 | ||
|
|
68f97a8116 | ||
|
|
c0dfc49db3 | ||
|
|
c3b0a87029 | ||
|
|
9056db0a0f | ||
|
|
73f6521452 | ||
|
|
e3262fc0a4 | ||
|
|
cc0c41eb11 | ||
|
|
f7b0c0f493 | ||
|
|
35fdae0d95 | ||
|
|
5ec8b64aa1 | ||
|
|
2898d27c53 | ||
|
|
affffca3da | ||
|
|
9ff8130e40 | ||
|
|
c9b75f7490 | ||
|
|
bf1d51b5d8 | ||
|
|
4b7820d1e5 | ||
|
|
f369e3c476 | ||
|
|
04884257d6 | ||
|
|
498b00a8e8 | ||
|
|
b3bf9f3f10 | ||
|
|
f606999a04 | ||
|
|
9708332a80 | ||
|
|
fc3780815d | ||
|
|
600e8d5dd0 | ||
|
|
4eee5ebdb7 | ||
|
|
7f008ea5c7 | ||
|
|
6ff73f35bd | ||
|
|
2501547acb | ||
|
|
0de34d7bf7 | ||
|
|
c9ef3ec1a3 | ||
|
|
2ec26e54b7 | ||
|
|
0d281538cf | ||
|
|
a63a6b7a20 | ||
|
|
40257e5b20 | ||
|
|
c9240eea72 | ||
|
|
4e87b30b40 | ||
|
|
04a187afb2 | ||
|
|
75aa52ab06 | ||
|
|
9294bf6be3 | ||
|
|
187de65f50 | ||
|
|
d588c26cf6 | ||
|
|
3ffbbe82e8 | ||
|
|
bea6f0beb4 | ||
|
|
4f32f2f915 | ||
|
|
aa0eb5153e | ||
|
|
dde5a4e2d8 | ||
|
|
6d501bff56 | ||
|
|
041880df34 | ||
|
|
5690aef057 | ||
|
|
68f3261582 | ||
|
|
f911e90e17 | ||
|
|
02377b1a92 | ||
|
|
9b73f0194c | ||
|
|
b2bb9340f9 | ||
|
|
1f10bdd7cd | ||
|
|
e464a8c332 | ||
|
|
22bb64e7b0 | ||
|
|
2672e2b505 | ||
|
|
7b532ebb7f | ||
|
|
f5230fdb14 | ||
|
|
78282d1508 | ||
|
|
3cb2cd8235 | ||
|
|
5236583544 | ||
|
|
2ce5dc3bbe | ||
|
|
59e8363075 | ||
|
|
68ce959d70 | ||
|
|
40f241dde3 | ||
|
|
dd04c5983e | ||
|
|
674b26bef7 | ||
|
|
c46495e687 | ||
|
|
f8dcfabc44 | ||
|
|
3284a18ad3 | ||
|
|
f30addc70d | ||
|
|
1b99233466 | ||
|
|
0aa3b6b399 | ||
|
|
6bf37f51be | ||
|
|
cbcb8b4b89 | ||
|
|
59b9e4b2ef | ||
|
|
0945744f9b | ||
|
|
a3e4c61a0b | ||
|
|
a894b295b6 | ||
|
|
b38e4b8ba7 | ||
|
|
6f22d5c151 | ||
|
|
4fdedf5f14 | ||
|
|
35646df63f | ||
|
|
d6eb8c4b48 | ||
|
|
1c1ca45e4e | ||
|
|
11fbf26045 | ||
|
|
e0f3a4f17b | ||
|
|
b824844043 | ||
|
|
489169c8ce | ||
|
|
acaa13aba0 | ||
|
|
dfa36e0fa7 | ||
|
|
a48add94f8 | ||
|
|
fa7442fddb | ||
|
|
fcec112613 | ||
|
|
d9096ca12d | ||
|
|
0e2276fc7a | ||
|
|
4db29f9399 | ||
|
|
7f57d678bd | ||
|
|
a845b2c5f8 | ||
|
|
90073ac1d0 | ||
|
|
f9ac25f2b8 | ||
|
|
c04d6f9ef8 | ||
|
|
8bb5ff47eb | ||
|
|
ca42d027ac | ||
|
|
114d4a2c1d | ||
|
|
157532b0fe | ||
|
|
92ed284499 | ||
|
|
67f6bd2bcb | ||
|
|
d7e8b5a4c2 | ||
|
|
46239e3d09 | ||
|
|
2a1bf9925a | ||
|
|
ab2198be28 | ||
|
|
f181b45760 | ||
|
|
e3ce0530bf | ||
|
|
80fb30b564 | ||
|
|
29469077ee | ||
|
|
cbcbb675d6 | ||
|
|
3cd3d1c09d | ||
|
|
dc46d52029 | ||
|
|
702ed21207 | ||
|
|
1a6a14fcd4 | ||
|
|
68dc052432 | ||
|
|
91995c92db | ||
|
|
d74d27163c | ||
|
|
e71f6aa80a | ||
|
|
5451a1f5c3 | ||
|
|
84181e2a1d | ||
|
|
cc9c3ea41f | ||
|
|
8debaa34d9 | ||
|
|
aa65280ec3 | ||
|
|
7532d5d09a | ||
|
|
5ed0c98f37 | ||
|
|
9c36c9dbdb | ||
|
|
77093553e1 | ||
|
|
31423ef697 | ||
|
|
f2b1f40763 | ||
|
|
12bc7a7805 | ||
|
|
b67b770c5d | ||
|
|
bc9da28f31 | ||
|
|
56fc694076 | ||
|
|
0bded2b06a | ||
|
|
a69474ea97 | ||
|
|
af3b2b0e15 | ||
|
|
b755798c2a | ||
|
|
d187c8b08d | ||
|
|
a385573672 | ||
|
|
41b803ed2b | ||
|
|
8db961042a | ||
|
|
a66f14b5c3 | ||
|
|
ebaad0f35a | ||
|
|
ecbfff3c60 | ||
|
|
f6c0156eea | ||
|
|
34dace4506 | ||
|
|
6de53054c1 | ||
|
|
4bca193ee1 | ||
|
|
8856099092 | ||
|
|
7a40a843d4 | ||
|
|
d0398c8a83 | ||
|
|
64c0ca14a3 | ||
|
|
fcc6bf0cb7 | ||
|
|
3f410ebbac | ||
|
|
7dcacc2cda | ||
|
|
abd2399aaf | ||
|
|
9b40c8ffa6 | ||
|
|
9bd28f39a5 | ||
|
|
fd39f09aa7 | ||
|
|
c0cc0a371b | ||
|
|
b468f643fb | ||
|
|
e5495b43c6 | ||
|
|
add6754381 | ||
|
|
aedfff7e3f | ||
|
|
7f6fbd6b80 | ||
|
|
8fcb3d6690 | ||
|
|
2e3ce32819 | ||
|
|
f63291b8ec | ||
|
|
3f22f55338 | ||
|
|
33fdbe48a5 | ||
|
|
095bc280cb | ||
|
|
31f7ec7538 | ||
|
|
f798877704 | ||
|
|
ee5ba8d90a | ||
|
|
4f9a5d0241 | ||
|
|
ce4e9f4f17 | ||
|
|
51eb494340 | ||
|
|
744cfbd924 | ||
|
|
d320d5c830 | ||
|
|
aa1543271e | ||
|
|
f393ea618e | ||
|
|
9384f3a8e9 | ||
|
|
972872a0c8 | ||
|
|
bb528fb15c | ||
|
|
19980426fb | ||
|
|
ca84925ff8 | ||
|
|
189656cbc4 | ||
|
|
a5f04f2a8e | ||
|
|
7249825c16 | ||
|
|
14c227a813 | ||
|
|
3ab017c32e | ||
|
|
80ce7bde26 | ||
|
|
b24245dd1d | ||
|
|
d130fc9465 | ||
|
|
295f369519 | ||
|
|
d5d9cb623d | ||
|
|
c0d8e44548 | ||
|
|
22c15f88dd | ||
|
|
68557eb843 | ||
|
|
5e5818df54 | ||
|
|
f12c60e9b4 | ||
|
|
e3c9b75c1a | ||
|
|
b7534015e0 | ||
|
|
e36bfa567d | ||
|
|
2a1e8047fe | ||
|
|
3b9a9a6308 | ||
|
|
6224d0ab89 | ||
|
|
44ea51306a | ||
|
|
175bc66c91 | ||
|
|
81aad196e8 | ||
|
|
e804fdfdba | ||
|
|
4860313bef | ||
|
|
fad3d79895 | ||
|
|
7ca0d357cb | ||
|
|
faa016eaee | ||
|
|
076a2ca307 | ||
|
|
ca61f989fc | ||
|
|
7c7145d1c5 | ||
|
|
980aa0a8bc | ||
|
|
a6a0b660d2 | ||
|
|
9b20f1ffd9 | ||
|
|
208e746467 | ||
|
|
5e9a4f9bc6 | ||
|
|
9e1987063d | ||
|
|
627008f34c | ||
|
|
4232ef7bf3 | ||
|
|
b3edcc5841 | ||
|
|
d2c98639a8 | ||
|
|
412b5073db | ||
|
|
feb009ce39 | ||
|
|
681fccd5e6 | ||
|
|
4b4b97540e | ||
|
|
6fdb7fa801 | ||
|
|
c1f802c397 | ||
|
|
ea996a0305 | ||
|
|
3b23542ec9 | ||
|
|
a8af0f7ecb | ||
|
|
fa0f177e51 | ||
|
|
e044858953 | ||
|
|
9fd1d606d7 | ||
|
|
c95ccbcb5e | ||
|
|
6b10a1089c | ||
|
|
a4b6df3e7d | ||
|
|
ed304f0dcd | ||
|
|
9d7b63923a | ||
|
|
bd8613b120 | ||
|
|
4f26150ef1 | ||
|
|
b7bccac71d | ||
|
|
bd7e6954c3 | ||
|
|
30472b853d | ||
|
|
c0c035ab54 | ||
|
|
47f0720b93 | ||
|
|
b78a6045fc | ||
|
|
7b6d30a994 | ||
|
|
cc5df71af9 | ||
|
|
2c35a552d5 | ||
|
|
fb3ee3878f | ||
|
|
de8f03bd75 | ||
|
|
f5f12fd76d | ||
|
|
b4fb1e20d8 | ||
|
|
2c76ad204a | ||
|
|
1c8a896a55 | ||
|
|
866cbdca4b | ||
|
|
78e42d9b4c | ||
|
|
b8abf2df95 | ||
|
|
9310a736c4 | ||
|
|
2c89178895 | ||
|
|
e0679b216e | ||
|
|
3ed429772d | ||
|
|
4bcf8b3ce9 | ||
|
|
e85f7da874 | ||
|
|
09f6f6c3ea | ||
|
|
4e7de5a464 | ||
|
|
fd0e75ea61 | ||
|
|
d0e68c7163 | ||
|
|
5164840c25 | ||
|
|
d7c3a0137f | ||
|
|
76c945a6e3 | ||
|
|
c90ed85d9d | ||
|
|
ac2d788e3d | ||
|
|
c37cb54acf | ||
|
|
12c294c780 | ||
|
|
bb4be085e4 | ||
|
|
5c6ee953d0 | ||
|
|
7e5410fc44 | ||
|
|
abb0a1987d | ||
|
|
c47efc597f | ||
|
|
57358e3499 | ||
|
|
35b9143d3e | ||
|
|
d2218f6f1c | ||
|
|
62609b5008 | ||
|
|
def9a98cb9 | ||
|
|
8716c33f41 | ||
|
|
90997d4a8b | ||
|
|
6e7f1501b5 | ||
|
|
eefbd8f6a6 | ||
|
|
907e0de35a | ||
|
|
f4d540bcc6 | ||
|
|
58b24d6ac9 | ||
|
|
420525e672 | ||
|
|
49fda513ee | ||
|
|
a391ba1622 | ||
|
|
ae9c4c48f4 | ||
|
|
591f69f771 | ||
|
|
baa3fe41c6 | ||
|
|
2b9f9a21e4 | ||
|
|
191e6b0496 | ||
|
|
b9c5d5b8a3 | ||
|
|
b1d7f7b156 | ||
|
|
e3744688e1 | ||
|
|
c2ceb02610 | ||
|
|
b2a6ad143c | ||
|
|
e2a2972b38 | ||
|
|
20d4cec4d6 | ||
|
|
53962b32b0 | ||
|
|
dcb4769298 | ||
|
|
523dda47fa | ||
|
|
50bcdc0abb | ||
|
|
59c338f461 | ||
|
|
654c3f2ad6 | ||
|
|
d5df266373 | ||
|
|
66013e44d4 | ||
|
|
770dd5a600 | ||
|
|
d7de578d4a | ||
|
|
300510f20a | ||
|
|
98766a490f | ||
|
|
678146b907 | ||
|
|
6d45d4d579 | ||
|
|
b79d025e66 | ||
|
|
8e79059fe3 | ||
|
|
25fb3c01c3 | ||
|
|
03ea80d130 | ||
|
|
5c2efd880d | ||
|
|
419736aaee | ||
|
|
e340963c99 | ||
|
|
18a4ff76e2 | ||
|
|
f2db25e697 | ||
|
|
a4124fdc1b | ||
|
|
98d6b49987 | ||
|
|
0f8f9bc052 | ||
|
|
7daa169cda | ||
|
|
e7ae9b33a1 | ||
|
|
986e52990e | ||
|
|
93f46c142c | ||
|
|
47bf9862bc | ||
|
|
0d2e835637 | ||
|
|
50273899c6 | ||
|
|
b44a654943 | ||
|
|
c8f33e340b | ||
|
|
8f56d2c659 | ||
|
|
11d2b3a1a8 | ||
|
|
f002b4ca4b | ||
|
|
ead1be4728 | ||
|
|
e579cd2eb2 | ||
|
|
0fbb69a3ef | ||
|
|
a511882cb8 | ||
|
|
3ccfc42b0f | ||
|
|
737a87b483 | ||
|
|
044d806b2a | ||
|
|
922e17f677 | ||
|
|
f5ccb6ea60 | ||
|
|
09495b1d93 | ||
|
|
ea9829946d | ||
|
|
f0c2fb23e3 | ||
|
|
fd6462263f | ||
|
|
1e322191ea | ||
|
|
a90c25d0e7 | ||
|
|
d7bd21c06d | ||
|
|
5e05a14f97 | ||
|
|
d757056891 | ||
|
|
8b3426a96e | ||
|
|
1c9d0967b1 | ||
|
|
6d674175f4 | ||
|
|
47cd1bc67e | ||
|
|
a9b7e295fe | ||
|
|
6b147d8a59 | ||
|
|
2fc27a6bb2 | ||
|
|
39768edd74 | ||
|
|
5575950822 | ||
|
|
af6b0dc189 | ||
|
|
2899e2d73c | ||
|
|
0a0243b78e | ||
|
|
60b7d8e8c0 | ||
|
|
9556ad8a55 | ||
|
|
9edf71d4de | ||
|
|
f561392136 | ||
|
|
526c682298 | ||
|
|
822df373bb | ||
|
|
9089c97339 | ||
|
|
52dbcbaca5 | ||
|
|
59ac365b52 | ||
|
|
362905e3df | ||
|
|
c9ca57f013 | ||
|
|
b7b15fe540 | ||
|
|
8ad9dcb1ac | ||
|
|
c406f0708f | ||
|
|
3eaf28675a | ||
|
|
bc91075bdf | ||
|
|
78031afee7 | ||
|
|
3a33ada3f9 | ||
|
|
cd8f17dbd0 | ||
|
|
0ea2ced46d | ||
|
|
2a61620dae | ||
|
|
7e944103c2 | ||
|
|
a2666de7c8 | ||
|
|
977c2f4947 | ||
|
|
f8c2d7dafd | ||
|
|
98f70e9b51 | ||
|
|
bccecdbf18 | ||
|
|
37c9e69b3e | ||
|
|
e7139fe0d1 | ||
|
|
48f3bc7045 | ||
|
|
c3932e945d | ||
|
|
b695d3c6ce | ||
|
|
eaafd0a00c | ||
|
|
d1399fe6a3 | ||
|
|
39010320ea | ||
|
|
53697154f5 | ||
|
|
82b4edfaf4 | ||
|
|
77552d6904 | ||
|
|
d1a42df121 | ||
|
|
cf75282078 | ||
|
|
90d7550104 | ||
|
|
b6b2d66d99 | ||
|
|
f21d901cf1 | ||
|
|
16a6c1706e | ||
|
|
a75b6e512a | ||
|
|
bb1e3a0692 | ||
|
|
3137eb41c4 | ||
|
|
38e3b2a28a | ||
|
|
5fd5d89b23 | ||
|
|
744629e9a8 | ||
|
|
12caa79178 | ||
|
|
feadc83050 | ||
|
|
a902d9b409 | ||
|
|
263f7b673b | ||
|
|
4a482875ca | ||
|
|
5f69216f70 | ||
|
|
57c6e4d5e8 | ||
|
|
859f23f4d5 | ||
|
|
b16e639f0c | ||
|
|
b73fd7a4e8 | ||
|
|
bf584faa32 | ||
|
|
1fa0903fa9 | ||
|
|
05124fa635 | ||
|
|
e12c68cf65 | ||
|
|
32e47fba93 | ||
|
|
e630868080 | ||
|
|
b2de5945c4 | ||
|
|
f7a75941b5 | ||
|
|
f0ff7ce231 | ||
|
|
24fef20485 | ||
|
|
125dfa7c40 | ||
|
|
df14fa4d75 | ||
|
|
dc21f2b5cf | ||
|
|
f0e7c744b3 | ||
|
|
9ac09d35be | ||
|
|
a4eb4b9280 | ||
|
|
fcbd618783 | ||
|
|
627a3109b3 | ||
|
|
566993c6df | ||
|
|
31b41b9ec0 | ||
|
|
3f709d97b6 | ||
|
|
e4820bcbc1 | ||
|
|
b45bee5954 | ||
|
|
917fa5dd0e | ||
|
|
ade61d6139 | ||
|
|
7fd22958ae | ||
|
|
16bfaad505 | ||
|
|
1507824435 | ||
|
|
694b5abd8d | ||
|
|
764b1ee49d | ||
|
|
bf2e38aed5 | ||
|
|
022f7465c4 | ||
|
|
44b3c9be6e | ||
|
|
ea355c20ad | ||
|
|
773d5f5f25 | ||
|
|
0612fe4ddd | ||
|
|
57c1e54a54 | ||
|
|
0793f98c87 | ||
|
|
f6bd4bfcf4 | ||
|
|
07dbc5a80d | ||
|
|
e06f85a328 | ||
|
|
daf3720ba0 | ||
|
|
ff2c2e04d8 | ||
|
|
b49067d165 | ||
|
|
e528f3f273 | ||
|
|
347a10723f | ||
|
|
33e277e8d2 | ||
|
|
7e7ec86f91 | ||
|
|
3797d2acbc | ||
|
|
2ee2207106 | ||
|
|
a6529a9537 | ||
|
|
497759b4d6 | ||
|
|
faaa62cfca | ||
|
|
bf82bfc63b | ||
|
|
131dc923ed | ||
|
|
cb407263ea | ||
|
|
c375d04bad | ||
|
|
402532b8a5 | ||
|
|
b554c133ea | ||
|
|
7f058169b9 | ||
|
|
2595e31575 | ||
|
|
03d4f80883 | ||
|
|
d3d881aa6b | ||
|
|
beffbaee39 | ||
|
|
dc53209565 | ||
|
|
9f3e8bed86 | ||
|
|
1128836826 | ||
|
|
cdd0a5525a | ||
|
|
6df5824616 | ||
|
|
053d7fdc4b | ||
|
|
028a5c60d7 | ||
|
|
28c88cd69f | ||
|
|
a29669432f | ||
|
|
a03bca2f72 | ||
|
|
dba42cf323 | ||
|
|
302b08023e | ||
|
|
125d104df4 | ||
|
|
53f81d067a | ||
|
|
9c9e2bbe27 | ||
|
|
aeee649700 | ||
|
|
0f04763ef6 | ||
|
|
3185c5df0f | ||
|
|
ed8f6aefcf | ||
|
|
aa3ce14ef3 | ||
|
|
957d40b011 | ||
|
|
83db685376 | ||
|
|
37adc9ff34 | ||
|
|
be2d52b908 | ||
|
|
3f2a9cedbe | ||
|
|
46b97dd4a8 | ||
|
|
e4d2b7331b | ||
|
|
0726ad1275 | ||
|
|
9e11e5c354 | ||
|
|
042bdd9c0c | ||
|
|
3890a539ee | ||
|
|
bc98b2470c | ||
|
|
66e656da79 | ||
|
|
04309754a5 | ||
|
|
bdac8ffcde | ||
|
|
71eb578db1 | ||
|
|
086f251370 | ||
|
|
2880b71b48 | ||
|
|
473cfffa0c | ||
|
|
df12f08ac3 | ||
|
|
6a2deb6d71 | ||
|
|
2c6454972f | ||
|
|
99ec667b20 | ||
|
|
a0c63705ef | ||
|
|
7ed0477a29 | ||
|
|
81a638cabb | ||
|
|
f9d2643d98 | ||
|
|
3f9ba4acb6 | ||
|
|
bb4a7e0e78 | ||
|
|
13d9702000 | ||
|
|
5b4cc214c5 | ||
|
|
bb01fbf6c0 | ||
|
|
65be904d15 | ||
|
|
077764ce18 | ||
|
|
e1579584ef | ||
|
|
3f937a2a39 | ||
|
|
925bb7173f | ||
|
|
aa0c52cea1 | ||
|
|
0411a8b437 | ||
|
|
2a00c7e8b8 | ||
|
|
39ce06c656 | ||
|
|
3eeab81063 | ||
|
|
ca8fe27954 | ||
|
|
d87ab23fec | ||
|
|
6db847c56f | ||
|
|
402364ae63 | ||
|
|
ca14e53c4a | ||
|
|
2d3fd621bf | ||
|
|
2467dbedd7 | ||
|
|
ce81d23574 | ||
|
|
6cd991bc57 | ||
|
|
d468814a6d | ||
|
|
92a4551bb2 | ||
|
|
e3c07b562c | ||
|
|
ba2e9d030c | ||
|
|
553b9fa5a1 | ||
|
|
a83ac0f0c5 | ||
|
|
d0443e3165 | ||
|
|
c698519d44 | ||
|
|
f3522b7b70 | ||
|
|
c3526adb69 | ||
|
|
82ffb1bc81 | ||
|
|
c1a81aefa4 | ||
|
|
c40220f723 | ||
|
|
d0b5338663 | ||
|
|
e6064b6eb6 | ||
|
|
cf2d9b86b0 | ||
|
|
e1ffb5e385 | ||
|
|
8e0ee7e9c5 | ||
|
|
44e2a857fa | ||
|
|
56ab103559 | ||
|
|
3e6bc0e10d | ||
|
|
4701d2480d | ||
|
|
c1de8c32c1 | ||
|
|
d23b9b508d | ||
|
|
fb4ebabee5 | ||
|
|
b2c8e9e31e | ||
|
|
8f74d4718f | ||
|
|
c18278e664 | ||
|
|
74942443d3 | ||
|
|
8cb89636eb | ||
|
|
f68ad7d0c3 | ||
|
|
ac89379ca6 | ||
|
|
b6ec587e5b | ||
|
|
6828082f8b | ||
|
|
576914cd2c | ||
|
|
62892b02d1 | ||
|
|
5635ee1d7c | ||
|
|
6922380569 | ||
|
|
9cf38a5740 | ||
|
|
906ca1bbe1 | ||
|
|
6d4b812b7c | ||
|
|
722c4196f2 | ||
|
|
a17e15f176 | ||
|
|
257cc75183 | ||
|
|
f94fef11ac | ||
|
|
e457d4ca0c | ||
|
|
8816c5048d | ||
|
|
2388662cc5 | ||
|
|
2b66492ed8 | ||
|
|
d5a5969bd4 | ||
|
|
3a6ff109be | ||
|
|
5a93760af9 | ||
|
|
efc0a7391d | ||
|
|
96ff8c87e5 | ||
|
|
7dc71027cf | ||
|
|
d16b5a6721 | ||
|
|
2c8a4ff154 | ||
|
|
ae1e4b1b8f | ||
|
|
d102b2facd | ||
|
|
863f84e3f6 | ||
|
|
3cb18d13b0 | ||
|
|
75a22724b9 | ||
|
|
4d5b122772 | ||
|
|
121d856b14 | ||
|
|
2b2a868fef | ||
|
|
414bc30f24 | ||
|
|
858f39827d | ||
|
|
6ddf6784ab | ||
|
|
ad6a4d4ec0 | ||
|
|
3dce0d654a | ||
|
|
79aa1cece7 | ||
|
|
1efcb24886 | ||
|
|
8b5fde8e6f | ||
|
|
3b51b993f3 | ||
|
|
4ef70ef2aa | ||
|
|
0d6a3ca28d | ||
|
|
b5b1c725c1 | ||
|
|
4c08624ce5 | ||
|
|
f2aa343cae | ||
|
|
4fe1c07b02 | ||
|
|
95dba59826 | ||
|
|
f39b3006f2 | ||
|
|
82e19f7188 | ||
|
|
ed0777cd8d | ||
|
|
fdb770a596 | ||
|
|
b3c5c0f852 | ||
|
|
2eaebd8921 | ||
|
|
61a4960380 | ||
|
|
2e53bf914e | ||
|
|
8f20ffaed3 | ||
|
|
edce63bf6c | ||
|
|
70fa41431b | ||
|
|
5121426602 | ||
|
|
fe9680fba8 | ||
|
|
df1c5c0f45 | ||
|
|
88f60a1ca8 | ||
|
|
7078f73ecf | ||
|
|
fda95fb903 | ||
|
|
18f8c7bc61 | ||
|
|
c01d6ba44b | ||
|
|
2fb67ee7db | ||
|
|
7210b0826a | ||
|
|
be63d893cd | ||
|
|
3b3ad0c1cb | ||
|
|
f14270fc4b | ||
|
|
b2d224abfc | ||
|
|
e081ee4b8e | ||
|
|
a6947fbc70 | ||
|
|
29467d40c8 | ||
|
|
62dbaaf02c | ||
|
|
3394575dca | ||
|
|
b66f212ec3 | ||
|
|
87518fbee1 | ||
|
|
0552da6be2 | ||
|
|
bde271566b | ||
|
|
34a808c258 | ||
|
|
a8171405b1 | ||
|
|
451c48406d | ||
|
|
0b84433533 | ||
|
|
cf630f0592 | ||
|
|
9bfa27d550 | ||
|
|
392c7af738 | ||
|
|
7ce55ca800 | ||
|
|
80a377d45a | ||
|
|
be3dd3e06f | ||
|
|
a1064f33b3 | ||
|
|
7f795d25aa | ||
|
|
875b0b91f1 | ||
|
|
76428887b8 | ||
|
|
cbc7e3d64a | ||
|
|
f2e1159173 | ||
|
|
b1c78d8bea | ||
|
|
3392839795 | ||
|
|
e147327459 | ||
|
|
900c01d16f | ||
|
|
a036ae92e8 | ||
|
|
f5f3b3f5d1 | ||
|
|
8bf21fd5a0 | ||
|
|
045866506f | ||
|
|
1b618b8c46 | ||
|
|
98987643b3 | ||
|
|
9a7999f158 | ||
|
|
076d3d3479 | ||
|
|
e22c7608e3 | ||
|
|
5084c90e08 | ||
|
|
de3696be4c | ||
|
|
8618c2a214 | ||
|
|
a1453ee8a5 | ||
|
|
e5a0c92336 | ||
|
|
31de96d120 | ||
|
|
ff1e2afc4b | ||
|
|
6ffcbc9b98 | ||
|
|
31a6c12952 | ||
|
|
6bc7cf29cc | ||
|
|
e2af286554 | ||
|
|
f69889264d | ||
|
|
ca7db7a2d2 | ||
|
|
1a0b1a3149 | ||
|
|
bbaebfd4c9 | ||
|
|
2b2e2f7854 | ||
|
|
3fc9f264d7 | ||
|
|
bf46461dd6 | ||
|
|
b6a8da34fe | ||
|
|
13aa5e0554 | ||
|
|
ffc0a5def4 | ||
|
|
2fb611518d | ||
|
|
0d387dfaca | ||
|
|
a83439fd35 | ||
|
|
2f5cf91d11 | ||
|
|
1f948b1266 | ||
|
|
133639122d | ||
|
|
cb6ef03775 | ||
|
|
39a61d2207 | ||
|
|
abc035b15a | ||
|
|
0acf6e8327 | ||
|
|
c3a03fc4d8 | ||
|
|
8432050094 | ||
|
|
b694577c38 | ||
|
|
9f0f24a5e5 | ||
|
|
9b8fbeb087 | ||
|
|
7755e238d3 | ||
|
|
7a82cae2bd | ||
|
|
11c25c68c5 | ||
|
|
0fca011bd4 | ||
|
|
6a4a4c62d7 | ||
|
|
622aee7c96 | ||
|
|
4de911222a | ||
|
|
47eede06c8 | ||
|
|
942df40fe7 | ||
|
|
325f83065c | ||
|
|
245edd94d7 | ||
|
|
e8d77448c7 | ||
|
|
6624660b0d | ||
|
|
d3f87dc357 | ||
|
|
b704c37e91 | ||
|
|
51eff82eb6 | ||
|
|
7566c45f64 | ||
|
|
ceb67b5018 | ||
|
|
11faf6ccfb | ||
|
|
a16c2f5462 | ||
|
|
a6f8dde790 | ||
|
|
cb351a7dbd | ||
|
|
0afdc04d88 | ||
|
|
69b2c3fc2d | ||
|
|
893268bba5 | ||
|
|
95f7821691 | ||
|
|
a004ae0d5b | ||
|
|
d6758aacce | ||
|
|
c2eea7776e | ||
|
|
c04f831b3f | ||
|
|
e22601f3df |
55
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
55
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please read before creating a new issue:
|
||||
|
||||
1. Make sure there is not already an open issue for this bug.
|
||||
2. All enhancements **must** be off. To quickly disable all enhancements with affecting your normal config, in advanced options you can check "Disable All Enhancements".
|
||||
3. All advanced options **must** be at their default values.
|
||||
4. No cheats may be active. If you were using cheats, they should be disabled, and the game rebooted before reporting the bug.
|
||||
5. Do not share save state files. Memory cards are okay.
|
||||
6. Verify your dump, as we can not assist with issues resulting from bad dumps.
|
||||
7. If playing PAL region software, please check whether or not the game has LibCrypt protection and that if it does, you have a correct SBI file (see https://github.com/stenzek/duckstation#libcrypt-protection-and-sbi-files for more information).
|
||||
8. Please post your issue report in English as unfortunately this is the only language spoken by the developers. The Discord server has many helpful people if you need help translating.
|
||||
|
||||
**Remove everything before and including this line before submitting.**
|
||||
|
||||
**Game details**
|
||||
[Serial Code, Region]
|
||||
[i.e SLUS-00404 Ace Combat 2 (USA)]
|
||||
|
||||
**Description of the issue / bug**
|
||||
[Describe what you are seeing and/or hearing during gameplay]
|
||||
|
||||
**Controller Troubleshoot Report**
|
||||
1. Have you installed any drivers or wrappers on your system, or do you have any programs like Steam open?
|
||||
2. Which controller backend are you using in general settings
|
||||
|
||||
**Note:**
|
||||
If you are using Duckstation on Android device please consider the following before report:
|
||||
Are you using any kind of "memory optimizer" program such as: Ccleaner, Wisecleaner, Clean Master, Boost Android etc. ?
|
||||
(if so, please, consider create an exception on it first and test; or even remove / uninstall it retest and then, if the problem persists continue with the report.
|
||||
|
||||
**Steps to reproduce**
|
||||
[Try to provide as much detail as possible to reproduce the issue]
|
||||
|
||||
**Enhancements information**
|
||||
[Make sure they are all turned off before report]
|
||||
|
||||
**Hardware/software**
|
||||
[If Android, which phone and Android version]
|
||||
[If desktop, your CPU, graphics card, and operating system]
|
||||
[GPU Renderer - D3D11/OpenGL/Vulkan]
|
||||
|
||||
**Emulator version**
|
||||
[Shown in the title bar of the emulator]
|
||||
|
||||
**Additional context**
|
||||
[Add any other context about the problem here]
|
||||
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
38
.github/workflows/linux-build.yml.disabled
vendored
38
.github/workflows/linux-build.yml.disabled
vendored
@@ -1,38 +0,0 @@
|
||||
name: Linux Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: Install packages
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install libsdl2-dev libgtk2.0-dev
|
||||
|
||||
- name: Compile debug build
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build-debug
|
||||
cd build-debug
|
||||
cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_QT_FRONTEND=OFF ..
|
||||
make
|
||||
|
||||
- name: Compile release build
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build-release
|
||||
cd build-release
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_QT_FRONTEND=OFF ..
|
||||
make
|
||||
|
||||
276
.github/workflows/rolling-release.yml
vendored
276
.github/workflows/rolling-release.yml
vendored
@@ -6,13 +6,16 @@ on:
|
||||
- '**.md'
|
||||
- 'appveyor.yml'
|
||||
- 'scripts/*'
|
||||
- '.github/ISSUE_TEMPLATE/*'
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
- 'appveyor.yml'
|
||||
- 'scripts/*'
|
||||
- '.github/ISSUE_TEMPLATE/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -24,19 +27,43 @@ jobs:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- name: Tag as release build
|
||||
- name: Tag as preview build
|
||||
if: github.ref == 'refs/heads/master'
|
||||
shell: cmd
|
||||
run: |
|
||||
echo #pragma once > src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_TAG "latest" >> src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_ASSET "duckstation-windows-x64-release.zip" >> src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_TAGS {"latest", "preview"} >> src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_TAG "preview" >> src/scmversion/tag.h
|
||||
|
||||
- name: Compile release build
|
||||
|
||||
- name: Tag as dev build
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
shell: cmd
|
||||
run: |
|
||||
echo #pragma once > src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_ASSET "duckstation-windows-x64-release.zip" >> src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_TAGS {"latest", "preview"} >> src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_TAG "latest" >> src/scmversion/tag.h
|
||||
|
||||
|
||||
- name: Compile x64 release build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
msbuild duckstation.sln -t:Build -p:Platform=x64;Configuration=ReleaseLTCG
|
||||
|
||||
- name: Create x64 symbols archive
|
||||
shell: cmd
|
||||
run: |
|
||||
"C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-x64-release-symbols.zip ./bin/x64/*.pdb
|
||||
|
||||
- name: Upload x64 release symbols artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "windows"
|
||||
path: "duckstation-windows-x64-release-symbols.zip"
|
||||
|
||||
- name: Remove extra bloat before archiving
|
||||
shell: cmd
|
||||
run: |
|
||||
@@ -46,48 +73,86 @@ jobs:
|
||||
del /Q bin\x64\*.iobj
|
||||
del /Q bin\x64\*.ipdb
|
||||
del /Q bin\x64\common-tests*
|
||||
del /Q bin\x64\duckstation-libretro-*
|
||||
rename bin\x64\updater-x64-ReleaseLTCG.exe updater.exe
|
||||
|
||||
- name: Create release archive
|
||||
|
||||
- name: Create x64 release archive
|
||||
shell: cmd
|
||||
run: |
|
||||
"C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-x64-release.zip ./bin/x64/*
|
||||
|
||||
- name: Upload release artifact
|
||||
- name: Upload x64 release artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "windows-x64"
|
||||
name: "windows"
|
||||
path: "duckstation-windows-x64-release.zip"
|
||||
|
||||
|
||||
windows-libretro-build:
|
||||
windows-arm64-build:
|
||||
runs-on: windows-2019
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- name: Compile release build
|
||||
- name: Tag as preview build
|
||||
if: github.ref == 'refs/heads/master'
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_LIBRETRO_CORE=ON -DCMAKE_C_COMPILER:FILEPATH="%VCToolsInstallDir%\bin\HostX64\x64\cl.exe" -DCMAKE_CXX_COMPILER:FILEPATH="%VCToolsInstallDir%\bin\HostX64\x64\cl.exe" ..
|
||||
ninja
|
||||
|
||||
- name: Create libretro core archive
|
||||
echo #pragma once > src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_ASSET "duckstation-windows-x64-release.zip" >> src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_TAGS {"latest", "preview"} >> src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_TAG "preview" >> src/scmversion/tag.h
|
||||
|
||||
|
||||
- name: Tag as dev build
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
shell: cmd
|
||||
run: |
|
||||
cd build
|
||||
"C:\Program Files\7-Zip\7z.exe" a -r duckstation_libretro.dll.zip ./duckstation_libretro.dll
|
||||
echo #pragma once > src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_ASSET "duckstation-windows-x64-release.zip" >> src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_TAGS {"latest", "preview"} >> src/scmversion/tag.h
|
||||
echo #define SCM_RELEASE_TAG "latest" >> src/scmversion/tag.h
|
||||
|
||||
- name: Upload release artifact
|
||||
|
||||
- name: Compile arm64 release build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64_arm64
|
||||
msbuild duckstation.sln -t:Build -p:Platform=ARM64;Configuration=ReleaseLTCG
|
||||
|
||||
- name: Create arm64 symbols archive
|
||||
shell: cmd
|
||||
run: |
|
||||
"C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-arm64-release-symbols.zip ./bin/ARM64/*.pdb
|
||||
|
||||
- name: Upload arm64 release symbols artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "windows-libretro-x64"
|
||||
path: "build/duckstation_libretro.dll.zip"
|
||||
name: "windows-arm64"
|
||||
path: "duckstation-windows-arm64-release-symbols.zip"
|
||||
|
||||
- name: Remove extra bloat before archiving
|
||||
shell: cmd
|
||||
run: |
|
||||
del /Q bin\ARM64\*.pdb
|
||||
del /Q bin\ARM64\*.exp
|
||||
del /Q bin\ARM64\*.lib
|
||||
del /Q bin\ARM64\*.iobj
|
||||
del /Q bin\ARM64\*.ipdb
|
||||
del /Q bin\ARM64\common-tests*
|
||||
rename bin\ARM64\updater-ARM64-ReleaseLTCG.exe updater.exe
|
||||
|
||||
- name: Create arm64 release archive
|
||||
shell: cmd
|
||||
run: |
|
||||
"C:\Program Files\7-Zip\7z.exe" a -r duckstation-windows-arm64-release.zip ./bin/ARM64/*
|
||||
|
||||
- name: Upload arm64 release artifact
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "windows-arm64"
|
||||
path: "duckstation-windows-arm64-release.zip"
|
||||
|
||||
|
||||
linux-build:
|
||||
@@ -101,28 +166,28 @@ jobs:
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install cmake ninja-build ccache libsdl2-dev libgtk2.0-dev qtbase5-dev qtbase5-dev-tools qtbase5-private-dev qt5-default qttools5-dev
|
||||
sudo apt-get -y install cmake ninja-build ccache libsdl2-dev libgtk-3-dev qtbase5-dev qtbase5-dev-tools qtbase5-private-dev qt5-default qttools5-dev libegl1-mesa-dev libevdev-dev libgbm-dev libdrm-dev libwayland-dev libwayland-egl-backend-dev extra-cmake-modules
|
||||
|
||||
- name: Compile build
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SDL_FRONTEND=ON -DBUILD_QT_FRONTEND=ON -DUSE_SDL2=ON -G Ninja ..
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_NOGUI_FRONTEND=ON -DBUILD_QT_FRONTEND=ON -DUSE_DRMKMS=ON -DUSE_EGL=ON -DUSE_SDL2=ON -DUSE_WAYLAND=ON -DUSE_X11=ON -G Ninja ..
|
||||
ninja
|
||||
../appimage/generate_appimages.sh $(pwd)
|
||||
|
||||
- name: Upload SDL AppImage
|
||||
- name: Upload NoGUI AppImage
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "linux-x64-appimage-sdl"
|
||||
path: "build/duckstation-sdl-x64.AppImage"
|
||||
name: "linux-x64-appimage-nogui"
|
||||
path: "build/duckstation-nogui-x64.AppImage"
|
||||
|
||||
- name: Upload SDL AppImage zsync
|
||||
- name: Upload NoGUI AppImage zsync
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "linux-x64-appimage-sdl-zsync"
|
||||
path: "build/duckstation-sdl-x64.AppImage.zsync"
|
||||
name: "linux-x64-appimage-nogui-zsync"
|
||||
path: "build/duckstation-nogui-x64.AppImage.zsync"
|
||||
|
||||
- name: Upload Qt AppImage
|
||||
uses: actions/upload-artifact@v1
|
||||
@@ -137,65 +202,6 @@ jobs:
|
||||
path: "build/duckstation-qt-x64.AppImage.zsync"
|
||||
|
||||
|
||||
linux-libretro-build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.1
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install packages
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
|
||||
|
||||
- name: Compile and zip Linux x64 libretro core
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build-libretro-linux-x64
|
||||
cd build-libretro-linux-x64
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_LIBRETRO_CORE=ON ..
|
||||
cmake --build . --parallel 2
|
||||
zip -j duckstation_libretro_x64.so.zip duckstation_libretro.so
|
||||
|
||||
- name: Upload Linux x64 libretro core
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "linux-libretro"
|
||||
path: "build-libretro-linux-x64/duckstation_libretro_x64.so.zip"
|
||||
|
||||
- name: Compile and zip Linux AArch64 libretro core
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build-libretro-linux-aarch64
|
||||
cd build-libretro-linux-aarch64
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_LIBRETRO_CORE=ON -DCMAKE_TOOLCHAIN_FILE=../CMakeModules/aarch64-cross-toolchain.cmake ..
|
||||
cmake --build . --parallel 2
|
||||
zip -j duckstation_libretro_linux_aarch64.so.zip duckstation_libretro.so
|
||||
|
||||
- name: Upload Linux AArch64 libretro core
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "linux-libretro"
|
||||
path: "build-libretro-linux-aarch64/duckstation_libretro_linux_aarch64.so.zip"
|
||||
|
||||
- name: Compile and zip Android AArch64 libretro core
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build-libretro-android-aarch64
|
||||
cd build-libretro-android-aarch64
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_LIBRETRO_CORE=ON -DANDROID_ABI=arm64-v8a -DCMAKE_TOOLCHAIN_FILE=${ANDROID_SDK_ROOT}/ndk-bundle/build/cmake/android.toolchain.cmake ..
|
||||
cmake --build . --parallel 2
|
||||
zip -j duckstation_libretro_android_aarch64.so.zip duckstation_libretro_android.so
|
||||
|
||||
- name: Upload Android AArch64 libretro core
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "linux-libretro"
|
||||
path: "build-libretro-android-aarch64/duckstation_libretro_android_aarch64.so.zip"
|
||||
|
||||
|
||||
android-build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
@@ -210,7 +216,7 @@ jobs:
|
||||
./gradlew assembleRelease
|
||||
|
||||
- name: Sign APK
|
||||
if: github.ref == 'refs/heads/master'
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev'
|
||||
uses: r0adkll/sign-android-release@v1
|
||||
with:
|
||||
releaseDirectory: android/app/build/outputs/apk/release
|
||||
@@ -220,18 +226,18 @@ jobs:
|
||||
keyPassword: ${{ secrets.APK_KEY_PASSWORD }}
|
||||
|
||||
- name: Rename APK
|
||||
if: github.ref == 'refs/heads/master'
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev'
|
||||
shell: bash
|
||||
run: |
|
||||
cd android
|
||||
mv app/build/outputs/apk/release/app-release-unsigned-signed.apk ../duckstation-android-aarch64.apk
|
||||
mv app/build/outputs/apk/release/app-release-unsigned-signed.apk ../duckstation-android.apk
|
||||
|
||||
- name: Upload APK
|
||||
if: github.ref == 'refs/heads/master'
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev'
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "android"
|
||||
path: "duckstation-android-aarch64.apk"
|
||||
path: "duckstation-android.apk"
|
||||
|
||||
macos-build:
|
||||
runs-on: macos-10.15
|
||||
@@ -255,7 +261,8 @@ jobs:
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SDL_FRONTEND=OFF -DBUILD_QT_FRONTEND=ON -DUSE_SDL2=ON -DQt5_DIR=/usr/local/opt/qt/lib/cmake/Qt5 ..
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.14
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_NOGUI_FRONTEND=OFF -DBUILD_QT_FRONTEND=ON -DUSE_SDL2=ON -DQt5_DIR=/usr/local/opt/qt/lib/cmake/Qt5 ..
|
||||
cmake --build . --parallel 2
|
||||
cd bin
|
||||
zip -r duckstation-mac-release.zip DuckStation.app/
|
||||
@@ -266,46 +273,31 @@ jobs:
|
||||
name: "macos-x64"
|
||||
path: "build/bin/duckstation-mac-release.zip"
|
||||
|
||||
- name: Compile libretro core and zip
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir build-libretro
|
||||
cd build-libretro
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_BUILD_TYPE=Release -DBUILD_LIBRETRO_CORE=ON ..
|
||||
cmake --build . --parallel 2
|
||||
zip -j duckstation_libretro_mac.dylib.zip duckstation_libretro.dylib
|
||||
|
||||
- name: Upload macOS libretro core
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "macos-x64"
|
||||
path: "build-libretro/duckstation_libretro_mac.dylib.zip"
|
||||
|
||||
|
||||
create-release:
|
||||
needs: [windows-build, windows-libretro-build, linux-build, linux-libretro-build, android-build, macos-build]
|
||||
needs: [windows-build, windows-arm64-build, linux-build, android-build, macos-build]
|
||||
runs-on: "ubuntu-latest"
|
||||
if: github.ref == 'refs/heads/master'
|
||||
if: github.ref == 'refs/heads/master' || github.ref == 'refs/heads/dev'
|
||||
steps:
|
||||
- name: Download Windows x64 Artifact
|
||||
- name: Download Windows Artifacts
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: "windows-x64"
|
||||
name: "windows"
|
||||
|
||||
- name: Download Windows libretro x64 Artifact
|
||||
- name: Download Windows ARM64 Artifact
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: "windows-libretro-x64"
|
||||
name: "windows-arm64"
|
||||
|
||||
- name: Download SDL AppImage Artifact
|
||||
- name: Download NoGUI AppImage Artifact
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: "linux-x64-appimage-sdl"
|
||||
name: "linux-x64-appimage-nogui"
|
||||
|
||||
- name: Download SDL AppImage zsync Artifact
|
||||
- name: Download NoGUI AppImage zsync Artifact
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: "linux-x64-appimage-sdl-zsync"
|
||||
name: "linux-x64-appimage-nogui-zsync"
|
||||
|
||||
- name: Download Qt AppImage Artifact
|
||||
uses: actions/download-artifact@v1
|
||||
@@ -317,11 +309,6 @@ jobs:
|
||||
with:
|
||||
name: "linux-x64-appimage-qt-zsync"
|
||||
|
||||
- name: Download Linux libretro core
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: "linux-libretro"
|
||||
|
||||
- name: Download Android APK
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
@@ -332,23 +319,42 @@ jobs:
|
||||
with:
|
||||
name: "macos-x64"
|
||||
|
||||
- name: Create release
|
||||
- name: Create preview release
|
||||
if: github.ref == 'refs/heads/master'
|
||||
uses: "marvinpinto/action-automatic-releases@latest"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
automatic_release_tag: "preview"
|
||||
prerelease: true
|
||||
title: "Latest Preview Build"
|
||||
files: |
|
||||
windows/duckstation-windows-x64-release.zip
|
||||
windows/duckstation-windows-x64-release-symbols.zip
|
||||
windows-arm64/duckstation-windows-arm64-release.zip
|
||||
windows-arm64/duckstation-windows-arm64-release-symbols.zip
|
||||
linux-x64-appimage-nogui/duckstation-nogui-x64.AppImage
|
||||
linux-x64-appimage-nogui-zsync/duckstation-nogui-x64.AppImage.zsync
|
||||
linux-x64-appimage-qt/duckstation-qt-x64.AppImage
|
||||
linux-x64-appimage-qt-zsync/duckstation-qt-x64.AppImage.zsync
|
||||
android/duckstation-android.apk
|
||||
|
||||
- name: Create dev release
|
||||
if: github.ref == 'refs/heads/dev'
|
||||
uses: "marvinpinto/action-automatic-releases@latest"
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
automatic_release_tag: "latest"
|
||||
prerelease: false
|
||||
title: "Latest Development Build"
|
||||
files: |
|
||||
windows-x64/duckstation-windows-x64-release.zip
|
||||
windows-libretro-x64/duckstation_libretro.dll.zip
|
||||
linux-x64-appimage-sdl/duckstation-sdl-x64.AppImage
|
||||
linux-x64-appimage-sdl-zsync/duckstation-sdl-x64.AppImage.zsync
|
||||
windows/duckstation-windows-x64-release.zip
|
||||
windows/duckstation-windows-x64-release-symbols.zip
|
||||
windows-arm64/duckstation-windows-arm64-release.zip
|
||||
windows-arm64/duckstation-windows-arm64-release-symbols.zip
|
||||
linux-x64-appimage-nogui/duckstation-nogui-x64.AppImage
|
||||
linux-x64-appimage-nogui-zsync/duckstation-nogui-x64.AppImage.zsync
|
||||
linux-x64-appimage-qt/duckstation-qt-x64.AppImage
|
||||
linux-x64-appimage-qt-zsync/duckstation-qt-x64.AppImage.zsync
|
||||
linux-libretro/duckstation_libretro_x64.so.zip
|
||||
linux-libretro/duckstation_libretro_linux_aarch64.so.zip
|
||||
linux-libretro/duckstation_libretro_android_aarch64.so.zip
|
||||
android/duckstation-android-aarch64.apk
|
||||
android/duckstation-android.apk
|
||||
macos-x64/duckstation-mac-release.zip
|
||||
macos-x64/duckstation_libretro_mac.dylib.zip
|
||||
|
||||
|
||||
38
.github/workflows/windows-build.yml.disabled
vendored
38
.github/workflows/windows-build.yml.disabled
vendored
@@ -1,38 +0,0 @@
|
||||
name: Windows Build
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: windows-2019
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
with:
|
||||
submodules: true
|
||||
|
||||
- name: Compile release build
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64
|
||||
msbuild duckstation.sln -t:Build -p:Platform=x64;Configuration=ReleaseLTCG
|
||||
|
||||
- name: Remove extra bloat before archiving
|
||||
shell: cmd
|
||||
run: |
|
||||
del /Q bin\x64\*.pdb
|
||||
del /Q bin\x64\*.exp
|
||||
del /Q bin\x64\*.lib
|
||||
del /Q bin\x64\*.iobj
|
||||
del /Q bin\x64\*.ipdb
|
||||
|
||||
- name: Upload release archive
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: "duckstation-windows-x64-release"
|
||||
path: ".\\bin\\x64"
|
||||
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(duckstation C CXX)
|
||||
|
||||
message("CMake Version: ${CMAKE_VERSION}")
|
||||
@@ -13,12 +13,14 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(SUPPORTS_WAYLAND TRUE)
|
||||
endif()
|
||||
|
||||
# Set minimum OS version for macOS. 10.14 should work.
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14.0" CACHE STRING "")
|
||||
|
||||
# Global options.
|
||||
if(NOT ANDROID)
|
||||
option(BUILD_SDL_FRONTEND "Build the SDL frontend" ON)
|
||||
option(BUILD_SDL_FRONTEND "Build the SDL frontend" OFF)
|
||||
option(BUILD_NOGUI_FRONTEND "Build the NoGUI frontend" ON)
|
||||
option(BUILD_QT_FRONTEND "Build the Qt frontend" ON)
|
||||
option(BUILD_LIBRETRO_CORE "Build a libretro core" OFF)
|
||||
option(ENABLE_DISCORD_PRESENCE "Build with Discord Rich Presence support" ON)
|
||||
option(USE_SDL2 "Link with SDL2 for controller support" ON)
|
||||
endif()
|
||||
@@ -34,28 +36,35 @@ endif()
|
||||
if(LINUX OR ANDROID)
|
||||
option(USE_EGL "Support EGL OpenGL context creation" ON)
|
||||
endif()
|
||||
if(LINUX AND NOT ANDROID)
|
||||
option(USE_DRMKMS "Support DRM/KMS OpenGL contexts" OFF)
|
||||
option(USE_FBDEV "Support FBDev OpenGL contexts" OFF)
|
||||
endif()
|
||||
|
||||
# Force EGL when using Wayland
|
||||
if(USE_WAYLAND)
|
||||
set(USE_EGL ON)
|
||||
endif()
|
||||
|
||||
# When we're building for libretro, everything else is invalid because of PIC.
|
||||
if(ANDROID OR BUILD_LIBRETRO_CORE)
|
||||
if(ANDROID)
|
||||
if(BUILD_SDL_FRONTEND)
|
||||
message(WARNING "Building for Android or libretro core, disabling SDL frontend")
|
||||
message(WARNING "Building for Android, disabling SDL frontend")
|
||||
set(BUILD_SDL_FRONTEND OFF)
|
||||
endif()
|
||||
if(BUILD_NOGUI_FRONTEND)
|
||||
message(WARNING "Building for Android, disabling NoGUI frontend")
|
||||
set(BUILD_QT_FRONTEND OFF)
|
||||
endif()
|
||||
if(BUILD_QT_FRONTEND)
|
||||
message(WARNING "Building for Android or libretro core, disabling Qt frontend")
|
||||
message(WARNING "Building for Android, disabling Qt frontend")
|
||||
set(BUILD_QT_FRONTEND OFF)
|
||||
endif()
|
||||
if(ENABLE_DISCORD_PRESENCE)
|
||||
message("Building for Android or libretro core, disabling Discord Presence support")
|
||||
message("Building for Android, disabling Discord Presence support")
|
||||
set(ENABLE_DISCORD_PRESENCE OFF)
|
||||
endif()
|
||||
if(USE_SDL2)
|
||||
message("Building for Android or libretro core, disabling SDL2 support")
|
||||
message("Building for Android, disabling SDL2 support")
|
||||
set(USE_SDL2 OFF)
|
||||
endif()
|
||||
if(USE_X11)
|
||||
@@ -64,14 +73,6 @@ if(ANDROID OR BUILD_LIBRETRO_CORE)
|
||||
if(USE_WAYLAND)
|
||||
set(USE_WAYLAND OFF)
|
||||
endif()
|
||||
if(BUILD_LIBRETRO_CORE AND USE_EGL)
|
||||
set(USE_EGL OFF)
|
||||
endif()
|
||||
|
||||
# Force PIC when compiling a libretro core.
|
||||
if(BUILD_LIBRETRO_CORE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -115,6 +116,17 @@ if(USE_WAYLAND)
|
||||
find_package(Wayland REQUIRED Egl)
|
||||
message(STATUS "Wayland support enabled")
|
||||
endif()
|
||||
if(USE_DRMKMS AND USE_FBDEV)
|
||||
message(FATAL_ERROR "Only one of DRM/KMS and FBDev can be enabled")
|
||||
endif()
|
||||
if(USE_DRMKMS)
|
||||
find_package(GBM REQUIRED)
|
||||
find_package(Libdrm REQUIRED)
|
||||
message(STATUS "DRM/KMS support enabled")
|
||||
endif()
|
||||
if(USE_FBDEV)
|
||||
message(STATUS "FBDev Support enabled")
|
||||
endif()
|
||||
|
||||
# Set _DEBUG macro for Debug builds.
|
||||
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG")
|
||||
@@ -145,18 +157,14 @@ if(MSVC)
|
||||
|
||||
# Enable LTO/LTCG on Release builds.
|
||||
if(${CMAKE_BUILD_TYPE} STREQUAL "Release")
|
||||
if (${CMAKE_VERSION} VERSION_LESS "3.9.0")
|
||||
message(WARNING "CMake version is less than 3.9.0, we can't enable LTCG/IPO. This will make the build slightly slower, consider updating your CMake version.")
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT IPO_IS_SUPPORTED)
|
||||
if(IPO_IS_SUPPORTED)
|
||||
message(STATUS "Enabling LTCG/IPO.")
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
|
||||
else()
|
||||
cmake_policy(SET CMP0069 NEW)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT IPO_IS_SUPPORTED)
|
||||
if(IPO_IS_SUPPORTED)
|
||||
message(STATUS "Enabling LTCG/IPO.")
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION ON)
|
||||
else()
|
||||
message(WARNING "LTCG/IPO is not supported, this will make the build slightly slower.")
|
||||
endif()
|
||||
message(WARNING "LTCG/IPO is not supported, this will make the build slightly slower.")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -192,15 +200,24 @@ elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "x86" OR ${CMAKE_SYSTEM_PROCESSOR} STR
|
||||
set(CPU_ARCH "x86")
|
||||
elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "aarch64")
|
||||
set(CPU_ARCH "aarch64")
|
||||
elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv7-a")
|
||||
set(CPU_ARCH "arm")
|
||||
elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "arm" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv7-a" OR ${CMAKE_SYSTEM_PROCESSOR} STREQUAL "armv7l")
|
||||
set(CPU_ARCH "aarch32")
|
||||
if(ANDROID)
|
||||
# Force ARM mode, since apparently ANDROID_ARM_MODE isn't working..
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -marm")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -marm")
|
||||
else()
|
||||
# Enable NEON.
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -marm -march=armv7-a")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -marm -march=armv7-a")
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Unknown system processor: " ${CMAKE_SYSTEM_PROCESSOR})
|
||||
endif()
|
||||
|
||||
|
||||
# Write binaries to a seperate directory.
|
||||
if(WIN32 AND NOT BUILD_LIBRETRO_CORE)
|
||||
if(WIN32)
|
||||
# For Windows, use the source directory, except for libretro.
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/${CPU_ARCH}")
|
||||
else()
|
||||
@@ -221,6 +238,6 @@ enable_testing()
|
||||
add_subdirectory(dep)
|
||||
add_subdirectory(src)
|
||||
|
||||
if(ANDROID AND NOT BUILD_LIBRETRO_CORE)
|
||||
if(ANDROID)
|
||||
add_subdirectory(android/app/src/cpp)
|
||||
endif()
|
||||
|
||||
70
CMakeModules/FindGBM.cmake
Normal file
70
CMakeModules/FindGBM.cmake
Normal file
@@ -0,0 +1,70 @@
|
||||
# https://fossies.org/linux/misc/xbmc-18.9-Leia.tar.gz/xbmc-18.9-Leia/cmake/modules/FindGBM.cmake?m=t
|
||||
|
||||
# FindGBM
|
||||
# ----------
|
||||
# Finds the GBM library
|
||||
#
|
||||
# This will define the following variables::
|
||||
#
|
||||
# GBM_FOUND - system has GBM
|
||||
# GBM_INCLUDE_DIRS - the GBM include directory
|
||||
# GBM_LIBRARIES - the GBM libraries
|
||||
# GBM_DEFINITIONS - the GBM definitions
|
||||
#
|
||||
# and the following imported targets::
|
||||
#
|
||||
# GBM::GBM - The GBM library
|
||||
|
||||
if(PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PC_GBM gbm QUIET)
|
||||
endif()
|
||||
|
||||
find_path(GBM_INCLUDE_DIR NAMES gbm.h
|
||||
PATHS ${PC_GBM_INCLUDEDIR})
|
||||
find_library(GBM_LIBRARY NAMES gbm
|
||||
PATHS ${PC_GBM_LIBDIR})
|
||||
|
||||
set(GBM_VERSION ${PC_GBM_VERSION})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(GBM
|
||||
REQUIRED_VARS GBM_LIBRARY GBM_INCLUDE_DIR
|
||||
VERSION_VAR GBM_VERSION)
|
||||
|
||||
include(CheckCSourceCompiles)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${GBM_LIBRARY})
|
||||
check_c_source_compiles("#include <gbm.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
gbm_bo_map(NULL, 0, 0, 0, 0, GBM_BO_TRANSFER_WRITE, NULL, NULL);
|
||||
}
|
||||
" GBM_HAS_BO_MAP)
|
||||
|
||||
check_c_source_compiles("#include <gbm.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
gbm_surface_create_with_modifiers(NULL, 0, 0, 0, NULL, 0);
|
||||
}
|
||||
" GBM_HAS_MODIFIERS)
|
||||
|
||||
if(GBM_FOUND)
|
||||
set(GBM_LIBRARIES ${GBM_LIBRARY})
|
||||
set(GBM_INCLUDE_DIRS ${GBM_INCLUDE_DIR})
|
||||
set(GBM_DEFINITIONS -DHAVE_GBM=1)
|
||||
if(GBM_HAS_BO_MAP)
|
||||
list(APPEND GBM_DEFINITIONS -DHAS_GBM_BO_MAP=1)
|
||||
endif()
|
||||
if(GBM_HAS_MODIFIERS)
|
||||
list(APPEND GBM_DEFINITIONS -DHAS_GBM_MODIFIERS=1)
|
||||
endif()
|
||||
if(NOT TARGET GBM::GBM)
|
||||
add_library(GBM::GBM UNKNOWN IMPORTED)
|
||||
set_target_properties(GBM::GBM PROPERTIES
|
||||
IMPORTED_LOCATION "${GBM_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${GBM_INCLUDE_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
mark_as_advanced(GBM_INCLUDE_DIR GBM_LIBRARY)
|
||||
33
CMakeModules/FindLIBEVDEV.cmake
Normal file
33
CMakeModules/FindLIBEVDEV.cmake
Normal file
@@ -0,0 +1,33 @@
|
||||
# - Try to find libevdev
|
||||
# Once done this will define
|
||||
# LIBEVDEV_FOUND - System has libevdev
|
||||
# LIBEVDEV_INCLUDE_DIRS - The libevdev include directories
|
||||
# LIBEVDEV_LIBRARIES - The libraries needed to use libevdev
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PC_LIBEVDEV QUIET libevdev)
|
||||
|
||||
FIND_PATH(
|
||||
LIBEVDEV_INCLUDE_DIR libevdev/libevdev.h
|
||||
HINTS ${PC_LIBEVDEV_INCLUDEDIR} ${PC_LIBEVDEV_INCLUDE_DIRS}
|
||||
/usr/include
|
||||
/usr/local/include
|
||||
${LIBEVDEV_PATH_INCLUDES}
|
||||
)
|
||||
|
||||
FIND_LIBRARY(
|
||||
LIBEVDEV_LIBRARY
|
||||
NAMES evdev libevdev
|
||||
HINTS ${PC_LIBEVDEV_LIBDIR} ${PC_LIBEVDEV_LIBRARY_DIRS}
|
||||
PATHS ${ADDITIONAL_LIBRARY_PATHS}
|
||||
${LIBEVDEV_PATH_LIB}
|
||||
)
|
||||
|
||||
set(LIBEVDEV_LIBRARIES ${LIBEVDEV_LIBRARY} )
|
||||
set(LIBEVDEV_INCLUDE_DIRS ${LIBEVDEV_INCLUDE_DIR} )
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(LIBEVDEV DEFAULT_MSG
|
||||
LIBEVDEV_LIBRARY LIBEVDEV_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(LIBEVDEV_INCLUDE_DIR LIBEVDEV_LIBRARY )
|
||||
107
CMakeModules/FindLibdrm.cmake
Normal file
107
CMakeModules/FindLibdrm.cmake
Normal file
@@ -0,0 +1,107 @@
|
||||
# https://raw.githubusercontent.com/KDE/kwin/master/cmake/modules/FindLibdrm.cmake
|
||||
|
||||
#.rst:
|
||||
# FindLibdrm
|
||||
# -------
|
||||
#
|
||||
# Try to find libdrm on a Unix system.
|
||||
#
|
||||
# This will define the following variables:
|
||||
#
|
||||
# ``Libdrm_FOUND``
|
||||
# True if (the requested version of) libdrm is available
|
||||
# ``Libdrm_VERSION``
|
||||
# The version of libdrm
|
||||
# ``Libdrm_LIBRARIES``
|
||||
# This can be passed to target_link_libraries() instead of the ``Libdrm::Libdrm``
|
||||
# target
|
||||
# ``Libdrm_INCLUDE_DIRS``
|
||||
# This should be passed to target_include_directories() if the target is not
|
||||
# used for linking
|
||||
# ``Libdrm_DEFINITIONS``
|
||||
# This should be passed to target_compile_options() if the target is not
|
||||
# used for linking
|
||||
#
|
||||
# If ``Libdrm_FOUND`` is TRUE, it will also define the following imported target:
|
||||
#
|
||||
# ``Libdrm::Libdrm``
|
||||
# The libdrm library
|
||||
#
|
||||
# In general we recommend using the imported target, as it is easier to use.
|
||||
# Bear in mind, however, that if the target is in the link interface of an
|
||||
# exported library, it must be made available by the package config file.
|
||||
|
||||
#=============================================================================
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
# SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#=============================================================================
|
||||
|
||||
if(CMAKE_VERSION VERSION_LESS 2.8.12)
|
||||
message(FATAL_ERROR "CMake 2.8.12 is required by FindLibdrm.cmake")
|
||||
endif()
|
||||
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
|
||||
message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use FindLibdrm.cmake")
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
# Use pkg-config to get the directories and then use these values
|
||||
# in the FIND_PATH() and FIND_LIBRARY() calls
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(PKG_Libdrm QUIET libdrm)
|
||||
|
||||
set(Libdrm_DEFINITIONS ${PKG_Libdrm_CFLAGS_OTHER})
|
||||
set(Libdrm_VERSION ${PKG_Libdrm_VERSION})
|
||||
|
||||
find_path(Libdrm_INCLUDE_DIR
|
||||
NAMES
|
||||
xf86drm.h
|
||||
HINTS
|
||||
${PKG_Libdrm_INCLUDE_DIRS}
|
||||
)
|
||||
find_library(Libdrm_LIBRARY
|
||||
NAMES
|
||||
drm
|
||||
HINTS
|
||||
${PKG_Libdrm_LIBRARY_DIRS}
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Libdrm
|
||||
FOUND_VAR
|
||||
Libdrm_FOUND
|
||||
REQUIRED_VARS
|
||||
Libdrm_LIBRARY
|
||||
Libdrm_INCLUDE_DIR
|
||||
VERSION_VAR
|
||||
Libdrm_VERSION
|
||||
)
|
||||
|
||||
if(Libdrm_FOUND AND NOT TARGET Libdrm::Libdrm)
|
||||
add_library(Libdrm::Libdrm UNKNOWN IMPORTED)
|
||||
set_target_properties(Libdrm::Libdrm PROPERTIES
|
||||
IMPORTED_LOCATION "${Libdrm_LIBRARY}"
|
||||
INTERFACE_COMPILE_OPTIONS "${Libdrm_DEFINITIONS}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}/libdrm"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(Libdrm_LIBRARY Libdrm_INCLUDE_DIR)
|
||||
|
||||
# compatibility variables
|
||||
set(Libdrm_LIBRARIES ${Libdrm_LIBRARY})
|
||||
set(Libdrm_INCLUDE_DIRS ${Libdrm_INCLUDE_DIR} "${Libdrm_INCLUDE_DIR}/libdrm")
|
||||
set(Libdrm_VERSION_STRING ${Libdrm_VERSION})
|
||||
|
||||
else()
|
||||
message(STATUS "FindLibdrm.cmake cannot find libdrm on Windows systems.")
|
||||
set(Libdrm_FOUND FALSE)
|
||||
endif()
|
||||
|
||||
include(FeatureSummary)
|
||||
set_package_properties(Libdrm PROPERTIES
|
||||
URL "https://wiki.freedesktop.org/dri/"
|
||||
DESCRIPTION "Userspace interface to kernel DRM services."
|
||||
)
|
||||
@@ -238,7 +238,7 @@ endif()
|
||||
# But for non-OSX systems, I will use the CMake Threads package.
|
||||
if(NOT APPLE)
|
||||
find_package(Threads QUIET)
|
||||
if(NOT CMAKE_THREAD_LIBS_INIT AND NOT WIN32)
|
||||
if(NOT Threads_FOUND AND NOT WIN32)
|
||||
set(SDL2_THREADS_NOT_FOUND "Could NOT find Threads (Threads is required by SDL2).")
|
||||
if(SDL2_FIND_REQUIRED)
|
||||
message(FATAL_ERROR ${SDL2_THREADS_NOT_FOUND})
|
||||
|
||||
14
CMakeModules/armv7-cross-toolchain.cmake
Normal file
14
CMakeModules/armv7-cross-toolchain.cmake
Normal file
@@ -0,0 +1,14 @@
|
||||
# Source: https://github.com/stenzek/duckstation/issues/626#issuecomment-660718306
|
||||
|
||||
# Target system
|
||||
SET(CMAKE_SYSTEM_NAME Linux)
|
||||
SET(CMAKE_SYSTEM_PROCESSOR armv7l)
|
||||
SET(CMAKE_SYSTEM_VERSION 1)
|
||||
set(CMAKE_CROSSCOMPILING TRUE)
|
||||
|
||||
# Cross compiler
|
||||
SET(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc)
|
||||
SET(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++)
|
||||
set(CMAKE_LIBRARY_ARCHITECTURE arm-linux-gnueabihf)
|
||||
|
||||
set(THREADS_PTHREAD_ARG "0" CACHE STRING "Result from TRY_RUN" FORCE)
|
||||
@@ -9,14 +9,21 @@ The following people have contributed to the project in some way, and are credit
|
||||
## Translators
|
||||
- Anderson Cardoso - Portuguese (Br)
|
||||
- @bajolzas - Portuguese (Pt)
|
||||
- posix - @Richard-L - German
|
||||
- posix - @Richard-L, blexx - German
|
||||
- @phoe-nix, @zkdpower - Chinese (Simplified)
|
||||
- Sorer - @MojoJojoDojo - Hebrew
|
||||
- Hipnosis183 - Spanish
|
||||
- Hipnosis - @Hipnosis183, MrHomunculus, @falsepopsky - Spanish
|
||||
- @RaydenX93 - Italian
|
||||
- @r57zone - Russian
|
||||
- @6lackmag3 - Russian (Android)
|
||||
- @DenSinH - Dutch
|
||||
- @BenjaminSiskoo - French
|
||||
- mikakunin - Japanese
|
||||
- Zuzia, Seba - Polish
|
||||
|
||||
## Cheat Database
|
||||
- Pugsy
|
||||
- Unicorngoulash
|
||||
|
||||
## Game Compatibility Database
|
||||
- @Zet-sensei
|
||||
@@ -37,6 +44,14 @@ The following people have contributed to the project in some way, and are credit
|
||||
- @Richard-L
|
||||
- @pan2marumie3
|
||||
- @CookiePLMonster
|
||||
- @LoStraniero91
|
||||
- @JFD62780
|
||||
- @lmarciano9
|
||||
- @Facepalm38
|
||||
- @Alien-Grey
|
||||
- @dmlipat
|
||||
- @Krusher97
|
||||
- @AngryScotsmanGaming
|
||||
|
||||
## Special Thanks
|
||||
The following people did not directly contribute to the emulator, but it would not be in the state if not for them.
|
||||
|
||||
53
NEWS.md
Normal file
53
NEWS.md
Normal file
@@ -0,0 +1,53 @@
|
||||
- 2021/01/24: Runahead added - work around input lag in some games by running frames ahead of time and backtracking on input. DuckStation's implementation works with upscaling and the hardware renderers, but you still require a powerful computer for higher frame counts.
|
||||
- 2021/01/24: Rewind added - you can now "smooth rewind" (but not for long), or "skip rewind" (for much long) while playing.
|
||||
- 2021/01/10: Option to sync to host refresh rate added (enabled by default). This will give the smoothest animation possible with zero duped frames, at the cost of running the game <1% faster. Users with variable refresh rate (GSync/FreeSync) displays will want to disable the option.
|
||||
- 2021/01/10: Audio resampling added when fast forwarding to fixed speeds. Instead of crackling audio, you'll now get pitch altered audio.
|
||||
- 2021/01/03: Per game settings and game properties added to Android version.
|
||||
- 2020/12/30: Box and Adaptive downsampling modes added. Adaptive downsampling will smooth 2D backgrounds but attempt to preserve 3D geometry via pixel similarity (only supported in D3D11/Vulkan). Box is a simple average filter which will downsample to native resolution.
|
||||
- 2020/12/30: Hotkey binding added to Android version. You can now bind hotkeys such as fast forward, save state, etc to controller buttons. The ability to bind multi-button combinations will be added in the future.
|
||||
- 2020/12/29: Controller mapping/binding added for Android version. By default mappings will be clear and you will have to set them, you can do this from `Settings -> Controllers -> Controller Mapping`. Profiles can be saved and loaded as well.
|
||||
- 2020/12/29: Dark theme added for Android. By default it will follow your system theme (Android 10+), but can be overridden in settings.
|
||||
- 2020/12/29: DirectInput/DInput controller interface added for Windows. You can use this if you are having difficulties with SDL. Vibration is not supported yet.
|
||||
- 2020/12/25: Partial texture replacement support added. For now, this is only applicable to a small number of games which upload backgrounds to video RAM every frame. Dumping and replacement options are available in `Advanced Settings`.
|
||||
- 2020/12/22: PGXP Depth Buffer enhancement added. This enhancement can eliminate "polygon fighting" in games, by giving the PS1 the depth buffer it never had. Compatibility is rather low at the moment, but for the games it does work in, it works very well. The depth buffer will be made available to postprocessing shaders in the future, enabling effects such as SSAO.
|
||||
- 2020/12/21: DuckStation now has two releases - Development and Preview. New features will appear in Preview first, and make their way to the Development release a few days later. To switch to preview, update to the latest development build (older builds will update to development), change the channel from `latest` to `preview` in general settings, and click `Check for Updates`.
|
||||
- 2020/12/16: Integrated CPU debugger added in Qt frontend.
|
||||
- 2020/12/13: Button layout for the touchscreen controller in the Android version can now be customized.
|
||||
- 2020/12/10: Translation support added for Android version. Currently Brazillian Portuguese, Italian, and Dutch are available.
|
||||
- 2020/11/27: Cover support added for game list in Android version. Procedure is the same as the desktop version, except you should place cover images in `<storage>/duckstation/covers` (see [Adding Game Covers](https://github.com/stenzek/duckstation/wiki/Adding-Game-Covers)).
|
||||
- 2020/11/27: Disc database is shipped with desktop and Android versions courtesy of redump.org. This will provide titles for games on Android, where it was not possible previously.
|
||||
- 2020/11/27: Compatibility databases added to libretro core - broken enhancements will be automatically disabled. You can turn this off by disabling "Apply Compatibility Settings" in the core options.
|
||||
- 2020/11/27: SDL game controller database is included with desktop versions courtesy of https://github.com/gabomdq/SDL_GameControllerDB.
|
||||
- 2020/11/21: OpenGL ES 2.0 host display support added. You cannot use the hardware renderer with GLES2, it still requires GLES3, but GLES2 GPUs can now use the software renderer.
|
||||
- 2020/11/21: Threaded renderer for software renderer added. Can result in a significant speed boost depending on the game.
|
||||
- 2020/11/21: AArch32/armv7 recompiler added. Android and Linux builds will follow after further testing, but for now you can build it yourself.
|
||||
- 2020/11/18: Window size (resize window to Nx content resolution) added to Qt and SDL frontends.
|
||||
- 2020/11/10: Widescreen hack now renders in the display aspect ratio instead of always 16:9.
|
||||
- 2020/11/01: Exclusive fullscreen option added for Windows D3D11 users. Enjoy buttery smooth PAL games.
|
||||
- 2020/10/31: Multisample antialiasing added as an enhancement.
|
||||
- 2020/10/30: Option to use analog stick as d-pad for analog controller added.
|
||||
- 2020/10/20: New cheat manager with memory scanning added. More features will be added over time.
|
||||
- 2020/10/05: CD-ROM read speedup enhancement added.
|
||||
- 2020/09/30: CPU overclocking is now supported. Use with caution as it will break games and increase system requirements. It can be set globally or per-game.
|
||||
- 2020/09/25: Cheat support added for libretro core.
|
||||
- 2020/09/23: Game covers added to Qt frontend (see [Adding Game Covers](https://github.com/stenzek/duckstation/wiki/Adding-Game-Covers)).
|
||||
- 2020/09/19: Memory card importer/editor added to Qt frontend.
|
||||
- 2020/09/13: Support for chaining post processing shaders added.
|
||||
- 2020/09/12: Additional texture filtering options added.
|
||||
- 2020/09/09: Basic cheat support added. Not all instructions/commands are supported yet.
|
||||
- 2020/09/01: Many additional user settings available, including memory cards and enhancements. Now you can set these per-game.
|
||||
- 2020/08/25: Automated builds for macOS now available.
|
||||
- 2020/08/22: XInput controller backend added.
|
||||
- 2020/08/20: Per-game setting overrides added. Mostly for compatibility, but some options are customizable.
|
||||
- 2020/08/19: CPU PGXP mode added. It is very slow and incompatible with the recompiler, only use for games which need it.
|
||||
- 2020/08/15: Playlist support/single memcard for multi-disc games in Qt frontend added.
|
||||
- 2020/08/07: Automatic updater for standalone Windows builds.
|
||||
- 2020/08/01: Initial PGXP (geometry/perspective correction) support.
|
||||
- 2020/07/28: Qt frontend supports displaying interface in multiple languages.
|
||||
- 2020/07/23: m3u multi-disc support for libretro core.
|
||||
- 2020/07/22: Support multiple bindings for each controller button/axis.
|
||||
- 2020/07/18: Widescreen hack enhancement added.
|
||||
- 2020/07/04: Vulkan renderer now available in libretro core.
|
||||
- 2020/07/02: Now available as a libretro core.
|
||||
- 2020/07/01: Lightgun support with custom crosshairs.
|
||||
- 2020/06/19: Vulkan hardware renderer added.
|
||||
165
README.md
165
README.md
@@ -1,57 +1,57 @@
|
||||
# DuckStation - PlayStation 1, aka. PSX Emulator
|
||||
[Latest News](#latest-news) | [Features](#features) | [Screenshots](#screenshots) | [Downloading and Running](#downloading-and-running) | [Libretro Core](#libretro-core) | [Building](#building) | [Disclaimers](#disclaimers)
|
||||
[Latest News](#latest-news) | [Features](#features) | [Screenshots](#screenshots) | [Downloading and Running](#downloading-and-running) | [Building](#building) | [Disclaimers](#disclaimers)
|
||||
|
||||
**Discord Server:** https://discord.gg/Buktv3t
|
||||
|
||||
**Latest Windows, Linux (AppImage), and Libretro Builds:** https://github.com/stenzek/duckstation/releases/tag/latest
|
||||
**Latest Windows, Linux (AppImage), Mac, Android** https://github.com/stenzek/duckstation/releases/tag/latest
|
||||
|
||||
**Available on Google Play:** https://play.google.com/store/apps/details?id=com.github.stenzek.duckstation&hl=en_AU&gl=US
|
||||
|
||||
**Game Compatibility List:** https://docs.google.com/spreadsheets/d/1H66MxViRjjE5f8hOl5RQmF5woS1murio2dsLn14kEqo/edit?usp=sharing
|
||||
|
||||
**Wiki:** https://www.duckstation.org/wiki/
|
||||
|
||||
DuckStation is an simulator/emulator of the Sony PlayStation(TM) console, focusing on playability, speed, and long-term maintainability. The goal is to be as accurate as possible while maintaining performance suitable for low-end devices. "Hack" options are discouraged, the default configuration should support all playable games with only some of the enhancements having compatibility issues.
|
||||
|
||||
A "BIOS" ROM image is required to to start the emulator and to play games. You can use an image from any hardware version or region, although mismatching game regions and BIOS regions may have compatibility issues. A ROM image is not provided with the emulator for legal reasons, you should dump this from your own console using Caetla or other means.
|
||||
|
||||
## Latest News
|
||||
Older entries are available at https://github.com/stenzek/duckstation/blob/master/NEWS.md
|
||||
|
||||
- 2020/10/05: CD-ROM read speedup enhancement added.
|
||||
- 2020/09/30: CPU overclocking is now supported. Use with caution as it will break games and increase system requirements. It can be set globally or per-game.
|
||||
- 2020/09/25: Cheat support added for libretro core.
|
||||
- 2020/09/23: Game covers added to Qt frontend (see [Adding Game Covers](https://github.com/stenzek/duckstation/wiki/Adding-Game-Covers)).
|
||||
- 2020/09/19: Memory card importer/editor added to Qt frontend.
|
||||
- 2020/09/13: Support for chaining post processing shaders added.
|
||||
- 2020/09/12: Additional texture filtering options added.
|
||||
- 2020/09/09: Basic cheat support added. Not all instructions/commands are supported yet.
|
||||
- 2020/09/01: Many additional user settings available, including memory cards and enhancements. Now you can set these per-game.
|
||||
- 2020/08/25: Automated builds for macOS now available.
|
||||
- 2020/08/22: XInput controller backend added.
|
||||
- 2020/08/20: Per-game setting overrides added. Mostly for compatibility, but some options are customizable.
|
||||
- 2020/08/19: CPU PGXP mode added. It is very slow and incompatible with the recompiler, only use for games which need it.
|
||||
- 2020/08/15: Playlist support/single memcard for multi-disc games in Qt frontend added.
|
||||
- 2020/08/07: Automatic updater for standalone Windows builds.
|
||||
- 2020/08/01: Initial PGXP (geometry/perspective correction) support.
|
||||
- 2020/07/28: Qt frontend supports displaying interface in multiple languages.
|
||||
- 2020/07/23: m3u multi-disc support for libretro core.
|
||||
- 2020/07/22: Support multiple bindings for each controller button/axis.
|
||||
- 2020/07/18: Widescreen hack enhancement added.
|
||||
- 2020/07/04: Vulkan renderer now available in libretro core.
|
||||
- 2020/07/02: Now available as a libretro core.
|
||||
- 2020/07/01: Lightgun support with custom crosshairs.
|
||||
- 2020/06/19: Vulkan hardware renderer added.
|
||||
- 2021/01/31: "Fullscreen UI" added, aka "Big Duck/TV Mode". This interface is fully navigatible with a controller. Currently it's limited to the NoGUI frontend, but it will be available directly in the Qt frontend in the near future, with more features being added (e.g. game grid) as well.
|
||||
- 2021/01/24: Runahead added - work around input lag in some games by running frames ahead of time and backtracking on input. DuckStation's implementation works with upscaling and the hardware renderers, but you still require a powerful computer for higher frame counts.
|
||||
- 2021/01/24: Rewind added - you can now "smooth rewind" (but not for long), or "skip rewind" (for much long) while playing.
|
||||
- 2021/01/10: Option to sync to host refresh rate added (enabled by default). This will give the smoothest animation possible with zero duped frames, at the cost of running the game <1% faster. Users with variable refresh rate (GSync/FreeSync) displays will want to disable the option.
|
||||
- 2021/01/10: Audio resampling added when fast forwarding to fixed speeds. Instead of crackling audio, you'll now get pitch altered audio.
|
||||
- 2021/01/03: Per game settings and game properties added to Android version.
|
||||
- 2020/12/30: Box and Adaptive downsampling modes added. Adaptive downsampling will smooth 2D backgrounds but attempt to preserve 3D geometry via pixel similarity (only supported in D3D11/Vulkan). Box is a simple average filter which will downsample to native resolution.
|
||||
- 2020/12/30: Hotkey binding added to Android version. You can now bind hotkeys such as fast forward, save state, etc to controller buttons. The ability to bind multi-button combinations will be added in the future.
|
||||
- 2020/12/29: Controller mapping/binding added for Android version. By default mappings will be clear and you will have to set them, you can do this from `Settings -> Controllers -> Controller Mapping`. Profiles can be saved and loaded as well.
|
||||
- 2020/12/29: Dark theme added for Android. By default it will follow your system theme (Android 10+), but can be overridden in settings.
|
||||
- 2020/12/29: DirectInput/DInput controller interface added for Windows. You can use this if you are having difficulties with SDL. Vibration is not supported yet.
|
||||
- 2020/12/25: Partial texture replacement support added. For now, this is only applicable to a small number of games which upload backgrounds to video RAM every frame. Dumping and replacement options are available in `Advanced Settings`.
|
||||
- 2020/12/22: PGXP Depth Buffer enhancement added. This enhancement can eliminate "polygon fighting" in games, by giving the PS1 the depth buffer it never had. Compatibility is rather low at the moment, but for the games it does work in, it works very well. The depth buffer will be made available to postprocessing shaders in the future, enabling effects such as SSAO.
|
||||
- 2020/12/21: DuckStation now has two releases - Development and Preview. New features will appear in Preview first, and make their way to the Development release a few days later. To switch to preview, update to the latest development build (older builds will update to development), change the channel from `latest` to `preview` in general settings, and click `Check for Updates`.
|
||||
- 2020/12/16: Integrated CPU debugger added in Qt frontend.
|
||||
- 2020/12/13: Button layout for the touchscreen controller in the Android version can now be customized.
|
||||
- 2020/12/10: Translation support added for Android version. Currently Brazillian Portuguese, Italian, and Dutch are available.
|
||||
|
||||
## Features
|
||||
|
||||
DuckStation features a fully-featured frontend built using Qt (pictured), as well as a simplified frontend based on SDL and Dear ImGui. An Android version has been started, but is not yet feature complete.
|
||||
DuckStation features a fully-featured frontend built using Qt, as well as a fullscreen/TV UI based on Dear ImGui. An Android version has been started, but is not yet feature complete.
|
||||
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/main-qt.png" alt="Main Window Screenshot" />
|
||||
<img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/bigduck.png" alt="Fullscreen UI Screenshot" />
|
||||
</p>
|
||||
|
||||
Other features include:
|
||||
|
||||
- CPU Recompiler/JIT (x86-64 and AArch64)
|
||||
- CPU Recompiler/JIT (x86-64, armv7/AArch32 and AArch64)
|
||||
- Hardware (D3D11, OpenGL, Vulkan) and software rendering
|
||||
- Upscaling, texture filtering, and true colour (24-bit) in hardware renderers
|
||||
- PGXP for geometry precision and texture correction
|
||||
- PGXP for geometry precision, texture correction, and depth buffer emulation
|
||||
- Adaptive downsampling filter
|
||||
- Post processing shader chains
|
||||
- "Fast boot" for skipping BIOS splash/intro
|
||||
- Save state support
|
||||
@@ -62,23 +62,22 @@ Other features include:
|
||||
- Digital and analog controllers for input (rumble is forwarded to host)
|
||||
- Namco GunCon lightgun support (simulated with mouse)
|
||||
- NeGcon support
|
||||
- Qt and SDL frontends for desktop
|
||||
- libretro core for Windows and Linux
|
||||
- Qt and NoGUI frontends for desktop
|
||||
- Automatic updates for Windows builds
|
||||
- Automatic content scanning - game titles/regions are provided by redump.org
|
||||
- Optional automatic switching of memory cards for each game
|
||||
- Supports loading cheats from libretro or PCSXR format lists
|
||||
- Memory card editor and save importer
|
||||
- Emulated CPU overclocking
|
||||
- Integrated and remote debugging
|
||||
|
||||
## System Requirements
|
||||
- A CPU faster than a potato. But it needs to be 64-bit (either x86_64 or AArch64/ARMv8) otherwise you won't get a recompiler and it'll be slow. There are no plans to add any 32-bit recompilers.
|
||||
- For the hardware renderers, a GPU capable of OpenGL 3.0/OpenGL ES 3.0/Direct3D 11 Feature Level 10.0 (or Vulkan 1.0) and above. So, basically anything made in the last 10 years or so.
|
||||
- SDL or XInput compatible game controller (e.g. XB360/XBOne). DualShock 3 users on Windows will need to install the official DualShock 3 drivers included as part of PlayStation Now.
|
||||
- Optional [SDL game contoller database files](#sdl-game-controller-database) are also supported.
|
||||
- A CPU faster than a potato. But it needs to be x86_64, AArch32/armv7, or AArch64/ARMv8, otherwise you won't get a recompiler and it'll be slow.
|
||||
- For the hardware renderers, a GPU capable of OpenGL 3.1/OpenGL ES 3.0/Direct3D 11 Feature Level 10.0 (or Vulkan 1.0) and above. So, basically anything made in the last 10 years or so.
|
||||
- SDL, XInput or DInput compatible game controller (e.g. XB360/XBOne). DualShock 3 users on Windows will need to install the official DualShock 3 drivers included as part of PlayStation Now.
|
||||
|
||||
## Downloading and running
|
||||
Binaries of DuckStation for Windows 64-bit, x86_64 Linux x86_64 (in AppImage format), and Android ARMv8/AArch64 are available via GitHub Releases and are automatically built with every commit/push. Binaries or packages distributed through other sources may be out of date and are not supported by the developer.
|
||||
Binaries of DuckStation for Windows x64/ARM64, x86_64 Linux x86_64 (in AppImage format), and Android ARMv8/AArch64 are available via GitHub Releases and are automatically built with every commit/push. Binaries or packages distributed through other sources may be out of date and are not supported by the developer.
|
||||
|
||||
### Windows
|
||||
|
||||
@@ -89,10 +88,10 @@ To download:
|
||||
- Alternatively, direct download link: https://github.com/stenzek/duckstation/releases/download/latest/duckstation-windows-x64-release.zip
|
||||
- Extract the archive **to a subdirectory**. The archive has no root subdirectory, so extracting to the current directory will drop a bunch of files in your download directory if you do not extract to a subdirectory.
|
||||
|
||||
Once downloaded and extracted, you can launch the Qt frontend from `duckstation-qt-x64-ReleaseLTCG.exe`, or the SDL frontend from `duckstation-sdl-x64-ReleaseLTCG.exe`.
|
||||
Once downloaded and extracted, you can launch the emulator with `duckstation-qt-x64-ReleaseLTCG.exe`.
|
||||
To set up:
|
||||
1. Either configure the path to a BIOS image in the settings, or copy one or more PlayStation BIOS images to the bios/ subdirectory. On Windows, by default this will be located in `C:\Users\YOUR_USERNAME\Documents\DuckStation\bios`. If you don't want to use the Documents directory to save the BIOS/memory cards/etc, you can use portable mode. See [User directory](#user-directories).
|
||||
2. If using the SDL frontend, add the directories containing your disc images by clicking `Settings->Add Game Directory`.
|
||||
2. If using the Qt frontend, add the directories containing your disc images by clicking `Settings->Add Game Directory`.
|
||||
2. Select a game from the list, or open a disc image file and enjoy.
|
||||
|
||||
**If you get an error about `vcruntime140_1.dll` being missing, you will need to update your Visual C++ runtime.** You can do that from this page: https://support.microsoft.com/en-au/help/2977003/the-latest-supported-visual-c-downloads. Specifically, you want the x64 runtime, which can be downloaded from https://aka.ms/vs/16/release/vc_redist.x64.exe.
|
||||
@@ -106,7 +105,7 @@ Prebuilt binaries for 64-bit Linux distros are available for download in the App
|
||||
**Linux users are encouraged to build from source when possible and optionally create their own AppImages for features such as desktop integration if desired.**
|
||||
|
||||
To download:
|
||||
- Go to https://github.com/stenzek/duckstation/releases/tag/latest, and download either `duckstation-qt-x64.AppImage` or `duckstation-sdl-x64.AppImage` for your desired frontend. Keep in mind that keyboard/controller bindings are currently not customizable through the SDL frontend and should be customized through the Qt frontend instead.
|
||||
- Go to https://github.com/stenzek/duckstation/releases/tag/latest, and download either `duckstation-qt-x64.AppImage` or `duckstation-nogui-x64.AppImage` for your desired frontend.
|
||||
- Run `chmod a+x` on the downloaded AppImage -- following this step, the AppImage can be run like a typical executable.
|
||||
- Optionally use a program such as [appimaged](https://github.com/AppImage/appimaged) or [AppImageLauncher](https://github.com/TheAssassin/AppImageLauncher) for desktop integration. [AppImageUpdate](https://github.com/AppImage/AppImageUpdate) can be used alongside appimaged to easily update your DuckStation AppImage.
|
||||
|
||||
@@ -126,27 +125,21 @@ they are investigated.
|
||||
|
||||
### Android
|
||||
|
||||
A prebuilt APK is now available for Android. However, please keep in mind that the Android version is not yet feature complete, it is more of a preview of things to come. You will need a device running a 64-bit AArch64 userland (anything made in the last few years).
|
||||
A prebuilt APK is now available for Android. However, please keep in mind that the Android version does not contain all features present in the desktop version yet. You will need a device with armv7 (32-bit ARM) or AArch64 (64-bit ARM). 64-bit is preferred, the requirements are higher for 32-bit, you'll probably want at least a 1.5GHz CPU.
|
||||
|
||||
Download link: https://github.com/stenzek/duckstation/releases/download/latest/duckstation-android-aarch64.apk
|
||||
|
||||
The main limitations are:
|
||||
- User directory is currently hardcoded to `<external storage path>/duckstation`. This is usually `/storage/emulated/0` or `/sdcard`'. So BIOS files go in `/sdcard/duckstation/bios`.
|
||||
- Lack of options in menu when emulator is running.
|
||||
- Performance is currently lower than the desktop x86_64 counterpart.
|
||||
Download link: https://github.com/stenzek/duckstation/releases/download/latest/duckstation-android.apk
|
||||
|
||||
To use:
|
||||
- Install and run the app for the first time.
|
||||
- This will create `/sdcard/duckstation`. Drop your BIOS files in `/sdcard/duckstation/bios`.
|
||||
- Add game directories by hitting the `+` icon and selecting a directory.
|
||||
- Map your controller buttons and axes by going into `Controller Mapping` under `Controllers` in `Settings`.
|
||||
- Tap a game to start.
|
||||
|
||||
|
||||
### Title Information
|
||||
|
||||
PlayStation game discs do not contain title information. For game titles, we use the redump.org database cross-referenced with the game's executable code.
|
||||
This database can be manually downloaded and added as `cache/redump.dat`, or automatically downloaded by going into the `Game List Settings` in the Qt Frontend,
|
||||
and clicking `Update Redump Database`.
|
||||
PlayStation game discs do not contain title information. For game titles, we use the redump.org database cross-referenced with the game's executable code. A version of the database is included with the DuckStation download, but you can replace this with a different database by saving it as `cache/redump.dat` in your user directory, or updated by going into the `Game List Settings` in the Qt Frontend, and clicking `Update Redump Database`.
|
||||
|
||||
### Region detection and BIOS images
|
||||
By default, DuckStation will emulate the region check present in the CD-ROM controller of the console. This means that when the region of the console does not match the disc, it will refuse to boot, giving a "Please insert PlayStation CD-ROM" message. DuckStation supports automatic detection disc regions, and if you set the console region to auto-detect as well, this should never be a problem.
|
||||
@@ -161,7 +154,11 @@ Mismatching the disc and console regions with the check disabled is supported, b
|
||||
|
||||
### LibCrypt protection and SBI files
|
||||
|
||||
A number of PAL region games use LibCrypt protection, requiring additional CD subchannel information to run properly. For these games, make sure that the CD image and its corresponding SBI (.sbi) file have the same name and are placed in the same directory. DuckStation will automatically load the SBI file when it is found next to the CD image.
|
||||
A number of PAL region games use LibCrypt protection, requiring additional CD subchannel information to run properly. libcrypt not functioning usually manifests as hanging or crashing, but can sometimes affect gameplay too, depending on how the game implemented it.
|
||||
|
||||
For these games, make sure that the CD image and its corresponding SBI (.sbi) file have the same name and are placed in the same directory. DuckStation will automatically load the SBI file when it is found next to the CD image.
|
||||
|
||||
For example, if your disc image was named `Spyro3.cue`, you would place the SBI file in the same directory, and name it `Spyro3.sbi`.
|
||||
|
||||
## Building
|
||||
|
||||
@@ -169,25 +166,28 @@ A number of PAL region games use LibCrypt protection, requiring additional CD su
|
||||
Requirements:
|
||||
- Visual Studio 2019
|
||||
|
||||
1. Clone the respository with submodules (`git clone --recursive` or `git clone` and `git submodule update --init`).
|
||||
1. Clone the respository with submodules (`git clone --recursive https://github.com/stenzek/duckstation.git -b dev`).
|
||||
2. Open the Visual Studio solution `duckstation.sln` in the root, or "Open Folder" for cmake build.
|
||||
3. Build solution.
|
||||
4. Binaries are located in `bin/x64`.
|
||||
5. Run `duckstation-sdl-x64-Release.exe`/`duckstation-qt-x64-Release.exe` or whichever config you used.
|
||||
5. Run `duckstation-qt-x64-Release.exe` or whichever config you used.
|
||||
|
||||
### Linux
|
||||
Requirements (Debian/Ubuntu package names):
|
||||
- CMake (`cmake`)
|
||||
- SDL2 (`libsdl2-dev`)
|
||||
- GTK2.0 for file selector (`libgtk2.0-dev`)
|
||||
- pkgconfig (`pkg-config`)
|
||||
- Qt 5 (`qtbase5-dev`, `qtbase5-private-dev`, `qtbase5-dev-tools`, `qttools5-dev`)
|
||||
- libevdev (`libevdev-dev`)
|
||||
- git (`git`) (Note: needed to clone the repository and at build time)
|
||||
- Optional for faster building: Ninja (`ninja-build`)
|
||||
- Optional for framebuffer output: DRM/GBM (`libgbm-dev`, `libdrm-dev`)
|
||||
|
||||
1. Clone the repository. Submodules aren't necessary, there is only one and it is only used for Windows.
|
||||
1. Clone the repository. Submodules aren't necessary, there is only one and it is only used for Windows (`git clone https://github.com/stenzek/duckstation.git -b dev`).
|
||||
2. Create a build directory, either in-tree or elsewhere.
|
||||
3. Run cmake to configure the build system. Assuming a build subdirectory of `build-release`, `cd build-release && cmake -DCMAKE_BUILD_TYPE=Release -GNinja ..`.
|
||||
4. Compile the source code. For the example above, run `ninja`.
|
||||
5. Run the binary, located in the build directory under `bin/duckstation-sdl`, or `bin/duckstation-qt`.
|
||||
5. Run the binary, located in the build directory under `bin/duckstation-qt`.
|
||||
|
||||
### macOS
|
||||
**NOTE:** macOS is highly experimental and not tested by the developer. Use at your own risk, things may be horribly broken.
|
||||
@@ -197,16 +197,14 @@ Requirements:
|
||||
- SDL2 (`brew install sdl2`)
|
||||
- Qt 5 (`brew install qt5`)
|
||||
|
||||
1. Clone the repository. Submodules aren't necessary, there is only one and it is only used for Windows.
|
||||
1. Clone the repository. Submodules aren't necessary, there is only one and it is only used for Windows (`git clone https://github.com/stenzek/duckstation.git -b dev`).
|
||||
2. Clone the mac externals repository (for MoltenVK): `git clone https://github.com/stenzek/duckstation-ext-mac.git dep/mac`.
|
||||
2. Create a build directory, either in-tree or elsewhere, e.g. `mkdir build-release`, `cd build-release`.
|
||||
3. Run cmake to configure the build system: `cmake -DCMAKE_BUILD_TYPE=Release -DQt5_DIR=/usr/local/opt/qt/lib/cmake/Qt5 ..`. You may need to tweak `Qt5_DIR` depending on your system.
|
||||
4. Compile the source code: `make`. Use `make -jN` where `N` is the number of CPU cores in your system for a faster build.
|
||||
5. Run the binary, located in the build directory under `bin/duckstation-sdl`, or `bin/DuckStation.app` for Qt.
|
||||
5. Run the binary, located in the build directory under `bin/DuckStation.app`.
|
||||
|
||||
### Android
|
||||
**NOTE:** The Android frontend is still incomplete, not all functionality is available yet. User directory is hardcoded to `/sdcard/duckstation` for now.
|
||||
|
||||
Requirements:
|
||||
- Android Studio with the NDK and CMake installed
|
||||
|
||||
@@ -232,32 +230,21 @@ If you wish to use a "portable" build, where the user directory is the same as w
|
||||
in the same directory as the DuckStation executable.
|
||||
|
||||
## Bindings for Qt frontend
|
||||
Your keyboard and any SDL-compatible game controller can be used to simulate the PS Controller. To bind keys/controllers to buttons, go to
|
||||
`Settings -> Port Settings`. Each of the buttons will be listed, along with the corresponding key it is bound to. To re-bind the button to a new key,
|
||||
click the button next to button name, and press the key/button you want to use within 5 seconds.
|
||||
Your keyboard or game controller can be used to simulate a variety of PlayStation controllers. Controller input is supported through DInput, XInput, and SDL backends and can be changed through `Settings -> General Settings`.
|
||||
|
||||
**Currently, it is only possible to bind one input to each controller button/axis. Multiple bindings per button are planned for the future.**
|
||||
|
||||
## Bindings for SDL frontend
|
||||
Keyboard bindings in the SDL frontend are currently not customizable in the frontend itself. You should use the Qt frontend to set up your key/controller bindings first.
|
||||
To bind your input device, go to `Settings -> Controller Settings`. Each of the buttons/axes for the simulated controller will be listed, alongside the corresponding key/button on your device that it is currently bound to. To rebind, click the box next to the button/axis name, and press the key or button on your input device that you wish to bind to. When binding rumble, simply press any button on the controller you wish to send rumble to.
|
||||
|
||||
## SDL Game Controller Database
|
||||
DuckStation uses the SDL2 GameController API for input handling which requires controller devices to have known input mappings.
|
||||
SDL2 provides an embedded database of recognised controllers in its own source code, however it is rather small and thus limited in practice.
|
||||
DuckStation releases ship with a database of game controller mappings for the SDL controller backend, courtesy of https://github.com/gabomdq/SDL_GameControllerDB. The included `gamecontrollerdb.txt` file can be found in the `database` subdirectory of the DuckStation program directory.
|
||||
|
||||
There is an officially endorsed [community sourced database](https://github.com/gabomdq/SDL_GameControllerDB) that can be used to support a much broader range of game controllers in DuckStation.
|
||||
If your controller is not recognized by DuckStation but can be found in the community database above, just download a recent copy of the `gamecontrollerdb.txt` database file and place it in your [User directory](#user-directories). Your controller should now be recognized by DuckStation.
|
||||
|
||||
Alternatively, you can also create your own custom controller mappings from scratch easily using readily available tools. See the referenced community database repository for more information.
|
||||
|
||||
Using a mappings database is specially useful when using non-XInput game controllers with DuckStation.
|
||||
If you are experiencing issues binding your controller with the SDL controller backend, you may need to add a custom mapping to the database file. Make a copy of `gamecontrollerdb.txt` and place it in your [user directory](#user-directories) (or directly in the program directory, if running in portable mode) and then follow the instructions in the [SDL_GameControllerDB repository](https://github.com/gabomdq/SDL_GameControllerDB) for creating a new mapping. Add this mapping to the new copy of `gamecontrollerdb.txt` and your controller should then be recognized properly.
|
||||
|
||||
## Default bindings
|
||||
Controller 1:
|
||||
- **D-Pad:** W/A/S/D
|
||||
- **Triangle/Square/Circle/Cross:** Numpad8/Numpad4/Numpad6/Numpad2
|
||||
- **L1/R1:** Q/E
|
||||
- **L2/L2:** 1/3
|
||||
- **L2/R2:** 1/3
|
||||
- **Start:** Enter
|
||||
- **Select:** Backspace
|
||||
|
||||
@@ -269,27 +256,6 @@ Hotkeys:
|
||||
- **Page Up/Down:** Increase/decrease resolution scale in hardware renderers
|
||||
- **End:** Toggle software renderer
|
||||
|
||||
## Libretro Core
|
||||
|
||||
DuckStation is available as a libretro core, which can be loaded into a frontend such as RetroArch. It supports most features of the full frontend, within the constraints and limitations of being a libretro core.
|
||||
|
||||
Prebuilt binaries for 64-bit Windows, Linux and Android can be found on the releases page. Direct links:
|
||||
- 64-bit Windows: https://github.com/stenzek/duckstation/releases/download/latest/duckstation_libretro.dll.zip
|
||||
- 64-bit Linux: https://github.com/stenzek/duckstation/releases/download/latest/duckstation_libretro_x64.so.zip
|
||||
- AArch64 Linux: https://github.com/stenzek/duckstation/releases/download/latest/duckstation_libretro_linux_aarch64.so.zip
|
||||
- AArch64 Android: https://github.com/stenzek/duckstation/releases/download/latest/duckstation_libretro_android_aarch64.so.zip
|
||||
|
||||
To use, download and extract, and install the core file in RetroArch or your preferred frontend.
|
||||
|
||||
To build on Windows, use cmake using the following commands from a `x64 Native Tools Command Prompt for VS 2019`:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_LIBRETRO_CORE=ON ..
|
||||
|
||||
You should then have a file named `duckstation_libretro.dll` which can be loaded as a core.
|
||||
|
||||
To build on Linux, follow the same instructions as for a normal build, but for cmake use `cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_LIBRETRO_CORE=ON ..`. The shared library will be named `duckstation_libretro.so` in the current directory.
|
||||
|
||||
## Tests
|
||||
- Passes amidog's CPU and GTE tests in both interpreter and recompiler modes, partial passing of CPX tests
|
||||
|
||||
@@ -302,9 +268,10 @@ To build on Linux, follow the same instructions as for a normal build, but for c
|
||||
<a href="https://raw.githubusercontent.com/stenzek/duckstation/md-images/croc.jpg"><img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/croc.jpg" alt="Croc" width="400" /></a>
|
||||
<a href="https://raw.githubusercontent.com/stenzek/duckstation/md-images/croc2.jpg"><img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/croc2.jpg" alt="Croc 2" width="400" /></a>
|
||||
<a href="https://raw.githubusercontent.com/stenzek/duckstation/md-images/ff7.jpg"><img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/ff7.jpg" alt="Final Fantasy 7" width="400" /></a>
|
||||
<a href="https://raw.githubusercontent.com/stenzek/duckstation/md-images/ff8.jpg"><img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/ff8.jpg" alt="Final Fantasy 8" width="400" /></a>
|
||||
<a href="https://raw.githubusercontent.com/stenzek/duckstation/md-images/main.png"><img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/main.png" alt="SDL Frontend" width="400" /></a>
|
||||
<a href="https://raw.githubusercontent.com/stenzek/duckstation/md-images/spyro.jpg"><img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/spyro.jpg" alt="Spyro 2" width="400" /></a>
|
||||
<a href="https://raw.githubusercontent.com/stenzek/duckstation/md-images/mm8.jpg"><img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/mm8.jpg" alt="Mega Man 8" width="400" /></a>
|
||||
<a href="https://raw.githubusercontent.com/stenzek/duckstation/md-images/ff8.jpg"><img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/ff8.jpg" alt="Final Fantasy 8 in Fullscreen UI" width="400" /></a>
|
||||
<a href="https://raw.githubusercontent.com/stenzek/duckstation/md-images/spyro.jpg"><img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/spyro.jpg" alt="Spyro in Fullscreen UI" width="400" /></a>
|
||||
<a href="https://raw.githubusercontent.com/stenzek/duckstation/md-images/tof.jpg"><img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/tof.jpg" alt="Threads of Fate in Fullscreen UI" width="400" /></a>
|
||||
<a href="https://raw.githubusercontent.com/stenzek/duckstation/md-images/gamegrid.png"><img src="https://raw.githubusercontent.com/stenzek/duckstation/md-images/gamegrid.png" alt="Game Grid" width="400" /></a>
|
||||
</p>
|
||||
|
||||
|
||||
1
android/.idea/gradle.xml
generated
1
android/.idea/gradle.xml
generated
@@ -14,6 +14,7 @@
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveModulePerSourceSet" value="false" />
|
||||
<option name="useQualifiedModuleNames" value="true" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
|
||||
2
android/.idea/misc.xml
generated
2
android/.idea/misc.xml
generated
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
||||
@@ -5,7 +5,7 @@ android {
|
||||
buildToolsVersion "29.0.2"
|
||||
defaultConfig {
|
||||
applicationId "com.github.stenzek.duckstation"
|
||||
minSdkVersion 21
|
||||
minSdkVersion 23
|
||||
targetSdkVersion 29
|
||||
versionCode(getBuildVersionCode())
|
||||
versionName "${getVersion()}"
|
||||
@@ -30,11 +30,14 @@ android {
|
||||
defaultConfig {
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
arguments "-DCMAKE_BUILD_TYPE=RelWithDebInfo"
|
||||
abiFilters "arm64-v8a"
|
||||
arguments "-DCMAKE_BUILD_TYPE=Release"
|
||||
abiFilters "arm64-v8a", "armeabi-v7a"
|
||||
}
|
||||
}
|
||||
}
|
||||
sourceSets {
|
||||
main.assets.srcDirs += "../../data"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -56,7 +59,7 @@ def getVersion() {
|
||||
def versionNumber = '0.0-unknown'
|
||||
|
||||
try {
|
||||
versionNumber = 'git describe --tags --exclude latest'.execute([], project.rootDir).text
|
||||
versionNumber = 'git describe --tags --exclude latest --exclude preview'.execute([], project.rootDir).text
|
||||
.trim()
|
||||
.replaceAll(/(-0)?-[^-]+$/, "")
|
||||
} catch (Exception e) {
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
set(SRCS
|
||||
android_controller_interface.cpp
|
||||
android_controller_interface.h
|
||||
android_host_interface.cpp
|
||||
android_host_interface.h
|
||||
android_progress_callback.cpp
|
||||
android_progress_callback.h
|
||||
android_settings_interface.cpp
|
||||
android_settings_interface.h
|
||||
)
|
||||
|
||||
186
android/app/src/cpp/android_controller_interface.cpp
Normal file
186
android/app/src/cpp/android_controller_interface.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
#include "android_controller_interface.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "core/controller.h"
|
||||
#include "core/host_interface.h"
|
||||
#include "core/system.h"
|
||||
#include <cmath>
|
||||
Log_SetChannel(AndroidControllerInterface);
|
||||
|
||||
AndroidControllerInterface::AndroidControllerInterface() = default;
|
||||
|
||||
AndroidControllerInterface::~AndroidControllerInterface() = default;
|
||||
|
||||
ControllerInterface::Backend AndroidControllerInterface::GetBackend() const
|
||||
{
|
||||
return ControllerInterface::Backend::Android;
|
||||
}
|
||||
|
||||
bool AndroidControllerInterface::Initialize(CommonHostInterface* host_interface)
|
||||
{
|
||||
if (!ControllerInterface::Initialize(host_interface))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AndroidControllerInterface::Shutdown()
|
||||
{
|
||||
ControllerInterface::Shutdown();
|
||||
}
|
||||
|
||||
void AndroidControllerInterface::PollEvents() {}
|
||||
|
||||
void AndroidControllerInterface::ClearBindings()
|
||||
{
|
||||
for (ControllerData& cd : m_controllers)
|
||||
{
|
||||
cd.axis_mapping.fill({});
|
||||
cd.button_mapping.fill({});
|
||||
cd.axis_button_mapping.fill({});
|
||||
cd.button_axis_mapping.fill({});
|
||||
}
|
||||
}
|
||||
|
||||
bool AndroidControllerInterface::BindControllerAxis(int controller_index, int axis_number, AxisSide axis_side,
|
||||
AxisCallback callback)
|
||||
{
|
||||
if (static_cast<u32>(controller_index) >= m_controllers.size())
|
||||
return false;
|
||||
|
||||
if (axis_number < 0 || axis_number >= NUM_AXISES)
|
||||
return false;
|
||||
|
||||
m_controllers[controller_index].axis_mapping[axis_number][axis_side] = std::move(callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AndroidControllerInterface::BindControllerButton(int controller_index, int button_number, ButtonCallback callback)
|
||||
{
|
||||
if (static_cast<u32>(controller_index) >= m_controllers.size())
|
||||
return false;
|
||||
|
||||
if (button_number < 0 || button_number >= NUM_BUTTONS)
|
||||
return false;
|
||||
|
||||
m_controllers[controller_index].button_mapping[button_number] = std::move(callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AndroidControllerInterface::BindControllerAxisToButton(int controller_index, int axis_number, bool direction,
|
||||
ButtonCallback callback)
|
||||
{
|
||||
if (static_cast<u32>(controller_index) >= m_controllers.size())
|
||||
return false;
|
||||
|
||||
if (axis_number < 0 || axis_number >= NUM_AXISES)
|
||||
return false;
|
||||
|
||||
m_controllers[controller_index].axis_button_mapping[axis_number][BoolToUInt8(direction)] = std::move(callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AndroidControllerInterface::BindControllerHatToButton(int controller_index, int hat_number,
|
||||
std::string_view hat_position, ButtonCallback callback)
|
||||
{
|
||||
// Hats don't exist in XInput
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AndroidControllerInterface::BindControllerButtonToAxis(int controller_index, int button_number,
|
||||
AxisCallback callback)
|
||||
{
|
||||
if (static_cast<u32>(controller_index) >= m_controllers.size())
|
||||
return false;
|
||||
|
||||
if (button_number < 0 || button_number >= NUM_BUTTONS)
|
||||
return false;
|
||||
|
||||
m_controllers[controller_index].button_axis_mapping[button_number] = std::move(callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AndroidControllerInterface::HandleAxisEvent(u32 index, u32 axis, float value)
|
||||
{
|
||||
Log_DevPrintf("controller %u axis %u %f", index, static_cast<u32>(axis), value);
|
||||
DebugAssert(index < NUM_CONTROLLERS);
|
||||
|
||||
if (DoEventHook(Hook::Type::Axis, index, static_cast<u32>(axis), value))
|
||||
return true;
|
||||
|
||||
const AxisCallback& cb = m_controllers[index].axis_mapping[static_cast<u32>(axis)][AxisSide::Full];
|
||||
if (cb)
|
||||
{
|
||||
cb(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// set the other direction to false so large movements don't leave the opposite on
|
||||
const bool outside_deadzone = (std::abs(value) >= m_controllers[index].deadzone);
|
||||
const bool positive = (value >= 0.0f);
|
||||
const ButtonCallback& other_button_cb =
|
||||
m_controllers[index].axis_button_mapping[static_cast<u32>(axis)][BoolToUInt8(!positive)];
|
||||
const ButtonCallback& button_cb =
|
||||
m_controllers[index].axis_button_mapping[static_cast<u32>(axis)][BoolToUInt8(positive)];
|
||||
if (button_cb)
|
||||
{
|
||||
button_cb(outside_deadzone);
|
||||
if (other_button_cb)
|
||||
other_button_cb(false);
|
||||
return true;
|
||||
}
|
||||
else if (other_button_cb)
|
||||
{
|
||||
other_button_cb(false);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool AndroidControllerInterface::HandleButtonEvent(u32 index, u32 button, bool pressed)
|
||||
{
|
||||
Log_DevPrintf("controller %u button %u %s", index, button, pressed ? "pressed" : "released");
|
||||
DebugAssert(index < NUM_CONTROLLERS);
|
||||
|
||||
if (DoEventHook(Hook::Type::Button, index, button, pressed ? 1.0f : 0.0f))
|
||||
return true;
|
||||
|
||||
const ButtonCallback& cb = m_controllers[index].button_mapping[button];
|
||||
if (cb)
|
||||
{
|
||||
cb(pressed);
|
||||
return true;
|
||||
}
|
||||
|
||||
const AxisCallback& axis_cb = m_controllers[index].button_axis_mapping[button];
|
||||
if (axis_cb)
|
||||
{
|
||||
axis_cb(pressed ? 1.0f : -1.0f);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
u32 AndroidControllerInterface::GetControllerRumbleMotorCount(int controller_index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void AndroidControllerInterface::SetControllerRumbleStrength(int controller_index, const float* strengths,
|
||||
u32 num_motors)
|
||||
{
|
||||
}
|
||||
|
||||
bool AndroidControllerInterface::SetControllerDeadzone(int controller_index, float size /* = 0.25f */)
|
||||
{
|
||||
if (static_cast<u32>(controller_index) >= NUM_CONTROLLERS)
|
||||
return false;
|
||||
|
||||
m_controllers[static_cast<u32>(controller_index)].deadzone = std::clamp(std::abs(size), 0.01f, 0.99f);
|
||||
Log_InfoPrintf("Controller %d deadzone size set to %f", controller_index,
|
||||
m_controllers[static_cast<u32>(controller_index)].deadzone);
|
||||
return true;
|
||||
}
|
||||
67
android/app/src/cpp/android_controller_interface.h
Normal file
67
android/app/src/cpp/android_controller_interface.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
#include "frontend-common/controller_interface.h"
|
||||
#include "core/types.h"
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
class AndroidControllerInterface final : public ControllerInterface
|
||||
{
|
||||
public:
|
||||
AndroidControllerInterface();
|
||||
~AndroidControllerInterface() override;
|
||||
|
||||
Backend GetBackend() const override;
|
||||
bool Initialize(CommonHostInterface* host_interface) override;
|
||||
void Shutdown() override;
|
||||
|
||||
// Removes all bindings. Call before setting new bindings.
|
||||
void ClearBindings() override;
|
||||
|
||||
// Binding to events. If a binding for this axis/button already exists, returns false.
|
||||
bool BindControllerAxis(int controller_index, int axis_number, AxisSide axis_side, AxisCallback callback) override;
|
||||
bool BindControllerButton(int controller_index, int button_number, ButtonCallback callback) override;
|
||||
bool BindControllerAxisToButton(int controller_index, int axis_number, bool direction,
|
||||
ButtonCallback callback) override;
|
||||
bool BindControllerHatToButton(int controller_index, int hat_number, std::string_view hat_position,
|
||||
ButtonCallback callback) override;
|
||||
bool BindControllerButtonToAxis(int controller_index, int button_number, AxisCallback callback) override;
|
||||
|
||||
// Changing rumble strength.
|
||||
u32 GetControllerRumbleMotorCount(int controller_index) override;
|
||||
void SetControllerRumbleStrength(int controller_index, const float* strengths, u32 num_motors) override;
|
||||
|
||||
// Set deadzone that will be applied on axis-to-button mappings
|
||||
bool SetControllerDeadzone(int controller_index, float size = 0.25f) override;
|
||||
|
||||
void PollEvents() override;
|
||||
|
||||
bool HandleAxisEvent(u32 index, u32 axis, float value);
|
||||
bool HandleButtonEvent(u32 index, u32 button, bool pressed);
|
||||
|
||||
private:
|
||||
enum : u32
|
||||
{
|
||||
NUM_CONTROLLERS = 1,
|
||||
NUM_AXISES = 12,
|
||||
NUM_BUTTONS = 23
|
||||
};
|
||||
|
||||
struct ControllerData
|
||||
{
|
||||
float deadzone = 0.25f;
|
||||
|
||||
std::array<std::array<AxisCallback, 3>, NUM_AXISES> axis_mapping;
|
||||
std::array<ButtonCallback, NUM_BUTTONS> button_mapping;
|
||||
std::array<std::array<ButtonCallback, 2>, NUM_AXISES> axis_button_mapping;
|
||||
std::array<AxisCallback, NUM_BUTTONS> button_axis_mapping;
|
||||
};
|
||||
|
||||
using ControllerDataArray = std::array<ControllerData, NUM_CONTROLLERS>;
|
||||
|
||||
ControllerDataArray m_controllers;
|
||||
|
||||
std::mutex m_event_intercept_mutex;
|
||||
Hook::Callback m_event_intercept_callback;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
#include "android_settings_interface.h"
|
||||
#include "common/byte_stream.h"
|
||||
#include "common/event.h"
|
||||
#include "common/progress_callback.h"
|
||||
#include "core/host_display.h"
|
||||
#include "frontend-common/common_host_interface.h"
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
@@ -21,11 +24,14 @@ public:
|
||||
AndroidHostInterface(jobject java_object, jobject context_object, std::string user_directory);
|
||||
~AndroidHostInterface() override;
|
||||
|
||||
ALWAYS_INLINE ANativeWindow* GetSurface() const { return m_surface; }
|
||||
|
||||
bool Initialize() override;
|
||||
void Shutdown() override;
|
||||
|
||||
const char* GetFrontendName() const override;
|
||||
void RequestExit() override;
|
||||
void RunLater(std::function<void()> func) override;
|
||||
|
||||
void ReportError(const char* message) override;
|
||||
void ReportMessage(const char* message) override;
|
||||
@@ -34,44 +40,63 @@ public:
|
||||
bool GetBoolSettingValue(const char* section, const char* key, bool default_value = false) override;
|
||||
int GetIntSettingValue(const char* section, const char* key, int default_value = 0) override;
|
||||
float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f) override;
|
||||
std::unique_ptr<ByteStream> OpenPackageFile(const char* path, u32 flags) override;
|
||||
bool GetMainDisplayRefreshRate(float* refresh_rate) override;
|
||||
|
||||
bool IsEmulationThreadRunning() const { return m_emulation_thread.joinable(); }
|
||||
bool IsEmulationThreadRunning() const { return m_emulation_thread_running.load(); }
|
||||
bool IsEmulationThreadPaused() const;
|
||||
bool StartEmulationThread(jobject emulation_activity, ANativeWindow* initial_surface,
|
||||
SystemBootParameters boot_params, bool resume_state);
|
||||
bool IsOnEmulationThread() const;
|
||||
void RunOnEmulationThread(std::function<void()> function, bool blocking = false);
|
||||
void PauseEmulationThread(bool paused);
|
||||
void StopEmulationThread();
|
||||
void StopEmulationThreadLoop();
|
||||
|
||||
void EmulationThreadEntryPoint(JNIEnv* env, jobject emulation_activity, SystemBootParameters boot_params,
|
||||
bool resume_state);
|
||||
|
||||
void SurfaceChanged(ANativeWindow* surface, int format, int width, int height);
|
||||
void SetDisplayAlignment(HostDisplay::Alignment alignment);
|
||||
|
||||
void SetControllerType(u32 index, std::string_view type_name);
|
||||
void SetControllerButtonState(u32 index, s32 button_code, bool pressed);
|
||||
void SetControllerAxisState(u32 index, s32 button_code, float value);
|
||||
void HandleControllerButtonEvent(u32 controller_index, u32 button_index, bool pressed);
|
||||
void HandleControllerAxisEvent(u32 controller_index, u32 axis_index, float value);
|
||||
void SetFastForwardEnabled(bool enabled);
|
||||
|
||||
void RefreshGameList(bool invalidate_cache, bool invalidate_database);
|
||||
void ApplySettings(bool display_osd_messages);
|
||||
void RefreshGameList(bool invalidate_cache, bool invalidate_database, ProgressCallback* progress_callback);
|
||||
void ApplySettings(bool display_osd_messages) override;
|
||||
|
||||
bool ImportPatchCodesFromString(const std::string& str);
|
||||
|
||||
jobjectArray GetInputProfileNames(JNIEnv* env) const;
|
||||
bool ApplyInputProfile(const char* profile_name);
|
||||
bool SaveInputProfile(const char* profile_name);
|
||||
|
||||
protected:
|
||||
void SetUserDirectory() override;
|
||||
void LoadSettings() override;
|
||||
void UpdateInputMap() override;
|
||||
void RegisterHotkeys() override;
|
||||
|
||||
bool AcquireHostDisplay() override;
|
||||
void ReleaseHostDisplay() override;
|
||||
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) override;
|
||||
void UpdateControllerInterface() override;
|
||||
|
||||
void OnSystemPaused(bool paused) override;
|
||||
void OnSystemDestroyed() override;
|
||||
void OnRunningGameChanged() override;
|
||||
|
||||
private:
|
||||
void EmulationThreadEntryPoint(jobject emulation_activity, ANativeWindow* initial_surface,
|
||||
SystemBootParameters boot_params, bool resume_state);
|
||||
void EmulationThreadLoop();
|
||||
void EmulationThreadLoop(JNIEnv* env);
|
||||
|
||||
void CreateImGuiContext();
|
||||
void DestroyImGuiContext();
|
||||
|
||||
void LoadAndConvertSettings();
|
||||
void SetVibration(bool enabled);
|
||||
void UpdateVibration();
|
||||
|
||||
jobject m_java_object = {};
|
||||
jobject m_emulation_activity_object = {};
|
||||
|
||||
@@ -82,13 +107,68 @@ private:
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_sleep_cv;
|
||||
std::deque<std::function<void()>> m_callback_queue;
|
||||
std::atomic_bool m_callbacks_outstanding{false};
|
||||
|
||||
std::thread m_emulation_thread;
|
||||
std::atomic_bool m_emulation_thread_stop_request{false};
|
||||
std::atomic_bool m_emulation_thread_running{false};
|
||||
std::thread::id m_emulation_thread_id{};
|
||||
|
||||
HostDisplay::Alignment m_display_alignment = HostDisplay::Alignment::Center;
|
||||
|
||||
u64 m_last_vibration_update_time = 0;
|
||||
bool m_last_vibration_state = false;
|
||||
bool m_vibration_enabled = false;
|
||||
};
|
||||
|
||||
namespace AndroidHelpers {
|
||||
|
||||
JNIEnv* GetJNIEnv();
|
||||
AndroidHostInterface* GetNativeClass(JNIEnv* env, jobject obj);
|
||||
std::string JStringToString(JNIEnv* env, jstring str);
|
||||
std::unique_ptr<GrowableMemoryByteStream> ReadInputStreamToMemory(JNIEnv* env, jobject obj, u32 chunk_size = 65536);
|
||||
jclass GetStringClass();
|
||||
|
||||
} // namespace AndroidHelpers
|
||||
|
||||
template<typename T>
|
||||
class LocalRefHolder
|
||||
{
|
||||
public:
|
||||
LocalRefHolder() : m_env(nullptr), m_object(nullptr) {}
|
||||
|
||||
LocalRefHolder(JNIEnv* env, T object) : m_env(env), m_object(object) {}
|
||||
|
||||
LocalRefHolder(const LocalRefHolder<T>&) = delete;
|
||||
LocalRefHolder(LocalRefHolder&& move) : m_env(move.m_env), m_object(move.m_object)
|
||||
{
|
||||
move.m_env = nullptr;
|
||||
move.m_object = {};
|
||||
}
|
||||
|
||||
~LocalRefHolder()
|
||||
{
|
||||
if (m_object)
|
||||
m_env->DeleteLocalRef(m_object);
|
||||
}
|
||||
|
||||
operator T() const { return m_object; }
|
||||
T operator*() const { return m_object; }
|
||||
|
||||
LocalRefHolder& operator=(const LocalRefHolder&) = delete;
|
||||
LocalRefHolder& operator=(LocalRefHolder&& move)
|
||||
{
|
||||
if (m_object)
|
||||
m_env->DeleteLocalRef(m_object);
|
||||
m_env = move.m_env;
|
||||
m_object = move.m_object;
|
||||
move.m_env = nullptr;
|
||||
move.m_object = {};
|
||||
return *this;
|
||||
}
|
||||
|
||||
T Get() const { return m_object; }
|
||||
|
||||
private:
|
||||
JNIEnv* m_env;
|
||||
T m_object;
|
||||
};
|
||||
|
||||
113
android/app/src/cpp/android_progress_callback.cpp
Normal file
113
android/app/src/cpp/android_progress_callback.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
#include "android_progress_callback.h"
|
||||
#include "android_host_interface.h"
|
||||
#include "common/log.h"
|
||||
#include "common/assert.h"
|
||||
Log_SetChannel(AndroidProgressCallback);
|
||||
|
||||
AndroidProgressCallback::AndroidProgressCallback(JNIEnv* env, jobject java_object)
|
||||
: m_java_object(java_object)
|
||||
{
|
||||
jclass cls = env->GetObjectClass(java_object);
|
||||
m_set_title_method = env->GetMethodID(cls, "setTitle", "(Ljava/lang/String;)V");
|
||||
m_set_status_text_method = env->GetMethodID(cls, "setStatusText", "(Ljava/lang/String;)V");
|
||||
m_set_progress_range_method = env->GetMethodID(cls, "setProgressRange", "(I)V");
|
||||
m_set_progress_value_method = env->GetMethodID(cls, "setProgressValue", "(I)V");
|
||||
m_modal_error_method = env->GetMethodID(cls, "modalError", "(Ljava/lang/String;)V");
|
||||
m_modal_information_method = env->GetMethodID(cls, "modalInformation", "(Ljava/lang/String;)V");
|
||||
m_modal_confirmation_method = env->GetMethodID(cls, "modalConfirmation", "(Ljava/lang/String;)Z");
|
||||
Assert(m_set_status_text_method && m_set_progress_range_method && m_set_progress_value_method && m_modal_error_method && m_modal_information_method && m_modal_confirmation_method);
|
||||
}
|
||||
|
||||
AndroidProgressCallback::~AndroidProgressCallback() = default;
|
||||
|
||||
bool AndroidProgressCallback::IsCancelled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::SetCancellable(bool cancellable)
|
||||
{
|
||||
if (m_cancellable == cancellable)
|
||||
return;
|
||||
|
||||
BaseProgressCallback::SetCancellable(cancellable);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::SetTitle(const char* title)
|
||||
{
|
||||
Assert(title);
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jstring> text_jstr(env, env->NewStringUTF(title));
|
||||
env->CallVoidMethod(m_java_object, m_set_title_method, text_jstr.Get());
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::SetStatusText(const char* text)
|
||||
{
|
||||
Assert(text);
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jstring> text_jstr(env, env->NewStringUTF(text));
|
||||
env->CallVoidMethod(m_java_object, m_set_status_text_method, text_jstr.Get());
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::SetProgressRange(u32 range)
|
||||
{
|
||||
BaseProgressCallback::SetProgressRange(range);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
env->CallVoidMethod(m_java_object, m_set_progress_range_method, static_cast<jint>(range));
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::SetProgressValue(u32 value)
|
||||
{
|
||||
const u32 old_value = m_progress_value;
|
||||
BaseProgressCallback::SetProgressValue(value);
|
||||
if (old_value == m_progress_value)
|
||||
return;
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
env->CallVoidMethod(m_java_object, m_set_progress_value_method, static_cast<jint>(value));
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::DisplayError(const char* message)
|
||||
{
|
||||
Log_ErrorPrintf("%s", message);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::DisplayWarning(const char* message)
|
||||
{
|
||||
Log_WarningPrintf("%s", message);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::DisplayInformation(const char* message)
|
||||
{
|
||||
Log_InfoPrintf("%s", message);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::DisplayDebugMessage(const char* message)
|
||||
{
|
||||
Log_DevPrintf("%s", message);
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::ModalError(const char* message)
|
||||
{
|
||||
Assert(message);
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jstring> message_jstr(env, env->NewStringUTF(message));
|
||||
env->CallVoidMethod(m_java_object, m_modal_error_method, message_jstr.Get());
|
||||
}
|
||||
|
||||
bool AndroidProgressCallback::ModalConfirmation(const char* message)
|
||||
{
|
||||
Assert(message);
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jstring> message_jstr(env, env->NewStringUTF(message));
|
||||
return env->CallBooleanMethod(m_java_object, m_modal_confirmation_method, message_jstr.Get());
|
||||
}
|
||||
|
||||
void AndroidProgressCallback::ModalInformation(const char* message)
|
||||
{
|
||||
Assert(message);
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jstring> message_jstr(env, env->NewStringUTF(message));
|
||||
env->CallVoidMethod(m_java_object, m_modal_information_method, message_jstr.Get());
|
||||
}
|
||||
38
android/app/src/cpp/android_progress_callback.h
Normal file
38
android/app/src/cpp/android_progress_callback.h
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "common/progress_callback.h"
|
||||
#include <jni.h>
|
||||
|
||||
class AndroidProgressCallback final : public BaseProgressCallback
|
||||
{
|
||||
public:
|
||||
AndroidProgressCallback(JNIEnv* env, jobject java_object);
|
||||
~AndroidProgressCallback();
|
||||
|
||||
bool IsCancelled() const override;
|
||||
|
||||
void SetCancellable(bool cancellable) override;
|
||||
void SetTitle(const char* title) override;
|
||||
void SetStatusText(const char* text) override;
|
||||
void SetProgressRange(u32 range) override;
|
||||
void SetProgressValue(u32 value) override;
|
||||
|
||||
void DisplayError(const char* message) override;
|
||||
void DisplayWarning(const char* message) override;
|
||||
void DisplayInformation(const char* message) override;
|
||||
void DisplayDebugMessage(const char* message) override;
|
||||
|
||||
void ModalError(const char* message) override;
|
||||
bool ModalConfirmation(const char* message) override;
|
||||
void ModalInformation(const char* message) override;
|
||||
|
||||
private:
|
||||
jobject m_java_object;
|
||||
|
||||
jmethodID m_set_title_method;
|
||||
jmethodID m_set_status_text_method;
|
||||
jmethodID m_set_progress_range_method;
|
||||
jmethodID m_set_progress_value_method;
|
||||
jmethodID m_modal_error_method;
|
||||
jmethodID m_modal_confirmation_method;
|
||||
jmethodID m_modal_information_method;
|
||||
};
|
||||
@@ -16,33 +16,76 @@ AndroidSettingsInterface::AndroidSettingsInterface(jobject java_context)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jclass c_preference_manager = env->FindClass("androidx/preference/PreferenceManager");
|
||||
jclass c_preference_editor = env->FindClass("android/content/SharedPreferences$Editor");
|
||||
jclass c_set = env->FindClass("java/util/Set");
|
||||
jclass c_helper = env->FindClass("com/github/stenzek/duckstation/PreferenceHelpers");
|
||||
jmethodID m_get_default_shared_preferences =
|
||||
env->GetStaticMethodID(c_preference_manager, "getDefaultSharedPreferences",
|
||||
"(Landroid/content/Context;)Landroid/content/SharedPreferences;");
|
||||
Assert(c_preference_manager && c_set && m_get_default_shared_preferences);
|
||||
Assert(c_preference_manager && c_preference_editor && c_set && c_helper && m_get_default_shared_preferences);
|
||||
m_set_class = reinterpret_cast<jclass>(env->NewGlobalRef(c_set));
|
||||
m_shared_preferences_editor_class = reinterpret_cast<jclass>(env->NewGlobalRef(c_preference_editor));
|
||||
m_helper_class = reinterpret_cast<jclass>(env->NewGlobalRef(c_helper));
|
||||
Assert(m_set_class && m_shared_preferences_editor_class && m_helper_class);
|
||||
|
||||
m_java_shared_preferences =
|
||||
env->DeleteLocalRef(c_set);
|
||||
env->DeleteLocalRef(c_preference_editor);
|
||||
env->DeleteLocalRef(c_helper);
|
||||
|
||||
jobject shared_preferences =
|
||||
env->CallStaticObjectMethod(c_preference_manager, m_get_default_shared_preferences, java_context);
|
||||
Assert(shared_preferences);
|
||||
m_java_shared_preferences = env->NewGlobalRef(shared_preferences);
|
||||
Assert(m_java_shared_preferences);
|
||||
m_java_shared_preferences = env->NewGlobalRef(m_java_shared_preferences);
|
||||
jclass c_shared_preferences = env->GetObjectClass(m_java_shared_preferences);
|
||||
env->DeleteLocalRef(c_preference_manager);
|
||||
env->DeleteLocalRef(shared_preferences);
|
||||
|
||||
m_get_boolean = env->GetMethodID(c_shared_preferences, "getBoolean", "(Ljava/lang/String;Z)Z");
|
||||
m_get_int = env->GetMethodID(c_shared_preferences, "getInt", "(Ljava/lang/String;I)I");
|
||||
m_get_float = env->GetMethodID(c_shared_preferences, "getFloat", "(Ljava/lang/String;F)F");
|
||||
m_get_string =
|
||||
env->GetMethodID(c_shared_preferences, "getString", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
|
||||
jclass c_shared_preferences = env->GetObjectClass(m_java_shared_preferences);
|
||||
m_shared_preferences_class = reinterpret_cast<jclass>(env->NewGlobalRef(c_shared_preferences));
|
||||
Assert(m_shared_preferences_class);
|
||||
env->DeleteLocalRef(c_shared_preferences);
|
||||
|
||||
m_get_boolean = env->GetMethodID(m_shared_preferences_class, "getBoolean", "(Ljava/lang/String;Z)Z");
|
||||
m_get_int = env->GetMethodID(m_shared_preferences_class, "getInt", "(Ljava/lang/String;I)I");
|
||||
m_get_float = env->GetMethodID(m_shared_preferences_class, "getFloat", "(Ljava/lang/String;F)F");
|
||||
m_get_string = env->GetMethodID(m_shared_preferences_class, "getString",
|
||||
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
|
||||
m_get_string_set =
|
||||
env->GetMethodID(c_shared_preferences, "getStringSet", "(Ljava/lang/String;Ljava/util/Set;)Ljava/util/Set;");
|
||||
m_set_to_array = env->GetMethodID(c_set, "toArray", "()[Ljava/lang/Object;");
|
||||
env->GetMethodID(m_shared_preferences_class, "getStringSet", "(Ljava/lang/String;Ljava/util/Set;)Ljava/util/Set;");
|
||||
m_set_to_array = env->GetMethodID(m_set_class, "toArray", "()[Ljava/lang/Object;");
|
||||
Assert(m_get_boolean && m_get_int && m_get_float && m_get_string && m_get_string_set && m_set_to_array);
|
||||
|
||||
m_edit = env->GetMethodID(m_shared_preferences_class, "edit", "()Landroid/content/SharedPreferences$Editor;");
|
||||
m_edit_set_string = env->GetMethodID(m_shared_preferences_editor_class, "putString", "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;");
|
||||
m_edit_commit = env->GetMethodID(m_shared_preferences_editor_class, "commit", "()Z");
|
||||
m_edit_remove = env->GetMethodID(m_shared_preferences_editor_class, "remove", "(Ljava/lang/String;)Landroid/content/SharedPreferences$Editor;");
|
||||
Assert(m_edit && m_edit_set_string && m_edit_commit && m_edit_remove);
|
||||
|
||||
m_helper_clear_section = env->GetStaticMethodID(m_helper_class, "clearSection", "(Landroid/content/SharedPreferences;Ljava/lang/String;)V");
|
||||
m_helper_add_to_string_list = env->GetStaticMethodID(m_helper_class, "addToStringList", "(Landroid/content/SharedPreferences;Ljava/lang/String;Ljava/lang/String;)Z");
|
||||
m_helper_remove_from_string_list = env->GetStaticMethodID(m_helper_class, "removeFromStringList", "(Landroid/content/SharedPreferences;Ljava/lang/String;Ljava/lang/String;)Z");
|
||||
m_helper_set_string_list = env->GetStaticMethodID(m_helper_class, "setStringList", "(Landroid/content/SharedPreferences;Ljava/lang/String;[Ljava/lang/String;)V");
|
||||
Assert(m_helper_clear_section && m_helper_add_to_string_list && m_helper_remove_from_string_list && m_helper_set_string_list);
|
||||
}
|
||||
|
||||
AndroidSettingsInterface::~AndroidSettingsInterface()
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
if (m_java_shared_preferences)
|
||||
AndroidHelpers::GetJNIEnv()->DeleteGlobalRef(m_java_shared_preferences);
|
||||
env->DeleteGlobalRef(m_java_shared_preferences);
|
||||
if (m_shared_preferences_editor_class)
|
||||
env->DeleteGlobalRef(m_shared_preferences_editor_class);
|
||||
if (m_shared_preferences_class)
|
||||
env->DeleteGlobalRef(m_shared_preferences_class);
|
||||
if (m_set_class)
|
||||
env->DeleteGlobalRef(m_set_class);
|
||||
if (m_helper_class)
|
||||
env->DeleteGlobalRef(m_helper_class);
|
||||
}
|
||||
|
||||
bool AndroidSettingsInterface::Save()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void AndroidSettingsInterface::Clear()
|
||||
@@ -52,25 +95,28 @@ void AndroidSettingsInterface::Clear()
|
||||
|
||||
int AndroidSettingsInterface::GetIntValue(const char* section, const char* key, int default_value /*= 0*/)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
|
||||
// Some of these settings are string lists...
|
||||
jstring string_object = reinterpret_cast<jstring>(
|
||||
env->CallObjectMethod(m_java_shared_preferences, m_get_string, env->NewStringUTF(GetSettingKey(section, key)),
|
||||
env->NewStringUTF(TinyString::FromFormat("%d", default_value))));
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> default_value_string(env, env->NewStringUTF(TinyString::FromFormat("%d", default_value)));
|
||||
LocalRefHolder<jstring> string_object(
|
||||
env, reinterpret_cast<jstring>(env->CallObjectMethod(m_java_shared_preferences, m_get_string, key_string.Get(),
|
||||
default_value_string.Get())));
|
||||
if (env->ExceptionCheck())
|
||||
{
|
||||
env->ExceptionClear();
|
||||
|
||||
// it might actually be an int (e.g. seek bar preference)
|
||||
const int int_value = static_cast<int>(env->CallIntMethod(m_java_shared_preferences, m_get_int,
|
||||
env->NewStringUTF(GetSettingKey(section, key)), default_value));
|
||||
const int int_value =
|
||||
static_cast<int>(env->CallIntMethod(m_java_shared_preferences, m_get_int, key_string.Get(), default_value));
|
||||
if (env->ExceptionCheck())
|
||||
{
|
||||
env->ExceptionClear();
|
||||
Log_DevPrintf("GetIntValue(%s, %s) -> %d (exception)", section, key, default_value);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
Log_DevPrintf("GetIntValue(%s, %s) -> %d (int)", section, key, int_value);
|
||||
return int_value;
|
||||
}
|
||||
|
||||
@@ -79,6 +125,7 @@ int AndroidSettingsInterface::GetIntValue(const char* section, const char* key,
|
||||
|
||||
const char* data = env->GetStringUTFChars(string_object, nullptr);
|
||||
Assert(data != nullptr);
|
||||
Log_DevPrintf("GetIntValue(%s, %s) -> %s", section, key, data);
|
||||
|
||||
std::optional<int> value = StringUtil::FromChars<int>(data);
|
||||
env->ReleaseStringUTFChars(string_object, data);
|
||||
@@ -88,77 +135,213 @@ int AndroidSettingsInterface::GetIntValue(const char* section, const char* key,
|
||||
float AndroidSettingsInterface::GetFloatValue(const char* section, const char* key, float default_value /*= 0.0f*/)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
#if 0
|
||||
return static_cast<float>(env->CallFloatMethod(m_java_shared_preferences, m_get_float,
|
||||
env->NewStringUTF(GetSettingKey(section, key)), default_value));
|
||||
#else
|
||||
// Some of these settings are string lists...
|
||||
jstring string_object = reinterpret_cast<jstring>(
|
||||
env->CallObjectMethod(m_java_shared_preferences, m_get_string, env->NewStringUTF(GetSettingKey(section, key)),
|
||||
env->NewStringUTF(TinyString::FromFormat("%f", default_value))));
|
||||
if (!string_object)
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> default_value_string(env, env->NewStringUTF(TinyString::FromFormat("%f", default_value)));
|
||||
LocalRefHolder<jstring> string_object(
|
||||
env, reinterpret_cast<jstring>(env->CallObjectMethod(m_java_shared_preferences, m_get_string, key_string.Get(),
|
||||
default_value_string.Get())));
|
||||
if (env->ExceptionCheck())
|
||||
{
|
||||
env->ExceptionClear();
|
||||
Log_DevPrintf("GetFloatValue(%s, %s) -> %f (exception)", section, key, default_value);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
if (!string_object)
|
||||
{
|
||||
Log_DevPrintf("GetFloatValue(%s, %s) -> %f (null)", section, key, default_value);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
const char* data = env->GetStringUTFChars(string_object, nullptr);
|
||||
Assert(data != nullptr);
|
||||
Log_DevPrintf("GetFloatValue(%s, %s) -> %s", section, key, data);
|
||||
|
||||
std::optional<float> value = StringUtil::FromChars<float>(data);
|
||||
env->ReleaseStringUTFChars(string_object, data);
|
||||
return value.value_or(default_value);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool AndroidSettingsInterface::GetBoolValue(const char* section, const char* key, bool default_value /*= false*/)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
return static_cast<bool>(env->CallBooleanMethod(m_java_shared_preferences, m_get_boolean,
|
||||
env->NewStringUTF(GetSettingKey(section, key)), default_value));
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
jboolean bool_value = static_cast<bool>(
|
||||
env->CallBooleanMethod(m_java_shared_preferences, m_get_boolean, key_string.Get(), default_value));
|
||||
if (env->ExceptionCheck())
|
||||
{
|
||||
Log_DevPrintf("GetBoolValue(%s, %s) -> %u (exception)", section, key, static_cast<unsigned>(default_value));
|
||||
env->ExceptionClear();
|
||||
return default_value;
|
||||
}
|
||||
|
||||
Log_DevPrintf("GetBoolValue(%s, %s) -> %u", section, key, static_cast<unsigned>(bool_value));
|
||||
return bool_value;
|
||||
}
|
||||
|
||||
std::string AndroidSettingsInterface::GetStringValue(const char* section, const char* key,
|
||||
const char* default_value /*= ""*/)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jobject string_object =
|
||||
env->CallObjectMethod(m_java_shared_preferences, m_get_string, env->NewStringUTF(GetSettingKey(section, key)),
|
||||
env->NewStringUTF(default_value));
|
||||
return AndroidHelpers::JStringToString(env, reinterpret_cast<jstring>(string_object));
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> default_value_string(env, env->NewStringUTF(default_value));
|
||||
LocalRefHolder<jstring> string_object(
|
||||
env, reinterpret_cast<jstring>(env->CallObjectMethod(m_java_shared_preferences, m_get_string, key_string.Get(),
|
||||
default_value_string.Get())));
|
||||
|
||||
if (env->ExceptionCheck())
|
||||
{
|
||||
env->ExceptionClear();
|
||||
Log_DevPrintf("GetStringValue(%s, %s) -> %s (exception)", section, key, default_value);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
if (!string_object)
|
||||
{
|
||||
Log_DevPrintf("GetStringValue(%s, %s) -> %s (null)", section, key, default_value);
|
||||
return default_value;
|
||||
}
|
||||
|
||||
const std::string ret(AndroidHelpers::JStringToString(env, string_object));
|
||||
Log_DevPrintf("GetStringValue(%s, %s) -> %s", section, key, ret.c_str());
|
||||
return ret;
|
||||
}
|
||||
|
||||
jobject AndroidSettingsInterface::GetPreferencesEditor(JNIEnv* env)
|
||||
{
|
||||
return env->CallObjectMethod(m_java_shared_preferences, m_edit);
|
||||
}
|
||||
|
||||
void AndroidSettingsInterface::CheckForException(JNIEnv *env, const char *task)
|
||||
{
|
||||
if (!env->ExceptionCheck())
|
||||
return;
|
||||
|
||||
Log_ErrorPrintf("JNI exception during %s", task);
|
||||
env->ExceptionClear();
|
||||
}
|
||||
|
||||
void AndroidSettingsInterface::SetIntValue(const char* section, const char* key, int value)
|
||||
{
|
||||
Log_ErrorPrintf("SetIntValue(\"%s\", \"%s\", %d) not implemented", section, key, value);
|
||||
Log_DevPrintf("SetIntValue(\"%s\", \"%s\", %d)", section, key, value);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jobject> editor(env, GetPreferencesEditor(env));
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> str_value(env, env->NewStringUTF(TinyString::FromFormat("%d", value)));
|
||||
|
||||
LocalRefHolder<jobject> dummy(env, env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
env->CallBooleanMethod(editor, m_edit_commit);
|
||||
|
||||
CheckForException(env, "SetIntValue");
|
||||
}
|
||||
|
||||
void AndroidSettingsInterface::SetFloatValue(const char* section, const char* key, float value)
|
||||
{
|
||||
Log_ErrorPrintf("SetFloatValue(\"%s\", \"%s\", %f) not implemented", section, key, value);
|
||||
Log_DevPrintf("SetFloatValue(\"%s\", \"%s\", %f)", section, key, value);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jobject> editor(env, GetPreferencesEditor(env));
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> str_value(env, env->NewStringUTF(TinyString::FromFormat("%f", value)));
|
||||
|
||||
LocalRefHolder<jobject> dummy(env, env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
env->CallBooleanMethod(editor, m_edit_commit);
|
||||
|
||||
CheckForException(env, "SetFloatValue");
|
||||
}
|
||||
|
||||
void AndroidSettingsInterface::SetBoolValue(const char* section, const char* key, bool value)
|
||||
{
|
||||
Log_ErrorPrintf("SetBoolValue(\"%s\", \"%s\", %u) not implemented", section, key, static_cast<unsigned>(value));
|
||||
Log_DevPrintf("SetBoolValue(\"%s\", \"%s\", %u)", section, key, static_cast<unsigned>(value));
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jobject> editor(env, GetPreferencesEditor(env));
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> str_value(env, env->NewStringUTF(value ? "true" : "false"));
|
||||
|
||||
LocalRefHolder<jobject> dummy(env, env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
env->CallBooleanMethod(editor, m_edit_commit);
|
||||
|
||||
CheckForException(env, "SetBoolValue");
|
||||
}
|
||||
|
||||
void AndroidSettingsInterface::SetStringValue(const char* section, const char* key, const char* value)
|
||||
{
|
||||
Log_ErrorPrintf("SetStringValue(\"%s\", \"%s\", \"%s\") not implemented", section, key, value);
|
||||
Log_DevPrintf("SetStringValue(\"%s\", \"%s\", \"%s\")", section, key, value);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jobject> editor(env, GetPreferencesEditor(env));
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> str_value(env, env->NewStringUTF(value));
|
||||
|
||||
LocalRefHolder<jobject> dummy(env, env->CallObjectMethod(editor, m_edit_set_string, key_string.Get(), str_value.Get()));
|
||||
env->CallBooleanMethod(editor, m_edit_commit);
|
||||
|
||||
CheckForException(env, "SetStringValue");
|
||||
}
|
||||
|
||||
void AndroidSettingsInterface::DeleteValue(const char* section, const char* key)
|
||||
{
|
||||
Log_ErrorPrintf("DeleteValue(\"%s\", \"%s\") not implemented", section, key);
|
||||
Log_DevPrintf("DeleteValue(\"%s\", \"%s\")", section, key);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jobject> editor(env, GetPreferencesEditor(env));
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jobject> dummy(env, env->CallObjectMethod(editor, m_edit_remove, key_string.Get()));
|
||||
env->CallBooleanMethod(editor, m_edit_commit);
|
||||
|
||||
CheckForException(env, "DeleteValue");
|
||||
}
|
||||
|
||||
void AndroidSettingsInterface::ClearSection(const char* section)
|
||||
{
|
||||
Log_DevPrintf("ClearSection(\"%s\")", section);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jstring> str_section(env, env->NewStringUTF(section));
|
||||
env->CallStaticVoidMethod(m_helper_class, m_helper_clear_section, m_java_shared_preferences, str_section.Get());
|
||||
|
||||
CheckForException(env, "ClearSection");
|
||||
}
|
||||
|
||||
std::vector<std::string> AndroidSettingsInterface::GetStringList(const char* section, const char* key)
|
||||
{
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
jobject values_set = env->CallObjectMethod(m_java_shared_preferences, m_get_string_set,
|
||||
env->NewStringUTF(GetSettingKey(section, key)), nullptr);
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jobject> values_set(
|
||||
env, env->CallObjectMethod(m_java_shared_preferences, m_get_string_set, key_string.Get(), nullptr));
|
||||
if (env->ExceptionCheck())
|
||||
{
|
||||
env->ExceptionClear();
|
||||
|
||||
// this might just be a string, not a string set
|
||||
LocalRefHolder<jstring> string_object(
|
||||
env, reinterpret_cast<jstring>(env->CallObjectMethod(m_java_shared_preferences, m_get_string, key_string.Get(), nullptr)));
|
||||
|
||||
if (!env->ExceptionCheck()) {
|
||||
std::vector<std::string> ret;
|
||||
if (string_object)
|
||||
ret.push_back(AndroidHelpers::JStringToString(env, string_object));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
env->ExceptionClear();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!values_set)
|
||||
return {};
|
||||
|
||||
jobjectArray values_array = reinterpret_cast<jobjectArray>(env->CallObjectMethod(values_set, m_set_to_array));
|
||||
LocalRefHolder<jobjectArray> values_array(
|
||||
env, reinterpret_cast<jobjectArray>(env->CallObjectMethod(values_set, m_set_to_array)));
|
||||
if (env->ExceptionCheck())
|
||||
{
|
||||
env->ExceptionClear();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!values_array)
|
||||
return {};
|
||||
|
||||
@@ -166,8 +349,11 @@ std::vector<std::string> AndroidSettingsInterface::GetStringList(const char* sec
|
||||
std::vector<std::string> values;
|
||||
values.reserve(size);
|
||||
for (jsize i = 0; i < size; i++)
|
||||
values.push_back(
|
||||
AndroidHelpers::JStringToString(env, reinterpret_cast<jstring>(env->GetObjectArrayElement(values_array, i))));
|
||||
{
|
||||
jstring str = reinterpret_cast<jstring>(env->GetObjectArrayElement(values_array, i));
|
||||
values.push_back(AndroidHelpers::JStringToString(env, str));
|
||||
env->DeleteLocalRef(str);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
@@ -175,17 +361,47 @@ std::vector<std::string> AndroidSettingsInterface::GetStringList(const char* sec
|
||||
void AndroidSettingsInterface::SetStringList(const char* section, const char* key,
|
||||
const std::vector<std::string>& items)
|
||||
{
|
||||
Log_ErrorPrintf("SetStringList(\"%s\", \"%s\") not implemented", section, key);
|
||||
Log_DevPrintf("SetStringList(\"%s\", \"%s\")", section, key);
|
||||
if (items.empty())
|
||||
{
|
||||
DeleteValue(section, key);
|
||||
return;
|
||||
}
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jobjectArray> items_array(env, env->NewObjectArray(static_cast<jsize>(items.size()), AndroidHelpers::GetStringClass(), nullptr));
|
||||
for (size_t i = 0; i < items.size(); i++)
|
||||
{
|
||||
LocalRefHolder<jstring> item_jstr(env, env->NewStringUTF(items[i].c_str()));
|
||||
env->SetObjectArrayElement(items_array, static_cast<jsize>(i), item_jstr);
|
||||
}
|
||||
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
env->CallStaticVoidMethod(m_helper_class, m_helper_set_string_list, m_java_shared_preferences, key_string.Get(), items_array.Get());
|
||||
|
||||
CheckForException(env, "SetStringList");
|
||||
}
|
||||
|
||||
bool AndroidSettingsInterface::RemoveFromStringList(const char* section, const char* key, const char* item)
|
||||
{
|
||||
Log_ErrorPrintf("RemoveFromStringList(\"%s\", \"%s\", \"%s\") not implemented", section, key, item);
|
||||
return false;
|
||||
Log_DevPrintf("RemoveFromStringList(\"%s\", \"%s\", \"%s\")", section, key, item);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> item_string(env, env->NewStringUTF(item));
|
||||
const bool result = env->CallStaticBooleanMethod(m_helper_class, m_helper_remove_from_string_list, m_java_shared_preferences, key_string.Get(), item_string.Get());
|
||||
CheckForException(env, "RemoveFromStringList");
|
||||
return result;
|
||||
}
|
||||
|
||||
bool AndroidSettingsInterface::AddToStringList(const char* section, const char* key, const char* item)
|
||||
{
|
||||
Log_ErrorPrintf("AddToStringList(\"%s\", \"%s\", \"%s\") not implemented", section, key, item);
|
||||
return false;
|
||||
Log_DevPrintf("AddToStringList(\"%s\", \"%s\", \"%s\")", section, key, item);
|
||||
|
||||
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||
LocalRefHolder<jstring> key_string(env, env->NewStringUTF(GetSettingKey(section, key)));
|
||||
LocalRefHolder<jstring> item_string(env, env->NewStringUTF(item));
|
||||
const bool result = env->CallStaticBooleanMethod(m_helper_class, m_helper_add_to_string_list, m_java_shared_preferences, key_string.Get(), item_string.Get());
|
||||
CheckForException(env, "AddToStringList");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ public:
|
||||
AndroidSettingsInterface(jobject java_context);
|
||||
~AndroidSettingsInterface();
|
||||
|
||||
bool Save() override;
|
||||
void Clear() override;
|
||||
|
||||
int GetIntValue(const char* section, const char* key, int default_value = 0) override;
|
||||
@@ -20,6 +21,7 @@ public:
|
||||
void SetBoolValue(const char* section, const char* key, bool value) override;
|
||||
void SetStringValue(const char* section, const char* key, const char* value) override;
|
||||
void DeleteValue(const char* section, const char* key) override;
|
||||
void ClearSection(const char* section) override;
|
||||
|
||||
std::vector<std::string> GetStringList(const char* section, const char* key) override;
|
||||
void SetStringList(const char* section, const char* key, const std::vector<std::string>& items) override;
|
||||
@@ -27,11 +29,26 @@ public:
|
||||
bool AddToStringList(const char* section, const char* key, const char* item) override;
|
||||
|
||||
private:
|
||||
jobject GetPreferencesEditor(JNIEnv* env);
|
||||
void CheckForException(JNIEnv* env, const char* task);
|
||||
|
||||
jclass m_set_class{};
|
||||
jclass m_shared_preferences_class{};
|
||||
jclass m_shared_preferences_editor_class{};
|
||||
jclass m_helper_class{};
|
||||
jobject m_java_shared_preferences{};
|
||||
jmethodID m_get_boolean{};
|
||||
jmethodID m_get_int{};
|
||||
jmethodID m_get_float{};
|
||||
jmethodID m_get_string{};
|
||||
jmethodID m_get_string_set{};
|
||||
jmethodID m_edit{};
|
||||
jmethodID m_edit_set_string{};
|
||||
jmethodID m_edit_commit{};
|
||||
jmethodID m_edit_remove{};
|
||||
jmethodID m_set_to_array{};
|
||||
jmethodID m_helper_clear_section{};
|
||||
jmethodID m_helper_add_to_string_list{};
|
||||
jmethodID m_helper_remove_from_string_list{};
|
||||
jmethodID m_helper_set_string_list{};
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
package="com.github.stenzek.duckstation">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
|
||||
<application
|
||||
@@ -13,13 +14,18 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".GameDirectoriesActivity"
|
||||
android:label="@string/title_activity_game_directories"
|
||||
android:theme="@style/AppTheme.NoActionBar"></activity>
|
||||
<activity
|
||||
android:name=".EmulationActivity"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:immersive="true"
|
||||
android:label="@string/title_activity_emulation"
|
||||
android:parentActivityName=".MainActivity"
|
||||
android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"
|
||||
android:immersive="true">
|
||||
android:exported="true">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.github.stenzek.duckstation.MainActivity" />
|
||||
@@ -33,6 +39,24 @@
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.github.stenzek.duckstation.MainActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ControllerMappingActivity"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:label="@string/title_activity_settings"
|
||||
android:parentActivityName=".MainActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.github.stenzek.duckstation.MainActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".GamePropertiesActivity"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
android:label="@string/activity_game_properties"
|
||||
android:parentActivityName=".MainActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="com.github.stenzek.duckstation.MainActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:configChanges="orientation|keyboardHidden|screenSize"
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Environment;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Locale;
|
||||
|
||||
public class AndroidHostInterface {
|
||||
public final static int DISPLAY_ALIGNMENT_TOP_OR_LEFT = 0;
|
||||
@@ -16,8 +21,6 @@ public class AndroidHostInterface {
|
||||
private long mNativePointer;
|
||||
private Context mContext;
|
||||
|
||||
static public native AndroidHostInterface create(Context context, String userDirectory);
|
||||
|
||||
public AndroidHostInterface(Context context) {
|
||||
this.mContext = context;
|
||||
}
|
||||
@@ -30,15 +33,35 @@ public class AndroidHostInterface {
|
||||
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public InputStream openAssetStream(String path) {
|
||||
try {
|
||||
return mContext.getAssets().open(path, AssetManager.ACCESS_STREAMING);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setContext(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
static public native String getScmVersion();
|
||||
|
||||
static public native String getFullScmVersion();
|
||||
|
||||
static public native boolean setThreadAffinity(int[] cpus);
|
||||
|
||||
static public native AndroidHostInterface create(Context context, String userDirectory);
|
||||
|
||||
public native boolean isEmulationThreadRunning();
|
||||
|
||||
public native boolean startEmulationThread(EmulationActivity emulationActivity, Surface surface, String filename, boolean resumeState, String state_filename);
|
||||
public native boolean runEmulationThread(EmulationActivity emulationActivity, String filename, boolean resumeState, String state_filename);
|
||||
|
||||
public native boolean isEmulationThreadPaused();
|
||||
|
||||
public native void pauseEmulationThread(boolean paused);
|
||||
|
||||
public native void stopEmulationThread();
|
||||
public native void stopEmulationThreadLoop();
|
||||
|
||||
public native boolean hasSurface();
|
||||
|
||||
@@ -55,10 +78,32 @@ public class AndroidHostInterface {
|
||||
|
||||
public static native int getControllerAxisCode(String controllerType, String axisName);
|
||||
|
||||
public native void refreshGameList(boolean invalidateCache, boolean invalidateDatabase);
|
||||
public static native String[] getControllerButtonNames(String controllerType);
|
||||
|
||||
public static native String[] getControllerAxisNames(String controllerType);
|
||||
|
||||
public native void handleControllerButtonEvent(int controllerIndex, int buttonIndex, boolean pressed);
|
||||
|
||||
public native void handleControllerAxisEvent(int controllerIndex, int axisIndex, float value);
|
||||
|
||||
public native String[] getInputProfileNames();
|
||||
|
||||
public native boolean loadInputProfile(String name);
|
||||
|
||||
public native boolean saveInputProfile(String name);
|
||||
|
||||
public native HotkeyInfo[] getHotkeyInfoList();
|
||||
|
||||
public native void refreshGameList(boolean invalidateCache, boolean invalidateDatabase, AndroidProgressCallback progressCallback);
|
||||
|
||||
public native GameListEntry[] getGameListEntries();
|
||||
|
||||
public native GameListEntry getGameListEntry(String path);
|
||||
|
||||
public native String getGameSettingValue(String path, String key);
|
||||
|
||||
public native void setGameSettingValue(String path, String key, String value);
|
||||
|
||||
public native void resetSystem();
|
||||
|
||||
public native void loadState(boolean global, int slot);
|
||||
@@ -71,14 +116,32 @@ public class AndroidHostInterface {
|
||||
|
||||
public native void setDisplayAlignment(int alignment);
|
||||
|
||||
public native CheatCode[] getCheatList();
|
||||
public native void setCheatEnabled(int index, boolean enabled);
|
||||
public native PatchCode[] getPatchCodeList();
|
||||
|
||||
public native void setPatchCodeEnabled(int index, boolean enabled);
|
||||
|
||||
public native boolean importPatchCodesFromString(String str);
|
||||
|
||||
public native void addOSDMessage(String message, float duration);
|
||||
|
||||
public native boolean hasAnyBIOSImages();
|
||||
|
||||
public native String importBIOSImage(byte[] data);
|
||||
|
||||
public native boolean isFastForwardEnabled();
|
||||
|
||||
public native void setFastForwardEnabled(boolean enabled);
|
||||
|
||||
public native String[] getMediaPlaylistPaths();
|
||||
|
||||
public native int getMediaPlaylistIndex();
|
||||
|
||||
public native boolean setMediaPlaylistIndex(int index);
|
||||
|
||||
public native boolean setMediaFilename(String filename);
|
||||
|
||||
public native SaveStateInfo[] getSaveStateInfo(boolean includeEmpty);
|
||||
|
||||
static {
|
||||
System.loadLibrary("duckstation-native");
|
||||
}
|
||||
@@ -104,4 +167,8 @@ public class AndroidHostInterface {
|
||||
static public AndroidHostInterface getInstance() {
|
||||
return mInstance;
|
||||
}
|
||||
|
||||
static public boolean hasInstanceAndEmulationThreadIsRunning() {
|
||||
return hasInstance() && getInstance().isEmulationThreadRunning();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
public class AndroidProgressCallback {
|
||||
private Activity mContext;
|
||||
private ProgressDialog mDialog;
|
||||
|
||||
public AndroidProgressCallback(Activity context) {
|
||||
mContext = context;
|
||||
mDialog = new ProgressDialog(context);
|
||||
mDialog.setMessage(context.getString(R.string.android_progress_callback_please_wait));
|
||||
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
|
||||
mDialog.setIndeterminate(false);
|
||||
mDialog.setMax(100);
|
||||
mDialog.setProgress(0);
|
||||
mDialog.show();
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
mDialog.dismiss();
|
||||
}
|
||||
|
||||
public void setTitle(String text) {
|
||||
mContext.runOnUiThread(() -> {
|
||||
mDialog.setTitle(text);
|
||||
});
|
||||
}
|
||||
|
||||
public void setStatusText(String text) {
|
||||
mContext.runOnUiThread(() -> {
|
||||
mDialog.setMessage(text);
|
||||
});
|
||||
}
|
||||
|
||||
public void setProgressRange(int range) {
|
||||
mContext.runOnUiThread(() -> {
|
||||
mDialog.setMax(range);
|
||||
});
|
||||
}
|
||||
|
||||
public void setProgressValue(int value) {
|
||||
mContext.runOnUiThread(() -> {
|
||||
mDialog.setProgress(value);
|
||||
});
|
||||
}
|
||||
|
||||
public void modalError(String message) {
|
||||
Object lock = new Object();
|
||||
mContext.runOnUiThread(() -> {
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setTitle("Error")
|
||||
.setMessage(message)
|
||||
.setPositiveButton(mContext.getString(R.string.android_progress_callback_ok), (dialog, button) -> {
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setOnDismissListener((dialogInterface) -> {
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
});
|
||||
|
||||
synchronized (lock) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void modalInformation(String message) {
|
||||
Object lock = new Object();
|
||||
mContext.runOnUiThread(() -> {
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setTitle(mContext.getString(R.string.android_progress_callback_information))
|
||||
.setMessage(message)
|
||||
.setPositiveButton(mContext.getString(R.string.android_progress_callback_ok), (dialog, button) -> {
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setOnDismissListener((dialogInterface) -> {
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
});
|
||||
|
||||
synchronized (lock) {
|
||||
try {
|
||||
lock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ConfirmationResult {
|
||||
public boolean result = false;
|
||||
}
|
||||
|
||||
public boolean modalConfirmation(String message) {
|
||||
ConfirmationResult result = new ConfirmationResult();
|
||||
mContext.runOnUiThread(() -> {
|
||||
new AlertDialog.Builder(mContext)
|
||||
.setTitle(mContext.getString(R.string.android_progress_callback_confirmation))
|
||||
.setMessage(message)
|
||||
.setPositiveButton(mContext.getString(R.string.android_progress_callback_yes), (dialog, button) -> {
|
||||
result.result = true;
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton(mContext.getString(R.string.android_progress_callback_no), (dialog, button) -> {
|
||||
result.result = false;
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setOnDismissListener((dialogInterface) -> {
|
||||
synchronized (result) {
|
||||
result.notify();
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
});
|
||||
|
||||
synchronized (result) {
|
||||
try {
|
||||
result.wait();
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
return result.result;
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
public class CheatCode {
|
||||
private int mIndex;
|
||||
private String mName;
|
||||
private boolean mEnabled;
|
||||
|
||||
public CheatCode(int index, String name, boolean enabled) {
|
||||
mIndex = index;
|
||||
mName = name;
|
||||
mEnabled = enabled;
|
||||
}
|
||||
|
||||
public int getIndex() { return mIndex; }
|
||||
public String getName() { return mName; }
|
||||
public boolean isEnabled() { return mEnabled; }
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class ControllerBindingDialog extends AlertDialog {
|
||||
private boolean mIsAxis;
|
||||
private String mSettingKey;
|
||||
private String mCurrentBinding;
|
||||
|
||||
public ControllerBindingDialog(Context context, String buttonName, String settingKey, String currentBinding, boolean isAxis) {
|
||||
super(context);
|
||||
|
||||
mIsAxis = isAxis;
|
||||
mSettingKey = settingKey;
|
||||
mCurrentBinding = currentBinding;
|
||||
if (mCurrentBinding == null)
|
||||
mCurrentBinding = getContext().getString(R.string.controller_binding_dialog_no_binding);
|
||||
|
||||
setTitle(buttonName);
|
||||
updateMessage();
|
||||
setButton(BUTTON_POSITIVE, context.getString(R.string.controller_binding_dialog_cancel), (dialogInterface, button) -> dismiss());
|
||||
setButton(BUTTON_NEGATIVE, context.getString(R.string.controller_binding_dialog_clear), (dialogInterface, button) -> {
|
||||
mCurrentBinding = null;
|
||||
updateBinding();
|
||||
dismiss();
|
||||
});
|
||||
|
||||
setOnKeyListener(new DialogInterface.OnKeyListener() {
|
||||
@Override
|
||||
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
|
||||
if (onKeyDown(keyCode, event))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateMessage() {
|
||||
setMessage(String.format(getContext().getString(R.string.controller_binding_dialog_message), mCurrentBinding));
|
||||
}
|
||||
|
||||
private void updateBinding() {
|
||||
SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(getContext()).edit();
|
||||
if (mCurrentBinding != null) {
|
||||
ArraySet<String> values = new ArraySet<>();
|
||||
values.add(mCurrentBinding);
|
||||
editor.putStringSet(mSettingKey, values);
|
||||
} else {
|
||||
try {
|
||||
editor.remove(mSettingKey);
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (mIsAxis || !EmulationSurfaceView.isDPadOrButtonEvent(event))
|
||||
return super.onKeyUp(keyCode, event);
|
||||
|
||||
int buttonIndex = EmulationSurfaceView.getButtonIndexForKeyCode(keyCode);
|
||||
if (buttonIndex < 0)
|
||||
return super.onKeyUp(keyCode, event);
|
||||
|
||||
// TODO: Multiple controllers
|
||||
final int controllerIndex = 0;
|
||||
mCurrentBinding = String.format("Controller%d/Button%d", controllerIndex, buttonIndex);
|
||||
updateMessage();
|
||||
updateBinding();
|
||||
dismiss();
|
||||
return true;
|
||||
}
|
||||
|
||||
private int mUpdatedAxisCode = -1;
|
||||
|
||||
private void setAxisCode(int axisCode, boolean positive) {
|
||||
final int axisIndex = EmulationSurfaceView.getAxisIndexForAxisCode(axisCode);
|
||||
if (mUpdatedAxisCode >= 0 || axisIndex < 0)
|
||||
return;
|
||||
|
||||
mUpdatedAxisCode = axisCode;
|
||||
|
||||
final int controllerIndex = 0;
|
||||
if (mIsAxis)
|
||||
mCurrentBinding = String.format("Controller%d/Axis%d", controllerIndex, axisIndex);
|
||||
else
|
||||
mCurrentBinding = String.format("Controller%d/%cAxis%d", controllerIndex, (positive) ? '+' : '-', axisIndex);
|
||||
|
||||
updateBinding();
|
||||
updateMessage();
|
||||
dismiss();
|
||||
}
|
||||
|
||||
final static float DETECT_THRESHOLD = 0.25f;
|
||||
|
||||
private HashMap<Integer, float[]> mStartingAxisValues = new HashMap<>();
|
||||
|
||||
private boolean doAxisDetection(MotionEvent event) {
|
||||
if ((event.getSource() & (InputDevice.SOURCE_JOYSTICK | InputDevice.SOURCE_GAMEPAD | InputDevice.SOURCE_DPAD)) == 0)
|
||||
return false;
|
||||
|
||||
final int[] axisCodes = EmulationSurfaceView.getKnownAxisCodes();
|
||||
final int deviceId = event.getDeviceId();
|
||||
|
||||
if (!mStartingAxisValues.containsKey(deviceId)) {
|
||||
final float[] axisValues = new float[axisCodes.length];
|
||||
for (int axisIndex = 0; axisIndex < axisCodes.length; axisIndex++) {
|
||||
final int axisCode = axisCodes[axisIndex];
|
||||
|
||||
// these are binary, so start at zero
|
||||
if (axisCode == MotionEvent.AXIS_HAT_X || axisCode == MotionEvent.AXIS_HAT_Y)
|
||||
axisValues[axisIndex] = 0.0f;
|
||||
else
|
||||
axisValues[axisIndex] = event.getAxisValue(axisCode);
|
||||
}
|
||||
|
||||
mStartingAxisValues.put(deviceId, axisValues);
|
||||
}
|
||||
|
||||
final float[] axisValues = mStartingAxisValues.get(deviceId);
|
||||
for (int axisIndex = 0; axisIndex < axisCodes.length; axisIndex++) {
|
||||
final float newValue = event.getAxisValue(axisCodes[axisIndex]);
|
||||
if (Math.abs(newValue - axisValues[axisIndex]) >= DETECT_THRESHOLD) {
|
||||
setAxisCode(axisCodes[axisIndex], newValue >= 0.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(@NonNull MotionEvent event) {
|
||||
if (doAxisDetection(event))
|
||||
return true;
|
||||
|
||||
return super.onGenericMotionEvent(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.Preference;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceViewHolder;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class ControllerBindingPreference extends Preference {
|
||||
private enum Type {
|
||||
BUTTON,
|
||||
AXIS,
|
||||
HOTKEY
|
||||
}
|
||||
|
||||
private String mBindingName;
|
||||
private String mValue;
|
||||
private TextView mValueView;
|
||||
private Type mType = Type.BUTTON;
|
||||
|
||||
private static int getIconForButton(String buttonName) {
|
||||
if (buttonName.equals("Up")) {
|
||||
return R.drawable.ic_controller_up_button_pressed;
|
||||
} else if (buttonName.equals("Right")) {
|
||||
return R.drawable.ic_controller_right_button_pressed;
|
||||
} else if (buttonName.equals("Down")) {
|
||||
return R.drawable.ic_controller_down_button_pressed;
|
||||
} else if (buttonName.equals("Left")) {
|
||||
return R.drawable.ic_controller_left_button_pressed;
|
||||
} else if (buttonName.equals("Triangle")) {
|
||||
return R.drawable.ic_controller_triangle_button_pressed;
|
||||
} else if (buttonName.equals("Circle")) {
|
||||
return R.drawable.ic_controller_circle_button_pressed;
|
||||
} else if (buttonName.equals("Cross")) {
|
||||
return R.drawable.ic_controller_cross_button_pressed;
|
||||
} else if (buttonName.equals("Square")) {
|
||||
return R.drawable.ic_controller_square_button_pressed;
|
||||
} else if (buttonName.equals("Start")) {
|
||||
return R.drawable.ic_controller_start_button_pressed;
|
||||
} else if (buttonName.equals("Select")) {
|
||||
return R.drawable.ic_controller_select_button_pressed;
|
||||
} else if (buttonName.equals("L1")) {
|
||||
return R.drawable.ic_controller_l1_button_pressed;
|
||||
} else if (buttonName.equals("L2")) {
|
||||
return R.drawable.ic_controller_l2_button_pressed;
|
||||
} else if (buttonName.equals("R1")) {
|
||||
return R.drawable.ic_controller_r1_button_pressed;
|
||||
} else if (buttonName.equals("R2")) {
|
||||
return R.drawable.ic_controller_r2_button_pressed;
|
||||
}
|
||||
|
||||
return R.drawable.ic_baseline_radio_button_unchecked_24;
|
||||
}
|
||||
|
||||
private static int getIconForAxis(String axisName) {
|
||||
return R.drawable.ic_baseline_radio_button_checked_24;
|
||||
}
|
||||
|
||||
private static int getIconForHotkey(String hotkeyDisplayName) {
|
||||
return R.drawable.ic_baseline_category_24;
|
||||
}
|
||||
|
||||
public ControllerBindingPreference(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
setWidgetLayoutResource(R.layout.layout_controller_binding_preference);
|
||||
setIconSpaceReserved(false);
|
||||
}
|
||||
|
||||
public ControllerBindingPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setWidgetLayoutResource(R.layout.layout_controller_binding_preference);
|
||||
setIconSpaceReserved(false);
|
||||
}
|
||||
|
||||
public ControllerBindingPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
setWidgetLayoutResource(R.layout.layout_controller_binding_preference);
|
||||
setIconSpaceReserved(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(PreferenceViewHolder holder) {
|
||||
super.onBindViewHolder(holder);
|
||||
|
||||
ImageView iconView = ((ImageView) holder.findViewById(R.id.controller_binding_icon));
|
||||
TextView nameView = ((TextView) holder.findViewById(R.id.controller_binding_name));
|
||||
mValueView = ((TextView) holder.findViewById(R.id.controller_binding_value));
|
||||
|
||||
int drawableId = R.drawable.ic_baseline_radio_button_checked_24;
|
||||
switch (mType) {
|
||||
case BUTTON:
|
||||
drawableId = getIconForButton(mBindingName);
|
||||
break;
|
||||
case AXIS:
|
||||
drawableId = getIconForAxis(mBindingName);
|
||||
break;
|
||||
case HOTKEY:
|
||||
drawableId = getIconForHotkey(mBindingName);
|
||||
break;
|
||||
}
|
||||
|
||||
iconView.setImageDrawable(ContextCompat.getDrawable(getContext(), drawableId));
|
||||
nameView.setText(mBindingName);
|
||||
updateValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
ControllerBindingDialog dialog = new ControllerBindingDialog(getContext(), mBindingName, getKey(), mValue, (mType == Type.AXIS));
|
||||
dialog.setOnDismissListener((dismissedDialog) -> updateValue());
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public void initButton(int controllerIndex, String buttonName) {
|
||||
mBindingName = buttonName;
|
||||
mType = Type.BUTTON;
|
||||
setKey(String.format("Controller%d/Button%s", controllerIndex, buttonName));
|
||||
updateValue();
|
||||
}
|
||||
|
||||
public void initAxis(int controllerIndex, String axisName) {
|
||||
mBindingName = axisName;
|
||||
mType = Type.AXIS;
|
||||
setKey(String.format("Controller%d/Axis%s", controllerIndex, axisName));
|
||||
updateValue();
|
||||
}
|
||||
|
||||
public void initHotkey(HotkeyInfo hotkeyInfo) {
|
||||
mBindingName = hotkeyInfo.getDisplayName();
|
||||
mType = Type.HOTKEY;
|
||||
setKey(hotkeyInfo.getBindingConfigKey());
|
||||
updateValue();
|
||||
}
|
||||
|
||||
private void updateValue(String value) {
|
||||
mValue = value;
|
||||
if (mValueView != null) {
|
||||
if (value != null)
|
||||
mValueView.setText(value);
|
||||
else
|
||||
mValueView.setText(getContext().getString(R.string.controller_binding_dialog_no_binding));
|
||||
}
|
||||
}
|
||||
|
||||
public void updateValue() {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
Set<String> values = PreferenceHelpers.getStringSet(prefs, getKey());
|
||||
if (values != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String value : values) {
|
||||
if (sb.length() > 0)
|
||||
sb.append(", ");
|
||||
sb.append(value);
|
||||
}
|
||||
|
||||
updateValue(sb.toString());
|
||||
} else {
|
||||
updateValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearBinding(SharedPreferences.Editor prefEditor) {
|
||||
try {
|
||||
prefEditor.remove(getKey());
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
updateValue(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class ControllerMappingActivity extends AppCompatActivity {
|
||||
|
||||
private static final int NUM_CONTROLLER_PORTS = 2;
|
||||
|
||||
private ArrayList<ControllerBindingPreference> mPreferences = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.settings_activity);
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings, new SettingsCollectionFragment(this))
|
||||
.commit();
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
actionBar.setTitle(R.string.controller_mapping_activity_title);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.menu_controller_mapping, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
final int id = item.getItemId();
|
||||
|
||||
//noinspection SimplifiableIfStatement
|
||||
if (id == R.id.action_load_profile) {
|
||||
doLoadProfile();
|
||||
return true;
|
||||
} else if (id == R.id.action_save_profile) {
|
||||
doSaveProfile();
|
||||
return true;
|
||||
} else if (id == R.id.action_clear_bindings) {
|
||||
doClearBindings();
|
||||
return true;
|
||||
} else {
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
private void displayError(String text) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.emulation_activity_error)
|
||||
.setMessage(text)
|
||||
.setNegativeButton(R.string.main_activity_ok, ((dialog, which) -> dialog.dismiss()))
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
private void doLoadProfile() {
|
||||
final String[] profileNames = AndroidHostInterface.getInstance().getInputProfileNames();
|
||||
if (profileNames == null) {
|
||||
displayError(getString(R.string.controller_mapping_activity_no_profiles_found));
|
||||
return;
|
||||
}
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.controller_mapping_activity_select_input_profile)
|
||||
.setItems(profileNames, (dialog, choice) -> {
|
||||
doLoadProfile(profileNames[choice]);
|
||||
dialog.dismiss();
|
||||
})
|
||||
.setNegativeButton("Cancel", ((dialog, which) -> dialog.dismiss()))
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
private void doLoadProfile(String profileName) {
|
||||
if (!AndroidHostInterface.getInstance().loadInputProfile(profileName)) {
|
||||
displayError(String.format(getString(R.string.controller_mapping_activity_failed_to_load_profile), profileName));
|
||||
return;
|
||||
}
|
||||
|
||||
updateAllBindings();
|
||||
}
|
||||
|
||||
private void doSaveProfile() {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
final EditText input = new EditText(this);
|
||||
builder.setTitle(R.string.controller_mapping_activity_input_profile_name);
|
||||
builder.setView(input);
|
||||
builder.setPositiveButton(R.string.controller_mapping_activity_save, (dialog, which) -> {
|
||||
final String name = input.getText().toString();
|
||||
if (name.isEmpty()) {
|
||||
displayError(getString(R.string.controller_mapping_activity_name_must_be_provided));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!AndroidHostInterface.getInstance().saveInputProfile(name)) {
|
||||
displayError(getString(R.string.controller_mapping_activity_failed_to_save_input_profile));
|
||||
return;
|
||||
}
|
||||
|
||||
Toast.makeText(ControllerMappingActivity.this, String.format(ControllerMappingActivity.this.getString(R.string.controller_mapping_activity_input_profile_saved), name),
|
||||
Toast.LENGTH_LONG).show();
|
||||
});
|
||||
builder.setNegativeButton(R.string.controller_mapping_activity_cancel, (dialog, which) -> dialog.dismiss());
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void doClearBindings() {
|
||||
SharedPreferences.Editor prefEdit = PreferenceManager.getDefaultSharedPreferences(this).edit();
|
||||
for (ControllerBindingPreference pref : mPreferences)
|
||||
pref.clearBinding(prefEdit);
|
||||
prefEdit.commit();
|
||||
}
|
||||
|
||||
private void updateAllBindings() {
|
||||
for (ControllerBindingPreference pref : mPreferences)
|
||||
pref.updateValue();
|
||||
}
|
||||
|
||||
public static class ControllerPortFragment extends PreferenceFragmentCompat {
|
||||
private ControllerMappingActivity activity;
|
||||
private int controllerIndex;
|
||||
|
||||
public ControllerPortFragment(ControllerMappingActivity activity, int controllerIndex) {
|
||||
this.activity = activity;
|
||||
this.controllerIndex = controllerIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
final SharedPreferences sp = getPreferenceManager().getSharedPreferences();
|
||||
final String defaultControllerType = controllerIndex == 0 ? "DigitalController" : "None";
|
||||
String controllerType = sp.getString(String.format("Controller%d/Type", controllerIndex), defaultControllerType);
|
||||
String[] controllerButtons = AndroidHostInterface.getControllerButtonNames(controllerType);
|
||||
String[] axisButtons = AndroidHostInterface.getControllerAxisNames(controllerType);
|
||||
|
||||
final PreferenceScreen ps = getPreferenceManager().createPreferenceScreen(getContext());
|
||||
if (controllerButtons != null) {
|
||||
for (String buttonName : controllerButtons) {
|
||||
final ControllerBindingPreference cbp = new ControllerBindingPreference(getContext(), null);
|
||||
cbp.initButton(controllerIndex, buttonName);
|
||||
ps.addPreference(cbp);
|
||||
activity.mPreferences.add(cbp);
|
||||
}
|
||||
}
|
||||
if (axisButtons != null) {
|
||||
for (String axisName : axisButtons) {
|
||||
final ControllerBindingPreference cbp = new ControllerBindingPreference(getContext(), null);
|
||||
cbp.initAxis(controllerIndex, axisName);
|
||||
ps.addPreference(cbp);
|
||||
activity.mPreferences.add(cbp);
|
||||
}
|
||||
}
|
||||
|
||||
setPreferenceScreen(ps);
|
||||
}
|
||||
}
|
||||
|
||||
public static class HotkeyFragment extends PreferenceFragmentCompat {
|
||||
private ControllerMappingActivity activity;
|
||||
private HotkeyInfo[] mHotkeyInfo;
|
||||
|
||||
public HotkeyFragment(ControllerMappingActivity activity) {
|
||||
this.activity = activity;
|
||||
this.mHotkeyInfo = AndroidHostInterface.getInstance().getHotkeyInfoList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
final PreferenceScreen ps = getPreferenceManager().createPreferenceScreen(getContext());
|
||||
if (mHotkeyInfo != null) {
|
||||
for (HotkeyInfo hotkeyInfo : mHotkeyInfo) {
|
||||
final ControllerBindingPreference cbp = new ControllerBindingPreference(getContext(), null);
|
||||
cbp.initHotkey(hotkeyInfo);
|
||||
ps.addPreference(cbp);
|
||||
activity.mPreferences.add(cbp);
|
||||
}
|
||||
}
|
||||
|
||||
setPreferenceScreen(ps);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SettingsCollectionFragment extends Fragment {
|
||||
private ControllerMappingActivity activity;
|
||||
private SettingsCollectionAdapter adapter;
|
||||
private ViewPager2 viewPager;
|
||||
|
||||
public SettingsCollectionFragment(ControllerMappingActivity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_controller_mapping, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
adapter = new SettingsCollectionAdapter(activity, this);
|
||||
viewPager = view.findViewById(R.id.view_pager);
|
||||
viewPager.setAdapter(adapter);
|
||||
|
||||
TabLayout tabLayout = view.findViewById(R.id.tab_layout);
|
||||
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
|
||||
if (position == NUM_CONTROLLER_PORTS)
|
||||
tab.setText("Hotkeys");
|
||||
else
|
||||
tab.setText(String.format("Port %d", position + 1));
|
||||
}).attach();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SettingsCollectionAdapter extends FragmentStateAdapter {
|
||||
private ControllerMappingActivity activity;
|
||||
|
||||
public SettingsCollectionAdapter(@NonNull ControllerMappingActivity activity, @NonNull Fragment fragment) {
|
||||
super(fragment);
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
if (position != NUM_CONTROLLER_PORTS)
|
||||
return new ControllerPortFragment(activity, position + 1);
|
||||
else
|
||||
return new HotkeyFragment(activity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return NUM_CONTROLLER_PORTS + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +1,31 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Vibrator;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.util.AndroidException;
|
||||
import android.util.Log;
|
||||
import android.view.Menu;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.View;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.widget.PopupMenu;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
/**
|
||||
@@ -39,7 +39,6 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
private SharedPreferences mPreferences;
|
||||
private boolean mWasDestroyed = false;
|
||||
private boolean mStopRequested = false;
|
||||
private boolean mWasPausedOnSurfaceLoss = false;
|
||||
private boolean mApplySettingsOnSurfaceRestored = false;
|
||||
private String mGameTitle = null;
|
||||
private EmulationSurfaceView mContentView;
|
||||
@@ -64,6 +63,19 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
private void reportErrorOnUIThread(String message) {
|
||||
// Toast.makeText(this, message, Toast.LENGTH_LONG);
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.emulation_activity_error)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.emulation_activity_ok, (dialog, button) -> {
|
||||
dialog.dismiss();
|
||||
enableFullscreenImmersive();
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
public void reportError(String message) {
|
||||
Log.e("EmulationActivity", message);
|
||||
|
||||
@@ -71,10 +83,11 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
runOnUiThread(() -> {
|
||||
// Toast.makeText(this, message, Toast.LENGTH_LONG);
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Error")
|
||||
.setTitle(R.string.emulation_activity_error)
|
||||
.setMessage(message)
|
||||
.setPositiveButton("OK", (dialog, button) -> {
|
||||
.setPositiveButton(R.string.emulation_activity_ok, (dialog, button) -> {
|
||||
dialog.dismiss();
|
||||
enableFullscreenImmersive();
|
||||
synchronized (lock) {
|
||||
lock.notify();
|
||||
}
|
||||
@@ -91,19 +104,25 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
}
|
||||
}
|
||||
|
||||
public void reportMessage(String message) {
|
||||
Log.i("EmulationActivity", message);
|
||||
runOnUiThread(() -> {
|
||||
Toast.makeText(this, message, Toast.LENGTH_SHORT);
|
||||
});
|
||||
private EmulationThread mEmulationThread;
|
||||
|
||||
private void stopEmulationThread() {
|
||||
if (mEmulationThread == null)
|
||||
return;
|
||||
|
||||
mEmulationThread.stopAndJoin();
|
||||
mEmulationThread = null;
|
||||
}
|
||||
|
||||
public void onEmulationStarted() {
|
||||
runOnUiThread(() -> {
|
||||
updateRequestedOrientation();
|
||||
updateOrientation();
|
||||
});
|
||||
}
|
||||
|
||||
public void onEmulationStopped() {
|
||||
runOnUiThread(() -> {
|
||||
AndroidHostInterface.getInstance().stopEmulationThread();
|
||||
if (!mWasDestroyed && !mStopRequested)
|
||||
finish();
|
||||
});
|
||||
@@ -115,14 +134,53 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
});
|
||||
}
|
||||
|
||||
public float getRefreshRate() {
|
||||
WindowManager windowManager = getWindowManager();
|
||||
if (windowManager == null) {
|
||||
windowManager = ((WindowManager) getSystemService(Context.WINDOW_SERVICE));
|
||||
if (windowManager == null)
|
||||
return -1.0f;
|
||||
}
|
||||
|
||||
Display display = windowManager.getDefaultDisplay();
|
||||
if (display == null)
|
||||
return -1.0f;
|
||||
|
||||
return display.getRefreshRate();
|
||||
}
|
||||
|
||||
public void openPauseMenu() {
|
||||
runOnUiThread(() -> {
|
||||
showMenu();
|
||||
});
|
||||
}
|
||||
|
||||
private void doApplySettings() {
|
||||
AndroidHostInterface.getInstance().applySettings();
|
||||
updateRequestedOrientation();
|
||||
updateControllers();
|
||||
updateSustainedPerformanceMode();
|
||||
}
|
||||
|
||||
private void applySettings() {
|
||||
if (!AndroidHostInterface.getInstance().isEmulationThreadRunning())
|
||||
return;
|
||||
|
||||
if (AndroidHostInterface.getInstance().hasSurface())
|
||||
AndroidHostInterface.getInstance().applySettings();
|
||||
else
|
||||
if (AndroidHostInterface.getInstance().hasSurface()) {
|
||||
doApplySettings();
|
||||
} else {
|
||||
mApplySettingsOnSurfaceRestored = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Ends the activity if it was restored without properly being created.
|
||||
private boolean checkActivityIsValid() {
|
||||
if (!AndroidHostInterface.hasInstance() || !AndroidHostInterface.getInstance().isEmulationThreadRunning()) {
|
||||
finish();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -132,61 +190,67 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
// Once we get a surface, we can boot.
|
||||
if (AndroidHostInterface.getInstance().isEmulationThreadRunning()) {
|
||||
final boolean hadSurface = AndroidHostInterface.getInstance().hasSurface();
|
||||
AndroidHostInterface.getInstance().surfaceChanged(holder.getSurface(), format, width, height);
|
||||
AndroidHostInterface.getInstance().surfaceChanged(holder.getSurface(), format, width, height);
|
||||
|
||||
if (mEmulationThread != null) {
|
||||
updateOrientation();
|
||||
|
||||
if (holder.getSurface() != null && !hadSurface && AndroidHostInterface.getInstance().isEmulationThreadPaused() && !mWasPausedOnSurfaceLoss) {
|
||||
AndroidHostInterface.getInstance().pauseEmulationThread(false);
|
||||
}
|
||||
|
||||
if (mApplySettingsOnSurfaceRestored) {
|
||||
AndroidHostInterface.getInstance().applySettings();
|
||||
mApplySettingsOnSurfaceRestored = false;
|
||||
doApplySettings();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
final String bootPath = getIntent().getStringExtra("bootPath");
|
||||
final boolean resumeState = getIntent().getBooleanExtra("resumeState", false);
|
||||
final boolean saveStateOnExit = getBooleanSetting("Main/SaveStateOnExit", true);
|
||||
final boolean resumeState = getIntent().getBooleanExtra("resumeState", saveStateOnExit);
|
||||
final String bootSaveStatePath = getIntent().getStringExtra("saveStatePath");
|
||||
|
||||
AndroidHostInterface.getInstance().startEmulationThread(this, holder.getSurface(), bootPath, resumeState, bootSaveStatePath);
|
||||
updateOrientation();
|
||||
mEmulationThread = EmulationThread.create(this, bootPath, resumeState, bootSaveStatePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
if (!AndroidHostInterface.getInstance().isEmulationThreadRunning())
|
||||
return;
|
||||
|
||||
Log.i("EmulationActivity", "Surface destroyed");
|
||||
|
||||
// Save the resume state in case we never get back again...
|
||||
if (!mStopRequested)
|
||||
if (AndroidHostInterface.getInstance().isEmulationThreadRunning() && !mStopRequested)
|
||||
AndroidHostInterface.getInstance().saveResumeState(true);
|
||||
|
||||
mWasPausedOnSurfaceLoss = AndroidHostInterface.getInstance().isEmulationThreadPaused();
|
||||
AndroidHostInterface.getInstance().pauseEmulationThread(true);
|
||||
AndroidHostInterface.getInstance().surfaceChanged(null, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Log.i("EmulationActivity", "OnCreate");
|
||||
|
||||
// we might be coming from a third-party launcher if the host interface isn't setup
|
||||
if (!AndroidHostInterface.hasInstance() && !AndroidHostInterface.createInstance(this)) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
enableFullscreenImmersive();
|
||||
setContentView(R.layout.activity_emulation);
|
||||
|
||||
mContentView = findViewById(R.id.fullscreen_content);
|
||||
mContentView.getHolder().addCallback(this);
|
||||
mContentView.setFocusableInTouchMode(true);
|
||||
mContentView.setFocusable(true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
mContentView.setFocusedByDefault(true);
|
||||
}
|
||||
mContentView.requestFocus();
|
||||
|
||||
// Sort out rotation.
|
||||
updateOrientation();
|
||||
updateSustainedPerformanceMode();
|
||||
|
||||
// Hook up controller input.
|
||||
updateControllers();
|
||||
registerInputDeviceListener();
|
||||
@@ -208,9 +272,9 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
Log.i("EmulationActivity", "OnStop");
|
||||
if (AndroidHostInterface.getInstance().isEmulationThreadRunning()) {
|
||||
if (mEmulationThread != null) {
|
||||
mWasDestroyed = true;
|
||||
AndroidHostInterface.getInstance().stopEmulationThread();
|
||||
stopEmulationThread();
|
||||
}
|
||||
|
||||
unregisterInputDeviceListener();
|
||||
@@ -220,9 +284,29 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
if (!checkActivityIsValid()) {
|
||||
// we must've got killed off in the background :(
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestCode == REQUEST_CODE_SETTINGS) {
|
||||
if (AndroidHostInterface.getInstance().isEmulationThreadRunning())
|
||||
if (AndroidHostInterface.getInstance().isEmulationThreadRunning()) {
|
||||
applySettings();
|
||||
}
|
||||
} else if (requestCode == REQUEST_IMPORT_PATCH_CODES) {
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
importPatchesFromFile(data.getData());
|
||||
} else if (requestCode == REQUEST_CHANGE_DISC_FILE) {
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
String path = GameDirectoriesActivity.getPathFromUri(this, data.getData());
|
||||
if (path == null)
|
||||
return;
|
||||
|
||||
AndroidHostInterface.getInstance().setMediaFilename(path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,10 +315,43 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
showMenu();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchKeyEvent(KeyEvent event) {
|
||||
if (event.getAction() == KeyEvent.ACTION_DOWN) {
|
||||
if (mContentView.onKeyDown(event.getKeyCode(), event))
|
||||
return true;
|
||||
} else if (event.getAction() == KeyEvent.ACTION_UP) {
|
||||
if (mContentView.onKeyUp(event.getKeyCode(), event))
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchGenericMotionEvent(MotionEvent ev) {
|
||||
if (mContentView.onGenericMotionEvent(ev))
|
||||
return true;
|
||||
|
||||
return super.dispatchGenericMotionEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
updateOrientation(newConfig.orientation);
|
||||
|
||||
if (checkActivityIsValid())
|
||||
updateOrientation(newConfig.orientation);
|
||||
}
|
||||
|
||||
private void updateRequestedOrientation() {
|
||||
final String orientation = getStringSetting("Main/EmulationScreenOrientation", "unspecified");
|
||||
if (orientation.equals("portrait"))
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT);
|
||||
else if (orientation.equals("landscape"))
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE);
|
||||
else
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
}
|
||||
|
||||
private void updateOrientation() {
|
||||
@@ -243,17 +360,16 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
}
|
||||
|
||||
private void updateOrientation(int newOrientation) {
|
||||
if (!AndroidHostInterface.getInstance().isEmulationThreadRunning())
|
||||
return;
|
||||
|
||||
if (newOrientation == Configuration.ORIENTATION_PORTRAIT)
|
||||
AndroidHostInterface.getInstance().setDisplayAlignment(AndroidHostInterface.DISPLAY_ALIGNMENT_TOP_OR_LEFT);
|
||||
else
|
||||
AndroidHostInterface.getInstance().setDisplayAlignment(AndroidHostInterface.DISPLAY_ALIGNMENT_CENTER);
|
||||
|
||||
if (mTouchscreenController != null)
|
||||
mTouchscreenController.updateOrientation();
|
||||
}
|
||||
|
||||
private void enableFullscreenImmersive()
|
||||
{
|
||||
private void enableFullscreenImmersive() {
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
|
||||
@@ -266,32 +382,43 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
}
|
||||
|
||||
private static final int REQUEST_CODE_SETTINGS = 0;
|
||||
private static final int REQUEST_IMPORT_PATCH_CODES = 1;
|
||||
private static final int REQUEST_CHANGE_DISC_FILE = 2;
|
||||
|
||||
private void onMenuClosed() {
|
||||
enableFullscreenImmersive();
|
||||
|
||||
if (AndroidHostInterface.getInstance().isEmulationThreadPaused())
|
||||
AndroidHostInterface.getInstance().pauseEmulationThread(false);
|
||||
}
|
||||
|
||||
private void showMenu() {
|
||||
if (getBooleanSetting("Main/PauseOnMenu", false) &&
|
||||
!AndroidHostInterface.getInstance().isEmulationThreadPaused()) {
|
||||
AndroidHostInterface.getInstance().pauseEmulationThread(true);
|
||||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
if (mGameTitle != null && !mGameTitle.isEmpty())
|
||||
builder.setTitle(mGameTitle);
|
||||
|
||||
builder.setItems(R.array.emulation_menu, (dialogInterface, i) -> {
|
||||
switch (i)
|
||||
{
|
||||
case 0: // Quick Load
|
||||
switch (i) {
|
||||
case 0: // Load State
|
||||
{
|
||||
AndroidHostInterface.getInstance().loadState(false, 0);
|
||||
showSaveStateMenu(false);
|
||||
return;
|
||||
}
|
||||
|
||||
case 1: // Quick Save
|
||||
case 1: // Save State
|
||||
{
|
||||
AndroidHostInterface.getInstance().saveState(false, 0);
|
||||
showSaveStateMenu(true);
|
||||
return;
|
||||
}
|
||||
|
||||
case 2: // Toggle Speed Limiter
|
||||
case 2: // Toggle Fast Forward
|
||||
{
|
||||
boolean newSetting = !getBooleanSetting("Main/SpeedLimiterEnabled", true);
|
||||
setBooleanSetting("Main/SpeedLimiterEnabled", newSetting);
|
||||
applySettings();
|
||||
AndroidHostInterface.getInstance().setFastForwardEnabled(!AndroidHostInterface.getInstance().isFastForwardEnabled());
|
||||
onMenuClosed();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -309,42 +436,67 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.setOnDismissListener(dialogInterface -> enableFullscreenImmersive());
|
||||
builder.setOnCancelListener(dialogInterface -> onMenuClosed());
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void showSaveStateMenu(boolean saving) {
|
||||
final SaveStateInfo[] infos = AndroidHostInterface.getInstance().getSaveStateInfo(true);
|
||||
if (infos == null) {
|
||||
onMenuClosed();
|
||||
return;
|
||||
}
|
||||
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
final ListView listView = new ListView(this);
|
||||
listView.setAdapter(new SaveStateInfo.ListAdapter(this, infos));
|
||||
builder.setView(listView);
|
||||
builder.setOnDismissListener((dialog) -> {
|
||||
onMenuClosed();
|
||||
});
|
||||
|
||||
final AlertDialog dialog = builder.create();
|
||||
|
||||
listView.setOnItemClickListener((parent, view, position, id) -> {
|
||||
SaveStateInfo info = infos[position];
|
||||
if (saving) {
|
||||
AndroidHostInterface.getInstance().saveState(info.isGlobal(), info.getSlot());
|
||||
} else {
|
||||
AndroidHostInterface.getInstance().loadState(info.isGlobal(), info.getSlot());
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private void showMoreMenu() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
if (mGameTitle != null && !mGameTitle.isEmpty())
|
||||
builder.setTitle(mGameTitle);
|
||||
|
||||
builder.setItems(R.array.emulation_more_menu, (dialogInterface, i) -> {
|
||||
switch (i)
|
||||
{
|
||||
switch (i) {
|
||||
case 0: // Reset
|
||||
{
|
||||
AndroidHostInterface.getInstance().resetSystem();
|
||||
onMenuClosed();
|
||||
return;
|
||||
}
|
||||
|
||||
case 1: // Cheats
|
||||
case 1: // Patch Codes
|
||||
{
|
||||
showCheatsMenu();
|
||||
showPatchesMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
case 2: // Change Disc
|
||||
{
|
||||
showDiscChangeMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
case 3: // Change Touchscreen Controller
|
||||
{
|
||||
showTouchscreenControllerMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
case 4: // Settings
|
||||
case 3: // Settings
|
||||
{
|
||||
Intent intent = new Intent(EmulationActivity.this, SettingsActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
@@ -352,14 +504,23 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
return;
|
||||
}
|
||||
|
||||
case 5: // Quit
|
||||
case 4: // Change Touchscreen Controller
|
||||
{
|
||||
finish();
|
||||
showTouchscreenControllerMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
case 5: // Edit Touchscreen Controller Layout
|
||||
{
|
||||
if (mTouchscreenController != null)
|
||||
mTouchscreenController.startLayoutEditing();
|
||||
|
||||
onMenuClosed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.setOnDismissListener(dialogInterface -> enableFullscreenImmersive());
|
||||
builder.setOnCancelListener(dialogInterface -> onMenuClosed());
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
@@ -369,28 +530,78 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
String[] values = getResources().getStringArray(R.array.settings_touchscreen_controller_view_values);
|
||||
setStringSetting("Controller1/TouchscreenControllerView", values[i]);
|
||||
updateControllers();
|
||||
onMenuClosed();
|
||||
});
|
||||
builder.setOnDismissListener(dialogInterface -> enableFullscreenImmersive());
|
||||
builder.setOnCancelListener(dialogInterface -> onMenuClosed());
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void showCheatsMenu() {
|
||||
final CheatCode[] cheats = AndroidHostInterface.getInstance().getCheatList();
|
||||
if (cheats == null) {
|
||||
AndroidHostInterface.getInstance().addOSDMessage("No cheats are loaded.", 5.0f);
|
||||
return;
|
||||
private void showPatchesMenu() {
|
||||
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
final PatchCode[] codes = AndroidHostInterface.getInstance().getPatchCodeList();
|
||||
if (codes != null) {
|
||||
CharSequence[] items = new CharSequence[codes.length];
|
||||
boolean[] itemsChecked = new boolean[codes.length];
|
||||
for (int i = 0; i < codes.length; i++) {
|
||||
final PatchCode cc = codes[i];
|
||||
items[i] = cc.getDescription();
|
||||
itemsChecked[i] = cc.isEnabled();
|
||||
}
|
||||
|
||||
builder.setMultiChoiceItems(items, itemsChecked, (dialogInterface, i, checked) -> {
|
||||
AndroidHostInterface.getInstance().setPatchCodeEnabled(i, checked);
|
||||
});
|
||||
}
|
||||
|
||||
builder.setNegativeButton(R.string.emulation_activity_ok, (dialogInterface, i) -> {
|
||||
dialogInterface.dismiss();
|
||||
});
|
||||
builder.setNeutralButton(R.string.emulation_activity_import_patch_codes, (dialogInterface, i) -> {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
intent.setType("*/*");
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.emulation_activity_choose_patch_code_file)), REQUEST_IMPORT_PATCH_CODES);
|
||||
});
|
||||
|
||||
builder.setOnDismissListener(dialogInterface -> onMenuClosed());
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
private void importPatchesFromFile(Uri uri) {
|
||||
String str = FileUtil.readFileFromUri(this, uri, 512 * 1024);
|
||||
if (str == null || !AndroidHostInterface.getInstance().importPatchCodesFromString(str)) {
|
||||
reportErrorOnUIThread(getString(R.string.emulation_activity_failed_to_import_patch_codes));
|
||||
}
|
||||
}
|
||||
|
||||
private void showDiscChangeMenu() {
|
||||
final String[] paths = AndroidHostInterface.getInstance().getMediaPlaylistPaths();
|
||||
final int currentPath = AndroidHostInterface.getInstance().getMediaPlaylistIndex();
|
||||
final int numPaths = (paths != null) ? paths.length : 0;
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
CharSequence[] items = new CharSequence[cheats.length];
|
||||
for (int i = 0; i < cheats.length; i++) {
|
||||
final CheatCode cc = cheats[i];
|
||||
items[i] = String.format("%s %s", cc.isEnabled() ? "(ON)" : "(OFF)", cc.getName());
|
||||
}
|
||||
CharSequence[] items = new CharSequence[numPaths + 1];
|
||||
for (int i = 0; i < numPaths; i++)
|
||||
items[i] = GameListEntry.getFileNameForPath(paths[i]);
|
||||
items[numPaths] = "Select New File...";
|
||||
|
||||
builder.setItems(items, (dialogInterface, i) -> AndroidHostInterface.getInstance().setCheatEnabled(i, !cheats[i].isEnabled()));
|
||||
builder.setOnDismissListener(dialogInterface -> enableFullscreenImmersive());
|
||||
builder.setSingleChoiceItems(items, (currentPath < numPaths) ? currentPath : -1, (dialogInterface, i) -> {
|
||||
dialogInterface.dismiss();
|
||||
onMenuClosed();
|
||||
|
||||
if (i < numPaths) {
|
||||
AndroidHostInterface.getInstance().setMediaPlaylistIndex(i);
|
||||
} else {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("*/*");
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.main_activity_choose_disc_image)), REQUEST_CHANGE_DISC_FILE);
|
||||
}
|
||||
});
|
||||
builder.setOnCancelListener(dialogInterface -> onMenuClosed());
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
@@ -403,6 +614,8 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
final String controllerType = getStringSetting("Controller1/Type", "DigitalController");
|
||||
final String viewType = getStringSetting("Controller1/TouchscreenControllerView", "digital");
|
||||
final boolean autoHideTouchscreenController = getBooleanSetting("Controller1/AutoHideTouchscreenController", false);
|
||||
final boolean hapticFeedback = getBooleanSetting("Controller1/HapticFeedback", false);
|
||||
final boolean vibration = getBooleanSetting("Controller1/Vibration", false);
|
||||
final FrameLayout activityLayout = findViewById(R.id.frameLayout);
|
||||
|
||||
Log.i("EmulationActivity", "Controller type: " + controllerType);
|
||||
@@ -410,22 +623,27 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
|
||||
final boolean hasAnyControllers = mContentView.initControllerMapping(controllerType);
|
||||
|
||||
if (controllerType == "none" || viewType == "none" || (hasAnyControllers && autoHideTouchscreenController)) {
|
||||
if (controllerType.equals("none") || viewType.equals("none") || (hasAnyControllers && autoHideTouchscreenController)) {
|
||||
if (mTouchscreenController != null) {
|
||||
activityLayout.removeView(mTouchscreenController);
|
||||
mTouchscreenController = null;
|
||||
mVibratorService = null;
|
||||
}
|
||||
} else {
|
||||
if (mTouchscreenController == null) {
|
||||
mTouchscreenController = new TouchscreenControllerView(this);
|
||||
if (vibration)
|
||||
mVibratorService = (Vibrator) getSystemService(VIBRATOR_SERVICE);
|
||||
|
||||
activityLayout.addView(mTouchscreenController);
|
||||
}
|
||||
|
||||
mTouchscreenController.init(0, controllerType, viewType);
|
||||
mTouchscreenController.init(0, controllerType, viewType, hapticFeedback);
|
||||
}
|
||||
}
|
||||
|
||||
private InputManager.InputDeviceListener mInputDeviceListener;
|
||||
|
||||
private void registerInputDeviceListener() {
|
||||
if (mInputDeviceListener != null)
|
||||
return;
|
||||
@@ -450,18 +668,53 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||
}
|
||||
};
|
||||
|
||||
InputManager inputManager = ((InputManager)getSystemService(Context.INPUT_SERVICE));
|
||||
InputManager inputManager = ((InputManager) getSystemService(Context.INPUT_SERVICE));
|
||||
if (inputManager != null)
|
||||
inputManager.registerInputDeviceListener(mInputDeviceListener, null);
|
||||
}
|
||||
|
||||
private void unregisterInputDeviceListener() {
|
||||
if (mInputDeviceListener == null)
|
||||
return;
|
||||
|
||||
InputManager inputManager = ((InputManager)getSystemService(Context.INPUT_SERVICE));
|
||||
InputManager inputManager = ((InputManager) getSystemService(Context.INPUT_SERVICE));
|
||||
if (inputManager != null)
|
||||
inputManager.unregisterInputDeviceListener(mInputDeviceListener);
|
||||
|
||||
|
||||
mInputDeviceListener = null;
|
||||
}
|
||||
|
||||
private Vibrator mVibratorService;
|
||||
|
||||
public void setVibration(boolean enabled) {
|
||||
if (mVibratorService == null)
|
||||
return;
|
||||
|
||||
runOnUiThread(() -> {
|
||||
if (mVibratorService == null)
|
||||
return;
|
||||
|
||||
if (enabled)
|
||||
mVibratorService.vibrate(1000);
|
||||
else
|
||||
mVibratorService.cancel();
|
||||
});
|
||||
}
|
||||
|
||||
private boolean mSustainedPerformanceModeEnabled = false;
|
||||
|
||||
private void updateSustainedPerformanceMode() {
|
||||
final boolean enabled = getBooleanSetting("Main/SustainedPerformanceMode", false);
|
||||
if (mSustainedPerformanceModeEnabled == enabled)
|
||||
return;
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
getWindow().setSustainedPerformanceMode(enabled);
|
||||
Log.i("EmulationActivity", String.format("%s sustained performance mode.", enabled ? "enabling" : "disabling"));
|
||||
} else {
|
||||
Log.e("EmulationActivity", "Sustained performance mode not supported.");
|
||||
}
|
||||
mSustainedPerformanceModeEnabled = enabled;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.input.InputManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceView;
|
||||
|
||||
import androidx.core.util.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class EmulationSurfaceView extends SurfaceView {
|
||||
@@ -27,71 +28,98 @@ public class EmulationSurfaceView extends SurfaceView {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
private boolean isDPadOrButtonEvent(KeyEvent event) {
|
||||
public static boolean isDPadOrButtonEvent(KeyEvent event) {
|
||||
final int source = event.getSource();
|
||||
return (source & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD ||
|
||||
(source & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD ||
|
||||
(source & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (isDPadOrButtonEvent(event) && event.getRepeatCount() == 0 &&
|
||||
handleControllerKey(event.getDeviceId(), keyCode, true)) {
|
||||
return true;
|
||||
}
|
||||
private boolean isExternalKeyCode(int keyCode) {
|
||||
switch (keyCode) {
|
||||
case KeyEvent.KEYCODE_BACK:
|
||||
case KeyEvent.KEYCODE_HOME:
|
||||
case KeyEvent.KEYCODE_MENU:
|
||||
case KeyEvent.KEYCODE_VOLUME_UP:
|
||||
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
||||
case KeyEvent.KEYCODE_VOLUME_MUTE:
|
||||
case KeyEvent.KEYCODE_POWER:
|
||||
case KeyEvent.KEYCODE_CAMERA:
|
||||
case KeyEvent.KEYCODE_CALL:
|
||||
case KeyEvent.KEYCODE_ENDCALL:
|
||||
case KeyEvent.KEYCODE_VOICE_ASSIST:
|
||||
return true;
|
||||
|
||||
return super.onKeyDown(keyCode, event);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (isDPadOrButtonEvent(event) && event.getRepeatCount() == 0 &&
|
||||
handleControllerKey(event.getDeviceId(), keyCode, false)) {
|
||||
return true;
|
||||
private static final int[] buttonKeyCodes = new int[]{
|
||||
KeyEvent.KEYCODE_BUTTON_A, // 0/Cross
|
||||
KeyEvent.KEYCODE_BUTTON_B, // 1/Circle
|
||||
KeyEvent.KEYCODE_BUTTON_X, // 2/Square
|
||||
KeyEvent.KEYCODE_BUTTON_Y, // 3/Triangle
|
||||
KeyEvent.KEYCODE_BUTTON_SELECT, // 4/Select
|
||||
KeyEvent.KEYCODE_BUTTON_MODE, // 5/Analog
|
||||
KeyEvent.KEYCODE_BUTTON_START, // 6/Start
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBL, // 7/L3
|
||||
KeyEvent.KEYCODE_BUTTON_THUMBR, // 8/R3
|
||||
KeyEvent.KEYCODE_BUTTON_L1, // 9/L1
|
||||
KeyEvent.KEYCODE_BUTTON_R1, // 10/R1
|
||||
KeyEvent.KEYCODE_DPAD_UP, // 11/Up
|
||||
KeyEvent.KEYCODE_DPAD_DOWN, // 12/Down
|
||||
KeyEvent.KEYCODE_DPAD_LEFT, // 13/Left
|
||||
KeyEvent.KEYCODE_DPAD_RIGHT, // 14/Right
|
||||
KeyEvent.KEYCODE_BUTTON_L2, // 15
|
||||
KeyEvent.KEYCODE_BUTTON_R2, // 16
|
||||
KeyEvent.KEYCODE_BUTTON_C, // 17
|
||||
KeyEvent.KEYCODE_BUTTON_Z, // 18
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN, // 19
|
||||
KeyEvent.KEYCODE_VOLUME_UP, // 20
|
||||
KeyEvent.KEYCODE_MENU, // 21
|
||||
KeyEvent.KEYCODE_CAMERA, // 22
|
||||
};
|
||||
private static final int[] axisCodes = new int[]{
|
||||
MotionEvent.AXIS_X, // 0/LeftX
|
||||
MotionEvent.AXIS_Y, // 1/LeftY
|
||||
MotionEvent.AXIS_Z, // 2/RightX
|
||||
MotionEvent.AXIS_RZ, // 3/RightY
|
||||
MotionEvent.AXIS_LTRIGGER, // 4/L2
|
||||
MotionEvent.AXIS_RTRIGGER, // 5/R2
|
||||
MotionEvent.AXIS_RX, // 6
|
||||
MotionEvent.AXIS_RY, // 7
|
||||
MotionEvent.AXIS_HAT_X, // 8
|
||||
MotionEvent.AXIS_HAT_Y, // 9
|
||||
MotionEvent.AXIS_GAS, // 10
|
||||
MotionEvent.AXIS_BRAKE, // 11
|
||||
};
|
||||
|
||||
public static int getButtonIndexForKeyCode(int keyCode) {
|
||||
for (int buttonIndex = 0; buttonIndex < buttonKeyCodes.length; buttonIndex++) {
|
||||
if (buttonKeyCodes[buttonIndex] == keyCode)
|
||||
return buttonIndex;
|
||||
}
|
||||
|
||||
return super.onKeyDown(keyCode, event);
|
||||
Log.e("EmulationSurfaceView", String.format("Button code %d not found", keyCode));
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
final int source = event.getSource();
|
||||
if ((source & InputDevice.SOURCE_JOYSTICK) == 0)
|
||||
return super.onGenericMotionEvent(event);
|
||||
public static int[] getKnownAxisCodes() {
|
||||
return axisCodes;
|
||||
}
|
||||
|
||||
final int deviceId = event.getDeviceId();
|
||||
for (AxisMapping mapping : mControllerAxisMapping) {
|
||||
if (mapping.deviceId != deviceId)
|
||||
continue;
|
||||
|
||||
final float axisValue = event.getAxisValue(mapping.deviceAxisOrButton);
|
||||
float emuValue;
|
||||
|
||||
if (mapping.deviceMotionRange != null) {
|
||||
final float transformedValue = (axisValue - mapping.deviceMotionRange.getMin()) / mapping.deviceMotionRange.getRange();
|
||||
emuValue = (transformedValue * 2.0f) - 1.0f;
|
||||
} else {
|
||||
emuValue = axisValue;
|
||||
}
|
||||
Log.d("EmulationSurfaceView", String.format("axis %d value %f emuvalue %f", mapping.deviceAxisOrButton, axisValue, emuValue));
|
||||
|
||||
if (mapping.axisMapping >= 0) {
|
||||
AndroidHostInterface.getInstance().setControllerAxisState(0, mapping.axisMapping, emuValue);
|
||||
}
|
||||
|
||||
final float DEAD_ZONE = 0.25f;
|
||||
if (mapping.negativeButton >= 0) {
|
||||
AndroidHostInterface.getInstance().setControllerButtonState(0, mapping.negativeButton, (emuValue <= -DEAD_ZONE));
|
||||
}
|
||||
if (mapping.positiveButton >= 0) {
|
||||
AndroidHostInterface.getInstance().setControllerButtonState(0, mapping.positiveButton, (emuValue >= DEAD_ZONE));
|
||||
}
|
||||
public static int getAxisIndexForAxisCode(int axisCode) {
|
||||
for (int axisIndex = 0; axisIndex < axisCodes.length; axisIndex++) {
|
||||
if (axisCodes[axisIndex] == axisCode)
|
||||
return axisIndex;
|
||||
}
|
||||
|
||||
return true;
|
||||
Log.e("EmulationSurfaceView", String.format("Axis code %d not found", axisCode));
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
private class ButtonMapping {
|
||||
public ButtonMapping(int deviceId, int deviceButton, int controllerIndex, int button) {
|
||||
this.deviceId = deviceId;
|
||||
@@ -139,16 +167,110 @@ public class EmulationSurfaceView extends SurfaceView {
|
||||
private ArrayList<ButtonMapping> mControllerKeyMapping;
|
||||
private ArrayList<AxisMapping> mControllerAxisMapping;
|
||||
|
||||
private void addControllerKeyMapping(int deviceId, int keyCode, int controllerIndex, String controllerType, String buttonName) {
|
||||
int mapping = AndroidHostInterface.getControllerButtonCode(controllerType, buttonName);
|
||||
Log.i("EmulationSurfaceView", String.format("Map %d to %d (%s)", keyCode, mapping,
|
||||
buttonName));
|
||||
if (mapping >= 0) {
|
||||
mControllerKeyMapping.add(new ButtonMapping(deviceId, keyCode, controllerIndex, mapping));
|
||||
private boolean handleControllerKey(int deviceId, int keyCode, int repeatCount, boolean pressed) {
|
||||
boolean result = false;
|
||||
for (ButtonMapping mapping : mControllerKeyMapping) {
|
||||
if (mapping.deviceId != deviceId || mapping.deviceAxisOrButton != keyCode)
|
||||
continue;
|
||||
|
||||
if (repeatCount == 0) {
|
||||
AndroidHostInterface.getInstance().handleControllerButtonEvent(0, mapping.buttonMapping, pressed);
|
||||
Log.d("EmulationSurfaceView", String.format("handleControllerKey %d -> %d %d", keyCode, mapping.buttonMapping, pressed ? 1 : 0));
|
||||
}
|
||||
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void addControllerAxisMapping(int deviceId, List<InputDevice.MotionRange> motionRanges, int axis, int controllerIndex, String controllerType, String axisName, String negativeButtonName, String positiveButtonName) {
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (!isDPadOrButtonEvent(event))
|
||||
return false;
|
||||
|
||||
if (handleControllerKey(event.getDeviceId(), keyCode, event.getRepeatCount(), true))
|
||||
return true;
|
||||
|
||||
// eat non-external button events anyway
|
||||
return !isExternalKeyCode(keyCode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
||||
if (!isDPadOrButtonEvent(event))
|
||||
return false;
|
||||
|
||||
if (handleControllerKey(event.getDeviceId(), keyCode, 0, false))
|
||||
return true;
|
||||
|
||||
// eat non-external button events anyway
|
||||
return !isExternalKeyCode(keyCode);
|
||||
}
|
||||
|
||||
private float clamp(float value, float min, float max) {
|
||||
return (value < min) ? min : ((value > max) ? max : value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
final int source = event.getSource();
|
||||
if ((source & (InputDevice.SOURCE_JOYSTICK | InputDevice.SOURCE_GAMEPAD | InputDevice.SOURCE_DPAD)) == 0)
|
||||
return false;
|
||||
|
||||
final int deviceId = event.getDeviceId();
|
||||
for (AxisMapping mapping : mControllerAxisMapping) {
|
||||
if (mapping.deviceId != deviceId)
|
||||
continue;
|
||||
|
||||
final float axisValue = event.getAxisValue(mapping.deviceAxisOrButton);
|
||||
float emuValue;
|
||||
|
||||
switch (mapping.deviceAxisOrButton) {
|
||||
case MotionEvent.AXIS_BRAKE:
|
||||
case MotionEvent.AXIS_GAS:
|
||||
case MotionEvent.AXIS_LTRIGGER:
|
||||
case MotionEvent.AXIS_RTRIGGER:
|
||||
// Scale 0..1 -> -1..1.
|
||||
emuValue = (clamp(axisValue, 0.0f, 1.0f) * 2.0f) - 1.0f;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Everything else should already by -1..1 as per Android documentation.
|
||||
emuValue = clamp(axisValue, -1.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
|
||||
Log.d("EmulationSurfaceView", String.format("axis %d value %f emuvalue %f", mapping.deviceAxisOrButton, axisValue, emuValue));
|
||||
|
||||
if (mapping.axisMapping >= 0) {
|
||||
AndroidHostInterface.getInstance().handleControllerAxisEvent(0, mapping.axisMapping, emuValue);
|
||||
}
|
||||
|
||||
final float DEAD_ZONE = 0.25f;
|
||||
if (mapping.negativeButton >= 0) {
|
||||
AndroidHostInterface.getInstance().handleControllerButtonEvent(0, mapping.negativeButton, (emuValue <= -DEAD_ZONE));
|
||||
}
|
||||
if (mapping.positiveButton >= 0) {
|
||||
AndroidHostInterface.getInstance().handleControllerButtonEvent(0, mapping.positiveButton, (emuValue >= DEAD_ZONE));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean addControllerKeyMapping(int deviceId, int keyCode, int controllerIndex) {
|
||||
int mapping = getButtonIndexForKeyCode(keyCode);
|
||||
Log.i("EmulationSurfaceView", String.format("Map %d to %d", keyCode, mapping));
|
||||
if (mapping >= 0) {
|
||||
mControllerKeyMapping.add(new ButtonMapping(deviceId, keyCode, controllerIndex, mapping));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean addControllerAxisMapping(int deviceId, List<InputDevice.MotionRange> motionRanges, int axis, int controllerIndex) {
|
||||
InputDevice.MotionRange range = null;
|
||||
for (InputDevice.MotionRange curRange : motionRanges) {
|
||||
if (curRange.getAxis() == axis) {
|
||||
@@ -157,26 +279,26 @@ public class EmulationSurfaceView extends SurfaceView {
|
||||
}
|
||||
}
|
||||
if (range == null)
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (axisName != null) {
|
||||
int mapping = AndroidHostInterface.getControllerAxisCode(controllerType, axisName);
|
||||
Log.i("EmulationSurfaceView", String.format("Map axis %d to %d (%s)", axis, mapping, axisName));
|
||||
if (mapping >= 0) {
|
||||
mControllerAxisMapping.add(new AxisMapping(deviceId, axis, range, controllerIndex, mapping));
|
||||
return;
|
||||
}
|
||||
int mapping = getAxisIndexForAxisCode(axis);
|
||||
int negativeButton = -1;
|
||||
int positiveButton = -1;
|
||||
|
||||
if (mapping >= 0) {
|
||||
Log.i("EmulationSurfaceView", String.format("Map axis %d to %d", axis, mapping));
|
||||
mControllerAxisMapping.add(new AxisMapping(deviceId, axis, range, controllerIndex, mapping));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (negativeButtonName != null && positiveButtonName != null) {
|
||||
final int negativeMapping = AndroidHostInterface.getControllerButtonCode(controllerType, negativeButtonName);
|
||||
final int positiveMapping = AndroidHostInterface.getControllerButtonCode(controllerType, positiveButtonName);
|
||||
Log.i("EmulationSurfaceView", String.format("Map axis %d to %d %d (button %s %s)", axis, negativeMapping, positiveMapping,
|
||||
negativeButtonName, positiveButtonName));
|
||||
if (negativeMapping >= 0 && positiveMapping >= 0) {
|
||||
mControllerAxisMapping.add(new AxisMapping(deviceId, axis, range, controllerIndex, positiveMapping, negativeMapping));
|
||||
}
|
||||
if (negativeButton >= 0 && negativeButton >= 0) {
|
||||
Log.i("EmulationSurfaceView", String.format("Map axis %d to buttons %d %d", axis, negativeButton, positiveButton));
|
||||
mControllerAxisMapping.add(new AxisMapping(deviceId, axis, range, controllerIndex, positiveButton, negativeButton));
|
||||
return true;
|
||||
}
|
||||
|
||||
Log.w("EmulationSurfaceView", String.format("Axis %d was not mapped", axis));
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean isJoystickDevice(int deviceId) {
|
||||
@@ -194,10 +316,7 @@ public class EmulationSurfaceView extends SurfaceView {
|
||||
if ((sources & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD)
|
||||
return true;
|
||||
|
||||
if ((sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
return (sources & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD;
|
||||
}
|
||||
|
||||
public boolean initControllerMapping(String controllerType) {
|
||||
@@ -213,48 +332,18 @@ public class EmulationSurfaceView extends SurfaceView {
|
||||
List<InputDevice.MotionRange> motionRanges = device.getMotionRanges();
|
||||
int controllerIndex = 0;
|
||||
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_DPAD_UP, controllerIndex, controllerType, "Up");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_DPAD_RIGHT, controllerIndex, controllerType, "Right");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_DPAD_DOWN, controllerIndex, controllerType, "Down");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_DPAD_LEFT, controllerIndex, controllerType, "Left");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_BUTTON_L1, controllerIndex, controllerType, "L1");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_BUTTON_L2, controllerIndex, controllerType, "L2");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_BUTTON_SELECT, controllerIndex, controllerType, "Select");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_BUTTON_START, controllerIndex, controllerType, "Start");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_BUTTON_Y, controllerIndex, controllerType, "Triangle");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_BUTTON_B, controllerIndex, controllerType, "Circle");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_BUTTON_A, controllerIndex, controllerType, "Cross");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_BUTTON_X, controllerIndex, controllerType, "Square");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_BUTTON_R1, controllerIndex, controllerType, "R1");
|
||||
addControllerKeyMapping(deviceId, KeyEvent.KEYCODE_BUTTON_R2, controllerIndex, controllerType, "R2");
|
||||
for (int keyCode : buttonKeyCodes) {
|
||||
addControllerKeyMapping(deviceId, keyCode, controllerIndex);
|
||||
}
|
||||
|
||||
if (motionRanges != null) {
|
||||
addControllerAxisMapping(deviceId, motionRanges, MotionEvent.AXIS_X, controllerIndex, controllerType, "LeftX", null, null);
|
||||
addControllerAxisMapping(deviceId, motionRanges, MotionEvent.AXIS_Y, controllerIndex, controllerType, "LeftY", null, null);
|
||||
addControllerAxisMapping(deviceId, motionRanges, MotionEvent.AXIS_RX, controllerIndex, controllerType, "RightX", null, null);
|
||||
addControllerAxisMapping(deviceId, motionRanges, MotionEvent.AXIS_RY, controllerIndex, controllerType, "RightY", null, null);
|
||||
addControllerAxisMapping(deviceId, motionRanges, MotionEvent.AXIS_Z, controllerIndex, controllerType, "RightX", null, null);
|
||||
addControllerAxisMapping(deviceId, motionRanges, MotionEvent.AXIS_RZ, controllerIndex, controllerType, "RightY", null, null);
|
||||
addControllerAxisMapping(deviceId, motionRanges, MotionEvent.AXIS_LTRIGGER, controllerIndex, controllerType, "L2", "L2", "L2");
|
||||
addControllerAxisMapping(deviceId, motionRanges, MotionEvent.AXIS_RTRIGGER, controllerIndex, controllerType, "R2", "R2", "R2");
|
||||
addControllerAxisMapping(deviceId, motionRanges, MotionEvent.AXIS_HAT_X, controllerIndex, controllerType, null, "Left", "Right");
|
||||
addControllerAxisMapping(deviceId, motionRanges, MotionEvent.AXIS_HAT_Y, controllerIndex, controllerType, null, "Up", "Down");
|
||||
for (int axisCode : axisCodes) {
|
||||
addControllerAxisMapping(deviceId, motionRanges, axisCode, controllerIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !mControllerKeyMapping.isEmpty() || !mControllerKeyMapping.isEmpty();
|
||||
}
|
||||
|
||||
private boolean handleControllerKey(int deviceId, int keyCode, boolean pressed) {
|
||||
boolean result = false;
|
||||
for (ButtonMapping mapping : mControllerKeyMapping) {
|
||||
if (mapping.deviceId != deviceId || mapping.deviceAxisOrButton != keyCode)
|
||||
continue;
|
||||
|
||||
AndroidHostInterface.getInstance().setControllerButtonState(0, mapping.buttonMapping, pressed);
|
||||
Log.d("EmulationSurfaceView", String.format("handleControllerKey %d -> %d %d", keyCode, mapping.buttonMapping, pressed ? 1 : 0));
|
||||
result = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.os.Build;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class EmulationThread extends Thread {
|
||||
private EmulationActivity emulationActivity;
|
||||
private String filename;
|
||||
private boolean resumeState;
|
||||
private String stateFilename;
|
||||
|
||||
private EmulationThread(EmulationActivity emulationActivity, String filename, boolean resumeState, String stateFilename) {
|
||||
super("EmulationThread");
|
||||
this.emulationActivity = emulationActivity;
|
||||
this.filename = filename;
|
||||
this.resumeState = resumeState;
|
||||
this.stateFilename = stateFilename;
|
||||
}
|
||||
|
||||
public static EmulationThread create(EmulationActivity emulationActivity, String filename, boolean resumeState, String stateFilename) {
|
||||
Log.i("EmulationThread", String.format("Starting emulation thread (%s)...", filename));
|
||||
|
||||
EmulationThread thread = new EmulationThread(emulationActivity, filename, resumeState, stateFilename);
|
||||
thread.start();
|
||||
return thread;
|
||||
}
|
||||
|
||||
private void setExclusiveCores() {
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
int[] cores = Process.getExclusiveCores();
|
||||
if (cores == null || cores.length == 0)
|
||||
throw new Exception("Invalid return value from getExclusiveCores()");
|
||||
|
||||
AndroidHostInterface.setThreadAffinity(cores);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e("EmulationThread", "getExclusiveCores() failed");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_MORE_FAVORABLE);
|
||||
setExclusiveCores();
|
||||
} catch (Exception e) {
|
||||
Log.i("EmulationThread", "Failed to set priority for emulation thread: " + e.getMessage());
|
||||
}
|
||||
|
||||
AndroidHostInterface.getInstance().runEmulationThread(emulationActivity, filename, resumeState, stateFilename);
|
||||
Log.i("EmulationThread", "Emulation thread exiting.");
|
||||
}
|
||||
|
||||
public void stopAndJoin() {
|
||||
AndroidHostInterface.getInstance().stopEmulationThreadLoop();
|
||||
try {
|
||||
join();
|
||||
} catch (InterruptedException e) {
|
||||
Log.i("EmulationThread", "join() interrupted: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,14 @@ import android.provider.DocumentsContract;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public final class FileUtil {
|
||||
static String TAG = "TAG";
|
||||
@@ -40,9 +46,11 @@ public final class FileUtil {
|
||||
} else return volumePath;
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
private static String getVolumePath(final String volumeId, Context context) {
|
||||
if (volumeId == null)
|
||||
return null;
|
||||
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return null;
|
||||
try {
|
||||
StorageManager mStorageManager =
|
||||
@@ -91,4 +99,77 @@ public final class FileUtil {
|
||||
if ((split.length >= 2) && (split[1] != null)) return split[1];
|
||||
else return File.separator;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getFullPathFromUri(@Nullable final Uri treeUri, Context con) {
|
||||
if (treeUri == null) return null;
|
||||
String volumePath = getVolumePath(getVolumeIdFromUri(treeUri), con);
|
||||
if (volumePath == null) return File.separator;
|
||||
if (volumePath.endsWith(File.separator))
|
||||
volumePath = volumePath.substring(0, volumePath.length() - 1);
|
||||
|
||||
String documentPath = getDocumentPathFromUri(treeUri);
|
||||
if (documentPath.endsWith(File.separator))
|
||||
documentPath = documentPath.substring(0, documentPath.length() - 1);
|
||||
|
||||
if (documentPath.length() > 0) {
|
||||
if (documentPath.startsWith(File.separator))
|
||||
return volumePath + documentPath;
|
||||
else
|
||||
return volumePath + File.separator + documentPath;
|
||||
} else return volumePath;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private static String getVolumeIdFromUri(final Uri treeUri) {
|
||||
try {
|
||||
final String docId = DocumentsContract.getDocumentId(treeUri);
|
||||
final String[] split = docId.split(":");
|
||||
if (split.length > 0) return split[0];
|
||||
else return null;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
private static String getDocumentPathFromUri(final Uri treeUri) {
|
||||
try {
|
||||
final String docId = DocumentsContract.getDocumentId(treeUri);
|
||||
final String[] split = docId.split(":");
|
||||
if ((split.length >= 2) && (split[1] != null)) return split[1];
|
||||
else return File.separator;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String readFileFromUri(final Context context, final Uri uri, int maxSize) {
|
||||
InputStream stream = null;
|
||||
try {
|
||||
stream = context.getContentResolver().openInputStream(uri);
|
||||
} catch (FileNotFoundException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder os = new StringBuilder();
|
||||
try {
|
||||
char[] buffer = new char[1024];
|
||||
InputStreamReader reader = new InputStreamReader(stream, Charset.forName(StandardCharsets.UTF_8.name()));
|
||||
int len;
|
||||
while ((len = reader.read(buffer)) > 0) {
|
||||
os.append(buffer, 0, len);
|
||||
if (os.length() > maxSize)
|
||||
return null;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (os.length() == 0)
|
||||
return null;
|
||||
|
||||
return os.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,331 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.util.Property;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.ListFragment;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class GameDirectoriesActivity extends AppCompatActivity {
|
||||
private static final int REQUEST_ADD_DIRECTORY_TO_GAME_LIST = 1;
|
||||
|
||||
private class DirectoryListAdapter extends RecyclerView.Adapter {
|
||||
private class Entry {
|
||||
private String mPath;
|
||||
private boolean mRecursive;
|
||||
|
||||
public Entry(String path, boolean recursive) {
|
||||
mPath = path;
|
||||
mRecursive = recursive;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return mPath;
|
||||
}
|
||||
|
||||
public boolean isRecursive() {
|
||||
return mRecursive;
|
||||
}
|
||||
|
||||
public void toggleRecursive() {
|
||||
mRecursive = !mRecursive;
|
||||
}
|
||||
}
|
||||
|
||||
private class EntryComparator implements Comparator<Entry> {
|
||||
@Override
|
||||
public int compare(Entry left, Entry right) {
|
||||
return left.getPath().compareTo(right.getPath());
|
||||
}
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private Entry[] mEntries;
|
||||
|
||||
public DirectoryListAdapter(Context context) {
|
||||
mContext = context;
|
||||
reload();
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
|
||||
ArrayList<Entry> entries = new ArrayList<>();
|
||||
|
||||
try {
|
||||
Set<String> paths = prefs.getStringSet("GameList/Paths", null);
|
||||
if (paths != null) {
|
||||
for (String path : paths)
|
||||
entries.add(new Entry(path, false));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
Set<String> paths = prefs.getStringSet("GameList/RecursivePaths", null);
|
||||
if (paths != null) {
|
||||
for (String path : paths)
|
||||
entries.add(new Entry(path, true));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
mEntries = new Entry[entries.size()];
|
||||
entries.toArray(mEntries);
|
||||
Arrays.sort(mEntries, new EntryComparator());
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
|
||||
private int mPosition;
|
||||
private Entry mEntry;
|
||||
private TextView mPathView;
|
||||
private TextView mRecursiveView;
|
||||
private ImageButton mToggleRecursiveView;
|
||||
private ImageButton mRemoveView;
|
||||
|
||||
public ViewHolder(View rootView) {
|
||||
super(rootView);
|
||||
mPathView = rootView.findViewById(R.id.path);
|
||||
mRecursiveView = rootView.findViewById(R.id.recursive);
|
||||
mToggleRecursiveView = rootView.findViewById(R.id.toggle_recursive);
|
||||
mToggleRecursiveView.setOnClickListener(this);
|
||||
mRemoveView = rootView.findViewById(R.id.remove);
|
||||
mRemoveView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
public void bindData(int position, Entry entry) {
|
||||
mPosition = position;
|
||||
mEntry = entry;
|
||||
updateText();
|
||||
}
|
||||
|
||||
private void updateText() {
|
||||
mPathView.setText(mEntry.getPath());
|
||||
mRecursiveView.setText(getString(mEntry.isRecursive() ? R.string.game_directories_scanning_subdirectories : R.string.game_directories_not_scanning_subdirectories));
|
||||
mToggleRecursiveView.setImageDrawable(getDrawable(mEntry.isRecursive() ? R.drawable.ic_baseline_folder_24 : R.drawable.ic_baseline_folder_open_24));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mToggleRecursiveView == v) {
|
||||
removeSearchDirectory(mContext, mEntry.getPath(), mEntry.isRecursive());
|
||||
mEntry.toggleRecursive();
|
||||
addSearchDirectory(mContext, mEntry.getPath(), mEntry.isRecursive());
|
||||
updateText();
|
||||
} else if (mRemoveView == v) {
|
||||
removeSearchDirectory(mContext, mEntry.getPath(), mEntry.isRecursive());
|
||||
reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
final View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||
((ViewHolder) holder).bindData(position, mEntries[position]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int position) {
|
||||
return R.layout.layout_game_directory_entry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return mEntries[position].getPath().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mEntries.length;
|
||||
}
|
||||
}
|
||||
|
||||
DirectoryListAdapter mDirectoryListAdapter;
|
||||
RecyclerView mRecyclerView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.activity_game_directories);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
mDirectoryListAdapter = new DirectoryListAdapter(this);
|
||||
mRecyclerView = findViewById(R.id.recycler_view);
|
||||
mRecyclerView.setAdapter(mDirectoryListAdapter);
|
||||
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
|
||||
|
||||
findViewById(R.id.fab).setOnClickListener((v) -> startAddGameDirectory());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
getMenuInflater().inflate(R.menu.menu_edit_game_directories, menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (item.getItemId() == R.id.add_directory) {
|
||||
startAddGameDirectory();
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.add_path) {
|
||||
startAddPath();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public static String getPathFromTreeUri(Context context, Uri treeUri) {
|
||||
String path = FileUtil.getFullPathFromTreeUri(treeUri, context);
|
||||
if (path.length() < 5) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.main_activity_error)
|
||||
.setMessage(R.string.main_activity_get_path_from_directory_error)
|
||||
.setPositiveButton(R.string.main_activity_ok, (dialog, button) -> {
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
return null;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static String getPathFromUri(Context context, Uri uri) {
|
||||
String path = FileUtil.getFullPathFromUri(uri, context);
|
||||
if (path.length() < 5) {
|
||||
new AlertDialog.Builder(context)
|
||||
.setTitle(R.string.main_activity_error)
|
||||
.setMessage(R.string.main_activity_get_path_from_file_error)
|
||||
.setPositiveButton(R.string.main_activity_ok, (dialog, button) -> {
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
return null;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static void addSearchDirectory(Context context, String path, boolean recursive) {
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String key = recursive ? "GameList/RecursivePaths" : "GameList/Paths";
|
||||
PreferenceHelpers.addToStringList(prefs, key, path);
|
||||
Log.i("GameDirectoriesActivity", "Added path '" + path + "' to game list search directories");
|
||||
}
|
||||
|
||||
public static void removeSearchDirectory(Context context, String path, boolean recursive) {
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
final String key = recursive ? "GameList/RecursivePaths" : "GameList/Paths";
|
||||
PreferenceHelpers.removeFromStringList(prefs, key, path);
|
||||
Log.i("GameDirectoriesActivity", "Removed path '" + path + "' from game list search directories");
|
||||
}
|
||||
|
||||
private void startAddGameDirectory() {
|
||||
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
i.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
i.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
|
||||
startActivityForResult(Intent.createChooser(i, getString(R.string.main_activity_choose_directory)),
|
||||
REQUEST_ADD_DIRECTORY_TO_GAME_LIST);
|
||||
}
|
||||
|
||||
private void startAddPath() {
|
||||
final EditText text = new EditText(this);
|
||||
text.setSingleLine();
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.edit_game_directories_add_path)
|
||||
.setMessage(R.string.edit_game_directories_add_path_summary)
|
||||
.setView(text)
|
||||
.setPositiveButton("Add", (dialog, which) -> {
|
||||
final String path = text.getText().toString();
|
||||
if (!path.isEmpty()) {
|
||||
addSearchDirectory(GameDirectoriesActivity.this, path, true);
|
||||
mDirectoryListAdapter.reload();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("Cancel", (dialog, which) -> dialog.dismiss())
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
||||
switch (requestCode) {
|
||||
case REQUEST_ADD_DIRECTORY_TO_GAME_LIST: {
|
||||
if (resultCode != RESULT_OK)
|
||||
return;
|
||||
|
||||
String path = getPathFromTreeUri(this, data.getData());
|
||||
if (path == null)
|
||||
return;
|
||||
|
||||
addSearchDirectory(this, path, true);
|
||||
mDirectoryListAdapter.reload();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,22 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.ArraySet;
|
||||
import android.os.AsyncTask;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Set;
|
||||
|
||||
public class GameList {
|
||||
private Context mContext;
|
||||
private Activity mContext;
|
||||
private GameListEntry[] mEntries;
|
||||
private ListViewAdapter mAdapter;
|
||||
|
||||
public GameList(Context context) {
|
||||
public GameList(Activity context) {
|
||||
mContext = context;
|
||||
mAdapter = new ListViewAdapter();
|
||||
mEntries = new GameListEntry[0];
|
||||
@@ -35,12 +30,25 @@ public class GameList {
|
||||
}
|
||||
|
||||
|
||||
public void refresh(boolean invalidateCache, boolean invalidateDatabase) {
|
||||
public void refresh(boolean invalidateCache, boolean invalidateDatabase, Activity parentActivity) {
|
||||
// Search and get entries from native code
|
||||
AndroidHostInterface.getInstance().refreshGameList(invalidateCache, invalidateDatabase);
|
||||
mEntries = AndroidHostInterface.getInstance().getGameListEntries();
|
||||
Arrays.sort(mEntries, new GameListEntryComparator());
|
||||
mAdapter.notifyDataSetChanged();
|
||||
AndroidProgressCallback progressCallback = new AndroidProgressCallback(mContext);
|
||||
AsyncTask.execute(() -> {
|
||||
AndroidHostInterface.getInstance().refreshGameList(invalidateCache, invalidateDatabase, progressCallback);
|
||||
GameListEntry[] newEntries = AndroidHostInterface.getInstance().getGameListEntries();
|
||||
Arrays.sort(newEntries, new GameListEntryComparator());
|
||||
|
||||
mContext.runOnUiThread(() -> {
|
||||
try {
|
||||
progressCallback.dismiss();
|
||||
} catch (Exception e) {
|
||||
Log.e("GameList", "Exception dismissing refresh progress");
|
||||
e.printStackTrace();
|
||||
}
|
||||
mEntries = newEntries;
|
||||
mAdapter.notifyDataSetChanged();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public int getEntryCount() {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.os.AsyncTask;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
@@ -9,7 +10,9 @@ import androidx.core.content.ContextCompat;
|
||||
public class GameListEntry {
|
||||
public enum EntryType {
|
||||
Disc,
|
||||
PSExe
|
||||
PSExe,
|
||||
Playlist,
|
||||
PSF
|
||||
}
|
||||
|
||||
public enum CompatibilityRating {
|
||||
@@ -24,20 +27,23 @@ public class GameListEntry {
|
||||
private String mPath;
|
||||
private String mCode;
|
||||
private String mTitle;
|
||||
private String mFileTitle;
|
||||
private long mSize;
|
||||
private String mModifiedTime;
|
||||
private DiscRegion mRegion;
|
||||
private EntryType mType;
|
||||
private CompatibilityRating mCompatibilityRating;
|
||||
private String mCoverPath;
|
||||
|
||||
|
||||
public GameListEntry(String path, String code, String title, long size, String modifiedTime, String region,
|
||||
String type, String compatibilityRating) {
|
||||
public GameListEntry(String path, String code, String title, String fileTitle, long size, String modifiedTime, String region,
|
||||
String type, String compatibilityRating, String coverPath) {
|
||||
mPath = path;
|
||||
mCode = code;
|
||||
mTitle = title;
|
||||
mFileTitle = fileTitle;
|
||||
mSize = size;
|
||||
mModifiedTime = modifiedTime;
|
||||
mCoverPath = coverPath;
|
||||
|
||||
try {
|
||||
mRegion = DiscRegion.valueOf(region);
|
||||
@@ -70,6 +76,10 @@ public class GameListEntry {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public String getFileTitle() {
|
||||
return mFileTitle;
|
||||
}
|
||||
|
||||
public String getModifiedTime() {
|
||||
return mModifiedTime;
|
||||
}
|
||||
@@ -86,25 +96,39 @@ public class GameListEntry {
|
||||
return mCompatibilityRating;
|
||||
}
|
||||
|
||||
public static String getFileNameForPath(String path) {
|
||||
int lastSlash = path.lastIndexOf('/');
|
||||
if (lastSlash > 0 && lastSlash < path.length() - 1)
|
||||
return path.substring(lastSlash + 1);
|
||||
else
|
||||
return path;
|
||||
}
|
||||
|
||||
private String getSubTitle() {
|
||||
String fileName = getFileNameForPath(mPath);
|
||||
String sizeString = String.format("%.2f MB", (double) mSize / 1048576.0);
|
||||
return String.format("%s (%s)", fileName, sizeString);
|
||||
}
|
||||
|
||||
public void fillView(View view) {
|
||||
((TextView) view.findViewById(R.id.game_list_view_entry_title)).setText(mTitle);
|
||||
((TextView) view.findViewById(R.id.game_list_view_entry_path)).setText(mPath);
|
||||
|
||||
String sizeString = String.format("%.2f MB", (double) mSize / 1048576.0);
|
||||
((TextView) view.findViewById(R.id.game_list_view_entry_size)).setText(sizeString);
|
||||
((TextView) view.findViewById(R.id.game_list_view_entry_subtitle)).setText(getSubTitle());
|
||||
|
||||
int regionDrawableId;
|
||||
switch (mRegion) {
|
||||
case NTSC_J:
|
||||
regionDrawableId = R.drawable.flag_jp;
|
||||
break;
|
||||
case PAL:
|
||||
regionDrawableId = R.drawable.flag_eu;
|
||||
break;
|
||||
case Other:
|
||||
regionDrawableId = R.drawable.ic_baseline_help_24;
|
||||
break;
|
||||
case NTSC_U:
|
||||
default:
|
||||
regionDrawableId = R.drawable.flag_us;
|
||||
break;
|
||||
case PAL:
|
||||
regionDrawableId = R.drawable.flag_eu;
|
||||
break;
|
||||
}
|
||||
|
||||
((ImageView) view.findViewById(R.id.game_list_view_entry_region_icon))
|
||||
@@ -112,17 +136,55 @@ public class GameListEntry {
|
||||
|
||||
int typeDrawableId;
|
||||
switch (mType) {
|
||||
case PSExe:
|
||||
typeDrawableId = R.drawable.ic_emblem_system;
|
||||
break;
|
||||
|
||||
case Playlist:
|
||||
typeDrawableId = R.drawable.ic_baseline_playlist_play_24;
|
||||
break;
|
||||
|
||||
case PSF:
|
||||
typeDrawableId = R.drawable.ic_baseline_library_music_24;
|
||||
break;
|
||||
|
||||
case Disc:
|
||||
default:
|
||||
typeDrawableId = R.drawable.ic_media_cdrom;
|
||||
break;
|
||||
}
|
||||
|
||||
case PSExe:
|
||||
typeDrawableId = R.drawable.ic_emblem_system;
|
||||
ImageView icon = ((ImageView) view.findViewById(R.id.game_list_view_entry_type_icon));
|
||||
icon.setImageDrawable(ContextCompat.getDrawable(view.getContext(), typeDrawableId));
|
||||
|
||||
if (mCoverPath != null) {
|
||||
new ImageLoadTask(icon).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mCoverPath);
|
||||
}
|
||||
|
||||
int compatibilityDrawableId;
|
||||
switch (mCompatibilityRating) {
|
||||
case DoesntBoot:
|
||||
compatibilityDrawableId = R.drawable.ic_star_1;
|
||||
break;
|
||||
case CrashesInIntro:
|
||||
compatibilityDrawableId = R.drawable.ic_star_2;
|
||||
break;
|
||||
case CrashesInGame:
|
||||
compatibilityDrawableId = R.drawable.ic_star_3;
|
||||
break;
|
||||
case GraphicalAudioIssues:
|
||||
compatibilityDrawableId = R.drawable.ic_star_4;
|
||||
break;
|
||||
case NoIssues:
|
||||
compatibilityDrawableId = R.drawable.ic_star_5;
|
||||
break;
|
||||
case Unknown:
|
||||
default:
|
||||
compatibilityDrawableId = R.drawable.ic_star_0;
|
||||
break;
|
||||
}
|
||||
|
||||
((ImageView) view.findViewById(R.id.game_list_view_entry_type_icon))
|
||||
.setImageDrawable(ContextCompat.getDrawable(view.getContext(), typeDrawableId));
|
||||
((ImageView) view.findViewById(R.id.game_list_view_compatibility_icon))
|
||||
.setImageDrawable(ContextCompat.getDrawable(view.getContext(), compatibilityDrawableId));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.util.Property;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.ListFragment;
|
||||
import androidx.preference.PreferenceFragmentCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
import androidx.preference.PreferenceScreen;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import com.google.android.material.tabs.TabLayout;
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class GamePropertiesActivity extends AppCompatActivity {
|
||||
PropertyListAdapter mPropertiesListAdapter;
|
||||
GameListEntry mGameListEntry;
|
||||
|
||||
public ListAdapter getPropertyListAdapter() {
|
||||
if (mPropertiesListAdapter != null)
|
||||
return mPropertiesListAdapter;
|
||||
|
||||
mPropertiesListAdapter = new PropertyListAdapter(this);
|
||||
mPropertiesListAdapter.addItem("title", "Title", mGameListEntry.getTitle());
|
||||
mPropertiesListAdapter.addItem("filetitle", "File Title", mGameListEntry.getFileTitle());
|
||||
mPropertiesListAdapter.addItem("serial", "Serial", mGameListEntry.getCode());
|
||||
mPropertiesListAdapter.addItem("type", "Type", mGameListEntry.getType().toString());
|
||||
mPropertiesListAdapter.addItem("path", "Path", mGameListEntry.getPath());
|
||||
mPropertiesListAdapter.addItem("region", "Region", mGameListEntry.getRegion().toString());
|
||||
mPropertiesListAdapter.addItem("compatibility", "Compatibility Rating", mGameListEntry.getCompatibilityRating().toString());
|
||||
return mPropertiesListAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
String path = getIntent().getStringExtra("path");
|
||||
if (path == null || path.isEmpty()) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
mGameListEntry = AndroidHostInterface.getInstance().getGameListEntry(path);
|
||||
if (mGameListEntry == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
setContentView(R.layout.settings_activity);
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(R.id.settings, new SettingsCollectionFragment(this))
|
||||
.commit();
|
||||
ActionBar actionBar = getSupportActionBar();
|
||||
if (actionBar != null) {
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
|
||||
setTitle(mGameListEntry.getTitle());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private void displayError(String text) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.emulation_activity_error)
|
||||
.setMessage(text)
|
||||
.setNegativeButton(R.string.main_activity_ok, ((dialog, which) -> dialog.dismiss()))
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
private void createBooleanGameSetting(PreferenceScreen ps, String key, int titleId) {
|
||||
GameSettingPreference pref = new GameSettingPreference(ps.getContext(), mGameListEntry.getPath(), key, titleId);
|
||||
ps.addPreference(pref);
|
||||
}
|
||||
|
||||
private void createListGameSetting(PreferenceScreen ps, String key, int titleId, int entryId, int entryValuesId) {
|
||||
GameSettingPreference pref = new GameSettingPreference(ps.getContext(), mGameListEntry.getPath(), key, titleId, entryId, entryValuesId);
|
||||
ps.addPreference(pref);
|
||||
}
|
||||
|
||||
public static class GameSettingsFragment extends PreferenceFragmentCompat {
|
||||
private GamePropertiesActivity activity;
|
||||
|
||||
public GameSettingsFragment(GamePropertiesActivity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
final PreferenceScreen ps = getPreferenceManager().createPreferenceScreen(getContext());
|
||||
activity.createListGameSetting(ps, "CPUOverclock", R.string.settings_cpu_overclocking, R.array.settings_advanced_cpu_overclock_entries, R.array.settings_advanced_cpu_overclock_values);
|
||||
activity.createListGameSetting(ps, "CDROMReadSpeedup", R.string.settings_cdrom_read_speedup, R.array.settings_cdrom_read_speedup_entries, R.array.settings_cdrom_read_speedup_values);
|
||||
|
||||
activity.createListGameSetting(ps, "DisplayAspectRatio", R.string.settings_aspect_ratio, R.array.settings_display_aspect_ratio_names, R.array.settings_display_aspect_ratio_values);
|
||||
activity.createListGameSetting(ps, "DisplayCropMode", R.string.settings_crop_mode, R.array.settings_display_crop_mode_entries, R.array.settings_display_crop_mode_values);
|
||||
activity.createListGameSetting(ps, "GPUDownsampleMode", R.string.settings_downsample_mode, R.array.settings_downsample_mode_entries, R.array.settings_downsample_mode_values);
|
||||
activity.createBooleanGameSetting(ps, "DisplayLinearUpscaling", R.string.settings_linear_upscaling);
|
||||
activity.createBooleanGameSetting(ps, "DisplayIntegerUpscaling", R.string.settings_integer_upscaling);
|
||||
activity.createBooleanGameSetting(ps, "DisplayForce4_3For24Bit", R.string.settings_force_4_3_for_24bit);
|
||||
|
||||
activity.createListGameSetting(ps, "GPUResolutionScale", R.string.settings_gpu_resolution_scale, R.array.settings_gpu_resolution_scale_entries, R.array.settings_gpu_resolution_scale_values);
|
||||
activity.createListGameSetting(ps, "GPUMSAA", R.string.settings_msaa, R.array.settings_gpu_msaa_entries, R.array.settings_gpu_msaa_values);
|
||||
activity.createBooleanGameSetting(ps, "GPUTrueColor", R.string.settings_true_color);
|
||||
activity.createBooleanGameSetting(ps, "GPUScaledDithering", R.string.settings_scaled_dithering);
|
||||
activity.createListGameSetting(ps, "GPUTextureFilter", R.string.settings_texture_filtering, R.array.settings_gpu_texture_filter_names, R.array.settings_gpu_texture_filter_values);
|
||||
activity.createBooleanGameSetting(ps, "GPUForceNTSCTimings", R.string.settings_force_ntsc_timings);
|
||||
activity.createBooleanGameSetting(ps, "GPUWidescreenHack", R.string.settings_widescreen_hack);
|
||||
activity.createBooleanGameSetting(ps, "GPUPGXP", R.string.settings_pgxp_geometry_correction);
|
||||
activity.createBooleanGameSetting(ps, "GPUPGXPDepthBuffer", R.string.settings_pgxp_depth_buffer);
|
||||
|
||||
setPreferenceScreen(ps);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ControllerSettingsFragment extends PreferenceFragmentCompat {
|
||||
private GamePropertiesActivity activity;
|
||||
|
||||
public ControllerSettingsFragment(GamePropertiesActivity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
private void createInputProfileSetting(PreferenceScreen ps) {
|
||||
final GameSettingPreference pref = new GameSettingPreference(ps.getContext(), activity.mGameListEntry.getPath(), "InputProfileName", R.string.settings_input_profile);
|
||||
|
||||
final String[] inputProfileNames = AndroidHostInterface.getInstance().getInputProfileNames();
|
||||
pref.setEntries(inputProfileNames);
|
||||
pref.setEntryValues(inputProfileNames);
|
||||
ps.addPreference(pref);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||
final PreferenceScreen ps = getPreferenceManager().createPreferenceScreen(getContext());
|
||||
|
||||
activity.createListGameSetting(ps, "Controller1Type", R.string.settings_controller_type, R.array.settings_controller_type_entries, R.array.settings_controller_type_values);
|
||||
activity.createListGameSetting(ps, "MemoryCard1Type", R.string.settings_memory_card_1_type, R.array.settings_memory_card_mode_entries, R.array.settings_memory_card_mode_values);
|
||||
activity.createListGameSetting(ps, "MemoryCard2Type", R.string.settings_memory_card_2_type, R.array.settings_memory_card_mode_entries, R.array.settings_memory_card_mode_values);
|
||||
createInputProfileSetting(ps);
|
||||
|
||||
setPreferenceScreen(ps);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SettingsCollectionFragment extends Fragment {
|
||||
private GamePropertiesActivity activity;
|
||||
private SettingsCollectionAdapter adapter;
|
||||
private ViewPager2 viewPager;
|
||||
|
||||
public SettingsCollectionFragment(GamePropertiesActivity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_controller_mapping, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
adapter = new SettingsCollectionAdapter(activity, this);
|
||||
viewPager = view.findViewById(R.id.view_pager);
|
||||
viewPager.setAdapter(adapter);
|
||||
|
||||
TabLayout tabLayout = view.findViewById(R.id.tab_layout);
|
||||
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
|
||||
switch (position) {
|
||||
case 0:
|
||||
tab.setText(R.string.game_properties_tab_summary);
|
||||
break;
|
||||
case 1:
|
||||
tab.setText(R.string.game_properties_tab_game_settings);
|
||||
break;
|
||||
case 2:
|
||||
tab.setText(R.string.game_properties_tab_controller_settings);
|
||||
break;
|
||||
}
|
||||
}).attach();
|
||||
}
|
||||
}
|
||||
|
||||
public static class SettingsCollectionAdapter extends FragmentStateAdapter {
|
||||
private GamePropertiesActivity activity;
|
||||
|
||||
public SettingsCollectionAdapter(@NonNull GamePropertiesActivity activity, @NonNull Fragment fragment) {
|
||||
super(fragment);
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
switch (position) {
|
||||
case 0: { // Summary
|
||||
ListFragment lf = new ListFragment();
|
||||
lf.setListAdapter(activity.getPropertyListAdapter());
|
||||
return lf;
|
||||
}
|
||||
|
||||
case 1: { // Game Settings
|
||||
return new GameSettingsFragment(activity);
|
||||
}
|
||||
|
||||
case 2: { // Controller Settings
|
||||
return new ControllerSettingsFragment(activity);
|
||||
}
|
||||
|
||||
// TODO: Memory Card Editor
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.preference.ListPreference;
|
||||
|
||||
public class GameSettingPreference extends ListPreference {
|
||||
private String mGamePath;
|
||||
|
||||
/**
|
||||
* Creates a boolean game property preference.
|
||||
*/
|
||||
public GameSettingPreference(Context context, String gamePath, String settingKey, int titleId) {
|
||||
super(context);
|
||||
mGamePath = gamePath;
|
||||
setPersistent(false);
|
||||
setTitle(titleId);
|
||||
setKey(settingKey);
|
||||
setIconSpaceReserved(false);
|
||||
setSummaryProvider(SimpleSummaryProvider.getInstance());
|
||||
|
||||
setEntries(R.array.settings_boolean_entries);
|
||||
setEntryValues(R.array.settings_boolean_values);
|
||||
|
||||
updateValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a list game property preference.
|
||||
*/
|
||||
public GameSettingPreference(Context context, String gamePath, String settingKey, int titleId, int entryArray, int entryValuesArray) {
|
||||
super(context);
|
||||
mGamePath = gamePath;
|
||||
setPersistent(false);
|
||||
setTitle(titleId);
|
||||
setKey(settingKey);
|
||||
setIconSpaceReserved(false);
|
||||
setSummaryProvider(SimpleSummaryProvider.getInstance());
|
||||
|
||||
setEntries(entryArray);
|
||||
setEntryValues(entryValuesArray);
|
||||
|
||||
updateValue();
|
||||
}
|
||||
|
||||
private void updateValue() {
|
||||
final String value = AndroidHostInterface.getInstance().getGameSettingValue(mGamePath, getKey());
|
||||
if (value == null)
|
||||
super.setValue("null");
|
||||
else
|
||||
super.setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String value) {
|
||||
super.setValue(value);
|
||||
if (value.equals("null"))
|
||||
AndroidHostInterface.getInstance().setGameSettingValue(mGamePath, getKey(), null);
|
||||
else
|
||||
AndroidHostInterface.getInstance().setGameSettingValue(mGamePath, getKey(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntries(CharSequence[] entries) {
|
||||
final int length = (entries != null) ? entries.length : 0;
|
||||
CharSequence[] newEntries = new CharSequence[length + 1];
|
||||
newEntries[0] = getContext().getString(R.string.game_properties_preference_use_global_setting);
|
||||
if (entries != null)
|
||||
System.arraycopy(entries, 0, newEntries, 1, entries.length);
|
||||
super.setEntries(newEntries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEntryValues(CharSequence[] entryValues) {
|
||||
final int length = (entryValues != null) ? entryValues.length : 0;
|
||||
CharSequence[] newEntryValues = new CharSequence[length + 1];
|
||||
newEntryValues[0] = "null";
|
||||
if (entryValues != null)
|
||||
System.arraycopy(entryValues, 0, newEntryValues, 1, length);
|
||||
super.setEntryValues(newEntryValues);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
public class HotkeyInfo {
|
||||
private String mCategory;
|
||||
private String mName;
|
||||
private String mDisplayName;
|
||||
|
||||
public HotkeyInfo(String category, String name, String displayName) {
|
||||
mCategory = category;
|
||||
mName = name;
|
||||
mDisplayName = displayName;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return mCategory;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return mDisplayName;
|
||||
}
|
||||
|
||||
public String getBindingConfigKey() {
|
||||
return String.format("Hotkeys/%s", mName);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.AsyncTask;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class ImageLoadTask extends AsyncTask<String, Void, Bitmap> {
|
||||
private WeakReference<ImageView> mView;
|
||||
|
||||
public ImageLoadTask(ImageView view) {
|
||||
mView = new WeakReference<>(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap doInBackground(String... strings) {
|
||||
try {
|
||||
return BitmapFactory.decodeFile(strings[0]);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Bitmap bitmap) {
|
||||
ImageView iv = mView.get();
|
||||
if (iv != null)
|
||||
iv.setImageBitmap(bitmap);
|
||||
}
|
||||
}
|
||||
@@ -1,71 +1,116 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.documentfile.provider.DocumentFile;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.collection.ArraySet;
|
||||
|
||||
import android.provider.DocumentsContract;
|
||||
import android.provider.MediaStore;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ListView;
|
||||
import android.widget.PopupMenu;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import static com.google.android.material.snackbar.Snackbar.make;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
private static final int REQUEST_EXTERNAL_STORAGE_PERMISSIONS = 1;
|
||||
private static final int REQUEST_ADD_DIRECTORY_TO_GAME_LIST = 2;
|
||||
private static final int REQUEST_IMPORT_BIOS_IMAGE = 3;
|
||||
private static final int REQUEST_START_FILE = 4;
|
||||
private static final int REQUEST_SETTINGS = 5;
|
||||
private static final int REQUEST_EDIT_GAME_DIRECTORIES = 6;
|
||||
|
||||
private GameList mGameList;
|
||||
private ListView mGameListView;
|
||||
private boolean mHasExternalStoragePermissions = false;
|
||||
|
||||
private void setLanguage() {
|
||||
String language = PreferenceManager.getDefaultSharedPreferences(this).getString("Main/Language", "none");
|
||||
if (language == null || language.equals("none")) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] parts = language.split("-");
|
||||
if (parts.length < 2)
|
||||
return;
|
||||
|
||||
Locale locale = new Locale(parts[0], parts[1]);
|
||||
Locale.setDefault(locale);
|
||||
|
||||
Resources res = getResources();
|
||||
Configuration config = res.getConfiguration();
|
||||
config.setLocale(locale);
|
||||
res.updateConfiguration(config, res.getDisplayMetrics());
|
||||
}
|
||||
|
||||
private void setTheme() {
|
||||
String theme = PreferenceManager.getDefaultSharedPreferences(this).getString("Main/Theme", "follow_system");
|
||||
if (theme == null)
|
||||
return;
|
||||
|
||||
if (theme.equals("follow_system")) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||
} else if (theme.equals("light")) {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
|
||||
} else if (theme.equals("dark")) {
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSettings() {
|
||||
setLanguage();
|
||||
setTheme();
|
||||
}
|
||||
|
||||
private boolean shouldResumeStateByDefault() {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
return prefs.getBoolean("Main/SaveStateOnExit", true);
|
||||
}
|
||||
|
||||
private static String getTitleString() {
|
||||
String scmVersion = AndroidHostInterface.getScmVersion();
|
||||
final int gitHashPos = scmVersion.indexOf("-g");
|
||||
if (gitHashPos > 0)
|
||||
scmVersion = scmVersion.substring(0, gitHashPos);
|
||||
|
||||
return String.format("DuckStation %s", scmVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
loadSettings();
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setTitle(getTitleString());
|
||||
|
||||
findViewById(R.id.fab_add_game_directory).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
@@ -107,6 +152,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
} else if (id == R.id.game_list_entry_menu_resume_game) {
|
||||
startEmulation(mGameList.getEntry(position).getPath(), true);
|
||||
return true;
|
||||
} else if (id == R.id.game_list_entry_menu_properties) {
|
||||
openGameProperties(mGameList.getEntry(position).getPath());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -127,7 +175,8 @@ public class MainActivity extends AppCompatActivity {
|
||||
throw new RuntimeException("Failed to create host interface");
|
||||
}
|
||||
|
||||
mGameList.refresh(false, false);
|
||||
AndroidHostInterface.getInstance().setContext(this);
|
||||
mGameList.refresh(false, false, this);
|
||||
}
|
||||
|
||||
private void startAddGameDirectory() {
|
||||
@@ -137,7 +186,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
|
||||
i.addCategory(Intent.CATEGORY_DEFAULT);
|
||||
i.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
|
||||
startActivityForResult(Intent.createChooser(i, "Choose directory"),
|
||||
startActivityForResult(Intent.createChooser(i, getString(R.string.main_activity_choose_directory)),
|
||||
REQUEST_ADD_DIRECTORY_TO_GAME_LIST);
|
||||
}
|
||||
|
||||
@@ -160,18 +209,35 @@ public class MainActivity extends AppCompatActivity {
|
||||
startEmulation(null, true);
|
||||
} else if (id == R.id.action_start_bios) {
|
||||
startEmulation(null, false);
|
||||
} else if (id == R.id.action_add_game_directory) {
|
||||
startAddGameDirectory();
|
||||
} else if (id == R.id.action_start_file) {
|
||||
startStartFile();
|
||||
} else if (id == R.id.action_edit_game_directories) {
|
||||
Intent intent = new Intent(this, GameDirectoriesActivity.class);
|
||||
startActivityForResult(intent, REQUEST_EDIT_GAME_DIRECTORIES);
|
||||
return true;
|
||||
} else if (id == R.id.action_scan_for_new_games) {
|
||||
mGameList.refresh(false, false);
|
||||
mGameList.refresh(false, false, this);
|
||||
} else if (id == R.id.action_rescan_all_games) {
|
||||
mGameList.refresh(true, true);
|
||||
mGameList.refresh(true, true, this);
|
||||
} else if (id == R.id.action_import_bios) {
|
||||
importBIOSImage();
|
||||
} else if (id == R.id.action_settings) {
|
||||
Intent intent = new Intent(this, SettingsActivity.class);
|
||||
startActivityForResult(intent, REQUEST_SETTINGS);
|
||||
return true;
|
||||
} else if (id == R.id.action_controller_mapping) {
|
||||
Intent intent = new Intent(this, ControllerMappingActivity.class);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
} else if (id == R.id.action_show_version) {
|
||||
showVersion();
|
||||
return true;
|
||||
} else if (id == R.id.action_github_respository) {
|
||||
openGithubRepository();
|
||||
return true;
|
||||
} else if (id == R.id.action_discord_server) {
|
||||
openDiscordServer();
|
||||
return true;
|
||||
}
|
||||
|
||||
return super.onOptionsItemSelected(item);
|
||||
@@ -186,31 +252,12 @@ public class MainActivity extends AppCompatActivity {
|
||||
if (resultCode != RESULT_OK)
|
||||
return;
|
||||
|
||||
Uri treeUri = data.getData();
|
||||
String path = FileUtil.getFullPathFromTreeUri(treeUri, this);
|
||||
if (path.length() < 5) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Error")
|
||||
.setMessage("Failed to get path for the selected directory. Please make sure the directory is in internal/external storage.\n\n" +
|
||||
"Tap the overflow button in the directory selector.\nSelect \"Show Internal Storage\".\n" +
|
||||
"Tap the menu button and select your device name or SD card.")
|
||||
.setPositiveButton("OK", (dialog, button) -> {})
|
||||
.create()
|
||||
.show();
|
||||
String path = GameDirectoriesActivity.getPathFromTreeUri(this, data.getData());
|
||||
if (path == null)
|
||||
return;
|
||||
}
|
||||
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
Set<String> currentValues = prefs.getStringSet("GameList/RecursivePaths", null);
|
||||
if (currentValues == null)
|
||||
currentValues = new HashSet<String>();
|
||||
|
||||
currentValues.add(path);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putStringSet("GameList/RecursivePaths", currentValues);
|
||||
editor.apply();
|
||||
Log.i("MainActivity", "Added path '" + path + "' to game list search directories");
|
||||
mGameList.refresh(false, false);
|
||||
GameDirectoriesActivity.addSearchDirectory(this, path, true);
|
||||
mGameList.refresh(false, false, this);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -221,6 +268,28 @@ public class MainActivity extends AppCompatActivity {
|
||||
onImportBIOSImageResult(data.getData());
|
||||
}
|
||||
break;
|
||||
|
||||
case REQUEST_START_FILE: {
|
||||
if (resultCode != RESULT_OK)
|
||||
return;
|
||||
|
||||
String path = GameDirectoriesActivity.getPathFromUri(this, data.getData());
|
||||
if (path == null)
|
||||
return;
|
||||
|
||||
startEmulation(path, shouldResumeStateByDefault());
|
||||
}
|
||||
break;
|
||||
|
||||
case REQUEST_SETTINGS: {
|
||||
loadSettings();
|
||||
}
|
||||
break;
|
||||
|
||||
case REQUEST_EDIT_GAME_DIRECTORIES: {
|
||||
mGameList.refresh(false, false, this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,13 +320,20 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(this,
|
||||
"External storage permissions are required to use DuckStation.",
|
||||
R.string.main_activity_external_storage_permissions_error,
|
||||
Toast.LENGTH_LONG);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean openGameProperties(String path) {
|
||||
Intent intent = new Intent(this, GamePropertiesActivity.class);
|
||||
intent.putExtra("path", path);
|
||||
startActivity(intent);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean startEmulation(String bootPath, boolean resumeState) {
|
||||
if (!doBIOSCheck())
|
||||
return false;
|
||||
@@ -269,15 +345,23 @@ public class MainActivity extends AppCompatActivity {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void startStartFile() {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("*/*");
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.main_activity_choose_disc_image)), REQUEST_START_FILE);
|
||||
}
|
||||
|
||||
private boolean doBIOSCheck() {
|
||||
if (AndroidHostInterface.getInstance().hasAnyBIOSImages())
|
||||
return true;
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Missing BIOS Image")
|
||||
.setMessage("No BIOS image was found in DuckStation's bios directory. Do you with to locate and import a BIOS image now?")
|
||||
.setPositiveButton("Yes", (dialog, button) -> importBIOSImage())
|
||||
.setNegativeButton("No", (dialog, button) -> {})
|
||||
.setTitle(R.string.main_activity_missing_bios_image)
|
||||
.setMessage(R.string.main_activity_missing_bios_image_prompt)
|
||||
.setPositiveButton(R.string.main_activity_yes, (dialog, button) -> importBIOSImage())
|
||||
.setNegativeButton(R.string.main_activity_no, (dialog, button) -> {
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
|
||||
@@ -288,7 +372,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||
intent.setType("*/*");
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
startActivityForResult(Intent.createChooser(intent, "Choose BIOS Image"), REQUEST_IMPORT_BIOS_IMAGE);
|
||||
startActivityForResult(Intent.createChooser(intent, getString(R.string.main_activity_choose_bios_image)), REQUEST_IMPORT_BIOS_IMAGE);
|
||||
}
|
||||
|
||||
private void onImportBIOSImageResult(Uri uri) {
|
||||
@@ -299,7 +383,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
try {
|
||||
stream = getContentResolver().openInputStream(uri);
|
||||
} catch (FileNotFoundException e) {
|
||||
Toast.makeText(this, "Failed to open BIOS image.", Toast.LENGTH_LONG);
|
||||
Toast.makeText(this, R.string.main_activity_failed_to_open_bios_image, Toast.LENGTH_LONG);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -310,25 +394,55 @@ public class MainActivity extends AppCompatActivity {
|
||||
while ((len = stream.read(buffer)) > 0) {
|
||||
os.write(buffer, 0, len);
|
||||
if (os.size() > MAX_BIOS_SIZE) {
|
||||
throw new IOException("BIOS image is too large.");
|
||||
throw new IOException(getString(R.string.main_activity_bios_image_too_large));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage("Failed to read BIOS image: " + e.getMessage())
|
||||
.setPositiveButton("OK", (dialog, button) -> {})
|
||||
.setMessage(getString(R.string.main_activity_failed_to_read_bios_image_prefix) + e.getMessage())
|
||||
.setPositiveButton(R.string.main_activity_ok, (dialog, button) -> {
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
return;
|
||||
}
|
||||
|
||||
String importResult = AndroidHostInterface.getInstance().importBIOSImage(os.toByteArray());
|
||||
String message = (importResult == null) ? "This BIOS image is invalid, or has already been imported." : ("BIOS '" + importResult + "' imported.");
|
||||
String message = (importResult == null) ? getString(R.string.main_activity_invalid_error) : ("BIOS '" + importResult + "' imported.");
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage(message)
|
||||
.setPositiveButton("OK", (dialog, button) -> {})
|
||||
.create()
|
||||
.show();
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.main_activity_ok, (dialog, button) -> {
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
private void showVersion() {
|
||||
final String message = AndroidHostInterface.getFullScmVersion();
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.main_activity_show_version_title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.main_activity_ok, (dialog, button) -> {
|
||||
})
|
||||
.setNeutralButton(R.string.main_activity_copy, (dialog, button) -> {
|
||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
|
||||
if (clipboard != null)
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText(getString(R.string.main_activity_show_version_title), message));
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
private void openGithubRepository() {
|
||||
final String url = "https://github.com/stenzek/duckstation";
|
||||
final Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
|
||||
private void openDiscordServer() {
|
||||
final String url = "https://discord.gg/Buktv3t";
|
||||
final Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
startActivity(browserIntent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
public class PatchCode {
|
||||
private int mIndex;
|
||||
private String mDescription;
|
||||
private boolean mEnabled;
|
||||
|
||||
public PatchCode(int index, String description, boolean enabled) {
|
||||
mIndex = index;
|
||||
mDescription = description;
|
||||
mEnabled = enabled;
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return mIndex;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return mEnabled;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class PreferenceHelpers {
|
||||
/**
|
||||
* Clears all preferences in the specified section (starting with sectionName/).
|
||||
* We really don't want to have to do this with JNI...
|
||||
*
|
||||
* @param prefs Preferences object.
|
||||
* @param sectionName Section to clear keys for.
|
||||
*/
|
||||
public static void clearSection(SharedPreferences prefs, String sectionName) {
|
||||
String testSectionName = sectionName + "/";
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
for (String keyName : prefs.getAll().keySet()) {
|
||||
if (keyName.startsWith(testSectionName)) {
|
||||
editor.remove(keyName);
|
||||
}
|
||||
}
|
||||
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
public static Set<String> getStringSet(SharedPreferences prefs, String keyName) {
|
||||
Set<String> values = null;
|
||||
try {
|
||||
values = prefs.getStringSet(keyName, null);
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
String singleValue = prefs.getString(keyName, null);
|
||||
if (singleValue != null) {
|
||||
values = new ArraySet<>();
|
||||
values.add(singleValue);
|
||||
}
|
||||
} catch (Exception e2) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
public static boolean addToStringList(SharedPreferences prefs, String keyName, String valueToAdd) {
|
||||
Set<String> values = getStringSet(prefs, keyName);
|
||||
if (values == null) {
|
||||
values = new ArraySet<>();
|
||||
} else {
|
||||
// We need to copy it otherwise the put doesn't save.
|
||||
Set<String> valuesCopy = new ArraySet<>();
|
||||
valuesCopy.addAll(values);
|
||||
values = valuesCopy;
|
||||
}
|
||||
|
||||
final boolean result = values.add(valueToAdd);
|
||||
prefs.edit().putStringSet(keyName, values).commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static boolean removeFromStringList(SharedPreferences prefs, String keyName, String valueToRemove) {
|
||||
Set<String> values = getStringSet(prefs, keyName);
|
||||
if (values == null)
|
||||
return false;
|
||||
|
||||
// We need to copy it otherwise the put doesn't save.
|
||||
Set<String> valuesCopy = new ArraySet<>();
|
||||
valuesCopy.addAll(values);
|
||||
values = valuesCopy;
|
||||
|
||||
final boolean result = values.remove(valueToRemove);
|
||||
prefs.edit().putStringSet(keyName, values).commit();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void setStringList(SharedPreferences prefs, String keyName, String[] values) {
|
||||
Set<String> valueSet = new ArraySet<String>();
|
||||
for (String value : values)
|
||||
valueSet.add(value);
|
||||
|
||||
prefs.edit().putStringSet(keyName, valueSet).commit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class PropertyListAdapter extends BaseAdapter {
|
||||
private class Item {
|
||||
public String key;
|
||||
public String title;
|
||||
public String value;
|
||||
|
||||
Item(String key, String title, String value) {
|
||||
this.key = key;
|
||||
this.title = title;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Context mContext;
|
||||
private ArrayList<Item> mItems = new ArrayList<>();
|
||||
|
||||
public PropertyListAdapter(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public Item getItemByKey(String key) {
|
||||
for (Item it : mItems) {
|
||||
if (it.key.equals(key))
|
||||
return it;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public int addItem(String key, String title, String value) {
|
||||
if (getItemByKey(key) != null)
|
||||
return -1;
|
||||
|
||||
Item it = new Item(key, title, value);
|
||||
int position = mItems.size();
|
||||
mItems.add(it);
|
||||
return position;
|
||||
}
|
||||
|
||||
public boolean removeItem(Item item) {
|
||||
return mItems.remove(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mItems.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return mItems.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(mContext)
|
||||
.inflate(R.layout.layout_game_property_entry, parent, false);
|
||||
}
|
||||
|
||||
TextView titleView = (TextView) convertView.findViewById(R.id.property_title);
|
||||
TextView valueView = (TextView) convertView.findViewById(R.id.property_value);
|
||||
Item prop = mItems.get(position);
|
||||
titleView.setText(prop.title);
|
||||
valueView.setText(prop.value);
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class SaveStateInfo {
|
||||
private String mPath;
|
||||
private String mGameTitle;
|
||||
private String mGameCode;
|
||||
private String mMediaPath;
|
||||
private String mTimestamp;
|
||||
private int mSlot;
|
||||
private boolean mGlobal;
|
||||
private Bitmap mScreenshot;
|
||||
|
||||
public SaveStateInfo(String path, String gameTitle, String gameCode, String mediaPath, String timestamp, int slot, boolean global,
|
||||
int screenshotWidth, int screenshotHeight, byte[] screenshotData) {
|
||||
mPath = path;
|
||||
mGameTitle = gameTitle;
|
||||
mGameCode = gameCode;
|
||||
mMediaPath = mediaPath;
|
||||
mTimestamp = timestamp;
|
||||
mSlot = slot;
|
||||
mGlobal = global;
|
||||
|
||||
if (screenshotData != null) {
|
||||
try {
|
||||
mScreenshot = Bitmap.createBitmap(screenshotWidth, screenshotHeight, Bitmap.Config.ARGB_8888);
|
||||
mScreenshot.copyPixelsFromBuffer(ByteBuffer.wrap(screenshotData));
|
||||
} catch (Exception e) {
|
||||
mScreenshot = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean exists() {
|
||||
return mPath != null;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return mPath;
|
||||
}
|
||||
|
||||
public String getGameTitle() {
|
||||
return mGameTitle;
|
||||
}
|
||||
|
||||
public String getGameCode() {
|
||||
return mGameCode;
|
||||
}
|
||||
|
||||
public String getMediaPath() {
|
||||
return mMediaPath;
|
||||
}
|
||||
|
||||
public String getTimestamp() {
|
||||
return mTimestamp;
|
||||
}
|
||||
|
||||
public int getSlot() {
|
||||
return mSlot;
|
||||
}
|
||||
|
||||
public boolean isGlobal() {
|
||||
return mGlobal;
|
||||
}
|
||||
|
||||
public Bitmap getScreenshot() {
|
||||
return mScreenshot;
|
||||
}
|
||||
|
||||
private void fillView(Context context, View view) {
|
||||
ImageView imageView = (ImageView) view.findViewById(R.id.image);
|
||||
TextView summaryView = (TextView) view.findViewById(R.id.summary);
|
||||
TextView gameView = (TextView) view.findViewById(R.id.game);
|
||||
TextView pathView = (TextView) view.findViewById(R.id.path);
|
||||
TextView timestampView = (TextView) view.findViewById(R.id.timestamp);
|
||||
|
||||
if (mScreenshot != null)
|
||||
imageView.setImageBitmap(mScreenshot);
|
||||
else
|
||||
imageView.setImageDrawable(context.getDrawable(R.drawable.ic_baseline_not_interested_60));
|
||||
|
||||
String summaryText;
|
||||
if (mGlobal)
|
||||
summaryView.setText(String.format(context.getString(R.string.save_state_info_global_save_n), mSlot));
|
||||
else if (mSlot == 0)
|
||||
summaryView.setText(R.string.save_state_info_quick_save);
|
||||
else
|
||||
summaryView.setText(String.format(context.getString(R.string.save_state_info_game_save_n), mSlot));
|
||||
|
||||
if (exists()) {
|
||||
gameView.setText(String.format("%s - %s", mGameCode, mGameTitle));
|
||||
|
||||
int lastSlashPosition = mMediaPath.lastIndexOf('/');
|
||||
if (lastSlashPosition >= 0)
|
||||
pathView.setText(mMediaPath.substring(lastSlashPosition + 1));
|
||||
else
|
||||
pathView.setText(mMediaPath);
|
||||
|
||||
timestampView.setText(mTimestamp);
|
||||
} else {
|
||||
gameView.setText(R.string.save_state_info_slot_is_empty);
|
||||
pathView.setText("");
|
||||
timestampView.setText("");
|
||||
}
|
||||
}
|
||||
|
||||
public static class ListAdapter extends BaseAdapter {
|
||||
private final Context mContext;
|
||||
private final SaveStateInfo[] mInfos;
|
||||
|
||||
public ListAdapter(Context context, SaveStateInfo[] infos) {
|
||||
mContext = context;
|
||||
mInfos = infos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mInfos.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return mInfos[position];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(mContext).inflate(R.layout.save_state_view_entry, parent, false);
|
||||
}
|
||||
|
||||
mInfos[position].fillView(mContext, convertView);
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
public static class SettingsFragment extends PreferenceFragmentCompat {
|
||||
private int resourceId;
|
||||
|
||||
public SettingsFragment(int resourceId) {
|
||||
this.resourceId = resourceId;
|
||||
}
|
||||
@@ -113,7 +114,7 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return 5;
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,9 @@ import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
|
||||
public class TouchscreenControllerAxisView extends View {
|
||||
public final class TouchscreenControllerAxisView extends View {
|
||||
private Drawable mBaseDrawable;
|
||||
private Drawable mStickUnpressedDrawable;
|
||||
private Drawable mStickPressedDrawable;
|
||||
@@ -18,6 +17,7 @@ public class TouchscreenControllerAxisView extends View {
|
||||
private int mDrawXPos = 0;
|
||||
private int mDrawYPos = 0;
|
||||
|
||||
private String mConfigName;
|
||||
private int mControllerIndex = -1;
|
||||
private int mXAxisCode = -1;
|
||||
private int mYAxisCode = -1;
|
||||
@@ -50,6 +50,14 @@ public class TouchscreenControllerAxisView extends View {
|
||||
mStickPressedDrawable.setCallback(this);
|
||||
}
|
||||
|
||||
public String getConfigName() {
|
||||
return mConfigName;
|
||||
}
|
||||
|
||||
public void setConfigName(String configName) {
|
||||
mConfigName = configName;
|
||||
}
|
||||
|
||||
public void setControllerAxis(int controllerIndex, int xCode, int yCode) {
|
||||
mControllerIndex = controllerIndex;
|
||||
mXAxisCode = xCode;
|
||||
@@ -84,21 +92,21 @@ public class TouchscreenControllerAxisView extends View {
|
||||
}
|
||||
|
||||
public void setPressed(int pointerId, float pointerX, float pointerY) {
|
||||
final float dx = pointerX - (float)(getX() + (float)(getWidth() / 2));
|
||||
final float dy = pointerY - (float)(getY() + (float)(getHeight() / 2));
|
||||
final float dx = pointerX - (float) (getX() + (float) (getWidth() / 2));
|
||||
final float dy = pointerY - (float) (getY() + (float) (getHeight() / 2));
|
||||
// Log.i("SetPressed", String.format("px=%f,py=%f dx=%f,dy=%f", pointerX, pointerY, dx, dy));
|
||||
|
||||
final float pointerDistance = Math.max(Math.abs(dx), Math.abs(dy));
|
||||
final float angle = (float)Math.atan2((double)dy, (double)dx);
|
||||
final float angle = (float) Math.atan2((double) dy, (double) dx);
|
||||
|
||||
final float maxDistance = (float)Math.min((getWidth() - getPaddingLeft() - getPaddingRight()) / 2, (getHeight() - getPaddingTop() - getPaddingBottom()) / 2);
|
||||
final float maxDistance = (float) Math.min((getWidth() - getPaddingLeft() - getPaddingRight()) / 2, (getHeight() - getPaddingTop() - getPaddingBottom()) / 2);
|
||||
final float length = Math.min(pointerDistance / maxDistance, 1.0f);
|
||||
// Log.i("SetPressed", String.format("pointerDist=%f,angle=%f,w=%d,h=%d,maxDist=%f,length=%f", pointerDistance, angle, getWidth(), getHeight(), maxDistance, length));
|
||||
|
||||
final float xValue = (float)Math.cos((double)angle) * length;
|
||||
final float yValue = (float)Math.sin((double)angle) * length;
|
||||
mDrawXPos = (int)(xValue * maxDistance);
|
||||
mDrawYPos = (int)(yValue * maxDistance);
|
||||
final float xValue = (float) Math.cos((double) angle) * length;
|
||||
final float yValue = (float) Math.sin((double) angle) * length;
|
||||
mDrawXPos = (int) (xValue * maxDistance);
|
||||
mDrawYPos = (int) (yValue * maxDistance);
|
||||
|
||||
boolean doUpdate = (pointerId != mPointerId || !mPressed || (xValue != mXValue || yValue != mYValue));
|
||||
mPointerId = pointerId;
|
||||
@@ -128,7 +136,7 @@ public class TouchscreenControllerAxisView extends View {
|
||||
hostInterface.setControllerButtonState(mControllerIndex, mRightButtonCode, (mXValue >= BUTTON_THRESHOLD));
|
||||
if (mUpButtonCode >= 0)
|
||||
hostInterface.setControllerButtonState(mControllerIndex, mUpButtonCode, (mYValue <= -BUTTON_THRESHOLD));
|
||||
if (mDownButtonCode >= 0)
|
||||
if (mDownButtonCode >= 0)
|
||||
hostInterface.setControllerButtonState(mControllerIndex, mDownButtonCode, (mYValue >= BUTTON_THRESHOLD));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,19 +5,20 @@ import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.HapticFeedbackConstants;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* TODO: document your custom view class.
|
||||
*/
|
||||
public class TouchscreenControllerButtonView extends View {
|
||||
public final class TouchscreenControllerButtonView extends View {
|
||||
private Drawable mUnpressedDrawable;
|
||||
private Drawable mPressedDrawable;
|
||||
private boolean mPressed = false;
|
||||
private boolean mHapticFeedback = false;
|
||||
private int mControllerIndex = -1;
|
||||
private int mButtonCode = -1;
|
||||
private String mConfigName;
|
||||
|
||||
public TouchscreenControllerButtonView(Context context) {
|
||||
super(context);
|
||||
@@ -83,6 +84,10 @@ public class TouchscreenControllerButtonView extends View {
|
||||
mPressed = pressed;
|
||||
invalidate();
|
||||
updateControllerState();
|
||||
|
||||
if (mHapticFeedback) {
|
||||
performHapticFeedback(pressed ? HapticFeedbackConstants.VIRTUAL_KEY : HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
|
||||
}
|
||||
}
|
||||
|
||||
public void setButtonCode(int controllerIndex, int code) {
|
||||
@@ -90,6 +95,18 @@ public class TouchscreenControllerButtonView extends View {
|
||||
mButtonCode = code;
|
||||
}
|
||||
|
||||
public void setConfigName(String name) {
|
||||
mConfigName = name;
|
||||
}
|
||||
|
||||
public String getConfigName() {
|
||||
return mConfigName;
|
||||
}
|
||||
|
||||
public void setHapticFeedback(boolean enabled) {
|
||||
mHapticFeedback = enabled;
|
||||
}
|
||||
|
||||
private void updateControllerState() {
|
||||
if (mButtonCode >= 0)
|
||||
AndroidHostInterface.getInstance().setControllerButtonState(mControllerIndex, mButtonCode, mPressed);
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
package com.github.stenzek.duckstation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -17,12 +26,23 @@ import java.util.ArrayList;
|
||||
public class TouchscreenControllerView extends FrameLayout {
|
||||
private int mControllerIndex;
|
||||
private String mControllerType;
|
||||
private String mViewType;
|
||||
private View mMainView;
|
||||
private ArrayList<TouchscreenControllerButtonView> mButtonViews = new ArrayList<>();
|
||||
private ArrayList<TouchscreenControllerAxisView> mAxisViews = new ArrayList<>();
|
||||
private boolean mHapticFeedback;
|
||||
private String mLayoutOrientation;
|
||||
private boolean mEditingLayout = false;
|
||||
private View mMovingView = null;
|
||||
private String mMovingName = null;
|
||||
private float mMovingLastX = 0.0f;
|
||||
private float mMovingLastY = 0.0f;
|
||||
private ConstraintLayout mEditLayout = null;
|
||||
|
||||
public TouchscreenControllerView(Context context) {
|
||||
super(context);
|
||||
setFocusable(false);
|
||||
setFocusableInTouchMode(false);
|
||||
}
|
||||
|
||||
public TouchscreenControllerView(Context context, AttributeSet attrs) {
|
||||
@@ -33,17 +53,108 @@ public class TouchscreenControllerView extends FrameLayout {
|
||||
super(context, attrs, defStyle);
|
||||
}
|
||||
|
||||
public void init(int controllerIndex, String controllerType, String viewType) {
|
||||
private String getConfigKeyForXTranslation(String name) {
|
||||
return String.format("TouchscreenController/%s/%s%sXTranslation", mViewType, name, mLayoutOrientation);
|
||||
}
|
||||
|
||||
private String getConfigKeyForYTranslation(String name) {
|
||||
return String.format("TouchscreenController/%s/%s%sYTranslation", mViewType, name, mLayoutOrientation);
|
||||
}
|
||||
|
||||
private void saveTranslationForButton(String name, float xTranslation, float yTranslation) {
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
final SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.putFloat(getConfigKeyForXTranslation(name), xTranslation);
|
||||
editor.putFloat(getConfigKeyForYTranslation(name), yTranslation);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private void clearTranslationForAllButtons() {
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
final SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
for (TouchscreenControllerButtonView buttonView : mButtonViews) {
|
||||
editor.remove(getConfigKeyForXTranslation(buttonView.getConfigName()));
|
||||
editor.remove(getConfigKeyForYTranslation(buttonView.getConfigName()));
|
||||
buttonView.setTranslationX(0.0f);
|
||||
buttonView.setTranslationY(0.0f);
|
||||
}
|
||||
|
||||
for (TouchscreenControllerAxisView axisView : mAxisViews) {
|
||||
editor.remove(getConfigKeyForXTranslation(axisView.getConfigName()));
|
||||
editor.remove(getConfigKeyForYTranslation(axisView.getConfigName()));
|
||||
axisView.setTranslationX(0.0f);
|
||||
axisView.setTranslationY(0.0f);
|
||||
}
|
||||
|
||||
editor.commit();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
private void reloadButtonTranslation() {
|
||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
|
||||
for (TouchscreenControllerButtonView buttonView : mButtonViews) {
|
||||
try {
|
||||
buttonView.setTranslationX(prefs.getFloat(getConfigKeyForXTranslation(buttonView.getConfigName()), 0.0f));
|
||||
buttonView.setTranslationY(prefs.getFloat(getConfigKeyForYTranslation(buttonView.getConfigName()), 0.0f));
|
||||
//Log.i("TouchscreenController", String.format("Translation for %s %f %f", buttonView.getConfigName(),
|
||||
// buttonView.getTranslationX(), buttonView.getTranslationY()));
|
||||
} catch (ClassCastException ex) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (TouchscreenControllerAxisView axisView : mAxisViews) {
|
||||
try {
|
||||
axisView.setTranslationX(prefs.getFloat(getConfigKeyForXTranslation(axisView.getConfigName()), 0.0f));
|
||||
axisView.setTranslationY(prefs.getFloat(getConfigKeyForYTranslation(axisView.getConfigName()), 0.0f));
|
||||
} catch (ClassCastException ex) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getOrientationString() {
|
||||
switch (getContext().getResources().getConfiguration().orientation) {
|
||||
case Configuration.ORIENTATION_PORTRAIT:
|
||||
return "Portrait";
|
||||
case Configuration.ORIENTATION_LANDSCAPE:
|
||||
default:
|
||||
return "Landscape";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the orientation of the layout has changed, and if so, reloads button translations.
|
||||
*/
|
||||
public void updateOrientation() {
|
||||
String newOrientation = getOrientationString();
|
||||
if (mLayoutOrientation != null && mLayoutOrientation.equals(newOrientation))
|
||||
return;
|
||||
|
||||
Log.i("TouchscreenController", "New orientation: " + newOrientation);
|
||||
mLayoutOrientation = newOrientation;
|
||||
reloadButtonTranslation();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void init(int controllerIndex, String controllerType, String viewType, boolean hapticFeedback) {
|
||||
mControllerIndex = controllerIndex;
|
||||
mControllerType = controllerType;
|
||||
mViewType = viewType;
|
||||
mHapticFeedback = hapticFeedback;
|
||||
mLayoutOrientation = getOrientationString();
|
||||
|
||||
if (mEditingLayout)
|
||||
endLayoutEditing();
|
||||
|
||||
mButtonViews.clear();
|
||||
mAxisViews.clear();
|
||||
removeAllViews();
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
switch (viewType)
|
||||
{
|
||||
switch (viewType) {
|
||||
case "digital":
|
||||
mMainView = inflater.inflate(R.layout.layout_touchscreen_controller_digital, this, true);
|
||||
break;
|
||||
@@ -66,40 +177,49 @@ public class TouchscreenControllerView extends FrameLayout {
|
||||
return;
|
||||
|
||||
mMainView.setOnTouchListener((view1, event) -> {
|
||||
return handleTouchEvent(event);
|
||||
if (mEditingLayout)
|
||||
return handleEditingTouchEvent(event);
|
||||
else
|
||||
return handleTouchEvent(event);
|
||||
});
|
||||
|
||||
linkButton(mMainView, R.id.controller_button_up, "Up");
|
||||
linkButton(mMainView, R.id.controller_button_right, "Right");
|
||||
linkButton(mMainView, R.id.controller_button_down, "Down");
|
||||
linkButton(mMainView, R.id.controller_button_left, "Left");
|
||||
linkButton(mMainView, R.id.controller_button_l1, "L1");
|
||||
linkButton(mMainView, R.id.controller_button_l2, "L2");
|
||||
linkButton(mMainView, R.id.controller_button_select, "Select");
|
||||
linkButton(mMainView, R.id.controller_button_start, "Start");
|
||||
linkButton(mMainView, R.id.controller_button_triangle, "Triangle");
|
||||
linkButton(mMainView, R.id.controller_button_circle, "Circle");
|
||||
linkButton(mMainView, R.id.controller_button_cross, "Cross");
|
||||
linkButton(mMainView, R.id.controller_button_square, "Square");
|
||||
linkButton(mMainView, R.id.controller_button_r1, "R1");
|
||||
linkButton(mMainView, R.id.controller_button_r2, "R2");
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
|
||||
|
||||
if (!linkAxis(mMainView, R.id.controller_axis_left, "Left"))
|
||||
linkAxisToButtons(mMainView, R.id.controller_axis_left, "");
|
||||
linkButton(mMainView, R.id.controller_button_up, "UpButton", "Up");
|
||||
linkButton(mMainView, R.id.controller_button_right, "RightButton", "Right");
|
||||
linkButton(mMainView, R.id.controller_button_down, "DownButton", "Down");
|
||||
linkButton(mMainView, R.id.controller_button_left, "LeftButton", "Left");
|
||||
linkButton(mMainView, R.id.controller_button_l1, "L1Button", "L1");
|
||||
linkButton(mMainView, R.id.controller_button_l2, "L2Button", "L2");
|
||||
linkButton(mMainView, R.id.controller_button_select, "SelectButton", "Select");
|
||||
linkButton(mMainView, R.id.controller_button_start, "StartButton", "Start");
|
||||
linkButton(mMainView, R.id.controller_button_triangle, "TriangleButton", "Triangle");
|
||||
linkButton(mMainView, R.id.controller_button_circle, "CircleButton", "Circle");
|
||||
linkButton(mMainView, R.id.controller_button_cross, "CrossButton", "Cross");
|
||||
linkButton(mMainView, R.id.controller_button_square, "SquareButton", "Square");
|
||||
linkButton(mMainView, R.id.controller_button_r1, "R1Button", "R1");
|
||||
linkButton(mMainView, R.id.controller_button_r2, "R2Button", "R2");
|
||||
|
||||
linkAxis(mMainView, R.id.controller_axis_right, "Right");
|
||||
if (!linkAxis(mMainView, R.id.controller_axis_left, "LeftAxis", "Left"))
|
||||
linkAxisToButtons(mMainView, R.id.controller_axis_left, "LeftAxis", "");
|
||||
|
||||
linkAxis(mMainView, R.id.controller_axis_right, "RightAxis", "Right");
|
||||
reloadButtonTranslation();
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
private void linkButton(View view, int id, String buttonName) {
|
||||
private void linkButton(View view, int id, String configName, String buttonName) {
|
||||
TouchscreenControllerButtonView buttonView = (TouchscreenControllerButtonView) view.findViewById(id);
|
||||
if (buttonView == null)
|
||||
return;
|
||||
|
||||
int code = AndroidHostInterface.getInstance().getControllerButtonCode(mControllerType, buttonName);
|
||||
int code = AndroidHostInterface.getControllerButtonCode(mControllerType, buttonName);
|
||||
Log.i("TouchscreenController", String.format("%s -> %d", buttonName, code));
|
||||
|
||||
if (code >= 0) {
|
||||
buttonView.setConfigName(configName);
|
||||
buttonView.setButtonCode(mControllerIndex, code);
|
||||
buttonView.setHapticFeedback(mHapticFeedback);
|
||||
mButtonViews.add(buttonView);
|
||||
} else {
|
||||
Log.e("TouchscreenController", String.format("Unknown button name '%s' " +
|
||||
@@ -107,31 +227,32 @@ public class TouchscreenControllerView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean linkAxis(View view, int id, String axisName) {
|
||||
private boolean linkAxis(View view, int id, String configName, String axisName) {
|
||||
TouchscreenControllerAxisView axisView = (TouchscreenControllerAxisView) view.findViewById(id);
|
||||
if (axisView == null)
|
||||
return false;
|
||||
|
||||
int xCode = AndroidHostInterface.getInstance().getControllerAxisCode(mControllerType, axisName + "X");
|
||||
int yCode = AndroidHostInterface.getInstance().getControllerAxisCode(mControllerType, axisName + "Y");
|
||||
int xCode = AndroidHostInterface.getControllerAxisCode(mControllerType, axisName + "X");
|
||||
int yCode = AndroidHostInterface.getControllerAxisCode(mControllerType, axisName + "Y");
|
||||
Log.i("TouchscreenController", String.format("%s -> %d/%d", axisName, xCode, yCode));
|
||||
if (xCode < 0 && yCode < 0)
|
||||
return false;
|
||||
|
||||
axisView.setConfigName(configName);
|
||||
axisView.setControllerAxis(mControllerIndex, xCode, yCode);
|
||||
mAxisViews.add(axisView);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean linkAxisToButtons(View view, int id, String buttonPrefix) {
|
||||
private boolean linkAxisToButtons(View view, int id, String configName, String buttonPrefix) {
|
||||
TouchscreenControllerAxisView axisView = (TouchscreenControllerAxisView) view.findViewById(id);
|
||||
if (axisView == null)
|
||||
return false;
|
||||
|
||||
int leftCode = AndroidHostInterface.getInstance().getControllerButtonCode(mControllerType, buttonPrefix + "Left");
|
||||
int rightCode = AndroidHostInterface.getInstance().getControllerButtonCode(mControllerType, buttonPrefix + "Right");
|
||||
int upCode = AndroidHostInterface.getInstance().getControllerButtonCode(mControllerType, buttonPrefix + "Up");
|
||||
int downCode = AndroidHostInterface.getInstance().getControllerButtonCode(mControllerType, buttonPrefix + "Down");
|
||||
int leftCode = AndroidHostInterface.getControllerButtonCode(mControllerType, buttonPrefix + "Left");
|
||||
int rightCode = AndroidHostInterface.getControllerButtonCode(mControllerType, buttonPrefix + "Right");
|
||||
int upCode = AndroidHostInterface.getControllerButtonCode(mControllerType, buttonPrefix + "Up");
|
||||
int downCode = AndroidHostInterface.getControllerButtonCode(mControllerType, buttonPrefix + "Down");
|
||||
Log.i("TouchscreenController", String.format("%s(ButtonAxis) -> %d,%d,%d,%d", buttonPrefix, leftCode, rightCode, upCode, downCode));
|
||||
if (leftCode < 0 && rightCode < 0 && upCode < 0 && downCode < 0)
|
||||
return false;
|
||||
@@ -141,11 +262,117 @@ public class TouchscreenControllerView extends FrameLayout {
|
||||
return true;
|
||||
}
|
||||
|
||||
private int dpToPixels(float dp) {
|
||||
return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics()));
|
||||
}
|
||||
|
||||
public void startLayoutEditing() {
|
||||
if (mEditLayout == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
mEditLayout = (ConstraintLayout) inflater.inflate(R.layout.layout_touchscreen_controller_edit, this, false);
|
||||
((Button) mEditLayout.findViewById(R.id.stop_editing)).setOnClickListener((view) -> endLayoutEditing());
|
||||
((Button) mEditLayout.findViewById(R.id.reset_layout)).setOnClickListener((view) -> clearTranslationForAllButtons());
|
||||
addView(mEditLayout);
|
||||
}
|
||||
|
||||
mEditingLayout = true;
|
||||
}
|
||||
|
||||
public void endLayoutEditing() {
|
||||
if (mEditLayout != null) {
|
||||
((ViewGroup) mMainView).removeView(mEditLayout);
|
||||
mEditLayout = null;
|
||||
}
|
||||
|
||||
mEditingLayout = false;
|
||||
mMovingView = null;
|
||||
mMovingName = null;
|
||||
mMovingLastX = 0.0f;
|
||||
mMovingLastY = 0.0f;
|
||||
}
|
||||
|
||||
private boolean handleEditingTouchEvent(MotionEvent event) {
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_UP: {
|
||||
if (mMovingView != null) {
|
||||
// save position
|
||||
saveTranslationForButton(mMovingName, mMovingView.getTranslationX(), mMovingView.getTranslationY());
|
||||
mMovingView = null;
|
||||
mMovingName = null;
|
||||
mMovingLastX = 0.0f;
|
||||
mMovingLastY = 0.0f;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
if (mMovingView != null) {
|
||||
// already moving a button
|
||||
return true;
|
||||
}
|
||||
|
||||
Rect rect = new Rect();
|
||||
final float x = event.getX();
|
||||
final float y = event.getY();
|
||||
for (TouchscreenControllerButtonView buttonView : mButtonViews) {
|
||||
buttonView.getHitRect(rect);
|
||||
if (rect.contains((int) x, (int) y)) {
|
||||
mMovingView = buttonView;
|
||||
mMovingName = buttonView.getConfigName();
|
||||
mMovingLastX = x;
|
||||
mMovingLastY = y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (TouchscreenControllerAxisView axisView : mAxisViews) {
|
||||
axisView.getHitRect(rect);
|
||||
if (rect.contains((int) x, (int) y)) {
|
||||
mMovingView = axisView;
|
||||
mMovingName = axisView.getConfigName();
|
||||
mMovingLastX = x;
|
||||
mMovingLastY = y;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// nothing..
|
||||
return true;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
if (mMovingView == null)
|
||||
return true;
|
||||
|
||||
final float x = event.getX();
|
||||
final float y = event.getY();
|
||||
final float dx = x - mMovingLastX;
|
||||
final float dy = y - mMovingLastY;
|
||||
mMovingLastX = x;
|
||||
mMovingLastY = y;
|
||||
|
||||
final float posX = mMovingView.getX() + dx;
|
||||
final float posY = mMovingView.getY() + dy;
|
||||
//Log.d("Position", String.format("%f %f -> (%f %f) %f %f",
|
||||
// mMovingView.getX(), mMovingView.getY(), dx, dy, posX, posY));
|
||||
mMovingView.setX(posX);
|
||||
mMovingView.setY(posY);
|
||||
mMovingView.invalidate();
|
||||
mMainView.requestLayout();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handleTouchEvent(MotionEvent event) {
|
||||
switch (event.getActionMasked())
|
||||
{
|
||||
case MotionEvent.ACTION_UP:
|
||||
{
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_UP: {
|
||||
if (!AndroidHostInterface.hasInstanceAndEmulationThreadIsRunning())
|
||||
return false;
|
||||
|
||||
for (TouchscreenControllerButtonView buttonView : mButtonViews) {
|
||||
buttonView.setPressed(false);
|
||||
}
|
||||
@@ -160,8 +387,10 @@ public class TouchscreenControllerView extends FrameLayout {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
{
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
if (!AndroidHostInterface.hasInstanceAndEmulationThreadIsRunning())
|
||||
return false;
|
||||
|
||||
Rect rect = new Rect();
|
||||
final int pointerCount = event.getPointerCount();
|
||||
final int liftedPointerIndex = (event.getActionMasked() == MotionEvent.ACTION_POINTER_UP) ? event.getActionIndex() : -1;
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2l-5.5,9h11z" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M17.5,17.5m-4.5,0a4.5,4.5 0,1 1,9 0a4.5,4.5 0,1 1,-9 0" />
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M3,13.5h8v8H3z" />
|
||||
</vector>
|
||||
10
android/app/src/main/res/drawable/ic_baseline_delete_24.xml
Normal file
10
android/app/src/main/res/drawable/ic_baseline_delete_24.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z" />
|
||||
</vector>
|
||||
10
android/app/src/main/res/drawable/ic_baseline_folder_24.xml
Normal file
10
android/app/src/main/res/drawable/ic_baseline_folder_24.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M10,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V8c0,-1.1 -0.9,-2 -2,-2h-8l-2,-2z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z" />
|
||||
</vector>
|
||||
10
android/app/src/main/res/drawable/ic_baseline_gamepad_24.xml
Normal file
10
android/app/src/main/res/drawable/ic_baseline_gamepad_24.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M15,7.5V2H9v5.5l3,3 3,-3zM7.5,9H2v6h5.5l3,-3 -3,-3zM9,16.5V22h6v-5.5l-3,-3 -3,3zM16.5,9l-3,3 3,3H22V9h-5.5z" />
|
||||
</vector>
|
||||
10
android/app/src/main/res/drawable/ic_baseline_help_24.xml
Normal file
10
android/app/src/main/res/drawable/ic_baseline_help_24.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,7h-3v5.5c0,1.38 -1.12,2.5 -2.5,2.5S10,13.88 10,12.5s1.12,-2.5 2.5,-2.5c0.57,0 1.08,0.19 1.5,0.51L14,5h4v2zM4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector android:height="60dp" android:tint="?attr/colorControlNormal"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="60dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8 0,-1.85 0.63,-3.55 1.69,-4.9L16.9,18.31C15.55,19.37 13.85,20 12,20zM18.31,16.9L7.1,5.69C8.45,4.63 10.15,4 12,4c4.42,0 8,3.58 8,8 0,1.85 -0.63,3.55 -1.69,4.9z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M4,10h12v2L4,12zM4,6h12v2L4,8zM4,14h8v2L4,16zM14,14v6l5,-3z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5 5,-2.24 5,-5 -2.24,-5 -5,-5zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z" />
|
||||
</vector>
|
||||
10
android/app/src/main/res/drawable/ic_baseline_save_24.xml
Normal file
10
android/app/src/main/res/drawable/ic_baseline_save_24.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z" />
|
||||
</vector>
|
||||
@@ -3,19 +3,19 @@
|
||||
android:height="194.89dp"
|
||||
android:viewportWidth="194.89"
|
||||
android:viewportHeight="194.89">
|
||||
<path
|
||||
android:pathData="M194.89,97.445A97.445,97.445 0,0 1,97.445 194.89,97.445 97.445,0 0,1 0,97.445 97.445,97.445 0,0 1,97.445 0,97.445 97.445,0 0,1 194.89,97.445Z"
|
||||
android:strokeAlpha="0.50645"
|
||||
android:fillColor="#1a1a1a"
|
||||
android:fillAlpha="0.504414"/>
|
||||
<path
|
||||
android:pathData="M178.82,97.445A81.381,81.381 0,0 1,97.439 178.826,81.381 81.381,0 0,1 16.058,97.445 81.381,81.381 0,0 1,97.439 16.064,81.381 81.381,0 0,1 178.82,97.445Z"
|
||||
android:strokeAlpha="0.50645"
|
||||
android:fillColor="#333333"
|
||||
android:fillAlpha="0.504414"/>
|
||||
<path
|
||||
android:pathData="M159.05,97.445A61.609,61.609 0,0 1,97.441 159.054,61.609 61.609,0 0,1 35.832,97.445 61.609,61.609 0,0 1,97.441 35.836,61.609 61.609,0 0,1 159.05,97.445Z"
|
||||
android:strokeAlpha="0.50645"
|
||||
android:fillColor="#1a1a1a"
|
||||
android:fillAlpha="0.504414"/>
|
||||
<path
|
||||
android:pathData="M194.89,97.445A97.445,97.445 0,0 1,97.445 194.89,97.445 97.445,0 0,1 0,97.445 97.445,97.445 0,0 1,97.445 0,97.445 97.445,0 0,1 194.89,97.445Z"
|
||||
android:strokeAlpha="0.50645"
|
||||
android:fillColor="#1a1a1a"
|
||||
android:fillAlpha="0.504414" />
|
||||
<path
|
||||
android:pathData="M178.82,97.445A81.381,81.381 0,0 1,97.439 178.826,81.381 81.381,0 0,1 16.058,97.445 81.381,81.381 0,0 1,97.439 16.064,81.381 81.381,0 0,1 178.82,97.445Z"
|
||||
android:strokeAlpha="0.50645"
|
||||
android:fillColor="#333333"
|
||||
android:fillAlpha="0.504414" />
|
||||
<path
|
||||
android:pathData="M159.05,97.445A61.609,61.609 0,0 1,97.441 159.054,61.609 61.609,0 0,1 35.832,97.445 61.609,61.609 0,0 1,97.441 35.836,61.609 61.609,0 0,1 159.05,97.445Z"
|
||||
android:strokeAlpha="0.50645"
|
||||
android:fillColor="#1a1a1a"
|
||||
android:fillAlpha="0.504414" />
|
||||
</vector>
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
android:height="191.756dp"
|
||||
android:viewportWidth="191.756"
|
||||
android:viewportHeight="191.756">
|
||||
<path
|
||||
android:pathData="M191.756,95.878A95.878,95.878 0,0 1,95.878 191.756,95.878 95.878,0 0,1 0,95.878 95.878,95.878 0,0 1,95.878 0,95.878 95.878,0 0,1 191.756,95.878Z"
|
||||
android:strokeAlpha="0.8"
|
||||
android:fillColor="#666666"
|
||||
android:fillAlpha="0.796784"/>
|
||||
<path
|
||||
android:pathData="M191.756,95.878A95.878,95.878 0,0 1,95.878 191.756,95.878 95.878,0 0,1 0,95.878 95.878,95.878 0,0 1,95.878 0,95.878 95.878,0 0,1 191.756,95.878Z"
|
||||
android:strokeAlpha="0.8"
|
||||
android:fillColor="#666666"
|
||||
android:fillAlpha="0.796784" />
|
||||
</vector>
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
android:height="191.756dp"
|
||||
android:viewportWidth="191.756"
|
||||
android:viewportHeight="191.756">
|
||||
<path
|
||||
android:pathData="M191.756,95.878A95.878,95.878 0,0 1,95.878 191.756,95.878 95.878,0 0,1 0,95.878 95.878,95.878 0,0 1,95.878 0,95.878 95.878,0 0,1 191.756,95.878Z"
|
||||
android:strokeAlpha="0.50645"
|
||||
android:fillColor="#666666"
|
||||
android:fillAlpha="0.504414"/>
|
||||
<path
|
||||
android:pathData="M191.756,95.878A95.878,95.878 0,0 1,95.878 191.756,95.878 95.878,0 0,1 0,95.878 95.878,95.878 0,0 1,95.878 0,95.878 95.878,0 0,1 191.756,95.878Z"
|
||||
android:strokeAlpha="0.50645"
|
||||
android:fillColor="#666666"
|
||||
android:fillAlpha="0.504414" />
|
||||
</vector>
|
||||
|
||||
BIN
android/app/src/main/res/drawable/ic_star_0.png
Normal file
BIN
android/app/src/main/res/drawable/ic_star_0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
BIN
android/app/src/main/res/drawable/ic_star_1.png
Normal file
BIN
android/app/src/main/res/drawable/ic_star_1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.0 KiB |
BIN
android/app/src/main/res/drawable/ic_star_2.png
Normal file
BIN
android/app/src/main/res/drawable/ic_star_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
BIN
android/app/src/main/res/drawable/ic_star_3.png
Normal file
BIN
android/app/src/main/res/drawable/ic_star_3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
BIN
android/app/src/main/res/drawable/ic_star_4.png
Normal file
BIN
android/app/src/main/res/drawable/ic_star_4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.2 KiB |
BIN
android/app/src/main/res/drawable/ic_star_5.png
Normal file
BIN
android/app/src/main/res/drawable/ic_star_5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.8 KiB |
@@ -0,0 +1,9 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/settings"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".GameDirectoriesActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:context=".MainActivity"
|
||||
tools:showIn="@layout/activity_main">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
app:backgroundTint="@color/fab_background"
|
||||
app:srcCompat="@android:drawable/ic_input_add" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -42,7 +42,7 @@
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_marginBottom="@dimen/fab_margin"
|
||||
android:layout_marginRight="96dp"
|
||||
app:backgroundTint="@android:color/background_light"
|
||||
app:backgroundTint="@color/fab_background"
|
||||
app:srcCompat="@drawable/ic_baseline_play_arrow_24" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
@@ -51,7 +51,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:layout_margin="@dimen/fab_margin"
|
||||
app:backgroundTint="@android:color/background_light"
|
||||
app:backgroundTint="@color/fab_background"
|
||||
app:srcCompat="@android:drawable/ic_input_add" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:tabTextAppearance="@style/TabTextAppearance"
|
||||
app:tabMode="fixed" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
android:id="@+id/view_pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -22,7 +22,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginRight="64dp"
|
||||
android:layout_marginRight="80dp"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:text="Game Title"
|
||||
@@ -32,11 +32,11 @@
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/game_list_view_entry_path"
|
||||
android:id="@+id/game_list_view_entry_subtitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:layout_marginRight="64dp"
|
||||
android:layout_marginRight="80dp"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:paddingBottom="8px"
|
||||
@@ -46,17 +46,16 @@
|
||||
app:layout_constraintStart_toEndOf="@+id/game_list_view_entry_type_icon"
|
||||
app:layout_constraintTop_toBottomOf="@+id/game_list_view_entry_title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/game_list_view_entry_size"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
<ImageView
|
||||
android:id="@+id/game_list_view_compatibility_icon"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:text="123.4 MB"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:textSize="12sp"
|
||||
app:srcCompat="@drawable/ic_star_5"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
@@ -64,13 +63,13 @@
|
||||
android:id="@+id/game_list_view_entry_region_icon"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_marginTop="8px"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:focusable="false"
|
||||
android:focusableInTouchMode="false"
|
||||
android:paddingBottom="8px"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/game_list_view_entry_size"
|
||||
app:layout_constraintTop_toBottomOf="@+id/game_list_view_compatibility_icon"
|
||||
app:srcCompat="@drawable/flag_jp" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/controller_binding_icon"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_alignParentTop="true"
|
||||
tools:srcCompat="@drawable/ic_media_cdrom" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/controller_binding_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:text="Up"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@id/controller_binding_icon" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/controller_binding_value"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:text="Controller0/Button0"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:layout_toRightOf="@id/controller_binding_icon"
|
||||
android:layout_below="@id/controller_binding_name" />
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/path"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:text="TextView"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/recursive"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_below="@id/path"
|
||||
android:layout_alignParentStart="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="TextView" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/remove"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:background="?android:selectableItemBackground"
|
||||
app:srcCompat="@drawable/ic_baseline_delete_24" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/toggle_recursive"
|
||||
android:layout_width="30dp"
|
||||
android:layout_height="30dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginTop="15dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:layout_toStartOf="@id/remove"
|
||||
android:background="?android:selectableItemBackground"
|
||||
app:srcCompat="@drawable/ic_baseline_folder_24" />
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/property_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:text="TextView"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/property_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="10dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_below="@id/property_title"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="TextView" />
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<Button
|
||||
android:id="@+id/stop_editing"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/touchscreen_controller_stop_editing"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/reset_layout"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="64dp"
|
||||
android:text="@string/touchscreen_controller_reset_layout"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
66
android/app/src/main/res/layout/save_state_view_entry.xml
Normal file
66
android/app/src/main/res/layout/save_state_view_entry.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:layout_marginLeft="15dp"
|
||||
android:layout_alignParentTop="true"
|
||||
android:scaleType="fitXY"
|
||||
tools:srcCompat="@drawable/ic_media_cdrom" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/summary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="Game Slot 1"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_toRightOf="@id/image" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/game"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:text="SCES-0000 - Game Name"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:layout_below="@id/summary"
|
||||
android:layout_toRightOf="@id/image" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/path"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:text="Dump Name.chd"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:layout_below="@id/game"
|
||||
android:layout_toRightOf="@id/image" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/timestamp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginTop="0dp"
|
||||
android:layout_marginBottom="14dp"
|
||||
android:text="Saved at Timestamp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||
android:layout_below="@id/path"
|
||||
android:layout_toRightOf="@id/image" />
|
||||
|
||||
</RelativeLayout>
|
||||
20
android/app/src/main/res/menu/menu_controller_mapping.xml
Normal file
20
android/app/src/main/res/menu/menu_controller_mapping.xml
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_load_profile"
|
||||
android:title="Load Profile"
|
||||
android:icon="@drawable/ic_baseline_folder_open_24"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_save_profile"
|
||||
android:title="Save Profile"
|
||||
android:icon="@drawable/ic_baseline_save_24"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_clear_bindings"
|
||||
android:title="Clear Bindings" />
|
||||
</menu>
|
||||
11
android/app/src/main/res/menu/menu_edit_game_directories.xml
Normal file
11
android/app/src/main/res/menu/menu_edit_game_directories.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/add_directory"
|
||||
android:title="@string/menu_edit_game_directories_add_directory" />
|
||||
<item
|
||||
android:id="@+id/add_path"
|
||||
android:title="@string/menu_edit_game_directories_add_path" />
|
||||
</menu>
|
||||
@@ -3,8 +3,11 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/game_list_entry_menu_start_game"
|
||||
android:title="Start Game" />
|
||||
android:title="@string/menu_game_list_entry_start_game" />
|
||||
<item
|
||||
android:id="@+id/game_list_entry_menu_resume_game"
|
||||
android:title="Resume Game" />
|
||||
android:title="@string/menu_game_list_entry_resume_game" />
|
||||
<item
|
||||
android:id="@+id/game_list_entry_menu_properties"
|
||||
android:title="Game Properties" />
|
||||
</menu>
|
||||
@@ -5,28 +5,46 @@
|
||||
<group android:id="@+id/start_menu">
|
||||
<item
|
||||
android:id="@+id/action_resume"
|
||||
android:title="Resume Last Session" />
|
||||
android:title="@string/menu_main_resume_last_session" />
|
||||
<item
|
||||
android:id="@+id/action_start_file"
|
||||
android:title="@string/menu_main_start_file" />
|
||||
<item
|
||||
android:id="@+id/action_start_bios"
|
||||
android:title="Start BIOS" />
|
||||
android:title="@string/menu_main_start_bios" />
|
||||
</group>
|
||||
<group android:id="@+id/game_list">
|
||||
<item
|
||||
android:id="@+id/action_add_game_directory"
|
||||
android:title="Add Game Directory" />
|
||||
android:id="@+id/action_edit_game_directories"
|
||||
android:title="@string/menu_main_edit_game_directories" />
|
||||
<item
|
||||
android:id="@+id/action_scan_for_new_games"
|
||||
android:title="Scan For New Games" />
|
||||
android:title="@string/menu_main_scan_for_new_games" />
|
||||
<item
|
||||
android:id="@+id/action_rescan_all_games"
|
||||
android:title="Rescan All Games" />
|
||||
android:title="@string/menu_main_rescan_all_games" />
|
||||
<item
|
||||
android:id="@+id/action_import_bios"
|
||||
android:title="Import BIOS" />
|
||||
android:title="@string/menu_main_import_bios" />
|
||||
<item
|
||||
android:id="@+id/action_show_version"
|
||||
android:title="@string/menu_main_show_version" />
|
||||
<item
|
||||
android:id="@+id/action_github_respository"
|
||||
android:title="@string/menu_main_github_repository" />
|
||||
<item
|
||||
android:id="@+id/action_discord_server"
|
||||
android:title="@string/menu_main_discord_server" />
|
||||
</group>
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:title="@string/action_settings"
|
||||
android:id="@+id/action_controller_mapping"
|
||||
android:icon="@drawable/ic_baseline_gamepad_24"
|
||||
android:orderInCategory="100"
|
||||
app:showAsAction="never" />
|
||||
android:title="@string/action_controller_mapping" />
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:icon="@drawable/ic_baseline_settings_24"
|
||||
android:orderInCategory="100"
|
||||
android:title="@string/action_settings"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
|
||||
235
android/app/src/main/res/values-es/arrays.xml
Normal file
235
android/app/src/main/res/values-es/arrays.xml
Normal file
@@ -0,0 +1,235 @@
|
||||
<resources>
|
||||
<string-array name="settings_console_region_entries">
|
||||
<item>Auto-Detectar</item>
|
||||
<item>NTSC-J (Japón)</item>
|
||||
<item>NTSC-U (US)</item>
|
||||
<item>PAL (Europa, Australia)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cpu_execution_mode_entries">
|
||||
<item>Intérprete (El más lento)</item>
|
||||
<item>Intérprete en Caché (Rápido)</item>
|
||||
<item>Recompilador (Más rápido)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cpu_fastmem_mode_entries">
|
||||
<item>Deshabilitado (El más lento)</item>
|
||||
<item>MMap (Hardware, Más rápido, 64-Bit Solamente)</item>
|
||||
<item>LUT (Rápido)</item>
|
||||
</string-array>
|
||||
<string-array name="gpu_renderer_entries">
|
||||
<item>Hardware (OpenGL)</item>
|
||||
<item>Hardware (Vulkan)</item>
|
||||
<item>Software</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_resolution_scale_entries">
|
||||
<item>1x</item>
|
||||
<item>2x</item>
|
||||
<item>3x (para 720p)</item>
|
||||
<item>4x</item>
|
||||
<item>5x (para 1080p)</item>
|
||||
<item>6x (para 1440p)</item>
|
||||
<item>7x</item>
|
||||
<item>8x</item>
|
||||
<item>9x (para 4K)</item>
|
||||
<item>10x</item>
|
||||
<item>11x</item>
|
||||
<item>12x</item>
|
||||
<item>13x</item>
|
||||
<item>14x</item>
|
||||
<item>15x</item>
|
||||
<item>16x</item>
|
||||
</string-array>
|
||||
<string-array name="settings_display_crop_mode_entries">
|
||||
<item>Ninguno</item>
|
||||
<item>Sólo área de sobreescaneo</item>
|
||||
<item>Todos los bordes</item>
|
||||
</string-array>
|
||||
<string-array name="settings_display_aspect_ratio_names">
|
||||
<item>Auto (Nativo del juego)</item>
|
||||
<item>4:3</item>
|
||||
<item>16:9</item>
|
||||
<item>16:10</item>
|
||||
<item>19:9</item>
|
||||
<item>20:9</item>
|
||||
<item>21:9</item>
|
||||
<item>32:9</item>
|
||||
<item>8:7</item>
|
||||
<item>5:4</item>
|
||||
<item>3:2</item>
|
||||
<item>2:1 (VRAM 1:1)</item>
|
||||
<item>1:1</item>
|
||||
<item>PAR 1:1</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_texture_filter_names">
|
||||
<item>Nearest-Neighbor</item>
|
||||
<item>Bilinear</item>
|
||||
<item>Bilinear (Sin unión de bordes)</item>
|
||||
<item>JINC2</item>
|
||||
<item>JINC2 (Sin unión de bordes)</item>
|
||||
<item>xBR</item>
|
||||
<item>xBR (Sin unión de bordes)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_controller_type_entries">
|
||||
<item>Control Digital (Mando)</item>
|
||||
<item>Control Analógico (DualShock)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_memory_card_mode_entries">
|
||||
<item>Sin tarjeta de memoria</item>
|
||||
<item>Compartido entre todos los juegos</item>
|
||||
<item>Tarjeta separada por juego (Código)</item>
|
||||
<item>Tarjeta separada por juego (Título)</item>
|
||||
</string-array>
|
||||
<string-array name="emulation_menu">
|
||||
<item>Cargar Estado</item>
|
||||
<item>Guardar Estado</item>
|
||||
<item>Activar Avance Rápido</item>
|
||||
<item>Más Opciones</item>
|
||||
<item>Salir</item>
|
||||
</string-array>
|
||||
<string-array name="emulation_more_menu">
|
||||
<item>Reiniciar</item>
|
||||
<item>Código de Trucos</item>
|
||||
<item>Cambiar Disco</item>
|
||||
<item>Configuraciones</item>
|
||||
<item>Cambiar Control de Pantalla Tactil</item>
|
||||
<item>Editar Diseño del Control de Pantalla Tactil</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cdrom_read_speedup_entries">
|
||||
<item>Ninguno (Velocidad Doble)</item>
|
||||
<item>2x (Velocidad Cuádruple)</item>
|
||||
<item>3x (6x Velocidad)</item>
|
||||
<item>4x (8x Velocidad)</item>
|
||||
<item>5x (10x Velocidad)</item>
|
||||
<item>6x (12x Velocidad)</item>
|
||||
<item>7x (14x Velocidad)</item>
|
||||
<item>8x (16x Velocidad)</item>
|
||||
<item>9x (18x Velocidad)</item>
|
||||
<item>10x (20x Velocidad)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_touchscreen_controller_view_entries">
|
||||
<item>Ninguno</item>
|
||||
<item>Digital Pad</item>
|
||||
<item>Pad Analógico Unico</item>
|
||||
<item>Pad Analógico Dual</item>
|
||||
</string-array>
|
||||
<string-array name="settings_audio_backend_entries">
|
||||
<item>Nulo (Sin Salida)</item>
|
||||
<item>Cubeb</item>
|
||||
<item>OpenSL ES (Recomendado)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_audio_buffer_size_entries">
|
||||
<item>1024 Fotogramas (23.22ms)</item>
|
||||
<item>2048 Fotogramas (46.44ms, Recomendado)</item>
|
||||
<item>3072 Fotogramas (69.66ms)</item>
|
||||
<item>4096 Fotogramas (92.88ms)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_log_level_entries">
|
||||
<item>Ninguno</item>
|
||||
<item>Error</item>
|
||||
<item>Alerta</item>
|
||||
<item>Alertas de Rendimiento</item>
|
||||
<item>Información</item>
|
||||
<item>Detalles</item>
|
||||
<item>Desarrollador</item>
|
||||
<item>Perfil</item>
|
||||
<item>Depurar</item>
|
||||
<item>Seguimiento</item>
|
||||
</string-array>
|
||||
<string-array name="settings_tabs">
|
||||
<item>General</item>
|
||||
<item>Pantalla</item>
|
||||
<item>Audio</item>
|
||||
<item>Mejoras</item>
|
||||
<item>Controles</item>
|
||||
<item>Avanzado</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_msaa_entries">
|
||||
<item>Deshabilitado</item>
|
||||
<item>2x MSAA</item>
|
||||
<item>4x MSAA</item>
|
||||
<item>8x MSAA</item>
|
||||
<item>2x SSAA</item>
|
||||
<item>4x SSAA</item>
|
||||
<item>8x SSAA</item>
|
||||
</string-array>
|
||||
<string-array name="settings_advanced_display_fps_limit_entries">
|
||||
<item>Ilimitado (Mostrar fotogramas)</item>
|
||||
<item>10 FPS</item>
|
||||
<item>15 FPS</item>
|
||||
<item>30 FPS</item>
|
||||
<item>45 FPS</item>
|
||||
<item>60 FPS</item>
|
||||
<item>75 FPS</item>
|
||||
<item>90 FPS</item>
|
||||
</string-array>
|
||||
<string-array name="settings_emulation_speed_entries">
|
||||
<item>Ilimitado</item>
|
||||
<item>10% [6 FPS (NTSC) / 5 FPS (PAL)]</item>
|
||||
<item>20% [12 FPS (NTSC) / 10 FPS (PAL)]</item>
|
||||
<item>30% [18 FPS (NTSC) / 15 FPS (PAL)]</item>
|
||||
<item>40% [24 FPS (NTSC) / 20 FPS (PAL)]</item>
|
||||
<item>50% [30 FPS (NTSC) / 25 FPS (PAL)]</item>
|
||||
<item>60% [36 FPS (NTSC) / 30 FPS (PAL)]</item>
|
||||
<item>70% [42 FPS (NTSC) / 35 FPS (PAL)]</item>
|
||||
<item>80% [48 FPS (NTSC) / 40 FPS (PAL)]</item>
|
||||
<item>90% [54 FPS (NTSC) / 45 FPS (PAL)]</item>
|
||||
<item>100% [60 FPS (NTSC) / 50 FPS (PAL), Defecto]</item>
|
||||
<item>125% [75 FPS (NTSC) / 62 FPS (PAL)]</item>
|
||||
<item>150% [90 FPS (NTSC) / 75 FPS (PAL)]</item>
|
||||
<item>175% [105 FPS (NTSC) / 87 FPS (PAL)]</item>
|
||||
<item>200% [120 FPS (NTSC) / 100 FPS (PAL)]</item>
|
||||
<item>250% [150 FPS (NTSC) / 125 FPS (PAL)]</item>
|
||||
<item>300% [180 FPS (NTSC) / 150 FPS (PAL)]</item>
|
||||
<item>350% [210 FPS (NTSC) / 175 FPS (PAL)]</item>
|
||||
<item>400% [240 FPS (NTSC) / 200 FPS (PAL)]</item>
|
||||
<item>450% [270 FPS (NTSC) / 225 FPS (PAL)]</item>
|
||||
<item>500% [300 FPS (NTSC) / 250 FPS (PAL)]</item>
|
||||
<item>600% [360 FPS (NTSC) / 300 FPS (PAL)]</item>
|
||||
<item>700% [420 FPS (NTSC) / 350 FPS (PAL)]</item>
|
||||
<item>800% [480 FPS (NTSC) / 400 FPS (PAL)]</item>
|
||||
<item>900% [540 FPS (NTSC) / 450 FPS (PAL)]</item>
|
||||
<item>1000% [600 FPS (NTSC) / 500 FPS (PAL)]</item>
|
||||
</string-array>
|
||||
<string-array name="settings_advanced_cpu_overclock_entries">
|
||||
<item>25% (8MHz)</item>
|
||||
<item>50% (16MHz)</item>
|
||||
<item>75% (24MHz)</item>
|
||||
<item>100% (33MHz, Defecto)</item>
|
||||
<item>125% (41MHz)</item>
|
||||
<item>150% (49MHz)</item>
|
||||
<item>175% (57MHz)</item>
|
||||
<item>200% (66MHz)</item>
|
||||
<item>225% (74MHz)</item>
|
||||
<item>250% (82MHz)</item>
|
||||
<item>275% (90MHz)</item>
|
||||
<item>300% (99MHz)</item>
|
||||
<item>350% (115MHz)</item>
|
||||
<item>400% (132MHz)</item>
|
||||
<item>450% (148MHz)</item>
|
||||
<item>500% (165MHz)</item>
|
||||
<item>500% (165MHz)</item>
|
||||
<item>600% (198MHz)</item>
|
||||
<item>700% (231MHz)</item>
|
||||
<item>800% (264MHz)</item>
|
||||
<item>900% (297MHz)</item>
|
||||
<item>1000% (330MHz)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_emulation_screen_orientation_entries">
|
||||
<item>Usar configuración del dipositivo</item>
|
||||
<item>Vertical</item>
|
||||
<item>Horizontal</item>
|
||||
</string-array>
|
||||
<string-array name="settings_theme_entries">
|
||||
<item>Usar configuración del Sistema</item>
|
||||
<item>Claro</item>
|
||||
<item>Oscuro</item>
|
||||
</string-array>
|
||||
<string-array name="settings_downsample_mode_entries">
|
||||
<item>Deshabilitado</item>
|
||||
<item>Cuadro (Reducir Resolución 3D/Suavizar todo)</item>
|
||||
<item>Adaptable (Preservar 3D/Suavizar 2D)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_boolean_entries">
|
||||
<item>Deshabilitado</item>
|
||||
<item>Halibtado</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
205
android/app/src/main/res/values-es/strings.xml
Normal file
205
android/app/src/main/res/values-es/strings.xml
Normal file
@@ -0,0 +1,205 @@
|
||||
<resources>
|
||||
<string name="app_name">DuckStation</string>
|
||||
<string name="action_settings">Configuraciones</string>
|
||||
<string name="action_controller_mapping">Mapeado del Control</string>
|
||||
<string name="title_activity_settings">Configuraciones</string>
|
||||
<string name="settings_console_region">Región de Consola</string>
|
||||
<string name="settings_console_tty_output">Habilitar Salida TTY</string>
|
||||
<string name="settings_console_fast_boot">Inicio Rápido</string>
|
||||
<string name="settings_osd_show_messages">Mostrar Mensajes</string>
|
||||
<string name="settings_osd_show_speed">Mostrar Velocidad de Emulación</string>
|
||||
<string name="settings_osd_show_show_fps">Mostrar FPS</string>
|
||||
<string name="settings_osd_show_show_vps">Mostrar VPS</string>
|
||||
<string name="settings_cpu_execution_mode">Modo de Ejecución de CPU</string>
|
||||
<string name="settings_gpu_renderer">Renderizador de GPU</string>
|
||||
<string name="settings_gpu_resolution_scale">Escala de Resolución</string>
|
||||
<string name="title_activity_emulation">Actividad de Emulación</string>
|
||||
<string name="settings_emulation_speed">Velocidad de Emulación</string>
|
||||
<string name="settings_fast_forward_speed">Velocidad de Avance Rápido</string>
|
||||
<string name="settings_save_state_on_exit">Guardar Estado al Salir</string>
|
||||
<string name="settings_pause_when_menu_opened">Pausar cuando el menu se abre</string>
|
||||
<string name="settings_emulation_screen_orientation">Orientación de pantalla del Emulador</string>
|
||||
<string name="settings_load_patch_codes">Cargar Códigos de Parche</string>
|
||||
<string name="settings_summary_save_state_on_exit">Guarda automáticamente el estado del emulador al apagar o salir. Después puedes continuar desde donde lo dejaste la última vez.</string>
|
||||
<string name="settings_summary_pause_when_menu_opened">Pausa la emulación dentro del juego cuando el menú es abierto.</string>
|
||||
<string name="settings_summary_load_patch_codes"><![CDATA[Cargar Códigos de Parche de trucos/<game name>.cht en formato PCSXR. Los códigos pueden ser activados dentro del juego.]]></string>
|
||||
<string name="settings_apply_compatibility_settings">Aplicar configuración de compatiblidad</string>
|
||||
<string name="settings_summary_apply_compatibility_settings">Desactiva las mejoras automáticamente cuando no son compatibles con los juegos.</string>
|
||||
<string name="settings_summary_video_sync">Habilita esta opción para coincidir la tasa de refresco de DuckStation con la de tu pantalla. La VSync se deshabilita automáticamente cuando no sea posible (ejemplo, no llegar al 100% de la velocidad).</string>
|
||||
<string name="settings_video_sync">Sincronizar Video</string>
|
||||
<string name="settings_cpu_overclocking">CPU Overclocking</string>
|
||||
<string name="settings_cdrom_region_check">Chequeo Regional de CD-ROM</string>
|
||||
<string name="settings_summary_cdrom_region_check">Evita que el emulador lea discos de regiones incorrectas. Comúnmente es seguro desactivarlo.</string>
|
||||
<string name="settings_cdrom_preload_image_to_ram">Precargar imagen a RAM de CD-ROM</string>
|
||||
<string name="settings_summary_preload_image_to_ram">Carga la imagen del juego en la RAM. Útil para cuando se usan directorios a través de una red. En algunas ocasiones puede eliminar las pausas que se generan cuando el juego inicia la reproducción de una pista de audio.</string>
|
||||
<string name="settings_pgxp_vertex_cache">Caché de vértices PGXP</string>
|
||||
<string name="settings_summary_pgxp_vertex_cache">Utiliza las coordenadas de la pantalla como respaldo cuando falla el seguimiento de vértices a través de la memoria. Puede mejorar la compatibilidad con PGXP.</string>
|
||||
<string name="settings_pgxp_cpu_mode">Modo PGXP CPU</string>
|
||||
<string name="settings_summary_pgxp_cpu_mode">Intenta rastrear la manipulación de vértices a través del CPU. Algunos juegos requieren esta opción para que PGXP sea efectivo. Muy lento e incompatible con el recompilador.</string>
|
||||
<string name="settings_cpu_recompiler_icache">CPU Recompilado ICache</string>
|
||||
<string name="settings_summary_cpu_recompiler_icache">Determina si la caché de instrucciones del CPU se simula en el recompilador. Mejora la precisión con un pequeño coste de rendimiento. Si los juegos corren demasiado rápido, intenta habilitar esta opción.</string>
|
||||
<string name="settings_cpu_recompiler_fastmem">CPU Recompilador de acceso rápido a la memoria</string>
|
||||
<string name="settings_summary_cpu_recompiler_fastmem">Hace que el acceso de memoria de los invitados sea más eficiente mediante el uso de errores de página y parches. Desactívalo si es inestable en tu dispositivo.</string>
|
||||
<string name="settings_presented_frame_limit">Límite de fotogramas presentados</string>
|
||||
<string name="settings_logging_level">Nivel de Registro</string>
|
||||
<string name="settings_log_to_file">Registrar en Archivo</string>
|
||||
<string name="settings_summary_log_to_file">Escribe los mensajes de registros duckstation.log en tu directorio de Usuario. Usarlo solamente para depurar, ya que ralentiza la emulación.</string>
|
||||
<string name="settings_log_to_logcat">Registrar en Logcat</string>
|
||||
<string name="settings_summary_log_to_logcat">Escribe los mensajes de registros en el registrador de mensajes de Android. Solo es útil cuando se conecta a una computadora con adb.</string>
|
||||
<string name="settings_volume">Volumen</string>
|
||||
<string name="settings_summary_volume">Controla el volumen de salida de audio del emulador.</string>
|
||||
<string name="settings_fast_forward_volume">Volumen de Avance Rápido</string>
|
||||
<string name="settings_summary_fast_forward_volume">Controla el volumen de salida de audio del emulador que durante el avance rápido.</string>
|
||||
<string name="settings_mute_all_sound">Silenciar todo</string>
|
||||
<string name="settings_summary_mute_all_sound">Evita que el emulador emita cualquier sonido.</string>
|
||||
<string name="settings_mute_cd_audio">Silenciar audio de CD</string>
|
||||
<string name="settings_summary_mute_cd_audio">Silecia a la fuerza el audio CD-DA y XA del CD-ROM. Puede usarse para deshabilitar la música de fondo en algunos juegos.</string>
|
||||
<string name="settings_audio_backend">Motor de Audio</string>
|
||||
<string name="settings_audio_buffer_size">Tamaño del Búfer de Audio</string>
|
||||
<string name="settings_summary_audio_buffer_size">Determina la latencia entre el audio que se genera y de salida por altavoces. Los valores más pequeños reducen la latencia, pero las variaciones en la velocidad de emulación causarán problemas.</string>
|
||||
<string name="settings_audio_sync">Sincronizar Audio</string>
|
||||
<string name="settings_summary_audio_sync">Acelera la velocidad de la emulación basado en los fotogramas de audio enviados por el motor de audio. Esto ayuda a eliminar ruidos o distorsiones si la emulación es muy rápida. La sincronización se desabilitará automáticamente si la velocidad no es del 100%.</string>
|
||||
<string name="settings_controller_type">Tipo de Control</string>
|
||||
<string name="settings_enable_analog_mode_on_reset">Habilitar el Modo Analógico al Reiniciar</string>
|
||||
<string name="settings_touchscreen_controller_view">Vista de Control de Pantalla Táctil</string>
|
||||
<string name="settings_auto_hide_touchscreen_controller">Auto-Ocultar el control de pantalla táctil</string>
|
||||
<string name="settings_summary_auto_hide_touchscreen_controller">Oculta el control de pantalla táctil cuando un control externo es detectado.</string>
|
||||
<string name="settings_vibrate_on_press">Vibrar al presionar</string>
|
||||
<string name="settings_summary_vibrate_on_press">Permite una vibración corta cuando un botón en la pantalla táctil es presionado. Requiere que \"Vibrar al presionar\" esté habilitado en tu dispositivo.</string>
|
||||
<string name="settings_enable_vibration">Activar Vibración</string>
|
||||
<string name="settings_summary_enable_vibration">Reenvía el ruido del juego al motor de vibración del teléfono..</string>
|
||||
<string name="settings_memory_card_1_type">Tarjeta de Memoria Tipo 1</string>
|
||||
<string name="settings_memory_card_2_type">Tarjeta de Memoria Tipo 2</string>
|
||||
<string name="settings_crop_mode">Modo de recorte</string>
|
||||
<string name="settings_aspect_ratio">Relación de Aspecto</string>
|
||||
<string name="settings_linear_upscaling">Escalado Lineal</string>
|
||||
<string name="settings_integer_upscaling">Escalado Entero</string>
|
||||
<string name="settings_summary_linear_upscaling">Suaviza la imagen al escalar la consola a la pantalla.</string>
|
||||
<string name="settings_summary_integer_upscaling">Añade relleno en la pantalla para asegurarse que la relación entre los pixeles de la consola y del anfitrión es un número entero. Puede resultar en una imagen más nítida en algunos juegos 2D.</string>
|
||||
<string name="settings_summary_osd_show_messages">Muestra mensajes en pantalla cuando ocurren eventos como crear o cargar estados guardados, tomar capturas, etc.</string>
|
||||
<string name="settings_summary_osd_show_speed">Establece la velocidad de emulación de destino. No se asegura que esa velocidad se vaya a alcanzar, y de no hacerlo, el emulador va a correr lo más rápido que pueda.</string>
|
||||
<string name="settings_summary_osd_show_fps">Muestra la velocidad de fotogramas interna del juego en la esquina superior derecha de la pantalla.</string>
|
||||
<string name="settings_summary_osd_show_vps">Muestra el número de fotogramas (o sincronizaciones verticales) mostradas por segundo por el sistema en la esquina superior derecha de la pantalla.</string>
|
||||
<string name="settings_cdrom_read_speedup">CD-ROM Aceleración de lectura</string>
|
||||
<string name="settings_summary_cdrom_read_speedup">Acelera la lectura del CD-ROM para el valor especificado. Sólo se aplica a lecturas de doble velocidad, y se ignora cuando se está reproduciendo audio. Puede mejorar las velocidades de carga en algunos juegos, mientras que puede interrumpir otros.</string>
|
||||
<string name="settings_summary_console_fast_boot">Omite el BIOS shell/intro, iniciando directamente en el juego. Normalmente es seguro habilitarlo, pero algunos juegos se tildan.</string>
|
||||
<string name="settings_msaa">Anti-aliasing de muestreo múltiple</string>
|
||||
<string name="settings_true_color">Renderizado de color verdadero (24-bit, deshabilita el tramado)</string>
|
||||
<string name="settings_summary_true_color">Esto produce mejores gradientes al costo de hacer algunos colores diferentes. Deshabilitar esta opción también activa el tramado. La mayoría de los juegos son compatibles con esta opción.</string>
|
||||
<string name="settings_scaled_dithering">Escalado de tramado (escalar patrón de tramado a resolución)</string>
|
||||
<string name="settings_summary_scaled_dithering">Escala el patrón del tramado a la escala de resolución de la GPU emulada. Esto disimula el tramado en altas resoluciones. Usualmente es seguro habilitarlo, y sólo está soportado por los renderizadores de hardware.</string>
|
||||
<string name="settings_disable_interlacing">Deshabilitar tramado (fuerza progresivamente renderizado/escaneo)</string>
|
||||
<string name="settings_summary_disable_interlacing">Fuerza el renderizado y visualización de fotogramas en modo progresivo. Esto elimina el efecto \"peine\" visto en juegos con 480i renderizándolos en 480p. Normalmente es seguro habilitarlo.</string>
|
||||
<string name="settings_texture_filtering">Filtrado de textura</string>
|
||||
<string name="settings_force_ntsc_timings">Forzar tiempos NTSC (60hz en PAL)</string>
|
||||
<string name="settings_summary_force_ntsc_timings">Usa los tiempos de fotograma del modo NTSC cuando la consola está en modo PAL, forzando los juegos PAL a correr a 60Hz.</string>
|
||||
<string name="settings_widescreen_hack">Hack de Pantalla Panorámica</string>
|
||||
<string name="settings_summary_widescreen_hack">Escala las posiciones de vértices en pantalla a una relación de aspecto panorámica, incrementando el campo de visión de 4:3 a 16:9 en juegos 3D. Tal vez no sea compatible con todos los juegos.</string>
|
||||
<string name="settings_force_4_3_for_24bit">Forzar 4:3 para escenas 24-bit</string>
|
||||
<string name="settings_summary_force_4_3_for_24bit">Cambia al modo de pantalla 4:3 cuando se muestra contenido de 24-bit, generalmente FMVs.</string>
|
||||
<string name="settings_chroma_smoothing_24bit">Suavizado de croma para escenas 24-bit</string>
|
||||
<string name="settings_summary_chrome_smoothing_24bit">Suaviza el efecto pixelado que se produce entre las transiciones de colores en contenido de 24-bit, generalmente en FMVs. Sólo se aplica a los renderizadores de hardware.</string>
|
||||
<string name="settings_pgxp_geometry_correction">PGXP Corrección de Geometría</string>
|
||||
<string name="settings_summary_pgxp_geometry_correction"><![CDATA[Reduce los polígonos \"temblorosos\" y las texturas \"deformadas\" que son comunes en juegos de PS1. >Solo funciona con los renderizadores de hardware. Tal vez no sea compatible con todos los juegos.]]></string>
|
||||
<string name="settings_pgxp_culling_correction">PGXP Correción de Culling</string>
|
||||
<string name="settings_summary_pgxp_culling_correction">Incrementa la precisión del culling de polígonos, reduciendo el número de huecos en la geometría. Requiere la correción de geometría activada.</string>
|
||||
<string name="settings_pgxp_texture_correction">PGXP Corrección de Texturas</string>
|
||||
<string name="settings_summary_pgxp_texture_correction">Usa interpolación con perspectiva correcta para las coordenadas de texturas y colores, enderezando texturas deformadas. Requiere la correción de geometría activada.</string>
|
||||
<string name="settings_pgxp_preserve_projection_precision">PGXP Conservar la precisión de proyección</string>
|
||||
<string name="settings_summary_pgxp_preserve_projection_precision">Permite precisión adicional para PGXP. Puede mejorar las imágenes en algunos juegos pero fallar en otros.</string>
|
||||
<string name="settings_pgxp_depth_buffer">PGXP Búfer de Profundidad</string>
|
||||
<string name="settings_summary_pgxp_depth_buffer">Intenta reducir los polígonos en el eje Z probando píxeles con los valores de profundidad de PGXP. Tiene baja compatibilidad, pero puede funcionar bien en algunos juegos.</string>
|
||||
<string name="menu_main_resume_last_session">Reanudar la última sesión</string>
|
||||
<string name="menu_main_start_file">Iniciar Archivo</string>
|
||||
<string name="menu_main_start_bios">Iniciar BIOS</string>
|
||||
<string name="menu_main_edit_game_directories">Editar Directorios de Juego</string>
|
||||
<string name="menu_main_scan_for_new_games">Escanear para juegos nuevos</string>
|
||||
<string name="menu_main_rescan_all_games">Reescanear todos los juegos</string>
|
||||
<string name="menu_main_import_bios">Importar BIOS</string>
|
||||
<string name="menu_main_show_version">Mostrar Versión</string>
|
||||
<string name="menu_main_github_repository">Repositorio de GitHub</string>
|
||||
<string name="menu_main_discord_server">Servidor de Discord</string>
|
||||
<string name="menu_game_list_entry_start_game">Iniciar Juego</string>
|
||||
<string name="menu_game_list_entry_resume_game">Reanudar Juego</string>
|
||||
<string name="android_progress_callback_please_wait">Cargando...</string>
|
||||
<string name="android_progress_callback_ok">OK</string>
|
||||
<string name="android_progress_callback_information">Información</string>
|
||||
<string name="android_progress_callback_confirmation">Confirmación</string>
|
||||
<string name="android_progress_callback_yes">Si</string>
|
||||
<string name="android_progress_callback_no">No</string>
|
||||
<string name="emulation_activity_error">Error</string>
|
||||
<string name="emulation_activity_ok">OK</string>
|
||||
<string name="emulation_activity_import_patch_codes">Importar códigos de parche...</string>
|
||||
<string name="emulation_activity_patch_on">(ENCENDIDO)</string>
|
||||
<string name="emulation_activity_patch_off">(APAGADO)</string>
|
||||
<string name="emulation_activity_choose_patch_code_file">Seleccionar Archivo con Códigos de Parche</string>
|
||||
<string name="emulation_activity_failed_to_import_patch_codes">Fallo al importar códigos de parche. Asegúrese de seleccionar un archivo con formato PCSXR o Libretro.</string>
|
||||
<string name="main_activity_choose_directory">Elegir Directorio</string>
|
||||
<string name="main_activity_error">Error</string>
|
||||
<string name="main_activity_get_path_from_file_error">No se pudo obtener la ruta del archivo seleccionado. Asegúrese de que el archivo esté un almacenamiento interno/externo.\n\nToque el botón que sobresale en el selector de directorio.\nSeleccione "Mostrar Almacenamiento Interno".\nToque el botón de menú y seleccione el nombre de su dispositivo o tarjeta SD .</string>
|
||||
<string name="main_activity_ok">OK</string>
|
||||
<string name="main_activity_get_path_from_directory_error">No se pudo obtener la ruta del directorio seleccionado. Asegúrese de que el directorio esté un almacenamiento interno/externo.\n\nToque el botón que sobresale en el selector de directorio.\nSeleccione \"Mostrar Almacenamiento Interno\".\nToque el botón de menú y seleccione el nombre de su dispositivo o tarjeta SD .</string>
|
||||
<string name="main_activity_external_storage_permissions_error">Se requieren permisos de almacenamiento externo para usar DuckStation.</string>
|
||||
<string name="main_activity_choose_disc_image">Seleccionar Imagen de Disco</string>
|
||||
<string name="main_activity_missing_bios_image_prompt">No se encontró ninguna imagen de BIOS en el directorio BIOS de DuckStation. ¿Quiere localizar e importar una imagen de BIOS ahora?</string>
|
||||
<string name="main_activity_missing_bios_image">Imagen BIOS Ausente</string>
|
||||
<string name="main_activity_yes">Si</string>
|
||||
<string name="main_activity_no">No</string>
|
||||
<string name="main_activity_choose_bios_image">Elegir Imagen BIOS</string>
|
||||
<string name="main_activity_failed_to_open_bios_image">Fallo al cargar la imagen BIOS.</string>
|
||||
<string name="main_activity_failed_to_read_bios_image_prefix">\"Fallo al leer la imagen BIOS: \"</string>
|
||||
<string name="main_activity_bios_image_too_large">La imagen de BIOS es demasiado grande.</string>
|
||||
<string name="main_activity_invalid_error">Esta Imagen BIOS es incorrecta, o ya ha sido importado.</string>
|
||||
<string name="main_activity_show_version_title">Version</string>
|
||||
<string name="main_activity_copy">Copiar</string>
|
||||
<string name="settings_gpu_thread">Renderizado de GPU con Hilos</string>
|
||||
<string name="settings_summary_gpu_thread">Usa un segundo hilo para dibujar los gráficos. Actualmente solo está disponible para el renderizador por software, pero puede dar un aumento significante de velocidad, y es seguro de usar.</string>
|
||||
<string name="settings_gpu_threaded_presentation">Presentación de GPU con subprocesos</string>
|
||||
<string name="settings_summary_gpu_threaded_presentation">Presenta fotogramas en un hilo de fondo cuando el Avance Rápido o VSync está desactivado. Esto puede mejorar considerablemente el rendimiento en el renderizador Vulkan.</string>
|
||||
<string name="settings_language">Idioma (reiniciar para aplicar)</string>
|
||||
<string name="touchscreen_controller_stop_editing">Dejar de editar</string>
|
||||
<string name="touchscreen_controller_reset_layout">Reiniciar Modelo</string>
|
||||
<string name="emulation_activity_touchscreen_controller_not_active">El control de pantalla táctil no está activo.</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_controller_mapping_summary">Permite asignar botones/ejes del control externo al controlador emulado.</string>
|
||||
<string name="settings_controller_mapping">Mapeado de Control</string>
|
||||
<string name="controller_binding_dialog_message">Presiona un botón del control para seleccionar una nueva asignación.\n\nAsignación Actual: %s</string>
|
||||
<string name="controller_binding_dialog_no_binding"><![CDATA[<Sin Asignar>]]></string>
|
||||
<string name="controller_binding_dialog_cancel">Cancelar</string>
|
||||
<string name="controller_binding_dialog_clear">Limpiar</string>
|
||||
<string name="controller_mapping_activity_title">Mapeado de Control</string>
|
||||
<string name="controller_mapping_activity_no_profiles_found">No se encontraron perfiles.</string>
|
||||
<string name="controller_mapping_activity_select_input_profile">Seleccionar perfil de control</string>
|
||||
<string name="controller_mapping_activity_failed_to_load_profile">Fallo al cargar el perfil \'%s\'</string>
|
||||
<string name="controller_mapping_activity_input_profile_name">Ingrese Nombre de Perfil:</string>
|
||||
<string name="controller_mapping_activity_save">Guardar</string>
|
||||
<string name="controller_mapping_activity_name_must_be_provided">Debe proporcionarse un nombre.</string>
|
||||
<string name="controller_mapping_activity_failed_to_save_input_profile">Fallo al guardar perfil de control.</string>
|
||||
<string name="controller_mapping_activity_input_profile_saved">Perfil de control \'%s\' guardado.</string>
|
||||
<string name="controller_mapping_activity_cancel">Cancelar</string>
|
||||
<string name="settings_use_analog_sticks_for_dpad">Usar las palancas analógicas como cruceta en el modo digital</string>
|
||||
<string name="settings_summary_enable_analog_mode_on_reset">Fuerza el control al modo analógico cuando la consola se reinicia/enciende</string>
|
||||
<string name="settings_summary_use_analog_sticks_for_dpad">Te permite usar las palancas analógicas para controlar la cruceta y los botones en modo digital.</string>
|
||||
<string name="settings_disable_all_enhancements">Desactiva todas las mejoras</string>
|
||||
<string name="settings_summary_disable_all_enhancements">Temporalmente desactiva todas las mejoras, el cual puede ser útil al depurar problemas.</string>
|
||||
<string name="settings_downsample_mode">Submuestreo</string>
|
||||
<string name="activity_game_properties">Propiedades del Juego</string>
|
||||
<string name="game_properties_preference_use_global_setting">Usar configuración global</string>
|
||||
<string name="settings_input_profile">Perfil de Control</string>
|
||||
<string name="game_properties_tab_summary">Resumen</string>
|
||||
<string name="game_properties_tab_game_settings">Configuraciones de juego</string>
|
||||
<string name="game_properties_tab_controller_settings">Configuraciones de Control</string>
|
||||
<string name="settings_audio_resampling">Remuestreo de Audio</string>
|
||||
<string name="settings_summary_audio_resampling">Cuando se ejecuta por encima del 100% de velocidad, vuelve a muestrear el audio de la velocidad objetivo en lugar de eliminar fotogramas. Produce un audio de avance/desaceleración mucho más agradable a un pequeño costo del rendimiento.</string>
|
||||
<string name="settings_general_sync_to_host_refresh_rate">Sincronizar con la velocidad de actualización del anfitrión</string>
|
||||
<string name="settings_summary_general_sync_to_host_refresh_rate">Ajusta la velocidad de emulación para que la frecuencia de actualización de la consola coincida con la frecuencia de actualización del anfitrión cuando las configuraciones de VSync y Remuestreo de audio estén habilitados. Esto da como resultado las animaciones más suaves posibles, a costa de aumentar potencialmente la velocidad de emulación en menos del 1%.</string>
|
||||
<string name="settings_sustained_performance_mode">Modo de Rendimiento Sostenido</string>
|
||||
<string name="settings_summary_sustained_performance_mode">Habilita el Modo de Rendimiento Sostenido por Android. Puede resultar en velocidades de fotogramas más consistentes para sesiones largas en algunos dispositivos.</string>
|
||||
<string name="title_activity_game_directories">Editar Directorio de juegos</string>
|
||||
<string name="settings_game_directories">Directorio de juegos</string>
|
||||
<string name="settings_summary_game_directories">Cambia la lista de directorio que se utiliza para buscar juegos.</string>
|
||||
<string name="game_directories_scanning_subdirectories">Escaneo de subdirectorios.</string>
|
||||
<string name="game_directories_not_scanning_subdirectories">No escanear subdirectorios.</string>
|
||||
<string name="settings_display_all_frames">Optimizar Frame Pacing</string>
|
||||
<string name="settings_summary_display_all_frames">Habilitar esta opción asegurará que cada fotograma que la consola renderice se muestre en la pantalla, para un ritmo de fotograma óptimo. Si tiene dificultades para mantener la velocidad máxima o problemas de audio, intente desactivar esta opción.</string>
|
||||
</resources>
|
||||
221
android/app/src/main/res/values-it/arrays.xml
Normal file
221
android/app/src/main/res/values-it/arrays.xml
Normal file
@@ -0,0 +1,221 @@
|
||||
<resources>
|
||||
<string-array name="settings_console_region_entries">
|
||||
<item>Auto-Rileva</item>
|
||||
<item>NTSC-J (Giappone)</item>
|
||||
<item>NTSC-U (US, Canada)</item>
|
||||
<item>PAL (Europa, Australia)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cpu_execution_mode_entries">
|
||||
<item>Interprete (Il più lento)</item>
|
||||
<item>Cached Interpreter (Più veloce)</item>
|
||||
<item>Recompiler (Il più veloce)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cpu_fastmem_mode_entries">
|
||||
<item>Disabilitato (Il più lento)</item>
|
||||
<item>MMap (Hardware, il più veloce, Solo 64-Bit)</item>
|
||||
<item>LUT (Più veloce)</item>
|
||||
</string-array>
|
||||
<string-array name="gpu_renderer_entries">
|
||||
<item>Hardware (OpenGL)</item>
|
||||
<item>Hardware (Vulkan)</item>
|
||||
<item>Software</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_resolution_scale_entries">
|
||||
<item>1x</item>
|
||||
<item>2x</item>
|
||||
<item>3x (per 720p)</item>
|
||||
<item>4x</item>
|
||||
<item>5x (per 1080p)</item>
|
||||
<item>6x (per 1440p)</item>
|
||||
<item>7x</item>
|
||||
<item>8x</item>
|
||||
<item>9x (per 4K)</item>
|
||||
<item>10x</item>
|
||||
<item>11x</item>
|
||||
<item>12x</item>
|
||||
<item>13x</item>
|
||||
<item>14x</item>
|
||||
<item>15x</item>
|
||||
<item>16x</item>
|
||||
</string-array>
|
||||
<string-array name="settings_display_crop_mode_entries">
|
||||
<item>None</item>
|
||||
<item>Solo area di Overscan</item>
|
||||
<item>Tutti i bordi</item>
|
||||
</string-array>
|
||||
<string-array name="settings_display_aspect_ratio_names">
|
||||
<item>Auto (Game Native)</item>
|
||||
<item>4:3</item>
|
||||
<item>16:9</item>
|
||||
<item>16:10</item>
|
||||
<item>19:9</item>
|
||||
<item>20:9</item>
|
||||
<item>21:9</item>
|
||||
<item>32:9</item>
|
||||
<item>8:7</item>
|
||||
<item>5:4</item>
|
||||
<item>3:2</item>
|
||||
<item>2:1 (VRAM 1:1)</item>
|
||||
<item>1:1</item>
|
||||
<item>PAR 1:1</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_texture_filter_names">
|
||||
<item>Nearest-Neighbor</item>
|
||||
<item>Bilineare</item>
|
||||
<item>Bilineare (No Blending Bordi)</item>
|
||||
<item>JINC2</item>
|
||||
<item>JINC2 (No Blending Bordi)</item>
|
||||
<item>xBR</item>
|
||||
<item>xBR (No Blending Bordi)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_controller_type_entries">
|
||||
<item>Controller Digitale (Gamepad)</item>
|
||||
<item>Controller Analogico (DualShock)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_memory_card_mode_entries">
|
||||
<item>No Memory Card</item>
|
||||
<item>Condivisa fra tutti i giochi</item>
|
||||
<item>MC separata per ogni gioco (Codice Gioco)</item>
|
||||
<item>MC separata per ogni gioco (Titolo Gioco)</item>
|
||||
</string-array>
|
||||
<string-array name="emulation_menu">
|
||||
<item>Carica Stato</item>
|
||||
<item>Salva Stato</item>
|
||||
<item>Abilita/Disabilita Avanti Veloce</item>
|
||||
<item>Altre Opzioni</item>
|
||||
<item>Esci</item>
|
||||
</string-array>
|
||||
<string-array name="emulation_more_menu">
|
||||
<item>Reset</item>
|
||||
<item>Codici Patch</item>
|
||||
<item>Cambia Disco</item>
|
||||
<item>Impostazioni</item>
|
||||
<item>Cambia Controller Touchscreen</item>
|
||||
<item>Edit Touchscreen Controller Layout</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cdrom_read_speedup_entries">
|
||||
<item>Nessuna Velocità Doppia)</item>
|
||||
<item>2x (Velocità Quadrupla</item>
|
||||
<item>3x (Velocità 6x)</item>
|
||||
<item>4x (Velocità 8x)</item>
|
||||
<item>5x (Velocità 10x)</item>
|
||||
<item>6x (Velocità 12x)</item>
|
||||
<item>7x (Velocità 14x)</item>
|
||||
<item>8x (Velocità 16x)</item>
|
||||
<item>9x (Velocità 18x)</item>
|
||||
<item>10x (Velocità 20x)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_touchscreen_controller_view_entries">
|
||||
<item>Nessuno</item>
|
||||
<item>Pad Digitale</item>
|
||||
<item>Pad Analogico Singolo</item>
|
||||
<item>Pad Analogico Doppio</item>
|
||||
</string-array>
|
||||
<string-array name="settings_audio_backend_entries">
|
||||
<item>Null (No Output)</item>
|
||||
<item>Cubeb</item>
|
||||
<item>OpenSL ES (Raccomandato)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_audio_buffer_size_entries">
|
||||
<item>1024 Fotogrammi (23.22ms)</item>
|
||||
<item>2048 Fotogrammi (46.44ms, Raccomandato)</item>
|
||||
<item>3072 Fotogrammi (69.66ms)</item>
|
||||
<item>4096 Fotogrammi (92.88ms)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_log_level_entries">
|
||||
<item>None</item>
|
||||
<item>Error</item>
|
||||
<item>Warning</item>
|
||||
<item>Performance Warnings</item>
|
||||
<item>Information</item>
|
||||
<item>Verbose</item>
|
||||
<item>Developer</item>
|
||||
<item>Profile</item>
|
||||
<item>Debug</item>
|
||||
<item>Trace</item>
|
||||
</string-array>
|
||||
<string-array name="settings_tabs">
|
||||
<item>Generali</item>
|
||||
<item>Display</item>
|
||||
<item>Audio</item>
|
||||
<item>Miglioramenti</item>
|
||||
<item>Controller</item>
|
||||
<item>Avanzate</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_msaa_entries">
|
||||
<item>Disabilitato</item>
|
||||
<item>2x MSAA</item>
|
||||
<item>4x MSAA</item>
|
||||
<item>8x MSAA</item>
|
||||
<item>2x SSAA</item>
|
||||
<item>4x SSAA</item>
|
||||
<item>8x SSAA</item>
|
||||
</string-array>
|
||||
<string-array name="settings_advanced_display_fps_limit_entries">
|
||||
<item>Senza Limite (Mostra tutti i fotogrammi)</item>
|
||||
<item>10 FPS</item>
|
||||
<item>15 FPS</item>
|
||||
<item>30 FPS</item>
|
||||
<item>45 FPS</item>
|
||||
<item>60 FPS</item>
|
||||
<item>75 FPS</item>
|
||||
<item>90 FPS</item>
|
||||
</string-array>
|
||||
<string-array name="settings_emulation_speed_entries">
|
||||
<item>Senza Limite</item>
|
||||
<item>10% [6 FPS (NTSC) / 5 FPS (PAL)]</item>
|
||||
<item>20% [12 FPS (NTSC) / 10 FPS (PAL)]</item>
|
||||
<item>30% [18 FPS (NTSC) / 15 FPS (PAL)]</item>
|
||||
<item>40% [24 FPS (NTSC) / 20 FPS (PAL)]</item>
|
||||
<item>50% [30 FPS (NTSC) / 25 FPS (PAL)]</item>
|
||||
<item>60% [36 FPS (NTSC) / 30 FPS (PAL)]</item>
|
||||
<item>70% [42 FPS (NTSC) / 35 FPS (PAL)]</item>
|
||||
<item>80% [48 FPS (NTSC) / 40 FPS (PAL)]</item>
|
||||
<item>90% [54 FPS (NTSC) / 45 FPS (PAL)]</item>
|
||||
<item>100% [60 FPS (NTSC) / 50 FPS (PAL), Default]</item>
|
||||
<item>125% [75 FPS (NTSC) / 62 FPS (PAL)]</item>
|
||||
<item>150% [90 FPS (NTSC) / 75 FPS (PAL)]</item>
|
||||
<item>175% [105 FPS (NTSC) / 87 FPS (PAL)]</item>
|
||||
<item>200% [120 FPS (NTSC) / 100 FPS (PAL)]</item>
|
||||
<item>250% [150 FPS (NTSC) / 125 FPS (PAL)]</item>
|
||||
<item>300% [180 FPS (NTSC) / 150 FPS (PAL)]</item>
|
||||
<item>350% [210 FPS (NTSC) / 175 FPS (PAL)]</item>
|
||||
<item>400% [240 FPS (NTSC) / 200 FPS (PAL)]</item>
|
||||
<item>450% [270 FPS (NTSC) / 225 FPS (PAL)]</item>
|
||||
<item>500% [300 FPS (NTSC) / 250 FPS (PAL)]</item>
|
||||
<item>600% [360 FPS (NTSC) / 300 FPS (PAL)]</item>
|
||||
<item>700% [420 FPS (NTSC) / 350 FPS (PAL)]</item>
|
||||
<item>800% [480 FPS (NTSC) / 400 FPS (PAL)]</item>
|
||||
<item>900% [540 FPS (NTSC) / 450 FPS (PAL)]</item>
|
||||
<item>1000% [600 FPS (NTSC) / 500 FPS (PAL)]</item>
|
||||
</string-array>
|
||||
<string-array name="settings_advanced_cpu_overclock_entries">
|
||||
<item>25% (8MHz)</item>
|
||||
<item>50% (16MHz)</item>
|
||||
<item>75% (24MHz)</item>
|
||||
<item>100% (33MHz, Default)</item>
|
||||
<item>125% (41MHz)</item>
|
||||
<item>150% (49MHz)</item>
|
||||
<item>175% (57MHz)</item>
|
||||
<item>200% (66MHz)</item>
|
||||
<item>225% (74MHz)</item>
|
||||
<item>250% (82MHz)</item>
|
||||
<item>275% (90MHz)</item>
|
||||
<item>300% (99MHz)</item>
|
||||
<item>350% (115MHz)</item>
|
||||
<item>400% (132MHz)</item>
|
||||
<item>450% (148MHz)</item>
|
||||
<item>500% (165MHz)</item>
|
||||
<item>500% (165MHz)</item>
|
||||
<item>600% (198MHz)</item>
|
||||
<item>700% (231MHz)</item>
|
||||
<item>800% (264MHz)</item>
|
||||
<item>900% (297MHz)</item>
|
||||
<item>1000% (330MHz)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_emulation_screen_orientation_entries">
|
||||
<item>Usa Impostazione Dispositivo</item>
|
||||
<item>Verticale</item>
|
||||
<item>Orizzontale</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
16
android/app/src/main/res/values-it/strings.xml
Normal file
16
android/app/src/main/res/values-it/strings.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<resources>
|
||||
<string name="app_name">DuckStation</string>
|
||||
<string name="action_settings">Impostazioni</string>
|
||||
<string name="title_activity_settings">Impostazioni</string>
|
||||
<string name="settings_console_region">Regione Console</string>
|
||||
<string name="settings_console_tty_output">Abilita TTY Output</string>
|
||||
<string name="settings_console_fast_boot">Avvio Rapido</string>
|
||||
<string name="settings_osd_show_messages">Mostra Messaggi</string>
|
||||
<string name="settings_osd_show_speed">Mostra Velocità Emulazione</string>
|
||||
<string name="settings_osd_show_show_fps">Mostra FPS</string>
|
||||
<string name="settings_osd_show_show_vps">Mostra VPS</string>
|
||||
<string name="settings_cpu_execution_mode">Modalità di Esecuzione CPU</string>
|
||||
<string name="settings_gpu_renderer">GPU Renderer</string>
|
||||
<string name="settings_gpu_resolution_scale">Scala di Risoluzione</string>
|
||||
<string name="title_activity_emulation">EmulationActivity</string>
|
||||
</resources>
|
||||
10
android/app/src/main/res/values-night/colors.xml
Normal file
10
android/app/src/main/res/values-night/colors.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="colorPrimary">#000000</color>
|
||||
<color name="colorPrimaryDark">#000000</color>
|
||||
<color name="colorAccent">#03A9F4</color>
|
||||
<color name="colorNavigation">#000000</color>
|
||||
|
||||
<color name="black_overlay">#66000000</color>
|
||||
<color name="fab_background">#222222</color>
|
||||
</resources>
|
||||
221
android/app/src/main/res/values-nl/arrays.xml
Normal file
221
android/app/src/main/res/values-nl/arrays.xml
Normal file
@@ -0,0 +1,221 @@
|
||||
<resources>
|
||||
<string-array name="settings_console_region_entries">
|
||||
<item>Automatisch Detecteren</item>
|
||||
<item>NTSC-J (Japan)</item>
|
||||
<item>NTSC-U (VS)</item>
|
||||
<item>PAL (Europa, Australië)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cpu_execution_mode_entries">
|
||||
<item>Interpreter (Langzaamst)</item>
|
||||
<item>Cached Interpreter (Sneller)</item>
|
||||
<item>Recompiler (Snelst)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cpu_fastmem_mode_entries">
|
||||
<item>Uitgeschakeld (Langzaamst)</item>
|
||||
<item>MMap (Hardware, Snelst, Alleen 64-Bit)</item>
|
||||
<item>LUT (Sneller)</item>
|
||||
</string-array>
|
||||
<string-array name="gpu_renderer_entries">
|
||||
<item>Hardware (OpenGL)</item>
|
||||
<item>Hardware (Vulkan)</item>
|
||||
<item>Software</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_resolution_scale_entries">
|
||||
<item>1x</item>
|
||||
<item>2x</item>
|
||||
<item>3x (voor 720p)</item>
|
||||
<item>4x</item>
|
||||
<item>5x (voor 1080p)</item>
|
||||
<item>6x (voor 1440p)</item>
|
||||
<item>7x</item>
|
||||
<item>8x</item>
|
||||
<item>9x (voor 4K)</item>
|
||||
<item>10x</item>
|
||||
<item>11x</item>
|
||||
<item>12x</item>
|
||||
<item>13x</item>
|
||||
<item>14x</item>
|
||||
<item>15x</item>
|
||||
<item>16x</item>
|
||||
</string-array>
|
||||
<string-array name="settings_display_crop_mode_entries">
|
||||
<item>Geen</item>
|
||||
<item>Alleen Overscangebied</item>
|
||||
<item>Alle Randen</item>
|
||||
</string-array>
|
||||
<string-array name="settings_display_aspect_ratio_names">
|
||||
<item>Auto (Game Native)</item>
|
||||
<item>4:3</item>
|
||||
<item>16:9</item>
|
||||
<item>16:10</item>
|
||||
<item>19:9</item>
|
||||
<item>20:9</item>
|
||||
<item>21:9</item>
|
||||
<item>32:9</item>
|
||||
<item>8:7</item>
|
||||
<item>5:4</item>
|
||||
<item>3:2</item>
|
||||
<item>2:1 (VRAM 1:1)</item>
|
||||
<item>1:1</item>
|
||||
<item>PAR 1:1</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_texture_filter_names">
|
||||
<item>Dichtstbijzijde</item>
|
||||
<item>Bilineair</item>
|
||||
<item>Bilineair (Geen Edge Blending)</item>
|
||||
<item>JINC2</item>
|
||||
<item>JINC2 (Geen Edge Blending)</item>
|
||||
<item>xBR</item>
|
||||
<item>xBR (Geen Edge Blending)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_controller_type_entries">
|
||||
<item>Digitale Controller (Gamepad)</item>
|
||||
<item>Analoge Controller (DualShock)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_memory_card_mode_entries">
|
||||
<item>Geen Geheugenkaart</item>
|
||||
<item>Gedeeld Tussen Alle Spellen</item>
|
||||
<item>Aparte Kaart Per Spel (Spelcode)</item>
|
||||
<item>Aparte Kaart Per Spel (Speltitel)</item>
|
||||
</string-array>
|
||||
<string-array name="emulation_menu">
|
||||
<item>Staat Laden</item>
|
||||
<item>Staat Opslaan</item>
|
||||
<item>Doorspoelen aan/uitzetten</item>
|
||||
<item>Meer Opties</item>
|
||||
<item>Afsluiten</item>
|
||||
</string-array>
|
||||
<string-array name="emulation_more_menu">
|
||||
<item>Resetten</item>
|
||||
<item>Patch Codes</item>
|
||||
<item>Disc Veranderen</item>
|
||||
<item>Instellingen</item>
|
||||
<item>Touchscreen Controller Aanpassen</item>
|
||||
<item>Edit Touchscreen Controller Layout</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cdrom_read_speedup_entries">
|
||||
<item>Geen (Dubbele Snelheid)</item>
|
||||
<item>2x (Vierdubbele Snelheid)</item>
|
||||
<item>3x (6x Snelheid)</item>
|
||||
<item>4x (8x Snelheid)</item>
|
||||
<item>5x (10x Snelheid)</item>
|
||||
<item>6x (12x Snelheid)</item>
|
||||
<item>7x (14x Snelheid)</item>
|
||||
<item>8x (16x Snelheid)</item>
|
||||
<item>9x (18x Snelheid)</item>
|
||||
<item>10x (20x Snelheid)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_touchscreen_controller_view_entries">
|
||||
<item>Geen</item>
|
||||
<item>Digitale Pad</item>
|
||||
<item>Enekele Analoge Pad</item>
|
||||
<item>Dubbele Analoge Pad</item>
|
||||
</string-array>
|
||||
<string-array name="settings_audio_backend_entries">
|
||||
<item>Null (Geen Output)</item>
|
||||
<item>Cubeb</item>
|
||||
<item>OpenSL ES (Aanbevolen)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_audio_buffer_size_entries">
|
||||
<item>1024 Frames (23.22ms)</item>
|
||||
<item>2048 Frames (46.44ms, Recommended)</item>
|
||||
<item>3072 Frames (69.66ms)</item>
|
||||
<item>4096 Frames (92.88ms)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_log_level_entries">
|
||||
<item>Geen</item>
|
||||
<item>Fout</item>
|
||||
<item>Waarschuwing</item>
|
||||
<item>Uitgebreid</item>
|
||||
<item>Prestatie Waarschuwingen</item>
|
||||
<item>Informatie</item>
|
||||
<item>Ontwikkelaar</item>
|
||||
<item>Profiel</item>
|
||||
<item>Debug</item>
|
||||
<item>Trace</item>
|
||||
</string-array>
|
||||
<string-array name="settings_tabs">
|
||||
<item>Algemeen</item>
|
||||
<item>Weergave</item>
|
||||
<item>Audio</item>
|
||||
<item>Verbeteringen</item>
|
||||
<item>Controllers</item>
|
||||
<item>Geavanceerd</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_msaa_entries">
|
||||
<item>Uitgeschakeld</item>
|
||||
<item>2x MSAA</item>
|
||||
<item>4x MSAA</item>
|
||||
<item>8x MSAA</item>
|
||||
<item>2x SSAA</item>
|
||||
<item>4x SSAA</item>
|
||||
<item>8x SSAA</item>
|
||||
</string-array>
|
||||
<string-array name="settings_advanced_display_fps_limit_entries">
|
||||
<item>Onbegrensd (Alle Frames Weergeven)</item>
|
||||
<item>10 FPS</item>
|
||||
<item>15 FPS</item>
|
||||
<item>30 FPS</item>
|
||||
<item>45 FPS</item>
|
||||
<item>60 FPS</item>
|
||||
<item>75 FPS</item>
|
||||
<item>90 FPS</item>
|
||||
</string-array>
|
||||
<string-array name="settings_emulation_speed_entries">
|
||||
<item>Onbegrensd</item>
|
||||
<item>10% [6 FPS (NTSC) / 5 FPS (PAL)]</item>
|
||||
<item>20% [12 FPS (NTSC) / 10 FPS (PAL)]</item>
|
||||
<item>30% [18 FPS (NTSC) / 15 FPS (PAL)]</item>
|
||||
<item>40% [24 FPS (NTSC) / 20 FPS (PAL)]</item>
|
||||
<item>50% [30 FPS (NTSC) / 25 FPS (PAL)]</item>
|
||||
<item>60% [36 FPS (NTSC) / 30 FPS (PAL)]</item>
|
||||
<item>70% [42 FPS (NTSC) / 35 FPS (PAL)]</item>
|
||||
<item>80% [48 FPS (NTSC) / 40 FPS (PAL)]</item>
|
||||
<item>90% [54 FPS (NTSC) / 45 FPS (PAL)]</item>
|
||||
<item>100% [60 FPS (NTSC) / 50 FPS (PAL), Default]</item>
|
||||
<item>125% [75 FPS (NTSC) / 62 FPS (PAL)]</item>
|
||||
<item>150% [90 FPS (NTSC) / 75 FPS (PAL)]</item>
|
||||
<item>175% [105 FPS (NTSC) / 87 FPS (PAL)]</item>
|
||||
<item>200% [120 FPS (NTSC) / 100 FPS (PAL)]</item>
|
||||
<item>250% [150 FPS (NTSC) / 125 FPS (PAL)]</item>
|
||||
<item>300% [180 FPS (NTSC) / 150 FPS (PAL)]</item>
|
||||
<item>350% [210 FPS (NTSC) / 175 FPS (PAL)]</item>
|
||||
<item>400% [240 FPS (NTSC) / 200 FPS (PAL)]</item>
|
||||
<item>450% [270 FPS (NTSC) / 225 FPS (PAL)]</item>
|
||||
<item>500% [300 FPS (NTSC) / 250 FPS (PAL)]</item>
|
||||
<item>600% [360 FPS (NTSC) / 300 FPS (PAL)]</item>
|
||||
<item>700% [420 FPS (NTSC) / 350 FPS (PAL)]</item>
|
||||
<item>800% [480 FPS (NTSC) / 400 FPS (PAL)]</item>
|
||||
<item>900% [540 FPS (NTSC) / 450 FPS (PAL)]</item>
|
||||
<item>1000% [600 FPS (NTSC) / 500 FPS (PAL)]</item>
|
||||
</string-array>
|
||||
<string-array name="settings_advanced_cpu_overclock_entries">
|
||||
<item>25% (8MHz)</item>
|
||||
<item>50% (16MHz)</item>
|
||||
<item>75% (24MHz)</item>
|
||||
<item>100% (33MHz, Default)</item>
|
||||
<item>125% (41MHz)</item>
|
||||
<item>150% (49MHz)</item>
|
||||
<item>175% (57MHz)</item>
|
||||
<item>200% (66MHz)</item>
|
||||
<item>225% (74MHz)</item>
|
||||
<item>250% (82MHz)</item>
|
||||
<item>275% (90MHz)</item>
|
||||
<item>300% (99MHz)</item>
|
||||
<item>350% (115MHz)</item>
|
||||
<item>400% (132MHz)</item>
|
||||
<item>450% (148MHz)</item>
|
||||
<item>500% (165MHz)</item>
|
||||
<item>500% (165MHz)</item>
|
||||
<item>600% (198MHz)</item>
|
||||
<item>700% (231MHz)</item>
|
||||
<item>800% (264MHz)</item>
|
||||
<item>900% (297MHz)</item>
|
||||
<item>1000% (330MHz)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_emulation_screen_orientation_entries">
|
||||
<item>Apparaatinstellingen Gebruiken</item>
|
||||
<item>Portret</item>
|
||||
<item>Landschap</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
154
android/app/src/main/res/values-nl/strings.xml
Normal file
154
android/app/src/main/res/values-nl/strings.xml
Normal file
@@ -0,0 +1,154 @@
|
||||
<resources>
|
||||
<string name="app_name">DuckStation</string>
|
||||
<string name="action_settings">Instellingen</string>
|
||||
<string name="title_activity_settings">Instellingen</string>
|
||||
<string name="settings_console_region">Console Gebied</string>
|
||||
<string name="settings_console_tty_output">TTY Output Aanzetten</string>
|
||||
<string name="settings_console_fast_boot">Snel Starten</string>
|
||||
<string name="settings_osd_show_messages">Toon Berichten</string>
|
||||
<string name="settings_osd_show_speed">Toon Emulatiesnelheid</string>
|
||||
<string name="settings_osd_show_show_fps">Toon FPS</string>
|
||||
<string name="settings_osd_show_show_vps">Toon VPS</string>
|
||||
<string name="settings_cpu_execution_mode">CPU Executie Modus</string>
|
||||
<string name="settings_gpu_renderer">GPU Renderer</string>
|
||||
<string name="settings_gpu_resolution_scale">Resolutie Schaal</string>
|
||||
<string name="title_activity_emulation">Emulatie Activiteit</string>
|
||||
<string name="settings_emulation_speed">Emulatie Snelheid</string>
|
||||
<string name="settings_fast_forward_speed">Doorspoelsnelheid</string>
|
||||
<string name="settings_save_state_on_exit">Staat Opslaan Bij Afsluiten</string>
|
||||
<string name="settings_pause_when_menu_opened">Pauze In Menus</string>
|
||||
<string name="settings_emulation_screen_orientation">Emulatie Schermorientatie</string>
|
||||
<string name="settings_load_patch_codes">Laad Patch Codes</string>
|
||||
<string name="settings_summary_save_state_on_exit">Slaat automatisch de emulator staat op bij afsluiten. Je kan dan de volgende keer verder vanaf waar je gebleven was.</string>
|
||||
<string name="settings_summary_pause_when_menu_opened">Pauzeert de emulatie wanneer je in een spel bent en het menu geopend is.</string>
|
||||
<string name="settings_summary_load_patch_codes"><![CDATA[Laad patch codes van cheats/<game name>.cht in PCSXR formaat. Codes kunnen aan- en uitgeschakeld worden in-game.]]></string>
|
||||
<string name="settings_apply_compatibility_settings">Pas Compatibiliteitsinstellingen Toe</string>
|
||||
<string name="settings_summary_apply_compatibility_settings">Schakelt automatisch verbeteringen uit wanneer ze niet door spellen ondersteund worden.</string>
|
||||
<string name="settings_summary_video_sync">Schakel deze optie in om DuckStation\'s verversingssnelheid te synchroniseren met je scherm. VSync is automatisch uitegeschakeld wanneer dit niet mogelijk is (bijvoorbeeld wanneer er niet op 100% snelheid gespeeld wordt).</string>
|
||||
<string name="settings_video_sync">Video Sync</string>
|
||||
<string name="settings_cpu_overclocking">CPU Overclocken</string>
|
||||
<string name="settings_cdrom_region_check">CD-ROM Regio Check</string>
|
||||
<string name="settings_summary_cdrom_region_check">Voorkomt dat discs uit incorrecte regio\'s geladen worden door de emulator. Over het algemeen veilig om uit te schakelen.</string>
|
||||
<string name="settings_cdrom_preload_image_to_ram">CD-ROM Image Voorladen naar RAM</string>
|
||||
<string name="settings_summary_preload_image_to_ram">Laadt de image van het spel in RAM. Handig voor netwerkpaden die instabiel kunnen worden tijdens het spelen van het spelen. Kan soms ook haperingen voorkomen wanneer spellen audio instellen.</string>
|
||||
<string name="settings_pgxp_vertex_cache">PGXP Vertex Cache</string>
|
||||
<string name="settings_summary_pgxp_vertex_cache">Gebruikt schermcoordinaten als terugval wanneer vertices niet door het geheugen getraceerd kunnen worden. Kan PGXP compatibiliteit verbeteren.</string>
|
||||
<string name="settings_pgxp_cpu_mode">PGXP CPU Modus</string>
|
||||
<string name="settings_summary_pgxp_cpu_mode">Probeert vertex manupulatie te traceren door de CPU. Sommige spellen vereisen deze optie om PGXP te gebruiken. Heel langzaam, en incompatibel met de recompiler.</string>
|
||||
<string name="settings_cpu_recompiler_icache">CPU Recompiler ICache</string>
|
||||
<string name="settings_summary_cpu_recompiler_icache">Bepaalt of de CPU\'s instructie cache gesimuleerd wordt in de recompiler. Verbetert precisie, maar kost een klein beetje prestatie. Als games te snel afspelen, probeer dan deze optie aan te zetten.</string>
|
||||
<string name="settings_cpu_recompiler_fastmem">CPU Recompiler Fast Memory Access</string>
|
||||
<string name="settings_summary_cpu_recompiler_fastmem">Maakt geheugentoegang van het gesimuleerde systeem meer efficient door page faults en backpatching te gebruiken. Schakel dit uit als het instabiel is op je systeem.</string>
|
||||
<string name="settings_presented_frame_limit">Getoonde Frame Begrenzing</string>
|
||||
<string name="settings_logging_level">Logging Niveau</string>
|
||||
<string name="settings_log_to_file">Log Naar Bestand</string>
|
||||
<string name="settings_summary_log_to_file">Schrijft de log naar duckstation.log in je gebruikersfolder. Gebruik dit alleen voor debuggen, want het kan vertraagt de emulatie.</string>
|
||||
<string name="settings_log_to_logcat">Log Naar Logcat</string>
|
||||
<string name="settings_summary_log_to_logcat">Schrijft logberichten naar de Android berichtenlog. Alleen nuttig wanneer gebruikt in combinatie met een computer met adb.</string>
|
||||
<string name="settings_volume">Volume</string>
|
||||
<string name="settings_summary_volume">Bedient het volume van de emulator.</string>
|
||||
<string name="settings_mute_all_sound">Alles Dempen</string>
|
||||
<string name="settings_summary_mute_all_sound">Voorkomt dat de emulator geluid produceert.</string>
|
||||
<string name="settings_mute_cd_audio">CD Audio Dempen</string>
|
||||
<string name="settings_summary_mute_cd_audio">Dempt zowel CD-DA en XA audio van de CD-ROM. Kan gebruikt worden om achtergrondmuziek uit te schakelen in sommige spellen.</string>
|
||||
<string name="settings_audio_backend">Audio Backend</string>
|
||||
<string name="settings_audio_buffer_size">Audio Buffer Grootte</string>
|
||||
<string name="settings_summary_audio_buffer_size">Bepaald de vertraging tussen de audiogeneratie en de output van de speakers. Kleinere waardes kunnen vertraging verkleinen, maar variaties in emulatiesnelheid kunnen voor haperingen zorgen.</string>
|
||||
<string name="settings_audio_sync">Audio Sync</string>
|
||||
<string name="settings_summary_audio_sync">Beperkt de emulatiesnelheid gebaseerd op hoe snel de audio backend samples opvraagt. Dit helpt om ruis en kraken te voorkomen wanneer de emulator te snel gaat. Wordt automatisch uitgeschakeld wanneer er niet op 100% snelheid afgespeeld wordt.</string>
|
||||
<string name="settings_controller_type">Controller Type</string>
|
||||
<string name="settings_enable_analog_mode_on_reset">Analoge Modus Bij Reset.</string>
|
||||
<string name="settings_touchscreen_controller_view">Touchscreen Controller Beeld</string>
|
||||
<string name="settings_auto_hide_touchscreen_controller">Automatisch Touchscreen Controller Verbergen</string>
|
||||
<string name="settings_summary_auto_hide_touchscreen_controller">Verbergt de touchscreen controller wanneer een externe controller gedetecteerd wordt.</string>
|
||||
<string name="settings_vibrate_on_press">Vibreren Bij Indrukking</string>
|
||||
<string name="settings_summary_vibrate_on_press">Schakelt een kleine trilling in wanneer een touchscreenknop ingedrukt wordt. Vereist dat \"Trillen bij aanraking\" ingeschakeld is op je apparaat.</string>
|
||||
<string name="settings_enable_vibration">Vibratie Inschakelen</string>
|
||||
<string name="settings_summary_enable_vibration">Vibreert de telefoon wanneer de spel dit voor een controller doet.</string>
|
||||
<string name="settings_memory_card_1_type">Geheugenkaart 1 Type</string>
|
||||
<string name="settings_memory_card_2_type">Geheugenkaart 2 Type</string>
|
||||
<string name="settings_crop_mode">Knip Modus</string>
|
||||
<string name="settings_aspect_ratio">Schermverhouding</string>
|
||||
<string name="settings_linear_upscaling">Lineaire Opschaling</string>
|
||||
<string name="settings_integer_upscaling">Integer Opschaling</string>
|
||||
<string name="settings_summary_linear_upscaling">Interpoleert tussen pixels wanneer het beeld opgeschaald wordt naar het apparaat.</string>
|
||||
<string name="settings_summary_integer_upscaling">Voegt randen toe aan het beeld zodat de verhouding tussen pixels op het apparaat, en pixels van de console een geheel getal is. Kan voor een scherper beeld zorgen in sommige 2D games.</string>
|
||||
<string name="settings_summary_osd_show_messages">Toont berichten op het scherm wanneer gebeurtenissen als het opslaan/laden van een spelstatus, schermafbeeldingen maken, etc. gebeuren.</string>
|
||||
<string name="settings_summary_osd_show_speed">Zet een doelsnelheid voor de emulatie. Het is niet gegarandeerd dat deze snelheid behaald wordt. Zo niet, dan gaat de emulator zo snel als hij kan.</string>
|
||||
<string name="settings_summary_osd_show_fps">Toont de interne framesnelheid van het spel in de rechter bovenhoek van het scherm.</string>
|
||||
<string name="settings_summary_osd_show_vps">Toont het aantal frames (of v-syncs) van het systeem in de rechter bovenhoek van het scherm.</string>
|
||||
<string name="settings_cdrom_read_speedup">CD-ROM Leesversnelling</string>
|
||||
<string name="settings_summary_cdrom_read_speedup">Versnelt de CD-ROM leessnelheid met de gegeven factor. Alleen van toepassing op dubbele-snelheids leesoperaties, en genegeerd wanneer audio afspeelt. Kan laadsnelheden vergroten bij sommige spellen, maar kan andere spellen niet goed laten werken.</string>
|
||||
<string name="settings_summary_console_fast_boot">Slaat de BIOS intro over, en start direct het spel. Over het algemeen veilig om in te schakelen, maar sommige spellen werken niet goed meer.</string>
|
||||
<string name="settings_msaa">Multisample Antialiasing</string>
|
||||
<string name="settings_true_color">True Color Rendering (24-bit, schakelt dithering uit)</string>
|
||||
<string name="settings_summary_true_color">Dit produceert mooiere gradienten, maar kan sommige kleuren een klein beetje verkleuren. Uitschakelen schakelt ook dithering uit. De meeste spellen zijn compatibel met deze optie.</string>
|
||||
<string name="settings_scaled_dithering">Geschaalde Dithering (schaal dither patrron naar resolutie)</string>
|
||||
<string name="settings_summary_scaled_dithering">Schaalt het dither patroon naar de resolutieschaal van de geëmuleerde GPU. Dit maakt het ditherpatroon minder duidelijk bij hogere resoluties. Over het algemeen veilig in te schakelen. Werkt alleen bij hardware renderers.</string>
|
||||
<string name="settings_disable_interlacing">Interlacing Uitschakelen(forceer progressieve render/scan)</string>
|
||||
<string name="settings_summary_disable_interlacing">Forceert het renderen en weergeven van frames naar de progressieve modus. Dit verwijdert het \"kam\" effect gezien in 480i spellen door ze in 480p te rendreren. Meestal veilig om in te schakelen.</string>
|
||||
<string name="settings_texture_filtering">Texture Filtering</string>
|
||||
<string name="settings_force_ntsc_timings">Forceer NTSC Timings (60hz-on-PAL)</string>
|
||||
<string name="settings_summary_force_ntsc_timings">Gebruikt NTSC frame timings wanneer de console in PAL modus is. Dwingt PAL spellen om in 60hz af te spelen.</string>
|
||||
<string name="settings_widescreen_hack">Widescreen Hack</string>
|
||||
<string name="settings_summary_widescreen_hack">Schaalt vertex posities in schermruimte op naar een breedbeeldverhouding, wat in essentie het gezichtsveld van 4:3 naar 16:9 verhoogt in 3D spellen. Niet compatibel met alle spellen.</string>
|
||||
<string name="settings_force_4_3_for_24bit">Forceer 4:3 Voor 24-Bit Schermen</string>
|
||||
<string name="settings_summary_force_4_3_for_24bit">Gaat terug naar een 4:3 schermverhouding wanneer 24-bit inhoud weergeven wordt, meestal FMVs.</string>
|
||||
<string name="settings_chroma_smoothing_24bit">Chroma Smoothing Voor 24-Bit Schermen</string>
|
||||
<string name="settings_summary_chrome_smoothing_24bit">Vermindert de blokkerigheid bij kleurtransities van 24-bit inhoud, meestal FMVs. Alleen van toepassing bij hardware renderers.</string>
|
||||
<string name="settings_pgxp_geometry_correction">PGXP Geometry Correction</string>
|
||||
<string name="settings_summary_pgxp_geometry_correction"><![CDATA[Vermindert \"wiebelige\" polygons en \"vervormde\" textures die vaak in PS1 spellen voorkomen. >Werkt alleen met hardware renderers. Mogelijk niet compatibel met alle spellen.]]></string>
|
||||
<string name="settings_pgxp_culling_correction">PGXP Culling Correction</string>
|
||||
<string name="settings_summary_pgxp_culling_correction">Verhoogt de precisie van polygon culling, vermindert het aantal gaten in objecten. Vereist dat \"geometry correction\" ingeschakeld is.</string>
|
||||
<string name="settings_pgxp_texture_correction">PGXP Texture Correction</string>
|
||||
<string name="settings_summary_pgxp_texture_correction">Gebruikt perspectief-correcte interpolatie voor texture coordinaten en kleuren, maakt vervormde textures weer glad. Vereist dat \"geometry correction\" ingeschakeld is.</string>
|
||||
<string name="settings_pgxp_preserve_projection_precision">PGXP Behoud Projectie Precisie</string>
|
||||
<string name="settings_summary_pgxp_preserve_projection_precision">Schakelt extra precisie in voor PGXP. Kan beeld verbeteren in sommige spellen, maar kan andere minder goed laten werken.</string>
|
||||
<string name="menu_main_resume_last_session">Hervat Vorige Sessie</string>
|
||||
<string name="menu_main_start_file">Start Bestand</string>
|
||||
<string name="menu_main_start_bios">Start BIOS</string>
|
||||
<string name="menu_main_edit_game_directories">Bewerk Gamemappen</string>
|
||||
<string name="menu_main_scan_for_new_games">Scan Naar Nieuwe Spellen</string>
|
||||
<string name="menu_main_rescan_all_games">Alle Spellen Opnieuw Scannen</string>
|
||||
<string name="menu_main_import_bios">Importeer BIOS</string>
|
||||
<string name="menu_main_show_version">Toon Versie</string>
|
||||
<string name="menu_main_github_repository">GitHub Repository</string>
|
||||
<string name="menu_main_discord_server">Discord Server</string>
|
||||
<string name="menu_game_list_entry_start_game">Start Spel</string>
|
||||
<string name="menu_game_list_entry_resume_game">Hervat Spel</string>
|
||||
<string name="android_progress_callback_please_wait">Wachten alstublieft...</string>
|
||||
<string name="android_progress_callback_ok">Oké</string>
|
||||
<string name="android_progress_callback_information">Informatie</string>
|
||||
<string name="android_progress_callback_confirmation">Bevestiging</string>
|
||||
<string name="android_progress_callback_yes">Ja</string>
|
||||
<string name="android_progress_callback_no">Nee</string>
|
||||
<string name="emulation_activity_error">Fout</string>
|
||||
<string name="emulation_activity_ok">Oké</string>
|
||||
<string name="emulation_activity_import_patch_codes">Importeer Patch Codes...</string>
|
||||
<string name="emulation_activity_patch_on">(AAN)</string>
|
||||
<string name="emulation_activity_patch_off">(UIT)</string>
|
||||
<string name="emulation_activity_choose_patch_code_file">Kies Een Patch Code Bestand</string>
|
||||
<string name="emulation_activity_failed_to_import_patch_codes">Patch codes importeren mislukt. Selecteer een PCSXR of Libretro formaat bestand.</string>
|
||||
<string name="main_activity_choose_directory">Kies folder</string>
|
||||
<string name="main_activity_error">Fout</string>
|
||||
<string name="main_activity_get_path_from_file_error">Mislukt om het bestand op te halen. Zorg ervoor dat het bestand in de interne/externe opslag is.\n\nTik op de overflow knop in de folderselectie.\nSelecteer "Toon Interne Opslag".\nTik op de menuknop en selecteer uw apparaat of SD kaart.</string>
|
||||
<string name="main_activity_ok">Oké</string>
|
||||
<string name="main_activity_get_path_from_directory_error">Pad van folder ophalen mislukt.Zorg ervoor dat de folder in de interne/externe opslag is.\n\nTik op de overflow knop in de folderselectie.\nSelecteer "Toon Interne Opslag".\nTik op de menuknop en selecteer uw apparaat of SD kaart.</string>
|
||||
<string name="main_activity_external_storage_permissions_error">Toegang tot externe opslag nodig om DuckStation te gebruiken.</string>
|
||||
<string name="main_activity_choose_disc_image">Kies Disc Image</string>
|
||||
<string name="main_activity_missing_bios_image_prompt">Er was geen BIOS image was gevonden in DuckStation\'s bios folder. Wilt u nu een BIOS image importeren?</string>
|
||||
<string name="main_activity_missing_bios_image">Ontbrekende BIOS Image</string>
|
||||
<string name="main_activity_yes">Ja</string>
|
||||
<string name="main_activity_no">Nee</string>
|
||||
<string name="main_activity_choose_bios_image">Kies BIOS Image</string>
|
||||
<string name="main_activity_failed_to_open_bios_image">BIOS image openen mislukt.</string>
|
||||
<string name="main_activity_failed_to_read_bios_image_prefix">\"BIOS image lezen mislukt: \"</string>
|
||||
<string name="main_activity_bios_image_too_large">BIOS image is te groot.</string>
|
||||
<string name="main_activity_invalid_error">Deze BIOS image is ongeldig, of is al geimporteerd.</string>
|
||||
<string name="main_activity_show_version_title">Versie</string>
|
||||
<string name="main_activity_copy">Kopieer</string>
|
||||
<string name="settings_fast_forward_volume">Doorspoel Volume</string>
|
||||
<string name="settings_summary_fast_forward_volume">Bedient het volume van de emulator wanneer het spel sneller afgespeeld wordt.</string>
|
||||
<string name="settings_gpu_thread">Threaded GPU Rendering</string>
|
||||
<string name="settings_summary_gpu_thread">Gebruikt een tweede thread voor het renderen van graphics. Op het moment alleen beschikbaar voor software renderers, maar kan een significante snelheidsverbetering opleveren, en is veilig te gebruiken.</string>
|
||||
</resources>
|
||||
235
android/app/src/main/res/values-pt-rBR/arrays.xml
Normal file
235
android/app/src/main/res/values-pt-rBR/arrays.xml
Normal file
@@ -0,0 +1,235 @@
|
||||
<resources>
|
||||
<string-array name="settings_console_region_entries">
|
||||
<item>Auto-Detectar</item>
|
||||
<item>NTSC-J (Japão)</item>
|
||||
<item>NTSC-U (US)</item>
|
||||
<item>PAL (Europa, Austrália)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cpu_execution_mode_entries">
|
||||
<item>Interpretador (Mais Lento)</item>
|
||||
<item>Interpretador Armazenado (Rápido)</item>
|
||||
<item>Recompilador (Mais Rápido)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cpu_fastmem_mode_entries">
|
||||
<item>Desabilitado (Lento)</item>
|
||||
<item>MMap (Hardware, Mais Rápido, (Dispositivos 64-Bits)</item>
|
||||
<item>LUT (Rápido)</item>
|
||||
</string-array>
|
||||
<string-array name="gpu_renderer_entries">
|
||||
<item>Dedicado (OpenGL)</item>
|
||||
<item>Dedicado (Vulkan)</item>
|
||||
<item>Software</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_resolution_scale_entries">
|
||||
<item>1x</item>
|
||||
<item>2x</item>
|
||||
<item>3x (para 720p)</item>
|
||||
<item>4x</item>
|
||||
<item>5x (para 1080p)</item>
|
||||
<item>6x (para 1440p)</item>
|
||||
<item>7x</item>
|
||||
<item>8x</item>
|
||||
<item>9x (para 4K)</item>
|
||||
<item>10x</item>
|
||||
<item>11x</item>
|
||||
<item>12x</item>
|
||||
<item>13x</item>
|
||||
<item>14x</item>
|
||||
<item>15x</item>
|
||||
<item>16x</item>
|
||||
</string-array>
|
||||
<string-array name="settings_display_crop_mode_entries">
|
||||
<item>None</item>
|
||||
<item>Área Renderizada</item>
|
||||
<item>Todas as Bordas</item>
|
||||
</string-array>
|
||||
<string-array name="settings_display_aspect_ratio_names">
|
||||
<item>Auto (Nativo)</item>
|
||||
<item>4:3</item>
|
||||
<item>16:9</item>
|
||||
<item>16:10</item>
|
||||
<item>19:9</item>
|
||||
<item>20:9</item>
|
||||
<item>21:9</item>
|
||||
<item>32:9</item>
|
||||
<item>8:7</item>
|
||||
<item>5:4</item>
|
||||
<item>3:2</item>
|
||||
<item>2:1 (VRAM 1:1)</item>
|
||||
<item>1:1</item>
|
||||
<item>PAR 1:1</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_texture_filter_names">
|
||||
<item>Nearest-Neighbor</item>
|
||||
<item>Bilinear</item>
|
||||
<item>Bilinear (Sem ajustes laterais)</item>
|
||||
<item>JINC2</item>
|
||||
<item>JINC2 (Sem ajustes laterais)</item>
|
||||
<item>xBR</item>
|
||||
<item>xBR (Sem ajustes laterais)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_controller_type_entries">
|
||||
<item>Controle Digital (Gamepad)</item>
|
||||
<item>Controle Analógico (DualShock)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_memory_card_mode_entries">
|
||||
<item>Sem Cartão de Memória</item>
|
||||
<item>Compartilhada entre Jogos</item>
|
||||
<item>Separado Por Jogo (Cód. Jogo)</item>
|
||||
<item>Separado Por Jogo (Título Jogo)</item>
|
||||
</string-array>
|
||||
<string-array name="emulation_menu">
|
||||
<item>Carregar Estado</item>
|
||||
<item>Salvar Estado</item>
|
||||
<item>Avanço (Fixo)</item>
|
||||
<item>Mais Opções</item>
|
||||
<item>Sair</item>
|
||||
</string-array>
|
||||
<string-array name="emulation_more_menu">
|
||||
<item>Reiniciar</item>
|
||||
<item>Trapaças</item>
|
||||
<item>Mudar Disco</item>
|
||||
<item>Configurações</item>
|
||||
<item>Mudar controle em Tela</item>
|
||||
<item>Editar Posição dos Controles (Tela)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cdrom_read_speedup_entries">
|
||||
<item>Nenhum</item>
|
||||
<item>2x (4x Veloz)</item>
|
||||
<item>3x (6x Veloz)</item>
|
||||
<item>4x (8x Veloz)</item>
|
||||
<item>5x (10x Veloz)</item>
|
||||
<item>6x (12x Veloz)</item>
|
||||
<item>7x (14x Veloz)</item>
|
||||
<item>8x (16x Veloz)</item>
|
||||
<item>9x (18x Veloz)</item>
|
||||
<item>10x (20x Veloz)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_touchscreen_controller_view_entries">
|
||||
<item>Nenhum</item>
|
||||
<item>Digital D-Pad</item>
|
||||
<item>1 Analógico</item>
|
||||
<item>2 Analógicos</item>
|
||||
</string-array>
|
||||
<string-array name="settings_audio_backend_entries">
|
||||
<item>Mudo</item>
|
||||
<item>Cubeb</item>
|
||||
<item>SLES (Recomendado)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_audio_buffer_size_entries">
|
||||
<item>1024 Quadros (23.22ms)</item>
|
||||
<item>2048 Quadros (46.44ms, Recomendado)</item>
|
||||
<item>3072 Quadros (69.66ms)</item>
|
||||
<item>4096 Quadros (92.88ms)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_log_level_entries">
|
||||
<item>Nenhum</item>
|
||||
<item>Erro</item>
|
||||
<item>Alerta</item>
|
||||
<item>Alertas de Performance</item>
|
||||
<item>Informação</item>
|
||||
<item>Detalhado</item>
|
||||
<item>Desenvolvedor</item>
|
||||
<item>Perfil</item>
|
||||
<item>Depuração</item>
|
||||
<item>Rota</item>
|
||||
</string-array>
|
||||
<string-array name="settings_tabs">
|
||||
<item>Geral</item>
|
||||
<item>Vídeo</item>
|
||||
<item>Áudio</item>
|
||||
<item>Melhorias</item>
|
||||
<item>Controles</item>
|
||||
<item>Avançado</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_msaa_entries">
|
||||
<item>Desativado</item>
|
||||
<item>2x MSAA</item>
|
||||
<item>4x MSAA</item>
|
||||
<item>8x MSAA</item>
|
||||
<item>2x SSAA</item>
|
||||
<item>4x SSAA</item>
|
||||
<item>8x SSAA</item>
|
||||
</string-array>
|
||||
<string-array name="settings_advanced_display_fps_limit_entries">
|
||||
<item>ilimitado (Mostrar Quadros)</item>
|
||||
<item>10 FPS</item>
|
||||
<item>15 FPS</item>
|
||||
<item>30 FPS</item>
|
||||
<item>45 FPS</item>
|
||||
<item>60 FPS</item>
|
||||
<item>75 FPS</item>
|
||||
<item>90 FPS</item>
|
||||
</string-array>
|
||||
<string-array name="settings_emulation_speed_entries">
|
||||
<item>ilimitado</item>
|
||||
<item>10% [6 FPS (NTSC) / 5 FPS (PAL)]</item>
|
||||
<item>20% [12 FPS (NTSC) / 10 FPS (PAL)]</item>
|
||||
<item>30% [18 FPS (NTSC) / 15 FPS (PAL)]</item>
|
||||
<item>40% [24 FPS (NTSC) / 20 FPS (PAL)]</item>
|
||||
<item>50% [30 FPS (NTSC) / 25 FPS (PAL)]</item>
|
||||
<item>60% [36 FPS (NTSC) / 30 FPS (PAL)]</item>
|
||||
<item>70% [42 FPS (NTSC) / 35 FPS (PAL)]</item>
|
||||
<item>80% [48 FPS (NTSC) / 40 FPS (PAL)]</item>
|
||||
<item>90% [54 FPS (NTSC) / 45 FPS (PAL)]</item>
|
||||
<item>100% [60 FPS (NTSC) / 50 FPS (PAL), Padrão]</item>
|
||||
<item>125% [75 FPS (NTSC) / 62 FPS (PAL)]</item>
|
||||
<item>150% [90 FPS (NTSC) / 75 FPS (PAL)]</item>
|
||||
<item>175% [105 FPS (NTSC) / 87 FPS (PAL)]</item>
|
||||
<item>200% [120 FPS (NTSC) / 100 FPS (PAL)]</item>
|
||||
<item>250% [150 FPS (NTSC) / 125 FPS (PAL)]</item>
|
||||
<item>300% [180 FPS (NTSC) / 150 FPS (PAL)]</item>
|
||||
<item>350% [210 FPS (NTSC) / 175 FPS (PAL)]</item>
|
||||
<item>400% [240 FPS (NTSC) / 200 FPS (PAL)]</item>
|
||||
<item>450% [270 FPS (NTSC) / 225 FPS (PAL)]</item>
|
||||
<item>500% [300 FPS (NTSC) / 250 FPS (PAL)]</item>
|
||||
<item>600% [360 FPS (NTSC) / 300 FPS (PAL)]</item>
|
||||
<item>700% [420 FPS (NTSC) / 350 FPS (PAL)]</item>
|
||||
<item>800% [480 FPS (NTSC) / 400 FPS (PAL)]</item>
|
||||
<item>900% [540 FPS (NTSC) / 450 FPS (PAL)]</item>
|
||||
<item>1000% [600 FPS (NTSC) / 500 FPS (PAL)]</item>
|
||||
</string-array>
|
||||
<string-array name="settings_advanced_cpu_overclock_entries">
|
||||
<item>25% (8MHz)</item>
|
||||
<item>50% (16MHz)</item>
|
||||
<item>75% (24MHz)</item>
|
||||
<item>100% (33MHz, Padrão)</item>
|
||||
<item>125% (41MHz)</item>
|
||||
<item>150% (49MHz)</item>
|
||||
<item>175% (57MHz)</item>
|
||||
<item>200% (66MHz)</item>
|
||||
<item>225% (74MHz)</item>
|
||||
<item>250% (82MHz)</item>
|
||||
<item>275% (90MHz)</item>
|
||||
<item>300% (99MHz)</item>
|
||||
<item>350% (115MHz)</item>
|
||||
<item>400% (132MHz)</item>
|
||||
<item>450% (148MHz)</item>
|
||||
<item>500% (165MHz)</item>
|
||||
<item>500% (165MHz)</item>
|
||||
<item>600% (198MHz)</item>
|
||||
<item>700% (231MHz)</item>
|
||||
<item>800% (264MHz)</item>
|
||||
<item>900% (297MHz)</item>
|
||||
<item>1000% (330MHz)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_emulation_screen_orientation_entries">
|
||||
<item>Padrão do Dispositivo</item>
|
||||
<item>Retrato</item>
|
||||
<item>Paisagem</item>
|
||||
</string-array>
|
||||
<string-array name="settings_theme_entries">
|
||||
<item>Padrão do Sistema</item>
|
||||
<item>Claro</item>
|
||||
<item>Escuro</item>
|
||||
</string-array>
|
||||
<string-array name="settings_downsample_mode_entries">
|
||||
<item>Desabilitado</item>
|
||||
<item>Misto (Reduz 3D / Suaviza Tudo)</item>
|
||||
<item>Adaptativo (Preserva o 3D / Suaviza 2D)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_boolean_entries">
|
||||
<item>Disabled</item>
|
||||
<item>Enabled</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
213
android/app/src/main/res/values-pt-rBR/strings.xml
Normal file
213
android/app/src/main/res/values-pt-rBR/strings.xml
Normal file
@@ -0,0 +1,213 @@
|
||||
<resources>
|
||||
<string name="app_name">DuckStation</string>
|
||||
<string name="action_settings">Configurações</string>
|
||||
<string name="action_controller_mapping">Mapear Controles</string>
|
||||
<string name="title_activity_settings">Configurações</string>
|
||||
<string name="settings_console_region">Região do Console</string>
|
||||
<string name="settings_console_tty_output">EAtivar saída TTY</string>
|
||||
<string name="settings_console_fast_boot">Inicio Rápido</string>
|
||||
<string name="settings_osd_show_messages">Mostrar Mensagens</string>
|
||||
<string name="settings_osd_show_speed">Mostrar Velocidade da Emulação</string>
|
||||
<string name="settings_osd_show_show_fps">Mostrar FPS</string>
|
||||
<string name="settings_osd_show_show_vps">Mostrar VPS</string>
|
||||
<string name="settings_cpu_execution_mode">Modo de execução CPU</string>
|
||||
<string name="settings_gpu_renderer">Renderizado por GPU</string>
|
||||
<string name="settings_gpu_resolution_scale">Escala de Resolução</string>
|
||||
<string name="title_activity_emulation">Atividade da Emulação</string>
|
||||
<string name="settings_emulation_speed">Velocidade da Emulação</string>
|
||||
<string name="settings_fast_forward_speed">Velocidade de Avanço Rápido</string>
|
||||
<string name="settings_save_state_on_exit">Salvar ao Sair</string>
|
||||
<string name="settings_pause_when_menu_opened">Pausar ao abrir Menu</string>
|
||||
<string name="settings_emulation_screen_orientation">Orientação de Tela</string>
|
||||
<string name="settings_load_patch_codes">Carregar Trapaças</string>
|
||||
<string name="settings_summary_save_state_on_exit">Salvar automaticamente o estado da emulação ao desligar.</string>
|
||||
<string name="settings_summary_pause_when_menu_opened">Pausa a emulação assim que o menu é aberto.</string>
|
||||
<string name="settings_summary_load_patch_codes"><![CDATA[Carregar códigos de trapaça formato .cht e ou PCSXR. Códigos podem ser alternados durante o jogo.]]></string>
|
||||
<string name="settings_apply_compatibility_settings">Aplicar Configurações de Compatibilidade</string>
|
||||
<string name="settings_summary_apply_compatibility_settings">Desativar automaticamente melhorias quando não são suportadas.</string>
|
||||
<string name="settings_summary_video_sync">Ative essa opção para que o emulador sincronize a taxa de FPS de acordo com a taxa do seu aparelho.</string>
|
||||
<string name="settings_video_sync">Sincronização Vertical</string>
|
||||
<string name="settings_cpu_overclocking">Aumento de Velocidade CPU (Overclock)</string>
|
||||
<string name="settings_cdrom_region_check">Checagem de Região do CD-ROM</string>
|
||||
<string name="settings_summary_cdrom_region_check">Previne que discos de regiões incorretas sejam lidos pelo emulador. Pode ser Desligado.</string>
|
||||
<string name="settings_cdrom_preload_image_to_ram">Carregar CD-ROM para memória RAM</string>
|
||||
<string name="settings_summary_preload_image_to_ram">LCarrega o jogo todo na RAM. Útil para prevenir instabilidades durante o jogo. Em alguns casos também elimina travamentos durante os carregamentos.</string>
|
||||
<string name="settings_pgxp_vertex_cache">PGXP - Vértice Armazenado</string>
|
||||
<string name="settings_summary_pgxp_vertex_cache">Reduz as oscilações nos poligonos tentando preservar os mesmos na hora da transferência para a RAM.</string>
|
||||
<string name="settings_pgxp_cpu_mode">PGXP - Modo CPU</string>
|
||||
<string name="settings_summary_pgxp_cpu_mode">Tenta reduzir oscilações nos poligonos preservando-os na hora da transferência para o CPU. Alguns jogos se beneficiam com esta opção junto com o PGXP para que funcione corretamente. Muito lento, e imcompativel com o modo recompilador.</string>
|
||||
<string name="settings_cpu_recompiler_icache">Recompilador CPU Modo ICache</string>
|
||||
<string name="settings_summary_cpu_recompiler_icache">Determina se a instrução enviada ao CPU emulado fica armazenada no recompilador. Melhor a precisão ao pequeno custo de performance. Se os jogos estão rodando muito rápido, tente ativar esta opção.</string>
|
||||
<string name="settings_cpu_recompiler_fastmem">Recompilador CPU Modo FASTMEM</string>
|
||||
<string name="settings_summary_cpu_recompiler_fastmem">Torna o acesso à memória mais eficiente. Desative se estiver instável em seu aparelho.</string>
|
||||
<string name="settings_presented_frame_limit">Limite do Quadro atual</string>
|
||||
<string name="settings_logging_level">Nível de Registro</string>
|
||||
<string name="settings_log_to_file">Registro para Arquivo</string>
|
||||
<string name="settings_summary_log_to_file">Escreve mensagens em um arquivo duckstation.log em seu diretório raiz. Use só para depuração isto deixará o emulador lento.</string>
|
||||
<string name="settings_log_to_logcat">Registro em Linha de Comando</string>
|
||||
<string name="settings_summary_log_to_logcat">Escreve um arquivo de registro para arquivo no Android. Só útil quando atrelado ao PC com adb.</string>
|
||||
<string name="settings_volume">Volume</string>
|
||||
<string name="settings_summary_volume">Controla o volume do emulador.</string>
|
||||
<string name="settings_fast_forward_volume">Volume do Avanço Rápido</string>
|
||||
<string name="settings_summary_fast_forward_volume">Controla o volume do áudio quando o avanço rápido é usado.</string>
|
||||
<string name="settings_mute_all_sound">Silenciar Tudo</string>
|
||||
<string name="settings_summary_mute_all_sound">Silencia totalmente o emulador.</string>
|
||||
<string name="settings_mute_cd_audio">Silenciar CD\'s de Áudio</string>
|
||||
<string name="settings_summary_mute_cd_audio">Silencia de forma forçada tanto para CD\'s de música quanto em jogos.</string>
|
||||
<string name="settings_audio_backend">Opções de Áudio</string>
|
||||
<string name="settings_audio_buffer_size">Latência do Volume:</string>
|
||||
<string name="settings_summary_audio_buffer_size">Determina a latência entre o áudio. Valores menores reduzem a latência (atraso), pode causar engasgos quando jogos são acelerados.</string>
|
||||
<string name="settings_audio_sync">Sincronia de Áudio</string>
|
||||
<string name="settings_summary_audio_sync">Limita a velocidade de emulação com base na opção escolhida. Isso ajuda a remover ruídos ou estalos se a emulação for muito rápida. A sincronização será automaticamente desativada se não funcionar a 100%.</string>
|
||||
<string name="settings_controller_type">Tipo de Controle</string>
|
||||
<string name="settings_enable_analog_mode_on_reset">Ativa modo analógico ao reiniciar</string>
|
||||
<string name="settings_touchscreen_controller_view">Visualização do controle em tela</string>
|
||||
<string name="settings_auto_hide_touchscreen_controller">Auto ocultar controles em tela</string>
|
||||
<string name="settings_summary_auto_hide_touchscreen_controller">Esconde controles em tela quando um controle externo é detectado.</string>
|
||||
<string name="settings_vibrate_on_press">Vibrar ao Pressionar</string>
|
||||
<string name="settings_summary_vibrate_on_press">Ativa vibração quando um botão da tela de toque é pressionado. Requer \"Vibrar ao Tocar\" ativado nas opções do seu dispositivo para funcionar.</string>
|
||||
<string name="settings_enable_vibration">Ativa Vibração</string>
|
||||
<string name="settings_summary_enable_vibration">Encaminha vibração do jogo para o motor do aparelho</string>
|
||||
<string name="settings_memory_card_1_type">Tipo de Cartão de Memória 1</string>
|
||||
<string name="settings_memory_card_2_type">Tipo de Cartão de Memória 2</string>
|
||||
<string name="settings_crop_mode">Modo de Corte</string>
|
||||
<string name="settings_aspect_ratio">Aspect Ratio</string>
|
||||
<string name="settings_linear_upscaling">Escalonamento Linear</string>
|
||||
<string name="settings_integer_upscaling">Escalonamento Integro</string>
|
||||
<string name="settings_summary_linear_upscaling">Suaviza a imagem ao aumentar a resolução da emulação.</string>
|
||||
<string name="settings_summary_integer_upscaling">Adiciona preenchimento a área exibida para garantir a proporção entre os pixels. Pode resultar em uma imagem mais nítida em jogos 2D.</string>
|
||||
<string name="settings_summary_osd_show_messages">Mostra mensagens em tela ao salvar, carregar, carregar trapaças quando eventos ou alertas ocorrerem.</string>
|
||||
<string name="settings_summary_osd_show_speed">Define a velocidade de emulação. Não é garantido que essa velocidade será atingida.</string>
|
||||
<string name="settings_summary_osd_show_fps">Mostra a taxa de quadros (FPS) interna do jogo no canto superior direito da tela.</string>
|
||||
<string name="settings_summary_osd_show_vps">Mostra o número de quadros (VPS) exibidos por segundo pelo sistema no canto superior direito da tela.</string>
|
||||
<string name="settings_cdrom_read_speedup">Velocidade de leitura CD-ROM</string>
|
||||
<string name="settings_summary_cdrom_read_speedup">Acelera a leitura de CD-ROM. Aplica-se apenas a leituras de velocidade 2x é ignorado quando o áudio está sendo reproduzido. Pode melhorar a velocidade de carregamento em alguns jogos, ao custo de quebrar outros.</string>
|
||||
<string name="settings_summary_console_fast_boot">Pula a tela de abertura clássica da Sony seguro ativar porém alguns raros jogos podem quebrar.</string>
|
||||
<string name="settings_msaa">Filtro Anti-serrilhado (MSAA)</string>
|
||||
<string name="settings_true_color">Renderização em (24 Cores, desativa dithering)</string>
|
||||
<string name="settings_summary_true_color">Produz gradientes de aparência mais agradável ao custo de fazer algumas cores ficarem ligeiramente diferentes. A maioria dos jogos são compatíveis com esta opção.</string>
|
||||
<string name="settings_scaled_dithering">Efeito Dithering Escalado (escala pontilhados para resolução). </string>
|
||||
<string name="settings_summary_scaled_dithering">Escala o dithering para a resolução da GPU emulada. Torna o padrão do pontilhado muito menos óbvio em resoluções mais altas.</string>
|
||||
<string name="settings_disable_interlacing">Desativar Entrelaçamento (força redenrização progressiva).</string>
|
||||
<string name="settings_summary_disable_interlacing">Força a renderização e exibição de quadros para o modo progressivo. Remove o efeito de \'alisamento\' visto em jogos 480i renderizando sempre em 480, seguro deixar ativo.</string>
|
||||
<string name="settings_texture_filtering">Filtragem de textura</string>
|
||||
<string name="settings_force_ntsc_timings">Força renderização em NTSC (60hz-em-PAL)</string>
|
||||
<string name="settings_summary_force_ntsc_timings">Usa temporizadores de quadro NTSC forçando os jogos PAL a rodarem a 60 Hz.</string>
|
||||
<string name="settings_widescreen_hack">Hack de tela panorâmica</string>
|
||||
<string name="settings_summary_widescreen_hack">Dimensiona as posições dos vértices da tela para a proporção mais larga (Panoramicas), basicamente aumenta o campo de visão de 4:3 para 16:09 em jogos 3D. Não é compatível com todos os jogos.</string>
|
||||
<string name="settings_force_4_3_for_24bit">Forçar 4:3 para telas 24-bits.</string>
|
||||
<string name="settings_summary_force_4_3_for_24bit">Volta a tela para 4:3 (Desativa efeito Esticado em FMVs).</string>
|
||||
<string name="settings_chroma_smoothing_24bit">Suavização de Croma</string>
|
||||
<string name="settings_summary_chrome_smoothing_24bit">Suavização de Croma (reduz artefatos em cenas FMV).</string>
|
||||
<string name="settings_pgxp_geometry_correction">Correção Geométrica PGXP</string>
|
||||
<string name="settings_summary_pgxp_geometry_correction"><![CDATA[Reduz as "oscilações" nos polígonos tentando preservar os mesmos na hora da transferência para a memória. Funciona apenas se rederizado por hardware e pode não ser compatível com todos os jogos Não compativel com todos os jogos.]]></string>
|
||||
<string name="settings_pgxp_culling_correction">Correção de Curvas PGXP</string>
|
||||
<string name="settings_summary_pgxp_culling_correction">Aumenta a precisão das curvas nos polígonos, reduzindo o número de buracos na geometria do mesmo. Requer a Correção Geometrica ativada. Requer que a correção geométrica esteja ativada.</string>
|
||||
<string name="settings_pgxp_texture_correction">Correção de Textura PGXP</string>
|
||||
<string name="settings_summary_pgxp_texture_correction">Utiliza interpolação corretiva em perspetiva para coordenadas e das cores na textura, endireitando as que estiverem distorcidas. Requer que a correção geométrica esteja ativada.</string>
|
||||
<string name="settings_pgxp_preserve_projection_precision">Preserva Precisão de Projeção PGXP</string>
|
||||
<string name="settings_summary_pgxp_preserve_projection_precision">Permite precisão adicional para PGXP; Pode melhorar o visual em alguns jogos e prejudicar outros.</string>
|
||||
<string name="settings_pgxp_depth_buffer">PGXP Modo Polimento (Z)</string>
|
||||
<string name="settings_summary_pgxp_depth_buffer">Tenta reduzir os poligonos no eixo Z (parte inferior ou cantos em alguns casos) ajustando os limites dos valores para que não haja conflitos na renderização de poligonos 3D. Gerando um efeito de buracos ou quadrados piscando. Tem pouca compatibilidade no geral, mas pode funcionar bem em alguns jogos. Outros precisam ser ajustados via limitador.</string>
|
||||
<string name="menu_main_resume_last_session">Resumir Jogo</string>
|
||||
<string name="menu_main_start_file">Iniciar Arquivo</string>
|
||||
<string name="menu_main_start_bios">Iniciar BIOS</string>
|
||||
<string name="menu_main_edit_game_directories">Editar Diretórios de Jogos</string>
|
||||
<string name="menu_main_scan_for_new_games">Escanear Jogos Novos</string>
|
||||
<string name="menu_main_rescan_all_games">Rescanear Tudo</string>
|
||||
<string name="menu_main_import_bios">Importar BIOS</string>
|
||||
<string name="menu_main_show_version">Mostrar Versão</string>
|
||||
<string name="menu_main_github_repository">Repositório no Github</string>
|
||||
<string name="menu_main_discord_server">Servidor no Discord</string>
|
||||
<string name="menu_game_list_entry_start_game">Iniciar Jogo</string>
|
||||
<string name="menu_game_list_entry_resume_game">Resumir Jogo</string>
|
||||
<string name="android_progress_callback_please_wait">Aguarde...</string>
|
||||
<string name="android_progress_callback_ok">OK</string>
|
||||
<string name="android_progress_callback_information">Informação</string>
|
||||
<string name="android_progress_callback_confirmation">Confirmação</string>
|
||||
<string name="android_progress_callback_yes">Sim</string>
|
||||
<string name="android_progress_callback_no">Não</string>
|
||||
<string name="emulation_activity_error">Erro</string>
|
||||
<string name="emulation_activity_ok">Ok</string>
|
||||
<string name="emulation_activity_import_patch_codes">Importar Trapaças...</string>
|
||||
<string name="emulation_activity_patch_on">(Ligar)</string>
|
||||
<string name="emulation_activity_patch_off">(Desligar)</string>
|
||||
<string name="emulation_activity_choose_patch_code_file">Escolher Arquivo de Trapaça</string>
|
||||
<string name="emulation_activity_failed_to_import_patch_codes">Falha ao importar trapaça. Assegure-se de ter escolhido o formato PCSXR ou Libretro CHT.</string>
|
||||
<string name="main_activity_choose_directory">Escolher Diretório</string>
|
||||
<string name="main_activity_error">Erro</string>
|
||||
<string name="main_activity_get_path_from_file_error">Falha ao obter o caminho para o arquivo selecionado. Certifique-se de que o arquivo esteja no armazenamento interno / externo.\n\nTap.\nSelect "Mostrar armazenamento interno".\nTap selecione o nome do seu dispositivo ou cartão SD.</string>
|
||||
<string name="main_activity_ok">OK</string>
|
||||
<string name="main_activity_get_path_from_directory_error">Falha ao obter o caminho para o arquivo selecionado. Certifique-se de que o arquivo esteja no armazenamento interno / externo. \n\nTap the overflow button in the directory selector.\nSelect \"Mostrar armazenamento interno\".\nTap selecione o nome do seu dispositivo ou cartão SD.</string>
|
||||
<string name="main_activity_external_storage_permissions_error">Permissões de armazenamento externo são necessárias para usar o DuckStation.</string>
|
||||
<string name="main_activity_choose_disc_image">Escolha Arquivo de Jogo</string>
|
||||
<string name="main_activity_missing_bios_image_prompt">Nenhum BIOS foi encontrada no diretório de BIOS do DuckStation. Deseja localizar e importar o BIOS agora?</string>
|
||||
<string name="main_activity_missing_bios_image">BIOS ausente.</string>
|
||||
<string name="main_activity_yes">Sim</string>
|
||||
<string name="main_activity_no">Não</string>
|
||||
<string name="main_activity_choose_bios_image">Escolher BIOS</string>
|
||||
<string name="main_activity_failed_to_open_bios_image">Falha ao Abrir BIOS.</string>
|
||||
<string name="main_activity_failed_to_read_bios_image_prefix">\"Falha ao ler BIOS: \"</string>
|
||||
<string name="main_activity_bios_image_too_large">Aqruivo BIOS muito grande.</string>
|
||||
<string name="main_activity_invalid_error">Esse BIOS não é válido, ou já foi importado.</string>
|
||||
<string name="main_activity_show_version_title">Versão</string>
|
||||
<string name="main_activity_copy">Copiar</string>
|
||||
<string name="settings_gpu_thread">Renderização Sequencial</string>
|
||||
<string name="settings_summary_gpu_thread">Usa um segundo processo para desenhar gráficos em tela. Atualmente só disponível para renderizadores por software, pode fornecer uma melhoria significativa na velocidade, pode ser usado a vontade.</string>
|
||||
<string name="settings_gpu_threaded_presentation">Renderização Sequencial</string>
|
||||
<string name="settings_summary_gpu_threaded_presentation">Apresenta quadros sequêncialmente em segundo plano quando o avanço rápido ou sincronizador vertical (Vsync) está desativado Isso pode melhorar o desempenho no renderizador Vulkan.</string>
|
||||
<string name="settings_language">Linguagem (Necessário Reiniciar)</string>
|
||||
<string name="touchscreen_controller_stop_editing">Terminado</string>
|
||||
<string name="touchscreen_controller_reset_layout">Redefinir</string>
|
||||
<string name="emulation_activity_touchscreen_controller_not_active">Controle em Tela não Ativado.</string>
|
||||
<string name="settings_theme">Tema</string>
|
||||
<string name="settings_controller_mapping_summary">Permite configurar e atribuir os botões de seu controle externo no emulador.</string>
|
||||
<string name="settings_controller_mapping">Mapear Controles</string>
|
||||
<string name="controller_binding_dialog_message">Pressione os botões para atribuí-los ao controle.\n\nAtribuição Atual: %s</string>
|
||||
<string name="controller_binding_dialog_no_binding"><![CDATA[<Nenhuma>]]></string>
|
||||
<string name="controller_binding_dialog_cancel">Cancelar</string>
|
||||
<string name="controller_binding_dialog_clear">Limpar</string>
|
||||
<string name="controller_mapping_activity_title">Mapear Controle</string>
|
||||
<string name="controller_mapping_activity_no_profiles_found">Perfil não encontrado.</string>
|
||||
<string name="controller_mapping_activity_select_input_profile">Escolha o Perfil de Entrada</string>
|
||||
<string name="controller_mapping_activity_failed_to_load_profile">Falha ao carregar o perfil \'%s\'</string>
|
||||
<string name="controller_mapping_activity_input_profile_name">Nome do Perfil:</string>
|
||||
<string name="controller_mapping_activity_save">Salvar</string>
|
||||
<string name="controller_mapping_activity_name_must_be_provided">Um nome deve ser escolhido.</string>
|
||||
<string name="controller_mapping_activity_failed_to_save_input_profile">Falha ao salvar perfil.</string>
|
||||
<string name="controller_mapping_activity_input_profile_saved">Perfil \'%s\' salvo.</string>
|
||||
<string name="controller_mapping_activity_cancel">Cancelar</string>
|
||||
<string name="settings_use_analog_sticks_for_dpad">Usar Analógicos como D-Pad no Modo Digital</string>
|
||||
<string name="settings_summary_enable_analog_mode_on_reset">Força os controles ao modo analógico quando emulador é reiniciado.</string>
|
||||
<string name="settings_summary_use_analog_sticks_for_dpad">Permite usar os analógicos como um direcional (D-Pad) no modo digital, assim como os botões.</string>
|
||||
<string name="settings_disable_all_enhancements">Desativar todas as Melhorias</string>
|
||||
<string name="settings_summary_disable_all_enhancements">Desliga temporariamente todas as melhorias, útil para achar problemas de performance.</string>
|
||||
<string name="settings_downsample_mode">Suavização da Imagem</string>
|
||||
<string name="activity_game_properties">Propriedades do Jogo</string>
|
||||
<string name="game_properties_preference_use_global_setting">Usar configuração global</string>
|
||||
<string name="settings_input_profile">Perfil de controle</string>
|
||||
<string name="game_properties_tab_summary">Sumário</string>
|
||||
<string name="game_properties_tab_game_settings">Configuração de Jogo</string>
|
||||
<string name="game_properties_tab_controller_settings">Configurações de Controle</string>
|
||||
<string name="settings_audio_resampling">Ajuste de Áudio</string>
|
||||
<string name="settings_summary_audio_resampling">Quando estiver rodando fora dos 100% da velocidade, o áudio será ajustado para que não haja queda de quadros. Produz uma melhor qualidade do avanço rápido no áudio ao custo pequeno de perda de performance.</string>
|
||||
<string name="settings_general_sync_to_host_refresh_rate">Sincronizar taxa de atualização</string>
|
||||
<string name="settings_summary_general_sync_to_host_refresh_rate">Ajusta a velocidade da emulação de acordo com a mesma taxa de atualização do dispositivo.</string>
|
||||
<string name="settings_sustained_performance_mode">Desempenho Contínuo</string>
|
||||
<string name="settings_summary_sustained_performance_mode">Ativa o modo contínuo de perfomance do Android. Resulta em um melhor desempenho de quadros e perfomance por longos periodos em alguns dispositivos.</string>
|
||||
<string name="title_activity_game_directories">Editar Diretórios de Jogos</string>
|
||||
<string name="settings_game_directories">Diretório de Jogos</string>
|
||||
<string name="settings_summary_game_directories">Altera a lista de diretórios usados para pesquisar jogos.</string>
|
||||
<string name="game_directories_scanning_subdirectories">Ler subdiretórios.</string>
|
||||
<string name="game_directories_not_scanning_subdirectories">Não ver subdiretórios.</string>
|
||||
<string name="settings_display_all_frames">Otimização de Quadros</string>
|
||||
<string name="settings_summary_display_all_frames">Garante que cada quadro renderizado será mostrado em tela, no seu rtimo adequado. Se estiver tendo dificuldades ao manter uma boa velocidade, ou estiver enfrentando algumas falhas de áudio tente desativar esta opção.</string>
|
||||
<string name="menu_edit_game_directories_add_directory">Adicionar Diretório</string>
|
||||
<string name="menu_edit_game_directories_add_path">Adicionar Caminho</string>
|
||||
<string name="edit_game_directories_add_path">Editar Caminho</string>
|
||||
<string name="edit_game_directories_add_path_summary">Insira o caminho completo dos seus jogos.\n\nUse um gerenciador de arquivos para ver o caminho.\n\nExemplo: /storage/emulated/0/games</string>
|
||||
<string name="save_state_info_slot_is_empty">Espaço Vazio</string>
|
||||
<string name="save_state_info_game_save_n">Id do Jogo %d</string>
|
||||
<string name="save_state_info_global_save_n">Espaço Global %d</string>
|
||||
<string name="save_state_info_quick_save">Salvar Rápido</string>
|
||||
</resources>
|
||||
235
android/app/src/main/res/values-ru/arrays.xml
Normal file
235
android/app/src/main/res/values-ru/arrays.xml
Normal file
@@ -0,0 +1,235 @@
|
||||
<resources>
|
||||
<string-array name="settings_console_region_entries">
|
||||
<item>Автоопределение</item>
|
||||
<item>NTSC-J (Япония)</item>
|
||||
<item>NTSC-U (США)</item>
|
||||
<item>PAL (Европа, Австралия)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cpu_execution_mode_entries">
|
||||
<item>Интерпретатор (самый медленный)</item>
|
||||
<item>Кэшированный интерпретатор (быстрее)</item>
|
||||
<item>Рекомпилятор (самый быстрый)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cpu_fastmem_mode_entries">
|
||||
<item>Выключен (самый медленный)</item>
|
||||
<item>MMap (аппаратный, самый быстрый, только для 64-бит)</item>
|
||||
<item>LUT (быстрее)</item>
|
||||
</string-array>
|
||||
<string-array name="gpu_renderer_entries">
|
||||
<item>Аппаратный (OpenGL)</item>
|
||||
<item>Аппаратный (Vulkan)</item>
|
||||
<item>Программный</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_resolution_scale_entries">
|
||||
<item>1x</item>
|
||||
<item>2x</item>
|
||||
<item>3x (для 720p)</item>
|
||||
<item>4x</item>
|
||||
<item>5x (для 1080p)</item>
|
||||
<item>6x (для 1440p)</item>
|
||||
<item>7x</item>
|
||||
<item>8x</item>
|
||||
<item>9x (для 4K)</item>
|
||||
<item>10x</item>
|
||||
<item>11x</item>
|
||||
<item>12x</item>
|
||||
<item>13x</item>
|
||||
<item>14x</item>
|
||||
<item>15x</item>
|
||||
<item>16x</item>
|
||||
</string-array>
|
||||
<string-array name="settings_display_crop_mode_entries">
|
||||
<item>Нет</item>
|
||||
<item>Только вылеты развёртки</item>
|
||||
<item>Все границы</item>
|
||||
</string-array>
|
||||
<string-array name="settings_display_aspect_ratio_names">
|
||||
<item>Автонастройка (нативное игре)</item>
|
||||
<item>4:3</item>
|
||||
<item>16:9</item>
|
||||
<item>16:10</item>
|
||||
<item>19:9</item>
|
||||
<item>20:9</item>
|
||||
<item>21:9</item>
|
||||
<item>32:9</item>
|
||||
<item>8:7</item>
|
||||
<item>5:4</item>
|
||||
<item>3:2</item>
|
||||
<item>2:1 (VRAM 1:1)</item>
|
||||
<item>1:1</item>
|
||||
<item>PAR 1:1</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_texture_filter_names">
|
||||
<item>Метод ближайшего соседа</item>
|
||||
<item>Билинейная</item>
|
||||
<item>Билинейная (без сглаживания краёв)</item>
|
||||
<item>JINC2</item>
|
||||
<item>JINC2 (без сглаживания краёв)</item>
|
||||
<item>xBR</item>
|
||||
<item>xBR (без сглаживания краёв)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_controller_type_entries">
|
||||
<item>Цифровой</item>
|
||||
<item>Аналоговый (DualShock)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_memory_card_mode_entries">
|
||||
<item>Без карты памяти</item>
|
||||
<item>Общая для всех игр</item>
|
||||
<item>Своя карта для каждой игры (по коду)</item>
|
||||
<item>Своя карта для каждой игры (по названию)</item>
|
||||
</string-array>
|
||||
<string-array name="emulation_menu">
|
||||
<item>Загрузить состояние</item>
|
||||
<item>Сохранить состояние</item>
|
||||
<item>Включить ускорение</item>
|
||||
<item>Другие опции</item>
|
||||
<item>Выход</item>
|
||||
</string-array>
|
||||
<string-array name="emulation_more_menu">
|
||||
<item>Сброс</item>
|
||||
<item>Чит-коды</item>
|
||||
<item>Сменить диск</item>
|
||||
<item>Настройки</item>
|
||||
<item>Сменить экранный контроллер</item>
|
||||
<item>Настроить экранный контроллер</item>
|
||||
</string-array>
|
||||
<string-array name="settings_cdrom_read_speedup_entries">
|
||||
<item>Нет (двойная скорость)</item>
|
||||
<item>2x (скорость 4x)</item>
|
||||
<item>3x (скорость 6x)</item>
|
||||
<item>4x (скорость 8x)</item>
|
||||
<item>5x (скорость 10x)</item>
|
||||
<item>6x (скорость 12x)</item>
|
||||
<item>7x (скорость 14x)</item>
|
||||
<item>8x (скорость 16x)</item>
|
||||
<item>9x (скорость 18x)</item>
|
||||
<item>10x (скорость 20x)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_touchscreen_controller_view_entries">
|
||||
<item>Нет</item>
|
||||
<item>Цифровой</item>
|
||||
<item>Аналоговый с одним стиком</item>
|
||||
<item>Аналоговый с двумя стиками</item>
|
||||
</string-array>
|
||||
<string-array name="settings_audio_backend_entries">
|
||||
<item>Нет (без звука)</item>
|
||||
<item>Cubeb</item>
|
||||
<item>OpenSL ES (рекомендовано)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_audio_buffer_size_entries">
|
||||
<item>1024 блока (23.22 мс)</item>
|
||||
<item>2048 блоков (46.44 мс, рекомендовано)</item>
|
||||
<item>3072 блока (69.66 мс)</item>
|
||||
<item>4096 блоков (92.88 мс)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_log_level_entries">
|
||||
<item>Нет</item>
|
||||
<item>Error</item>
|
||||
<item>Warning</item>
|
||||
<item>Performance Warnings</item>
|
||||
<item>Information</item>
|
||||
<item>Verbose</item>
|
||||
<item>Developer</item>
|
||||
<item>Profile</item>
|
||||
<item>Debug</item>
|
||||
<item>Trace</item>
|
||||
</string-array>
|
||||
<string-array name="settings_tabs">
|
||||
<item>Основные</item>
|
||||
<item>Экран</item>
|
||||
<item>Звук</item>
|
||||
<item>Улучшения</item>
|
||||
<item>Контроллеры</item>
|
||||
<item>Расширенные</item>
|
||||
</string-array>
|
||||
<string-array name="settings_gpu_msaa_entries">
|
||||
<item>Выключен</item>
|
||||
<item>2x MSAA</item>
|
||||
<item>4x MSAA</item>
|
||||
<item>8x MSAA</item>
|
||||
<item>2x SSAA</item>
|
||||
<item>4x SSAA</item>
|
||||
<item>8x SSAA</item>
|
||||
</string-array>
|
||||
<string-array name="settings_advanced_display_fps_limit_entries">
|
||||
<item>Без ограничений (выводить все кадры)</item>
|
||||
<item>10 FPS</item>
|
||||
<item>15 FPS</item>
|
||||
<item>30 FPS</item>
|
||||
<item>45 FPS</item>
|
||||
<item>60 FPS</item>
|
||||
<item>75 FPS</item>
|
||||
<item>90 FPS</item>
|
||||
</string-array>
|
||||
<string-array name="settings_emulation_speed_entries">
|
||||
<item>Без ограничений</item>
|
||||
<item>10% [6 FPS (NTSC) / 5 FPS (PAL)]</item>
|
||||
<item>20% [12 FPS (NTSC) / 10 FPS (PAL)]</item>
|
||||
<item>30% [18 FPS (NTSC) / 15 FPS (PAL)]</item>
|
||||
<item>40% [24 FPS (NTSC) / 20 FPS (PAL)]</item>
|
||||
<item>50% [30 FPS (NTSC) / 25 FPS (PAL)]</item>
|
||||
<item>60% [36 FPS (NTSC) / 30 FPS (PAL)]</item>
|
||||
<item>70% [42 FPS (NTSC) / 35 FPS (PAL)]</item>
|
||||
<item>80% [48 FPS (NTSC) / 40 FPS (PAL)]</item>
|
||||
<item>90% [54 FPS (NTSC) / 45 FPS (PAL)]</item>
|
||||
<item>100% [60 FPS (NTSC) / 50 FPS (PAL), по умолчанию]</item>
|
||||
<item>125% [75 FPS (NTSC) / 62 FPS (PAL)]</item>
|
||||
<item>150% [90 FPS (NTSC) / 75 FPS (PAL)]</item>
|
||||
<item>175% [105 FPS (NTSC) / 87 FPS (PAL)]</item>
|
||||
<item>200% [120 FPS (NTSC) / 100 FPS (PAL)]</item>
|
||||
<item>250% [150 FPS (NTSC) / 125 FPS (PAL)]</item>
|
||||
<item>300% [180 FPS (NTSC) / 150 FPS (PAL)]</item>
|
||||
<item>350% [210 FPS (NTSC) / 175 FPS (PAL)]</item>
|
||||
<item>400% [240 FPS (NTSC) / 200 FPS (PAL)]</item>
|
||||
<item>450% [270 FPS (NTSC) / 225 FPS (PAL)]</item>
|
||||
<item>500% [300 FPS (NTSC) / 250 FPS (PAL)]</item>
|
||||
<item>600% [360 FPS (NTSC) / 300 FPS (PAL)]</item>
|
||||
<item>700% [420 FPS (NTSC) / 350 FPS (PAL)]</item>
|
||||
<item>800% [480 FPS (NTSC) / 400 FPS (PAL)]</item>
|
||||
<item>900% [540 FPS (NTSC) / 450 FPS (PAL)]</item>
|
||||
<item>1000% [600 FPS (NTSC) / 500 FPS (PAL)]</item>
|
||||
</string-array>
|
||||
<string-array name="settings_advanced_cpu_overclock_entries">
|
||||
<item>25% (8 МГц)</item>
|
||||
<item>50% (16 МГц)</item>
|
||||
<item>75% (24 МГц)</item>
|
||||
<item>100% (33 МГц, по умолчанию)</item>
|
||||
<item>125% (41 МГц)</item>
|
||||
<item>150% (49 МГц)</item>
|
||||
<item>175% (57 МГц)</item>
|
||||
<item>200% (66 МГц)</item>
|
||||
<item>225% (74 МГц)</item>
|
||||
<item>250% (82 МГц)</item>
|
||||
<item>275% (90 МГц)</item>
|
||||
<item>300% (99 МГц)</item>
|
||||
<item>350% (115 МГц)</item>
|
||||
<item>400% (132 МГц)</item>
|
||||
<item>450% (148 МГц)</item>
|
||||
<item>500% (165 МГц)</item>
|
||||
<item>500% (165 МГц)</item>
|
||||
<item>600% (198 МГц)</item>
|
||||
<item>700% (231 МГц)</item>
|
||||
<item>800% (264 МГц)</item>
|
||||
<item>900% (297 МГц)</item>
|
||||
<item>1000% (330 МГц)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_emulation_screen_orientation_entries">
|
||||
<item>По выбору системы</item>
|
||||
<item>Портретная</item>
|
||||
<item>Ландшафтная</item>
|
||||
</string-array>
|
||||
<string-array name="settings_theme_entries">
|
||||
<item>По выбору системы</item>
|
||||
<item>Светлая</item>
|
||||
<item>Тёмная</item>
|
||||
</string-array>
|
||||
<string-array name="settings_downsample_mode_entries">
|
||||
<item>Выключен</item>
|
||||
<item>Прямоугольный (понижать 3D/сглаживать всё)</item>
|
||||
<item>Адаптивный (не понижать 3D/сглаживать 2D)</item>
|
||||
</string-array>
|
||||
<string-array name="settings_boolean_entries">
|
||||
<item>Выключен</item>
|
||||
<item>Включен</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
207
android/app/src/main/res/values-ru/strings.xml
Normal file
207
android/app/src/main/res/values-ru/strings.xml
Normal file
@@ -0,0 +1,207 @@
|
||||
<resources>
|
||||
<string name="app_name">DuckStation</string>
|
||||
<string name="action_settings">Настройки</string>
|
||||
<string name="action_controller_mapping">Привязки геймпада</string>
|
||||
<string name="title_activity_settings">Настройки</string>
|
||||
<string name="settings_console_region">Регион консоли</string>
|
||||
<string name="settings_console_tty_output">Включить вывод в TTY</string>
|
||||
<string name="settings_console_fast_boot">Быстрый запуск</string>
|
||||
<string name="settings_osd_show_messages">Показывать сообщения</string>
|
||||
<string name="settings_osd_show_speed">Показывать скорость эмуляции</string>
|
||||
<string name="settings_osd_show_show_fps">Показывать FPS</string>
|
||||
<string name="settings_osd_show_show_vps">Показывать VPS</string>
|
||||
<string name="settings_cpu_execution_mode">Режим работы CPU</string>
|
||||
<string name="settings_gpu_renderer">Графический движок</string>
|
||||
<string name="settings_gpu_resolution_scale">Множитель разрешения</string>
|
||||
<string name="title_activity_emulation">EmulationActivity</string>
|
||||
<string name="settings_emulation_speed">Скорость эмуляции</string>
|
||||
<string name="settings_fast_forward_speed">Скорость перемотки</string>
|
||||
<string name="settings_save_state_on_exit">Сохранять при выходе</string>
|
||||
<string name="settings_pause_when_menu_opened">Пауза при вызове меню</string>
|
||||
<string name="settings_emulation_screen_orientation">Ориентация экрана эмуляции</string>
|
||||
<string name="settings_load_patch_codes">Загружать чит-коды</string>
|
||||
<string name="settings_summary_save_state_on_exit">Автоматически сохранять состояние эмулятора при выходе. Позволяет продолжить игру с места остановки.</string>
|
||||
<string name="settings_summary_pause_when_menu_opened">Останавливать эмуляцию при вызове меню во время игры.</string>
|
||||
<string name="settings_summary_load_patch_codes">Загружать чит-коды из cheats/<название игры>.cht в формате PCSXR или Libretro. Коды можно активировать внутри игры.</string>
|
||||
<string name="settings_apply_compatibility_settings">Применять настройки совместимости</string>
|
||||
<string name="settings_summary_apply_compatibility_settings">Автоматически отключать улучшения, которые не поддерживаются играми.</string>
|
||||
<string name="settings_summary_video_sync">Включите эту настройку для синхронизации частоты обновления DuckStation с частотой экрана устройства. Синхронизация будет автоматически выключена, если скорость эмуляции не равна 100%.</string>
|
||||
<string name="settings_video_sync">Видеосинхронизация</string>
|
||||
<string name="settings_cpu_overclocking">Разгон CPU</string>
|
||||
<string name="settings_cdrom_region_check">Проверка региона CD-ROM</string>
|
||||
<string name="settings_summary_cdrom_region_check">Снижает вероятность некорректного считывания региона диска эмулятором. Как правило, не влияет на стабильность.</string>
|
||||
<string name="settings_cdrom_preload_image_to_ram">Предзагрузка образа CD-ROM в ОЗУ</string>
|
||||
<string name="settings_summary_preload_image_to_ram">Загружать образ диска в оперативную память. Полезно при запуске игр с ненадёжных сетевых ресурсов. В некоторых случаях помогает устранить заикания при запуске игрой аудиодорожек.</string>
|
||||
<string name="settings_pgxp_vertex_cache">Вершинный кэш PGXP</string>
|
||||
<string name="settings_summary_pgxp_vertex_cache">Использовать координаты экрана в качестве резерва при отслеживании вершин с помощью отказов памяти. Может улучшать совместимость PGXP.</string>
|
||||
<string name="settings_pgxp_cpu_mode">Режим CPU PGXP</string>
|
||||
<string name="settings_summary_pgxp_cpu_mode">Пытаться отслеживать изменения вершин с помощью CPU. Требуется для правильной работы PGXP в некоторых играх. Очень медленный режим, несовместимый с рекомпилятором.</string>
|
||||
<string name="settings_cpu_recompiler_icache">Рекомпиляция кэша команд CPU</string>
|
||||
<string name="settings_summary_cpu_recompiler_icache">Включает симуляцию кэша команд CPU в рекомпиляторе. Повышает точность, незначительно снижая производительность. Попробуйте включить данную опцию, если игры идут слишком быстро.</string>
|
||||
<string name="settings_cpu_recompiler_fastmem">Быстрый доступ к памяти в рекомпиляторе CPU</string>
|
||||
<string name="settings_summary_cpu_recompiler_fastmem">Повышает эффективность доступа к гостевой памяти с помощью бэкпатчей и ошибок страниц. Отключите при нестабильной работе на устройстве.</string>
|
||||
<string name="settings_presented_frame_limit">Ограничение частоты кадров</string>
|
||||
<string name="settings_logging_level">Уровень логирования</string>
|
||||
<string name="settings_log_to_file">Лог в файл</string>
|
||||
<string name="settings_summary_log_to_file">Записывать сообщения лога в duckstation.log в папке эмулятора. Используйте только для отладки из-за замедления эмуляции.</string>
|
||||
<string name="settings_log_to_logcat">Лог в Logcat</string>
|
||||
<string name="settings_summary_log_to_logcat">Записывать сообщения лога в журнал отладки Android. Полезно только при подключении к ПК с adb.</string>
|
||||
<string name="settings_volume">Громкость</string>
|
||||
<string name="settings_summary_volume">Управление громкостью звука эмулятора.</string>
|
||||
<string name="settings_fast_forward_volume">Громкость при ускорении</string>
|
||||
<string name="settings_summary_fast_forward_volume">Управление громкостью звука эмулятора при ускоренной перемотке.</string>
|
||||
<string name="settings_mute_all_sound">Отключить звук</string>
|
||||
<string name="settings_summary_mute_all_sound">Отключает вывод звука в эмуляторе.</string>
|
||||
<string name="settings_mute_cd_audio">Отключить CD аудио</string>
|
||||
<string name="settings_summary_mute_cd_audio">Принудительное отключение CD-DA и XA дорожек диска. Может быть использовано для отключения фоновой музыки в играх.</string>
|
||||
<string name="settings_audio_backend">Аудио бэкенд</string>
|
||||
<string name="settings_audio_buffer_size">Размер аудио буфера</string>
|
||||
<string name="settings_summary_audio_buffer_size">Определяет задержку звука между обработкой и выводом. Меньшие значения снижают задержку, но вызывают треск при нестабильной скорости эмуляции.</string>
|
||||
<string name="settings_audio_sync">Аудиосинхронизация</string>
|
||||
<string name="settings_summary_audio_sync">Ускоряет эмуляцию в зависимости от скорости вывода блоков бэкендом. Помогает устранить искажения звука, если эмуляция идёт слишком быстро. Синхронизация автоматически выключается, если скорость эмуляции не равна 100%.</string>
|
||||
<string name="settings_controller_type">Тип контроллера</string>
|
||||
<string name="settings_enable_analog_mode_on_reset">Включить аналоговый режим при сбросе</string>
|
||||
<string name="settings_touchscreen_controller_view">Вид экранного контроллера</string>
|
||||
<string name="settings_auto_hide_touchscreen_controller">Автоотключение экранного контроллера</string>
|
||||
<string name="settings_summary_auto_hide_touchscreen_controller">Скрывать экранный контроллер при подключении внешнего геймпада.</string>
|
||||
<string name="settings_vibrate_on_press">Вибрация при нажатии</string>
|
||||
<string name="settings_summary_vibrate_on_press">Включает короткую вибрацию при нажатии экранных кнопок. Виброотклик должен быть включен в настройках системы.</string>
|
||||
<string name="settings_enable_vibration">Включить вибрацию</string>
|
||||
<string name="settings_summary_enable_vibration">Перенаправлять отдачу в игре на вибромотор смартфона.</string>
|
||||
<string name="settings_memory_card_1_type">Тип карты памяти 1</string>
|
||||
<string name="settings_memory_card_2_type">Тип карты памяти 2</string>
|
||||
<string name="settings_crop_mode">Режим обрезки</string>
|
||||
<string name="settings_aspect_ratio">Соотношение сторон</string>
|
||||
<string name="settings_linear_upscaling">Линейное масштабирование</string>
|
||||
<string name="settings_integer_upscaling">Целочисленное масштабирование</string>
|
||||
<string name="settings_summary_linear_upscaling">Сглаживать картинку при масштабировании изображения с консоли под экран.</string>
|
||||
<string name="settings_summary_integer_upscaling">Добавлять пустое пространство к изображению, чтобы пиксельное соотношение устройства и консоли равнялось целому числу. Позволяет получить более чёткую картинку в некоторых 2D-играх.</string>
|
||||
<string name="settings_summary_osd_show_messages">Выводить на экран сообщения о системных событиях, таких как сохранение/загрузка, снимок экрана и т.д.</string>
|
||||
<string name="settings_summary_osd_show_speed">Установка скорости эмуляции. Достижение заданной скорости не гарантируется и в этом случае эмулятор будет работать на максимально возможной скорости</string>
|
||||
<string name="settings_summary_osd_show_fps">Показывать внутреннюю частоту кадров игры в правом верхнем углу экрана.</string>
|
||||
<string name="settings_summary_osd_show_vps">Показывать количество кадров (или синхронизаций) в секунду в правом верхнем углу экрана.</string>
|
||||
<string name="settings_cdrom_read_speedup">Ускорение считывания CD-ROM</string>
|
||||
<string name="settings_summary_cdrom_read_speedup">Повышает скорость чтения диска на заданный множитель. Применяется только при считывании с двойной скоростью и не учитывается при воспроизведении аудио. Ускоряет загрузки в некоторых играх, но приводит к ошибкам в других.</string>
|
||||
<string name="settings_summary_console_fast_boot">Пропускать BIOS/заставку, загружаясь прямо в игру. Как правило, не влияет на стабильность, но некоторые игры могут перестать запускаться.</string>
|
||||
<string name="settings_msaa">Мультисэмплинг</string>
|
||||
<string name="settings_true_color">Рендеринг в True Color (24-бита, без дизеринга)</string>
|
||||
<string name="settings_summary_true_color">Позволяет получить более качественные градиенты, но меняет отображение некоторых цветов. Отключение опции также включает дизеринг. Большинство игр совместимо с этой настройкой.</string>
|
||||
<string name="settings_scaled_dithering">Масштабируемый дизеринг (изменять паттерн дизеринга под разрешение)</string>
|
||||
<string name="settings_summary_scaled_dithering">Масштабировать паттерн дизеринга согласно разрешению эмулируемого GPU. Маскирует эффект зашумления на высоких разрешениях. Поддерживается только аппаратными рендерами и, как правило, не влияет на стабильность.</string>
|
||||
<string name="settings_disable_interlacing">Отключить чересстрочность (рендеринг в построчной развёртке)</string>
|
||||
<string name="settings_summary_disable_interlacing">Принудительная отрисовка и вывод кадров в прогрессивной развёртке. Убирает эффект \"гребёнки\" для игр в 480i. Как правило, не влияет на стабильность.</string>
|
||||
<string name="settings_texture_filtering">Фильтрация текстур</string>
|
||||
<string name="settings_force_ntsc_timings">Принудительный NTSC-тайминг (PAL в 60 Гц)</string>
|
||||
<string name="settings_summary_force_ntsc_timings">Использовать NTSC-тайминги кадра, если консоль в режиме PAL, тем самым принудительно запуская игры PAL в 60 Гц.</string>
|
||||
<string name="settings_widescreen_hack">Широкоформатный хак</string>
|
||||
<string name="settings_summary_widescreen_hack">Масштабировать позиции вершин в пространстве экрана до широкоформатных соотношений сторон, расширяя область обзора в 3D-играх с 4:3 до 16:9. Совместимо не со всеми играми.</string>
|
||||
<string name="settings_force_4_3_for_24bit">Включать 4:3 для 24-бит</string>
|
||||
<string name="settings_summary_force_4_3_for_24bit">Переключаться обратно на соотношение 4:3 при отображении 24-битного контента, например видеороликов.</string>
|
||||
<string name="settings_chroma_smoothing_24bit">Сглаживание цвета для 24-бит</string>
|
||||
<string name="settings_summary_chrome_smoothing_24bit">Сглаживает блочность цветовых переходов в 24-битном контенте, например видеороликах. Работает только с аппаратным рендерингом.</string>
|
||||
<string name="settings_pgxp_geometry_correction">Коррекция геометрии PGXP</string>
|
||||
<string name="settings_summary_pgxp_geometry_correction">Уменьшает дрожание и искривление текстур, присущее играм PS1. Работает только с аппаратными рендерами. Совместимо не со всеми играми.</string>
|
||||
<string name="settings_pgxp_culling_correction">Коррекция выборки PGXP</string>
|
||||
<string name="settings_summary_pgxp_culling_correction">Повышает точность отбраковки полигонов, снижая количество провалов в геометрии. Требуется включение коррекции геометрии.</string>
|
||||
<string name="settings_pgxp_texture_correction">Коррекция текстур PGXP</string>
|
||||
<string name="settings_summary_pgxp_texture_correction">Использовать перспективно-корректную интерполяцию для координат текстур и цветов, выпрямляя искривлённые текстуры. Должна быть включена коррекция геометрии.</string>
|
||||
<string name="settings_pgxp_preserve_projection_precision">Точность проекции PGXP</string>
|
||||
<string name="settings_summary_pgxp_preserve_projection_precision">Включает повышенную точность PGXP. Может улучшать графику в некоторых играх, но приводить к ошибкам в других.</string>
|
||||
<string name="settings_pgxp_depth_buffer">Буфер глубины PGXP</string>
|
||||
<string name="settings_summary_pgxp_depth_buffer">Пытаться устранять наложения полигонов проверкой пикселей по значениям глубины PGXP. Обладает низкой совместимостью, но в ряде игр даёт хороший эффект.</string>
|
||||
<string name="menu_main_resume_last_session">Продолжить сеанс</string>
|
||||
<string name="menu_main_start_file">Запустить файл</string>
|
||||
<string name="menu_main_start_bios">Запустить BIOS</string>
|
||||
<string name="menu_main_edit_game_directories">Настроить каталоги игр</string>
|
||||
<string name="menu_main_scan_for_new_games">Обновить список игр</string>
|
||||
<string name="menu_main_rescan_all_games">Проверить игры в списке</string>
|
||||
<string name="menu_main_import_bios">Импорт BIOS</string>
|
||||
<string name="menu_main_show_version">Показать версию</string>
|
||||
<string name="menu_main_github_repository">Репозиторий GitHub</string>
|
||||
<string name="menu_main_discord_server">Сервер Discord</string>
|
||||
<string name="menu_game_list_entry_start_game">Запустить игру</string>
|
||||
<string name="menu_game_list_entry_resume_game">Продолжить игру</string>
|
||||
<string name="android_progress_callback_please_wait">Пожалуйста, подождите...</string>
|
||||
<string name="android_progress_callback_ok">OK</string>
|
||||
<string name="android_progress_callback_information">Информация</string>
|
||||
<string name="android_progress_callback_confirmation">Подтверждение</string>
|
||||
<string name="android_progress_callback_yes">Да</string>
|
||||
<string name="android_progress_callback_no">Нет</string>
|
||||
<string name="emulation_activity_error">Ошибка</string>
|
||||
<string name="emulation_activity_ok">OK</string>
|
||||
<string name="emulation_activity_import_patch_codes">Импорт чит-кодов...</string>
|
||||
<string name="emulation_activity_patch_on">(ВКЛ)</string>
|
||||
<string name="emulation_activity_patch_off">(ВЫКЛ)</string>
|
||||
<string name="emulation_activity_choose_patch_code_file">Выберите файл с чит-кодами</string>
|
||||
<string name="emulation_activity_failed_to_import_patch_codes">Не удалось импортировать чит-коды. Убедитесь, что выбранный файл в формате PCSXR или Libretro.</string>
|
||||
<string name="main_activity_choose_directory">Выберите каталог</string>
|
||||
<string name="main_activity_error">Ошибка</string>
|
||||
<string name="main_activity_get_path_from_file_error">Не удалось получить путь к указанному файлу. Пожалуйста, убедитесь, что файл присутствует во внутреннем/внешнем хранилище. Нажмите значок настроек в окне выбора каталога. Включите отображение внутренней памяти. Нажмите кнопку меню и выберите устройство или SD-карту.</string>
|
||||
<string name="main_activity_ok">OK</string>
|
||||
<string name="main_activity_get_path_from_directory_error">Не удалось получить путь к указанному каталогу. Пожалуйста, убедитесь, что каталог присутствует во внутреннем/внешнем хранилище. Нажмите значок настроек в окне выбора каталога. Включите отображение внутренней памяти. Нажмите кнопку меню и выберите устройство или SD-карту.</string>
|
||||
<string name="main_activity_external_storage_permissions_error">DuckStation требуются разрешения для доступа к внешнему хранилищу.</string>
|
||||
<string name="main_activity_choose_disc_image">Выберите образ диска</string>
|
||||
<string name="main_activity_missing_bios_image_prompt">Не найдены образы BIOS в папке bios эмулятора. Указать путь и импортировать образ BIOS сейчас?</string>
|
||||
<string name="main_activity_missing_bios_image">Отсутствует образ BIOS</string>
|
||||
<string name="main_activity_yes">Да</string>
|
||||
<string name="main_activity_no">Нет</string>
|
||||
<string name="main_activity_choose_bios_image">Выберите образ BIOS</string>
|
||||
<string name="main_activity_failed_to_open_bios_image">Не удалось открыть образ BIOS.</string>
|
||||
<string name="main_activity_failed_to_read_bios_image_prefix">\"Не удалось прочитать образ BIOS: \"</string>
|
||||
<string name="main_activity_bios_image_too_large">Образ BIOS слишком большой.</string>
|
||||
<string name="main_activity_invalid_error">Образ BIOS неверный или уже был импортирован.</string>
|
||||
<string name="main_activity_show_version_title">Версия</string>
|
||||
<string name="main_activity_copy">Копировать</string>
|
||||
<string name="settings_gpu_thread">Рендеринг GPU в отдельном потоке</string>
|
||||
<string name="settings_summary_gpu_thread">Использовать второй поток для отрисовки графики. В настоящее время доступно только для программного рендера, существенно повышая производительность и не влияя на стабильность.</string>
|
||||
<string name="settings_gpu_threaded_presentation">Вывод с GPU в отдельном потоке</string>
|
||||
<string name="settings_summary_gpu_threaded_presentation">Выводить кадры в фоновом потоке, если отключена перемотка или видеосинхронизация. Может значительно улучшать производительность при рендеринге на Vulkan.</string>
|
||||
<string name="settings_language">Язык (требуется перезапуск)</string>
|
||||
<string name="touchscreen_controller_stop_editing">Закрыть редактор</string>
|
||||
<string name="touchscreen_controller_reset_layout">Сбросить макет</string>
|
||||
<string name="emulation_activity_touchscreen_controller_not_active">Экранный контроллер неактивен.</string>
|
||||
<string name="settings_theme">Тема</string>
|
||||
<string name="settings_controller_mapping_summary">Позволяет настроить привязки внешнего геймпада к кнопкам и осям эмулируемого контроллера.</string>
|
||||
<string name="settings_controller_mapping">Настройка контроллера</string>
|
||||
<string name="controller_binding_dialog_message">Нажмите кнопку геймпада для установки новой привязки.\n\nТекущая привязка: %s</string>
|
||||
<string name="controller_binding_dialog_no_binding"><Нет привязки></string>
|
||||
<string name="controller_binding_dialog_cancel">Отмена</string>
|
||||
<string name="controller_binding_dialog_clear">Удалить</string>
|
||||
<string name="controller_mapping_activity_title">Настройка контроллера</string>
|
||||
<string name="controller_mapping_activity_no_profiles_found">Профили не найдены.</string>
|
||||
<string name="controller_mapping_activity_select_input_profile">Выбор профиля ввода</string>
|
||||
<string name="controller_mapping_activity_failed_to_load_profile">Не удалось загрузить профиль \'%s\'</string>
|
||||
<string name="controller_mapping_activity_input_profile_name">Имя профиля ввода:</string>
|
||||
<string name="controller_mapping_activity_save">Сохранить</string>
|
||||
<string name="controller_mapping_activity_name_must_be_provided">Требуется ввести имя.</string>
|
||||
<string name="controller_mapping_activity_failed_to_save_input_profile">Не удалось сохранить профиль ввода.</string>
|
||||
<string name="controller_mapping_activity_input_profile_saved">Профиль ввода \'%s\' сохранён.</string>
|
||||
<string name="controller_mapping_activity_cancel">Отмена</string>
|
||||
<string name="settings_use_analog_sticks_for_dpad">Аналог в качестве d-pad в цифровом режиме</string>
|
||||
<string name="settings_summary_enable_analog_mode_on_reset">Переключает контроллер в аналоговый режим при выключении/перезагрузке консоли.</string>
|
||||
<string name="settings_summary_use_analog_sticks_for_dpad">Позволяет использовать аналоговый джойстик как d-pad вместе с кнопками в цифровом режиме.</string>
|
||||
<string name="settings_disable_all_enhancements">Отключить улучшения</string>
|
||||
<string name="settings_summary_disable_all_enhancements">Временно отключить все улучшения. Полезно для отладки.</string>
|
||||
<string name="settings_downsample_mode">Даунсэмплинг</string>
|
||||
<string name="activity_game_properties">Свойства</string>
|
||||
<string name="game_properties_preference_use_global_setting">Глобальная настройка</string>
|
||||
<string name="settings_input_profile">Профиль ввода</string>
|
||||
<string name="game_properties_tab_summary">Сведения</string>
|
||||
<string name="game_properties_tab_game_settings">Настройки игры</string>
|
||||
<string name="game_properties_tab_controller_settings">Настройки контроллера</string>
|
||||
<string name="settings_audio_resampling">Аудио ресэмплинг</string>
|
||||
<string name="settings_summary_audio_resampling">Изменять частоту звука вместо пропуска блоков, если скорость эмуляции выходит за 100%. Улучшает звук при ускорении/замедлении, незначительно влияя на производительность.</string>
|
||||
<string name="settings_general_sync_to_host_refresh_rate">Синхронизировать с частотой хоста</string>
|
||||
<string name="settings_summary_general_sync_to_host_refresh_rate">Подстраивать скорость эмуляции для совпадения частоты обновления консоли с частотой хоста, если включены Видеосинхронизация и Аудио ресэмплинг. Обеспечивает наиболее плавную анимацию за счёт возможного завышения скорости менее чем на 1%.</string>
|
||||
<string name="settings_sustained_performance_mode">Режим устойчивой производительности</string>
|
||||
<string name="settings_summary_sustained_performance_mode">Включает режим устойчивой производительности Андроид. Может обеспечивать более стабильный фреймрейт при продолжительных сеансах игры на некоторых устройствах.</string>
|
||||
<string name="title_activity_game_directories">Настройка каталогов игр</string>
|
||||
<string name="settings_game_directories">Каталоги игр</string>
|
||||
<string name="settings_summary_game_directories">Настроить список каталогов, используемых при поиске игр.</string>
|
||||
<string name="game_directories_scanning_subdirectories">Сканировать подкаталоги.</string>
|
||||
<string name="game_directories_not_scanning_subdirectories">Не сканировать подкаталоги.</string>
|
||||
<string name="settings_display_all_frames">Оптимальное распределение кадров</string>
|
||||
<string name="settings_summary_display_all_frames">Включение настройки гарантирует, что все кадры с консоли будут выведены на экран для оптимального распределения. Попробуйте отключить опцию, если не получается достичь полной скорости или есть проблемы со звуком.</string>
|
||||
<string name="settings_osd_show_show_resolution">Показывать разрешение</string>
|
||||
<string name="settings_summary_osd_show_resolution">Показывать разрешение рендеринга игры в правом верхнем углу экрана.</string>
|
||||
</resources>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user