Compare commits
881 commits
Author | SHA1 | Date | |
---|---|---|---|
|
fec4428ebf | ||
|
90725252d3 | ||
|
e71cbc9bc3 | ||
|
761f9264ba | ||
|
449c14093d | ||
|
6d0479d7c1 | ||
|
3a4f067313 | ||
|
da9dd8522a | ||
|
e635b9ec8a | ||
|
506bf24775 | ||
|
fa123cea3f | ||
|
c061bb7b47 | ||
|
b286537b69 | ||
|
67f0388eae | ||
|
1a460d73be | ||
|
2111c94f1e | ||
|
3a654b3609 | ||
|
c59ee99364 | ||
|
5990cb3b02 | ||
|
1bd00a87f9 | ||
|
88e986ca53 | ||
|
90279e6f9e | ||
|
e378a52b5a | ||
|
fb59320829 | ||
|
3ea05bd200 | ||
|
432eb0d2b3 | ||
|
9bc50a4b9c | ||
|
1948bea209 | ||
|
96e1c8fcd3 | ||
|
5a6ad5f02f | ||
|
08fbf6be74 | ||
|
7bfcdbf442 | ||
|
ebefbdc4db | ||
|
0a65519867 | ||
|
4616b3bc19 | ||
|
7af8736f91 | ||
|
589402b44c | ||
|
b0fdb8e790 | ||
|
0a0ea75013 | ||
|
2da79414d6 | ||
|
da797831ba | ||
|
b1a1b4caa7 | ||
|
7d5cedf476 | ||
|
d42974b5db | ||
|
a376bb5c9b | ||
|
042ab6de03 | ||
|
4cb66217c2 | ||
|
86d1bde845 | ||
|
54a78902bc | ||
|
d85c963c4e | ||
|
1cae66f163 | ||
|
5b409c39ac | ||
|
154e927264 | ||
|
c2fd85647a | ||
|
d30f2646ec | ||
|
b559725920 | ||
|
86ea40a9e5 | ||
|
40404ba7ba | ||
|
5042594f3b | ||
|
84c358660c | ||
|
33c3e67b31 | ||
|
0c6c455d4d | ||
|
ca5cc349e9 | ||
|
81bdfef0be | ||
|
29083f0a08 | ||
|
59f51f7d0c | ||
|
bde51b6d27 | ||
|
4ea15c9170 | ||
|
8604a98edf | ||
|
5f48028284 | ||
|
3eb89847ad | ||
|
cb8b13e129 | ||
|
80ccede765 | ||
|
7c2918f3f7 | ||
|
e8c0b7f9c5 | ||
|
8cc9bfbb36 | ||
|
36c716d030 | ||
|
5a2a33224c | ||
|
7c5f7954e5 | ||
|
3787358bda | ||
|
1b0c08cad2 | ||
|
7257f34ff5 | ||
|
4e2c825277 | ||
|
d3946f8fe5 | ||
|
54978542d2 | ||
|
4cfc5fb32a | ||
|
12f5b6bc98 | ||
|
dc80828397 | ||
|
4ce0768ba1 | ||
|
79d24cba11 | ||
|
6be642a118 | ||
|
cbf1e414a5 | ||
|
bdb467d09f | ||
|
cc669d7cab | ||
|
875464d6b4 | ||
|
2e9bbae46d | ||
|
055dbc7fb6 | ||
|
b436fdca64 | ||
|
4d4029ab51 | ||
|
67d4bd484a | ||
|
8909e6e7ea | ||
|
6a6e4a2af6 | ||
|
d2f20224ba | ||
|
4af4780f18 | ||
|
1c9af52ca2 | ||
|
700a7575d7 | ||
|
60b0e3db85 | ||
|
303b106137 | ||
|
da907499a0 | ||
|
1d2429c3ea | ||
|
4606be05ec | ||
|
8940d78ea4 | ||
|
873fca076a | ||
|
63cbfbb523 | ||
|
bfdc6f0240 | ||
|
28461a1d44 | ||
|
9aacc412ca | ||
|
5f822b32d5 | ||
|
545ac81d81 | ||
|
79a9de25d4 | ||
|
6b1f39bdf0 | ||
|
d8df0dc4fa | ||
|
1e54614d41 | ||
|
c3e3b2358d | ||
|
156328fbfb | ||
|
c2b479889c | ||
|
713e19a4a1 | ||
|
ab2005d50d | ||
|
7e51f50999 | ||
|
18f3865f8b | ||
|
e1f830c7f7 | ||
|
286ec8a5f5 | ||
|
547d47d9dc | ||
|
87652bfeda | ||
|
72b4194c95 | ||
|
d299081c40 | ||
|
a11019150a | ||
|
3b6190b69a | ||
|
b251f84ab1 | ||
|
93c143d79d | ||
|
4df171abfc | ||
|
1a40deccb1 | ||
|
c35304e485 | ||
|
0089d73588 | ||
|
a7c1ea769c | ||
|
842634e64e | ||
|
04d047ff75 | ||
|
83aafa31f0 | ||
|
1c5c03f8d4 | ||
|
b324a5b9e2 | ||
|
1baf8a13ed | ||
|
b2c0f18e62 | ||
|
b78450c88d | ||
|
3df63beef4 | ||
|
f30eed7980 | ||
|
878ff419fd | ||
|
43991b7653 | ||
|
bea7b00c7d | ||
|
0e94eae483 | ||
|
33f45cf1f5 | ||
|
224ddac07c | ||
|
20a6e0bf0d | ||
|
889c2453de | ||
|
efb6cdd30d | ||
|
47ffd76fae | ||
|
66be960150 | ||
|
452510b932 | ||
|
782b7550f8 | ||
|
4629314961 | ||
|
b85dba277f | ||
|
f16d171632 | ||
|
f4eb8bc644 | ||
|
a8d3fb0835 | ||
|
b32ab1f49d | ||
|
e22bc58060 | ||
|
7a4f3f4836 | ||
|
6a79309722 | ||
|
c70388dbeb | ||
|
69e8e1c2c4 | ||
|
9be353a9b4 | ||
|
b214782a15 | ||
|
e47923704e | ||
|
d7e4cf18b5 | ||
|
10451a676b | ||
|
49b65242b9 | ||
|
8cfffb8119 | ||
|
b299609a9b | ||
|
a1601b5c8b | ||
|
8554cd5f0a | ||
|
d481fdd664 | ||
|
4cc62d4870 | ||
|
f322ba5102 | ||
|
07cee43a2b | ||
|
b2454233a2 | ||
|
884597615b | ||
|
b5bd89b66d | ||
|
9638ba0be4 | ||
|
8fc6675a05 | ||
|
369f810b51 | ||
|
4b0591c9ea | ||
|
1287ec7b6d | ||
|
70d93a1ad8 | ||
|
abe0709a81 | ||
|
d459f9c1c3 | ||
|
760c67c759 | ||
|
965ea6d012 | ||
|
f374b1dd51 | ||
|
08aedfde63 | ||
|
818271c7ad | ||
|
02f8250aff | ||
|
272c24d8e4 | ||
|
7ae8412919 | ||
|
158be432fc | ||
|
48f286334c | ||
|
52655335c0 | ||
|
a6750e7aef | ||
|
85d363c17d | ||
|
edb047ec34 | ||
|
28c5b565c0 | ||
|
8cf0fbef1b | ||
|
cdc61ea95a | ||
|
54d0618f02 | ||
|
827cf93aca | ||
|
8c80099339 | ||
|
238d84ba3b | ||
|
5d28f11ccf | ||
|
af1fe13996 | ||
|
c97a174cd1 | ||
|
89d129211e | ||
|
49a94a13c5 | ||
|
afaf18f124 | ||
|
fa9ce5fc70 | ||
|
5eb628eb5f | ||
|
82068031f3 | ||
|
96f684e51c | ||
|
3e72323653 | ||
|
d14b1da5a1 | ||
|
1c8b7a61b0 | ||
|
90420160f2 | ||
|
779e30e3e5 | ||
|
a8041bce8c | ||
|
683b4186d8 | ||
|
699d2ded6d | ||
|
3567a4bc55 | ||
|
c0c8545dc2 | ||
|
c12c3bce8d | ||
|
f1b7830952 | ||
|
656b7da5e4 | ||
|
1420e714e7 | ||
|
4b21d601e2 | ||
|
545bbd5c45 | ||
|
9055076d0d | ||
|
8830747e90 | ||
|
4adc50039c | ||
|
595e4e0341 | ||
|
201edfb02d | ||
|
08237e6a2c | ||
|
2cffafff86 | ||
|
e421f02500 | ||
|
1674ad5a2c | ||
|
2754df9b94 | ||
|
471bdd6ab9 | ||
|
2debced399 | ||
|
c694ce9a25 | ||
|
dfc7b59de2 | ||
|
b09972892a | ||
|
937348a36f | ||
|
4b11eb0984 | ||
|
5babeaf692 | ||
|
24bb295d82 | ||
|
27de70dce9 | ||
|
4a90b1ede1 | ||
|
992c9fb98c | ||
|
458e466f67 | ||
|
1256395688 | ||
|
ac3840ddb0 | ||
|
dbdf21b1ab | ||
|
45eda2f120 | ||
|
58e1a53699 | ||
|
98b5d56021 | ||
|
c772b1c702 | ||
|
0602467c61 | ||
|
e756815b51 | ||
|
ff7e0f9ca8 | ||
|
16cac4c05a | ||
|
520e00c553 | ||
|
d208c24c0c | ||
|
4a2491df81 | ||
|
17b9699c24 | ||
|
88e0782f71 | ||
|
860eacc7e6 | ||
|
0cf5687e64 | ||
|
85bae2e94e | ||
|
e9f98bffa9 | ||
|
6e65367e07 | ||
|
68a6d73a18 | ||
|
c50c9a35a7 | ||
|
24c4e02143 | ||
|
195f3388e9 | ||
|
eacd9fdbff | ||
|
d1922798c5 | ||
|
e666afd1a3 | ||
|
91ebff1b5f | ||
|
bec63c43a1 | ||
|
e6c97edb1c | ||
|
f572373fc1 | ||
|
e26f58595e | ||
|
6668ba3e37 | ||
|
57ecc18f32 | ||
|
45dd69d62a | ||
|
c7db6fe5dc | ||
|
908222f26f | ||
|
04e18987dc | ||
|
e557bd2976 | ||
|
d0f13de4c5 | ||
|
f095e6af0b | ||
|
b7bc520afa | ||
|
13bff60257 | ||
|
11e7eb7fd6 | ||
|
1413cdaebf | ||
|
2abf642922 | ||
|
32ddc28789 | ||
|
33cb3d9c9f | ||
|
a0e506affc | ||
|
bb19049fc0 | ||
|
da23ec1a06 | ||
|
19b69bbdc2 | ||
|
df5d14e3d8 | ||
|
f16486757b | ||
|
156c3031a2 | ||
|
2dc5c706b5 | ||
|
be75fa43a3 | ||
|
c319e59545 | ||
|
7aa41d0b35 | ||
|
0f80d0af7a | ||
|
d436ed8695 | ||
|
855a374f67 | ||
|
a716e395ec | ||
|
1fa9ce126b | ||
|
cd519499ee | ||
|
e4d4a35674 | ||
|
964d46a12a | ||
|
fc397b2b58 | ||
|
850aadb0f6 | ||
|
0aa1ed21b2 | ||
|
a5ea268826 | ||
|
91f8f81418 | ||
|
85af58f0a7 | ||
|
2d72b66042 | ||
|
6399cb55e2 | ||
|
cb2448a004 | ||
|
a8c68baa6f | ||
|
a93f4d4c64 | ||
|
9bd711958b | ||
|
3d9a1a8b5d | ||
|
0a0f623c7c | ||
|
04d6c52784 | ||
|
906abe0fb3 | ||
|
b8712b37c3 | ||
|
d7bca1460c | ||
|
8fc61cdb7b | ||
|
82df95cf88 | ||
|
b90c15919b | ||
|
4176a19256 | ||
|
a6e3d41628 | ||
|
b333bf8a0c | ||
|
ec754f5820 | ||
|
ed67069487 | ||
|
be1c801fc2 | ||
|
69b07da7df | ||
|
2a6cd3c5ea | ||
|
a419312cc7 | ||
|
8b26e1e3fc | ||
|
f49f378839 | ||
|
8611e98b92 | ||
|
f219432c6a | ||
|
5c40fb0cbf | ||
|
bbcd21de05 | ||
|
4214d9bce4 | ||
|
8091e44206 | ||
|
69c79a7f6c | ||
|
69daed6ace | ||
|
af552edd9d | ||
|
dec3df39e0 | ||
|
270f4b00a9 | ||
|
eb7e02fbc2 | ||
|
20335b7d2d | ||
|
9415cee59a | ||
|
e4550b3e4f | ||
|
5c1e2912a3 | ||
|
ac55c3e324 | ||
|
25098082c7 | ||
|
90abf8a376 | ||
|
e36b6c77a7 | ||
|
53c76ae0d4 | ||
|
bd38f9a8ab | ||
|
93e5f8e32c | ||
|
2f50038db9 | ||
|
b4ae32960c | ||
|
c9a4e4e41e | ||
|
00037d8a5e | ||
|
6279ce3df2 | ||
|
e1268f57b5 | ||
|
cab30494f6 | ||
|
7e480e35ec | ||
|
ccf9693877 | ||
|
b51e2fd25f | ||
|
7e7856fa44 | ||
|
ed00ddc805 | ||
|
2ca886f64f | ||
|
801d14e463 | ||
|
0fc95ae8ef | ||
|
d013582223 | ||
|
cb0e69847c | ||
|
6bef278f43 | ||
|
aad7bb817e | ||
|
a4ec770587 | ||
|
219a560cbe | ||
|
27ddb1272a | ||
|
71b8031d5b | ||
|
967d9398ce | ||
|
0ecdf00e64 | ||
|
8b4eacc7b6 | ||
|
139f35588d | ||
|
e5bed23cee | ||
|
441aa2346c | ||
|
c02b382262 | ||
|
f6ebf83982 | ||
|
9b4e5841e7 | ||
|
db80131213 | ||
|
b8a186d5cd | ||
|
a2649ffb76 | ||
|
2cd50e7f37 | ||
|
c535ae43ee | ||
|
11c9279329 | ||
|
fe53214c86 | ||
|
0878474e01 | ||
|
c8eb1c1128 | ||
|
fe4bbea2ef | ||
|
61e2e71f68 | ||
|
133082c232 | ||
|
ea59933b18 | ||
|
b384cb8ad9 | ||
|
c73758959b | ||
|
8eab353491 | ||
|
bee414a4f8 | ||
|
c4e45ee6b8 | ||
|
398d84d13e | ||
|
186fd3b94b | ||
|
d87477832b | ||
|
276cf9e06f | ||
|
83dabc2737 | ||
|
e608436315 | ||
|
096d0a89ee | ||
|
a1ff34d417 | ||
|
fe566e960b | ||
|
6f6167a201 | ||
|
a3886a948f | ||
|
f248893ffe | ||
|
a12b721c95 | ||
|
623a9a64d6 | ||
|
c7e22c540d | ||
|
ea03d135da | ||
|
0a49dc0af7 | ||
|
173bd03a53 | ||
|
dc629e1b3f | ||
|
d47e964c80 | ||
|
1b071b62fa | ||
|
31902e92a9 | ||
|
0e4079f304 | ||
|
35b49d0b6c | ||
|
0fe62f9b46 | ||
|
de9375122b | ||
|
5b408e784b | ||
|
800c11ff62 | ||
|
eacf290f31 | ||
|
d4cf54d56c | ||
|
1c9a3ac3d3 | ||
|
29d9ed7224 | ||
|
12d25fe20d | ||
|
4e9f824165 | ||
|
79bfb67156 | ||
|
85a17c3fcd | ||
|
842943fa4c | ||
|
798c651a17 | ||
|
2cf4563533 | ||
|
2f9d5e30b4 | ||
|
9a50a57d32 | ||
|
aa7a6bfe7a | ||
|
80527edcb6 | ||
|
332fbcfff1 | ||
|
66bcf384f3 | ||
|
70f443b06e | ||
|
81932421cf | ||
|
70f733ffb8 | ||
|
64dc0a01dc | ||
|
6a424a7a66 | ||
|
e4b81d61a4 | ||
|
ad380b8c5a | ||
|
0490c6753f | ||
|
21492f81a9 | ||
|
fb8130a868 | ||
|
2fc9c0a573 | ||
|
8e303d8d08 | ||
|
f004aa6021 | ||
|
88e8491c7f | ||
|
3ff16e1588 | ||
|
f77c9720a6 | ||
|
5161ef8ff3 | ||
|
27828b1351 | ||
|
eab1a12b07 | ||
|
2eaaccd96b | ||
|
609eb6d880 | ||
|
2fc66fd3ba | ||
|
01875e080a | ||
|
8c296905f4 | ||
|
000dc09a30 | ||
|
b797c92b2e | ||
|
1af7664efc | ||
|
466e67a2fd | ||
|
ea6818eb4b | ||
|
a43f0e3fb4 | ||
|
0aa024876e | ||
|
9884663347 | ||
|
55d99734e1 | ||
|
1cc3bbf68d | ||
|
1c355041fa | ||
|
37f9f5d894 | ||
|
43a1c89478 | ||
|
5da93d17bd | ||
|
2548bde538 | ||
|
4070bea697 | ||
|
a85ca0459a | ||
|
2e696deccf | ||
|
428401870b | ||
|
2b76f89b7b | ||
|
df3200a465 | ||
|
35b15fdd48 | ||
|
3b9490e633 | ||
|
429dc2a944 | ||
|
5284109fd4 | ||
|
3270cfe602 | ||
|
5488e9ca7c | ||
|
40e2774b7f | ||
|
dcd64802a3 | ||
|
27ad7b01f3 | ||
|
a00a5e0f8f | ||
|
5a1263194e | ||
|
2b34ef4a89 | ||
|
cda4aa9ee6 | ||
|
e13fe49bbb | ||
|
c89fe05b8a | ||
|
fe9939689d | ||
|
18df066463 | ||
|
929019e76b | ||
|
57193e7944 | ||
|
ff1eaed0a4 | ||
|
498d19fa51 | ||
|
e71a8bec9c | ||
|
daf621dbe1 | ||
|
844d954a26 | ||
|
048eca557e | ||
|
d68b81c037 | ||
|
823e0def0b | ||
|
e4bea43fd6 | ||
|
25d8e5807f | ||
|
361172b4de | ||
|
2c81aa2863 | ||
|
10504984a2 | ||
|
3505bf0df9 | ||
|
24705fe67e | ||
|
5ba773a393 | ||
|
4b07ebed86 | ||
|
e5c09a092d | ||
|
fc83d518e2 | ||
|
67fe3214fe | ||
|
1e1665fea9 | ||
|
914193a765 | ||
|
02496b57eb | ||
|
90f8fbfcb4 | ||
|
fdfb012aa1 | ||
|
ddc14cea09 | ||
|
58da6ea8a4 | ||
|
43dfda0f19 | ||
|
9d8868cffd | ||
|
d459cb1d6c | ||
|
8bca988b55 | ||
|
d010d95e18 | ||
|
4b46b6ebf9 | ||
|
89a800da8e | ||
|
85161b08ea | ||
|
9110176e00 | ||
|
de980f997b | ||
|
cc1e7e2162 | ||
|
6279ed699a | ||
|
7b580ac80e | ||
|
267b1b3cc8 | ||
|
505abbe532 | ||
|
fa4cc35a9c | ||
|
093364f615 | ||
|
f7c6ec3b57 | ||
|
7a5bb2859e | ||
|
33eb096ef8 | ||
|
f58354af06 | ||
|
d3dc9e2f71 | ||
|
b685cb8b13 | ||
|
16a312ee15 | ||
|
6af4a04987 | ||
|
5dd3c02ffb | ||
|
363c71e66c | ||
|
0a51a80d91 | ||
|
3c25be4c63 | ||
|
51e2ef893c | ||
|
641c23e58c | ||
|
45bdfd67b1 | ||
|
e2884e8413 | ||
|
adcc9d93e4 | ||
|
f37bd9363f | ||
|
f5d3cc9fbf | ||
|
a797297105 | ||
|
8cb7a230cc | ||
|
3b4960194f | ||
|
10654ce1ca | ||
|
b256c89e23 | ||
|
74ea66efc5 | ||
|
9dc12e0681 | ||
|
0be8c45e37 | ||
|
7e77af0de8 | ||
|
45ae6bd3a8 | ||
|
880cc20fcf | ||
|
ce63596716 | ||
|
362b8e6621 | ||
|
8cee60ebf5 | ||
|
28ca4cd795 | ||
|
e6d012d05d | ||
|
61ba0f2d35 | ||
|
9cc3fc0c4c | ||
|
eabd22423c | ||
|
fdc8bfd58e | ||
|
c6d0769a95 | ||
|
407411cc0b | ||
|
01b6380242 | ||
|
31eea40ea5 | ||
|
864604c1e7 | ||
|
e3c9f0b219 | ||
|
2229adbd21 | ||
|
3d52692536 | ||
|
a2276c922f | ||
|
a473a34794 | ||
|
63f54478f0 | ||
|
5c39674907 | ||
|
8ff2d7f9b2 | ||
|
491b415759 | ||
|
99a1a0133d | ||
|
ca1c42c280 | ||
|
52accdde43 | ||
|
2ff5bff00a | ||
|
f0c20d70bc | ||
|
6e6c84eebb | ||
|
8fc61769ab | ||
|
ef3bd02819 | ||
|
29494efd94 | ||
|
d09254a8ae | ||
|
5fe1637e17 | ||
|
85a2ca1bf4 | ||
|
c108da5e02 | ||
|
62cbe4d031 | ||
|
313620cad9 | ||
|
b6a054cdad | ||
|
76a14b3bae | ||
|
194f29206b | ||
|
707b11ccd8 | ||
|
442ae3a210 | ||
|
04bc7a8926 | ||
|
ecf0416b64 | ||
|
dcdafda658 | ||
|
a3fda72f88 | ||
|
84b8bc8c48 | ||
|
f78acb8049 | ||
|
ebf705d10d | ||
|
3c06098ead | ||
|
88dc2cc864 | ||
|
87b838f1bf | ||
|
af996c55ba | ||
|
0be099d1ea | ||
|
fa82dad38d | ||
|
97f97dc6e2 | ||
|
179115970f | ||
|
75663d0601 | ||
|
7343497f36 | ||
|
c13c8046d4 | ||
|
ef2467bc60 | ||
|
6f3c7d358b | ||
|
b5718010ee | ||
|
23770e2704 | ||
|
6d78750824 | ||
|
99e06d8325 | ||
|
aaef3b66f9 | ||
|
6c2e0be07d | ||
|
0768342cd0 | ||
|
9a5dc71027 | ||
|
fe7cb3f4af | ||
|
5e2930a81b | ||
|
87ad45788d | ||
|
745da35324 | ||
|
c849af47da | ||
|
7094544c29 | ||
|
d94167f89b | ||
|
fe01df588b | ||
|
a05879b247 | ||
|
c0621d0760 | ||
|
1ce414ef8c | ||
|
281a7eefbf | ||
|
1430f1c4c2 | ||
|
49ab77cac2 | ||
|
46f92f145f | ||
|
02d506f843 | ||
|
e536f5d635 | ||
|
3aa977c6c5 | ||
|
74e8f7d4eb | ||
|
0525d96aec | ||
|
dc32e9a563 | ||
|
adb8cccf8c | ||
|
c74d2ea581 | ||
|
d28ead1438 | ||
|
83f4fcc65a | ||
|
5341ee8f1e | ||
|
1c691e1690 | ||
|
f2fac171a0 | ||
|
0ab7bf3b17 | ||
|
d7036fc4e7 | ||
|
058530bd90 | ||
|
6757117e0d | ||
|
13136c6686 | ||
|
d0c4b8ff3b | ||
|
56398acdc9 | ||
|
b2c653ed1b | ||
|
cad4174845 | ||
|
8b93ef76ac | ||
|
c0960dcccd | ||
|
bf050027c6 | ||
|
0f2fa4e738 | ||
|
d31e79ebc8 | ||
|
9ca7e88b6c | ||
|
c17c2307c7 | ||
|
6c73fb13c5 | ||
|
6dc75db37f | ||
|
a82c03312a | ||
|
ae24bd0701 | ||
|
b42c7a122b | ||
|
3298bd14db | ||
|
03292b5fbc | ||
|
18f1ea5453 | ||
|
385a2232a2 | ||
|
fa0ee8a080 | ||
|
7c6b498918 | ||
|
c30fbd3801 | ||
|
6386605b97 | ||
|
c57f2db6c0 | ||
|
c4fa9f7c8a | ||
|
9fd94f0b17 | ||
|
ab37cb571d | ||
|
102361174a | ||
|
cc44801acd | ||
|
71109d701d | ||
|
f553e80080 | ||
|
eb23d7eab3 | ||
|
53c3b5096c | ||
|
a9a31c3306 | ||
|
101cc4d0ac | ||
|
0cccde79f4 | ||
|
8132f07ee4 | ||
|
8afda8da5b | ||
|
29c946af99 | ||
|
09f6d46aaa | ||
|
6527df7211 | ||
|
79079f35e2 | ||
|
c3cd6acefe | ||
|
63a09dd51f | ||
|
e6880b1564 | ||
|
a78a1a099f | ||
|
00bf1bc6b8 | ||
|
609d3fc196 | ||
|
95c1923c37 | ||
|
4dd66498d5 | ||
|
13d680e8b6 | ||
|
ba3db24f30 | ||
|
ed0864d24f | ||
|
a40e74a15d | ||
|
7c9d17f5ca | ||
|
9aded6e556 | ||
|
200a884589 | ||
|
c1162678bb | ||
|
62880f0fd6 | ||
|
400593cf5f | ||
|
070d5b1c6d | ||
|
3f9c8d8535 | ||
|
a87397a214 | ||
|
7a78b2cf20 | ||
|
469ae2805c | ||
|
a98fcfc56f | ||
|
62af35c42b | ||
|
aa40eaf4cc | ||
|
bd1d7b7a81 | ||
|
5df44e0c4f | ||
|
60b565273e | ||
|
135c8cb508 | ||
|
11f42f8c16 | ||
|
1f7fc2274a | ||
|
708b10c194 | ||
|
798b10ec69 | ||
|
7537759a60 | ||
|
42252e6442 | ||
|
0b56427019 | ||
|
cc130d52e9 | ||
|
171733870d | ||
|
5a2e554a6c | ||
|
f2b7f14dcd | ||
|
3bb4af5e13 | ||
|
9bc46229c6 | ||
|
1769783dc7 | ||
|
93042b9283 | ||
|
7571e58ce5 | ||
|
b0261234a6 | ||
|
9bbaab7871 | ||
|
a36fd6dd57 | ||
|
8d5485fbeb | ||
|
7b9012671a | ||
|
5984c27960 | ||
|
8b6008e196 | ||
|
1c501aceef | ||
|
f922870785 | ||
|
8502cd9285 | ||
|
7e7d4f7f16 | ||
|
d4a84c337e | ||
|
0125dbe31a | ||
|
f48359f630 | ||
|
7345e2e255 | ||
|
1941db3c12 | ||
|
e900e9d614 | ||
|
dddb784469 | ||
|
057436edc0 | ||
|
9e30e6a14c | ||
|
e35251ac74 | ||
|
5e37b7f083 | ||
|
1313f46ba4 | ||
|
2f961e15e3 | ||
|
039066663a | ||
|
d2432df114 | ||
|
aef846996f | ||
|
dbacce0c56 | ||
|
2f163a3814 | ||
|
8902e796da | ||
|
9178afea64 | ||
|
3ea29de2ee | ||
|
88927f546a | ||
|
f24a18a848 | ||
|
4eeecc9378 | ||
|
4172194479 | ||
|
ab1498aa67 | ||
|
13f2ddc77e | ||
|
493a9b0359 | ||
|
1be0742f67 | ||
|
90a820e1f9 | ||
|
593644af53 | ||
|
7c33ff59cd | ||
|
32dca9096c | ||
|
977ab53b68 | ||
|
d19a00d97d | ||
|
23636b22fa | ||
|
504c61c779 | ||
|
b5b94a3526 | ||
|
df3c90109b | ||
|
6b5508d52b | ||
|
c3487251b6 | ||
|
3806b99b8b | ||
|
70ba204168 | ||
|
b79c12901b | ||
|
c0c3153cd8 | ||
|
1c50ca065d | ||
|
fb0a2a6801 |
253
.github/gles.patch
vendored
Normal file
|
@ -0,0 +1,253 @@
|
|||
diff --git a/src/host_shaders/opengl_fragment_shader.frag b/src/host_shaders/opengl_fragment_shader.frag
|
||||
index 9f07df0b..96a35afa 100644
|
||||
--- a/src/host_shaders/opengl_fragment_shader.frag
|
||||
+++ b/src/host_shaders/opengl_fragment_shader.frag
|
||||
@@ -1,4 +1,5 @@
|
||||
-#version 410 core
|
||||
+#version 300 es
|
||||
+precision mediump float;
|
||||
|
||||
in vec4 v_quaternion;
|
||||
in vec4 v_colour;
|
||||
@@ -41,8 +42,8 @@ vec3 normal;
|
||||
const uint samplerEnabledBitfields[2] = uint[2](0x7170e645u, 0x7f013fefu);
|
||||
|
||||
bool isSamplerEnabled(uint environment_id, uint lut_id) {
|
||||
- uint index = 7 * environment_id + lut_id;
|
||||
- uint arrayIndex = (index >> 5);
|
||||
+ uint index = 7u * environment_id + lut_id;
|
||||
+ uint arrayIndex = (index >> 5u);
|
||||
return (samplerEnabledBitfields[arrayIndex] & (1u << (index & 31u))) != 0u;
|
||||
}
|
||||
|
||||
@@ -166,11 +167,17 @@ float lutLookup(uint lut, int index) {
|
||||
return texelFetch(u_tex_luts, ivec2(index, int(lut)), 0).r;
|
||||
}
|
||||
|
||||
+// some gles versions have bitfieldExtractCompat and complain if you redefine it, some don't and compile error, using this instead
|
||||
+uint bitfieldExtractCompat(uint val, int off, int size) {
|
||||
+ uint mask = uint((1 << size) - 1);
|
||||
+ return uint(val >> off) & mask;
|
||||
+}
|
||||
+
|
||||
vec3 regToColor(uint reg) {
|
||||
// Normalization scale to convert from [0...255] to [0.0...1.0]
|
||||
const float scale = 1.0 / 255.0;
|
||||
|
||||
- return scale * vec3(float(bitfieldExtract(reg, 20, 8)), float(bitfieldExtract(reg, 10, 8)), float(bitfieldExtract(reg, 00, 8)));
|
||||
+ return scale * vec3(float(bitfieldExtractCompat(reg, 20, 8)), float(bitfieldExtractCompat(reg, 10, 8)), float(bitfieldExtractCompat(reg, 00, 8)));
|
||||
}
|
||||
|
||||
// Convert an arbitrary-width floating point literal to an f32
|
||||
@@ -201,7 +208,7 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
|
||||
// These are the spotlight attenuation LUTs
|
||||
bit_in_config1 = 8 + int(light_id & 7u);
|
||||
lut_index = 8u + light_id;
|
||||
- } else if (lut_id <= 6) {
|
||||
+ } else if (lut_id <= 6u) {
|
||||
bit_in_config1 = 16 + int(lut_id);
|
||||
lut_index = lut_id;
|
||||
} else {
|
||||
@@ -210,16 +217,16 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
|
||||
|
||||
bool current_sampler_enabled = isSamplerEnabled(environment_id, lut_id); // 7 luts per environment
|
||||
|
||||
- if (!current_sampler_enabled || (bitfieldExtract(GPUREG_LIGHTING_CONFIG1, bit_in_config1, 1) != 0u)) {
|
||||
+ if (!current_sampler_enabled || (bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG1, bit_in_config1, 1) != 0u)) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
- uint scale_id = bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_SCALE, int(lut_id) << 2, 3);
|
||||
+ uint scale_id = bitfieldExtractCompat(GPUREG_LIGHTING_LUTINPUT_SCALE, int(lut_id) << 2, 3);
|
||||
float scale = float(1u << scale_id);
|
||||
if (scale_id >= 6u) scale /= 256.0;
|
||||
|
||||
float delta = 1.0;
|
||||
- uint input_id = bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_SELECT, int(lut_id) << 2, 3);
|
||||
+ uint input_id = bitfieldExtractCompat(GPUREG_LIGHTING_LUTINPUT_SELECT, int(lut_id) << 2, 3);
|
||||
switch (input_id) {
|
||||
case 0u: {
|
||||
delta = dot(normal, normalize(half_vector));
|
||||
@@ -243,9 +250,9 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
|
||||
|
||||
// Sign extend them. Normally bitfieldExtract would do that but it's missing on some versions
|
||||
// of GLSL so we do it manually
|
||||
- int se_x = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 0, 13);
|
||||
- int se_y = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_LOW, 16, 13);
|
||||
- int se_z = bitfieldExtract(GPUREG_LIGHTi_SPOTDIR_HIGH, 0, 13);
|
||||
+ int se_x = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_LOW), 0, 13));
|
||||
+ int se_y = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_LOW), 16, 13));
|
||||
+ int se_z = int(bitfieldExtractCompat(uint(GPUREG_LIGHTi_SPOTDIR_HIGH), 0, 13));
|
||||
|
||||
if ((se_x & 0x1000) == 0x1000) se_x |= 0xffffe000;
|
||||
if ((se_y & 0x1000) == 0x1000) se_y |= 0xffffe000;
|
||||
@@ -272,9 +279,9 @@ float lightLutLookup(uint environment_id, uint lut_id, uint light_id, vec3 light
|
||||
}
|
||||
|
||||
// 0 = enabled
|
||||
- if (bitfieldExtract(GPUREG_LIGHTING_LUTINPUT_ABS, 1 + (int(lut_id) << 2), 1) == 0u) {
|
||||
+ if (bitfieldExtractCompat(GPUREG_LIGHTING_LUTINPUT_ABS, 1 + (int(lut_id) << 2), 1) == 0u) {
|
||||
// Two sided diffuse
|
||||
- if (bitfieldExtract(GPUREG_LIGHTi_CONFIG, 1, 1) == 0u) {
|
||||
+ if (bitfieldExtractCompat(GPUREG_LIGHTi_CONFIG, 1, 1) == 0u) {
|
||||
delta = max(delta, 0.0);
|
||||
} else {
|
||||
delta = abs(delta);
|
||||
@@ -298,7 +305,7 @@ vec3 rotateVec3ByQuaternion(vec3 v, vec4 q) {
|
||||
// Implements the following algorthm: https://mathb.in/26766
|
||||
void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
|
||||
uint GPUREG_LIGHTING_ENABLE = readPicaReg(0x008Fu);
|
||||
- if (bitfieldExtract(GPUREG_LIGHTING_ENABLE, 0, 1) == 0u) {
|
||||
+ if (bitfieldExtractCompat(GPUREG_LIGHTING_ENABLE, 0, 1) == 0u) {
|
||||
primary_color = secondary_color = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
@@ -315,7 +322,7 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
|
||||
GPUREG_LIGHTING_LUTINPUT_ABS = readPicaReg(0x01D0u);
|
||||
GPUREG_LIGHTING_LUTINPUT_SELECT = readPicaReg(0x01D1u);
|
||||
|
||||
- uint bump_mode = bitfieldExtract(GPUREG_LIGHTING_CONFIG0, 28, 2);
|
||||
+ uint bump_mode = bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG0, 28, 2);
|
||||
|
||||
// Bump mode is ignored for now because it breaks some games ie. Toad Treasure Tracker
|
||||
switch (bump_mode) {
|
||||
@@ -328,15 +335,15 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
|
||||
vec4 diffuse_sum = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
vec4 specular_sum = vec4(0.0, 0.0, 0.0, 1.0);
|
||||
|
||||
- uint environment_id = bitfieldExtract(GPUREG_LIGHTING_CONFIG0, 4, 4);
|
||||
- bool clamp_highlights = bitfieldExtract(GPUREG_LIGHTING_CONFIG0, 27, 1) == 1u;
|
||||
+ uint environment_id = bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG0, 4, 4);
|
||||
+ bool clamp_highlights = bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG0, 27, 1) == 1u;
|
||||
|
||||
uint light_id;
|
||||
vec3 light_vector;
|
||||
vec3 half_vector;
|
||||
|
||||
for (uint i = 0u; i < GPUREG_LIGHTING_NUM_LIGHTS; i++) {
|
||||
- light_id = bitfieldExtract(GPUREG_LIGHTING_LIGHT_PERMUTATION, int(i) << 2, 3);
|
||||
+ light_id = bitfieldExtractCompat(GPUREG_LIGHTING_LIGHT_PERMUTATION, int(i) << 2, 3);
|
||||
|
||||
uint GPUREG_LIGHTi_SPECULAR0 = readPicaReg(0x0140u + (light_id << 4u));
|
||||
uint GPUREG_LIGHTi_SPECULAR1 = readPicaReg(0x0141u + (light_id << 4u));
|
||||
@@ -348,12 +355,12 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
|
||||
|
||||
float light_distance;
|
||||
vec3 light_position = vec3(
|
||||
- decodeFP(bitfieldExtract(GPUREG_LIGHTi_VECTOR_LOW, 0, 16), 5u, 10u), decodeFP(bitfieldExtract(GPUREG_LIGHTi_VECTOR_LOW, 16, 16), 5u, 10u),
|
||||
- decodeFP(bitfieldExtract(GPUREG_LIGHTi_VECTOR_HIGH, 0, 16), 5u, 10u)
|
||||
+ decodeFP(bitfieldExtractCompat(GPUREG_LIGHTi_VECTOR_LOW, 0, 16), 5u, 10u), decodeFP(bitfieldExtractCompat(GPUREG_LIGHTi_VECTOR_LOW, 16, 16), 5u, 10u),
|
||||
+ decodeFP(bitfieldExtractCompat(GPUREG_LIGHTi_VECTOR_HIGH, 0, 16), 5u, 10u)
|
||||
);
|
||||
|
||||
// Positional Light
|
||||
- if (bitfieldExtract(GPUREG_LIGHTi_CONFIG, 0, 1) == 0u) {
|
||||
+ if (bitfieldExtractCompat(GPUREG_LIGHTi_CONFIG, 0, 1) == 0u) {
|
||||
light_vector = light_position + v_view;
|
||||
}
|
||||
|
||||
@@ -369,23 +376,23 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
|
||||
float NdotL = dot(normal, light_vector); // N dot Li
|
||||
|
||||
// Two sided diffuse
|
||||
- if (bitfieldExtract(GPUREG_LIGHTi_CONFIG, 1, 1) == 0u)
|
||||
+ if (bitfieldExtractCompat(GPUREG_LIGHTi_CONFIG, 1, 1) == 0u)
|
||||
NdotL = max(0.0, NdotL);
|
||||
else
|
||||
NdotL = abs(NdotL);
|
||||
|
||||
float geometric_factor;
|
||||
- bool use_geo_0 = bitfieldExtract(GPUREG_LIGHTi_CONFIG, 2, 1) == 1u;
|
||||
- bool use_geo_1 = bitfieldExtract(GPUREG_LIGHTi_CONFIG, 3, 1) == 1u;
|
||||
+ bool use_geo_0 = bitfieldExtractCompat(GPUREG_LIGHTi_CONFIG, 2, 1) == 1u;
|
||||
+ bool use_geo_1 = bitfieldExtractCompat(GPUREG_LIGHTi_CONFIG, 3, 1) == 1u;
|
||||
if (use_geo_0 || use_geo_1) {
|
||||
geometric_factor = dot(half_vector, half_vector);
|
||||
geometric_factor = geometric_factor == 0.0 ? 0.0 : min(NdotL / geometric_factor, 1.0);
|
||||
}
|
||||
|
||||
float distance_attenuation = 1.0;
|
||||
- if (bitfieldExtract(GPUREG_LIGHTING_CONFIG1, 24 + int(light_id), 1) == 0u) {
|
||||
- uint GPUREG_LIGHTi_ATTENUATION_BIAS = bitfieldExtract(readPicaReg(0x014Au + (light_id << 4u)), 0, 20);
|
||||
- uint GPUREG_LIGHTi_ATTENUATION_SCALE = bitfieldExtract(readPicaReg(0x014Bu + (light_id << 4u)), 0, 20);
|
||||
+ if (bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG1, 24 + int(light_id), 1) == 0u) {
|
||||
+ uint GPUREG_LIGHTi_ATTENUATION_BIAS = bitfieldExtractCompat(readPicaReg(0x014Au + (light_id << 4u)), 0, 20);
|
||||
+ uint GPUREG_LIGHTi_ATTENUATION_SCALE = bitfieldExtractCompat(readPicaReg(0x014Bu + (light_id << 4u)), 0, 20);
|
||||
|
||||
float distance_attenuation_bias = decodeFP(GPUREG_LIGHTi_ATTENUATION_BIAS, 7u, 12u);
|
||||
float distance_attenuation_scale = decodeFP(GPUREG_LIGHTi_ATTENUATION_SCALE, 7u, 12u);
|
||||
@@ -430,8 +437,8 @@ void calcLighting(out vec4 primary_color, out vec4 secondary_color) {
|
||||
specular_sum.rgb += light_factor * clamp_factor * (specular0 + specular1);
|
||||
}
|
||||
|
||||
- uint fresnel_output1 = bitfieldExtract(GPUREG_LIGHTING_CONFIG0, 2, 1);
|
||||
- uint fresnel_output2 = bitfieldExtract(GPUREG_LIGHTING_CONFIG0, 3, 1);
|
||||
+ uint fresnel_output1 = bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG0, 2, 1);
|
||||
+ uint fresnel_output2 = bitfieldExtractCompat(GPUREG_LIGHTING_CONFIG0, 3, 1);
|
||||
// Uses parameters from the last light as Fresnel is only applied to the last light
|
||||
float fresnel_factor;
|
||||
|
||||
diff --git a/src/host_shaders/opengl_vertex_shader.vert b/src/host_shaders/opengl_vertex_shader.vert
|
||||
index 057f9a88..dc735ced 100644
|
||||
--- a/src/host_shaders/opengl_vertex_shader.vert
|
||||
+++ b/src/host_shaders/opengl_vertex_shader.vert
|
||||
@@ -1,4 +1,6 @@
|
||||
-#version 410 core
|
||||
+#version 300 es
|
||||
+precision mediump float;
|
||||
+precision mediump int;
|
||||
|
||||
layout(location = 0) in vec4 a_coords;
|
||||
layout(location = 1) in vec4 a_quaternion;
|
||||
@@ -18,7 +20,7 @@ out vec2 v_texcoord2;
|
||||
flat out vec4 v_textureEnvColor[6];
|
||||
flat out vec4 v_textureEnvBufferColor;
|
||||
|
||||
-out float gl_ClipDistance[2];
|
||||
+// out float gl_ClipDistance[2];
|
||||
|
||||
// TEV uniforms
|
||||
uniform uint u_textureEnvColor[6];
|
||||
@@ -81,8 +83,8 @@ void main() {
|
||||
);
|
||||
|
||||
// There's also another, always-on clipping plane based on vertex z
|
||||
- gl_ClipDistance[0] = -a_coords.z;
|
||||
- gl_ClipDistance[1] = dot(clipData, a_coords);
|
||||
+ // gl_ClipDistance[0] = -a_coords.z;
|
||||
+ // gl_ClipDistance[1] = dot(clipData, a_coords);
|
||||
|
||||
v_quaternion = a_quaternion;
|
||||
}
|
||||
diff --git a/third_party/opengl/opengl.hpp b/third_party/opengl/opengl.hpp
|
||||
index 607815fa..cbfcc096 100644
|
||||
--- a/third_party/opengl/opengl.hpp
|
||||
+++ b/third_party/opengl/opengl.hpp
|
||||
@@ -602,22 +602,22 @@ namespace OpenGL {
|
||||
static void disableScissor() { glDisable(GL_SCISSOR_TEST); }
|
||||
static void enableBlend() { glEnable(GL_BLEND); }
|
||||
static void disableBlend() { glDisable(GL_BLEND); }
|
||||
- static void enableLogicOp() { glEnable(GL_COLOR_LOGIC_OP); }
|
||||
- static void disableLogicOp() { glDisable(GL_COLOR_LOGIC_OP); }
|
||||
+ static void enableLogicOp() { /* glEnable(GL_COLOR_LOGIC_OP); */ }
|
||||
+ static void disableLogicOp() { /* glDisable(GL_COLOR_LOGIC_OP); */ }
|
||||
static void enableDepth() { glEnable(GL_DEPTH_TEST); }
|
||||
static void disableDepth() { glDisable(GL_DEPTH_TEST); }
|
||||
static void enableStencil() { glEnable(GL_STENCIL_TEST); }
|
||||
static void disableStencil() { glDisable(GL_STENCIL_TEST); }
|
||||
|
||||
- static void enableClipPlane(GLuint index) { glEnable(GL_CLIP_DISTANCE0 + index); }
|
||||
- static void disableClipPlane(GLuint index) { glDisable(GL_CLIP_DISTANCE0 + index); }
|
||||
+ static void enableClipPlane(GLuint index) { /* glEnable(GL_CLIP_DISTANCE0 + index); */ }
|
||||
+ static void disableClipPlane(GLuint index) { /* glDisable(GL_CLIP_DISTANCE0 + index); */ }
|
||||
|
||||
static void setDepthFunc(DepthFunc func) { glDepthFunc(static_cast<GLenum>(func)); }
|
||||
static void setColourMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { glColorMask(r, g, b, a); }
|
||||
static void setDepthMask(GLboolean mask) { glDepthMask(mask); }
|
||||
|
||||
// TODO: Add a proper enum for this
|
||||
- static void setLogicOp(GLenum op) { glLogicOp(op); }
|
||||
+ static void setLogicOp(GLenum op) { /* glLogicOp(op); */ }
|
||||
|
||||
enum Primitives {
|
||||
Triangle = GL_TRIANGLES,
|
20
.github/mac-bundle-qt.sh
vendored
|
@ -5,16 +5,16 @@ PATH="$PATH:/usr/libexec"
|
|||
|
||||
# Construct the app iconset.
|
||||
mkdir alber.iconset
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 16x16 alber.iconset/icon_16x16.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 32x32 alber.iconset/icon_16x16@2x.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 32x32 alber.iconset/icon_32x32.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 64x64 alber.iconset/icon_32x32@2x.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 128x128 alber.iconset/icon_128x128.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 256x256 alber.iconset/icon_128x128@2x.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 256x256 alber.iconset/icon_256x256.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 512x512 alber.iconset/icon_256x256@2x.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 512x512 alber.iconset/icon_512x512.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 1024x1024 alber.iconset/icon_512x512@2x.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 16x16 alber.iconset/icon_16x16.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 32x32 alber.iconset/icon_16x16@2x.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 32x32 alber.iconset/icon_32x32.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 64x64 alber.iconset/icon_32x32@2x.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 128x128 alber.iconset/icon_128x128.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 256x256 alber.iconset/icon_128x128@2x.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 256x256 alber.iconset/icon_256x256.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 512x512 alber.iconset/icon_256x256@2x.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 512x512 alber.iconset/icon_512x512.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 1024x1024 alber.iconset/icon_512x512@2x.png
|
||||
iconutil --convert icns alber.iconset
|
||||
|
||||
# Set up the .app directory
|
||||
|
|
20
.github/mac-bundle.sh
vendored
|
@ -5,16 +5,16 @@ PATH="$PATH:/usr/libexec"
|
|||
|
||||
# Construct the app iconset.
|
||||
mkdir alber.iconset
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 16x16 alber.iconset/icon_16x16.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 32x32 alber.iconset/icon_16x16@2x.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 32x32 alber.iconset/icon_32x32.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 64x64 alber.iconset/icon_32x32@2x.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 128x128 alber.iconset/icon_128x128.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 256x256 alber.iconset/icon_128x128@2x.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 256x256 alber.iconset/icon_256x256.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 512x512 alber.iconset/icon_256x256@2x.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 512x512 alber.iconset/icon_512x512.png
|
||||
convert docs/img/alber-icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 1024x1024 alber.iconset/icon_512x512@2x.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 16x16 alber.iconset/icon_16x16.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 32x32 alber.iconset/icon_16x16@2x.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 32x32 alber.iconset/icon_32x32.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 64x64 alber.iconset/icon_32x32@2x.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 128x128 alber.iconset/icon_128x128.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 256x256 alber.iconset/icon_128x128@2x.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 256x256 alber.iconset/icon_256x256.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 512x512 alber.iconset/icon_256x256@2x.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 72 -resize 512x512 alber.iconset/icon_512x512.png
|
||||
convert docs/img/mac_icon.ico -alpha on -background none -units PixelsPerInch -density 144 -resize 1024x1024 alber.iconset/icon_512x512@2x.png
|
||||
iconutil --convert icns alber.iconset
|
||||
|
||||
# Set up the .app directory
|
||||
|
|
145
.github/workflows/Android_Build.yml
vendored
Normal file
|
@ -0,0 +1,145 @@
|
|||
name: Android Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
x64:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
build_type:
|
||||
- release
|
||||
|
||||
steps:
|
||||
- name: Set BUILD_TYPE variable
|
||||
run: echo "BUILD_TYPE=${{ matrix.build_type }}" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup CCache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
|
||||
- name: Set up gradle caches
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-pandroid-x86_64-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pandroid-x86_64-
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=x86_64 -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
# Apply patch for GLES compatibility
|
||||
git apply ./.github/gles.patch
|
||||
# Build the project with CMake
|
||||
cmake --build ${{github.workspace}}/build --config ${{ env.BUILD_TYPE }}
|
||||
|
||||
# Strip the generated library and move it to the appropriate location
|
||||
${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --strip-unneeded ./build/libAlber.so
|
||||
mv ./build/libAlber.so ./src/pandroid/app/src/main/jniLibs/x86_64/
|
||||
|
||||
# Build the Android app with Gradle
|
||||
cd src/pandroid
|
||||
./gradlew assemble${{ env.BUILD_TYPE }}
|
||||
cd ../..
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Android APKs (x86-64)
|
||||
path: |
|
||||
./src/pandroid/app/build/outputs/apk/${{ env.BUILD_TYPE }}/app-${{ env.BUILD_TYPE }}.apk
|
||||
|
||||
arm64:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
build_type:
|
||||
- release
|
||||
|
||||
steps:
|
||||
- name: Set BUILD_TYPE variable
|
||||
run: echo "BUILD_TYPE=${{ matrix.build_type }}" >> $GITHUB_ENV
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup CCache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
|
||||
- name: Set up gradle caches
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-pandroid-arm64-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pandroid-arm64-
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DBUILD_HYDRA_CORE=1 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DENABLE_VULKAN=0 -DENABLE_USER_BUILD=ON -DCMAKE_CXX_FLAGS="-march=armv8-a+crypto" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
# Apply patch for GLES compatibility
|
||||
git apply ./.github/gles.patch
|
||||
# Build the project with CMake
|
||||
cmake --build ${{github.workspace}}/build --config ${{ env.BUILD_TYPE }}
|
||||
|
||||
# Strip the generated library and move it to the appropriate location
|
||||
${ANDROID_NDK_ROOT}/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --strip-unneeded ./build/libAlber.so
|
||||
mv ./build/libAlber.so ./src/pandroid/app/src/main/jniLibs/arm64-v8a/
|
||||
|
||||
# Build the Android app with Gradle
|
||||
cd src/pandroid
|
||||
./gradlew assemble${{ env.BUILD_TYPE }}
|
||||
ls -R app/build/outputs
|
||||
cd ../..
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Android APKs (arm64)
|
||||
path: |
|
||||
./src/pandroid/app/build/outputs/apk/${{ env.BUILD_TYPE }}/app-${{ env.BUILD_TYPE }}.apk
|
18
.github/workflows/HTTP_Build.yml
vendored
|
@ -16,24 +16,30 @@ jobs:
|
|||
# well on Windows or Mac. You can convert this to a matrix build if you need
|
||||
# cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Install newer Clang
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x ./llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, Glslang
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_USER_BUILD=ON -DENABLE_HTTP_SERVER=ON
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DENABLE_USER_BUILD=ON -DENABLE_HTTP_SERVER=ON
|
||||
|
||||
- name: Build
|
||||
# Build your program with the given configuration
|
||||
|
|
215
.github/workflows/Hydra_Build.yml
vendored
Normal file
|
@ -0,0 +1,215 @@
|
|||
name: Hydra Core Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
Windows:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_HYDRA_CORE=ON
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload Hydra core
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Windows Hydra core
|
||||
path: '${{github.workspace}}/build/${{ env.BUILD_TYPE }}/Alber.dll'
|
||||
|
||||
- name: Configure CMake (Again)
|
||||
run: |
|
||||
rm -r -fo ${{github.workspace}}/build
|
||||
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_LIBRETRO_CORE=ON
|
||||
|
||||
- name: Build (Again)
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload Libretro core
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Windows Libretro core
|
||||
path: |
|
||||
${{github.workspace}}/build/${{ env.BUILD_TYPE }}/panda3ds_libretro.dll
|
||||
${{github.workspace}}/docs/libretro/panda3ds_libretro.info
|
||||
|
||||
MacOS:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_HYDRA_CORE=ON -DCMAKE_OSX_ARCHITECTURE=x86_64
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload core
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MacOS Hydra core
|
||||
path: '${{github.workspace}}/build/libAlber.dylib'
|
||||
|
||||
- name: Configure CMake (Again)
|
||||
run: |
|
||||
rm -rf ${{github.workspace}}/build
|
||||
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DBUILD_LIBRETRO_CORE=ON -DCMAKE_OSX_ARCHITECTURE=x86_64
|
||||
|
||||
- name: Build (Again)
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} && ls -R ${{github.workspace}}/build
|
||||
|
||||
- name: Upload Libretro core
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MacOS Libretro core
|
||||
path: |
|
||||
${{github.workspace}}/build/panda3ds_libretro.dylib
|
||||
${{github.workspace}}/docs/libretro/panda3ds_libretro.info
|
||||
|
||||
Linux:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Install misc packages
|
||||
run: |
|
||||
sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
|
||||
|
||||
- name: Install newer Clang
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x ./llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DENABLE_USER_BUILD=ON -DBUILD_HYDRA_CORE=ON
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload core
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Linux Hydra core
|
||||
path: '${{github.workspace}}/build/libAlber.so'
|
||||
|
||||
- name: Configure CMake (Again)
|
||||
run: |
|
||||
rm -rf ${{github.workspace}}/build
|
||||
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DENABLE_USER_BUILD=ON -DBUILD_LIBRETRO_CORE=ON
|
||||
|
||||
- name: Build (Again)
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload Libretro core
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Linux Libretro core
|
||||
path: |
|
||||
${{github.workspace}}/build/panda3ds_libretro.so
|
||||
${{github.workspace}}/docs/libretro/panda3ds_libretro.info
|
||||
|
||||
Android-x64:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Install misc packages
|
||||
run: |
|
||||
sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake -DANDROID_ABI=x86_64 -DBUILD_HYDRA_CORE=1 -DENABLE_VULKAN=0
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload core
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Android Hydra core
|
||||
path: '${{github.workspace}}/build/libAlber.so'
|
||||
|
||||
ARM-Libretro:
|
||||
runs-on: ubuntu-24.04-arm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Install misc packages
|
||||
run: |
|
||||
sudo apt-get update && sudo apt install libx11-dev libxext-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
|
||||
|
||||
- name: Install newer Clang
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x ./llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Configure CMake
|
||||
run: |
|
||||
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DENABLE_USER_BUILD=ON -DBUILD_LIBRETRO_CORE=ON -DENABLE_VULKAN=OFF -DCRYPTOPP_OPT_DISABLE_ASM=ON
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload Libretro core
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Linux arm64 Libretro core
|
||||
path: |
|
||||
${{github.workspace}}/build/panda3ds_libretro.so
|
||||
${{github.workspace}}/docs/libretro/panda3ds_libretro.info
|
30
.github/workflows/Linux_AppImage_Build.yml
vendored
|
@ -16,41 +16,33 @@ jobs:
|
|||
# well on Windows or Mac. You can convert this to a matrix build if you need
|
||||
# cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Install misc packages
|
||||
run: sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev libfuse2
|
||||
run: sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev
|
||||
|
||||
- name: Install newer Clang
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x ./llvm.sh
|
||||
sudo ./llvm.sh 16
|
||||
|
||||
- name: Install newer CMake
|
||||
run: |
|
||||
sudo curl -s https://apt.kitware.com/keys/kitware-archive-latest.asc | gpg --dearmor | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 42D5A192B819C5DA
|
||||
sudo add-apt-repository -y 'deb https://apt.kitware.com/ubuntu/ focal main'
|
||||
sudo apt-get update
|
||||
sudo apt-get install cmake
|
||||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
run: |
|
||||
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
||||
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-focal.list http://packages.lunarg.com/vulkan/lunarg-vulkan-focal.list
|
||||
sudo apt update
|
||||
sudo apt install vulkan-sdk
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-16 -DCMAKE_CXX_COMPILER=clang++-16 -DENABLE_USER_BUILD=ON
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DENABLE_USER_BUILD=ON
|
||||
|
||||
- name: Build
|
||||
# Build your program with the given configuration
|
||||
|
@ -60,7 +52,7 @@ jobs:
|
|||
run: ./.github/linux-appimage.sh
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Linux executable
|
||||
path: './Alber-x86_64.AppImage'
|
||||
|
|
22
.github/workflows/Linux_Build.yml
vendored
|
@ -16,34 +16,40 @@ jobs:
|
|||
# well on Windows or Mac. You can convert this to a matrix build if you need
|
||||
# cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Install misc packages
|
||||
run: sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev
|
||||
run: sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libwayland-dev
|
||||
|
||||
- name: Install newer Clang
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x ./llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, Glslang
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DENABLE_USER_BUILD=ON
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DENABLE_USER_BUILD=ON
|
||||
|
||||
- name: Build
|
||||
# Build your program with the given configuration
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Linux executable
|
||||
path: './build/Alber'
|
||||
|
|
62
.github/workflows/MacOS_Build.yml
vendored
|
@ -12,28 +12,29 @@ env:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally
|
||||
# well on Windows or Mac. You can convert this to a matrix build if you need
|
||||
# cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
|
||||
name: MacOS-${{ matrix.arch }}
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, Glslang
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }}
|
||||
|
||||
- name: Build
|
||||
# Build your program with the given configuration
|
||||
|
@ -49,10 +50,45 @@ jobs:
|
|||
run: codesign --force -s - -vvvv Alber.app
|
||||
|
||||
- name: Zip it up
|
||||
run: zip -r Alber Alber.app
|
||||
run: zip -r Alber-${{ matrix.arch }} Alber.app
|
||||
|
||||
- name: Upload MacOS App
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MacOS Alber App Bundle
|
||||
path: 'Alber.zip'
|
||||
name: MacOS Alber App Bundle (${{ matrix.arch }})
|
||||
path: Alber-${{ matrix.arch }}.zip
|
||||
|
||||
MacOS-Universal:
|
||||
name: MacOS-Universal
|
||||
needs: [build]
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Download x86_64
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: MacOS Alber App Bundle (x86_64)
|
||||
path: x86_64
|
||||
- name: Download ARM64
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: MacOS Alber App Bundle (arm64)
|
||||
path: arm64
|
||||
- name: Combine app bundles
|
||||
shell: bash
|
||||
run: |
|
||||
set -x
|
||||
unzip x86_64/*.zip -d x86_64
|
||||
unzip arm64/*.zip -d arm64
|
||||
lipo {x86_64,arm64}/Alber.app/Contents/MacOS/Alber -create -output Alber
|
||||
cp -v -a arm64/Alber.app Alber.app
|
||||
cp -v Alber Alber.app/Contents/MacOS/Alber
|
||||
# Mix in x86_64 files that do not appear in the ARM64 build (e.g. libvulkan)
|
||||
cp -v -R -n x86_64/Alber.app/* Alber.app/ || true
|
||||
codesign --force -s - -vvvv Alber.app
|
||||
zip -r -y Alber-universal.zip Alber.app
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MacOS Alber App Bundle (universal)
|
||||
path: Alber-universal.zip
|
||||
|
|
113
.github/workflows/Qt_Build.yml
vendored
|
@ -15,7 +15,7 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
|
@ -26,11 +26,11 @@ jobs:
|
|||
version: 6.2.0
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, Glslang
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DENABLE_QT_GUI=ON
|
||||
|
@ -45,35 +45,46 @@ jobs:
|
|||
windeployqt --dir upload upload/Alber.exe
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Windows executable
|
||||
path: upload
|
||||
|
||||
MacOS:
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [x86_64, arm64]
|
||||
|
||||
name: MacOS-${{ matrix.arch }}
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, Glslang
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Install bundle dependencies
|
||||
run: |
|
||||
brew install dylibbundler imagemagick
|
||||
|
||||
- name: Install qt
|
||||
run: brew install qt && which macdeployqt
|
||||
- name: Install Qt
|
||||
uses: jurplel/install-qt-action@v3
|
||||
with:
|
||||
aqtversion: '==3.1.*'
|
||||
version: '6.8.1'
|
||||
host: 'mac'
|
||||
target: 'desktop'
|
||||
arch: 'clang_64'
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DENABLE_QT_GUI=ON
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_USER_BUILD=ON -DENABLE_QT_GUI=ON -DCMAKE_OSX_ARCHITECTURES=${{ matrix.arch }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
@ -87,52 +98,78 @@ jobs:
|
|||
run: codesign --force -s - -vvvv Alber.app
|
||||
|
||||
- name: Zip it up
|
||||
run: zip -r Alber Alber.app
|
||||
run: zip -r Alber-${{ matrix.arch }} Alber.app
|
||||
|
||||
- name: Upload MacOS App
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MacOS Alber App Bundle
|
||||
path: 'Alber.zip'
|
||||
name: MacOS Alber App Bundle (${{ matrix.arch }})
|
||||
path: Alber-${{ matrix.arch }}.zip
|
||||
|
||||
Linux:
|
||||
runs-on: ubuntu-20.04
|
||||
MacOS-Universal:
|
||||
name: MacOS-Universal
|
||||
needs: [MacOS]
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Download x86_64
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: MacOS Alber App Bundle (x86_64)
|
||||
path: x86_64
|
||||
- name: Download ARM64
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: MacOS Alber App Bundle (arm64)
|
||||
path: arm64
|
||||
- name: Combine app bundles
|
||||
shell: bash
|
||||
run: |
|
||||
set -x
|
||||
unzip x86_64/*.zip -d x86_64
|
||||
unzip arm64/*.zip -d arm64
|
||||
lipo {x86_64,arm64}/Alber.app/Contents/MacOS/Alber -create -output Alber
|
||||
cp -v -a arm64/Alber.app Alber.app
|
||||
cp -v Alber Alber.app/Contents/MacOS/Alber
|
||||
# Mix in x86_64 files that do not appear in the ARM64 build (e.g. libvulkan)
|
||||
cp -v -R -n x86_64/Alber.app/* Alber.app/ || true
|
||||
codesign --force -s - -vvvv Alber.app
|
||||
zip -r -y Alber-universal.zip Alber.app
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: MacOS Alber App Bundle (universal)
|
||||
path: Alber-universal.zip
|
||||
|
||||
Linux:
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Install misc packages
|
||||
run: |
|
||||
sudo apt-get update && sudo apt install libx11-dev libgl1-mesa-glx mesa-common-dev libfuse2 libwayland-dev
|
||||
sudo add-apt-repository -y ppa:savoury1/qt-6-2
|
||||
sudo apt-get update && sudo apt install libx11-dev libgl1 libglx-mesa0 mesa-common-dev libfuse2 libwayland-dev libgl1-mesa-dev
|
||||
sudo apt update
|
||||
sudo apt install qt6-base-dev qt6-base-private-dev
|
||||
sudo apt install qt6-base-dev qt6-base-private-dev qt6-tools-dev
|
||||
|
||||
- name: Install newer Clang
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x ./llvm.sh
|
||||
sudo ./llvm.sh 16
|
||||
|
||||
- name: Install newer CMake
|
||||
run: |
|
||||
sudo curl -s https://apt.kitware.com/keys/kitware-archive-latest.asc | gpg --dearmor | tee /usr/share/keyrings/kitware-archive-keyring.gpg >/dev/null
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 42D5A192B819C5DA
|
||||
sudo add-apt-repository -y 'deb https://apt.kitware.com/ubuntu/ focal main'
|
||||
sudo apt-get update
|
||||
sudo apt-get install cmake
|
||||
sudo ./llvm.sh 17
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
run: |
|
||||
wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
|
||||
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-focal.list http://packages.lunarg.com/vulkan/lunarg-vulkan-focal.list
|
||||
sudo apt update
|
||||
sudo apt install vulkan-sdk
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-16 -DCMAKE_CXX_COMPILER=clang++-16 -DENABLE_USER_BUILD=ON -DENABLE_QT_GUI=ON
|
||||
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_C_COMPILER=clang-17 -DCMAKE_CXX_COMPILER=clang++-17 -DENABLE_USER_BUILD=ON -DENABLE_QT_GUI=ON
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
@ -143,7 +180,7 @@ jobs:
|
|||
./.github/linux-appimage-qt.sh
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Linux executable
|
||||
path: './Alber-x86_64.AppImage'
|
||||
|
|
12
.github/workflows/Windows_Build.yml
vendored
|
@ -19,16 +19,16 @@ jobs:
|
|||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@v1.2.0
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: latest
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, Glslang
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Configure CMake
|
||||
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
|
||||
|
@ -40,7 +40,7 @@ jobs:
|
|||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Upload executable
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Windows executable
|
||||
path: './build/Release/Alber.exe'
|
||||
path: './build/${{ env.BUILD_TYPE }}/Alber.exe'
|
||||
|
|
39
.github/workflows/iOS_Build.yml
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
name: iOS Simulator Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
# The CMake configure and build commands are platform agnostic and should work equally
|
||||
# well on Windows or Mac. You can convert this to a matrix build if you need
|
||||
# cross-platform coverage.
|
||||
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Fetch submodules
|
||||
run: git submodule update --init --recursive
|
||||
|
||||
- name: Update Xcode
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest
|
||||
|
||||
- name: Setup Vulkan SDK
|
||||
uses: humbletim/setup-vulkan-sdk@main
|
||||
with:
|
||||
vulkan-query-version: 1.3.296.0
|
||||
vulkan-use-cache: true
|
||||
vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang
|
||||
|
||||
- name: Build core and frontend
|
||||
run: cd src/pandios && ./build.sh
|
10
.gitignore
vendored
|
@ -19,7 +19,7 @@ build/
|
|||
|
||||
.vs/
|
||||
.vscode/*.log
|
||||
|
||||
.cache/
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
|
@ -58,9 +58,15 @@ fb.bat
|
|||
*.3ds
|
||||
*.3dsx
|
||||
*.app
|
||||
*.cia
|
||||
*.cci
|
||||
*.cxi
|
||||
*.elf
|
||||
*.smdh
|
||||
|
||||
config.toml
|
||||
# Compiled Metal shader files
|
||||
*.ir
|
||||
*.metallib
|
||||
|
||||
config.toml
|
||||
CMakeSettings.json
|
||||
|
|
131
.gitlab-ci.yml
Normal file
|
@ -0,0 +1,131 @@
|
|||
# DESCRIPTION: GitLab CI/CD for libRetro (NOT FOR GitLab-proper)
|
||||
|
||||
##############################################################################
|
||||
################################# BOILERPLATE ################################
|
||||
##############################################################################
|
||||
|
||||
# Core definitions
|
||||
.core-defs:
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CORENAME: panda3ds
|
||||
BASE_CORE_ARGS: -DBUILD_LIBRETRO_CORE=ON -DENABLE_USER_BUILD=ON -DENABLE_VULKAN=OFF -DENABLE_LUAJIT=OFF -DENABLE_DISCORD_RPC=OFF -DENABLE_METAL=OFF
|
||||
CORE_ARGS: ${BASE_CORE_ARGS}
|
||||
|
||||
# Inclusion templates, required for the build to work
|
||||
|
||||
include:
|
||||
################################## DESKTOPS ################################
|
||||
# Linux
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/linux-cmake.yml'
|
||||
|
||||
# Windows
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/windows-cmake-mingw.yml'
|
||||
|
||||
# MacOS
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: 'osx-cmake-x86.yml'
|
||||
|
||||
# MacOS
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: 'osx-cmake-arm64.yml'
|
||||
|
||||
################################## CELLULAR ################################
|
||||
# Android
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/android-cmake.yml'
|
||||
|
||||
# iOS
|
||||
- project: 'libretro-infrastructure/ci-templates'
|
||||
file: '/ios-cmake.yml'
|
||||
|
||||
# Stages for building
|
||||
stages:
|
||||
- build-prepare
|
||||
- build-static
|
||||
- build-shared
|
||||
|
||||
##############################################################################
|
||||
#################################### STAGES ##################################
|
||||
##############################################################################
|
||||
#
|
||||
################################### DESKTOPS #################################
|
||||
# Linux 64-bit
|
||||
libretro-build-linux-x64:
|
||||
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-amd64-ubuntu:latest
|
||||
before_script:
|
||||
- export NUMPROC=$(($(nproc)/5))
|
||||
- sudo apt-get update -qy
|
||||
- sudo apt-get install -qy software-properties-common
|
||||
- sudo add-apt-repository -y ppa:savoury1/build-tools
|
||||
- sudo add-apt-repository -y ppa:savoury1/gcc-defaults-12
|
||||
- sudo apt-get update -qy
|
||||
- sudo apt-get install -qy cmake gcc-12 g++-12
|
||||
variables:
|
||||
CC: /usr/bin/gcc-12
|
||||
CXX: /usr/bin/g++-12
|
||||
extends:
|
||||
- .libretro-linux-cmake-x86_64
|
||||
- .core-defs
|
||||
|
||||
# Windows 64-bit
|
||||
libretro-build-windows-x64:
|
||||
extends:
|
||||
- .libretro-windows-cmake-x86_64
|
||||
- .core-defs
|
||||
|
||||
# MacOS 64-bit
|
||||
libretro-build-osx-x64:
|
||||
tags:
|
||||
- mac-apple-silicon
|
||||
variables:
|
||||
CORE_ARGS: ${BASE_CORE_ARGS} -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCRYPTOPP_AMD64=1
|
||||
extends:
|
||||
- .libretro-osx-cmake-x86
|
||||
- .core-defs
|
||||
|
||||
# MacOS arm 64-bit
|
||||
libretro-build-osx-arm64:
|
||||
tags:
|
||||
- mac-apple-silicon
|
||||
extends:
|
||||
- .libretro-osx-cmake-arm64
|
||||
- .core-defs
|
||||
|
||||
################################### CELLULAR #################################
|
||||
# Android ARMv7a
|
||||
#android-armeabi-v7a:
|
||||
# extends:
|
||||
# - .libretro-android-cmake-armeabi-v7a
|
||||
# - .core-defs
|
||||
|
||||
# Android ARMv8a
|
||||
# android-arm64-v8a:
|
||||
# extends:
|
||||
# - .libretro-android-cmake-arm64-v8a
|
||||
# - .core-defs
|
||||
|
||||
# Android 64-bit x86
|
||||
# android-x86_64:
|
||||
# extends:
|
||||
# - .libretro-android-cmake-x86_64
|
||||
# - .core-defs
|
||||
|
||||
# Android 32-bit x86
|
||||
# android-x86:
|
||||
# extends:
|
||||
# - .libretro-android-cmake-x86
|
||||
# - .core-defs
|
||||
|
||||
# iOS
|
||||
# libretro-build-ios-arm64:
|
||||
# extends:
|
||||
# - .libretro-ios-cmake-arm64
|
||||
# - .core-defs
|
||||
# variables:
|
||||
# CORE_ARGS: -DBUILD_LIBRETRO_CORE=ON -DBUILD_PLAY=OFF -DENABLE_AMAZON_S3=off -DBUILD_TESTS=OFF -DCMAKE_TOOLCHAIN_FILE=deps/Dependencies/cmake-ios/ios.cmake -DTARGET_IOS=ON
|
||||
# LIBNAME: ${CORENAME}_libretro_ios.dylib
|
||||
|
||||
################################### CONSOLES #################################
|
64
.gitmodules
vendored
|
@ -1,15 +1,6 @@
|
|||
[submodule "third_party/elfio"]
|
||||
path = third_party/elfio
|
||||
url = https://github.com/serge1/ELFIO
|
||||
[submodule "third_party/dynarmic"]
|
||||
path = third_party/dynarmic
|
||||
url = https://github.com/merryhime/dynarmic
|
||||
[submodule "third_party/SDL2"]
|
||||
path = third_party/SDL2
|
||||
url = https://github.com/libsdl-org/SDL
|
||||
[submodule "third_party/boost"]
|
||||
path = third_party/boost
|
||||
url = https://github.com/citra-emu/ext-boost
|
||||
[submodule "third_party/cryptopp/cryptopp"]
|
||||
path = third_party/cryptopp/cryptopp
|
||||
url = https://github.com/weidai11/cryptopp
|
||||
|
@ -40,3 +31,58 @@
|
|||
[submodule "third_party/mio"]
|
||||
path = third_party/mio
|
||||
url = https://github.com/vimpunk/mio
|
||||
[submodule "third_party/hydra_core"]
|
||||
path = third_party/hydra_core
|
||||
url = https://github.com/hydra-emu/core
|
||||
[submodule "third_party/zep"]
|
||||
path = third_party/zep
|
||||
url = https://github.com/Panda3DS-emu/zep
|
||||
[submodule "third_party/luv"]
|
||||
path = third_party/luv
|
||||
url = https://github.com/luvit/luv
|
||||
[submodule "third_party/libuv"]
|
||||
path = third_party/libuv
|
||||
url = https://github.com/libuv/libuv
|
||||
[submodule "third_party/miniaudio"]
|
||||
path = third_party/miniaudio
|
||||
url = https://github.com/mackron/miniaudio
|
||||
[submodule "third_party/teakra"]
|
||||
path = third_party/teakra
|
||||
url = https://github.com/wwylele/teakra
|
||||
[submodule "third_party/boost"]
|
||||
path = third_party/boost
|
||||
url = https://github.com/Panda3DS-emu/ext-boost
|
||||
[submodule "third_party/dynarmic"]
|
||||
path = third_party/dynarmic
|
||||
url = https://github.com/Panda3DS-emu/dynarmic
|
||||
[submodule "third_party/nihstro"]
|
||||
path = third_party/nihstro
|
||||
url = https://github.com/neobrain/nihstro.git
|
||||
[submodule "third_party/Catch2"]
|
||||
path = third_party/Catch2
|
||||
url = https://github.com/catchorg/Catch2.git
|
||||
[submodule "third_party/capstone"]
|
||||
path = third_party/capstone
|
||||
url = https://github.com/capstone-engine/capstone
|
||||
[submodule "third_party/hips"]
|
||||
path = third_party/hips
|
||||
url = https://github.com/wheremyfoodat/Hips
|
||||
[submodule "third_party/metal-cpp"]
|
||||
path = third_party/metal-cpp
|
||||
url = https://github.com/Panda3DS-emu/metal-cpp
|
||||
[submodule "third_party/fmt"]
|
||||
path = third_party/fmt
|
||||
url = https://github.com/fmtlib/fmt
|
||||
[submodule "third_party/fdk-aac"]
|
||||
path = third_party/fdk-aac
|
||||
url = https://github.com/Panda3DS-emu/fdk-aac/
|
||||
[submodule "third_party/cryptoppwin"]
|
||||
path = third_party/cryptoppwin
|
||||
url = https://github.com/shadps4-emu/ext-cryptoppwin
|
||||
[submodule "third_party/oaknut"]
|
||||
path = third_party/oaknut
|
||||
url = https://github.com/panda3ds-emu/oaknut
|
||||
[submodule "third_party/SDL2"]
|
||||
path = third_party/SDL2
|
||||
url = https://github.com/libsdl-org/SDL
|
||||
branch = SDL2
|
||||
|
|
789
CMakeLists.txt
|
@ -1,9 +1,9 @@
|
|||
# We need to be able to use enable_language(OBJC) on Mac, so we need CMake 3.16 vs the 3.10 we use otherwise. Blame Apple.
|
||||
# We need to be able to use enable_language(OBJC) on Mac, so we need CMake 3.16 vs the 3.11 we use otherwise. Blame Apple.
|
||||
if (APPLE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version")
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
else()
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
cmake_minimum_required(VERSION 3.11)
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
@ -19,69 +19,175 @@ endif()
|
|||
|
||||
project(Alber)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
|
||||
if(APPLE)
|
||||
enable_language(OBJC)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-nonliteral -Wno-format-security")
|
||||
endif()
|
||||
# Enable RC support in order to use resource files for application icons
|
||||
if(WIN32)
|
||||
enable_language(RC)
|
||||
set(APP_RESOURCES docs/img/windows_icon.rc)
|
||||
endif()
|
||||
|
||||
option(DISABLE_PANIC_DEV "Make a build with fewer and less intrusive asserts" OFF)
|
||||
if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-format-nonliteral -Wno-format-security -Wno-invalid-offsetof")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-interference-size")
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
set(DEFAULT_OPENGL_PROFILE OpenGLES)
|
||||
else()
|
||||
set(DEFAULT_OPENGL_PROFILE OpenGL)
|
||||
endif()
|
||||
|
||||
option(DISABLE_PANIC_DEV "Make a build with fewer and less intrusive asserts" ON)
|
||||
option(GPU_DEBUG_INFO "Enable additional GPU debugging info" OFF)
|
||||
option(ENABLE_OPENGL "Enable OpenGL rendering backend" ON)
|
||||
option(ENABLE_VULKAN "Enable Vulkan rendering backend" ON)
|
||||
option(ENABLE_METAL "Enable Metal rendering backend (if available)" ON)
|
||||
option(ENABLE_WAYLAND "Enable Wayland support on Linux platforms" ON)
|
||||
option(ENABLE_LTO "Enable link-time optimization" OFF)
|
||||
option(ENABLE_TESTS "Compile unit-tests" OFF)
|
||||
option(ENABLE_USER_BUILD "Make a user-facing build. These builds have various assertions disabled, LTO, and more" OFF)
|
||||
option(ENABLE_HTTP_SERVER "Enable HTTP server. Used for Discord bot support" OFF)
|
||||
option(ENABLE_DISCORD_RPC "Compile with Discord RPC support (disabled by default)" ON)
|
||||
option(ENABLE_LUAJIT "Enable scripting with the Lua programming language" ON)
|
||||
option(ENABLE_QT_GUI "Enable the Qt GUI. If not selected then the emulator uses a minimal SDL-based UI instead" OFF)
|
||||
option(USE_SYSTEM_SDL2 "Use the system's SDL2 package" OFF)
|
||||
option(ENABLE_GIT_VERSIONING "Enables querying git for the emulator version" ON)
|
||||
option(BUILD_HYDRA_CORE "Build a Hydra core" OFF)
|
||||
option(BUILD_LIBRETRO_CORE "Build a Libretro core" OFF)
|
||||
option(ENABLE_RENDERDOC_API "Build with support for Renderdoc's capture API for graphics debugging" ON)
|
||||
option(DISABLE_SSE4 "Build with SSE4 instructions disabled, may reduce performance" OFF)
|
||||
option(USE_LIBRETRO_AUDIO "Enable to use the LR audio device with the LR core. Otherwise our own device is used" OFF)
|
||||
option(IOS_SIMULATOR_BUILD "Compiling for IOS simulator (Set to off if compiling for a real iPhone)" ON)
|
||||
|
||||
# Discord RPC & LuaJIT are currently not supported on iOS
|
||||
if(IOS)
|
||||
set(ENABLE_DISCORD_RPC OFF)
|
||||
set(ENABLE_LUAJIT OFF)
|
||||
endif()
|
||||
|
||||
set(OPENGL_PROFILE ${DEFAULT_OPENGL_PROFILE} CACHE STRING "OpenGL profile to use if OpenGL is enabled. Valid values are 'OpenGL' and 'OpenGLES'.")
|
||||
set_property(CACHE OPENGL_PROFILE PROPERTY STRINGS OpenGL OpenGLES)
|
||||
|
||||
if(ENABLE_OPENGL AND (OPENGL_PROFILE STREQUAL "OpenGLES"))
|
||||
message(STATUS "Building with OpenGLES support")
|
||||
add_compile_definitions(USING_GLES)
|
||||
endif()
|
||||
|
||||
if(BUILD_HYDRA_CORE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif()
|
||||
|
||||
if(BUILD_LIBRETRO_CORE)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
add_compile_definitions(__LIBRETRO__)
|
||||
|
||||
if(USE_LIBRETRO_AUDIO)
|
||||
add_compile_definitions(USE_LIBRETRO_AUDIO_DEVICE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC" AND ENABLE_USER_BUILD)
|
||||
# Disable stack buffer overflow checks in user builds
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /GS-")
|
||||
endif()
|
||||
|
||||
# Generate versioning files
|
||||
find_package(Git)
|
||||
set(PANDA3DS_VERSION "0.9")
|
||||
|
||||
if(NOT EXISTS ${CMAKE_BINARY_DIR}/include/version.hpp.in)
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/include/version.hpp.in "#define PANDA3DS_VERSION \"\${PANDA3DS_VERSION}\"")
|
||||
endif()
|
||||
|
||||
if(GIT_FOUND AND ENABLE_GIT_VERSIONING)
|
||||
execute_process(
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags --abbrev=0
|
||||
OUTPUT_VARIABLE git_version_tag OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
execute_process(
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} rev-parse --short=7 HEAD
|
||||
OUTPUT_VARIABLE git_version_rev OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(NOT git_version_tag STREQUAL "")
|
||||
set(PANDA3DS_VERSION "${git_version_tag}")
|
||||
execute_process(
|
||||
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} COMMAND ${GIT_EXECUTABLE} describe --tags
|
||||
OUTPUT_VARIABLE git_version_desc OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(git_version_tag STREQUAL git_version_desc)
|
||||
set(git_version_rev "")
|
||||
endif()
|
||||
unset(git_version_desc)
|
||||
endif()
|
||||
if(NOT git_version_rev STREQUAL "")
|
||||
set(PANDA3DS_VERSION "${PANDA3DS_VERSION}.${git_version_rev}")
|
||||
endif()
|
||||
string(REGEX REPLACE "^v" "" PANDA3DS_VERSION "${PANDA3DS_VERSION}")
|
||||
unset(git_version_tag)
|
||||
unset(git_version_rev)
|
||||
endif()
|
||||
configure_file(${CMAKE_BINARY_DIR}/include/version.hpp.in ${CMAKE_BINARY_DIR}/include/version.hpp)
|
||||
include_directories(${CMAKE_BINARY_DIR}/include/)
|
||||
|
||||
add_library(AlberCore STATIC)
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include/)
|
||||
include_directories(${PROJECT_SOURCE_DIR}/include/kernel)
|
||||
include_directories (${FMT_INCLUDE_DIR})
|
||||
include_directories(${FMT_INCLUDE_DIR})
|
||||
include_directories(third_party/boost/)
|
||||
include_directories(third_party/elfio/)
|
||||
include_directories(third_party/hips/include/)
|
||||
include_directories(third_party/imgui/)
|
||||
include_directories(third_party/dynarmic/src)
|
||||
include_directories(third_party/cryptopp/)
|
||||
include_directories(third_party/cityhash/include)
|
||||
include_directories(third_party/result/include)
|
||||
include_directories(third_party/xxhash/include)
|
||||
include_directories(third_party/httplib)
|
||||
include_directories(third_party/stb)
|
||||
include_directories(third_party/opengl)
|
||||
include_directories(third_party/miniaudio)
|
||||
include_directories(third_party/mio/single_include)
|
||||
|
||||
add_compile_definitions(NOMINMAX) # Make windows.h not define min/max macros because third-party deps don't like it
|
||||
add_compile_definitions(WIN32_LEAN_AND_MEAN) # Make windows.h not include literally everything
|
||||
add_compile_definitions(SDL_MAIN_HANDLED)
|
||||
|
||||
if(ENABLE_WAYLAND)
|
||||
add_compile_definitions(WAYLAND_ENABLED)
|
||||
endif()
|
||||
|
||||
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
|
||||
add_subdirectory(third_party/discord-rpc)
|
||||
include_directories(third_party/discord-rpc/include)
|
||||
endif()
|
||||
|
||||
if(ENABLE_QT_GUI)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets)
|
||||
|
||||
# We can't use qt_standard_project_setup since it's Qt 6.3+ and we don't need to set the minimum that high
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
if (NOT ANDROID)
|
||||
if (USE_SYSTEM_SDL2)
|
||||
find_package(SDL2 CONFIG REQUIRED)
|
||||
target_link_libraries(AlberCore PUBLIC SDL2::SDL2)
|
||||
else()
|
||||
set(SDL_STATIC ON CACHE BOOL "" FORCE)
|
||||
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_TEST OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(third_party/SDL2)
|
||||
target_link_libraries(AlberCore PUBLIC SDL2-static)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(SDL_STATIC ON CACHE BOOL "" FORCE)
|
||||
set(SDL_SHARED OFF CACHE BOOL "" FORCE)
|
||||
set(SDL_TEST OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(third_party/SDL2)
|
||||
|
||||
add_subdirectory(third_party/fmt)
|
||||
add_subdirectory(third_party/toml11)
|
||||
include_directories(${SDL2_INCLUDE_DIR})
|
||||
include_directories(third_party/toml11)
|
||||
include_directories(third_party/glm)
|
||||
include_directories(third_party/renderdoc)
|
||||
include_directories(third_party/duckstation)
|
||||
|
||||
add_subdirectory(third_party/cmrc)
|
||||
|
||||
|
@ -93,10 +199,32 @@ add_compile_definitions(BOOST_NO_CXX98_FUNCTION_BASE) # Forbid Boost from using
|
|||
add_library(boost INTERFACE)
|
||||
target_include_directories(boost SYSTEM INTERFACE ${Boost_INCLUDE_DIR})
|
||||
|
||||
set(CRYPTOPP_BUILD_TESTING OFF)
|
||||
add_subdirectory(third_party/cryptopp)
|
||||
if(ANDROID)
|
||||
set(CRYPTOPP_OPT_DISABLE_ASM ON CACHE BOOL "" FORCE)
|
||||
target_sources(AlberCore PRIVATE src/jni_driver.cpp)
|
||||
target_link_libraries(AlberCore PRIVATE EGL log)
|
||||
endif()
|
||||
|
||||
add_subdirectory(third_party/glad)
|
||||
|
||||
# Cryptopp doesn't support compiling under clang-cl, so we have to include it as a prebuilt MSVC static library
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC)
|
||||
add_subdirectory(third_party/cryptoppwin)
|
||||
include_directories(third_party/cryptoppwin/include)
|
||||
target_link_libraries(AlberCore PRIVATE cryptoppwin)
|
||||
|
||||
# Also silence some of clang-cl's more... intrusive warnings
|
||||
set(WARNING_FLAGS "/W1 -Wno-unused-function -Wno-unused-but-set-variable -Wno-reorder-ctor")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${WARNING_FLAGS}")
|
||||
else()
|
||||
set(CRYPTOPP_BUILD_TESTING OFF)
|
||||
|
||||
add_subdirectory(third_party/cryptopp)
|
||||
include_directories(third_party/cryptopp)
|
||||
target_link_libraries(AlberCore PRIVATE cryptopp)
|
||||
endif()
|
||||
|
||||
if(ENABLE_LUAJIT)
|
||||
add_subdirectory(third_party/LuaJIT luajit)
|
||||
include_directories(third_party/LuaJIT/src ${CMAKE_BINARY_DIR}/luajit)
|
||||
|
@ -107,25 +235,73 @@ if(ENABLE_LUAJIT)
|
|||
target_compile_definitions(minilua PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||
target_compile_definitions(buildvm PRIVATE _CRT_SECURE_NO_WARNINGS)
|
||||
endif()
|
||||
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_LUA=1")
|
||||
target_link_libraries(AlberCore PRIVATE libluajit)
|
||||
endif()
|
||||
|
||||
# Check for x64
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86-64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
|
||||
# Detect target architecture
|
||||
if (NOT APPLE OR "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "")
|
||||
# Normal target detection
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86-64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
|
||||
set(HOST_X64 TRUE)
|
||||
else()
|
||||
set(HOST_X64 FALSE)
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
set(HOST_ARM64 TRUE)
|
||||
else()
|
||||
set(HOST_ARM64 FALSE)
|
||||
endif()
|
||||
else()
|
||||
# Apple target detection
|
||||
if("x86_64" IN_LIST CMAKE_OSX_ARCHITECTURES)
|
||||
set(HOST_X64 TRUE)
|
||||
else()
|
||||
set(HOST_X64 FALSE)
|
||||
endif()
|
||||
|
||||
if("arm64" IN_LIST CMAKE_OSX_ARCHITECTURES)
|
||||
set(HOST_ARM64 TRUE)
|
||||
else()
|
||||
set(HOST_ARM64 FALSE)
|
||||
endif()
|
||||
|
||||
if (HOST_ARM64 AND HOST_X64)
|
||||
message(FATAL_ERROR "Universal builds not supported like this! Please compile separately and stitch together")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (HOST_X64)
|
||||
add_subdirectory(third_party/xbyak) # Add xbyak submodule for x86 JITs
|
||||
include_directories(third_party/xbyak)
|
||||
add_compile_definitions(PANDA3DS_DYNAPICA_SUPPORTED)
|
||||
add_compile_definitions(PANDA3DS_X64_HOST)
|
||||
else()
|
||||
set(HOST_X64 FALSE)
|
||||
endif()
|
||||
|
||||
# Check for arm64
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
set(HOST_ARM64 TRUE)
|
||||
if (HOST_ARM64)
|
||||
add_subdirectory(third_party/oaknut) # Add Oaknut submodule for arm64 JITs
|
||||
include_directories(third_party/oaknut/include)
|
||||
add_compile_definitions(PANDA3DS_DYNAPICA_SUPPORTED)
|
||||
add_compile_definitions(PANDA3DS_ARM64_HOST)
|
||||
else()
|
||||
set(HOST_ARM64 FALSE)
|
||||
endif()
|
||||
|
||||
# Enable SSE4.1 if it's not explicitly disabled
|
||||
# Annoyingly, we can't easily do this if we're using MSVC cause there's no SSE4.1 flag, only SSE4.1
|
||||
if(NOT MSVC OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND NOT DISABLE_SSE4 AND HOST_X64)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse4.1")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.1")
|
||||
elseif(MSVC AND NOT DISABLE_SSE4)
|
||||
# Tell our SIMD code to use SSE4.1 by defining the relevant macros.
|
||||
# Clang defines these macros, MSVC does not.
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D__SSE3__ /D__SSE4_1__")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /D__SSE3__ /D__SSE4_1__")
|
||||
endif()
|
||||
|
||||
if(ENABLE_RENDERDOC_API)
|
||||
find_package(RenderDoc 1.6.0 MODULE REQUIRED)
|
||||
add_compile_definitions(PANDA3DS_ENABLE_RENDERDOC)
|
||||
endif()
|
||||
|
||||
if(HOST_X64 OR HOST_ARM64)
|
||||
|
@ -138,11 +314,21 @@ else()
|
|||
message(FATAL_ERROR "Currently unsupported CPU architecture")
|
||||
endif()
|
||||
|
||||
add_subdirectory(third_party/teakra EXCLUDE_FROM_ALL)
|
||||
add_subdirectory(third_party/fdk-aac)
|
||||
|
||||
set(CAPSTONE_ARCHITECTURE_DEFAULT OFF)
|
||||
set(CAPSTONE_ARM_SUPPORT ON)
|
||||
set(CAPSTONE_BUILD_MACOS_THIN ON)
|
||||
add_subdirectory(third_party/capstone)
|
||||
include_directories(third_party/capstone/include)
|
||||
|
||||
set(SOURCE_FILES src/emulator.cpp src/io_file.cpp src/config.cpp
|
||||
src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
|
||||
src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
|
||||
src/http_server.cpp src/stb_image_write.c src/core/cheats.cpp src/core/action_replay.cpp
|
||||
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp
|
||||
src/core/CPU/cpu_dynarmic.cpp src/core/CPU/dynarmic_cycles.cpp
|
||||
src/core/memory.cpp src/renderer.cpp src/core/renderer_null/renderer_null.cpp
|
||||
src/http_server.cpp src/stb_image_write.c src/core/cheats.cpp src/core/action_replay.cpp
|
||||
src/discord_rpc.cpp src/lua.cpp src/memory_mapped_file.cpp src/renderdoc.cpp
|
||||
src/frontend_settings.cpp src/miniaudio/miniaudio.cpp
|
||||
)
|
||||
set(CRYPTO_SOURCE_FILES src/core/crypto/aes_engine.cpp)
|
||||
set(KERNEL_SOURCE_FILES src/core/kernel/kernel.cpp src/core/kernel/resource_limits.cpp
|
||||
|
@ -162,36 +348,33 @@ set(SERVICE_SOURCE_FILES src/core/services/service_manager.cpp src/core/services
|
|||
src/core/services/act.cpp src/core/services/nfc.cpp src/core/services/dlp_srvr.cpp
|
||||
src/core/services/ir_user.cpp src/core/services/http.cpp src/core/services/soc.cpp
|
||||
src/core/services/ssl.cpp src/core/services/news_u.cpp src/core/services/amiibo_device.cpp
|
||||
src/core/services/csnd.cpp src/core/services/nwm_uds.cpp
|
||||
src/core/services/csnd.cpp src/core/services/nwm_uds.cpp src/core/services/fonts.cpp
|
||||
src/core/services/ns.cpp
|
||||
)
|
||||
set(PICA_SOURCE_FILES src/core/PICA/gpu.cpp src/core/PICA/regs.cpp src/core/PICA/shader_unit.cpp
|
||||
src/core/PICA/shader_interpreter.cpp src/core/PICA/dynapica/shader_rec.cpp
|
||||
src/core/PICA/dynapica/shader_rec_emitter_x64.cpp src/core/PICA/pica_hash.cpp
|
||||
src/core/PICA/dynapica/shader_rec_emitter_arm64.cpp src/core/PICA/shader_gen_glsl.cpp
|
||||
src/core/PICA/shader_decompiler.cpp src/core/PICA/draw_acceleration.cpp
|
||||
)
|
||||
|
||||
set(LOADER_SOURCE_FILES src/core/loader/elf.cpp src/core/loader/ncsd.cpp src/core/loader/ncch.cpp src/core/loader/3dsx.cpp src/core/loader/lz77.cpp)
|
||||
set(FS_SOURCE_FILES src/core/fs/archive_self_ncch.cpp src/core/fs/archive_save_data.cpp src/core/fs/archive_sdmc.cpp
|
||||
src/core/fs/archive_ext_save_data.cpp src/core/fs/archive_ncch.cpp src/core/fs/romfs.cpp
|
||||
src/core/fs/ivfc.cpp src/core/fs/archive_user_save_data.cpp src/core/fs/archive_system_save_data.cpp
|
||||
src/core/fs/archive_twl_photo.cpp src/core/fs/archive_twl_sound.cpp src/core/fs/archive_card_spi.cpp
|
||||
)
|
||||
|
||||
set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selector.cpp src/core/applets/software_keyboard.cpp src/core/applets/applet_manager.cpp)
|
||||
set(APPLET_SOURCE_FILES src/core/applets/applet.cpp src/core/applets/mii_selector.cpp src/core/applets/software_keyboard.cpp src/core/applets/applet_manager.cpp
|
||||
src/core/applets/error_applet.cpp
|
||||
)
|
||||
set(AUDIO_SOURCE_FILES src/core/audio/dsp_core.cpp src/core/audio/null_core.cpp src/core/audio/teakra_core.cpp
|
||||
src/core/audio/miniaudio_device.cpp src/core/audio/hle_core.cpp src/core/audio/aac_decoder.cpp
|
||||
src/core/audio/audio_interpolation.cpp
|
||||
)
|
||||
set(RENDERER_SW_SOURCE_FILES src/core/renderer_sw/renderer_sw.cpp)
|
||||
|
||||
# Frontend source files
|
||||
if(ENABLE_QT_GUI)
|
||||
set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp)
|
||||
set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp)
|
||||
|
||||
source_group("Source Files\\Qt" FILES ${FRONTEND_SOURCE_FILES})
|
||||
source_group("Header Files\\Qt" FILES ${FRONTEND_HEADER_FILES})
|
||||
include_directories(${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||
else()
|
||||
set(FRONTEND_SOURCE_FILES src/panda_sdl/main.cpp)
|
||||
set(FRONTEND_HEADER_FILES "")
|
||||
endif()
|
||||
|
||||
set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
|
||||
set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp include/input_mappings.hpp
|
||||
include/cpu.hpp include/cpu_dynarmic.hpp include/memory.hpp include/renderer.hpp include/kernel/kernel.hpp
|
||||
include/dynarmic_cp15.hpp include/kernel/resource_limits.hpp include/kernel/kernel_types.hpp
|
||||
include/kernel/config_mem.hpp include/services/service_manager.hpp include/services/apt.hpp
|
||||
|
@ -206,7 +389,7 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
|
|||
include/services/mic.hpp include/services/cecd.hpp include/services/ac.hpp
|
||||
include/services/am.hpp include/services/boss.hpp include/services/frd.hpp include/services/nim.hpp
|
||||
include/fs/archive_ext_save_data.hpp include/fs/archive_ncch.hpp include/services/mcu/mcu_hwc.hpp
|
||||
include/colour.hpp include/services/y2r.hpp include/services/cam.hpp include/services/ssl.hpp
|
||||
include/colour.hpp include/services/y2r.hpp include/services/cam.hpp include/services/ssl.hpp
|
||||
include/services/ldr_ro.hpp include/ipc.hpp include/services/act.hpp include/services/nfc.hpp
|
||||
include/system_models.hpp include/services/dlp_srvr.hpp include/PICA/dynapica/pica_recs.hpp
|
||||
include/PICA/dynapica/x64_regs.hpp include/PICA/dynapica/vertex_loader_rec.hpp include/PICA/dynapica/shader_rec.hpp
|
||||
|
@ -217,17 +400,37 @@ set(HEADER_FILES include/emulator.hpp include/helpers.hpp include/termcolor.hpp
|
|||
include/config.hpp include/services/ir_user.hpp include/http_server.hpp include/cheats.hpp
|
||||
include/action_replay.hpp include/renderer_sw/renderer_sw.hpp include/compiler_builtins.hpp
|
||||
include/fs/romfs.hpp include/fs/ivfc.hpp include/discord_rpc.hpp include/services/http.hpp include/result/result_cfg.hpp
|
||||
include/applets/applet.hpp include/applets/mii_selector.hpp include/math_util.hpp include/services/soc.hpp
|
||||
include/applets/applet.hpp include/applets/mii_selector.hpp include/math_util.hpp include/services/soc.hpp
|
||||
include/services/news_u.hpp include/applets/software_keyboard.hpp include/applets/applet_manager.hpp include/fs/archive_user_save_data.hpp
|
||||
include/services/amiibo_device.hpp include/services/nfc_types.hpp include/swap.hpp include/services/csnd.hpp include/services/nwm_uds.hpp
|
||||
include/fs/archive_system_save_data.hpp include/lua_manager.hpp include/memory_mapped_file.hpp
|
||||
include/fs/archive_system_save_data.hpp include/lua_manager.hpp include/memory_mapped_file.hpp include/hydra_icon.hpp
|
||||
include/PICA/dynapica/shader_rec_emitter_arm64.hpp include/scheduler.hpp include/applets/error_applet.hpp include/PICA/shader_gen.hpp
|
||||
include/audio/dsp_core.hpp include/audio/null_core.hpp include/audio/teakra_core.hpp
|
||||
include/audio/miniaudio_device.hpp include/ring_buffer.hpp include/bitfield.hpp include/audio/dsp_shared_mem.hpp
|
||||
include/audio/hle_core.hpp include/capstone.hpp include/audio/aac.hpp include/PICA/pica_frag_config.hpp
|
||||
include/PICA/pica_frag_uniforms.hpp include/PICA/shader_gen_types.hpp include/PICA/shader_decompiler.hpp
|
||||
include/PICA/pica_vert_config.hpp include/sdl_sensors.hpp include/PICA/draw_acceleration.hpp include/renderdoc.hpp
|
||||
include/align.hpp include/audio/aac_decoder.hpp include/PICA/pica_simd.hpp include/services/fonts.hpp
|
||||
include/audio/audio_interpolation.hpp include/audio/hle_mixer.hpp include/audio/dsp_simd.hpp
|
||||
include/services/dsp_firmware_db.hpp include/frontend_settings.hpp include/fs/archive_twl_photo.hpp
|
||||
include/fs/archive_twl_sound.hpp include/fs/archive_card_spi.hpp include/services/ns.hpp include/audio/audio_device.hpp
|
||||
include/audio/audio_device_interface.hpp include/audio/libretro_audio_device.hpp
|
||||
)
|
||||
|
||||
if(IOS)
|
||||
set(SOURCE_FILES ${SOURCE_FILES} src/miniaudio/miniaudio.m)
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_IOS=1")
|
||||
|
||||
if (IOS_SIMULATOR_BUILD)
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_IOS_SIMULATOR=1")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
cmrc_add_resource_library(
|
||||
resources_console_fonts
|
||||
NAMESPACE ConsoleFonts
|
||||
WHENCE "src/core/services/fonts/"
|
||||
"src/core/services/fonts/CitraSharedFontUSRelocated.bin"
|
||||
resources_console_fonts
|
||||
NAMESPACE ConsoleFonts
|
||||
WHENCE "src/core/services/fonts/"
|
||||
"src/core/services/fonts/SharedFontReplacement.bin"
|
||||
)
|
||||
|
||||
set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
||||
|
@ -240,18 +443,34 @@ set(THIRD_PARTY_SOURCE_FILES third_party/imgui/imgui.cpp
|
|||
third_party/xxhash/xxhash.c
|
||||
)
|
||||
|
||||
if(ENABLE_LUAJIT AND NOT ANDROID)
|
||||
# Build luv and libuv for Lua TCP server usage if we're not on Android
|
||||
include_directories(third_party/luv/src)
|
||||
include_directories(third_party/luv/deps/lua-compat-5.3/c-api)
|
||||
include_directories(third_party/libuv/include)
|
||||
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/luv/src/luv.c)
|
||||
set(LIBUV_BUILD_SHARED OFF)
|
||||
|
||||
add_subdirectory(third_party/libuv)
|
||||
target_link_libraries(AlberCore PRIVATE uv_a)
|
||||
endif()
|
||||
|
||||
set(GL_CONTEXT_SOURCE_FILES "")
|
||||
|
||||
if(ENABLE_QT_GUI)
|
||||
include_directories(third_party/duckstation)
|
||||
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/window_info.cpp third_party/duckstation/gl/context.cpp)
|
||||
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/window_info.cpp third_party/duckstation/gl/context.cpp)
|
||||
|
||||
if(APPLE)
|
||||
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_agl.mm)
|
||||
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_agl.mm)
|
||||
elseif(WIN32)
|
||||
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_wgl.cpp)
|
||||
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_wgl.cpp)
|
||||
else()
|
||||
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/context_egl.cpp third_party/duckstation/gl/context_egl_wayland.cpp
|
||||
third_party/duckstation/gl/context_egl_x11.cpp third_party/duckstation/gl/context_glx.cpp third_party/duckstation/gl/x11_window.cpp)
|
||||
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_egl.cpp third_party/duckstation/gl/context_egl_x11.cpp
|
||||
third_party/duckstation/gl/context_glx.cpp third_party/duckstation/gl/x11_window.cpp)
|
||||
|
||||
if(ENABLE_WAYLAND)
|
||||
set(GL_CONTEXT_SOURCE_FILES ${GL_CONTEXT_SOURCE_FILES} third_party/duckstation/gl/context_egl_wayland.cpp)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
@ -263,70 +482,80 @@ source_group("Source Files\\Core\\Loader" FILES ${LOADER_SOURCE_FILES})
|
|||
source_group("Source Files\\Core\\Services" FILES ${SERVICE_SOURCE_FILES})
|
||||
source_group("Source Files\\Core\\Applets" FILES ${APPLET_SOURCE_FILES})
|
||||
source_group("Source Files\\Core\\PICA" FILES ${PICA_SOURCE_FILES})
|
||||
source_group("Source Files\\Core\\Audio" FILES ${AUDIO_SOURCE_FILES})
|
||||
source_group("Source Files\\Core\\Software Renderer" FILES ${RENDERER_SW_SOURCE_FILES})
|
||||
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES})
|
||||
source_group("Source Files\\Third Party" FILES ${THIRD_PARTY_SOURCE_FILES} ${GL_CONTEXT_SOURCE_FILES})
|
||||
|
||||
set(RENDERER_GL_SOURCE_FILES "") # Empty by default unless we are compiling with the GL renderer
|
||||
set(RENDERER_VK_SOURCE_FILES "") # Empty by default unless we are compiling with the VK renderer
|
||||
|
||||
if(ENABLE_OPENGL)
|
||||
# This may look weird but opengl.hpp is our header even if it's in the third_party folder
|
||||
set(RENDERER_GL_INCLUDE_FILES third_party/opengl/opengl.hpp
|
||||
include/renderer_gl/renderer_gl.hpp include/renderer_gl/textures.hpp
|
||||
include/renderer_gl/surfaces.hpp include/renderer_gl/surface_cache.hpp
|
||||
include/renderer_gl/gl_state.hpp
|
||||
)
|
||||
set(RENDERER_GL_INCLUDE_FILES third_party/opengl/opengl.hpp
|
||||
include/renderer_gl/renderer_gl.hpp include/renderer_gl/textures.hpp
|
||||
include/renderer_gl/surfaces.hpp include/renderer_gl/surface_cache.hpp
|
||||
include/renderer_gl/gl_state.hpp include/renderer_gl/gl_driver.hpp
|
||||
)
|
||||
|
||||
set(RENDERER_GL_SOURCE_FILES src/core/renderer_gl/renderer_gl.cpp
|
||||
set(RENDERER_GL_SOURCE_FILES src/core/renderer_gl/renderer_gl.cpp
|
||||
src/core/renderer_gl/textures.cpp src/core/renderer_gl/etc1.cpp
|
||||
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.frag
|
||||
src/host_shaders/opengl_display.vert src/host_shaders/opengl_vertex_shader.vert
|
||||
src/core/renderer_gl/gl_state.cpp src/host_shaders/opengl_display.vert
|
||||
src/host_shaders/opengl_display.frag src/host_shaders/opengl_es_display.vert
|
||||
src/host_shaders/opengl_es_display.frag src/host_shaders/opengl_vertex_shader.vert
|
||||
src/host_shaders/opengl_fragment_shader.frag
|
||||
)
|
||||
)
|
||||
|
||||
set(THIRD_PARTY_SOURCE_FILES ${THIRD_PARTY_SOURCE_FILES} third_party/duckstation/gl/stream_buffer.cpp)
|
||||
|
||||
set(HEADER_FILES ${HEADER_FILES} ${RENDERER_GL_INCLUDE_FILES})
|
||||
source_group("Source Files\\Core\\OpenGL Renderer" FILES ${RENDERER_GL_SOURCE_FILES})
|
||||
|
||||
cmrc_add_resource_library(
|
||||
resources_renderer_gl
|
||||
NAMESPACE RendererGL
|
||||
WHENCE "src/host_shaders/"
|
||||
"src/host_shaders/opengl_display.frag"
|
||||
"src/host_shaders/opengl_display.vert"
|
||||
"src/host_shaders/opengl_vertex_shader.vert"
|
||||
"src/host_shaders/opengl_fragment_shader.frag"
|
||||
)
|
||||
cmrc_add_resource_library(
|
||||
resources_renderer_gl
|
||||
NAMESPACE RendererGL
|
||||
WHENCE "src/host_shaders/"
|
||||
"src/host_shaders/opengl_display.vert"
|
||||
"src/host_shaders/opengl_display.frag"
|
||||
"src/host_shaders/opengl_es_display.vert"
|
||||
"src/host_shaders/opengl_es_display.frag"
|
||||
"src/host_shaders/opengl_vertex_shader.vert"
|
||||
"src/host_shaders/opengl_fragment_shader.frag"
|
||||
)
|
||||
|
||||
target_sources(AlberCore PRIVATE ${RENDERER_GL_SOURCE_FILES})
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_OPENGL=1")
|
||||
target_link_libraries(AlberCore PRIVATE resources_renderer_gl)
|
||||
endif()
|
||||
|
||||
if(ENABLE_VULKAN)
|
||||
find_package(
|
||||
Vulkan 1.3.206 REQUIRED
|
||||
COMPONENTS glslangValidator
|
||||
)
|
||||
find_package(
|
||||
Vulkan REQUIRED
|
||||
COMPONENTS glslang
|
||||
)
|
||||
|
||||
set(RENDERER_VK_INCLUDE_FILES include/renderer_vk/renderer_vk.hpp
|
||||
include/renderer_vk/vk_api.hpp include/renderer_vk/vk_debug.hpp
|
||||
include/renderer_vk/vk_descriptor_heap.hpp
|
||||
include/renderer_vk/vk_descriptor_update_batch.hpp
|
||||
include/renderer_vk/vk_sampler_cache.hpp
|
||||
include/renderer_vk/vk_memory.hpp include/renderer_vk/vk_pica.hpp
|
||||
)
|
||||
set(RENDERER_VK_INCLUDE_FILES include/renderer_vk/renderer_vk.hpp
|
||||
include/renderer_vk/vk_api.hpp include/renderer_vk/vk_debug.hpp
|
||||
include/renderer_vk/vk_descriptor_heap.hpp
|
||||
include/renderer_vk/vk_descriptor_update_batch.hpp
|
||||
include/renderer_vk/vk_sampler_cache.hpp
|
||||
include/renderer_vk/vk_memory.hpp include/renderer_vk/vk_pica.hpp
|
||||
)
|
||||
|
||||
set(RENDERER_VK_SOURCE_FILES src/core/renderer_vk/renderer_vk.cpp
|
||||
src/core/renderer_vk/vk_api.cpp src/core/renderer_vk/vk_debug.cpp
|
||||
src/core/renderer_vk/vk_descriptor_heap.cpp
|
||||
src/core/renderer_vk/vk_descriptor_update_batch.cpp
|
||||
src/core/renderer_vk/vk_sampler_cache.cpp
|
||||
src/core/renderer_vk/vk_memory.cpp src/core/renderer_vk/vk_pica.cpp
|
||||
)
|
||||
set(RENDERER_VK_SOURCE_FILES src/core/renderer_vk/renderer_vk.cpp
|
||||
src/core/renderer_vk/vk_api.cpp src/core/renderer_vk/vk_debug.cpp
|
||||
src/core/renderer_vk/vk_descriptor_heap.cpp
|
||||
src/core/renderer_vk/vk_descriptor_update_batch.cpp
|
||||
src/core/renderer_vk/vk_sampler_cache.cpp
|
||||
src/core/renderer_vk/vk_memory.cpp src/core/renderer_vk/vk_pica.cpp
|
||||
)
|
||||
|
||||
set(HEADER_FILES ${HEADER_FILES} ${RENDERER_VK_INCLUDE_FILES})
|
||||
source_group("Source Files\\Core\\Vulkan Renderer" FILES ${RENDERER_VK_SOURCE_FILES})
|
||||
|
||||
set(RENDERER_VK_HOST_SHADERS_SOURCE
|
||||
"src/host_shaders/vulkan_display.frag"
|
||||
"src/host_shaders/vulkan_display.vert"
|
||||
)
|
||||
set(RENDERER_VK_HOST_SHADERS_SOURCE
|
||||
"src/host_shaders/vulkan_display.frag"
|
||||
"src/host_shaders/vulkan_display.vert"
|
||||
)
|
||||
|
||||
set(RENDERER_VK_HOST_SHADERS_FLAGS -e main --target-env vulkan1.1)
|
||||
|
||||
|
@ -339,103 +568,295 @@ if(ENABLE_VULKAN)
|
|||
|
||||
# Compile each vulkan shader into an .spv file
|
||||
foreach( HOST_SHADER_SOURCE ${RENDERER_VK_HOST_SHADERS_SOURCE} )
|
||||
get_filename_component( FILE_NAME ${HOST_SHADER_SOURCE} NAME )
|
||||
set( HOST_SHADER_SPIRV "${PROJECT_BINARY_DIR}/host_shaders/${FILE_NAME}.spv" )
|
||||
add_custom_command(
|
||||
OUTPUT ${HOST_SHADER_SPIRV}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_BINARY_DIR}/host_shaders/"
|
||||
COMMAND Vulkan::glslangValidator ${RENDERER_VK_HOST_SHADERS_FLAGS} -V "${PROJECT_SOURCE_DIR}/${HOST_SHADER_SOURCE}" -o ${HOST_SHADER_SPIRV}
|
||||
DEPENDS ${HOST_SHADER_SOURCE}
|
||||
)
|
||||
list( APPEND RENDERER_VK_HOST_SHADERS_SPIRV ${HOST_SHADER_SPIRV} )
|
||||
get_filename_component( FILE_NAME ${HOST_SHADER_SOURCE} NAME )
|
||||
set( HOST_SHADER_SPIRV "${PROJECT_BINARY_DIR}/host_shaders/${FILE_NAME}.spv" )
|
||||
add_custom_command(
|
||||
OUTPUT ${HOST_SHADER_SPIRV}
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_BINARY_DIR}/host_shaders/"
|
||||
COMMAND glslang ${RENDERER_VK_HOST_SHADERS_FLAGS} -V "${PROJECT_SOURCE_DIR}/${HOST_SHADER_SOURCE}" -o ${HOST_SHADER_SPIRV}
|
||||
DEPENDS ${HOST_SHADER_SOURCE}
|
||||
)
|
||||
list( APPEND RENDERER_VK_HOST_SHADERS_SPIRV ${HOST_SHADER_SPIRV} )
|
||||
endforeach()
|
||||
|
||||
cmrc_add_resource_library(
|
||||
resources_renderer_vk
|
||||
NAMESPACE RendererVK
|
||||
WHENCE "${PROJECT_BINARY_DIR}/host_shaders/"
|
||||
${RENDERER_VK_HOST_SHADERS_SPIRV}
|
||||
)
|
||||
cmrc_add_resource_library(
|
||||
resources_renderer_vk
|
||||
NAMESPACE RendererVK
|
||||
WHENCE "${PROJECT_BINARY_DIR}/host_shaders/"
|
||||
${RENDERER_VK_HOST_SHADERS_SPIRV}
|
||||
)
|
||||
|
||||
target_sources(AlberCore PRIVATE ${RENDERER_VK_SOURCE_FILES})
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_VULKAN=1")
|
||||
target_link_libraries(AlberCore PRIVATE Vulkan::Vulkan resources_renderer_vk)
|
||||
endif()
|
||||
|
||||
if(ENABLE_METAL AND APPLE)
|
||||
set(RENDERER_MTL_INCLUDE_FILES include/renderer_mtl/renderer_mtl.hpp
|
||||
include/renderer_mtl/mtl_depth_stencil_cache.hpp
|
||||
include/renderer_mtl/mtl_blit_pipeline_cache.hpp
|
||||
include/renderer_mtl/mtl_draw_pipeline_cache.hpp
|
||||
include/renderer_mtl/mtl_render_target.hpp
|
||||
include/renderer_mtl/mtl_texture.hpp
|
||||
include/renderer_mtl/mtl_vertex_buffer_cache.hpp
|
||||
include/renderer_mtl/mtl_lut_texture.hpp
|
||||
include/renderer_mtl/mtl_command_encoder.hpp
|
||||
include/renderer_mtl/mtl_common.hpp
|
||||
include/renderer_mtl/pica_to_mtl.hpp
|
||||
include/renderer_mtl/objc_helper.hpp
|
||||
include/renderer_mtl/texture_decoder.hpp
|
||||
)
|
||||
|
||||
set(RENDERER_MTL_SOURCE_FILES src/core/renderer_mtl/metal_cpp_impl.cpp
|
||||
src/core/renderer_mtl/renderer_mtl.cpp
|
||||
src/core/renderer_mtl/mtl_texture.cpp
|
||||
src/core/renderer_mtl/mtl_lut_texture.cpp
|
||||
src/core/renderer_mtl/pica_to_mtl.cpp
|
||||
src/core/renderer_mtl/objc_helper.mm
|
||||
src/core/renderer_mtl/texture_decoder.cpp
|
||||
src/host_shaders/metal_shaders.metal
|
||||
src/host_shaders/metal_blit.metal
|
||||
#src/host_shaders/metal_copy_to_lut_texture.metal
|
||||
)
|
||||
|
||||
set(HEADER_FILES ${HEADER_FILES} ${RENDERER_MTL_INCLUDE_FILES})
|
||||
source_group("Source Files\\Core\\Metal Renderer" FILES ${RENDERER_MTL_SOURCE_FILES})
|
||||
|
||||
set(RENDERER_MTL_HOST_SHADERS_SOURCES)
|
||||
function (add_metal_shader SHADER)
|
||||
set(SHADER_SOURCE "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.metal")
|
||||
set(SHADER_IR "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.ir")
|
||||
set(SHADER_METALLIB "${CMAKE_SOURCE_DIR}/src/host_shaders/${SHADER}.metallib")
|
||||
|
||||
# MacOS, iOS and the iOS simulator all use different compilation options for shaders
|
||||
set(MetalSDK "macosx")
|
||||
if(IOS)
|
||||
if (IOS_SIMULATOR_BUILD)
|
||||
set(MetalSDK "iphonesimulator")
|
||||
else()
|
||||
set(MetalSDK "iphoneos")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# TODO: only include sources in debug builds
|
||||
add_custom_command(
|
||||
OUTPUT ${SHADER_IR}
|
||||
COMMAND xcrun -sdk ${MetalSDK} metal -gline-tables-only -frecord-sources -o ${SHADER_IR} -c ${SHADER_SOURCE}
|
||||
DEPENDS ${SHADER_SOURCE}
|
||||
VERBATIM)
|
||||
add_custom_command(
|
||||
OUTPUT ${SHADER_METALLIB}
|
||||
COMMAND xcrun -sdk ${MetalSDK} metallib -o ${SHADER_METALLIB} ${SHADER_IR}
|
||||
DEPENDS ${SHADER_IR}
|
||||
VERBATIM)
|
||||
set(RENDERER_MTL_HOST_SHADERS_SOURCES ${RENDERER_MTL_HOST_SHADERS_SOURCES} ${SHADER_METALLIB})
|
||||
endfunction()
|
||||
|
||||
add_metal_shader(metal_shaders)
|
||||
add_metal_shader(metal_blit)
|
||||
#add_metal_shader(metal_copy_to_lut_texture)
|
||||
|
||||
add_custom_target(
|
||||
compile_msl_shaders
|
||||
DEPENDS ${RENDERER_MTL_HOST_SHADERS_SOURCES}
|
||||
)
|
||||
|
||||
cmrc_add_resource_library(
|
||||
resources_renderer_mtl
|
||||
NAMESPACE RendererMTL
|
||||
WHENCE "src/host_shaders/"
|
||||
"src/host_shaders/metal_shaders.metallib"
|
||||
"src/host_shaders/metal_blit.metallib"
|
||||
#"src/host_shaders/metal_copy_to_lut_texture.metallib"
|
||||
)
|
||||
add_dependencies(resources_renderer_mtl compile_msl_shaders)
|
||||
|
||||
target_sources(AlberCore PRIVATE ${RENDERER_MTL_SOURCE_FILES})
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_METAL=1")
|
||||
target_include_directories(AlberCore PRIVATE third_party/metal-cpp)
|
||||
# TODO: check if all of them are needed
|
||||
target_link_libraries(AlberCore PUBLIC "-framework Metal" "-framework Foundation" "-framework QuartzCore" resources_renderer_mtl)
|
||||
endif()
|
||||
|
||||
source_group("Header Files\\Core" FILES ${HEADER_FILES})
|
||||
set(ALL_SOURCES ${SOURCE_FILES} ${FRONTEND_SOURCE_FILES} ${FS_SOURCE_FILES} ${CRYPTO_SOURCE_FILES} ${KERNEL_SOURCE_FILES}
|
||||
set(ALL_SOURCES ${SOURCE_FILES} ${FS_SOURCE_FILES} ${CRYPTO_SOURCE_FILES} ${KERNEL_SOURCE_FILES}
|
||||
${LOADER_SOURCE_FILES} ${SERVICE_SOURCE_FILES} ${APPLET_SOURCE_FILES} ${RENDERER_SW_SOURCE_FILES} ${PICA_SOURCE_FILES} ${THIRD_PARTY_SOURCE_FILES}
|
||||
${HEADER_FILES} ${FRONTEND_HEADER_FILES})
|
||||
${AUDIO_SOURCE_FILES} ${HEADER_FILES} ${FRONTEND_HEADER_FILES})
|
||||
target_sources(AlberCore PRIVATE ${ALL_SOURCES})
|
||||
|
||||
if(ENABLE_OPENGL)
|
||||
# Add the OpenGL source files to ALL_SOURCES
|
||||
set(ALL_SOURCES ${ALL_SOURCES} ${RENDERER_GL_SOURCE_FILES})
|
||||
endif()
|
||||
|
||||
if(ENABLE_VULKAN)
|
||||
# Add the Vulkan source files to ALL_SOURCES
|
||||
set(ALL_SOURCES ${ALL_SOURCES} ${RENDERER_VK_SOURCE_FILES})
|
||||
endif()
|
||||
|
||||
add_executable(Alber ${ALL_SOURCES})
|
||||
|
||||
if(ENABLE_LTO OR ENABLE_USER_BUILD)
|
||||
set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
endif()
|
||||
|
||||
target_link_libraries(Alber PRIVATE dynarmic SDL2-static cryptopp glad resources_console_fonts)
|
||||
target_link_libraries(AlberCore PRIVATE dynarmic glad resources_console_fonts teakra fdk-aac)
|
||||
target_link_libraries(AlberCore PUBLIC glad capstone fmt::fmt)
|
||||
|
||||
if(ENABLE_DISCORD_RPC AND NOT ANDROID)
|
||||
target_compile_definitions(Alber PUBLIC "PANDA3DS_ENABLE_DISCORD_RPC=1")
|
||||
target_link_libraries(Alber PRIVATE discord-rpc)
|
||||
endif()
|
||||
|
||||
if(ENABLE_LUAJIT)
|
||||
target_compile_definitions(Alber PUBLIC "PANDA3DS_ENABLE_LUA=1")
|
||||
target_link_libraries(Alber PRIVATE libluajit)
|
||||
endif()
|
||||
|
||||
if(ENABLE_OPENGL)
|
||||
target_compile_definitions(Alber PUBLIC "PANDA3DS_ENABLE_OPENGL=1")
|
||||
target_link_libraries(Alber PRIVATE resources_renderer_gl)
|
||||
endif()
|
||||
|
||||
if(ENABLE_VULKAN)
|
||||
target_compile_definitions(Alber PUBLIC "PANDA3DS_ENABLE_VULKAN=1")
|
||||
target_link_libraries(Alber PRIVATE Vulkan::Vulkan resources_renderer_vk)
|
||||
endif()
|
||||
|
||||
if(ENABLE_QT_GUI)
|
||||
target_compile_definitions(Alber PUBLIC "PANDA3DS_FRONTEND_QT=1")
|
||||
target_link_libraries(Alber PRIVATE Qt6::Widgets)
|
||||
|
||||
if(LINUX OR FREEBSD)
|
||||
find_package(X11 REQUIRED)
|
||||
target_link_libraries(Alber PRIVATE ${X11_LIBRARIES})
|
||||
|
||||
if(ENABLE_OPENGL)
|
||||
find_package(OpenGL REQUIRED COMPONENTS OpenGL EGL GLX)
|
||||
target_link_libraries(Alber PRIVATE OpenGL::OpenGL OpenGL::EGL OpenGL::GLX)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
qt_add_resources(Alber "app_images"
|
||||
PREFIX "/"
|
||||
FILES
|
||||
docs/img/rsob_icon.png
|
||||
)
|
||||
else()
|
||||
target_compile_definitions(Alber PUBLIC "PANDA3DS_FRONTEND_SDL=1")
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_ENABLE_DISCORD_RPC=1")
|
||||
target_link_libraries(AlberCore PRIVATE discord-rpc)
|
||||
endif()
|
||||
|
||||
if(GPU_DEBUG_INFO)
|
||||
target_compile_definitions(Alber PRIVATE GPU_DEBUG_INFO=1)
|
||||
target_compile_definitions(AlberCore PRIVATE GPU_DEBUG_INFO=1)
|
||||
endif()
|
||||
|
||||
if(ENABLE_USER_BUILD)
|
||||
target_compile_definitions(Alber PRIVATE PANDA3DS_USER_BUILD=1)
|
||||
target_compile_definitions(AlberCore PRIVATE PANDA3DS_USER_BUILD=1)
|
||||
endif()
|
||||
|
||||
if(ENABLE_USER_BUILD OR DISABLE_PANIC_DEV)
|
||||
target_compile_definitions(Alber PRIVATE PANDA3DS_LIMITED_PANICS=1)
|
||||
target_compile_definitions(AlberCore PRIVATE PANDA3DS_LIMITED_PANICS=1)
|
||||
endif()
|
||||
|
||||
if(ENABLE_HTTP_SERVER)
|
||||
target_compile_definitions(Alber PRIVATE PANDA3DS_ENABLE_HTTP_SERVER=1)
|
||||
target_compile_definitions(AlberCore PRIVATE PANDA3DS_ENABLE_HTTP_SERVER=1)
|
||||
endif()
|
||||
|
||||
# Configure frontend
|
||||
|
||||
if(ENABLE_QT_GUI)
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_FRONTEND_QT=1")
|
||||
else()
|
||||
target_compile_definitions(AlberCore PUBLIC "PANDA3DS_FRONTEND_SDL=1")
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE)
|
||||
add_executable(Alber)
|
||||
|
||||
if(ENABLE_QT_GUI)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets LinguistTools)
|
||||
if(NOT ENABLE_OPENGL)
|
||||
message(FATAL_ERROR "Qt frontend requires OpenGL")
|
||||
endif()
|
||||
|
||||
option(GENERATE_QT_TRANSLATION "Generate Qt translation file" OFF)
|
||||
set(QT_LANGUAGES docs/translations)
|
||||
|
||||
set(FRONTEND_SOURCE_FILES src/panda_qt/main.cpp src/panda_qt/screen.cpp src/panda_qt/main_window.cpp src/panda_qt/about_window.cpp
|
||||
src/panda_qt/config_window.cpp src/panda_qt/zep.cpp src/panda_qt/text_editor.cpp src/panda_qt/cheats_window.cpp src/panda_qt/mappings.cpp
|
||||
src/panda_qt/patch_window.cpp src/panda_qt/elided_label.cpp src/panda_qt/shader_editor.cpp src/panda_qt/translations.cpp
|
||||
)
|
||||
set(FRONTEND_HEADER_FILES include/panda_qt/screen.hpp include/panda_qt/main_window.hpp include/panda_qt/about_window.hpp
|
||||
include/panda_qt/config_window.hpp include/panda_qt/text_editor.hpp include/panda_qt/cheats_window.hpp
|
||||
include/panda_qt/patch_window.hpp include/panda_qt/elided_label.hpp include/panda_qt/shader_editor.hpp
|
||||
)
|
||||
|
||||
source_group("Source Files\\Qt" FILES ${FRONTEND_SOURCE_FILES})
|
||||
source_group("Header Files\\Qt" FILES ${FRONTEND_HEADER_FILES})
|
||||
include_directories(${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||
|
||||
include_directories(third_party/zep/include) # Include zep for text editor usage
|
||||
configure_file(third_party/zep/cmake/config_app.h.cmake ${CMAKE_BINARY_DIR}/zep_config/config_app.h)
|
||||
include_directories(${CMAKE_BINARY_DIR}/zep_config)
|
||||
|
||||
target_compile_definitions(Alber PUBLIC "ZEP_QT=1")
|
||||
target_compile_definitions(Alber PUBLIC "ZEP_FEATURE_CPP_FILE_SYSTEM=1")
|
||||
target_link_libraries(Alber PRIVATE Qt6::Widgets)
|
||||
|
||||
# We can't use qt_standard_project_setup since it's Qt 6.3+ and we don't need to set the minimum that high
|
||||
set_target_properties(Alber PROPERTIES AUTOMOC ON)
|
||||
set_target_properties(Alber PROPERTIES AUTORCC ON)
|
||||
set_target_properties(Alber PROPERTIES AUTOUIC ON)
|
||||
|
||||
if(LINUX OR FREEBSD)
|
||||
find_package(X11 REQUIRED)
|
||||
target_link_libraries(Alber PRIVATE ${X11_LIBRARIES})
|
||||
|
||||
if(ENABLE_OPENGL)
|
||||
find_package(OpenGL REQUIRED COMPONENTS OpenGL EGL GLX)
|
||||
target_link_libraries(Alber PRIVATE OpenGL::OpenGL OpenGL::EGL OpenGL::GLX)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Generates an en.ts file for translations
|
||||
# To update the file, use cmake --build --target Alber_lupdate
|
||||
if(GENERATE_QT_TRANSLATION)
|
||||
find_package(Qt6 REQUIRED COMPONENTS LinguistTools)
|
||||
qt_add_lupdate(Alber TS_FILES ${QT_LANGUAGES}/en.ts
|
||||
SOURCES ${FRONTEND_SOURCE_FILES}
|
||||
INCLUDE_DIRECTORIES ${FRONTEND_HEADER_FILES}
|
||||
NO_GLOBAL_TARGET
|
||||
)
|
||||
endif()
|
||||
|
||||
qt_add_resources(AlberCore "app_images"
|
||||
PREFIX "/"
|
||||
FILES
|
||||
docs/img/rsob_icon.png docs/img/rstarstruck_icon.png docs/img/rpog_icon.png docs/img/rsyn_icon.png
|
||||
docs/img/settings_icon.png docs/img/display_icon.png docs/img/speaker_icon.png
|
||||
docs/img/sparkling_icon.png docs/img/battery_icon.png docs/img/sdcard_icon.png
|
||||
docs/img/rnap_icon.png docs/img/rcow_icon.png docs/img/skyemu_icon.png
|
||||
)
|
||||
|
||||
# Translation files in Qt's .ts format. Will be converted into binary files and embedded into the executable
|
||||
set(TRANSLATIONS_TS docs/translations/en.ts docs/translations/el.ts docs/translations/es.ts docs/translations/pt_br.ts docs/translations/nl.ts
|
||||
docs/translations/sv.ts
|
||||
)
|
||||
|
||||
set_source_files_properties(${TRANSLATIONS_TS} PROPERTIES OUTPUT_LOCATION "${CMAKE_CURRENT_BINARY_DIR}/translations")
|
||||
qt_add_translation(TRANSLATIONS_QM ${TRANSLATIONS_TS})
|
||||
|
||||
set(TRANSLATIONS_QRC ${CMAKE_CURRENT_BINARY_DIR}/translations/translations.qrc)
|
||||
file(WRITE ${TRANSLATIONS_QRC} "<RCC><qresource prefix=\"translations\">\n")
|
||||
foreach (QM ${TRANSLATIONS_QM})
|
||||
message("${QM}")
|
||||
get_filename_component(QM_FILE ${QM} NAME)
|
||||
file(APPEND ${TRANSLATIONS_QRC} "<file>${QM_FILE}</file>\n")
|
||||
endforeach (QM)
|
||||
file(APPEND ${TRANSLATIONS_QRC} "</qresource></RCC>")
|
||||
|
||||
qt_add_resources(TRANSLATIONS ${TRANSLATIONS_QRC})
|
||||
set(APP_RESOURCES ${APP_RESOURCES} ${TRANSLATIONS})
|
||||
else()
|
||||
set(FRONTEND_SOURCE_FILES src/panda_sdl/main.cpp src/panda_sdl/frontend_sdl.cpp src/panda_sdl/mappings.cpp)
|
||||
set(FRONTEND_HEADER_FILES "include/panda_sdl/frontend_sdl.hpp")
|
||||
endif()
|
||||
|
||||
target_link_libraries(Alber PRIVATE AlberCore)
|
||||
target_sources(Alber PRIVATE ${FRONTEND_SOURCE_FILES} ${FRONTEND_HEADER_FILES} ${GL_CONTEXT_SOURCE_FILES} ${APP_RESOURCES})
|
||||
elseif(BUILD_HYDRA_CORE)
|
||||
target_compile_definitions(AlberCore PRIVATE PANDA3DS_HYDRA_CORE=1)
|
||||
include_directories(third_party/hydra_core/include)
|
||||
|
||||
set(SHARED_SOURCE_FILES src/hydra_core.cpp)
|
||||
if(IOS)
|
||||
set(SHARED_SOURCE_FILES ${SHARED_SOURCE_FILES} src/ios_driver.mm)
|
||||
endif()
|
||||
|
||||
add_library(Alber SHARED ${SHARED_SOURCE_FILES})
|
||||
target_link_libraries(Alber PUBLIC AlberCore)
|
||||
elseif(BUILD_LIBRETRO_CORE)
|
||||
include_directories(third_party/libretro/include)
|
||||
add_library(panda3ds_libretro SHARED src/libretro_core.cpp)
|
||||
target_link_libraries(panda3ds_libretro PUBLIC AlberCore)
|
||||
set_target_properties(panda3ds_libretro PROPERTIES PREFIX "")
|
||||
endif()
|
||||
|
||||
if(ENABLE_LTO OR ENABLE_USER_BUILD)
|
||||
if (NOT BUILD_LIBRETRO_CORE)
|
||||
set_target_properties(Alber PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
else()
|
||||
set_target_properties(panda3ds_libretro PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(ENABLE_TESTS)
|
||||
enable_testing()
|
||||
|
||||
find_package(Catch2 3)
|
||||
if(NOT Catch2_FOUND)
|
||||
add_subdirectory(third_party/Catch2)
|
||||
endif()
|
||||
|
||||
add_library(nihstro-headers INTERFACE)
|
||||
target_include_directories(nihstro-headers SYSTEM INTERFACE ./third_party/nihstro/include)
|
||||
|
||||
add_executable(AlberTests
|
||||
tests/shader.cpp
|
||||
)
|
||||
target_link_libraries(
|
||||
AlberTests
|
||||
PRIVATE
|
||||
Catch2::Catch2WithMain
|
||||
AlberCore
|
||||
nihstro-headers
|
||||
)
|
||||
|
||||
add_test(AlberTests AlberTests)
|
||||
endif()
|
||||
|
|
25
cmake/FindRenderDoc.cmake
Normal file
|
@ -0,0 +1,25 @@
|
|||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(RENDERDOC_INCLUDE_DIR third_party/renderdoc)
|
||||
|
||||
if (RENDERDOC_INCLUDE_DIR AND EXISTS "${RENDERDOC_INCLUDE_DIR}/renderdoc_app.h")
|
||||
file(STRINGS "${RENDERDOC_INCLUDE_DIR}/renderdoc_app.h" RENDERDOC_VERSION_LINE REGEX "typedef struct RENDERDOC_API")
|
||||
string(REGEX REPLACE ".*typedef struct RENDERDOC_API_([0-9]+)_([0-9]+)_([0-9]+).*" "\\1.\\2.\\3" RENDERDOC_VERSION "${RENDERDOC_VERSION_LINE}")
|
||||
unset(RENDERDOC_VERSION_LINE)
|
||||
endif()
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(RenderDoc
|
||||
REQUIRED_VARS RENDERDOC_INCLUDE_DIR
|
||||
VERSION_VAR RENDERDOC_VERSION
|
||||
)
|
||||
|
||||
if (RenderDoc_FOUND AND NOT TARGET RenderDoc::API)
|
||||
add_library(RenderDoc::API INTERFACE IMPORTED)
|
||||
set_target_properties(RenderDoc::API PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${RENDERDOC_INCLUDE_DIR}"
|
||||
)
|
||||
endif()
|
||||
|
||||
mark_as_advanced(RENDERDOC_INCLUDE_DIR)
|
BIN
docs/3ds/accelerometer_readings/readings_flat_1.png
Normal file
After Width: | Height: | Size: 148 KiB |
BIN
docs/3ds/accelerometer_readings/readings_flat_2.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
docs/3ds/accelerometer_readings/readings_shaking_1.png
Normal file
After Width: | Height: | Size: 212 KiB |
BIN
docs/3ds/accelerometer_readings/readings_shaking_2.png
Normal file
After Width: | Height: | Size: 65 KiB |
79
docs/3ds/lighting.md
Normal file
|
@ -0,0 +1,79 @@
|
|||
## Info on the lighting implementation
|
||||
|
||||
### Missing shadow attenuation
|
||||
Shadow attenuation samples a texture unit, and that likely needs render to texture for most games so that they can construct
|
||||
their shadow map. As such the colors are not multiplied by the shadow attenuation value, so there's no shadows.
|
||||
|
||||
### Missing bump mapping
|
||||
Bump mapping also samples a texture unit, most likely doesn't need render to texture however may need better texture sampling
|
||||
implementation (such as GPUREG_TEXUNITi_BORDER_COLOR, GPUREG_TEXUNITi_BORDER_PARAM). Bump mapping would work for some things,
|
||||
namely the 3ds-examples bump mapping demo, but would break others such as Toad Treasure Tracker with a naive `texture` implementation.
|
||||
|
||||
Also the CP configuration is missing, because it needs a tangent map implementation. It is currently marked with error_unimpl.
|
||||
|
||||
### samplerEnabledBitfields
|
||||
Holds the enabled state of the lighting samples for various PICA configurations
|
||||
As explained in https://www.3dbrew.org/wiki/GPU/Internal_Registers#GPUREG_LIGHTING_CONFIG0
|
||||
|
||||
```c
|
||||
const bool samplerEnabled[9 * 7] = bool[9 * 7](
|
||||
// D0 D1 SP FR RB RG RR
|
||||
true, false, true, false, false, false, true, // Configuration 0: D0, SP, RR
|
||||
false, false, true, true, false, false, true, // Configuration 1: FR, SP, RR
|
||||
true, true, false, false, false, false, true, // Configuration 2: D0, D1, RR
|
||||
true, true, false, true, false, false, false, // Configuration 3: D0, D1, FR
|
||||
true, true, true, false, true, true, true, // Configuration 4: All except for FR
|
||||
true, false, true, true, true, true, true, // Configuration 5: All except for D1
|
||||
true, true, true, true, false, false, true, // Configuration 6: All except for RB and RG
|
||||
false, false, false, false, false, false, false, // Configuration 7: Unused
|
||||
true, true, true, true, true, true, true // Configuration 8: All
|
||||
);
|
||||
```
|
||||
|
||||
The above has been condensed to two uints for performance reasons.
|
||||
You can confirm they are the same by running the following:
|
||||
```c
|
||||
const uint samplerEnabledBitfields[2] = { 0x7170e645u, 0x7f013fefu };
|
||||
for (int i = 0; i < 9 * 7; i++) {
|
||||
unsigned arrayIndex = (i >> 5);
|
||||
bool b = (samplerEnabledBitfields[arrayIndex] & (1u << (i & 31))) != 0u;
|
||||
if (samplerEnabled[i] == b) {
|
||||
printf("%d: happy\n", i);
|
||||
} else {
|
||||
printf("%d: unhappy\n", i);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### lightLutLookup
|
||||
lut_id is one of these values
|
||||
0 D0
|
||||
1 D1
|
||||
2 SP
|
||||
3 FR
|
||||
4 RB
|
||||
5 RG
|
||||
6 RR
|
||||
|
||||
lut_index on the other hand represents the actual index of the LUT in the texture
|
||||
u_tex_luts has 24 LUTs for lighting and they are used like so:
|
||||
0 D0
|
||||
1 D1
|
||||
2 is missing because SP uses LUTs 8-15
|
||||
3 FR
|
||||
4 RB
|
||||
5 RG
|
||||
6 RR
|
||||
8-15 SP0-7
|
||||
16-23 DA0-7, but this is not handled in this function as the lookup is a bit different
|
||||
|
||||
The light environment configuration controls which LUTs are available for use
|
||||
If a LUT is not available in the selected configuration, its value will always read a constant 1.0 regardless of the enable state in GPUREG_LIGHTING_CONFIG1
|
||||
If RR is enabled but not RG or RB, the output of RR is used for the three components; Red, Green and Blue.
|
||||
|
||||
### Distance attenuation
|
||||
Distance attenuation is computed differently from the other factors, for example
|
||||
it doesn't store its scale in GPUREG_LIGHTING_LUTINPUT_SCALE and it doesn't use
|
||||
GPUREG_LIGHTING_LUTINPUT_SELECT. Instead, it uses the distance from the light to the
|
||||
fragment and the distance attenuation scale and bias to calculate where in the LUT to look up.
|
||||
See: https://www.3dbrew.org/wiki/GPU/Internal_Registers#GPUREG_LIGHTi_ATTENUATION_SCALE
|
BIN
docs/img/KirbyAndroid.png
Normal file
After Width: | Height: | Size: 567 KiB |
BIN
docs/img/battery_icon.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
docs/img/display_icon.png
Normal file
After Width: | Height: | Size: 166 B |
Before Width: | Height: | Size: 2 MiB After Width: | Height: | Size: 2 MiB |
BIN
docs/img/rcow_icon.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
docs/img/rnap_icon.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
docs/img/rpog_icon.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
docs/img/rstarstruck_icon.png
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
docs/img/rsyn_icon.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
docs/img/sdcard_icon.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
docs/img/settings_icon.png
Normal file
After Width: | Height: | Size: 697 B |
BIN
docs/img/skyemu_icon.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
docs/img/sparkling_icon.png
Normal file
After Width: | Height: | Size: 549 B |
BIN
docs/img/speaker_icon.png
Normal file
After Width: | Height: | Size: 507 B |
BIN
docs/img/windows_alt_icon.ico
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
docs/img/windows_icon.ico
Normal file
After Width: | Height: | Size: 54 KiB |
1
docs/img/windows_icon.rc
Normal file
|
@ -0,0 +1 @@
|
|||
AlberIcon ICON "windows_icon.ico"
|
34
docs/libretro/panda3ds_libretro.info
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Software Information
|
||||
display_name = "Nintendo - 3DS (Panda3DS)"
|
||||
authors = "Panda3DS Authors (tm)"
|
||||
supported_extensions = "3ds|3dsx|elf|axf|cci|cxi|app|ncch"
|
||||
corename = "Panda3DS"
|
||||
categories = "Emulator"
|
||||
license = "GPLv3"
|
||||
permissions = ""
|
||||
display_version = "Git"
|
||||
|
||||
# Hardware Information
|
||||
manufacturer = "Nintendo"
|
||||
systemname = "3DS"
|
||||
systemid = "3ds"
|
||||
|
||||
# Libretro Information
|
||||
database = "Nintendo - Nintendo 3DS"
|
||||
supports_no_game = "false"
|
||||
savestate = "true"
|
||||
savestate_features = "basic"
|
||||
cheats = "false"
|
||||
input_descriptors = "true"
|
||||
memory_descriptors = "false"
|
||||
libretro_saves = "true"
|
||||
core_options = "true"
|
||||
core_options_version = "1.0"
|
||||
load_subsystem = "false"
|
||||
hw_render = "true"
|
||||
required_hw_api = "OpenGL Core >= 4.1"
|
||||
needs_fullpath = "true"
|
||||
disk_control = "false"
|
||||
is_experimental = "true"
|
||||
|
||||
description = "Panda3DS !"
|
763
docs/translations/el.ts
Normal file
|
@ -0,0 +1,763 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE TS><TS version="2.1" language="el">
|
||||
<context>
|
||||
<name>AboutWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Σχετικά με το Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
|
||||
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
|
||||
<translation>Τo Panda3DS είναι ένας δωρεάν και open source εξομοιωτής του Nintendo 3DS, για Windows, MacOS και Linux</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
|
||||
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
|
||||
<translation>Επισκεφτείται το panda3ds.com για βοήθεια με το Panda3DS και συνδέσμους στις επίσημες σελίδες υποστήριξης μας.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
|
||||
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEditDialog</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
|
||||
<source>Edit Cheat</source>
|
||||
<translation>Επεξεργασία Κωδικού</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
|
||||
<source>Cheat name</source>
|
||||
<translation>Όνομα κωδικού</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEntryWidget</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
|
||||
<source>Edit</source>
|
||||
<translation>Επεξεργασία</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatsWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
|
||||
<source>Cheats</source>
|
||||
<translation>Κωδικοί</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
|
||||
<source>Add</source>
|
||||
<translation>Προσθήκη</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
|
||||
<source>Remove</source>
|
||||
<translation>Αφαίρεση</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ConfigWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
|
||||
<source>Configuration</source>
|
||||
<translation>Ρυθμίσεις</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
|
||||
<source>Interface Settings</source>
|
||||
<translation>Ρυθμίσεις Διεπαφής</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
|
||||
<source>System</source>
|
||||
<translation>Σύστημα</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
|
||||
<source>Light</source>
|
||||
<translation>Φωτεινό</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
|
||||
<source>Dark</source>
|
||||
<translation>Σκοτεινό</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
|
||||
<source>Greetings Cat</source>
|
||||
<translation>Γεια Σου Γάτα</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
|
||||
<source>Cream</source>
|
||||
<translation>Κρέμα</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
|
||||
<source>Color theme</source>
|
||||
<translation>Χρώματα</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
|
||||
<source>Happy panda</source>
|
||||
<translation>Χαρούμενο Πάντα</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
|
||||
<source>Happy panda (colourful)</source>
|
||||
<translation>Χαρούμενο Πάντα (χρωματιστό)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
|
||||
<source>Sleepy panda</source>
|
||||
<translation>Πάντα που νυστάζει</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
|
||||
<source>Cow panda</source>
|
||||
<translation>Αγελάδα πάντα</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
|
||||
<source>The penguin from SkyEmu</source>
|
||||
<translation>Ο πιγκουίνος από το SkyEmu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
|
||||
<source>Window icon</source>
|
||||
<translation>Εικονίδιο Παραθύρου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
|
||||
<source>Language</source>
|
||||
<translation>Γλώσσα</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
|
||||
<source>Show version on window title</source>
|
||||
<translation>Εμφάνιση έκδοσης στον τίτλο του παραθύρου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber v%1</source>
|
||||
<translation>Αλβέρτος v%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber</source>
|
||||
<translation>Αλβέρτος</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
|
||||
<source>Remember window position</source>
|
||||
<translation>Αποθήκευση θέσης παραθύρου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
|
||||
<source>General Settings</source>
|
||||
<translation>Γενικές Ρυθμίσεις</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
|
||||
<source>Browse...</source>
|
||||
<translation>Περιήγηση</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
|
||||
<source>Select Directory</source>
|
||||
<translation>Επιλογή φακέλου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
|
||||
<source>Default ROMs path</source>
|
||||
<translation>Προεπιλεγμένος φάκελος ROM</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
|
||||
<source>Enable Discord RPC</source>
|
||||
<translation>Ενεργοποίηση Discord RPC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
|
||||
<source>Use portable build</source>
|
||||
<translation>Ενεργοποίηση φορητής εγκατάστασης</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
|
||||
<source>Print version in console output</source>
|
||||
<translation>Εκτύπωση έκδοσης στην κονσόλα</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
|
||||
<source>Graphics Settings</source>
|
||||
<translation>Ρυθμίσεις Γραφικών</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
|
||||
<source>Null</source>
|
||||
<translation>Κανένα</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
|
||||
<source>OpenGL</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
|
||||
<source>Vulkan</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
|
||||
<source>GPU renderer</source>
|
||||
<translation>Πυρήνας GPU</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
|
||||
<source>Enable Renderdoc</source>
|
||||
<translation>Ενεργοποίηση Renderdoc</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
|
||||
<source>Enable shader JIT</source>
|
||||
<translation>Ενεργοποίηση μεταγλωττιστή shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
|
||||
<source>Enable VSync</source>
|
||||
<translation>Ενεργοποίηση VSync</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
|
||||
<source>Use ubershaders (No stutter, maybe slower)</source>
|
||||
<translation>Χρήση ubershaders (Χωρίς stutter, ίσως πιο αργό)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
|
||||
<source>Accurate shader multiplication</source>
|
||||
<translation>Ακριβής πολλαπλασιασμός στα shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
|
||||
<source>Accelerate shaders</source>
|
||||
<translation>Επιτάχυνση shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
|
||||
<source>Force shadergen when rendering lights</source>
|
||||
<translation>Εξαναγκασμός shadergen όταν υπάρχουν φώτα</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
|
||||
<source>Light threshold for forcing shadergen</source>
|
||||
<translation>Αριθμός φωτών για εξαναγκασμό shadergen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
|
||||
<source>Audio Settings</source>
|
||||
<translation>Ρυθμίσεις Ήχου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
|
||||
<source>LLE</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
|
||||
<source>HLE</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
|
||||
<source>DSP emulation</source>
|
||||
<translation>Εξομοίωση DSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
|
||||
<source>Enable audio</source>
|
||||
<translation>Ενεργοποίηση ήχου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
|
||||
<source>Enable AAC audio</source>
|
||||
<translation>Ενεργοποίηση ήχου AAC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
|
||||
<source>Print DSP firmware</source>
|
||||
<translation>Εκτύπωση λογισμικού DSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
|
||||
<source>Mute audio device</source>
|
||||
<translation>Σίγαση συσκευής ήχου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
|
||||
<source>Cubic</source>
|
||||
<translation>Κυβική</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
|
||||
<source>Linear</source>
|
||||
<translation>Γραμμική</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
|
||||
<source>Volume curve</source>
|
||||
<translation>Κλίμακα ήχου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
|
||||
<source>Audio device volume</source>
|
||||
<translation>Ένταση ήχου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
|
||||
<source>Battery Settings</source>
|
||||
<translation>Ρυθμίσεις μπαταρίας</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
|
||||
<source>Battery percentage</source>
|
||||
<translation>Ποσοστό μπαταρίας</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
|
||||
<source>Charger plugged</source>
|
||||
<translation>Φορτιστής</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
|
||||
<source>SD Card Settings</source>
|
||||
<translation>Ρυθμίσης κάρτας SD</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
|
||||
<source>Enable virtual SD card</source>
|
||||
<translation>Ενεργοποίηση εικονικής SD</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
|
||||
<source>Write protect virtual SD card</source>
|
||||
<translation>Προστασία της SD από εγγραφή</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>Interface</source>
|
||||
<translation>Διεπαφή</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>User Interface settings</source>
|
||||
<translation>Ρυθμίσεις διεπαφής</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General</source>
|
||||
<translation>Γενικά</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General emulator settings</source>
|
||||
<translation>Γενικές ρυθμίσεις εξομοιωτή</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics</source>
|
||||
<translation>Γραφικά</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics emulation and output settings</source>
|
||||
<translation>Ρυθμίσεις εξομοίωσης γραφικών</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio</source>
|
||||
<translation>Ήχος</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio emulation and output settings</source>
|
||||
<translation>Ρυθμίσεις εξομοίωσης ήχου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery</source>
|
||||
<translation>Μπαταρία</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery emulation settings</source>
|
||||
<translation>Ρυθμίσεις εξομοίωσης μπαταρίας</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card</source>
|
||||
<translation>Κάρτα SD</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card emulation settings</source>
|
||||
<translation>Ρυθμίσεις εξομοίωσης κάρτας SD</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
|
||||
<source>Language change successful</source>
|
||||
<translation>Επιτυχία αλλαγής γλώσσας</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
|
||||
<source>Restart Panda3DS for the new language to be used.</source>
|
||||
<translation>Επανεκκινήστε το Panda3DS για να εφαρμοστεί η νέα γλώσσα.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
|
||||
<source>Language change failed</source>
|
||||
<translation>Αποτυχία αλλαγής γλώσσας</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
|
||||
<source>The language you selected is not included in Panda3DS. If you're seeing this, someone messed up the language UI code...</source>
|
||||
<translation>Το Panda3DS δεν υποστηρίζει τον γλώσσα που επιλέξατε. Αν το βλέπετε αυτό, κάποιος έκανε λάθος στον κώδικα, κατηγορήστε τον Πάρη...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
|
||||
<source>Alber</source>
|
||||
<translation>Αλβέρτος</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
|
||||
<source>File</source>
|
||||
<translation>Αρχεία</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
|
||||
<source>Emulation</source>
|
||||
<translation>Εξομοίωση</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
|
||||
<source>Tools</source>
|
||||
<translation>Εργαλεία</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
|
||||
<source>About</source>
|
||||
<translation>Σχετικά</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
|
||||
<source>Load game</source>
|
||||
<translation>Φόρτωση παιχνιδιού</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
|
||||
<source>Load Lua script</source>
|
||||
<translation>Φόρτωση αρχείου Lua</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
|
||||
<source>Open Panda3DS folder</source>
|
||||
<translation>Άνοιγμα φακέλου Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
|
||||
<source>Pause</source>
|
||||
<translation>Παύση</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
|
||||
<source>Resume</source>
|
||||
<translation>Συνέχεια</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
|
||||
<source>Reset</source>
|
||||
<translation>Επανέναρξη</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
|
||||
<source>Configure</source>
|
||||
<translation>Ρύθμιση</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
|
||||
<source>Dump RomFS</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
|
||||
<source>Open Lua Editor</source>
|
||||
<translation>Άνοιγμα Lua Editor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
|
||||
<source>Open Cheats Editor</source>
|
||||
<translation>Άνοιγμα Editor κωδικών</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
|
||||
<source>Open Patch Window</source>
|
||||
<translation>Άνοιγμα παραθύρου για patching</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
|
||||
<source>Open Shader Editor</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
|
||||
<source>Dump loaded DSP firmware</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Σχετικά με το Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
|
||||
<source>Select 3DS ROM to load</source>
|
||||
<translation>Επιλέξτε 3DS ROM για να φορτώσετε</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
|
||||
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Select Lua script to load</source>
|
||||
<translation>Επιλέξτε αρχείο Lua για να φορτώσετε</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Lua scripts (*.lua *.txt)</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
|
||||
<source>Select folder to dump RomFS files to</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
|
||||
<source>Invalid format for RomFS dumping</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
|
||||
<source>The currently loaded app is not in a format that supports RomFS</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
|
||||
<source>OK</source>
|
||||
<translation>ΟΚ</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS found</source>
|
||||
<translation>Δεν βρέθηκε RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS partition was found in the loaded app</source>
|
||||
<translation>Δεν βρέθηκε RomFS στην εφαρμογή που έχει φορτωθεί</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>Select file</source>
|
||||
<translation>Επιλέξτε αρχείο</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>DSP firmware file (*.cdc)</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>No DSP firmware loaded</source>
|
||||
<translation>Δεν έχει φορτωθεί DSP firmware</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
|
||||
<source>Failed to open output file</source>
|
||||
<translation>Αποτυχία ανοίγματος του αρχείου εξόδου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
|
||||
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
|
||||
<source>ROM patcher</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
|
||||
<source>Select input file</source>
|
||||
<translation>Επιλογή αρχείου εισόδου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
|
||||
<source>Select</source>
|
||||
<translation>Επιλέξτε</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Select patch file</source>
|
||||
<translation>Επιλογή αρχείου patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
|
||||
<source>Apply patch</source>
|
||||
<translation>Εφαρμογή patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<source>Select file to patch</source>
|
||||
<translation>Επιλέξτε αρχείο να κάνετε patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>All files (*.*)</source>
|
||||
<translation>Όλα τα αρχεία (*.*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Patch files (*.ips *.ups *.bps)</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Paths not provided correctly</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Please provide paths for both the input file and the patch file</source>
|
||||
<translation>Παρακαλούμε διαλέξτε και αρχείο εισόδου και αρχείο patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>Select file</source>
|
||||
<translation>Επιλογή αρχείου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No output path</source>
|
||||
<translation>Δεν επιλέχθηκε φάκελος εξόδου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No path was provided for the output file, no patching was done</source>
|
||||
<translation>Δεν επιλέχθηκε αρχείο εξόδου. Δεν έγινε patching</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown patch format</source>
|
||||
<translation>Άγνωστο είδος patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
|
||||
<translation>Άγνωστο είδος αρχείου patch. Υποστηρίζονται αρχεία IPS, UPS και BPS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Failed to open input files</source>
|
||||
<translation>Αποτυχία ανοίγματος των αρχείων εισόδου</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Make sure they're in a directory Panda3DS has access to</source>
|
||||
<translation>Βεβαιωθείτε ότι είναι σε φάκελο που έχει πρόσβαση το Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Patching Success</source>
|
||||
<translation>Επιτυχής Εφαρμογή Patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Your file was patched successfully.</source>
|
||||
<translation>To αρχείο σας έγινε patch με επιτυχία</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
|
||||
<source>Checksum mismatch</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
|
||||
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
|
||||
<translation>Το patch εφαρμόστηκε με επιτυχία αλλά ανιχνεύτηκε σφάλμα στο checksum. Ενδέχεται τα αρχεία εισόδου η εξόδου να είναι λανθασμένα</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>Patching error</source>
|
||||
<translation>Σφάλμα στο patching</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>An error occured while patching</source>
|
||||
<translation>Προέκυψε σφάλμα στο patching</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow::PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
|
||||
<source>OK</source>
|
||||
<translation>ΟΚ</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ShaderEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
|
||||
<source>Reload shader</source>
|
||||
<translation>Επαναφόρτωση shader</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TextEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
|
||||
<source>Lua Editor</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
|
||||
<source>Load script</source>
|
||||
<translation>Φόρτωση αρχείου</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
766
docs/translations/en.ts
Normal file
|
@ -0,0 +1,766 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1">
|
||||
<context>
|
||||
<name>AboutWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
|
||||
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
|
||||
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
|
||||
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEditDialog</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
|
||||
<source>Edit Cheat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
|
||||
<source>Cheat name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEntryWidget</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
|
||||
<source>Edit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatsWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
|
||||
<source>Cheats</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
|
||||
<source>Add</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
|
||||
<source>Remove</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ConfigWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
|
||||
<source>Configuration</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
|
||||
<source>Interface Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
|
||||
<source>System</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
|
||||
<source>Light</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
|
||||
<source>Dark</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
|
||||
<source>Greetings Cat</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
|
||||
<source>Cream</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
|
||||
<source>Color theme</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
|
||||
<source>Happy panda</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
|
||||
<source>Happy panda (colourful)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
|
||||
<source>Sleepy panda</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
|
||||
<source>Cow panda</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
|
||||
<source>The penguin from SkyEmu</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
|
||||
<source>Window icon</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
|
||||
<source>Language</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
|
||||
<source>Show version on window title</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="15"/>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber v%1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
|
||||
<source>Remember window position</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
|
||||
<source>General Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
|
||||
<source>Browse...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
|
||||
<source>Select Directory</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
|
||||
<source>Default ROMs path</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
|
||||
<source>Enable Discord RPC</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
|
||||
<source>Use portable build</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
|
||||
<source>Print version in console output</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
|
||||
<source>Graphics Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
|
||||
<source>Null</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
|
||||
<source>OpenGL</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
|
||||
<source>Vulkan</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
|
||||
<source>GPU renderer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
|
||||
<source>Enable Renderdoc</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
|
||||
<source>Enable shader JIT</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
|
||||
<source>Enable VSync</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
|
||||
<source>Use ubershaders (No stutter, maybe slower)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
|
||||
<source>Accurate shader multiplication</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
|
||||
<source>Accelerate shaders</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
|
||||
<source>Force shadergen when rendering lights</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
|
||||
<source>Light threshold for forcing shadergen</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
|
||||
<source>Audio Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
|
||||
<source>LLE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
|
||||
<source>HLE</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
|
||||
<source>DSP emulation</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
|
||||
<source>Enable audio</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
|
||||
<source>Enable AAC audio</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
|
||||
<source>Print DSP firmware</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
|
||||
<source>Mute audio device</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
|
||||
<source>Cubic</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
|
||||
<source>Linear</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
|
||||
<source>Volume curve</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
|
||||
<source>Audio device volume</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
|
||||
<source>Battery Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
|
||||
<source>Battery percentage</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
|
||||
<source>Charger plugged</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
|
||||
<source>SD Card Settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
|
||||
<source>Enable virtual SD card</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
|
||||
<source>Write protect virtual SD card</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>Interface</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>User Interface settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General emulator settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics emulation and output settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio emulation and output settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery emulation settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card emulation settings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
|
||||
<source>Language change successful</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
|
||||
<source>Restart Panda3DS for the new language to be used.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
|
||||
<source>Language change failed</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
|
||||
<source>The language you selected is not included in Panda3DS. If you're seeing this, someone messed up the language UI code...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
|
||||
<source>Alber</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
|
||||
<source>File</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
|
||||
<source>Emulation</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
|
||||
<source>Tools</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
|
||||
<source>About</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
|
||||
<source>Load game</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
|
||||
<source>Load Lua script</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
|
||||
<source>Open Panda3DS folder</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
|
||||
<source>Pause</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
|
||||
<source>Resume</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
|
||||
<source>Reset</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
|
||||
<source>Configure</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
|
||||
<source>Dump RomFS</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
|
||||
<source>Open Lua Editor</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
|
||||
<source>Open Cheats Editor</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
|
||||
<source>Open Patch Window</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
|
||||
<source>Open Shader Editor</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
|
||||
<source>Dump loaded DSP firmware</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
|
||||
<source>Select 3DS ROM to load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
|
||||
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Select Lua script to load</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Lua scripts (*.lua *.txt)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
|
||||
<source>Select folder to dump RomFS files to</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
|
||||
<source>Invalid format for RomFS dumping</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
|
||||
<source>The currently loaded app is not in a format that supports RomFS</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
|
||||
<source>OK</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS found</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS partition was found in the loaded app</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>Select file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>DSP firmware file (*.cdc)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>No DSP firmware loaded</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
|
||||
<source>Failed to open output file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
|
||||
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
|
||||
<source>ROM patcher</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
|
||||
<source>Select input file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
|
||||
<source>Select</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Select patch file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
|
||||
<source>Apply patch</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<source>Select file to patch</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>All files (*.*)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Patch files (*.ips *.ups *.bps)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Paths not provided correctly</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Please provide paths for both the input file and the patch file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>Select file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No output path</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No path was provided for the output file, no patching was done</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown patch format</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Failed to open input files</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Make sure they're in a directory Panda3DS has access to</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Patching Success</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Your file was patched successfully.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
|
||||
<source>Checksum mismatch</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
|
||||
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>Patching error</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>An error occured while patching</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow::PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
|
||||
<source>OK</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ShaderEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
|
||||
<source>Reload shader</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TextEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
|
||||
<source>Lua Editor</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
|
||||
<source>Load script</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
763
docs/translations/es.ts
Normal file
|
@ -0,0 +1,763 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE TS><TS version="2.1" language="es">
|
||||
<context>
|
||||
<name>AboutWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Acerca de Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
|
||||
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
|
||||
<translation>Panda3DS es un emulador libre y de código abierto de Nintendo 3DS para Windows, MacOS y Linux</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
|
||||
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
|
||||
<translation>Visita panda3ds.com para obtener ayuda con Panda3DS y los links a nuestras páginas oficiales de soporte.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
|
||||
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></source>
|
||||
<translation>Panda3DS es desarrollado por voluntarios en su tiempo libre. Debajo se encuentran los voluntarios que están de acuerdo con ser listados aquí, en ningún orden en particular.<br>Si piensas que deberías ser listado, por favor infórmanos<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEditDialog</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
|
||||
<source>Edit Cheat</source>
|
||||
<translation>Editar Truco</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
|
||||
<source>Cheat name</source>
|
||||
<translation>Nombre del truco</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEntryWidget</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
|
||||
<source>Edit</source>
|
||||
<translation>Editar</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatsWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
|
||||
<source>Cheats</source>
|
||||
<translation>Trucos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
|
||||
<source>Add</source>
|
||||
<translation>Añadir</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
|
||||
<source>Remove</source>
|
||||
<translation>Quitar</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ConfigWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
|
||||
<source>Configuration</source>
|
||||
<translation>Configuración</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
|
||||
<source>Interface Settings</source>
|
||||
<translation>Configuración de Interfaz</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
|
||||
<source>System</source>
|
||||
<translation>Sistema</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
|
||||
<source>Light</source>
|
||||
<translation>Claro</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
|
||||
<source>Dark</source>
|
||||
<translation>Oscuro</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
|
||||
<source>Greetings Cat</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
|
||||
<source>Cream</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
|
||||
<source>Color theme</source>
|
||||
<translation>Tema de color</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
|
||||
<source>Happy panda</source>
|
||||
<translation>Panda feliz</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
|
||||
<source>Happy panda (colourful)</source>
|
||||
<translation>Panda feliz (colorido)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
|
||||
<source>Sleepy panda</source>
|
||||
<translation>Panda somnoliento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
|
||||
<source>Cow panda</source>
|
||||
<translation>Panda vaca</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
|
||||
<source>The penguin from SkyEmu</source>
|
||||
<translation>El pungüino de SkyEmu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
|
||||
<source>Window icon</source>
|
||||
<translation>Icono de ventana</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
|
||||
<source>Language</source>
|
||||
<translation>Idioma</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
|
||||
<source>Show version on window title</source>
|
||||
<translation>Mostrar versión en la barra de título de la ventana</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber v%1</source>
|
||||
<translation>Alber v%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber</source>
|
||||
<translation>Alber</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
|
||||
<source>Remember window position</source>
|
||||
<translation>Recordar posición de la ventana</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
|
||||
<source>General Settings</source>
|
||||
<translation>Configuración General</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
|
||||
<source>Browse...</source>
|
||||
<translation>Examinar...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
|
||||
<source>Select Directory</source>
|
||||
<translation>Seleccionar Directorio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
|
||||
<source>Default ROMs path</source>
|
||||
<translation>Ruta predeterminada de ROMs</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
|
||||
<source>Enable Discord RPC</source>
|
||||
<translation>Activar Discord RPC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
|
||||
<source>Use portable build</source>
|
||||
<translation>Usar build portable</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
|
||||
<source>Print version in console output</source>
|
||||
<translation>Imprimir versión en consola</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
|
||||
<source>Graphics Settings</source>
|
||||
<translation>Configuración de Gráficos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
|
||||
<source>Null</source>
|
||||
<translation>Nulo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
|
||||
<source>OpenGL</source>
|
||||
<translation>OpenGL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
|
||||
<source>Vulkan</source>
|
||||
<translation>Vulkan</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
|
||||
<source>GPU renderer</source>
|
||||
<translation>Renderizador GPU</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
|
||||
<source>Enable Renderdoc</source>
|
||||
<translation>Activar Renderdoc</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
|
||||
<source>Enable shader JIT</source>
|
||||
<translation>Activar JIT de shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
|
||||
<source>Enable VSync</source>
|
||||
<translation>Activar VSync</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
|
||||
<source>Use ubershaders (No stutter, maybe slower)</source>
|
||||
<translation>Usar ubershaders (No stuttering, puede ser más lento)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
|
||||
<source>Accurate shader multiplication</source>
|
||||
<translation>Multiplicación precisa de shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
|
||||
<source>Accelerate shaders</source>
|
||||
<translation>Acelerar shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
|
||||
<source>Force shadergen when rendering lights</source>
|
||||
<translation>Forzar shadergen al renderizar luces</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
|
||||
<source>Light threshold for forcing shadergen</source>
|
||||
<translation>Umbral de luz para forzar shadergen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
|
||||
<source>Audio Settings</source>
|
||||
<translation>Configuración de Audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
|
||||
<source>LLE</source>
|
||||
<translation>LLE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
|
||||
<source>HLE</source>
|
||||
<translation>HLE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
|
||||
<source>DSP emulation</source>
|
||||
<translation>Emulación de DSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
|
||||
<source>Enable audio</source>
|
||||
<translation>Activar audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
|
||||
<source>Enable AAC audio</source>
|
||||
<translation>Activar audio AAC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
|
||||
<source>Print DSP firmware</source>
|
||||
<translation>Imprimir firmware DSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
|
||||
<source>Mute audio device</source>
|
||||
<translation>Silenciar dispositivo de audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
|
||||
<source>Cubic</source>
|
||||
<translation>Cúbico</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
|
||||
<source>Linear</source>
|
||||
<translation>Linear</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
|
||||
<source>Volume curve</source>
|
||||
<translation>Curva del volumen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
|
||||
<source>Audio device volume</source>
|
||||
<translation>Volumen del dispositivo de audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
|
||||
<source>Battery Settings</source>
|
||||
<translation>Configuración de Batería</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
|
||||
<source>Battery percentage</source>
|
||||
<translation>Porcentaje de batería</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
|
||||
<source>Charger plugged</source>
|
||||
<translation>Cargador conectado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
|
||||
<source>SD Card Settings</source>
|
||||
<translation>Configuración de Tarjeta SD</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
|
||||
<source>Enable virtual SD card</source>
|
||||
<translation>Activar tarjeta SD virtual</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
|
||||
<source>Write protect virtual SD card</source>
|
||||
<translation>Proteger tarjeta SD de escritura</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>Interface</source>
|
||||
<translation>Interfaz</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>User Interface settings</source>
|
||||
<translation>Configuración de Interfaz de Usuario</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General</source>
|
||||
<translation>General</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General emulator settings</source>
|
||||
<translation>Configuración general del emulador</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics</source>
|
||||
<translation>Gráficos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics emulation and output settings</source>
|
||||
<translation>Configuración de emulación de gráficos y salida de vídeo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio</source>
|
||||
<translation>Audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio emulation and output settings</source>
|
||||
<translation>Configuración de emulación y salida de audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery</source>
|
||||
<translation>Batería</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery emulation settings</source>
|
||||
<translation>Configuración de emulación de la batería</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card</source>
|
||||
<translation>Tarjeta SD</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card emulation settings</source>
|
||||
<translation>Configuración de emulación de la tarjeta SD</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
|
||||
<source>Language change successful</source>
|
||||
<translation>Idioma cambiado correctamente</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
|
||||
<source>Restart Panda3DS for the new language to be used.</source>
|
||||
<translation>Reinicie Panda3DS para utilizar el nuevo idioma.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
|
||||
<source>Language change failed</source>
|
||||
<translation>Cambio de idioma fallido</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
|
||||
<source>The language you selected is not included in Panda3DS. If you're seeing this, someone messed up the language UI code...</source>
|
||||
<translation>El idioma que ha seleccionado no está incluido en Panda3DS. Si está viendo esto, alguien cometió un error en el código...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
|
||||
<source>Alber</source>
|
||||
<translation>Alber</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
|
||||
<source>File</source>
|
||||
<translation>Archivo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
|
||||
<source>Emulation</source>
|
||||
<translation>Emulación</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
|
||||
<source>Tools</source>
|
||||
<translation>Herramientas</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
|
||||
<source>About</source>
|
||||
<translation>Acerca de</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
|
||||
<source>Load game</source>
|
||||
<translation>Cargar juego</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
|
||||
<source>Load Lua script</source>
|
||||
<translation>Cargar script Lua</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
|
||||
<source>Open Panda3DS folder</source>
|
||||
<translation>Abrir carpeta Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
|
||||
<source>Pause</source>
|
||||
<translation>Pausar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
|
||||
<source>Resume</source>
|
||||
<translation>Reanudar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
|
||||
<source>Reset</source>
|
||||
<translation>Reiniciar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
|
||||
<source>Configure</source>
|
||||
<translation>Configurar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
|
||||
<source>Dump RomFS</source>
|
||||
<translation>Volcar RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
|
||||
<source>Open Lua Editor</source>
|
||||
<translation>Abrir Editor Lua</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
|
||||
<source>Open Cheats Editor</source>
|
||||
<translation>Abrir Editor de Trucos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
|
||||
<source>Open Patch Window</source>
|
||||
<translation>Abrir Ventana de Parches</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
|
||||
<source>Open Shader Editor</source>
|
||||
<translation>Abrir Editor de Shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
|
||||
<source>Dump loaded DSP firmware</source>
|
||||
<translation>Volcar firmware DSP cargado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Acerca de Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
|
||||
<source>Select 3DS ROM to load</source>
|
||||
<translation>Seleccione el ROM de 3DS a cargar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
|
||||
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
|
||||
<translation>ROMs de Nintendo 3DS (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Select Lua script to load</source>
|
||||
<translation>Seleccione el script Lua a cargar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Lua scripts (*.lua *.txt)</source>
|
||||
<translation>Scripts Lua (*.lua *.txt)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
|
||||
<source>Select folder to dump RomFS files to</source>
|
||||
<translation>Seleccione la carpeta donde volcar los archivos del RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
|
||||
<source>Invalid format for RomFS dumping</source>
|
||||
<translation>Formato inváido para volcado de RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
|
||||
<source>The currently loaded app is not in a format that supports RomFS</source>
|
||||
<translation>La aplicación cargada no tiene un formato que soporta RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
|
||||
<source>OK</source>
|
||||
<translation>OK</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS found</source>
|
||||
<translation>RomFS no encontrado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS partition was found in the loaded app</source>
|
||||
<translation>No se encontró una partición RomFS en la aplicación cargada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>Select file</source>
|
||||
<translation>Seleccionar archivo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>DSP firmware file (*.cdc)</source>
|
||||
<translation>Archivo de firmware DSP (*.cdc)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>No DSP firmware loaded</source>
|
||||
<translation>Firmware DSP no cargado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
|
||||
<translation>La aplicación cargada no ha subido un firmware al DSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
|
||||
<source>Failed to open output file</source>
|
||||
<translation>Error al abrir el archivo de salida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
|
||||
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
|
||||
<translation>No se pudo escribir el firmware DSP cargado al archivo seleccionado. Por favor asegure que tiene los permisos necesarios para acceder a este archivo</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
|
||||
<source>ROM patcher</source>
|
||||
<translation>Parcheador de ROM</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
|
||||
<source>Select input file</source>
|
||||
<translation>Seleccione el archivo de entrada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
|
||||
<source>Select</source>
|
||||
<translation>Seleccionar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Select patch file</source>
|
||||
<translation>Seleccione el archivo de parche</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
|
||||
<source>Apply patch</source>
|
||||
<translation>Aplicar parche</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<source>Select file to patch</source>
|
||||
<translation>Seleccione el archivo a parchear</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>All files (*.*)</source>
|
||||
<translation>Todos los archivos (*.*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Patch files (*.ips *.ups *.bps)</source>
|
||||
<translation>Archivos de parche (*.ips *.ups *.bps)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Paths not provided correctly</source>
|
||||
<translation>Rutas no proporcionadas correctamente</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Please provide paths for both the input file and the patch file</source>
|
||||
<translation>Por favor proporcione rutas para el archivo de entrada y el parche</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>Select file</source>
|
||||
<translation>Seleccionar archivo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No output path</source>
|
||||
<translation>No hay archivo de salida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No path was provided for the output file, no patching was done</source>
|
||||
<translation>No se ha proporcionado una ruta para el archivo de salida, no se ha aplicado el parche</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown patch format</source>
|
||||
<translation>Formato del parche desconocido</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
|
||||
<translation>Formato desconocido del archivo de parche. Actualmente son soportados IPS, UPS y BPS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Failed to open input files</source>
|
||||
<translation>Error al abrir archivos de entrada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Make sure they're in a directory Panda3DS has access to</source>
|
||||
<translation>Asegure que estén en un directorio al que Panda3DS tenga acceso</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Patching Success</source>
|
||||
<translation>Parche exitoso</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Your file was patched successfully.</source>
|
||||
<translation>Su archivo fue parcheado con éxito.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
|
||||
<source>Checksum mismatch</source>
|
||||
<translation>Discrepancia en la suma de verificación</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
|
||||
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
|
||||
<translation>El parche se aplicó con éxito pero se detectó una discrepancia en la suma de verificación. Los archivos de entrada o salida pueden no ser correctos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>Patching error</source>
|
||||
<translation>Error de parcheo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>An error occured while patching</source>
|
||||
<translation>Ha ocurrido un error en el parcheo</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow::PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
|
||||
<source>OK</source>
|
||||
<translation>OK</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ShaderEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
|
||||
<source>Reload shader</source>
|
||||
<translation>Recargar shader</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TextEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
|
||||
<source>Lua Editor</source>
|
||||
<translation>Editor Lua</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
|
||||
<source>Load script</source>
|
||||
<translation>Cargar script</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
763
docs/translations/nl.ts
Normal file
|
@ -0,0 +1,763 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE TS><TS version="2.1" language="nl">
|
||||
<context>
|
||||
<name>AboutWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Over Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
|
||||
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
|
||||
<translation>Panda3DS is een gratis, open source Nintendo 3DS-emulator voor Windows, MacOS en Linux</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
|
||||
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
|
||||
<translation>Bezoek panda3ds.com voor ondersteuning van Panda3DS en links naar onze officiële ondersteuningskanalen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
|
||||
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></source>
|
||||
<translation>Panda3DS wordt ontwikkeld door vrijwilligers in hun vrije tijd. Hieronder een lijst van sommige van deze vrijwilligers die akkoord zijn met een vermelding, in willekeurige volgorde.<br>Als jij vindt dat je op deze lijst zou moeten staan, laat het ons dan weten<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEditDialog</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
|
||||
<source>Edit Cheat</source>
|
||||
<translation>Cheat bewerken</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
|
||||
<source>Cheat name</source>
|
||||
<translation>Cheatnaam</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEntryWidget</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
|
||||
<source>Edit</source>
|
||||
<translation>Bewerken</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatsWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
|
||||
<source>Cheats</source>
|
||||
<translation>Cheats</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
|
||||
<source>Add</source>
|
||||
<translation>Toevoegen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
|
||||
<source>Remove</source>
|
||||
<translation>Verwijderen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ConfigWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
|
||||
<source>Configuration</source>
|
||||
<translation>Instellingen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
|
||||
<source>Interface Settings</source>
|
||||
<translation>Interfaceinstellingen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
|
||||
<source>System</source>
|
||||
<translation>Systeem</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
|
||||
<source>Light</source>
|
||||
<translation>Licht</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
|
||||
<source>Dark</source>
|
||||
<translation>Donker</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
|
||||
<source>Greetings Cat</source>
|
||||
<translation>Begroetingskat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
|
||||
<source>Cream</source>
|
||||
<translation>Crème</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
|
||||
<source>Color theme</source>
|
||||
<translation>Kleurenthema</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
|
||||
<source>Happy panda</source>
|
||||
<translation>Blije panda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
|
||||
<source>Happy panda (colourful)</source>
|
||||
<translation>Blije panda (kleurrijk)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
|
||||
<source>Sleepy panda</source>
|
||||
<translation>Slaperige panda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
|
||||
<source>Cow panda</source>
|
||||
<translation>Koeienpanda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
|
||||
<source>The penguin from SkyEmu</source>
|
||||
<translation>De pinguïn van SkyEmu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
|
||||
<source>Window icon</source>
|
||||
<translation>Venstericoon</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
|
||||
<source>Language</source>
|
||||
<translation>Taal</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
|
||||
<source>Show version on window title</source>
|
||||
<translation>Toon versie in venstertitel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber v%1</source>
|
||||
<translation>Alber v%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber</source>
|
||||
<translation>Alber</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
|
||||
<source>Remember window position</source>
|
||||
<translation>Vensterpositie onthouden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
|
||||
<source>General Settings</source>
|
||||
<translation>Algemene instellingen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
|
||||
<source>Browse...</source>
|
||||
<translation>Bladeren...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
|
||||
<source>Select Directory</source>
|
||||
<translation>Kies map</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
|
||||
<source>Default ROMs path</source>
|
||||
<translation>Standaard pad voor ROMs</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
|
||||
<source>Enable Discord RPC</source>
|
||||
<translation>Discord RPC inschakelen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
|
||||
<source>Use portable build</source>
|
||||
<translation>Portable build gebruiken</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
|
||||
<source>Print version in console output</source>
|
||||
<translation>Versie afdrukken in consoleuitvoer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
|
||||
<source>Graphics Settings</source>
|
||||
<translation>Grafische instellingen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
|
||||
<source>Null</source>
|
||||
<translation>Null</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
|
||||
<source>OpenGL</source>
|
||||
<translation>OpenGL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
|
||||
<source>Vulkan</source>
|
||||
<translation>Vulkan</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
|
||||
<source>GPU renderer</source>
|
||||
<translation>Renderen op videokaart</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
|
||||
<source>Enable Renderdoc</source>
|
||||
<translation>Renderdoc inschakelen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
|
||||
<source>Enable shader JIT</source>
|
||||
<translation>Shader JIT inschakelen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
|
||||
<source>Enable VSync</source>
|
||||
<translation>VSync inschakelen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
|
||||
<source>Use ubershaders (No stutter, maybe slower)</source>
|
||||
<translation>Ubershaders gebruiken (geen haperingen, mogelijk langzamer)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
|
||||
<source>Accurate shader multiplication</source>
|
||||
<translation>Nauwkeurige vermenigvuldigen in shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
|
||||
<source>Accelerate shaders</source>
|
||||
<translation>Shaders versnellen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
|
||||
<source>Force shadergen when rendering lights</source>
|
||||
<translation>Shadergen afdwingen bij tekenen licht</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
|
||||
<source>Light threshold for forcing shadergen</source>
|
||||
<translation>Lichtgrens voor afdwingen shadergen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
|
||||
<source>Audio Settings</source>
|
||||
<translation>Audioinstellingen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
|
||||
<source>LLE</source>
|
||||
<translation>LLE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
|
||||
<source>HLE</source>
|
||||
<translation>HLE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
|
||||
<source>DSP emulation</source>
|
||||
<translation>DSP-emulatie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
|
||||
<source>Enable audio</source>
|
||||
<translation>Audio inschakelen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
|
||||
<source>Enable AAC audio</source>
|
||||
<translation>AAC-audio inschakelen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
|
||||
<source>Print DSP firmware</source>
|
||||
<translation>DSP-firmware afdrukken</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
|
||||
<source>Mute audio device</source>
|
||||
<translation>Audioapparaat dempen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
|
||||
<source>Cubic</source>
|
||||
<translation>Kubiek</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
|
||||
<source>Linear</source>
|
||||
<translation>Lineair</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
|
||||
<source>Volume curve</source>
|
||||
<translation>Volumecurve</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
|
||||
<source>Audio device volume</source>
|
||||
<translation>Volume audioapparaat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
|
||||
<source>Battery Settings</source>
|
||||
<translation>Batterij-instellingen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
|
||||
<source>Battery percentage</source>
|
||||
<translation>Batterijpercentage</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
|
||||
<source>Charger plugged</source>
|
||||
<translation>Oplader aangesloten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
|
||||
<source>SD Card Settings</source>
|
||||
<translation>Instellingen SD-kaart</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
|
||||
<source>Enable virtual SD card</source>
|
||||
<translation>Virtuele SD-kaart inschakelen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
|
||||
<source>Write protect virtual SD card</source>
|
||||
<translation>Virtuele SD-kaart schrijfbeveiligen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>Interface</source>
|
||||
<translation>Interface</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>User Interface settings</source>
|
||||
<translation>Instellingen gebruikersinterface</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General</source>
|
||||
<translation>Algemeen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General emulator settings</source>
|
||||
<translation>Algemene emulatorinstellingen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics</source>
|
||||
<translation>Weergave</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics emulation and output settings</source>
|
||||
<translation>Instellingen grafische emulatie en weergave </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio</source>
|
||||
<translation>Audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio emulation and output settings</source>
|
||||
<translation>Instellingen audioemulatie en weergave</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery</source>
|
||||
<translation>Batterij</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery emulation settings</source>
|
||||
<translation>Instellingen batterijemulatie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card</source>
|
||||
<translation>SD-kaart</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card emulation settings</source>
|
||||
<translation>Instellingen SD-kaart-emulatie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
|
||||
<source>Language change successful</source>
|
||||
<translation>Taal succesvol ingesteld</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
|
||||
<source>Restart Panda3DS for the new language to be used.</source>
|
||||
<translation>Herstart Panda3DS om de nieuw gekozen taal te gebruiken.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
|
||||
<source>Language change failed</source>
|
||||
<translation>Wijzigen van taal mislukt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
|
||||
<source>The language you selected is not included in Panda3DS. If you're seeing this, someone messed up the language UI code...</source>
|
||||
<translation>De gekozen taal is niet beschikbaar in Panda3DS. Als je dit leest heeft iemand de taalcode verprutst...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
|
||||
<source>Alber</source>
|
||||
<translation>Alber</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
|
||||
<source>File</source>
|
||||
<translation>Bestand</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
|
||||
<source>Emulation</source>
|
||||
<translation>Emulatie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
|
||||
<source>Tools</source>
|
||||
<translation>Hulpmiddelen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
|
||||
<source>About</source>
|
||||
<translation>Over</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
|
||||
<source>Load game</source>
|
||||
<translation>Spel laden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
|
||||
<source>Load Lua script</source>
|
||||
<translation>LUA-script laden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
|
||||
<source>Open Panda3DS folder</source>
|
||||
<translation>Open Panda3DS-map</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
|
||||
<source>Pause</source>
|
||||
<translation>Pauzeren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
|
||||
<source>Resume</source>
|
||||
<translation>Hervatten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
|
||||
<source>Reset</source>
|
||||
<translation>Reset</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
|
||||
<source>Configure</source>
|
||||
<translation>Instellingen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
|
||||
<source>Dump RomFS</source>
|
||||
<translation>RomFS dumpen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
|
||||
<source>Open Lua Editor</source>
|
||||
<translation>Open LUA-editor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
|
||||
<source>Open Cheats Editor</source>
|
||||
<translation>Open cheats-editor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
|
||||
<source>Open Patch Window</source>
|
||||
<translation>Open patchvenster</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
|
||||
<source>Open Shader Editor</source>
|
||||
<translation>Open shader-editor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
|
||||
<source>Dump loaded DSP firmware</source>
|
||||
<translation>Geladen DSP-firmware dumpen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Over Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
|
||||
<source>Select 3DS ROM to load</source>
|
||||
<translation>Kies 3DS ROM om te laden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
|
||||
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
|
||||
<translation>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Select Lua script to load</source>
|
||||
<translation>Kies LUA-script om te laden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Lua scripts (*.lua *.txt)</source>
|
||||
<translation>LUA-scripts (*.lua *.txt)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
|
||||
<source>Select folder to dump RomFS files to</source>
|
||||
<translation>Kies map om RomFS-bestanden heen te dumpen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
|
||||
<source>Invalid format for RomFS dumping</source>
|
||||
<translation>Ongeldig formaat voor RomFS dump</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
|
||||
<source>The currently loaded app is not in a format that supports RomFS</source>
|
||||
<translation>Het formaat van de momenteel geladen applicatie ondersteunt geen RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
|
||||
<source>OK</source>
|
||||
<translation>OK</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS found</source>
|
||||
<translation>Geen RomFS gevonden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS partition was found in the loaded app</source>
|
||||
<translation>Geen RomFS-partitie gevonden in de geladen applicatie</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>Select file</source>
|
||||
<translation>Selecteer bestand</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>DSP firmware file (*.cdc)</source>
|
||||
<translation>DSP-firmware-bestand (*.cdc)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>No DSP firmware loaded</source>
|
||||
<translation>Geen DSP-firmware geladen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
|
||||
<translation>De momenteel geladen applicatie heeft geen firmware geüpload naar de DSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
|
||||
<source>Failed to open output file</source>
|
||||
<translation>Uitvoerbestand openen mislukt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
|
||||
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
|
||||
<translation>De momenteel geladen DSP-firmware kan niet worden geschreven naar het gekozen bestand. Controleer de permissies van het gekozen bestand</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
|
||||
<source>ROM patcher</source>
|
||||
<translation>ROM-patcher</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
|
||||
<source>Select input file</source>
|
||||
<translation>Kies invoerbestand</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
|
||||
<source>Select</source>
|
||||
<translation>Kies</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Select patch file</source>
|
||||
<translation>Kies patchbestand</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
|
||||
<source>Apply patch</source>
|
||||
<translation>Patch toepassen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<source>Select file to patch</source>
|
||||
<translation>Kies bestand om te patchen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>All files (*.*)</source>
|
||||
<translation>Alle bestanden (*.*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Patch files (*.ips *.ups *.bps)</source>
|
||||
<translation>Patch-bestanden (*.ips *.ups *.bps)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Paths not provided correctly</source>
|
||||
<translation>Paden incorrect meegegeven</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Please provide paths for both the input file and the patch file</source>
|
||||
<translation>Geef paden van invoerbestand en patchbestand op</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>Select file</source>
|
||||
<translation>Kies bestand</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No output path</source>
|
||||
<translation>Geen uitvoerpad</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No path was provided for the output file, no patching was done</source>
|
||||
<translation>Geen pad opgegeven voor uitvoerbestand, patch niet toegepast</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown patch format</source>
|
||||
<translation>Onbekend patchformaat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
|
||||
<translation>Ongeldig formaat van patchbestand. Momenteel wordt IPS, UPS en BPS ondersteund</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Failed to open input files</source>
|
||||
<translation>Openen van invoerbestanden mislukt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Make sure they're in a directory Panda3DS has access to</source>
|
||||
<translation>Zorg ervoor dat ze in een map staan waar Panda3DS toegang toe heeft</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Patching Success</source>
|
||||
<translation>Patch succesvol</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Your file was patched successfully.</source>
|
||||
<translation>Het bestand is succesvol gepatcht.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
|
||||
<source>Checksum mismatch</source>
|
||||
<translation>Checksum komt niet overeen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
|
||||
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
|
||||
<translation>Patch is succesvol toegepast maar de checksum komt niet overeen. Invoer- of uitvoerbestand is mogelijk ongeldig</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>Patching error</source>
|
||||
<translation>Fout tijdens patchen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>An error occured while patching</source>
|
||||
<translation>Er is bij het patchen een fout opgetreden</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow::PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
|
||||
<source>OK</source>
|
||||
<translation>OK</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ShaderEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
|
||||
<source>Reload shader</source>
|
||||
<translation>Shader herladen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TextEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
|
||||
<source>Lua Editor</source>
|
||||
<translation>LUA-editor</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
|
||||
<source>Load script</source>
|
||||
<translation>Script laden</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
764
docs/translations/pt_br.ts
Normal file
|
@ -0,0 +1,764 @@
|
|||
<?xml version="1.0" ?><!DOCTYPE TS><TS version="2.1" language="es">
|
||||
<context>
|
||||
<name>AboutWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Sobre o Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
|
||||
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
|
||||
<translation>Panda3DS é um emulador gratuito e open-source para Windows, MacOS e Linux</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
|
||||
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
|
||||
<translation>Visite panda3ds.com para obter ajuda e links de suporte oficial.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
|
||||
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></source>
|
||||
<translation>Panda3DS é desenvolvido por voluntários em seu tempo livre. Abaixo a lista de alguns volutário<br>(Lista sem nenhuma ordem específica)<br>Se acha que seu nome deveria estar listado aqui por favor informe-nos<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEditDialog</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
|
||||
<source>Edit Cheat</source>
|
||||
<translation>Editar Trapaças</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
|
||||
<source>Cheat name</source>
|
||||
<translation>Nome da trapaça</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEntryWidget</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
|
||||
<source>Edit</source>
|
||||
<translation>Editar</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatsWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
|
||||
<source>Cheats</source>
|
||||
<translation>Trapaça</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
|
||||
<source>Add</source>
|
||||
<translation>Adicionar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
|
||||
<source>Remove</source>
|
||||
<translation>Sair</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ConfigWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
|
||||
<source>Configuration</source>
|
||||
<translation>Configurações</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
|
||||
<source>Interface Settings</source>
|
||||
<translation>Configurações da interface</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
|
||||
<source>System</source>
|
||||
<translation>Sistema</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
|
||||
<source>Light</source>
|
||||
<translation>Claro</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
|
||||
<source>Dark</source>
|
||||
<translation>Escuro</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
|
||||
<source>Greetings Cat</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
|
||||
<source>Cream</source>
|
||||
<translation type="unfinished"/>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
|
||||
<source>Color theme</source>
|
||||
<translation>Tema de color</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
|
||||
<source>Happy panda</source>
|
||||
<translation>Panda feliz</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
|
||||
<source>Happy panda (colourful)</source>
|
||||
<translation>Panda feliz (colorido)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
|
||||
<source>Sleepy panda</source>
|
||||
<translation>Panda sonolento</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
|
||||
<source>Cow panda</source>
|
||||
<translation>Panda vaca</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
|
||||
<source>The penguin from SkyEmu</source>
|
||||
<translation>O pinguim do SkyEmu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
|
||||
<source>Window icon</source>
|
||||
<translation>Icone da janela</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
|
||||
<source>Language</source>
|
||||
<translation>Idioma</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
|
||||
<source>Show version on window title</source>
|
||||
<translation>Mostrar versão na barra de título</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber v%1</source>
|
||||
<translation>Alber v%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber</source>
|
||||
<translation>Alber</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
|
||||
<source>Remember window position</source>
|
||||
<translation>Lembrar posição da janela</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
|
||||
<source>General Settings</source>
|
||||
<translation>Configurações gerais</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
|
||||
<source>Browse...</source>
|
||||
<translation>Navegar...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
|
||||
<source>Select Directory</source>
|
||||
<translation>Selecionar o diretório</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
|
||||
<source>Default ROMs path</source>
|
||||
<translation>Diretório padrão das ROMs</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
|
||||
<source>Enable Discord RPC</source>
|
||||
<translation>Ativar Discord RPC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
|
||||
<source>Use portable build</source>
|
||||
<translation>Usar build portatil</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
|
||||
<source>Print version in console output</source>
|
||||
<translation>Imprimir versão no console</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
|
||||
<source>Graphics Settings</source>
|
||||
<translation>Configurações gráficas</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
|
||||
<source>Null</source>
|
||||
<translation>Nulo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
|
||||
<source>OpenGL</source>
|
||||
<translation>OpenGL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
|
||||
<source>Vulkan</source>
|
||||
<translation>Vulkan</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
|
||||
<source>GPU renderer</source>
|
||||
<translation>Renderizador GPU</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
|
||||
<source>Enable Renderdoc</source>
|
||||
<translation>Ativar Renderdoc</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
|
||||
<source>Enable shader JIT</source>
|
||||
<translation>Ativar JIT de shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
|
||||
<source>Enable VSync</source>
|
||||
<translation>Ativar VSync</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
|
||||
<source>Use ubershaders (No stutter, maybe slower)</source>
|
||||
<translation>Usar ubershaders (No stuttering, puede ser más lento)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
|
||||
<source>Accurate shader multiplication</source>
|
||||
<translation>Multiplicação precisa de shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
|
||||
<source>Accelerate shaders</source>
|
||||
<translation>Graficos acelerados</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
|
||||
<source>Force shadergen when rendering lights</source>
|
||||
<translation>Forçar shadergen ao renderizar luzes.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
|
||||
<source>Light threshold for forcing shadergen</source>
|
||||
<translation>Limear de luzes para forçar shadergen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
|
||||
<source>Audio Settings</source>
|
||||
<translation>Configurações de audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
|
||||
<source>LLE</source>
|
||||
<translation>LLE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
|
||||
<source>HLE</source>
|
||||
<translation>HLE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
|
||||
<source>DSP emulation</source>
|
||||
<translation>Emulação DSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
|
||||
<source>Enable audio</source>
|
||||
<translation>Ativar audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
|
||||
<source>Enable AAC audio</source>
|
||||
<translation>Ativar audio AAC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
|
||||
<source>Print DSP firmware</source>
|
||||
<translation>Imprimir firmware DSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
|
||||
<source>Mute audio device</source>
|
||||
<translation>Silenciar dispositivo de audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
|
||||
<source>Cubic</source>
|
||||
<translation>Cúbico</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
|
||||
<source>Linear</source>
|
||||
<translation>Linear</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
|
||||
<source>Volume curve</source>
|
||||
<translation>Curva de volume</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
|
||||
<source>Audio device volume</source>
|
||||
<translation>Volume do dispositivo de audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
|
||||
<source>Battery Settings</source>
|
||||
<translation>Configurações de bateria</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
|
||||
<source>Battery percentage</source>
|
||||
<translation>Porcentagem da bateria</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
|
||||
<source>Charger plugged</source>
|
||||
<translation>Carregador conectado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
|
||||
<source>SD Card Settings</source>
|
||||
<translation>Configurações do cartão de memoria</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
|
||||
<source>Enable virtual SD card</source>
|
||||
<translation>Ativar cartão de memoria virtual</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
|
||||
<source>Write protect virtual SD card</source>
|
||||
<translation>Proteger cartão de memoria virtual contra escrita</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>Interface</source>
|
||||
<translation>Interface</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>User Interface settings</source>
|
||||
<translation>Configurações da interface de usuario</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General</source>
|
||||
<translation>Geral</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General emulator settings</source>
|
||||
<translation>Configurações gerais do emulador</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics</source>
|
||||
<translation>Gráficos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics emulation and output settings</source>
|
||||
<translation>Configurações da emulação e saida de video</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio</source>
|
||||
<translation>Audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio emulation and output settings</source>
|
||||
<translation>Configurações da emulação e saida de audio</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery</source>
|
||||
<translation>Bateria</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery emulation settings</source>
|
||||
<translation>Configuração da emulação da bateria</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card</source>
|
||||
<translation>Cartão de memoria</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card emulation settings</source>
|
||||
<translation>Configurar a emulação do cartão de memoria</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
|
||||
<source>Language change successful</source>
|
||||
<translation>Idioma alterado com sucesso</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
|
||||
<source>Restart Panda3DS for the new language to be used.</source>
|
||||
<translation>Para aplicar o novo idioma feche e abra o emulador.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
|
||||
<source>Language change failed</source>
|
||||
<translation>A mudaça de idioma falhou</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
|
||||
<source>The language you selected is not included in Panda3DS. If you're seeing this, someone messed up the language UI code...</source>
|
||||
<translation>O idioma selecionado não existe no Panda3DS. Se você esta vendo esse erro existe um erro no codigo...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
|
||||
<source>Alber</source>
|
||||
<translation>Alber</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
|
||||
<source>File</source>
|
||||
<translation>Arquivo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
|
||||
<source>Emulation</source>
|
||||
<translation>Emulação</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
|
||||
<source>Tools</source>
|
||||
<translation>Ferramentas</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
|
||||
<source>About</source>
|
||||
<translation>Sobre</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
|
||||
<source>Load game</source>
|
||||
<translation>Carregar jogo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
|
||||
<source>Load Lua script</source>
|
||||
<translation>Carregar Script Lua</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
|
||||
<source>Open Panda3DS folder</source>
|
||||
<translation>Abrir pasta do Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
|
||||
<source>Pause</source>
|
||||
<translation>Pausar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
|
||||
<source>Resume</source>
|
||||
<translation>Continuar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
|
||||
<source>Reset</source>
|
||||
<translation>Reiniciar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
|
||||
<source>Configure</source>
|
||||
<translation>Configurar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
|
||||
<source>Dump RomFS</source>
|
||||
<translation>Extrair RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
|
||||
<source>Open Lua Editor</source>
|
||||
<translation>Abrir editor Lua</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
|
||||
<source>Open Cheats Editor</source>
|
||||
<translation>Abrir editor de trapaças</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
|
||||
<source>Open Patch Window</source>
|
||||
<translation>Abrir janela de trapaças</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
|
||||
<source>Open Shader Editor</source>
|
||||
<translation>Abrir editor de shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
|
||||
<source>Dump loaded DSP firmware</source>
|
||||
<translation>Extrair firmware DSP carregado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Sobre o Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
|
||||
<source>Select 3DS ROM to load</source>
|
||||
<translation>Selecione uma ROM de 3DS para carregar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
|
||||
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
|
||||
<translation>ROMs de Nintendo 3DS (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Select Lua script to load</source>
|
||||
<translation>Selecione uma ROM de 3DS para carregar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Lua scripts (*.lua *.txt)</source>
|
||||
<translation>Scripts Lua (*.lua *.txt)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
|
||||
<source>Select folder to dump RomFS files to</source>
|
||||
<translation>Selecione onde a RomFS será extraida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
|
||||
<source>Invalid format for RomFS dumping</source>
|
||||
<translation>Formato de RomFS inválido</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
|
||||
<source>The currently loaded app is not in a format that supports RomFS</source>
|
||||
<translation>O aplicativo carregado não suporta RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
|
||||
<source>OK</source>
|
||||
<translation>OK</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS found</source>
|
||||
<translation>RomFS no encontrado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS partition was found in the loaded app</source>
|
||||
<translation>A partição RomFS não foi encontrada no aplicativo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>Select file</source>
|
||||
<translation>Selecionar arquivo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>DSP firmware file (*.cdc)</source>
|
||||
<translation>Arquivo do firmware DSP (*.cdc)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>No DSP firmware loaded</source>
|
||||
<translation>Nenhum firmware DSP carregado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
|
||||
<translation>A aplicação não carregou um firmware DSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
|
||||
<source>Failed to open output file</source>
|
||||
<translation>Erro ao abrir arquvio de destino</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
|
||||
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
|
||||
<translation>O firmware DSP carregado não pode escrever no arquivo selecionado. Porfavor veja se você tem permissão para modificalo-lo.</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
|
||||
<source>ROM patcher</source>
|
||||
<translation>Editor de ROM</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
|
||||
<source>Select input file</source>
|
||||
<translation>Selecione o arquivo de entrada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
|
||||
<source>Select</source>
|
||||
<translation>Selecionar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Select patch file</source>
|
||||
<translation>Seleciona um arquivo de patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
|
||||
<source>Apply patch</source>
|
||||
<translation>Aplicar patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<source>Select file to patch</source>
|
||||
<translation>Selecione um arquivo para editar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>All files (*.*)</source>
|
||||
<translation>Todos os arquivos (*.*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Patch files (*.ips *.ups *.bps)</source>
|
||||
<translation>Arquivos de patch (*.ips *.ups *.bps)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Paths not provided correctly</source>
|
||||
<translation>Diretórios não fornecidos corretamente</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Please provide paths for both the input file and the patch file</source>
|
||||
<translation>Por favor selecione os diretórios tanto para o arquivo de origem como para o patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>Select file</source>
|
||||
<translation>Selecionar arquivo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No output path</source>
|
||||
<translation>Sem diretório de saida</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No path was provided for the output file, no patching was done</source>
|
||||
<translation>Nenhum diretorio de destino foi fornecido, patch não aplicado.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown patch format</source>
|
||||
<translation>Formato de patch desconhecido</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
|
||||
<translation>Arquivo de patch inválido. Atualmete são suportado patches nos formatos IPS, UPS e BPS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Failed to open input files</source>
|
||||
<translation>Falha ao abrir os arquivos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Make sure they're in a directory Panda3DS has access to</source>
|
||||
<translation>Certifique-se de que eles estejam em um diretório ao qual o Panda3DS tenha acesso</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Patching Success</source>
|
||||
<translation>Patch aplicado</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Your file was patched successfully.</source>
|
||||
<translation>O patch foi aplicado com sucesso ao arquivo</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
|
||||
<source>Checksum mismatch</source>
|
||||
<translation>Checagem inválido</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
|
||||
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
|
||||
<translation>O patch foi aplicado com sucesso porem a checagem falhou. O arquivo de origem ou destino pode não estar correto.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>Patching error</source>
|
||||
<translation>Erro de patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>An error occured while patching</source>
|
||||
<translation>Um erro ocorreu ao aplicar o patch</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow::PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
|
||||
<source>OK</source>
|
||||
<translation>OK</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ShaderEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
|
||||
<source>Reload shader</source>
|
||||
<translation>Recargar shader</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TextEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
|
||||
<source>Lua Editor</source>
|
||||
<translation>Editor Lua</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
|
||||
<source>Load script</source>
|
||||
<translation>Cargar script</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
||||
|
774
docs/translations/sv.ts
Normal file
|
@ -0,0 +1,774 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE TS>
|
||||
<TS version="2.1" language="sv_SE">
|
||||
<extra-po-header-language>sv</extra-po-header-language>
|
||||
<extra-po-header-language_team></extra-po-header-language_team>
|
||||
<extra-po-header-last_translator>Daniel Nylander <github@danielnylander.se></extra-po-header-last_translator>
|
||||
<extra-po-header-po_revision_date></extra-po-header-po_revision_date>
|
||||
<extra-po-header-pot_creation_date></extra-po-header-pot_creation_date>
|
||||
<extra-po-header-project_id_version></extra-po-header-project_id_version>
|
||||
<extra-po-header-x_generator>Poedit 3.5</extra-po-header-x_generator>
|
||||
<extra-po-headers>Project-Id-Version,POT-Creation-Date,PO-Revision-Date,Last-Translator,Language-Team,Language,MIME-Version,Content-Type,Content-Transfer-Encoding,X-Qt-Contexts,X-Generator</extra-po-headers>
|
||||
<context>
|
||||
<name>AboutWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="16"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Om Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="35"/>
|
||||
<source>Panda3DS is a free and open source Nintendo 3DS emulator, for Windows, MacOS and Linux</source>
|
||||
<translation>Panda3DS är en Nintendo 3DS-emulator med fri och öppen källkod för Windows, MacOS och Linux</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="36"/>
|
||||
<source>Visit panda3ds.com for help with Panda3DS and links to our official support sites.</source>
|
||||
<translation>Besök panda3ds.com för att få hjälp med Panda3DS och länkar till våra officiella supportwebbplatser.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/about_window.cpp" line="38"/>
|
||||
<source>Panda3DS is developed by volunteers in their spare time. Below is a list of some of these volunteers who've agreed to be listed here, in no particular order.<br>If you think you should be listed here too, please inform us<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></source>
|
||||
<translation>Panda3DS utvecklas av volontärer på deras fritid. Nedan finns en lista över några av dessa volontärer som har gått med på att listas här, utan någon särskild ordning.<br>Om du tycker att du också borde listas här, informera oss<br><br>- Peach (wheremyfoodat)<br>- noumidev<br>- liuk707<br>- Wunk<br>- marysaka<br>- Sky<br>- merryhime<br>- TGP17<br>- Shadow<br></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEditDialog</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="72"/>
|
||||
<source>Edit Cheat</source>
|
||||
<translation>Redigera fusk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="82"/>
|
||||
<source>Cheat name</source>
|
||||
<translation>Fusknamn</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatEntryWidget</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="34"/>
|
||||
<source>Edit</source>
|
||||
<translation>Redigera</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>CheatsWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="164"/>
|
||||
<source>Cheats</source>
|
||||
<translation>Fusk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="177"/>
|
||||
<source>Add</source>
|
||||
<translation>Lägg till</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/cheats_window.cpp" line="178"/>
|
||||
<source>Remove</source>
|
||||
<translation>Ta bort</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ConfigWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="7"/>
|
||||
<source>Configuration</source>
|
||||
<translation>Konfiguration</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="63"/>
|
||||
<source>Interface Settings</source>
|
||||
<translation>Inställningar för gränssnitt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="69"/>
|
||||
<source>System</source>
|
||||
<translation>System</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="70"/>
|
||||
<source>Light</source>
|
||||
<translation>Ljus</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="71"/>
|
||||
<source>Dark</source>
|
||||
<translation>Mörk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="72"/>
|
||||
<source>Greetings Cat</source>
|
||||
<translation>Hälsningskatt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="73"/>
|
||||
<source>Cream</source>
|
||||
<translation>Grädde</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="81"/>
|
||||
<source>Color theme</source>
|
||||
<translation>Färgtema</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="84"/>
|
||||
<source>Happy panda</source>
|
||||
<translation>Glad panda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="85"/>
|
||||
<source>Happy panda (colourful)</source>
|
||||
<translation>Glad panda (färgglad)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="86"/>
|
||||
<source>Sleepy panda</source>
|
||||
<translation>Sömnig panda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="87"/>
|
||||
<source>Cow panda</source>
|
||||
<translation>Ko-panda</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="88"/>
|
||||
<source>The penguin from SkyEmu</source>
|
||||
<translation>Pingvinen från SkyEmu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="97"/>
|
||||
<source>Window icon</source>
|
||||
<translation>Fönsterikon</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="100"/>
|
||||
<source>Language</source>
|
||||
<translation>Språk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="102"/>
|
||||
<source>Show version on window title</source>
|
||||
<translation>Visa version på fönstertitel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="15"/>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber v%1</source>
|
||||
<translation>Alber v%1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="109"/>
|
||||
<source>Alber</source>
|
||||
<translation>Alber</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="114"/>
|
||||
<source>Remember window position</source>
|
||||
<translation>Kom ihåg fönstrets position</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="119"/>
|
||||
<source>General Settings</source>
|
||||
<translation>Allmänna inställningar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="130"/>
|
||||
<source>Browse...</source>
|
||||
<translation>Bläddra...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="134"/>
|
||||
<source>Select Directory</source>
|
||||
<translation>Välj katalog</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="146"/>
|
||||
<source>Default ROMs path</source>
|
||||
<translation>Standardsökväg för ROMar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="148"/>
|
||||
<source>Enable Discord RPC</source>
|
||||
<translation>Aktivera Discord RPC</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="152"/>
|
||||
<source>Use portable build</source>
|
||||
<translation>Använd portabelt bygge</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="156"/>
|
||||
<source>Print version in console output</source>
|
||||
<translation>Skriv ut versionen i konsolutmatningen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="161"/>
|
||||
<source>Graphics Settings</source>
|
||||
<translation>Grafikinställningar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="167"/>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="221"/>
|
||||
<source>Null</source>
|
||||
<translation>Null</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="168"/>
|
||||
<source>OpenGL</source>
|
||||
<translation>OpenGL</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="169"/>
|
||||
<source>Vulkan</source>
|
||||
<translation>Vulkan</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="175"/>
|
||||
<source>GPU renderer</source>
|
||||
<translation>GPU-rendering</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="177"/>
|
||||
<source>Enable Renderdoc</source>
|
||||
<translation>Aktivera Renderdoc</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="181"/>
|
||||
<source>Enable shader JIT</source>
|
||||
<translation>Aktivera shader JIT</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="185"/>
|
||||
<source>Enable VSync</source>
|
||||
<translation>Aktivera VSync</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="189"/>
|
||||
<source>Use ubershaders (No stutter, maybe slower)</source>
|
||||
<translation>Använda ubershaders (inga hackningar, kanske långsammare)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="193"/>
|
||||
<source>Accurate shader multiplication</source>
|
||||
<translation>Korrekt multiplicering av shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="197"/>
|
||||
<source>Accelerate shaders</source>
|
||||
<translation>Snabbare shaders</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="201"/>
|
||||
<source>Force shadergen when rendering lights</source>
|
||||
<translation>Tvinga fram shadergen vid rendering av ljus</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="212"/>
|
||||
<source>Light threshold for forcing shadergen</source>
|
||||
<translation>Ljuströskel för att tvinga shadergen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="215"/>
|
||||
<source>Audio Settings</source>
|
||||
<translation>Ljudinställningar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="222"/>
|
||||
<source>LLE</source>
|
||||
<translation>LLE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="223"/>
|
||||
<source>HLE</source>
|
||||
<translation>HLE</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="229"/>
|
||||
<source>DSP emulation</source>
|
||||
<translation>DSP-emulering</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="231"/>
|
||||
<source>Enable audio</source>
|
||||
<translation>Aktivera ljud</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="235"/>
|
||||
<source>Enable AAC audio</source>
|
||||
<translation>Aktivera AAC-ljud</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="239"/>
|
||||
<source>Print DSP firmware</source>
|
||||
<translation>Skriv ut firmware för DSP</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="243"/>
|
||||
<source>Mute audio device</source>
|
||||
<translation>Stäng av ljudet på audioenheten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="248"/>
|
||||
<source>Cubic</source>
|
||||
<translation>Kubisk</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="249"/>
|
||||
<source>Linear</source>
|
||||
<translation>Linjär</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="255"/>
|
||||
<source>Volume curve</source>
|
||||
<translation>Volymkurva</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="273"/>
|
||||
<source>Audio device volume</source>
|
||||
<translation>Ljudenhetens volym</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="276"/>
|
||||
<source>Battery Settings</source>
|
||||
<translation>Batteriinställningar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="288"/>
|
||||
<source>Battery percentage</source>
|
||||
<translation>Batteriprocent</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="290"/>
|
||||
<source>Charger plugged</source>
|
||||
<translation>Laddaren är ansluten</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="295"/>
|
||||
<source>SD Card Settings</source>
|
||||
<translation>Inställningar för SD-kort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="300"/>
|
||||
<source>Enable virtual SD card</source>
|
||||
<translation>Aktivera virtuellt SD-kort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="304"/>
|
||||
<source>Write protect virtual SD card</source>
|
||||
<translation>Skrivskydd för virtuellt SD-kort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>Interface</source>
|
||||
<translation>Gränssnitt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="309"/>
|
||||
<source>User Interface settings</source>
|
||||
<translation>Inställningar för användargränssnitt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General</source>
|
||||
<translation>Allmänt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="310"/>
|
||||
<source>General emulator settings</source>
|
||||
<translation>Allmänna inställningar för emulatorn</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics</source>
|
||||
<translation>Grafik</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="311"/>
|
||||
<source>Graphics emulation and output settings</source>
|
||||
<translation>Inställningar för grafikemulering och utdata</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio</source>
|
||||
<translation>Ljud</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="312"/>
|
||||
<source>Audio emulation and output settings</source>
|
||||
<translation>Inställningar för ljudemulering och utdata</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery</source>
|
||||
<translation>Batteri</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="313"/>
|
||||
<source>Battery emulation settings</source>
|
||||
<translation>Inställningar för batteriemulering</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card</source>
|
||||
<translation>SD-kort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/config_window.cpp" line="314"/>
|
||||
<source>SD Card emulation settings</source>
|
||||
<translation>Inställningar för emulering av SD-kort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="75"/>
|
||||
<source>Language change successful</source>
|
||||
<translation>Språkändringen lyckades</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="76"/>
|
||||
<source>Restart Panda3DS for the new language to be used.</source>
|
||||
<translation>Starta om Panda3DS för att det nya språket ska kunna användas.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="82"/>
|
||||
<source>Language change failed</source>
|
||||
<translation>Språkändringen misslyckades</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/translations.cpp" line="83"/>
|
||||
<source>The language you selected is not included in Panda3DS. If you're seeing this, someone messed up the language UI code...</source>
|
||||
<translation>Det språk du valde ingår inte i Panda3DS. Om du ser detta, har någon rört till koden för språkgränssnittet...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MainWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="20"/>
|
||||
<source>Alber</source>
|
||||
<translation>Alber</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="38"/>
|
||||
<source>File</source>
|
||||
<translation>Arkiv</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="39"/>
|
||||
<source>Emulation</source>
|
||||
<translation>Emulering</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="40"/>
|
||||
<source>Tools</source>
|
||||
<translation>Verktyg</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="41"/>
|
||||
<source>About</source>
|
||||
<translation>Om</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="44"/>
|
||||
<source>Load game</source>
|
||||
<translation>Läs in spel</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="45"/>
|
||||
<source>Load Lua script</source>
|
||||
<translation>Läs in Lua-skript</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="46"/>
|
||||
<source>Open Panda3DS folder</source>
|
||||
<translation>Öppna Panda3DS-mappen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="55"/>
|
||||
<source>Pause</source>
|
||||
<translation>Pausa</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="56"/>
|
||||
<source>Resume</source>
|
||||
<translation>Återuppta</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="57"/>
|
||||
<source>Reset</source>
|
||||
<translation>Starta om</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="58"/>
|
||||
<source>Configure</source>
|
||||
<translation>Konfigurera</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="64"/>
|
||||
<source>Dump RomFS</source>
|
||||
<translation>Dumpa RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="65"/>
|
||||
<source>Open Lua Editor</source>
|
||||
<translation>Öppna Lua-redigeraren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="66"/>
|
||||
<source>Open Cheats Editor</source>
|
||||
<translation>Öppna fuskredigeraren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="67"/>
|
||||
<source>Open Patch Window</source>
|
||||
<translation>Öppna patchfönstret</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="68"/>
|
||||
<source>Open Shader Editor</source>
|
||||
<translation>Öppna shader-redigeraren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="69"/>
|
||||
<source>Dump loaded DSP firmware</source>
|
||||
<translation>Dumpa inläst DSP-firmware</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="78"/>
|
||||
<source>About Panda3DS</source>
|
||||
<translation>Om Panda3DS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="195"/>
|
||||
<source>Select 3DS ROM to load</source>
|
||||
<translation>Välj 3DS ROM att läsa in</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="196"/>
|
||||
<source>Nintendo 3DS ROMs (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</source>
|
||||
<translation>Nintendo 3DS ROM (*.3ds *.cci *.cxi *.app *.ncch *.3dsx *.elf *.axf)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Select Lua script to load</source>
|
||||
<translation>Välj Lua-skript som ska läsas in</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="209"/>
|
||||
<source>Lua scripts (*.lua *.txt)</source>
|
||||
<translation>Lua-skript (*.lua *.txt)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="272"/>
|
||||
<source>Select folder to dump RomFS files to</source>
|
||||
<translation>Välj mapp för att dumpa RomFS-filer till</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="288"/>
|
||||
<source>Invalid format for RomFS dumping</source>
|
||||
<translation>Ogiltigt format för RomFS-dumpning</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="289"/>
|
||||
<source>The currently loaded app is not in a format that supports RomFS</source>
|
||||
<translation>Den aktuella appen är inte i ett format som stöder RomFS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="292"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="323"/>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="336"/>
|
||||
<source>OK</source>
|
||||
<translation>Ok</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS found</source>
|
||||
<translation>Ingen RomFS hittades</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="299"/>
|
||||
<source>No RomFS partition was found in the loaded app</source>
|
||||
<translation>Ingen RomFS-partition hittades i den inlästa appen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>Select file</source>
|
||||
<translation>Välj fil</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="305"/>
|
||||
<source>DSP firmware file (*.cdc)</source>
|
||||
<translation>DSP firmware-fil (*.cdc)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>No DSP firmware loaded</source>
|
||||
<translation>Ingen firmware för DSP inläst</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="320"/>
|
||||
<source>The currently loaded app has not uploaded a firmware to the DSP</source>
|
||||
<translation>Den aktuella appen har inte skickat upp någon firmware till DSP:n</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="331"/>
|
||||
<source>Failed to open output file</source>
|
||||
<translation>Misslyckades med att öppna utdatafilen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/main_window.cpp" line="332"/>
|
||||
<source>The currently loaded DSP firmware could not be written to the selected file. Please make sure you have permission to access this file</source>
|
||||
<translation>Den aktuella DSP-firmware som lästes in kunde inte skrivas till den valda filen. Kontrollera att du har behörighet att komma åt den här filen</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="15"/>
|
||||
<source>ROM patcher</source>
|
||||
<translation>ROM-patchare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="23"/>
|
||||
<source>Select input file</source>
|
||||
<translation>Välj inmatningsfil</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="24"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="36"/>
|
||||
<source>Select</source>
|
||||
<translation>Välj</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="35"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Select patch file</source>
|
||||
<translation>Välj patchfil</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="47"/>
|
||||
<source>Apply patch</source>
|
||||
<translation>Applicera patch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<source>Select file to patch</source>
|
||||
<translation>Välj fil som ska patchas</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="56"/>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>All files (*.*)</source>
|
||||
<translation>Alla filer (*.*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="63"/>
|
||||
<source>Patch files (*.ips *.ups *.bps)</source>
|
||||
<translation>Patch-filer (*.ips *.ups *.bps)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Paths not provided correctly</source>
|
||||
<translation>Sökvägar anges inte korrekt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="71"/>
|
||||
<source>Please provide paths for both the input file and the patch file</source>
|
||||
<translation>Ange sökvägar för både indatafilen och patchfilen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="80"/>
|
||||
<source>Select file</source>
|
||||
<translation>Välj fil</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No output path</source>
|
||||
<translation>Ingen sökväg för utmatning</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="84"/>
|
||||
<source>No path was provided for the output file, no patching was done</source>
|
||||
<translation>Ingen sökväg angavs för utdatafilen, ingen patchning gjordes</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown patch format</source>
|
||||
<translation>Okänt patchformat</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="99"/>
|
||||
<source>Unknown format for patch file. Currently IPS, UPS and BPS are supported</source>
|
||||
<translation>Okänt format för patchfil. För närvarande stöds IPS, UPS och BPS</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Failed to open input files</source>
|
||||
<translation>Misslyckades med att öppna indatafiler</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="108"/>
|
||||
<source>Make sure they're in a directory Panda3DS has access to</source>
|
||||
<translation>Se till att de finns i en katalog som Panda3DS har tillgång till</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Patching Success</source>
|
||||
<translation>Patchning lyckades</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="135"/>
|
||||
<source>Your file was patched successfully.</source>
|
||||
<translation>Din fil patchades.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="141"/>
|
||||
<source>Checksum mismatch</source>
|
||||
<translation>Kontrollsumman stämmer inte överens</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="142"/>
|
||||
<source>Patch was applied successfully but a checksum mismatch was detected. The input or output files might not be correct</source>
|
||||
<translation>Patchen applicerades men en avvikelse i kontrollsumman upptäcktes. Inmatnings- eller utdatafilerna kanske inte är korrekta</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>Patching error</source>
|
||||
<translation>Fel vid patchning</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="146"/>
|
||||
<source>An error occured while patching</source>
|
||||
<translation>Ett fel uppstod vid patchning</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PatchWindow::PatchWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/patch_window.cpp" line="153"/>
|
||||
<source>OK</source>
|
||||
<translation>Ok</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ShaderEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/shader_editor.cpp" line="26"/>
|
||||
<source>Reload shader</source>
|
||||
<translation>Läs om shader</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>TextEditorWindow</name>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="12"/>
|
||||
<source>Lua Editor</source>
|
||||
<translation>Lua-redigerare</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../../src/panda_qt/text_editor.cpp" line="27"/>
|
||||
<source>Load script</source>
|
||||
<translation>Läs in skript</translation>
|
||||
</message>
|
||||
</context>
|
||||
</TS>
|
45
include/PICA/draw_acceleration.hpp
Normal file
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace PICA {
|
||||
struct DrawAcceleration {
|
||||
static constexpr u32 maxAttribCount = 16;
|
||||
static constexpr u32 maxLoaderCount = 12;
|
||||
|
||||
struct AttributeInfo {
|
||||
u32 offset;
|
||||
u32 stride;
|
||||
|
||||
u8 type;
|
||||
u8 componentCount;
|
||||
|
||||
std::array<float, 4> fixedValue; // For fixed attributes
|
||||
};
|
||||
|
||||
struct Loader {
|
||||
// Data to upload for this loader
|
||||
u8* data;
|
||||
usize size;
|
||||
};
|
||||
|
||||
u8* indexBuffer;
|
||||
|
||||
// Minimum and maximum index in the index buffer for a draw call
|
||||
u16 minimumIndex, maximumIndex;
|
||||
u32 totalAttribCount;
|
||||
u32 totalLoaderCount;
|
||||
u32 enabledAttributeMask;
|
||||
u32 fixedAttributes;
|
||||
u32 vertexDataSize;
|
||||
|
||||
std::array<AttributeInfo, maxAttribCount> attributeInfo;
|
||||
std::array<Loader, maxLoaderCount> loaders;
|
||||
|
||||
bool canBeAccelerated;
|
||||
bool indexed;
|
||||
bool useShortIndices;
|
||||
};
|
||||
} // namespace PICA
|
|
@ -2,7 +2,7 @@
|
|||
#include "helpers.hpp"
|
||||
#include "vertex_loader_rec.hpp"
|
||||
|
||||
// Common file for our PICA JITs (From vertex config -> CPU assembly and from PICA shader -> CPU assembly)
|
||||
// Common file for our PICA JITs (From PICA shader -> CPU assembly)
|
||||
|
||||
namespace Dynapica {
|
||||
#ifdef PANDA3DS_DYNAPICA_SUPPORTED
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
#pragma once
|
||||
#include "PICA/shader.hpp"
|
||||
|
||||
#if defined(PANDA3DS_DYNAPICA_SUPPORTED) && defined(PANDA3DS_X64_HOST)
|
||||
#if defined(PANDA3DS_DYNAPICA_SUPPORTED) && (defined(PANDA3DS_X64_HOST) || defined(PANDA3DS_ARM64_HOST))
|
||||
#define PANDA3DS_SHADER_JIT_SUPPORTED
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef PANDA3DS_X64_HOST
|
||||
#include "shader_rec_emitter_x64.hpp"
|
||||
#elif defined(PANDA3DS_ARM64_HOST)
|
||||
#include "shader_rec_emitter_arm64.hpp"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
@ -20,8 +22,11 @@ class ShaderJIT {
|
|||
|
||||
ShaderCache cache;
|
||||
#endif
|
||||
bool accurateMul = false;
|
||||
|
||||
public:
|
||||
void setAccurateMul(bool value) { accurateMul = value; }
|
||||
|
||||
#ifdef PANDA3DS_SHADER_JIT_SUPPORTED
|
||||
// Call this before starting to process a batch of vertices
|
||||
// This will read the PICA config (uploaded shader and shader operand descriptors) and search if we've already compiled this shader
|
||||
|
@ -34,11 +39,11 @@ class ShaderJIT {
|
|||
static constexpr bool isAvailable() { return true; }
|
||||
#else
|
||||
void prepare(PICAShader& shaderUnit) {
|
||||
Helpers::panic("Vertex Loader JIT: Tried to run ShaderJIT::Prepare on platform that does not support shader jit");
|
||||
Helpers::panic("Shader JIT: Tried to run ShaderJIT::Prepare on platform that does not support shader jit");
|
||||
}
|
||||
|
||||
void run(PICAShader& shaderUnit) {
|
||||
Helpers::panic("Vertex Loader JIT: Tried to run ShaderJIT::Run on platform that does not support shader jit");
|
||||
Helpers::panic("Shader JIT: Tried to run ShaderJIT::Run on platform that does not support shader jit");
|
||||
}
|
||||
|
||||
// Define dummy callback. This should never be called if the shader JIT is not supported
|
||||
|
|
137
include/PICA/dynapica/shader_rec_emitter_arm64.hpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
#pragma once
|
||||
|
||||
// Only do anything if we're on an x64 target with JIT support enabled
|
||||
#if defined(PANDA3DS_DYNAPICA_SUPPORTED) && defined(PANDA3DS_ARM64_HOST)
|
||||
#include <array>
|
||||
#include <oaknut/code_block.hpp>
|
||||
#include <oaknut/oaknut.hpp>
|
||||
|
||||
#include "PICA/shader.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "logger.hpp"
|
||||
|
||||
class ShaderEmitter : private oaknut::CodeBlock, public oaknut::CodeGenerator {
|
||||
static constexpr size_t executableMemorySize = PICAShader::maxInstructionCount * 96; // How much executable memory to alloc for each shader
|
||||
// Allocate some extra space as padding for security purposes in the extremely unlikely occasion we manage to overflow the above size
|
||||
static constexpr size_t allocSize = executableMemorySize + 0x1000;
|
||||
|
||||
// If the swizzle field is this value then the swizzle pattern is .xyzw so we don't need a shuffle
|
||||
static constexpr uint noSwizzle = 0x1B;
|
||||
|
||||
using f24 = Floats::f24;
|
||||
using vec4f = std::array<f24, 4>;
|
||||
|
||||
// An array of labels (incl pointers) to each compiled (to x64) PICA instruction
|
||||
std::array<oaknut::Label, PICAShader::maxInstructionCount> instructionLabels;
|
||||
// A vector of PCs that can potentially return based on the state of the PICA callstack.
|
||||
// Filled before compiling a shader by scanning the code for call instructions
|
||||
std::vector<u32> returnPCs;
|
||||
|
||||
// An array of 128-bit masks for blending registers together to perform masked writes.
|
||||
// Eg for writing only the x and y components, the mask is 0x00000000'00000000'FFFFFFFF'FFFF
|
||||
oaknut::Label blendMasks;
|
||||
|
||||
u32 recompilerPC = 0; // PC the recompiler is currently recompiling @
|
||||
u32 loopLevel = 0; // The current loop nesting level (0 = not in a loop)
|
||||
|
||||
// Shows whether the loaded shader has any log2 and exp2 instructions
|
||||
bool codeHasLog2 = false;
|
||||
bool codeHasExp2 = false;
|
||||
// Whether to compile this shader using accurate, safe, non-IEEE multiplication (slow) or faster but less accurate mul
|
||||
bool useSafeMUL = false;
|
||||
|
||||
oaknut::Label log2Func, exp2Func;
|
||||
oaknut::Label emitLog2Func();
|
||||
oaknut::Label emitExp2Func();
|
||||
|
||||
// Emit a PICA200-compliant multiplication that handles "0 * inf = 0"
|
||||
void emitSafeMUL(oaknut::QReg src1, oaknut::QReg src2, oaknut::QReg scratch0);
|
||||
|
||||
template <typename T>
|
||||
T getLabelPointer(const oaknut::Label& label) {
|
||||
auto pointer = reinterpret_cast<u8*>(oaknut::CodeBlock::ptr()) + label.offset();
|
||||
return reinterpret_cast<T>(pointer);
|
||||
}
|
||||
|
||||
// Compile all instructions from [current recompiler PC, end)
|
||||
void compileUntil(const PICAShader& shaderUnit, u32 endPC);
|
||||
// Compile instruction "instr"
|
||||
void compileInstruction(const PICAShader& shaderUnit);
|
||||
|
||||
bool isCall(u32 instruction) {
|
||||
const u32 opcode = instruction >> 26;
|
||||
return (opcode == ShaderOpcodes::CALL) || (opcode == ShaderOpcodes::CALLC) || (opcode == ShaderOpcodes::CALLU);
|
||||
}
|
||||
|
||||
// Scan the shader code for call instructions to fill up the returnPCs vector before starting compilation
|
||||
// We also scan for log2/exp2 instructions to see whether to emit the relevant functions
|
||||
void scanCode(const PICAShader& shaderUnit);
|
||||
|
||||
// Load register with number "srcReg" indexed by index "idx" into the arm64 register "reg"
|
||||
template <int sourceIndex>
|
||||
void loadRegister(oaknut::QReg dest, const PICAShader& shader, u32 src, u32 idx, u32 operandDescriptor);
|
||||
void storeRegister(oaknut::QReg source, const PICAShader& shader, u32 dest, u32 operandDescriptor);
|
||||
|
||||
const vec4f& getSourceRef(const PICAShader& shader, u32 src);
|
||||
const vec4f& getDestRef(const PICAShader& shader, u32 dest);
|
||||
|
||||
// Check the value of the cmp register for instructions like ifc and callc
|
||||
// Result is returned in the zero flag. If the comparison is true then zero == 1, else zero == 0
|
||||
void checkCmpRegister(const PICAShader& shader, u32 instruction);
|
||||
|
||||
// Check the value of the bool uniform for instructions like ifu and callu
|
||||
// Result is returned in the zero flag. If the comparison is true then zero == 0, else zero == 1 (Opposite of checkCmpRegister)
|
||||
void checkBoolUniform(const PICAShader& shader, u32 instruction);
|
||||
|
||||
// Instruction recompilation functions
|
||||
void recADD(const PICAShader& shader, u32 instruction);
|
||||
void recCALL(const PICAShader& shader, u32 instruction);
|
||||
void recCALLC(const PICAShader& shader, u32 instruction);
|
||||
void recCALLU(const PICAShader& shader, u32 instruction);
|
||||
void recCMP(const PICAShader& shader, u32 instruction);
|
||||
void recDP3(const PICAShader& shader, u32 instruction);
|
||||
void recDP4(const PICAShader& shader, u32 instruction);
|
||||
void recDPH(const PICAShader& shader, u32 instruction);
|
||||
void recEMIT(const PICAShader& shader, u32 instruction);
|
||||
void recEND(const PICAShader& shader, u32 instruction);
|
||||
void recEX2(const PICAShader& shader, u32 instruction);
|
||||
void recFLR(const PICAShader& shader, u32 instruction);
|
||||
void recIFC(const PICAShader& shader, u32 instruction);
|
||||
void recIFU(const PICAShader& shader, u32 instruction);
|
||||
void recJMPC(const PICAShader& shader, u32 instruction);
|
||||
void recJMPU(const PICAShader& shader, u32 instruction);
|
||||
void recLG2(const PICAShader& shader, u32 instruction);
|
||||
void recLOOP(const PICAShader& shader, u32 instruction);
|
||||
void recMAD(const PICAShader& shader, u32 instruction);
|
||||
void recMAX(const PICAShader& shader, u32 instruction);
|
||||
void recMIN(const PICAShader& shader, u32 instruction);
|
||||
void recMOVA(const PICAShader& shader, u32 instruction);
|
||||
void recMOV(const PICAShader& shader, u32 instruction);
|
||||
void recMUL(const PICAShader& shader, u32 instruction);
|
||||
void recRCP(const PICAShader& shader, u32 instruction);
|
||||
void recRSQ(const PICAShader& shader, u32 instruction);
|
||||
void recSETEMIT(const PICAShader& shader, u32 instruction);
|
||||
void recSGE(const PICAShader& shader, u32 instruction);
|
||||
void recSLT(const PICAShader& shader, u32 instruction);
|
||||
|
||||
MAKE_LOG_FUNCTION(log, shaderJITLogger)
|
||||
|
||||
public:
|
||||
// Callback type used for instructions
|
||||
using InstructionCallback = const void (*)(PICAShader& shaderUnit);
|
||||
// Callback type used for the JIT prologue. This is what the caller will call
|
||||
using PrologueCallback = const void (*)(PICAShader& shaderUnit, InstructionCallback cb);
|
||||
|
||||
PrologueCallback prologueCb = nullptr;
|
||||
|
||||
// Initialize our emitter with "allocSize" bytes of memory allocated for the code buffer
|
||||
ShaderEmitter(bool useSafeMUL) : oaknut::CodeBlock(allocSize), oaknut::CodeGenerator(oaknut::CodeBlock::ptr()), useSafeMUL(useSafeMUL) {}
|
||||
|
||||
// PC must be a valid entrypoint here. It doesn't have that much overhead in this case, so we use std::array<>::at() to assert it does
|
||||
InstructionCallback getInstructionCallback(u32 pc) { return getLabelPointer<InstructionCallback>(instructionLabels.at(pc)); }
|
||||
|
||||
PrologueCallback getPrologueCallback() { return prologueCb; }
|
||||
void compile(const PICAShader& shaderUnit);
|
||||
};
|
||||
|
||||
#endif // arm64 recompiler check
|
|
@ -32,6 +32,8 @@ class ShaderEmitter : public Xbyak::CodeGenerator {
|
|||
Label negateVector;
|
||||
// Vector value of (1.0, 1.0, 1.0, 1.0) for SLT(i)/SGE(i)
|
||||
Label onesVector;
|
||||
// Vector value of (0xFF, 0xFF, 0xFF, 0) for setting the w component to 0 in DP3
|
||||
Label dp3Vector;
|
||||
|
||||
u32 recompilerPC = 0; // PC the recompiler is currently recompiling @
|
||||
u32 loopLevel = 0; // The current loop nesting level (0 = not in a loop)
|
||||
|
@ -43,12 +45,17 @@ class ShaderEmitter : public Xbyak::CodeGenerator {
|
|||
// Shows whether the loaded shader has any log2 and exp2 instructions
|
||||
bool codeHasLog2 = false;
|
||||
bool codeHasExp2 = false;
|
||||
// Whether to compile this shader using accurate, safe, non-IEEE multiplication (slow) or faster but less accurate mul
|
||||
bool useSafeMUL = false;
|
||||
|
||||
Xbyak::Label log2Func, exp2Func;
|
||||
Xbyak::Label emitLog2Func();
|
||||
Xbyak::Label emitExp2Func();
|
||||
Xbyak::util::Cpu cpuCaps;
|
||||
|
||||
// Emit a PICA200-compliant multiplication that handles "0 * inf = 0"
|
||||
void emitSafeMUL(Xbyak::Xmm src1, Xbyak::Xmm src2, Xbyak::Xmm scratch);
|
||||
|
||||
// Compile all instructions from [current recompiler PC, end)
|
||||
void compileUntil(const PICAShader& shaderUnit, u32 endPC);
|
||||
// Compile instruction "instr"
|
||||
|
@ -125,7 +132,7 @@ class ShaderEmitter : public Xbyak::CodeGenerator {
|
|||
PrologueCallback prologueCb = nullptr;
|
||||
|
||||
// Initialize our emitter with "allocSize" bytes of RWX memory
|
||||
ShaderEmitter() : Xbyak::CodeGenerator(allocSize) {
|
||||
ShaderEmitter(bool useSafeMUL) : Xbyak::CodeGenerator(allocSize), useSafeMUL(useSafeMUL) {
|
||||
cpuCaps = Xbyak::util::Cpu();
|
||||
|
||||
haveSSE4_1 = cpuCaps.has(Xbyak::util::Cpu::tSSE41);
|
||||
|
|
|
@ -1,17 +1,25 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
|
||||
#include "PICA/draw_acceleration.hpp"
|
||||
#include "PICA/dynapica/shader_rec.hpp"
|
||||
#include "PICA/float_types.hpp"
|
||||
#include "PICA/pica_vertex.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "PICA/shader_unit.hpp"
|
||||
#include "compiler_builtins.hpp"
|
||||
#include "config.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "renderer.hpp"
|
||||
|
||||
enum class ShaderExecMode {
|
||||
Interpreter, // Interpret shaders on the CPU
|
||||
JIT, // Recompile shaders to CPU machine code
|
||||
Hardware, // Recompiler shaders to host shaders and run them on the GPU
|
||||
};
|
||||
|
||||
class GPU {
|
||||
static constexpr u32 regNum = 0x300;
|
||||
static constexpr u32 extRegNum = 0x1000;
|
||||
|
@ -35,10 +43,16 @@ class GPU {
|
|||
|
||||
std::array<vec4f, 16> immediateModeAttributes; // Vertex attributes uploaded via immediate mode submission
|
||||
std::array<PICA::Vertex, 3> immediateModeVertices;
|
||||
|
||||
// Pointers for the output registers as arranged after GPUREG_VSH_OUTMAP_MASK is applied
|
||||
std::array<Floats::f24*, 16> vsOutputRegisters;
|
||||
// Previous value for GPUREG_VSH_OUTMAP_MASK
|
||||
u32 oldVsOutputMask;
|
||||
|
||||
uint immediateModeVertIndex;
|
||||
uint immediateModeAttrIndex; // Index of the immediate mode attribute we're uploading
|
||||
|
||||
template <bool indexed, bool useShaderJIT>
|
||||
template <bool indexed, ShaderExecMode mode>
|
||||
void drawArrays();
|
||||
|
||||
// Silly method of avoiding linking problems. TODO: Change to something less silly
|
||||
|
@ -74,6 +88,7 @@ class GPU {
|
|||
std::unique_ptr<Renderer> renderer;
|
||||
PICA::Vertex getImmediateModeVertex();
|
||||
|
||||
void getAcceleratedDrawInfo(PICA::DrawAcceleration& accel, bool indexed);
|
||||
public:
|
||||
// 256 entries per LUT with each LUT as its own row forming a 2D image 256 * LUT_COUNT
|
||||
// Encoded in PICA native format
|
||||
|
@ -85,9 +100,13 @@ class GPU {
|
|||
// Set to false by the renderer when the lighting_lut is uploaded ot the GPU
|
||||
bool lightingLUTDirty = false;
|
||||
|
||||
bool fogLUTDirty = false;
|
||||
std::array<uint32_t, 128> fogLUT;
|
||||
|
||||
GPU(Memory& mem, EmulatorConfig& config);
|
||||
void display() { renderer->display(); }
|
||||
void screenshot(const std::string& name) { renderer->screenshot(name); }
|
||||
void deinitGraphicsContext() { renderer->deinitGraphicsContext(); }
|
||||
|
||||
#if defined(PANDA3DS_FRONTEND_SDL)
|
||||
void initGraphicsContext(SDL_Window* window) { renderer->initGraphicsContext(window); }
|
||||
|
@ -156,13 +175,39 @@ class GPU {
|
|||
u32 index = paddr - PhysicalAddrs::VRAM;
|
||||
return (T*)&vram[index];
|
||||
} else [[unlikely]] {
|
||||
Helpers::panic("[GPU] Tried to access unknown physical address: %08X", paddr);
|
||||
Helpers::warn("[GPU] Tried to access unknown physical address: %08X", paddr);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Renderer* getRenderer() { return renderer.get(); }
|
||||
private:
|
||||
// GPU external registers
|
||||
// We have them in the end of the struct for cache locality reasons. Tl;dr we want the more commonly used things to be packed in the start
|
||||
// Of the struct, instead of externalRegs being in the middle
|
||||
ExternalRegisters externalRegs;
|
||||
|
||||
ALWAYS_INLINE void setVsOutputMask(u32 val) {
|
||||
val &= 0xffff;
|
||||
|
||||
// Avoid recomputing this if not necessary
|
||||
if (oldVsOutputMask != val) [[unlikely]] {
|
||||
oldVsOutputMask = val;
|
||||
|
||||
uint count = 0;
|
||||
// See which registers are actually enabled and ignore the disabled ones
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (val & 1) {
|
||||
vsOutputRegisters[count++] = &shaderUnit.vs.outputs[i][0];
|
||||
}
|
||||
|
||||
val >>= 1;
|
||||
}
|
||||
|
||||
// For the others, map the index to a vs output directly (TODO: What does hw actually do?)
|
||||
for (; count < 16; count++) {
|
||||
vsOutputRegisters[count] = &shaderUnit.vs.outputs[count][0];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
257
include/PICA/pica_frag_config.hpp
Normal file
|
@ -0,0 +1,257 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "PICA/pica_hash.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "bitfield.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace PICA {
|
||||
struct OutputConfig {
|
||||
union {
|
||||
u32 raw{};
|
||||
// Merge the enable + compare function into 1 field to avoid duplicate shaders
|
||||
// enable == off means a CompareFunction of Always
|
||||
BitField<0, 3, CompareFunction> alphaTestFunction;
|
||||
BitField<3, 1, u32> depthMapEnable;
|
||||
BitField<4, 4, LogicOpMode> logicOpMode;
|
||||
};
|
||||
};
|
||||
|
||||
struct TextureConfig {
|
||||
u32 texUnitConfig;
|
||||
u32 texEnvUpdateBuffer;
|
||||
|
||||
// There's 6 TEV stages, and each one is configured via 4 word-sized registers
|
||||
// (+ the constant color register, which we don't include here, otherwise we'd generate too many shaders)
|
||||
std::array<u32, 4 * 6> tevConfigs;
|
||||
};
|
||||
|
||||
struct FogConfig {
|
||||
union {
|
||||
u32 raw{};
|
||||
|
||||
BitField<0, 3, FogMode> mode;
|
||||
BitField<3, 1, u32> flipDepth;
|
||||
};
|
||||
};
|
||||
|
||||
struct Light {
|
||||
union {
|
||||
u16 raw;
|
||||
BitField<0, 3, u16> num;
|
||||
BitField<3, 1, u16> directional;
|
||||
BitField<4, 1, u16> twoSidedDiffuse;
|
||||
BitField<5, 1, u16> distanceAttenuationEnable;
|
||||
BitField<6, 1, u16> spotAttenuationEnable;
|
||||
BitField<7, 1, u16> geometricFactor0;
|
||||
BitField<8, 1, u16> geometricFactor1;
|
||||
BitField<9, 1, u16> shadowEnable;
|
||||
};
|
||||
};
|
||||
|
||||
struct LightingLUTConfig {
|
||||
union {
|
||||
u32 raw;
|
||||
BitField<0, 1, u32> enable;
|
||||
BitField<1, 1, u32> absInput;
|
||||
BitField<2, 3, u32> type;
|
||||
BitField<5, 3, u32> scale;
|
||||
};
|
||||
};
|
||||
|
||||
struct LightingConfig {
|
||||
union {
|
||||
u32 raw{};
|
||||
BitField<0, 1, u32> enable;
|
||||
BitField<1, 4, u32> lightNum;
|
||||
BitField<5, 2, u32> bumpMode;
|
||||
BitField<7, 2, u32> bumpSelector;
|
||||
BitField<9, 1, u32> bumpRenorm;
|
||||
BitField<10, 1, u32> clampHighlights;
|
||||
BitField<11, 4, u32> config;
|
||||
BitField<15, 1, u32> enablePrimaryAlpha;
|
||||
BitField<16, 1, u32> enableSecondaryAlpha;
|
||||
BitField<17, 1, u32> enableShadow;
|
||||
BitField<18, 1, u32> shadowPrimary;
|
||||
BitField<19, 1, u32> shadowSecondary;
|
||||
BitField<20, 1, u32> shadowInvert;
|
||||
BitField<21, 1, u32> shadowAlpha;
|
||||
BitField<22, 2, u32> shadowSelector;
|
||||
};
|
||||
|
||||
std::array<LightingLUTConfig, 7> luts{};
|
||||
|
||||
std::array<Light, 8> lights{};
|
||||
|
||||
LightingConfig(const std::array<u32, 0x300>& regs) {
|
||||
// Ignore lighting registers if it's disabled
|
||||
if ((regs[InternalRegs::LightingEnable] & 1) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const u32 config0 = regs[InternalRegs::LightConfig0];
|
||||
const u32 config1 = regs[InternalRegs::LightConfig1];
|
||||
const u32 totalLightCount = Helpers::getBits<0, 3>(regs[InternalRegs::LightNumber]) + 1;
|
||||
|
||||
enable = 1;
|
||||
lightNum = totalLightCount;
|
||||
|
||||
enableShadow = Helpers::getBit<0>(config0);
|
||||
if (enableShadow) [[unlikely]] {
|
||||
shadowPrimary = Helpers::getBit<16>(config0);
|
||||
shadowSecondary = Helpers::getBit<17>(config0);
|
||||
shadowInvert = Helpers::getBit<18>(config0);
|
||||
shadowAlpha = Helpers::getBit<19>(config0);
|
||||
shadowSelector = Helpers::getBits<24, 2>(config0);
|
||||
}
|
||||
|
||||
enablePrimaryAlpha = Helpers::getBit<2>(config0);
|
||||
enableSecondaryAlpha = Helpers::getBit<3>(config0);
|
||||
config = Helpers::getBits<4, 4>(config0);
|
||||
|
||||
bumpSelector = Helpers::getBits<22, 2>(config0);
|
||||
clampHighlights = Helpers::getBit<27>(config0);
|
||||
bumpMode = Helpers::getBits<28, 2>(config0);
|
||||
bumpRenorm = Helpers::getBit<30>(config0) ^ 1; // 0 = enable so flip it with xor
|
||||
|
||||
for (int i = 0; i < totalLightCount; i++) {
|
||||
auto& light = lights[i];
|
||||
light.num = (regs[InternalRegs::LightPermutation] >> (i * 4)) & 0x7;
|
||||
|
||||
const u32 lightConfig = regs[InternalRegs::Light0Config + 0x10 * light.num];
|
||||
light.directional = Helpers::getBit<0>(lightConfig);
|
||||
light.twoSidedDiffuse = Helpers::getBit<1>(lightConfig);
|
||||
light.geometricFactor0 = Helpers::getBit<2>(lightConfig);
|
||||
light.geometricFactor1 = Helpers::getBit<3>(lightConfig);
|
||||
|
||||
light.shadowEnable = ((config1 >> light.num) & 1) ^ 1; // This also does 0 = enabled
|
||||
light.spotAttenuationEnable = ((config1 >> (8 + light.num)) & 1) ^ 1; // Same here
|
||||
light.distanceAttenuationEnable = ((config1 >> (24 + light.num)) & 1) ^ 1; // Of course same here
|
||||
}
|
||||
|
||||
LightingLUTConfig& d0 = luts[Lights::LUT_D0];
|
||||
LightingLUTConfig& d1 = luts[Lights::LUT_D1];
|
||||
LightingLUTConfig& sp = luts[spotlightLutIndex];
|
||||
LightingLUTConfig& fr = luts[Lights::LUT_FR];
|
||||
LightingLUTConfig& rb = luts[Lights::LUT_RB];
|
||||
LightingLUTConfig& rg = luts[Lights::LUT_RG];
|
||||
LightingLUTConfig& rr = luts[Lights::LUT_RR];
|
||||
|
||||
d0.enable = Helpers::getBit<16>(config1) == 0;
|
||||
d1.enable = Helpers::getBit<17>(config1) == 0;
|
||||
fr.enable = Helpers::getBit<19>(config1) == 0;
|
||||
rb.enable = Helpers::getBit<20>(config1) == 0;
|
||||
rg.enable = Helpers::getBit<21>(config1) == 0;
|
||||
rr.enable = Helpers::getBit<22>(config1) == 0;
|
||||
sp.enable = 1;
|
||||
|
||||
const u32 lutAbs = regs[InternalRegs::LightLUTAbs];
|
||||
const u32 lutSelect = regs[InternalRegs::LightLUTSelect];
|
||||
const u32 lutScale = regs[InternalRegs::LightLUTScale];
|
||||
|
||||
if (d0.enable) {
|
||||
d0.absInput = Helpers::getBit<1>(lutAbs) == 0;
|
||||
d0.type = Helpers::getBits<0, 3>(lutSelect);
|
||||
d0.scale = Helpers::getBits<0, 3>(lutScale);
|
||||
}
|
||||
|
||||
if (d1.enable) {
|
||||
d1.absInput = Helpers::getBit<5>(lutAbs) == 0;
|
||||
d1.type = Helpers::getBits<4, 3>(lutSelect);
|
||||
d1.scale = Helpers::getBits<4, 3>(lutScale);
|
||||
}
|
||||
|
||||
sp.absInput = Helpers::getBit<9>(lutAbs) == 0;
|
||||
sp.type = Helpers::getBits<8, 3>(lutSelect);
|
||||
sp.scale = Helpers::getBits<8, 3>(lutScale);
|
||||
|
||||
if (fr.enable) {
|
||||
fr.absInput = Helpers::getBit<13>(lutAbs) == 0;
|
||||
fr.type = Helpers::getBits<12, 3>(lutSelect);
|
||||
fr.scale = Helpers::getBits<12, 3>(lutScale);
|
||||
}
|
||||
|
||||
if (rb.enable) {
|
||||
rb.absInput = Helpers::getBit<17>(lutAbs) == 0;
|
||||
rb.type = Helpers::getBits<16, 3>(lutSelect);
|
||||
rb.scale = Helpers::getBits<16, 3>(lutScale);
|
||||
}
|
||||
|
||||
if (rg.enable) {
|
||||
rg.absInput = Helpers::getBit<21>(lutAbs) == 0;
|
||||
rg.type = Helpers::getBits<20, 3>(lutSelect);
|
||||
rg.scale = Helpers::getBits<20, 3>(lutScale);
|
||||
}
|
||||
|
||||
if (rr.enable) {
|
||||
rr.absInput = Helpers::getBit<25>(lutAbs) == 0;
|
||||
rr.type = Helpers::getBits<24, 3>(lutSelect);
|
||||
rr.scale = Helpers::getBits<24, 3>(lutScale);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Config used for identifying unique fragment pipeline configurations
|
||||
struct FragmentConfig {
|
||||
OutputConfig outConfig;
|
||||
TextureConfig texConfig;
|
||||
FogConfig fogConfig;
|
||||
LightingConfig lighting;
|
||||
|
||||
bool operator==(const FragmentConfig& config) const {
|
||||
// Hash function and equality operator required by std::unordered_map
|
||||
return std::memcmp(this, &config, sizeof(FragmentConfig)) == 0;
|
||||
}
|
||||
|
||||
FragmentConfig(const std::array<u32, 0x300>& regs) : lighting(regs) {
|
||||
auto alphaTestConfig = regs[InternalRegs::AlphaTestConfig];
|
||||
auto alphaTestFunction = Helpers::getBits<4, 3>(alphaTestConfig);
|
||||
|
||||
outConfig.alphaTestFunction =
|
||||
(alphaTestConfig & 1) ? static_cast<PICA::CompareFunction>(alphaTestFunction) : PICA::CompareFunction::Always;
|
||||
outConfig.depthMapEnable = regs[InternalRegs::DepthmapEnable] & 1;
|
||||
|
||||
// Shows if blending is enabled. If it is not enabled, then logic ops are enabled instead
|
||||
const bool blendingEnabled = (regs[InternalRegs::ColourOperation] & (1 << 8)) != 0;
|
||||
outConfig.logicOpMode = blendingEnabled ? LogicOpMode::Copy : LogicOpMode(Helpers::getBits<0, 4>(regs[InternalRegs::LogicOp]));
|
||||
|
||||
texConfig.texUnitConfig = regs[InternalRegs::TexUnitCfg];
|
||||
texConfig.texEnvUpdateBuffer = regs[InternalRegs::TexEnvUpdateBuffer];
|
||||
|
||||
// Set up TEV stages. Annoyingly we can't just memcpy as the TEV registers are arranged like
|
||||
// {Source, Operand, Combiner, Color, Scale} and we want to skip the color register since it's uploaded via UBO
|
||||
#define setupTevStage(stage) \
|
||||
std::memcpy(&texConfig.tevConfigs[stage * 4], ®s[InternalRegs::TexEnv##stage##Source], 3 * sizeof(u32)); \
|
||||
texConfig.tevConfigs[stage * 4 + 3] = regs[InternalRegs::TexEnv##stage##Source + 4];
|
||||
|
||||
setupTevStage(0);
|
||||
setupTevStage(1);
|
||||
setupTevStage(2);
|
||||
setupTevStage(3);
|
||||
setupTevStage(4);
|
||||
setupTevStage(5);
|
||||
#undef setupTevStage
|
||||
|
||||
fogConfig.mode = (FogMode)Helpers::getBits<0, 3>(regs[InternalRegs::TexEnvUpdateBuffer]);
|
||||
|
||||
if (fogConfig.mode == FogMode::Fog) {
|
||||
fogConfig.flipDepth = Helpers::getBit<16>(regs[InternalRegs::TexEnvUpdateBuffer]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(
|
||||
std::has_unique_object_representations<OutputConfig>() && std::has_unique_object_representations<TextureConfig>() &&
|
||||
std::has_unique_object_representations<FogConfig>() && std::has_unique_object_representations<Light>()
|
||||
);
|
||||
} // namespace PICA
|
||||
|
||||
// Override std::hash for our fragment config class
|
||||
template <>
|
||||
struct std::hash<PICA::FragmentConfig> {
|
||||
std::size_t operator()(const PICA::FragmentConfig& config) const noexcept { return PICAHash::computeHash((const char*)&config, sizeof(config)); }
|
||||
};
|
47
include/PICA/pica_frag_uniforms.hpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace PICA {
|
||||
struct LightUniform {
|
||||
using vec3 = std::array<float, 3>;
|
||||
|
||||
// std140 requires vec3s be aligned to 16 bytes
|
||||
alignas(16) vec3 specular0;
|
||||
alignas(16) vec3 specular1;
|
||||
alignas(16) vec3 diffuse;
|
||||
alignas(16) vec3 ambient;
|
||||
alignas(16) vec3 position;
|
||||
alignas(16) vec3 spotlightDirection;
|
||||
|
||||
float distanceAttenuationBias;
|
||||
float distanceAttenuationScale;
|
||||
};
|
||||
|
||||
struct FragmentUniforms {
|
||||
using vec3 = std::array<float, 3>;
|
||||
using vec4 = std::array<float, 4>;
|
||||
static constexpr usize tevStageCount = 6;
|
||||
|
||||
s32 alphaReference;
|
||||
float depthScale;
|
||||
float depthOffset;
|
||||
|
||||
alignas(16) vec4 constantColors[tevStageCount];
|
||||
alignas(16) vec4 tevBufferColor;
|
||||
alignas(16) vec4 clipCoords;
|
||||
|
||||
// Note: We upload these as a u32 and decode on GPU.
|
||||
// Particularly the fog colour since fog is really uncommon and it doesn't matter if we decode on GPU.
|
||||
u32 globalAmbientLight;
|
||||
u32 fogColor;
|
||||
// NOTE: THIS MUST BE LAST so that if lighting is disabled we can potentially omit uploading it
|
||||
LightUniform lightUniforms[8];
|
||||
};
|
||||
|
||||
// Assert that lightUniforms is the last member of the structure
|
||||
static_assert(offsetof(FragmentUniforms, lightUniforms) + 8 * sizeof(LightUniform) == sizeof(FragmentUniforms));
|
||||
} // namespace PICA
|
275
include/PICA/pica_simd.hpp
Normal file
|
@ -0,0 +1,275 @@
|
|||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
|
||||
#include "compiler_builtins.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
#if defined(_M_AMD64) || defined(__x86_64__)
|
||||
#define PICA_SIMD_X64
|
||||
#include <immintrin.h>
|
||||
#elif defined(_M_ARM64) || defined(__aarch64__)
|
||||
#define PICA_SIMD_ARM64
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
// Optimized functions for analyzing PICA index buffers (Finding minimum and maximum index values inside them)
|
||||
namespace PICA::IndexBuffer {
|
||||
// Non-SIMD, portable algorithm
|
||||
template <bool useShortIndices>
|
||||
std::pair<u16, u16> analyzePortable(u8* indexBuffer, u32 vertexCount) {
|
||||
u16 minimumIndex = std::numeric_limits<u16>::max();
|
||||
u16 maximumIndex = 0;
|
||||
|
||||
// Calculate the minimum and maximum indices used in the index buffer, so we'll only upload them
|
||||
if constexpr (useShortIndices) {
|
||||
u16* indexBuffer16 = reinterpret_cast<u16*>(indexBuffer);
|
||||
|
||||
for (u32 i = 0; i < vertexCount; i++) {
|
||||
u16 index = indexBuffer16[i];
|
||||
minimumIndex = std::min(minimumIndex, index);
|
||||
maximumIndex = std::max(maximumIndex, index);
|
||||
}
|
||||
} else {
|
||||
for (u32 i = 0; i < vertexCount; i++) {
|
||||
u16 index = u16(indexBuffer[i]);
|
||||
minimumIndex = std::min(minimumIndex, index);
|
||||
maximumIndex = std::max(maximumIndex, index);
|
||||
}
|
||||
}
|
||||
|
||||
return {minimumIndex, maximumIndex};
|
||||
}
|
||||
|
||||
#ifdef PICA_SIMD_ARM64
|
||||
template <bool useShortIndices>
|
||||
ALWAYS_INLINE std::pair<u16, u16> analyzeNEON(u8* indexBuffer, u32 vertexCount) {
|
||||
// We process 16 bytes per iteration, which is 8 vertices if we're using u16 indices or 16 vertices if we're using u8 indices
|
||||
constexpr u32 vertsPerLoop = (useShortIndices) ? 8 : 16;
|
||||
|
||||
if (vertexCount < vertsPerLoop) {
|
||||
return analyzePortable<useShortIndices>(indexBuffer, vertexCount);
|
||||
}
|
||||
|
||||
u16 minimumIndex, maximumIndex;
|
||||
|
||||
if constexpr (useShortIndices) {
|
||||
// 16-bit indices
|
||||
uint16x8_t minima = vdupq_n_u16(0xffff);
|
||||
uint16x8_t maxima = vdupq_n_u16(0);
|
||||
|
||||
while (vertexCount >= vertsPerLoop) {
|
||||
const uint16x8_t data = vld1q_u16(reinterpret_cast<u16*>(indexBuffer));
|
||||
minima = vminq_u16(data, minima);
|
||||
maxima = vmaxq_u16(data, maxima);
|
||||
|
||||
indexBuffer += 16;
|
||||
vertexCount -= vertsPerLoop;
|
||||
}
|
||||
|
||||
// Do horizontal min/max operations to get the actual minimum and maximum from all the vertices we processed with SIMD
|
||||
// We want to gather the actual minimum and maximum in the line bottom lane of the minima/maxima vectors
|
||||
// uint16x4_t foldedMinima1 = vmin_u16(vget_high_u16(minima), vget_low_u16(minima));
|
||||
// uint16x4_t foldedMaxima1 = vmax_u16(vget_high_u16(maxima), vget_low_u16(maxima));
|
||||
|
||||
uint16x8_t foldedMinima1 = vpminq_u16(minima, minima);
|
||||
uint16x8_t foldedMinima2 = vpminq_u16(foldedMinima1, foldedMinima1);
|
||||
uint16x8_t foldedMinima3 = vpminq_u16(foldedMinima2, foldedMinima2);
|
||||
|
||||
uint16x8_t foldedMaxima1 = vpmaxq_u16(maxima, maxima);
|
||||
uint16x8_t foldedMaxima2 = vpmaxq_u16(foldedMaxima1, foldedMaxima1);
|
||||
uint16x8_t foldedMaxima3 = vpmaxq_u16(foldedMaxima2, foldedMaxima2);
|
||||
|
||||
minimumIndex = vgetq_lane_u16(foldedMinima3, 0);
|
||||
maximumIndex = vgetq_lane_u16(foldedMaxima3, 0);
|
||||
} else {
|
||||
// 8-bit indices
|
||||
uint8x16_t minima = vdupq_n_u8(0xff);
|
||||
uint8x16_t maxima = vdupq_n_u8(0);
|
||||
|
||||
while (vertexCount >= vertsPerLoop) {
|
||||
uint8x16_t data = vld1q_u8(indexBuffer);
|
||||
minima = vminq_u8(data, minima);
|
||||
maxima = vmaxq_u8(data, maxima);
|
||||
|
||||
indexBuffer += 16;
|
||||
vertexCount -= vertsPerLoop;
|
||||
}
|
||||
|
||||
// Do a similar horizontal min/max as in the u16 case, except now we're working uint8x16 instead of uint16x4 so we need 4 folds
|
||||
uint8x16_t foldedMinima1 = vpminq_u8(minima, minima);
|
||||
uint8x16_t foldedMinima2 = vpminq_u8(foldedMinima1, foldedMinima1);
|
||||
uint8x16_t foldedMinima3 = vpminq_u8(foldedMinima2, foldedMinima2);
|
||||
uint8x16_t foldedMinima4 = vpminq_u8(foldedMinima3, foldedMinima3);
|
||||
|
||||
uint8x16_t foldedMaxima1 = vpmaxq_u8(maxima, maxima);
|
||||
uint8x16_t foldedMaxima2 = vpmaxq_u8(foldedMaxima1, foldedMaxima1);
|
||||
uint8x16_t foldedMaxima3 = vpmaxq_u8(foldedMaxima2, foldedMaxima2);
|
||||
uint8x16_t foldedMaxima4 = vpmaxq_u8(foldedMaxima3, foldedMaxima3);
|
||||
|
||||
minimumIndex = u16(vgetq_lane_u8(foldedMinima4, 0));
|
||||
maximumIndex = u16(vgetq_lane_u8(foldedMaxima4, 0));
|
||||
}
|
||||
|
||||
// If any indices could not be processed cause the buffer size is not 16-byte aligned, process them the naive way
|
||||
// Calculate the minimum and maximum indices used in the index buffer, so we'll only upload them
|
||||
while (vertexCount > 0) {
|
||||
if constexpr (useShortIndices) {
|
||||
u16 index = *reinterpret_cast<u16*>(indexBuffer);
|
||||
minimumIndex = std::min(minimumIndex, index);
|
||||
maximumIndex = std::max(maximumIndex, index);
|
||||
indexBuffer += 2;
|
||||
} else {
|
||||
u16 index = u16(*indexBuffer++);
|
||||
minimumIndex = std::min(minimumIndex, index);
|
||||
maximumIndex = std::max(maximumIndex, index);
|
||||
}
|
||||
|
||||
vertexCount -= 1;
|
||||
}
|
||||
|
||||
return {minimumIndex, maximumIndex};
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(PICA_SIMD_X64) && (defined(__SSE4_1__) || defined(__AVX__))
|
||||
template <bool useShortIndices>
|
||||
ALWAYS_INLINE std::pair<u16, u16> analyzeSSE4_1(u8* indexBuffer, u32 vertexCount) {
|
||||
// We process 16 bytes per iteration, which is 8 vertices if we're using u16
|
||||
// indices or 16 vertices if we're using u8 indices
|
||||
constexpr u32 vertsPerLoop = (useShortIndices) ? 8 : 16;
|
||||
|
||||
if (vertexCount < vertsPerLoop) {
|
||||
return analyzePortable<useShortIndices>(indexBuffer, vertexCount);
|
||||
}
|
||||
|
||||
u16 minimumIndex, maximumIndex;
|
||||
|
||||
if constexpr (useShortIndices) {
|
||||
// Calculate the horizontal minimum/maximum value across an SSE vector of 16-bit unsigned integers.
|
||||
// Based on https://stackoverflow.com/a/22259607
|
||||
auto horizontalMin16 = [](__m128i vector) -> u16 { return u16(_mm_cvtsi128_si32(_mm_minpos_epu16(vector))); };
|
||||
|
||||
auto horizontalMax16 = [](__m128i vector) -> u16 {
|
||||
// We have an instruction to compute horizontal minimum but not maximum, so we use it.
|
||||
// To use it, we have to subtract each value from 0xFFFF (which we do with an xor), then execute a horizontal minimum
|
||||
__m128i flipped = _mm_xor_si128(vector, _mm_set_epi32(0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu));
|
||||
u16 min = u16(_mm_cvtsi128_si32(_mm_minpos_epu16(flipped)));
|
||||
return u16(min ^ 0xffff);
|
||||
};
|
||||
|
||||
// 16-bit indices
|
||||
// Initialize the minima vector to all FFs (So 0xFFFF for each 16-bit lane)
|
||||
// And the maxima vector to all 0s (0 for each 16-bit lane)
|
||||
__m128i minima = _mm_set_epi32(0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu);
|
||||
__m128i maxima = _mm_set_epi32(0, 0, 0, 0);
|
||||
|
||||
while (vertexCount >= vertsPerLoop) {
|
||||
const __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(indexBuffer));
|
||||
minima = _mm_min_epu16(data, minima);
|
||||
maxima = _mm_max_epu16(data, maxima);
|
||||
|
||||
indexBuffer += 16;
|
||||
vertexCount -= vertsPerLoop;
|
||||
}
|
||||
|
||||
minimumIndex = u16(horizontalMin16(minima));
|
||||
maximumIndex = u16(horizontalMax16(maxima));
|
||||
} else {
|
||||
// Calculate the horizontal minimum/maximum value across an SSE vector of 8-bit unsigned integers.
|
||||
// Based on https://stackoverflow.com/a/22259607
|
||||
auto horizontalMin8 = [](__m128i vector) -> u8 {
|
||||
vector = _mm_min_epu8(vector, _mm_shuffle_epi32(vector, _MM_SHUFFLE(3, 2, 3, 2)));
|
||||
vector = _mm_min_epu8(vector, _mm_shuffle_epi32(vector, _MM_SHUFFLE(1, 1, 1, 1)));
|
||||
vector = _mm_min_epu8(vector, _mm_shufflelo_epi16(vector, _MM_SHUFFLE(1, 1, 1, 1)));
|
||||
vector = _mm_min_epu8(vector, _mm_srli_epi16(vector, 8));
|
||||
return u8(_mm_cvtsi128_si32(vector));
|
||||
};
|
||||
|
||||
auto horizontalMax8 = [](__m128i vector) -> u8 {
|
||||
vector = _mm_max_epu8(vector, _mm_shuffle_epi32(vector, _MM_SHUFFLE(3, 2, 3, 2)));
|
||||
vector = _mm_max_epu8(vector, _mm_shuffle_epi32(vector, _MM_SHUFFLE(1, 1, 1, 1)));
|
||||
vector = _mm_max_epu8(vector, _mm_shufflelo_epi16(vector, _MM_SHUFFLE(1, 1, 1, 1)));
|
||||
vector = _mm_max_epu8(vector, _mm_srli_epi16(vector, 8));
|
||||
return u8(_mm_cvtsi128_si32(vector));
|
||||
};
|
||||
|
||||
// 8-bit indices
|
||||
// Initialize the minima vector to all FFs (So 0xFF for each 8-bit lane)
|
||||
// And the maxima vector to all 0s (0 for each 8-bit lane)
|
||||
__m128i minima = _mm_set_epi32(0xffffffffu, 0xffffffffu, 0xffffffffu, 0xffffffffu);
|
||||
__m128i maxima = _mm_set_epi32(0, 0, 0, 0);
|
||||
|
||||
while (vertexCount >= vertsPerLoop) {
|
||||
const __m128i data = _mm_loadu_si128(reinterpret_cast<const __m128i*>(indexBuffer));
|
||||
minima = _mm_min_epu8(data, minima);
|
||||
maxima = _mm_max_epu8(data, maxima);
|
||||
|
||||
indexBuffer += 16;
|
||||
vertexCount -= vertsPerLoop;
|
||||
}
|
||||
|
||||
minimumIndex = u16(horizontalMin8(minima));
|
||||
maximumIndex = u16(horizontalMax8(maxima));
|
||||
}
|
||||
|
||||
// If any indices could not be processed cause the buffer size
|
||||
// is not 16-byte aligned, process them the naive way
|
||||
// Calculate the minimum and maximum indices used in the index
|
||||
// buffer, so we'll only upload them
|
||||
while (vertexCount > 0) {
|
||||
if constexpr (useShortIndices) {
|
||||
u16 index = *reinterpret_cast<u16*>(indexBuffer);
|
||||
minimumIndex = std::min(minimumIndex, index);
|
||||
maximumIndex = std::max(maximumIndex, index);
|
||||
indexBuffer += 2;
|
||||
} else {
|
||||
u16 index = u16(*indexBuffer++);
|
||||
minimumIndex = std::min(minimumIndex, index);
|
||||
maximumIndex = std::max(maximumIndex, index);
|
||||
}
|
||||
|
||||
vertexCount -= 1;
|
||||
}
|
||||
|
||||
return {minimumIndex, maximumIndex};
|
||||
}
|
||||
#endif
|
||||
|
||||
// Analyzes a PICA index buffer to get the minimum and maximum indices in the
|
||||
// buffer, and returns them in a pair in the form [min, max]. Takes a template
|
||||
// parameter to decide whether the indices in the buffer are u8 or u16
|
||||
template <bool useShortIndices>
|
||||
std::pair<u16, u16> analyze(u8* indexBuffer, u32 vertexCount) {
|
||||
#if defined(PICA_SIMD_ARM64)
|
||||
return analyzeNEON<useShortIndices>(indexBuffer, vertexCount);
|
||||
#elif defined(PICA_SIMD_X64) && (defined(__SSE4_1__) || defined(__AVX__))
|
||||
// Annoyingly, MSVC refuses to define __SSE4_1__ even when we're building with AVX
|
||||
return analyzeSSE4_1<useShortIndices>(indexBuffer, vertexCount);
|
||||
#else
|
||||
return analyzePortable<useShortIndices>(indexBuffer, vertexCount);
|
||||
#endif
|
||||
}
|
||||
|
||||
// In some really unfortunate scenarios (eg Android Studio emulator), we don't have access to glDrawRangeElementsBaseVertex
|
||||
// So we need to subtract the base vertex index from every index in the index buffer ourselves
|
||||
// This is not really common, so we do it without SIMD for the moment, just to be able to run on Android Studio
|
||||
template <bool useShortIndices>
|
||||
void subtractBaseIndex(u8* indexBuffer, u32 indexCount, u16 baseIndex) {
|
||||
// Calculate the minimum and maximum indices used in the index buffer, so we'll only upload them
|
||||
if constexpr (useShortIndices) {
|
||||
u16* indexBuffer16 = reinterpret_cast<u16*>(indexBuffer);
|
||||
|
||||
for (u32 i = 0; i < indexCount; i++) {
|
||||
indexBuffer16[i] -= baseIndex;
|
||||
}
|
||||
} else {
|
||||
u8 baseIndex8 = u8(baseIndex);
|
||||
|
||||
for (u32 i = 0; i < indexCount; i++) {
|
||||
indexBuffer[i] -= baseIndex8;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace PICA::IndexBuffer
|
57
include/PICA/pica_vert_config.hpp
Normal file
|
@ -0,0 +1,57 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "PICA/pica_hash.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "PICA/shader.hpp"
|
||||
#include "bitfield.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace PICA {
|
||||
// Configuration struct used
|
||||
struct VertConfig {
|
||||
PICAHash::HashType shaderHash;
|
||||
PICAHash::HashType opdescHash;
|
||||
u32 entrypoint;
|
||||
|
||||
// PICA registers for configuring shader output->fragment semantic mapping
|
||||
std::array<u32, 7> outmaps{};
|
||||
u16 outputMask;
|
||||
u8 outputCount;
|
||||
bool usingUbershader;
|
||||
|
||||
// Pad to 56 bytes so that the compiler won't insert unnecessary padding, which in turn will affect our unordered_map lookup
|
||||
// As the padding will get hashed and memcmp'd...
|
||||
u32 pad{};
|
||||
|
||||
bool operator==(const VertConfig& config) const {
|
||||
// Hash function and equality operator required by std::unordered_map
|
||||
return std::memcmp(this, &config, sizeof(VertConfig)) == 0;
|
||||
}
|
||||
|
||||
VertConfig(PICAShader& shader, const std::array<u32, 0x300>& regs, bool usingUbershader) : usingUbershader(usingUbershader) {
|
||||
shaderHash = shader.getCodeHash();
|
||||
opdescHash = shader.getOpdescHash();
|
||||
entrypoint = shader.entrypoint;
|
||||
|
||||
outputCount = regs[PICA::InternalRegs::ShaderOutputCount] & 7;
|
||||
outputMask = regs[PICA::InternalRegs::VertexShaderOutputMask];
|
||||
for (int i = 0; i < outputCount; i++) {
|
||||
// Mask out unused bits
|
||||
outmaps[i] = regs[PICA::InternalRegs::ShaderOutmap0 + i] & 0x1F1F1F1F;
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace PICA
|
||||
|
||||
static_assert(sizeof(PICA::VertConfig) == 56);
|
||||
|
||||
// Override std::hash for our vertex config class
|
||||
template <>
|
||||
struct std::hash<PICA::VertConfig> {
|
||||
std::size_t operator()(const PICA::VertConfig& config) const noexcept { return PICAHash::computeHash((const char*)&config, sizeof(config)); }
|
||||
};
|
|
@ -1,7 +1,8 @@
|
|||
#pragma once
|
||||
#include "PICA/float_types.hpp"
|
||||
#include <array>
|
||||
|
||||
#include "PICA/float_types.hpp"
|
||||
|
||||
namespace PICA {
|
||||
// A representation of the output vertex as it comes out of the vertex shader, with padding and all
|
||||
struct Vertex {
|
||||
|
|
|
@ -51,6 +51,18 @@ namespace PICA {
|
|||
#undef defineTexEnv
|
||||
// clang-format on
|
||||
|
||||
// Fog registers
|
||||
FogColor = 0xE1,
|
||||
FogLUTIndex = 0xE6,
|
||||
FogLUTData0 = 0xE8,
|
||||
FogLUTData1 = 0xE9,
|
||||
FogLUTData2 = 0xEA,
|
||||
FogLUTData3 = 0xEB,
|
||||
FogLUTData4 = 0xEC,
|
||||
FogLUTData5 = 0xED,
|
||||
FogLUTData6 = 0xEE,
|
||||
FogLUTData7 = 0xEF,
|
||||
|
||||
// Framebuffer registers
|
||||
ColourOperation = 0x100,
|
||||
BlendFunc = 0x101,
|
||||
|
@ -67,7 +79,29 @@ namespace PICA {
|
|||
ColourBufferLoc = 0x11D,
|
||||
FramebufferSize = 0x11E,
|
||||
|
||||
//LightingRegs
|
||||
// Lighting registers
|
||||
LightingEnable = 0x8F,
|
||||
Light0Specular0 = 0x140,
|
||||
Light0Specular1 = 0x141,
|
||||
Light0Diffuse = 0x142,
|
||||
Light0Ambient = 0x143,
|
||||
Light0XY = 0x144,
|
||||
Light0Z = 0x145,
|
||||
Light0SpotlightXY = 0x146,
|
||||
Light0SpotlightZ = 0x147,
|
||||
Light0Config = 0x149,
|
||||
Light0AttenuationBias = 0x14A,
|
||||
Light0AttenuationScale = 0x14B,
|
||||
|
||||
LightGlobalAmbient = 0x1C0,
|
||||
LightNumber = 0x1C2,
|
||||
LightConfig0 = 0x1C3,
|
||||
LightConfig1 = 0x1C4,
|
||||
LightPermutation = 0x1D9,
|
||||
LightLUTAbs = 0x1D0,
|
||||
LightLUTSelect = 0x1D1,
|
||||
LightLUTScale = 0x1D2,
|
||||
|
||||
LightingLUTIndex = 0x01C5,
|
||||
LightingLUTData0 = 0x01C8,
|
||||
LightingLUTData1 = 0x01C9,
|
||||
|
@ -143,6 +177,7 @@ namespace PICA {
|
|||
VertexIntUniform3 = 0x2B4,
|
||||
|
||||
VertexShaderEntrypoint = 0x2BA,
|
||||
VertexShaderOutputMask = 0x2BD,
|
||||
VertexShaderTransferEnd = 0x2BF,
|
||||
VertexFloatUniformIndex = 0x2C0,
|
||||
VertexFloatUniformData0 = 0x2C1,
|
||||
|
@ -230,7 +265,8 @@ namespace PICA {
|
|||
enum : u32 {
|
||||
LUT_D0 = 0,
|
||||
LUT_D1,
|
||||
LUT_FR,
|
||||
// LUT 2 is not used, the emulator internally uses it for referring to the current source's spotlight in shaders
|
||||
LUT_FR = 0x3,
|
||||
LUT_RB,
|
||||
LUT_RG,
|
||||
LUT_RR,
|
||||
|
@ -254,6 +290,11 @@ namespace PICA {
|
|||
};
|
||||
}
|
||||
|
||||
// There's actually 8 different LUTs (SP0-SP7), one for each light with different indices (8-15)
|
||||
// We use an unused LUT value for "this light source's spotlight" instead and figure out which light source to use in compileLutLookup
|
||||
// This is particularly intuitive in several places, such as checking if a LUT is enabled
|
||||
static constexpr int spotlightLutIndex = 2;
|
||||
|
||||
enum class TextureFmt : u32 {
|
||||
RGBA8 = 0x0,
|
||||
RGB8 = 0x1,
|
||||
|
@ -344,4 +385,156 @@ namespace PICA {
|
|||
GeometryPrimitive = 3,
|
||||
};
|
||||
|
||||
enum class CompareFunction : u32 {
|
||||
Never = 0,
|
||||
Always = 1,
|
||||
Equal = 2,
|
||||
NotEqual = 3,
|
||||
Less = 4,
|
||||
LessOrEqual = 5,
|
||||
Greater = 6,
|
||||
GreaterOrEqual = 7,
|
||||
};
|
||||
|
||||
enum class LogicOpMode : u32 {
|
||||
Clear = 0,
|
||||
And = 1,
|
||||
ReverseAnd = 2,
|
||||
Copy = 3,
|
||||
Set = 4,
|
||||
InvertedCopy = 5,
|
||||
Nop = 6,
|
||||
Invert = 7,
|
||||
Nand = 8,
|
||||
Or = 9,
|
||||
Nor = 10,
|
||||
Xor = 11,
|
||||
Equiv = 12,
|
||||
InvertedAnd = 13,
|
||||
ReverseOr = 14,
|
||||
InvertedOr = 15,
|
||||
};
|
||||
|
||||
enum class FogMode : u32 {
|
||||
Disabled = 0,
|
||||
Fog = 5,
|
||||
Gas = 7,
|
||||
};
|
||||
|
||||
struct TexEnvConfig {
|
||||
enum class Source : u8 {
|
||||
PrimaryColor = 0x0,
|
||||
PrimaryFragmentColor = 0x1,
|
||||
SecondaryFragmentColor = 0x2,
|
||||
Texture0 = 0x3,
|
||||
Texture1 = 0x4,
|
||||
Texture2 = 0x5,
|
||||
Texture3 = 0x6,
|
||||
// TODO: Inbetween values are unknown
|
||||
PreviousBuffer = 0xD,
|
||||
Constant = 0xE,
|
||||
Previous = 0xF,
|
||||
};
|
||||
|
||||
enum class ColorOperand : u8 {
|
||||
SourceColor = 0x0,
|
||||
OneMinusSourceColor = 0x1,
|
||||
SourceAlpha = 0x2,
|
||||
OneMinusSourceAlpha = 0x3,
|
||||
SourceRed = 0x4,
|
||||
OneMinusSourceRed = 0x5,
|
||||
// TODO: Inbetween values are unknown
|
||||
SourceGreen = 0x8,
|
||||
OneMinusSourceGreen = 0x9,
|
||||
// Inbetween values are unknown
|
||||
SourceBlue = 0xC,
|
||||
OneMinusSourceBlue = 0xD,
|
||||
};
|
||||
|
||||
enum class AlphaOperand : u8 {
|
||||
SourceAlpha = 0x0,
|
||||
OneMinusSourceAlpha = 0x1,
|
||||
SourceRed = 0x2,
|
||||
OneMinusSourceRed = 0x3,
|
||||
SourceGreen = 0x4,
|
||||
OneMinusSourceGreen = 0x5,
|
||||
SourceBlue = 0x6,
|
||||
OneMinusSourceBlue = 0x7,
|
||||
};
|
||||
|
||||
enum class Operation : u8 {
|
||||
Replace = 0,
|
||||
Modulate = 1,
|
||||
Add = 2,
|
||||
AddSigned = 3,
|
||||
Lerp = 4,
|
||||
Subtract = 5,
|
||||
Dot3RGB = 6,
|
||||
Dot3RGBA = 7,
|
||||
MultiplyAdd = 8,
|
||||
AddMultiply = 9,
|
||||
};
|
||||
|
||||
// RGB sources
|
||||
Source colorSource1, colorSource2, colorSource3;
|
||||
// Alpha sources
|
||||
Source alphaSource1, alphaSource2, alphaSource3;
|
||||
|
||||
// RGB operands
|
||||
ColorOperand colorOperand1, colorOperand2, colorOperand3;
|
||||
// Alpha operands
|
||||
AlphaOperand alphaOperand1, alphaOperand2, alphaOperand3;
|
||||
|
||||
// Texture environment operations for this stage
|
||||
Operation colorOp, alphaOp;
|
||||
|
||||
u32 constColor;
|
||||
|
||||
private:
|
||||
// These are the only private members since their value doesn't actually reflect the scale
|
||||
// So we make them public so we'll always use the appropriate member functions instead
|
||||
u8 colorScale;
|
||||
u8 alphaScale;
|
||||
|
||||
public:
|
||||
// Create texture environment object from TEV registers
|
||||
TexEnvConfig(u32 source, u32 operand, u32 combiner, u32 color, u32 scale) : constColor(color) {
|
||||
colorSource1 = Helpers::getBits<0, 4, Source>(source);
|
||||
colorSource2 = Helpers::getBits<4, 4, Source>(source);
|
||||
colorSource3 = Helpers::getBits<8, 4, Source>(source);
|
||||
|
||||
alphaSource1 = Helpers::getBits<16, 4, Source>(source);
|
||||
alphaSource2 = Helpers::getBits<20, 4, Source>(source);
|
||||
alphaSource3 = Helpers::getBits<24, 4, Source>(source);
|
||||
|
||||
colorOperand1 = Helpers::getBits<0, 4, ColorOperand>(operand);
|
||||
colorOperand2 = Helpers::getBits<4, 4, ColorOperand>(operand);
|
||||
colorOperand3 = Helpers::getBits<8, 4, ColorOperand>(operand);
|
||||
|
||||
alphaOperand1 = Helpers::getBits<12, 3, AlphaOperand>(operand);
|
||||
alphaOperand2 = Helpers::getBits<16, 3, AlphaOperand>(operand);
|
||||
alphaOperand3 = Helpers::getBits<20, 3, AlphaOperand>(operand);
|
||||
|
||||
colorOp = Helpers::getBits<0, 4, Operation>(combiner);
|
||||
alphaOp = Helpers::getBits<16, 4, Operation>(combiner);
|
||||
|
||||
colorScale = Helpers::getBits<0, 2>(scale);
|
||||
alphaScale = Helpers::getBits<16, 2>(scale);
|
||||
}
|
||||
|
||||
u32 getColorScale() { return (colorScale <= 2) ? (1 << colorScale) : 1; }
|
||||
u32 getAlphaScale() { return (alphaScale <= 2) ? (1 << alphaScale) : 1; }
|
||||
|
||||
bool isPassthroughStage() {
|
||||
// clang-format off
|
||||
// Thank you to the Citra dev that wrote this out
|
||||
return (
|
||||
colorOp == Operation::Replace && alphaOp == Operation::Replace &&
|
||||
colorSource1 == Source::Previous && alphaSource1 == Source::Previous &&
|
||||
colorOperand1 == ColorOperand::SourceColor && alphaOperand1 == AlphaOperand::SourceAlpha &&
|
||||
getColorScale() == 1 && getAlphaScale() == 1
|
||||
);
|
||||
// clang-format on
|
||||
}
|
||||
};
|
||||
} // namespace PICA
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
|
||||
#include "PICA/float_types.hpp"
|
||||
|
@ -21,7 +23,7 @@ namespace ShaderOpcodes {
|
|||
DST = 0x04,
|
||||
EX2 = 0x05,
|
||||
LG2 = 0x06,
|
||||
LIT = 0x07,
|
||||
LITP = 0x07,
|
||||
MUL = 0x08,
|
||||
SGE = 0x09,
|
||||
SLT = 0x0A,
|
||||
|
@ -56,6 +58,10 @@ namespace ShaderOpcodes {
|
|||
};
|
||||
}
|
||||
|
||||
namespace PICA::ShaderGen {
|
||||
class ShaderDecompiler;
|
||||
};
|
||||
|
||||
// Note: All PICA f24 vec4 registers must have the alignas(16) specifier to make them easier to access in SSE/NEON code in the JIT
|
||||
class PICAShader {
|
||||
using f24 = Floats::f24;
|
||||
|
@ -90,14 +96,22 @@ class PICAShader {
|
|||
public:
|
||||
// These are placed close to the temp registers and co because it helps the JIT generate better code
|
||||
u32 entrypoint = 0; // Initial shader PC
|
||||
u32 boolUniform;
|
||||
std::array<std::array<u8, 4>, 4> intUniforms;
|
||||
|
||||
// We want these registers in this order & with this alignment for uploading them directly to a UBO
|
||||
// When emulating shaders on the GPU. Plus this alignment for float uniforms is necessary for doing SIMD in the shader->CPU recompilers.
|
||||
alignas(16) std::array<vec4f, 96> floatUniforms;
|
||||
alignas(16) std::array<std::array<u8, 4>, 4> intUniforms;
|
||||
u32 boolUniform;
|
||||
|
||||
alignas(16) std::array<vec4f, 16> fixedAttributes; // Fixed vertex attributes
|
||||
alignas(16) std::array<vec4f, 16> inputs; // Attributes passed to the shader
|
||||
alignas(16) std::array<vec4f, 16> outputs;
|
||||
alignas(16) vec4f dummy = vec4f({f24::zero(), f24::zero(), f24::zero(), f24::zero()}); // Dummy register used by the JIT
|
||||
|
||||
// We use a hashmap for matching 3DS shaders to their equivalent compiled code in our shader cache in the shader JIT
|
||||
// We choose our hash type to be a 64-bit integer by default, as the collision chance is very tiny and generating it is decently optimal
|
||||
// Ideally we want to be able to support multiple different types of hash depending on compilation settings, but let's get this working first
|
||||
using Hash = PICAHash::HashType;
|
||||
|
||||
protected:
|
||||
std::array<u32, 128> operandDescriptors;
|
||||
|
@ -116,20 +130,20 @@ class PICAShader {
|
|||
std::array<CallInfo, 4> callInfo;
|
||||
ShaderType type;
|
||||
|
||||
// We use a hashmap for matching 3DS shaders to their equivalent compiled code in our shader cache in the shader JIT
|
||||
// We choose our hash type to be a 64-bit integer by default, as the collision chance is very tiny and generating it is decently optimal
|
||||
// Ideally we want to be able to support multiple different types of hash depending on compilation settings, but let's get this working first
|
||||
using Hash = PICAHash::HashType;
|
||||
|
||||
Hash lastCodeHash = 0; // Last hash computed for the shader code (Used for the JIT caching mechanism)
|
||||
Hash lastOpdescHash = 0; // Last hash computed for the operand descriptors (Also used for the JIT)
|
||||
|
||||
public:
|
||||
bool uniformsDirty = false;
|
||||
|
||||
protected:
|
||||
bool codeHashDirty = false;
|
||||
bool opdescHashDirty = false;
|
||||
|
||||
// Add these as friend classes for the JIT so it has access to all important state
|
||||
friend class ShaderJIT;
|
||||
friend class ShaderEmitter;
|
||||
friend class PICA::ShaderGen::ShaderDecompiler;
|
||||
|
||||
vec4f getSource(u32 source);
|
||||
vec4f& getDest(u32 dest);
|
||||
|
@ -151,6 +165,7 @@ class PICAShader {
|
|||
void jmpc(u32 instruction);
|
||||
void jmpu(u32 instruction);
|
||||
void lg2(u32 instruction);
|
||||
void litp(u32 instruction);
|
||||
void loop(u32 instruction);
|
||||
void mad(u32 instruction);
|
||||
void madi(u32 instruction);
|
||||
|
@ -220,13 +235,9 @@ class PICAShader {
|
|||
public:
|
||||
static constexpr size_t maxInstructionCount = 4096;
|
||||
std::array<u32, maxInstructionCount> loadedShader; // Currently loaded & active shader
|
||||
std::array<u32, maxInstructionCount> bufferedShader; // Shader to be transferred when the SH_CODETRANSFER_END reg gets written to
|
||||
|
||||
PICAShader(ShaderType type) : type(type) {}
|
||||
|
||||
// Theese functions are in the header to be inlined more easily, though with LTO I hope I'll be able to move them
|
||||
void finalize() { std::memcpy(&loadedShader[0], &bufferedShader[0], 4096 * sizeof(u32)); }
|
||||
|
||||
void setBufferIndex(u32 index) { bufferIndex = index & 0xfff; }
|
||||
void setOpDescriptorIndex(u32 index) { opDescriptorIndex = index & 0x7f; }
|
||||
|
||||
|
@ -235,7 +246,7 @@ class PICAShader {
|
|||
Helpers::panic("o no, shader upload overflew");
|
||||
}
|
||||
|
||||
bufferedShader[bufferIndex++] = word;
|
||||
loadedShader[bufferIndex++] = word;
|
||||
bufferIndex &= 0xfff;
|
||||
|
||||
codeHashDirty = true; // Signal the JIT if necessary that the program hash has potentially changed
|
||||
|
@ -256,14 +267,16 @@ class PICAShader {
|
|||
|
||||
void uploadFloatUniform(u32 word) {
|
||||
floatUniformBuffer[floatUniformWordCount++] = word;
|
||||
if (floatUniformIndex >= 96) {
|
||||
Helpers::panic("[PICA] Tried to write float uniform %d", floatUniformIndex);
|
||||
}
|
||||
|
||||
if ((f32UniformTransfer && floatUniformWordCount >= 4) || (!f32UniformTransfer && floatUniformWordCount >= 3)) {
|
||||
vec4f& uniform = floatUniforms[floatUniformIndex++];
|
||||
floatUniformWordCount = 0;
|
||||
|
||||
// Check if the program tries to upload to a non-existent uniform, and empty the queue without writing in that case
|
||||
if (floatUniformIndex >= 96) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
vec4f& uniform = floatUniforms[floatUniformIndex++];
|
||||
|
||||
if (f32UniformTransfer) {
|
||||
uniform[0] = f24::fromFloat32(*(float*)&floatUniformBuffer[3]);
|
||||
uniform[1] = f24::fromFloat32(*(float*)&floatUniformBuffer[2]);
|
||||
|
@ -275,6 +288,7 @@ class PICAShader {
|
|||
uniform[2] = f24::fromRaw(((floatUniformBuffer[0] & 0xff) << 16) | (floatUniformBuffer[1] >> 16));
|
||||
uniform[3] = f24::fromRaw(floatUniformBuffer[0] >> 8);
|
||||
}
|
||||
uniformsDirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -286,6 +300,12 @@ class PICAShader {
|
|||
u[1] = getBits<8, 8>(word);
|
||||
u[2] = getBits<16, 8>(word);
|
||||
u[3] = getBits<24, 8>(word);
|
||||
uniformsDirty = true;
|
||||
}
|
||||
|
||||
void uploadBoolUniform(u32 value) {
|
||||
boolUniform = value;
|
||||
uniformsDirty = true;
|
||||
}
|
||||
|
||||
void run();
|
||||
|
@ -293,4 +313,13 @@ class PICAShader {
|
|||
|
||||
Hash getCodeHash();
|
||||
Hash getOpdescHash();
|
||||
};
|
||||
|
||||
// Returns how big the PICA uniforms are combined. Used for hw accelerated shaders where we upload the uniforms to our GPU.
|
||||
static constexpr usize totalUniformSize() { return sizeof(floatUniforms) + sizeof(intUniforms) + sizeof(boolUniform); }
|
||||
void* getUniformPointer() { return static_cast<void*>(&floatUniforms); }
|
||||
};
|
||||
|
||||
static_assert(
|
||||
offsetof(PICAShader, intUniforms) == offsetof(PICAShader, floatUniforms) + 96 * sizeof(float) * 4 &&
|
||||
offsetof(PICAShader, boolUniform) == offsetof(PICAShader, intUniforms) + 4 * sizeof(u8) * 4
|
||||
);
|
131
include/PICA/shader_decompiler.hpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
#pragma once
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "PICA/shader.hpp"
|
||||
#include "PICA/shader_gen_types.hpp"
|
||||
|
||||
struct EmulatorConfig;
|
||||
|
||||
namespace PICA::ShaderGen {
|
||||
// Control flow analysis is partially based on
|
||||
// https://github.com/PabloMK7/citra/blob/d0179559466ff09731d74474322ee880fbb44b00/src/video_core/shader/generator/glsl_shader_decompiler.cpp#L33
|
||||
struct ControlFlow {
|
||||
// A continuous range of addresses
|
||||
struct AddressRange {
|
||||
u32 start, end;
|
||||
AddressRange(u32 start, u32 end) : start(start), end(end) {}
|
||||
|
||||
// Use lexicographic comparison for functions in order to sort them in a set
|
||||
bool operator<(const AddressRange& other) const { return std::tie(start, end) < std::tie(other.start, other.end); }
|
||||
};
|
||||
|
||||
struct Function {
|
||||
using Labels = std::set<u32>;
|
||||
|
||||
enum class ExitMode {
|
||||
Unknown, // Can't guarantee whether we'll exit properly, fall back to CPU shaders (can happen with jmp shenanigans)
|
||||
AlwaysReturn, // All paths reach the return point.
|
||||
Conditional, // One or more code paths reach the return point or an END instruction conditionally.
|
||||
AlwaysEnd, // All paths reach an END instruction.
|
||||
};
|
||||
|
||||
u32 start; // Starting PC of the function
|
||||
u32 end; // End PC of the function
|
||||
Labels outLabels{}; // Labels this function can "goto" (jump) to
|
||||
ExitMode exitMode = ExitMode::Unknown;
|
||||
|
||||
explicit Function(u32 start, u32 end) : start(start), end(end) {}
|
||||
bool operator<(const Function& other) const { return AddressRange(start, end) < AddressRange(other.start, other.end); }
|
||||
|
||||
std::string getIdentifier() const { return fmt::format("fn_{}_{}", start, end); }
|
||||
// To handle weird control flow, we have to return from each function a bool that indicates whether or not the shader reached an end
|
||||
// instruction and should thus terminate. This is necessary for games like Rayman and Gravity Falls, which have "END" instructions called
|
||||
// from within functions deep in the callstack
|
||||
std::string getForwardDecl() const { return fmt::format("bool fn_{}_{}();\n", start, end); }
|
||||
std::string getCallStatement() const { return fmt::format("fn_{}_{}()", start, end); }
|
||||
};
|
||||
|
||||
std::set<Function> functions{};
|
||||
std::map<AddressRange, Function::ExitMode> exitMap{};
|
||||
|
||||
// Tells us whether analysis of the shader we're trying to compile failed, in which case we'll need to fail back to shader emulation
|
||||
// On the CPU
|
||||
bool analysisFailed = false;
|
||||
|
||||
// This will recursively add all functions called by the function too, as analyzeFunction will call addFunction on control flow instructions
|
||||
const Function* addFunction(const PICAShader& shader, u32 start, u32 end) {
|
||||
auto searchIterator = functions.find(Function(start, end));
|
||||
if (searchIterator != functions.end()) {
|
||||
return &(*searchIterator);
|
||||
}
|
||||
|
||||
// Add this function and analyze it if it doesn't already exist
|
||||
Function function(start, end);
|
||||
function.exitMode = analyzeFunction(shader, start, end, function.outLabels);
|
||||
|
||||
// This function could not be fully analyzed, report failure
|
||||
if (function.exitMode == Function::ExitMode::Unknown) {
|
||||
analysisFailed = true;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Add function to our function list
|
||||
auto [it, added] = functions.insert(std::move(function));
|
||||
return &(*it);
|
||||
}
|
||||
|
||||
void analyze(const PICAShader& shader, u32 entrypoint);
|
||||
Function::ExitMode analyzeFunction(const PICAShader& shader, u32 start, u32 end, Function::Labels& labels);
|
||||
};
|
||||
|
||||
class ShaderDecompiler {
|
||||
using AddressRange = ControlFlow::AddressRange;
|
||||
using Function = ControlFlow::Function;
|
||||
|
||||
ControlFlow controlFlow{};
|
||||
|
||||
PICAShader& shader;
|
||||
EmulatorConfig& config;
|
||||
std::string decompiledShader;
|
||||
|
||||
u32 entrypoint;
|
||||
|
||||
API api;
|
||||
Language language;
|
||||
bool compilationError = false;
|
||||
|
||||
void compileInstruction(u32& pc, bool& finished);
|
||||
// Compile range "range" and returns the end PC or if we're "finished" with the program (called an END instruction)
|
||||
std::pair<u32, bool> compileRange(const AddressRange& range);
|
||||
void callFunction(const Function& function);
|
||||
const Function* findFunction(const AddressRange& range);
|
||||
|
||||
void writeAttributes();
|
||||
|
||||
std::string getSource(u32 source, u32 index) const;
|
||||
std::string getDest(u32 dest) const;
|
||||
std::string getSwizzlePattern(u32 swizzle) const;
|
||||
std::string getDestSwizzle(u32 destinationMask) const;
|
||||
const char* getCondition(u32 cond, u32 refX, u32 refY);
|
||||
|
||||
void setDest(u32 operandDescriptor, const std::string& dest, const std::string& value);
|
||||
// Returns if the instruction uses the typical register encodings most instructions use
|
||||
// With some exceptions like MAD/MADI, and the control flow instructions which are completely different
|
||||
bool usesCommonEncoding(u32 instruction) const;
|
||||
|
||||
public:
|
||||
ShaderDecompiler(PICAShader& shader, EmulatorConfig& config, u32 entrypoint, API api, Language language)
|
||||
: shader(shader), entrypoint(entrypoint), config(config), api(api), language(language), decompiledShader("") {}
|
||||
|
||||
std::string decompile();
|
||||
};
|
||||
|
||||
std::string decompileShader(PICAShader& shader, EmulatorConfig& config, u32 entrypoint, API api, Language language);
|
||||
} // namespace PICA::ShaderGen
|
43
include/PICA/shader_gen.hpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
#include "PICA/gpu.hpp"
|
||||
#include "PICA/pica_frag_config.hpp"
|
||||
#include "PICA/pica_vert_config.hpp"
|
||||
#include "PICA/regs.hpp"
|
||||
#include "PICA/shader_gen_types.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace PICA::ShaderGen {
|
||||
class FragmentGenerator {
|
||||
API api;
|
||||
Language language;
|
||||
|
||||
void compileTEV(std::string& shader, int stage, const PICA::FragmentConfig& config);
|
||||
void getSource(std::string& shader, PICA::TexEnvConfig::Source source, int index, const PICA::FragmentConfig& config);
|
||||
void getColorOperand(std::string& shader, PICA::TexEnvConfig::Source source, PICA::TexEnvConfig::ColorOperand color, int index, const PICA::FragmentConfig& config);
|
||||
void getAlphaOperand(std::string& shader, PICA::TexEnvConfig::Source source, PICA::TexEnvConfig::AlphaOperand alpha, int index, const PICA::FragmentConfig& config);
|
||||
void getColorOperation(std::string& shader, PICA::TexEnvConfig::Operation op);
|
||||
void getAlphaOperation(std::string& shader, PICA::TexEnvConfig::Operation op);
|
||||
|
||||
void applyAlphaTest(std::string& shader, const PICA::FragmentConfig& config);
|
||||
void compileLights(std::string& shader, const PICA::FragmentConfig& config);
|
||||
void compileLUTLookup(std::string& shader, const PICA::FragmentConfig& config, u32 lightIndex, u32 lutID);
|
||||
bool isSamplerEnabled(u32 environmentID, u32 lutID);
|
||||
|
||||
void compileFog(std::string& shader, const PICA::FragmentConfig& config);
|
||||
void compileLogicOps(std::string& shader, const PICA::FragmentConfig& config);
|
||||
|
||||
public:
|
||||
FragmentGenerator(API api, Language language) : api(api), language(language) {}
|
||||
std::string generate(const PICA::FragmentConfig& config, void* driverInfo = nullptr);
|
||||
std::string getDefaultVertexShader();
|
||||
// For when PICA shader is acceleration is enabled. Turn the PICA shader source into a proper vertex shader
|
||||
std::string getVertexShaderAccelerated(const std::string& picaSource, const PICA::VertConfig& vertConfig, bool usingUbershader);
|
||||
|
||||
void setTarget(API api, Language language) {
|
||||
this->api = api;
|
||||
this->language = language;
|
||||
}
|
||||
};
|
||||
}; // namespace PICA::ShaderGen
|
9
include/PICA/shader_gen_types.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
namespace PICA::ShaderGen {
|
||||
// Graphics API this shader is targetting
|
||||
enum class API { GL, GLES, Vulkan, Metal };
|
||||
|
||||
// Shading language to use
|
||||
enum class Language { GLSL, MSL };
|
||||
} // namespace PICA::ShaderGen
|
|
@ -2,10 +2,9 @@
|
|||
#include "PICA/shader.hpp"
|
||||
|
||||
class ShaderUnit {
|
||||
|
||||
public:
|
||||
PICAShader vs; // Vertex shader
|
||||
PICAShader gs; // Geometry shader
|
||||
public:
|
||||
PICAShader vs; // Vertex shader
|
||||
PICAShader gs; // Geometry shader
|
||||
|
||||
ShaderUnit() : vs(ShaderType::Vertex), gs(ShaderType::Geometry) {}
|
||||
void reset();
|
||||
|
|
100
include/align.hpp
Normal file
|
@ -0,0 +1,100 @@
|
|||
// SPDX-FileCopyrightText: 2019-2022 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "compiler_builtins.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <malloc.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
template <typename T>
|
||||
constexpr bool isAligned(T value, unsigned int alignment) {
|
||||
return (value % static_cast<T>(alignment)) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T alignUp(T value, unsigned int alignment) {
|
||||
return (value + static_cast<T>(alignment - 1)) / static_cast<T>(alignment) * static_cast<T>(alignment);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T alignDown(T value, unsigned int alignment) {
|
||||
return value / static_cast<T>(alignment) * static_cast<T>(alignment);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool isAlignedPow2(T value, unsigned int alignment) {
|
||||
return (value & static_cast<T>(alignment - 1)) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T alignUpPow2(T value, unsigned int alignment) {
|
||||
return (value + static_cast<T>(alignment - 1)) & static_cast<T>(~static_cast<T>(alignment - 1));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T alignDownPow2(T value, unsigned int alignment) {
|
||||
return value & static_cast<T>(~static_cast<T>(alignment - 1));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr bool isPow2(T value) {
|
||||
return (value & (value - 1)) == 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T previousPow2(T value) {
|
||||
if (value == static_cast<T>(0)) return 0;
|
||||
|
||||
value |= (value >> 1);
|
||||
value |= (value >> 2);
|
||||
value |= (value >> 4);
|
||||
if constexpr (sizeof(T) >= 16) value |= (value >> 8);
|
||||
if constexpr (sizeof(T) >= 32) value |= (value >> 16);
|
||||
if constexpr (sizeof(T) >= 64) value |= (value >> 32);
|
||||
return value - (value >> 1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T nextPow2(T value) {
|
||||
// https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
|
||||
if (value == static_cast<T>(0)) return 0;
|
||||
|
||||
value--;
|
||||
value |= (value >> 1);
|
||||
value |= (value >> 2);
|
||||
value |= (value >> 4);
|
||||
if constexpr (sizeof(T) >= 16) value |= (value >> 8);
|
||||
if constexpr (sizeof(T) >= 32) value |= (value >> 16);
|
||||
if constexpr (sizeof(T) >= 64) value |= (value >> 32);
|
||||
value++;
|
||||
return value;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE static void* alignedMalloc(size_t size, size_t alignment) {
|
||||
#ifdef _WIN32
|
||||
return _aligned_malloc(size, alignment);
|
||||
#else
|
||||
// Unaligned sizes are slow on macOS.
|
||||
#ifdef __APPLE__
|
||||
if (isPow2(alignment)) size = (size + alignment - 1) & ~(alignment - 1);
|
||||
#endif
|
||||
void* ret = nullptr;
|
||||
return (posix_memalign(&ret, alignment, size) == 0) ? ret : nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
ALWAYS_INLINE static void alignedFree(void* ptr) {
|
||||
#ifdef _MSC_VER
|
||||
_aligned_free(ptr);
|
||||
#else
|
||||
free(ptr);
|
||||
#endif
|
||||
}
|
||||
} // namespace Common
|
5
include/android_utils.hpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
namespace AndroidUtils {
|
||||
int openDocument(const char* directory, const char* mode);
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "kernel/kernel_types.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
|
@ -65,24 +68,27 @@ namespace Applets {
|
|||
};
|
||||
|
||||
struct Parameter {
|
||||
u32 senderID;
|
||||
u32 destID;
|
||||
APTSignal signal;
|
||||
std::vector<u8> data;
|
||||
u32 senderID; // ID of the parameter sender
|
||||
u32 destID; // ID of the app to receive parameter
|
||||
u32 signal; // Signal type (eg request)
|
||||
u32 object; // Some applets will also respond with shared memory handles for transferring data between the sender and called
|
||||
std::vector<u8> data; // Misc data
|
||||
};
|
||||
|
||||
class AppletBase {
|
||||
protected:
|
||||
Memory& mem;
|
||||
std::optional<Parameter>& nextParameter;
|
||||
|
||||
public:
|
||||
virtual const char* name() = 0;
|
||||
|
||||
// Called by APT::StartLibraryApplet and similar
|
||||
virtual Result::HorizonResult start() = 0;
|
||||
virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector<u8>& parameters, u32 appID) = 0;
|
||||
// Transfer parameters from application -> applet
|
||||
virtual Result::HorizonResult receiveParameter() = 0;
|
||||
virtual Result::HorizonResult receiveParameter(const Parameter& parameter) = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
AppletBase(Memory& mem) : mem(mem) {}
|
||||
AppletBase(Memory& mem, std::optional<Parameter>& nextParam) : mem(mem), nextParameter(nextParam) {}
|
||||
};
|
||||
} // namespace Applets
|
|
@ -1,3 +1,7 @@
|
|||
#pragma once
|
||||
#include <optional>
|
||||
|
||||
#include "applets/error_applet.hpp"
|
||||
#include "applets/mii_selector.hpp"
|
||||
#include "applets/software_keyboard.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
@ -8,10 +12,15 @@ namespace Applets {
|
|||
class AppletManager {
|
||||
MiiSelectorApplet miiSelector;
|
||||
SoftwareKeyboardApplet swkbd;
|
||||
ErrorApplet error;
|
||||
std::optional<Applets::Parameter> nextParameter = std::nullopt;
|
||||
|
||||
public:
|
||||
AppletManager(Memory& mem);
|
||||
void reset();
|
||||
AppletBase* getApplet(u32 id);
|
||||
|
||||
Applets::Parameter glanceParameter();
|
||||
Applets::Parameter receiveParameter();
|
||||
};
|
||||
} // namespace Applets
|
15
include/applets/error_applet.hpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include <string>
|
||||
|
||||
#include "applets/applet.hpp"
|
||||
|
||||
namespace Applets {
|
||||
class ErrorApplet final : public AppletBase {
|
||||
public:
|
||||
virtual const char* name() override { return "Error/EULA Agreement"; }
|
||||
virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector<u8>& parameters, u32 appID) override;
|
||||
virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override;
|
||||
virtual void reset() override;
|
||||
|
||||
ErrorApplet(Memory& memory, std::optional<Parameter>& nextParam) : AppletBase(memory, nextParam) {}
|
||||
};
|
||||
} // namespace Applets
|
|
@ -1,13 +1,83 @@
|
|||
#include <string>
|
||||
|
||||
#include "applets/applet.hpp"
|
||||
#include "swap.hpp"
|
||||
|
||||
namespace Applets {
|
||||
struct MiiConfig {
|
||||
u8 enableCancelButton;
|
||||
u8 enableGuestMii;
|
||||
u8 showOnTopScreen;
|
||||
std::array<u8, 0x5> pad1;
|
||||
std::array<u16_le, 0x40> title;
|
||||
std::array<u8, 0x4> pad2;
|
||||
u8 showGuestMiis;
|
||||
std::array<u8, 0x3> pad3;
|
||||
u32 initiallySelectedIndex;
|
||||
std::array<u8, 0x6> guestMiiWhitelist;
|
||||
std::array<u8, 0x64> userMiiWhitelist;
|
||||
std::array<u8, 0x2> pad4;
|
||||
u32 magicValue;
|
||||
};
|
||||
static_assert(sizeof(MiiConfig) == 0x104, "Mii config size is wrong");
|
||||
|
||||
// Some members of this struct are not properly aligned so we need pragma pack
|
||||
#pragma pack(push, 1)
|
||||
struct MiiData {
|
||||
u8 version;
|
||||
u8 miiOptions;
|
||||
u8 miiPos;
|
||||
u8 consoleID;
|
||||
|
||||
u64_be systemID;
|
||||
u32_be miiID;
|
||||
std::array<u8, 0x6> creatorMAC;
|
||||
u16 padding;
|
||||
|
||||
u16_be miiDetails;
|
||||
std::array<char16_t, 0xA> miiName;
|
||||
u8 height;
|
||||
u8 width;
|
||||
|
||||
u8 faceStyle;
|
||||
u8 faceDetails;
|
||||
u8 hairStyle;
|
||||
u8 hairDetails;
|
||||
u32_be eyeDetails;
|
||||
u32_be eyebrowDetails;
|
||||
u16_be noseDetails;
|
||||
u16_be mouthDetails;
|
||||
u16_be moustacheDetails;
|
||||
u16_be beardDetails;
|
||||
u16_be glassesDetails;
|
||||
u16_be moleDetails;
|
||||
|
||||
std::array<char16_t, 0xA> authorName;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(MiiData) == 0x5C, "MiiData structure has incorrect size");
|
||||
|
||||
struct MiiResult {
|
||||
u32_be returnCode;
|
||||
u32_be isGuestMiiSelected;
|
||||
u32_be selectedGuestMiiIndex;
|
||||
MiiData selectedMiiData;
|
||||
u16_be unknown1;
|
||||
u16_be miiChecksum;
|
||||
std::array<u16_le, 0xC> guestMiiName;
|
||||
};
|
||||
static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size");
|
||||
|
||||
class MiiSelectorApplet final : public AppletBase {
|
||||
public:
|
||||
virtual const char* name() override { return "Mii Selector"; }
|
||||
virtual Result::HorizonResult start() override;
|
||||
virtual Result::HorizonResult receiveParameter() override;
|
||||
virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector<u8>& parameters, u32 appID) override;
|
||||
virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override;
|
||||
virtual void reset() override;
|
||||
|
||||
MiiSelectorApplet(Memory& memory) : AppletBase(memory) {}
|
||||
MiiResult output;
|
||||
MiiConfig config;
|
||||
MiiResult getDefaultMii();
|
||||
MiiSelectorApplet(Memory& memory, std::optional<Parameter>& nextParam) : AppletBase(memory, nextParam) {}
|
||||
};
|
||||
} // namespace Applets
|
|
@ -1,13 +1,162 @@
|
|||
#include <array>
|
||||
|
||||
#include "applets/applet.hpp"
|
||||
#include "swap.hpp"
|
||||
|
||||
namespace Applets {
|
||||
// Software keyboard definitions adapted from libctru/Citra
|
||||
// Keyboard input filtering flags. Allows the caller to specify what input is explicitly not allowed
|
||||
namespace SoftwareKeyboardFilter {
|
||||
enum Filter : u32 {
|
||||
Digits = 1, // Disallow the use of more than a certain number of digits (0 or more)
|
||||
At = 1 << 1, // Disallow the use of the @ sign.
|
||||
Percent = 1 << 2, // Disallow the use of the % sign.
|
||||
Backslash = 1 << 3, // Disallow the use of the \ sign.
|
||||
Profanity = 1 << 4, // Disallow profanity using Nintendo's profanity filter.
|
||||
Callback = 1 << 5, // Use a callback in order to check the input.
|
||||
};
|
||||
} // namespace SoftwareKeyboardFilter
|
||||
|
||||
// Keyboard features.
|
||||
namespace SoftwareKeyboardFeature {
|
||||
enum Feature {
|
||||
Parental = 1, // Parental PIN mode.
|
||||
DarkenTopScreen = 1 << 1, // Darken the top screen when the keyboard is shown.
|
||||
PredictiveInput = 1 << 2, // Enable predictive input (necessary for Kanji input in JPN systems).
|
||||
Multiline = 1 << 3, // Enable multiline input.
|
||||
FixedWidth = 1 << 4, // Enable fixed-width mode.
|
||||
AllowHome = 1 << 5, // Allow the usage of the HOME button.
|
||||
AllowReset = 1 << 6, // Allow the usage of a software-reset combination.
|
||||
AllowPower = 1 << 7, // Allow the usage of the POWER button.
|
||||
DefaultQWERTY = 1 << 9, // Default to the QWERTY page when the keyboard is shown.
|
||||
};
|
||||
} // namespace SoftwareKeyboardFeature
|
||||
|
||||
class SoftwareKeyboardApplet final : public AppletBase {
|
||||
public:
|
||||
static constexpr int MAX_BUTTON = 3; // Maximum number of buttons that can be in the keyboard.
|
||||
static constexpr int MAX_BUTTON_TEXT_LEN = 16; // Maximum button text length, in UTF-16 code units.
|
||||
static constexpr int MAX_HINT_TEXT_LEN = 64; // Maximum hint text length, in UTF-16 code units.
|
||||
static constexpr int MAX_CALLBACK_MSG_LEN = 256; // Maximum filter callback error message length, in UTF-16 code units.
|
||||
|
||||
// Keyboard types
|
||||
enum class SoftwareKeyboardType : u32 {
|
||||
Normal, // Normal keyboard with several pages (QWERTY/accents/symbol/mobile)
|
||||
QWERTY, // QWERTY keyboard only.
|
||||
NumPad, // Number pad.
|
||||
Western, // On JPN systems, a text keyboard without Japanese input capabilities, otherwise same as SWKBD_TYPE_NORMAL.
|
||||
};
|
||||
|
||||
// Keyboard dialog buttons.
|
||||
enum class SoftwareKeyboardButtonConfig : u32 {
|
||||
SingleButton, // Ok button
|
||||
DualButton, // Cancel | Ok buttons
|
||||
TripleButton, // Cancel | I Forgot | Ok buttons
|
||||
NoButton, // No button (returned by swkbdInputText in special cases)
|
||||
};
|
||||
|
||||
// Accepted input types.
|
||||
enum class SoftwareKeyboardValidInput : u32 {
|
||||
Anything, // All inputs are accepted.
|
||||
NotEmpty, // Empty inputs are not accepted.
|
||||
NotEmptyNotBlank, // Empty or blank inputs (consisting solely of whitespace) are not accepted.
|
||||
NotBlank, // Blank inputs (consisting solely of whitespace) are not accepted, but empty inputs are.
|
||||
FixedLen, // The input must have a fixed length (specified by maxTextLength in swkbdInit)
|
||||
};
|
||||
|
||||
// Keyboard password modes.
|
||||
enum class SoftwareKeyboardPasswordMode : u32 {
|
||||
None, // Characters are not concealed.
|
||||
Hide, // Characters are concealed immediately.
|
||||
HideDelay, // Characters are concealed a second after they've been typed.
|
||||
};
|
||||
|
||||
// Keyboard filter callback return values.
|
||||
enum class SoftwareKeyboardCallbackResult : u32 {
|
||||
OK, // Specifies that the input is valid.
|
||||
Close, // Displays an error message, then closes the keyboard.
|
||||
Continue, // Displays an error message and continues displaying the keyboard.
|
||||
};
|
||||
|
||||
// Keyboard return values.
|
||||
enum class SoftwareKeyboardResult : s32 {
|
||||
None = -1, // Dummy/unused.
|
||||
InvalidInput = -2, // Invalid parameters to swkbd.
|
||||
OutOfMem = -3, // Out of memory.
|
||||
|
||||
D0Click = 0, // The button was clicked in 1-button dialogs.
|
||||
D1Click0, // The left button was clicked in 2-button dialogs.
|
||||
D1Click1, // The right button was clicked in 2-button dialogs.
|
||||
D2Click0, // The left button was clicked in 3-button dialogs.
|
||||
D2Click1, // The middle button was clicked in 3-button dialogs.
|
||||
D2Click2, // The right button was clicked in 3-button dialogs.
|
||||
|
||||
HomePressed = 10, // The HOME button was pressed.
|
||||
ResetPressed, // The soft-reset key combination was pressed.
|
||||
PowerPressed, // The POWER button was pressed.
|
||||
|
||||
ParentalOK = 20, // The parental PIN was verified successfully.
|
||||
ParentalFail, // The parental PIN was incorrect.
|
||||
|
||||
BannedInput = 30, // The filter callback returned SoftwareKeyboardCallback::CLOSE.
|
||||
};
|
||||
|
||||
struct SoftwareKeyboardConfig {
|
||||
enum_le<SoftwareKeyboardType> type;
|
||||
enum_le<SoftwareKeyboardButtonConfig> numButtonsM1;
|
||||
enum_le<SoftwareKeyboardValidInput> validInput;
|
||||
enum_le<SoftwareKeyboardPasswordMode> passwordMode;
|
||||
s32_le isParentalScreen;
|
||||
s32_le darkenTopScreen;
|
||||
u32_le filterFlags;
|
||||
u32_le saveStateFlags;
|
||||
u16_le maxTextLength;
|
||||
u16_le dictWordCount;
|
||||
u16_le maxDigits;
|
||||
std::array<std::array<u16_le, MAX_BUTTON_TEXT_LEN + 1>, MAX_BUTTON> buttonText;
|
||||
std::array<u16_le, 2> numpadKeys;
|
||||
std::array<u16_le, MAX_HINT_TEXT_LEN + 1> hintText; // Text to display when asking the user for input
|
||||
bool predictiveInput;
|
||||
bool multiline;
|
||||
bool fixedWidth;
|
||||
bool allowHome;
|
||||
bool allowReset;
|
||||
bool allowPower;
|
||||
bool unknown;
|
||||
bool defaultQwerty;
|
||||
std::array<bool, 4> buttonSubmitsText;
|
||||
u16_le language;
|
||||
|
||||
u32_le initialTextOffset; // Offset of the default text in the output SharedMemory
|
||||
u32_le dictOffset;
|
||||
u32_le initialStatusOffset;
|
||||
u32_le initialLearningOffset;
|
||||
u32_le sharedMemorySize; // Size of the SharedMemory
|
||||
u32_le version;
|
||||
|
||||
enum_le<SoftwareKeyboardResult> returnCode;
|
||||
|
||||
u32_le statusOffset;
|
||||
u32_le learningOffset;
|
||||
|
||||
u32_le textOffset; // Offset in the SharedMemory where the output text starts
|
||||
u16_le textLength; // Length in characters of the output text
|
||||
|
||||
enum_le<SoftwareKeyboardCallbackResult> callbackResult;
|
||||
std::array<u16_le, MAX_CALLBACK_MSG_LEN + 1> callbackMessage;
|
||||
bool skipAtCheck;
|
||||
std::array<u8, 0xAB> pad;
|
||||
};
|
||||
static_assert(sizeof(SoftwareKeyboardConfig) == 0x400, "Software keyboard config size is wrong");
|
||||
|
||||
virtual const char* name() override { return "Software Keyboard"; }
|
||||
virtual Result::HorizonResult start() override;
|
||||
virtual Result::HorizonResult receiveParameter() override;
|
||||
virtual Result::HorizonResult start(const MemoryBlock* sharedMem, const std::vector<u8>& parameters, u32 appID) override;
|
||||
virtual Result::HorizonResult receiveParameter(const Applets::Parameter& parameter) override;
|
||||
virtual void reset() override;
|
||||
|
||||
SoftwareKeyboardApplet(Memory& memory) : AppletBase(memory) {}
|
||||
SoftwareKeyboardApplet(Memory& memory, std::optional<Parameter>& nextParam) : AppletBase(memory, nextParam) {}
|
||||
void closeKeyboard(u32 appID);
|
||||
|
||||
SoftwareKeyboardConfig config;
|
||||
};
|
||||
} // namespace Applets
|
82
include/audio/aac.hpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "swap.hpp"
|
||||
|
||||
namespace Audio::AAC {
|
||||
namespace ResultCode {
|
||||
enum : u32 {
|
||||
Success = 0,
|
||||
};
|
||||
}
|
||||
|
||||
// Enum values and struct definitions based off Citra
|
||||
namespace Command {
|
||||
enum : u16 {
|
||||
Init = 0, // Initialize encoder/decoder
|
||||
EncodeDecode = 1, // Encode/Decode AAC
|
||||
Shutdown = 2, // Shutdown encoder/decoder
|
||||
LoadState = 3,
|
||||
SaveState = 4,
|
||||
};
|
||||
}
|
||||
|
||||
namespace SampleRate {
|
||||
enum : u32 {
|
||||
Rate48000 = 0,
|
||||
Rate44100 = 1,
|
||||
Rate32000 = 2,
|
||||
Rate24000 = 3,
|
||||
Rate22050 = 4,
|
||||
Rate16000 = 5,
|
||||
Rate12000 = 6,
|
||||
Rate11025 = 7,
|
||||
Rate8000 = 8,
|
||||
};
|
||||
}
|
||||
|
||||
namespace Mode {
|
||||
enum : u16 {
|
||||
None = 0,
|
||||
Decode = 1,
|
||||
Encode = 2,
|
||||
};
|
||||
}
|
||||
|
||||
struct DecodeResponse {
|
||||
u32_le sampleRate;
|
||||
u32_le channelCount;
|
||||
u32_le size;
|
||||
u32_le unknown1;
|
||||
u32_le unknown2;
|
||||
u32_le sampleCount;
|
||||
};
|
||||
|
||||
struct DecodeRequest {
|
||||
u32_le address; // Address of input AAC stream
|
||||
u32_le size; // Size of input AAC stream
|
||||
u32_le destAddrLeft; // Output address for left channel samples
|
||||
u32_le destAddrRight; // Output address for right channel samples
|
||||
u32_le unknown1;
|
||||
u32_le unknown2;
|
||||
};
|
||||
|
||||
struct Message {
|
||||
u16_le mode = Mode::None; // Encode or decode AAC?
|
||||
u16_le command = Command::Init;
|
||||
u32_le resultCode = ResultCode::Success;
|
||||
|
||||
// Info on the AAC request
|
||||
union {
|
||||
std::array<u8, 24> commandData{};
|
||||
|
||||
DecodeResponse decodeResponse;
|
||||
DecodeRequest decodeRequest;
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(Message) == 32);
|
||||
static_assert(std::is_trivially_copyable<Message>());
|
||||
} // namespace Audio::AAC
|
25
include/audio/aac_decoder.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
|
||||
#include "audio/aac.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
struct AAC_DECODER_INSTANCE;
|
||||
|
||||
namespace Audio::AAC {
|
||||
class Decoder {
|
||||
using DecoderHandle = AAC_DECODER_INSTANCE*;
|
||||
using PaddrCallback = std::function<u8*(u32)>;
|
||||
|
||||
DecoderHandle decoderHandle = nullptr;
|
||||
|
||||
bool isInitialized() { return decoderHandle != nullptr; }
|
||||
void initialize();
|
||||
|
||||
public:
|
||||
// Decode function. Takes in a reference to the AAC response & request, and a callback for paddr -> pointer conversions
|
||||
// We also allow for optionally muting the AAC output (setting all of it to 0) instead of properly decoding it, for debug/research purposes
|
||||
void decode(AAC::Message& response, const AAC::Message& request, PaddrCallback paddrCallback, bool enableAudio = true);
|
||||
~Decoder();
|
||||
};
|
||||
} // namespace Audio::AAC
|
9
include/audio/audio_device.hpp
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#if defined(__LIBRETRO__) && defined(USE_LIBRETRO_AUDIO_DEVICE)
|
||||
#include "audio/libretro_audio_device.hpp"
|
||||
using AudioDevice = LibretroAudioDevice;
|
||||
#else
|
||||
#include "audio/miniaudio_device.hpp"
|
||||
using AudioDevice = MiniAudioDevice;
|
||||
#endif
|
36
include/audio/audio_device_interface.hpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "ring_buffer.hpp"
|
||||
|
||||
class AudioDeviceInterface {
|
||||
protected:
|
||||
static constexpr usize maxFrameCount = 0x2000;
|
||||
|
||||
using Samples = Common::RingBuffer<s16, maxFrameCount * 2>;
|
||||
using RenderBatchCallback = usize (*)(const s16*, usize);
|
||||
|
||||
Samples* samples = nullptr;
|
||||
|
||||
const AudioDeviceConfig& audioSettings;
|
||||
// Store the last stereo sample we output. We play this when underruning to avoid pops.
|
||||
std::array<s16, 2> lastStereoSample{};
|
||||
|
||||
public:
|
||||
AudioDeviceInterface(Samples* samples, const AudioDeviceConfig& audioSettings) : samples(samples), audioSettings(audioSettings) {}
|
||||
|
||||
bool running = false;
|
||||
Samples* getSamples() { return samples; }
|
||||
|
||||
// If safe is on, we create a null audio device
|
||||
virtual void init(Samples& samples, bool safe = false) = 0;
|
||||
virtual void close() = 0;
|
||||
|
||||
virtual void start() = 0;
|
||||
virtual void stop() = 0;
|
||||
|
||||
// Only used for audio devices that render multiple audio frames in one go, eg the libretro audio device.
|
||||
virtual void renderBatch(RenderBatchCallback callback) {}
|
||||
};
|
58
include/audio/audio_interpolation.hpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <deque>
|
||||
|
||||
#include "audio/hle_mixer.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace Audio::Interpolation {
|
||||
// A variable length buffer of signed PCM16 stereo samples.
|
||||
using StereoBuffer16 = std::deque<std::array<s16, 2>>;
|
||||
using StereoFrame16 = Audio::DSPMixer::StereoFrame<s16>;
|
||||
|
||||
struct State {
|
||||
// Two historical samples.
|
||||
std::array<s16, 2> xn1 = {}; //< x[n-1]
|
||||
std::array<s16, 2> xn2 = {}; //< x[n-2]
|
||||
// Current fractional position.
|
||||
u64 fposition = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* No interpolation. This is equivalent to a zero-order hold. There is a two-sample predelay.
|
||||
* @param state Interpolation state.
|
||||
* @param input Input buffer.
|
||||
* @param rate Stretch factor. Must be a positive non-zero value.
|
||||
* rate > 1.0 performs decimation and rate < 1.0 performs upsampling.
|
||||
* @param output The resampled audio buffer.
|
||||
* @param outputi The index of output to start writing to.
|
||||
*/
|
||||
void none(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, usize& outputi);
|
||||
|
||||
/**
|
||||
* Linear interpolation. This is equivalent to a first-order hold. There is a two-sample predelay.
|
||||
* @param state Interpolation state.
|
||||
* @param input Input buffer.
|
||||
* @param rate Stretch factor. Must be a positive non-zero value.
|
||||
* rate > 1.0 performs decimation and rate < 1.0 performs upsampling.
|
||||
* @param output The resampled audio buffer.
|
||||
* @param outputi The index of output to start writing to.
|
||||
*/
|
||||
void linear(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, usize& outputi);
|
||||
|
||||
/**
|
||||
* Polyphase interpolation. This is currently stubbed to just perform linear interpolation
|
||||
* @param state Interpolation state.
|
||||
* @param input Input buffer.
|
||||
* @param rate Stretch factor. Must be a positive non-zero value.
|
||||
* rate > 1.0 performs decimation and rate < 1.0 performs upsampling.
|
||||
* @param output The resampled audio buffer.
|
||||
* @param outputi The index of output to start writing to.
|
||||
*/
|
||||
void polyphase(State& state, StereoBuffer16& input, float rate, StereoFrame16& output, usize& outputi);
|
||||
} // namespace Audio::Interpolation
|
69
include/audio/dsp_core.hpp
Normal file
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "ring_buffer.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
// The DSP core must have access to the DSP service to be able to trigger interrupts properly
|
||||
class DSPService;
|
||||
class Memory;
|
||||
struct EmulatorConfig;
|
||||
|
||||
namespace Audio {
|
||||
// There are 160 stereo samples in 1 audio frame, so 320 samples total
|
||||
static constexpr u64 samplesInFrame = 160;
|
||||
// 1 frame = 4096 DSP cycles = 8192 ARM11 cycles
|
||||
static constexpr u64 cyclesPerFrame = samplesInFrame * 8192;
|
||||
// For LLE DSP cores, we run the DSP for N cycles at a time, every N*2 arm11 cycles since the ARM11 runs twice as fast
|
||||
static constexpr u64 lleSlice = 16384;
|
||||
|
||||
class DSPCore {
|
||||
// 0x2000 stereo (= 2 channel) samples
|
||||
using Samples = Common::RingBuffer<s16, 0x2000 * 2>;
|
||||
|
||||
protected:
|
||||
Memory& mem;
|
||||
Scheduler& scheduler;
|
||||
DSPService& dspService;
|
||||
EmulatorConfig& settings;
|
||||
|
||||
Samples sampleBuffer;
|
||||
bool audioEnabled = false;
|
||||
|
||||
MAKE_LOG_FUNCTION(log, dspLogger)
|
||||
|
||||
public:
|
||||
enum class Type { Null, Teakra, HLE };
|
||||
DSPCore(Memory& mem, Scheduler& scheduler, DSPService& dspService, EmulatorConfig& settings)
|
||||
: mem(mem), scheduler(scheduler), dspService(dspService), settings(settings) {}
|
||||
virtual ~DSPCore() {}
|
||||
|
||||
virtual void reset() = 0;
|
||||
virtual void runAudioFrame(u64 eventTimestamp) = 0;
|
||||
virtual u8* getDspMemory() = 0;
|
||||
|
||||
virtual u16 recvData(u32 regId) = 0;
|
||||
virtual bool recvDataIsReady(u32 regId) = 0;
|
||||
virtual void setSemaphore(u16 value) = 0;
|
||||
virtual void writeProcessPipe(u32 channel, u32 size, u32 buffer) = 0;
|
||||
virtual std::vector<u8> readPipe(u32 channel, u32 peer, u32 size, u32 buffer) = 0;
|
||||
virtual void loadComponent(std::vector<u8>& data, u32 programMask, u32 dataMask) = 0;
|
||||
virtual void unloadComponent() = 0;
|
||||
virtual void setSemaphoreMask(u16 value) = 0;
|
||||
|
||||
static Audio::DSPCore::Type typeFromString(std::string inString);
|
||||
static const char* typeToString(Audio::DSPCore::Type type);
|
||||
|
||||
Samples& getSamples() { return sampleBuffer; }
|
||||
virtual void setAudioEnabled(bool enable) { audioEnabled = enable; }
|
||||
};
|
||||
|
||||
std::unique_ptr<DSPCore> makeDSPCore(EmulatorConfig& config, Memory& mem, Scheduler& scheduler, DSPService& dspService);
|
||||
} // namespace Audio
|
534
include/audio/dsp_shared_mem.hpp
Normal file
|
@ -0,0 +1,534 @@
|
|||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "bitfield.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "swap.hpp"
|
||||
|
||||
namespace Audio::HLE {
|
||||
// The application-accessible region of DSP memory consists of two parts. Both are marked as IO and
|
||||
// have Read/Write permissions.
|
||||
|
||||
// First Region: 0x1FF50000 (Size: 0x8000)
|
||||
// Second Region: 0x1FF70000 (Size: 0x8000)
|
||||
|
||||
// The DSP reads from each region alternately based on the frame counter for each region much like a
|
||||
// double-buffer. The frame counter is located as the very last u16 of each region and is
|
||||
// incremented each audio tick.
|
||||
|
||||
constexpr u32 region0Offset = 0x50000;
|
||||
constexpr u32 region1Offset = 0x70000;
|
||||
|
||||
// Number of DSP voices
|
||||
constexpr u32 sourceCount = 24;
|
||||
// There are 160 stereo samples in 1 audio frame, so 320 samples total
|
||||
static constexpr u64 samplesInFrame = 160;
|
||||
|
||||
/**
|
||||
* The DSP is native 16-bit. The DSP also appears to be big-endian. When reading 32-bit numbers from
|
||||
* its memory regions, the higher and lower 16-bit halves are swapped compared to the little-endian
|
||||
* layout of the ARM11. Hence from the ARM11's point of view the memory space appears to be
|
||||
* middle-endian.
|
||||
*
|
||||
* Unusually this does not appear to be an issue for floating point numbers. The DSP makes the more
|
||||
* sensible choice of keeping that little-endian. There are also some exceptions such as the
|
||||
* IntermediateMixSamples structure, which is little-endian.
|
||||
*
|
||||
* This struct implements the conversion to and from this middle-endianness.
|
||||
*/
|
||||
struct u32_dsp {
|
||||
u32_dsp() = default;
|
||||
operator u32() const { return Convert(storage); }
|
||||
void operator=(u32 newValue) { storage = Convert(newValue); }
|
||||
|
||||
private:
|
||||
static constexpr u32 Convert(u32 value) { return (value << 16) | (value >> 16); }
|
||||
u32_le storage;
|
||||
};
|
||||
static_assert(std::is_trivially_copyable<u32_dsp>::value, "u32_dsp isn't trivially copyable");
|
||||
|
||||
// There are 15 structures in each memory region. A table of them in the order they appear in memory
|
||||
// is presented below:
|
||||
// # First Region DSP Address Purpose Control
|
||||
// 5 0x8400 DSP Status DSP
|
||||
// 9 0x8410 DSP Debug Info DSP
|
||||
// 6 0x8540 Final Mix Samples DSP
|
||||
// 2 0x8680 Source Status [24] DSP
|
||||
// 8 0x8710 Compressor Table Application
|
||||
// 4 0x9430 DSP Configuration Application
|
||||
// 7 0x9492 Intermediate Mix Samples DSP + App
|
||||
// 1 0x9E92 Source Configuration [24] Application
|
||||
// 3 0xA792 Source ADPCM Coefficients [24] Application
|
||||
// 10 0xA912 Surround Sound Related
|
||||
// 11 0xAA12 Surround Sound Related
|
||||
// 12 0xAAD2 Surround Sound Related
|
||||
// 13 0xAC52 Surround Sound Related
|
||||
// 14 0xAC5C Surround Sound Related
|
||||
// 0 0xBFFF Frame Counter Application
|
||||
//
|
||||
// #: This refers to the order in which they appear in the DspPipe::Audio DSP pipe.
|
||||
// See also: HLE::PipeRead.
|
||||
//
|
||||
// Note that the above addresses do vary slightly between audio firmwares observed; the addresses
|
||||
// are not fixed in stone. The addresses above are only an examplar; they're what this
|
||||
// implementation does and provides to applications.
|
||||
//
|
||||
// Application requests the DSP service to convert DSP addresses into ARM11 virtual addresses using
|
||||
// the ConvertProcessAddressFromDspDram service call. Applications seem to derive the addresses for
|
||||
// the second region via:
|
||||
// second_region_dsp_addr = first_region_dsp_addr | 0x10000
|
||||
//
|
||||
// Applications maintain most of its own audio state, the memory region is used mainly for
|
||||
// communication and not storage of state.
|
||||
//
|
||||
// In the documentation below, filter and effect transfer functions are specified in the z domain.
|
||||
// (If you are more familiar with the Laplace transform, z = exp(sT). The z domain is the digital
|
||||
// frequency domain, just like how the s domain is the analog frequency domain.)
|
||||
|
||||
#define ASSERT_DSP_STRUCT(name, size) \
|
||||
static_assert(std::is_standard_layout<name>::value, "DSP structure " #name " doesn't use standard layout"); \
|
||||
static_assert(std::is_trivially_copyable<name>::value, "DSP structure " #name " isn't trivially copyable"); \
|
||||
static_assert(sizeof(name) == (size), "Unexpected struct size for DSP structure " #name)
|
||||
|
||||
struct SourceConfiguration {
|
||||
struct Configuration {
|
||||
/// These dirty flags are set by the application when it updates the fields in this struct.
|
||||
/// The DSP clears these each audio frame.
|
||||
union {
|
||||
u32_le dirtyRaw;
|
||||
|
||||
BitField<0, 1, u32> formatDirty;
|
||||
BitField<1, 1, u32> monoOrStereoDirty;
|
||||
BitField<2, 1, u32> adpcmCoefficientsDirty;
|
||||
/// Tends to be set when a looped buffer is queued.
|
||||
BitField<3, 1, u32> partialEmbeddedBufferDirty;
|
||||
BitField<4, 1, u32> partialResetFlag;
|
||||
|
||||
BitField<16, 1, u32> enableDirty;
|
||||
BitField<17, 1, u32> interpolationDirty;
|
||||
BitField<18, 1, u32> rateMultiplierDirty;
|
||||
BitField<19, 1, u32> bufferQueueDirty;
|
||||
BitField<20, 1, u32> loopRelatedDirty;
|
||||
/// Tends to also be set when embedded buffer is updated.
|
||||
BitField<21, 1, u32> playPositionDirty;
|
||||
BitField<22, 1, u32> filtersEnabledDirty;
|
||||
BitField<23, 1, u32> simpleFilterDirty;
|
||||
BitField<24, 1, u32> biquadFilterDirty;
|
||||
BitField<25, 1, u32> gain0Dirty;
|
||||
BitField<26, 1, u32> gain1Dirty;
|
||||
BitField<27, 1, u32> gain2Dirty;
|
||||
BitField<28, 1, u32> syncCountDirty;
|
||||
BitField<29, 1, u32> resetFlag;
|
||||
BitField<30, 1, u32> embeddedBufferDirty;
|
||||
};
|
||||
|
||||
// Gain control
|
||||
|
||||
/**
|
||||
* Gain is between 0.0-1.0. This determines how much will this source appear on each of the
|
||||
* 12 channels that feed into the intermediate mixers. Each of the three intermediate mixers
|
||||
* is fed two left and two right channels.
|
||||
*/
|
||||
float_le gain[3][4];
|
||||
|
||||
// Interpolation
|
||||
|
||||
/// Multiplier for sample rate. Resampling occurs with the selected interpolation method.
|
||||
float_le rateMultiplier;
|
||||
|
||||
enum class InterpolationMode : u8 {
|
||||
Polyphase = 0,
|
||||
Linear = 1,
|
||||
None = 2,
|
||||
};
|
||||
|
||||
InterpolationMode interpolationMode;
|
||||
u8 pad; ///< Interpolation related
|
||||
|
||||
// Filters
|
||||
|
||||
/**
|
||||
* This is the simplest normalized first-order digital recursive filter.
|
||||
* The transfer function of this filter is:
|
||||
* H(z) = b0 / (1 - a1 z^-1)
|
||||
* Note the feedbackward coefficient is negated.
|
||||
* Values are signed fixed point with 15 fractional bits.
|
||||
*/
|
||||
struct SimpleFilter {
|
||||
s16_le b0;
|
||||
s16_le a1;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is a normalised biquad filter (second-order).
|
||||
* The transfer function of this filter is:
|
||||
* H(z) = (b0 + b1 z^-1 + b2 z^-2) / (1 - a1 z^-1 - a2 z^-2)
|
||||
* Nintendo chose to negate the feedbackward coefficients. This differs from standard
|
||||
* notation as in: https://ccrma.stanford.edu/~jos/filters/Direct_Form_I.html
|
||||
* Values are signed fixed point with 14 fractional bits.simple_filter_enabled
|
||||
*/
|
||||
struct BiquadFilter {
|
||||
s16_le a2;
|
||||
s16_le a1;
|
||||
s16_le b2;
|
||||
s16_le b1;
|
||||
s16_le b0;
|
||||
};
|
||||
|
||||
union {
|
||||
u16_le filters_enabled;
|
||||
BitField<0, 1, u16> simpleFilterEnabled;
|
||||
BitField<1, 1, u16> biquadFilterEnabled;
|
||||
};
|
||||
|
||||
SimpleFilter simpleFilter;
|
||||
BiquadFilter biquadFilter;
|
||||
|
||||
// Buffer Queue
|
||||
|
||||
/// A buffer of audio data from the application, along with metadata about it.
|
||||
struct Buffer {
|
||||
/// Physical memory address of the start of the buffer
|
||||
u32_dsp physicalAddress;
|
||||
|
||||
/// This is length in terms of samples.
|
||||
/// Note that in different buffer formats a sample takes up different number of bytes.
|
||||
u32_dsp length;
|
||||
|
||||
/// ADPCM Predictor (4 bits) and Scale (4 bits)
|
||||
union {
|
||||
u16_le adpcm_ps;
|
||||
BitField<0, 4, u16> adpcmScale;
|
||||
BitField<4, 4, u16> adpcmPredictor;
|
||||
};
|
||||
|
||||
/// ADPCM Historical Samples (y[n-1] and y[n-2])
|
||||
u16_le adpcm_yn[2];
|
||||
|
||||
/// This is non-zero when the ADPCM values above are to be updated.
|
||||
u8 adpcmDirty;
|
||||
|
||||
/// Is a looping buffer.
|
||||
u8 isLooping;
|
||||
|
||||
/// This value is shown in SourceStatus::previous_buffer_id when this buffer has
|
||||
/// finished. This allows the emulated application to tell what buffer is currently
|
||||
/// playing.
|
||||
u16_le bufferID;
|
||||
|
||||
u16 pad;
|
||||
};
|
||||
|
||||
u16_le buffersDirty; ///< Bitmap indicating which buffers are dirty (bit i -> buffers[i])
|
||||
Buffer buffers[4]; ///< Queued Buffers
|
||||
|
||||
// Playback controls
|
||||
u32_dsp loopRelated;
|
||||
u8 enable;
|
||||
u8 pad1;
|
||||
u16_le syncCount; ///< Application-side sync count (See also: SourceStatus::sync_count)
|
||||
u32_dsp playPosition; ///< Position. (Units: number of samples)
|
||||
u16 pad2[2];
|
||||
|
||||
// Embedded Buffer
|
||||
// This buffer is often the first buffer to be used when initiating audio playback,
|
||||
// after which the buffer queue is used.
|
||||
|
||||
u32_dsp physicalAddress;
|
||||
|
||||
/// This is length in terms of samples.
|
||||
/// Note a sample takes up different number of bytes in different buffer formats.
|
||||
u32_dsp length;
|
||||
|
||||
enum class MonoOrStereo : u16_le {
|
||||
Mono = 1,
|
||||
Stereo = 2,
|
||||
};
|
||||
|
||||
enum class Format : u16_le {
|
||||
PCM8 = 0,
|
||||
PCM16 = 1,
|
||||
ADPCM = 2,
|
||||
};
|
||||
|
||||
union {
|
||||
u16_le flags1Raw;
|
||||
BitField<0, 2, MonoOrStereo> monoOrStereo;
|
||||
BitField<2, 2, Format> format;
|
||||
BitField<5, 1, u16> fadeIn;
|
||||
};
|
||||
|
||||
/// ADPCM Predictor (4 bit) and Scale (4 bit)
|
||||
union {
|
||||
u16_le adpcm_ps;
|
||||
BitField<0, 4, u16> adpcmScale;
|
||||
BitField<4, 4, u16> adpcmPredictor;
|
||||
};
|
||||
|
||||
/// ADPCM Historical Samples (y[n-1] and y[n-2])
|
||||
u16_le adpcm_yn[2];
|
||||
|
||||
union {
|
||||
u16_le flags2Raw;
|
||||
BitField<0, 1, u16> adpcmDirty; ///< Has the ADPCM info above been changed?
|
||||
BitField<1, 1, u16> isLooping; ///< Is this a looping buffer?
|
||||
};
|
||||
|
||||
/// Buffer id of embedded buffer (used as a buffer id in SourceStatus to reference this
|
||||
/// buffer).
|
||||
u16_le bufferID;
|
||||
};
|
||||
|
||||
Configuration config[sourceCount];
|
||||
};
|
||||
ASSERT_DSP_STRUCT(SourceConfiguration::Configuration, 192);
|
||||
ASSERT_DSP_STRUCT(SourceConfiguration::Configuration::Buffer, 20);
|
||||
|
||||
struct SourceStatus {
|
||||
struct Status {
|
||||
u8 enabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
|
||||
u8 currentBufferIDDirty; ///< Non-zero when current_buffer_id changes
|
||||
u16_le syncCount; ///< Is set by the DSP to the value of SourceConfiguration::sync_count
|
||||
u32_dsp samplePosition; ///< Number of samples into the current buffer
|
||||
u16_le currentBufferID; ///< Updated when a buffer finishes playing
|
||||
u16_le previousBufferID; ///< Updated when all buffers in the queue finish playing
|
||||
};
|
||||
|
||||
Status status[sourceCount];
|
||||
};
|
||||
ASSERT_DSP_STRUCT(SourceStatus::Status, 12);
|
||||
|
||||
struct DspConfiguration {
|
||||
/// These dirty flags are set by the application when it updates the fields in this struct.
|
||||
/// The DSP clears these each audio frame.
|
||||
union {
|
||||
u32_le dirtyRaw;
|
||||
|
||||
BitField<6, 1, u32> auxFrontBypass0Dirty;
|
||||
BitField<7, 1, u32> auxFrontBypass1Dirty;
|
||||
BitField<8, 1, u32> auxBusEnable0Dirty;
|
||||
BitField<9, 1, u32> auxBusEnable1Dirty;
|
||||
BitField<10, 1, u32> delayEffect0Dirty;
|
||||
BitField<11, 1, u32> delayEffect1Dirty;
|
||||
BitField<12, 1, u32> reverbEffect0Dirty;
|
||||
BitField<13, 1, u32> reverbEffect1Dirty;
|
||||
|
||||
BitField<15, 1, u32> outputBufferCountDirty;
|
||||
BitField<16, 1, u32> masterVolumeDirty;
|
||||
|
||||
BitField<24, 1, u32> auxVolume0Dirty;
|
||||
BitField<25, 1, u32> auxVolume1Dirty;
|
||||
BitField<26, 1, u32> outputFormatDirty;
|
||||
BitField<27, 1, u32> clippingModeDirty;
|
||||
BitField<28, 1, u32> headphonesConnectedDirty;
|
||||
BitField<29, 1, u32> surroundDepthDirty;
|
||||
BitField<30, 1, u32> surroundSpeakerPositionDirty;
|
||||
BitField<31, 1, u32> rearRatioDirty;
|
||||
};
|
||||
|
||||
/// The DSP has three intermediate audio mixers. This controls the volume level (0.0-1.0) for
|
||||
/// each at the final mixer.
|
||||
float_le masterVolume;
|
||||
std::array<float_le, 2> auxVolumes;
|
||||
|
||||
u16_le outputBufferCount;
|
||||
u16 pad1[2];
|
||||
|
||||
enum class OutputFormat : u16_le {
|
||||
Mono = 0,
|
||||
Stereo = 1,
|
||||
Surround = 2,
|
||||
};
|
||||
|
||||
OutputFormat outputFormat;
|
||||
|
||||
u16_le clippingMode; ///< Not sure of the exact gain equation for the limiter.
|
||||
u16_le headphonesConnected; ///< Application updates the DSP on headphone status.
|
||||
|
||||
u16_le surroundDepth;
|
||||
u16_le surroundSpeakerPosition;
|
||||
u16 pad2; ///< TODO: Surround sound related
|
||||
u16_le rearRatio;
|
||||
std::array<u16_le, 2> auxFrontBypass;
|
||||
std::array<u16_le, 2> auxBusEnable;
|
||||
|
||||
/**
|
||||
* This is delay with feedback.
|
||||
* Transfer function:
|
||||
* H(z) = a z^-N / (1 - b z^-1 + a g z^-N)
|
||||
* where
|
||||
* N = frameCount * samplesInFrame
|
||||
* g, a and b are fixed point with 7 fractional bits
|
||||
*/
|
||||
struct DelayEffect {
|
||||
/// These dirty flags are set by the application when it updates the fields in this struct.
|
||||
/// The DSP clears these each audio frame.
|
||||
union {
|
||||
u16_le dirtyRaw;
|
||||
BitField<0, 1, u16> enableDirty;
|
||||
BitField<1, 1, u16> workBufferAddressDirty;
|
||||
BitField<2, 1, u16> otherDirty; ///< Set when anything else has been changed
|
||||
};
|
||||
|
||||
u16_le enable;
|
||||
u16 pad3;
|
||||
u16_le outputs;
|
||||
/// The application allocates a block of memory for the DSP to use as a work buffer.
|
||||
u32_dsp workBufferAddress;
|
||||
/// Frames to delay by
|
||||
u16_le frameCount;
|
||||
|
||||
// Coefficients
|
||||
s16_le g; ///< Fixed point with 7 fractional bits
|
||||
s16_le a; ///< Fixed point with 7 fractional bits
|
||||
s16_le b; ///< Fixed point with 7 fractional bits
|
||||
};
|
||||
|
||||
DelayEffect delayEffect[2];
|
||||
struct ReverbEffect {
|
||||
u16 pad[26]; ///< TODO
|
||||
};
|
||||
|
||||
ReverbEffect reverbEffect[2];
|
||||
|
||||
u16_le syncMode;
|
||||
u16 pad3;
|
||||
union {
|
||||
u32_le dirtyRaw2;
|
||||
|
||||
BitField<16, 1, u32> syncModeDirty;
|
||||
};
|
||||
};
|
||||
ASSERT_DSP_STRUCT(DspConfiguration, 196);
|
||||
ASSERT_DSP_STRUCT(DspConfiguration::DelayEffect, 20);
|
||||
ASSERT_DSP_STRUCT(DspConfiguration::ReverbEffect, 52);
|
||||
static_assert(offsetof(DspConfiguration, syncMode) == 0xBC);
|
||||
static_assert(offsetof(DspConfiguration, dirtyRaw2) == 0xC0);
|
||||
|
||||
struct AdpcmCoefficients {
|
||||
/// Coefficients are signed fixed point with 11 fractional bits.
|
||||
/// Each source has 16 coefficients associated with it.
|
||||
s16_le coeff[sourceCount][16];
|
||||
};
|
||||
ASSERT_DSP_STRUCT(AdpcmCoefficients, 768);
|
||||
|
||||
struct DspStatus {
|
||||
u16_le unknown;
|
||||
u16_le droppedFrames;
|
||||
u16 pad0[0xE];
|
||||
};
|
||||
ASSERT_DSP_STRUCT(DspStatus, 32);
|
||||
|
||||
/// Final mixed output in PCM16 stereo format, what you hear out of the speakers.
|
||||
/// When the application writes to this region it has no effect.
|
||||
struct FinalMixSamples {
|
||||
s16_le pcm16[samplesInFrame][2];
|
||||
};
|
||||
ASSERT_DSP_STRUCT(FinalMixSamples, 640);
|
||||
|
||||
/// DSP writes output of intermediate mixers 1 and 2 here.
|
||||
/// Writes to this region by the application edits the output of the intermediate mixers.
|
||||
/// This seems to be intended to allow the application to do custom effects on the ARM11.
|
||||
/// Values that exceed s16 range will be clipped by the DSP after further processing.
|
||||
struct IntermediateMixSamples {
|
||||
struct Samples {
|
||||
s32_le pcm32[4][samplesInFrame]; ///< Little-endian as opposed to DSP middle-endian.
|
||||
};
|
||||
|
||||
Samples mix1;
|
||||
Samples mix2;
|
||||
};
|
||||
ASSERT_DSP_STRUCT(IntermediateMixSamples, 5120);
|
||||
|
||||
/// Compressor table
|
||||
struct Compressor {
|
||||
u16 pad[0xD20]; ///< TODO
|
||||
};
|
||||
|
||||
/// There is no easy way to implement this in a HLE implementation.
|
||||
struct DspDebug {
|
||||
u16 pad[0x130];
|
||||
};
|
||||
ASSERT_DSP_STRUCT(DspDebug, 0x260);
|
||||
|
||||
struct SharedMemory {
|
||||
/// Padding
|
||||
u16 pad[0x400];
|
||||
|
||||
DspStatus dspStatus;
|
||||
DspDebug dspDebug;
|
||||
FinalMixSamples finalSamples;
|
||||
SourceStatus sourceStatuses;
|
||||
|
||||
Compressor compressor;
|
||||
DspConfiguration dspConfiguration;
|
||||
IntermediateMixSamples intermediateMixSamples;
|
||||
SourceConfiguration sourceConfigurations;
|
||||
AdpcmCoefficients adpcmCoefficients;
|
||||
|
||||
struct {
|
||||
u16 pad[0x100];
|
||||
} unknown10;
|
||||
|
||||
struct {
|
||||
u16 pad[0xC0];
|
||||
} unknown11;
|
||||
|
||||
struct {
|
||||
u16 pad[0x180];
|
||||
} unknown12;
|
||||
|
||||
struct {
|
||||
u16 pad[0xA];
|
||||
} unknown13;
|
||||
|
||||
struct {
|
||||
u16 pad[0x13A3];
|
||||
} unknown14;
|
||||
|
||||
u16_le frameCounter;
|
||||
};
|
||||
ASSERT_DSP_STRUCT(SharedMemory, 0x8000);
|
||||
|
||||
union DspMemory {
|
||||
std::array<u8, 0x80000> rawMemory{};
|
||||
struct {
|
||||
u8 unused0[0x50000];
|
||||
SharedMemory region0;
|
||||
u8 unused1[0x18000];
|
||||
SharedMemory region1;
|
||||
u8 unused2[0x8000];
|
||||
};
|
||||
};
|
||||
static_assert(offsetof(DspMemory, region0) == region0Offset, "DSP region 0 is at the wrong offset");
|
||||
static_assert(offsetof(DspMemory, region1) == region1Offset, "DSP region 1 is at the wrong offset");
|
||||
|
||||
// Structures must have an offset that is a multiple of two.
|
||||
static_assert(offsetof(SharedMemory, frameCounter) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, sourceConfigurations) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, sourceStatuses) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, adpcmCoefficients) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, dspConfiguration) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, dspStatus) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, finalSamples) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, intermediateMixSamples) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, compressor) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, dspDebug) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, unknown10) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, unknown11) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, unknown12) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, unknown13) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
static_assert(offsetof(SharedMemory, unknown14) % 2 == 0, "Structures in HLE::SharedMemory must be 2-byte aligned");
|
||||
|
||||
#undef INSERT_PADDING_DSPWORDS
|
||||
#undef ASSERT_DSP_STRUCT
|
||||
|
||||
} // namespace Audio::HLE
|
78
include/audio/dsp_simd.hpp
Normal file
|
@ -0,0 +1,78 @@
|
|||
#pragma once
|
||||
|
||||
#include "audio/hle_mixer.hpp"
|
||||
#include "compiler_builtins.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
#if defined(_M_AMD64) || defined(__x86_64__)
|
||||
#define DSP_SIMD_X64
|
||||
#include <immintrin.h>
|
||||
#elif defined(_M_ARM64) || defined(__aarch64__)
|
||||
#define DSP_SIMD_ARM64
|
||||
#include <arm_neon.h>
|
||||
#endif
|
||||
|
||||
// Optimized SIMD functions for mixing the stereo output of a DSP voice into a quadraphonic intermediate mix
|
||||
namespace DSP::MixIntoQuad {
|
||||
using IntermediateMix = Audio::DSPMixer::IntermediateMix;
|
||||
using StereoFrame16 = Audio::DSPMixer::StereoFrame<s16>;
|
||||
|
||||
// Non-SIMD, portable algorithm
|
||||
ALWAYS_INLINE static void mixPortable(IntermediateMix& mix, StereoFrame16& frame, const float* gains) {
|
||||
for (usize sampleIndex = 0; sampleIndex < Audio::samplesInFrame; sampleIndex++) {
|
||||
// Mono samples are in the format: (l, r)
|
||||
// When converting to quad, gain0 and gain2 are applied to the left sample, gain1 and gain3 to the right one
|
||||
mix[sampleIndex][0] += s32(frame[sampleIndex][0] * gains[0]);
|
||||
mix[sampleIndex][1] += s32(frame[sampleIndex][1] * gains[1]);
|
||||
mix[sampleIndex][2] += s32(frame[sampleIndex][0] * gains[2]);
|
||||
mix[sampleIndex][3] += s32(frame[sampleIndex][1] * gains[3]);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(DSP_SIMD_X64) && (defined(__SSE4_1__) || defined(__AVX__))
|
||||
ALWAYS_INLINE static void mixSSE4_1(IntermediateMix& mix, StereoFrame16& frame, const float* gains) {
|
||||
__m128 gains_ = _mm_load_ps(gains);
|
||||
|
||||
for (usize sampleIndex = 0; sampleIndex < Audio::samplesInFrame; sampleIndex++) {
|
||||
// The stereo samples, repeated every 4 bytes inside the vector register
|
||||
__m128i stereoSamples = _mm_castps_si128(_mm_load1_ps((float*)&frame[sampleIndex][0]));
|
||||
|
||||
__m128 currentFrame = _mm_cvtepi32_ps(_mm_cvtepi16_epi32(stereoSamples));
|
||||
__m128i offset = _mm_cvttps_epi32(_mm_mul_ps(currentFrame, gains_));
|
||||
__m128i intermediateMixPrev = _mm_load_si128((__m128i*)&mix[sampleIndex][0]);
|
||||
__m128i result = _mm_add_epi32(intermediateMixPrev, offset);
|
||||
_mm_store_si128((__m128i*)&mix[sampleIndex][0], result);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DSP_SIMD_ARM64
|
||||
ALWAYS_INLINE static void mixNEON(IntermediateMix& mix, StereoFrame16& frame, const float* gains) {
|
||||
float32x4_t gains_ = vld1q_f32(gains);
|
||||
|
||||
for (usize sampleIndex = 0; sampleIndex < Audio::samplesInFrame; sampleIndex++) {
|
||||
// Load l and r samples and repeat them every 4 bytes
|
||||
int32x4_t stereoSamples = vld1q_dup_s32((s32*)&frame[sampleIndex][0]);
|
||||
// Expand the bottom 4 s16 samples into an int32x4 with sign extension, then convert them to float32x4
|
||||
float32x4_t currentFrame = vcvtq_f32_s32(vmovl_s16(vget_low_s16(vreinterpretq_s16_s32(stereoSamples))));
|
||||
|
||||
// Multiply samples by their respective gains, truncate the result, and add it into the intermediate mix buffer
|
||||
int32x4_t offset = vcvtq_s32_f32(vmulq_f32(currentFrame, gains_));
|
||||
int32x4_t intermediateMixPrev = vld1q_s32((s32*)&mix[sampleIndex][0]);
|
||||
int32x4_t result = vaddq_s32(intermediateMixPrev, offset);
|
||||
vst1q_s32((s32*)&mix[sampleIndex][0], result);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Mixes the stereo output of a DSP voice into a quadraphonic intermediate mix
|
||||
static void mix(IntermediateMix& mix, StereoFrame16& frame, const float* gains) {
|
||||
#if defined(DSP_SIMD_ARM64)
|
||||
return mixNEON(mix, frame, gains);
|
||||
#elif defined(DSP_SIMD_X64) && (defined(__SSE4_1__) || defined(__AVX__))
|
||||
return mixSSE4_1(mix, frame, gains);
|
||||
#else
|
||||
return mixPortable(mix, frame, gains);
|
||||
#endif
|
||||
}
|
||||
} // namespace DSP::MixIntoQuad
|
227
include/audio/hle_core.hpp
Normal file
|
@ -0,0 +1,227 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "audio/aac.hpp"
|
||||
#include "audio/aac_decoder.hpp"
|
||||
#include "audio/audio_interpolation.hpp"
|
||||
#include "audio/dsp_core.hpp"
|
||||
#include "audio/dsp_shared_mem.hpp"
|
||||
#include "audio/hle_mixer.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
namespace Audio {
|
||||
struct DSPSource {
|
||||
// Audio buffer information
|
||||
// https://www.3dbrew.org/wiki/DSP_Memory_Region
|
||||
struct Buffer {
|
||||
u32 paddr; // Physical address of the buffer
|
||||
u32 sampleCount; // Total number of samples
|
||||
u8 adpcmScale; // ADPCM predictor/scale
|
||||
u8 pad1; // Unknown
|
||||
|
||||
std::array<s16, 2> previousSamples; // ADPCM y[n-1] and y[n-2]
|
||||
bool adpcmDirty;
|
||||
bool looping;
|
||||
u16 bufferID;
|
||||
u8 pad2;
|
||||
|
||||
u32 playPosition = 0; // Current position in the buffer
|
||||
SampleFormat format;
|
||||
SourceType sourceType;
|
||||
|
||||
bool fromQueue = false; // Is this buffer from the buffer queue or an embedded buffer?
|
||||
bool hasPlayedOnce = false; // Has the buffer been played at least once before?
|
||||
|
||||
bool operator<(const Buffer& other) const {
|
||||
// Lower ID = Higher priority
|
||||
// If this buffer ID is greater than the other one, then this buffer has a lower priority
|
||||
return this->bufferID > other.bufferID;
|
||||
}
|
||||
};
|
||||
|
||||
// Buffer of decoded PCM16 samples. TODO: Are there better alternatives to use over deque?
|
||||
using SampleBuffer = std::deque<std::array<s16, 2>>;
|
||||
using BufferQueue = std::priority_queue<Buffer>;
|
||||
using InterpolationMode = HLE::SourceConfiguration::Configuration::InterpolationMode;
|
||||
using InterpolationState = Audio::Interpolation::State;
|
||||
|
||||
// The samples this voice output for this audio frame.
|
||||
// Aligned to 4 for SIMD purposes.
|
||||
alignas(4) DSPMixer::StereoFrame<s16> currentFrame;
|
||||
BufferQueue buffers;
|
||||
|
||||
SampleFormat sampleFormat = SampleFormat::ADPCM;
|
||||
SourceType sourceType = SourceType::Stereo;
|
||||
InterpolationMode interpolationMode = InterpolationMode::Linear;
|
||||
InterpolationState interpolationState;
|
||||
|
||||
// There's one gain configuration for each of the 3 intermediate mixing stages
|
||||
// And each gain configuration is composed of 4 gain values, one for each sample in a quad-channel sample
|
||||
// Aligned to 16 for SIMD purposes
|
||||
alignas(16) std::array<std::array<float, 4>, 3> gains;
|
||||
// Of the 3 intermediate mix stages, typically only the first one is actually enabled and the other ones do nothing
|
||||
// Ie their gain is vec4(0.0). We track which stages are disabled (have a gain of all 0s) using this bitfield and skip them
|
||||
// In order to save up on CPU time.
|
||||
uint enabledMixStages = 0;
|
||||
|
||||
u32 samplePosition; // Sample number into the current audio buffer
|
||||
float rateMultiplier;
|
||||
u16 syncCount;
|
||||
u16 currentBufferID;
|
||||
u16 previousBufferID;
|
||||
|
||||
bool enabled; // Is the source enabled?
|
||||
bool isBufferIDDirty = false; // Did we change buffers?
|
||||
|
||||
// ADPCM decoding info:
|
||||
// An array of fixed point S5.11 coefficients. These provide "weights" for the history samples
|
||||
// The system describing how an ADPCM output sample is generated is
|
||||
// y[n] = x[n] + 0.5 + coeff1 * y[n-1] + coeff2 * y[n-2]
|
||||
// Where y[n] is the output sample we're generating, x[n] is the ADPCM "differential" of the current sample
|
||||
// And coeff1/coeff2 are the coefficients from this array that are used for weighing the history samples
|
||||
std::array<s16, 16> adpcmCoefficients;
|
||||
s16 history1; // y[n-1], the previous output sample
|
||||
s16 history2; // y[n-2], the previous previous output sample
|
||||
|
||||
SampleBuffer currentSamples;
|
||||
int index = 0; // Index of the voice in [0, 23] for debugging
|
||||
|
||||
void reset();
|
||||
|
||||
// Push a buffer to the buffer queue
|
||||
void pushBuffer(const Buffer& buffer) { buffers.push(buffer); }
|
||||
|
||||
// Pop a buffer from the buffer queue and return it
|
||||
Buffer popBuffer() {
|
||||
assert(!buffers.empty());
|
||||
|
||||
Buffer ret = buffers.top();
|
||||
buffers.pop();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
DSPSource() { reset(); }
|
||||
};
|
||||
|
||||
class HLE_DSP : public DSPCore {
|
||||
// The audio frame types are public in case we want to use them for unit tests
|
||||
public:
|
||||
template <typename T, usize channelCount = 1>
|
||||
using Sample = DSPMixer::Sample<T, channelCount>;
|
||||
|
||||
template <typename T, usize channelCount>
|
||||
using Frame = DSPMixer::Frame<T, channelCount>;
|
||||
|
||||
template <typename T>
|
||||
using MonoFrame = DSPMixer::MonoFrame<T>;
|
||||
|
||||
template <typename T>
|
||||
using StereoFrame = DSPMixer::StereoFrame<T>;
|
||||
|
||||
template <typename T>
|
||||
using QuadFrame = DSPMixer::QuadFrame<T>;
|
||||
|
||||
using Source = Audio::DSPSource;
|
||||
using SampleBuffer = Source::SampleBuffer;
|
||||
using IntermediateMix = DSPMixer::IntermediateMix;
|
||||
|
||||
private:
|
||||
enum class DSPState : u32 {
|
||||
Off,
|
||||
On,
|
||||
Slep,
|
||||
};
|
||||
|
||||
// Number of DSP pipes
|
||||
static constexpr size_t pipeCount = 8;
|
||||
DSPState dspState;
|
||||
|
||||
std::array<std::vector<u8>, pipeCount> pipeData; // The data of each pipe
|
||||
std::array<Source, Audio::HLE::sourceCount> sources; // DSP voices
|
||||
Audio::HLE::DspMemory dspRam;
|
||||
|
||||
Audio::DSPMixer mixer;
|
||||
std::unique_ptr<Audio::AAC::Decoder> aacDecoder;
|
||||
|
||||
void resetAudioPipe();
|
||||
bool loaded = false; // Have we loaded a component?
|
||||
|
||||
// Get the index for the current region we'll be reading. Returns the region with the highest frame counter
|
||||
// Accounting for whether one of the frame counters has wrapped around
|
||||
usize readRegionIndex() const {
|
||||
const auto counter0 = dspRam.region0.frameCounter;
|
||||
const auto counter1 = dspRam.region1.frameCounter;
|
||||
|
||||
// Handle wraparound cases first
|
||||
if (counter0 == 0xffff && counter1 != 0xfffe) {
|
||||
return 1;
|
||||
} else if (counter1 == 0xffff && counter0 != 0xfffe) {
|
||||
return 0;
|
||||
} else {
|
||||
return (counter0 > counter1) ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
// DSP shared memory is double buffered; One region is being written to while the other one is being read from
|
||||
Audio::HLE::SharedMemory& readRegion() { return readRegionIndex() == 0 ? dspRam.region0 : dspRam.region1; }
|
||||
Audio::HLE::SharedMemory& writeRegion() { return readRegionIndex() == 0 ? dspRam.region1 : dspRam.region0; }
|
||||
|
||||
// Get a pointer of type T* to the data starting from physical address paddr
|
||||
template <typename T>
|
||||
T* getPointerPhys(u32 paddr, u32 size = 0) {
|
||||
if (paddr >= PhysicalAddrs::FCRAM && paddr + size <= PhysicalAddrs::FCRAMEnd) {
|
||||
u8* fcram = mem.getFCRAM();
|
||||
u32 index = paddr - PhysicalAddrs::FCRAM;
|
||||
|
||||
return (T*)&fcram[index];
|
||||
} else if (paddr >= PhysicalAddrs::DSP_RAM && paddr + size <= PhysicalAddrs::DSP_RAM_End) {
|
||||
u32 index = paddr - PhysicalAddrs::DSP_RAM;
|
||||
return (T*)&dspRam.rawMemory[index];
|
||||
} else [[unlikely]] {
|
||||
Helpers::warn("[DSP] Tried to access unknown physical address: %08X", paddr);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void handleAACRequest(const AAC::Message& request);
|
||||
void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients);
|
||||
void updateMixerConfig(HLE::SharedMemory& sharedMem);
|
||||
void generateFrame(StereoFrame<s16>& frame);
|
||||
void generateFrame(DSPSource& source);
|
||||
void outputFrame();
|
||||
// Perform the final mix, mixing the quadraphonic samples from all voices into the output audio frame
|
||||
void performMix(Audio::HLE::SharedMemory& readRegion, Audio::HLE::SharedMemory& writeRegion);
|
||||
|
||||
// Decode an entire buffer worth of audio
|
||||
void decodeBuffer(DSPSource& source);
|
||||
|
||||
SampleBuffer decodePCM8(const u8* data, usize sampleCount, Source& source);
|
||||
SampleBuffer decodePCM16(const u8* data, usize sampleCount, Source& source);
|
||||
SampleBuffer decodeADPCM(const u8* data, usize sampleCount, Source& source);
|
||||
|
||||
public:
|
||||
HLE_DSP(Memory& mem, Scheduler& scheduler, DSPService& dspService, EmulatorConfig& config);
|
||||
~HLE_DSP() override {}
|
||||
|
||||
void reset() override;
|
||||
void runAudioFrame(u64 eventTimestamp) override;
|
||||
|
||||
u8* getDspMemory() override { return dspRam.rawMemory.data(); }
|
||||
|
||||
u16 recvData(u32 regId) override;
|
||||
bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready
|
||||
void writeProcessPipe(u32 channel, u32 size, u32 buffer) override;
|
||||
std::vector<u8> readPipe(u32 channel, u32 peer, u32 size, u32 buffer) override;
|
||||
|
||||
void loadComponent(std::vector<u8>& data, u32 programMask, u32 dataMask) override;
|
||||
void unloadComponent() override;
|
||||
void setSemaphore(u16 value) override {}
|
||||
void setSemaphoreMask(u16 value) override {}
|
||||
};
|
||||
} // namespace Audio
|
50
include/audio/hle_mixer.hpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
|
||||
#include "audio/dsp_shared_mem.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace Audio {
|
||||
using SampleFormat = HLE::SourceConfiguration::Configuration::Format;
|
||||
using SourceType = HLE::SourceConfiguration::Configuration::MonoOrStereo;
|
||||
|
||||
class DSPMixer {
|
||||
public:
|
||||
template <typename T, usize channelCount = 1>
|
||||
using Sample = std::array<T, channelCount>;
|
||||
|
||||
template <typename T, usize channelCount>
|
||||
using Frame = std::array<Sample<T, channelCount>, 160>;
|
||||
|
||||
template <typename T>
|
||||
using MonoFrame = Frame<T, 1>;
|
||||
|
||||
template <typename T>
|
||||
using StereoFrame = Frame<T, 2>;
|
||||
|
||||
template <typename T>
|
||||
using QuadFrame = Frame<T, 4>;
|
||||
|
||||
// Internally the DSP uses four channels when mixing.
|
||||
// Neatly, QuadFrame<s32> means that every sample is a uint32x4 value, which is particularly nice for SIMD mixing
|
||||
using IntermediateMix = QuadFrame<s32>;
|
||||
|
||||
private:
|
||||
using ChannelFormat = HLE::DspConfiguration::OutputFormat;
|
||||
// The audio from each DSP voice is converted to quadraphonic and then fed into 3 intermediate mixing stages
|
||||
// Two of these intermediate mixers (second and third) are used for effects, including custom effects done on the CPU
|
||||
static constexpr usize mixerStageCount = 3;
|
||||
|
||||
public:
|
||||
ChannelFormat channelFormat = ChannelFormat::Stereo;
|
||||
std::array<float, mixerStageCount> volumes;
|
||||
std::array<bool, 2> enableAuxStages;
|
||||
|
||||
void reset() {
|
||||
channelFormat = ChannelFormat::Stereo;
|
||||
|
||||
volumes.fill(0.0);
|
||||
enableAuxStages.fill(false);
|
||||
}
|
||||
};
|
||||
} // namespace Audio
|
61
include/audio/libretro_audio_device.hpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
#include <cstring>
|
||||
|
||||
#include "audio/audio_device_interface.hpp"
|
||||
|
||||
class LibretroAudioDevice final : public AudioDeviceInterface {
|
||||
bool initialized = false;
|
||||
|
||||
public:
|
||||
LibretroAudioDevice(const AudioDeviceConfig& audioSettings) : AudioDeviceInterface(nullptr, audioSettings), initialized(false) {
|
||||
running = false;
|
||||
}
|
||||
|
||||
void init(Samples& samples, bool safe = false) override {
|
||||
this->samples = &samples;
|
||||
|
||||
initialized = true;
|
||||
running = false;
|
||||
}
|
||||
|
||||
void close() override {
|
||||
initialized = false;
|
||||
running = false;
|
||||
};
|
||||
|
||||
void start() override { running = true; }
|
||||
void stop() override { running = false; };
|
||||
|
||||
void renderBatch(RenderBatchCallback callback) override {
|
||||
if (running) {
|
||||
static constexpr usize sampleRate = 32768; // 3DS samples per second
|
||||
static constexpr usize frameCount = sampleRate / 60; // 3DS samples per video frame
|
||||
static constexpr usize channelCount = 2;
|
||||
static s16 audioBuffer[frameCount * channelCount];
|
||||
|
||||
usize samplesWritten = 0;
|
||||
samplesWritten += samples->pop(audioBuffer, frameCount * channelCount);
|
||||
|
||||
// Get the last sample for underrun handling
|
||||
if (samplesWritten != 0) {
|
||||
std::memcpy(&lastStereoSample[0], &audioBuffer[(samplesWritten - 1) * 2], sizeof(lastStereoSample));
|
||||
}
|
||||
|
||||
// If underruning, copy the last output sample
|
||||
{
|
||||
s16* pointer = &audioBuffer[samplesWritten * 2];
|
||||
s16 l = lastStereoSample[0];
|
||||
s16 r = lastStereoSample[1];
|
||||
|
||||
for (usize i = samplesWritten; i < frameCount; i++) {
|
||||
*pointer++ = l;
|
||||
*pointer++ = r;
|
||||
}
|
||||
}
|
||||
|
||||
callback(audioBuffer, sizeof(audioBuffer) / (channelCount * sizeof(s16)));
|
||||
}
|
||||
}
|
||||
|
||||
bool isInitialized() const { return initialized; }
|
||||
};
|
33
include/audio/miniaudio_device.hpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
#include <atomic>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "audio/audio_device_interface.hpp"
|
||||
#include "miniaudio.h"
|
||||
|
||||
class MiniAudioDevice final : public AudioDeviceInterface {
|
||||
static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate
|
||||
static constexpr ma_uint32 channelCount = 2; // Audio output is stereo
|
||||
|
||||
bool initialized = false;
|
||||
|
||||
ma_device device;
|
||||
ma_context context;
|
||||
ma_device_config deviceConfig;
|
||||
|
||||
// Store the last stereo sample we output. We play this when underruning to avoid pops.
|
||||
std::vector<std::string> audioDevices;
|
||||
|
||||
public:
|
||||
MiniAudioDevice(const AudioDeviceConfig& audioSettings);
|
||||
|
||||
// If safe is on, we create a null audio device
|
||||
void init(Samples& samples, bool safe = false) override;
|
||||
void close() override;
|
||||
|
||||
void start() override;
|
||||
void stop() override;
|
||||
|
||||
bool isInitialized() const { return initialized; }
|
||||
};
|
46
include/audio/null_core.hpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
|
||||
#include "audio/dsp_core.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
namespace Audio {
|
||||
class NullDSP : public DSPCore {
|
||||
enum class DSPState : u32 {
|
||||
Off,
|
||||
On,
|
||||
Slep,
|
||||
};
|
||||
|
||||
// Number of DSP pipes
|
||||
static constexpr size_t pipeCount = 8;
|
||||
DSPState dspState;
|
||||
|
||||
std::array<std::vector<u8>, pipeCount> pipeData; // The data of each pipe
|
||||
std::array<u8, Memory::DSP_RAM_SIZE> dspRam;
|
||||
|
||||
void resetAudioPipe();
|
||||
bool loaded = false; // Have we loaded a component?
|
||||
|
||||
public:
|
||||
NullDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService, EmulatorConfig& config) : DSPCore(mem, scheduler, dspService, config) {}
|
||||
~NullDSP() override {}
|
||||
|
||||
void reset() override;
|
||||
void runAudioFrame(u64 eventTimestamp) override;
|
||||
|
||||
u8* getDspMemory() override { return dspRam.data(); }
|
||||
|
||||
u16 recvData(u32 regId) override;
|
||||
bool recvDataIsReady(u32 regId) override { return true; } // Treat data as always ready
|
||||
void writeProcessPipe(u32 channel, u32 size, u32 buffer) override;
|
||||
std::vector<u8> readPipe(u32 channel, u32 peer, u32 size, u32 buffer) override;
|
||||
|
||||
// NOPs for null DSP core
|
||||
void loadComponent(std::vector<u8>& data, u32 programMask, u32 dataMask) override;
|
||||
void unloadComponent() override;
|
||||
void setSemaphore(u16 value) override {}
|
||||
void setSemaphoreMask(u16 value) override {}
|
||||
};
|
||||
|
||||
} // namespace Audio
|
104
include/audio/teakra_core.hpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
|
||||
#include "audio/dsp_core.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "swap.hpp"
|
||||
#include "teakra/teakra.h"
|
||||
|
||||
namespace Audio {
|
||||
class TeakraDSP : public DSPCore {
|
||||
Teakra::Teakra teakra;
|
||||
u32 pipeBaseAddr;
|
||||
bool running; // Is the DSP running?
|
||||
bool loaded; // Have we finished loading a binary with LoadComponent?
|
||||
bool signalledData;
|
||||
bool signalledSemaphore;
|
||||
|
||||
uint audioFrameIndex = 0; // Index in our audio frame
|
||||
std::array<s16, 160 * 2> audioFrame;
|
||||
|
||||
// Get a pointer to a data memory address
|
||||
u8* getDataPointer(u32 address) { return getDspMemory() + Memory::DSP_DATA_MEMORY_OFFSET + address; }
|
||||
|
||||
enum class PipeDirection {
|
||||
DSPtoCPU = 0,
|
||||
CPUtoDSP = 1,
|
||||
};
|
||||
|
||||
// A lot of Teakra integration code, especially pipe stuff is based on Citra's integration here:
|
||||
// https://github.com/citra-emu/citra/blob/master/src/audio_core/lle/lle.cpp
|
||||
struct PipeStatus {
|
||||
// All addresses and sizes here refer to byte values, NOT 16-bit values.
|
||||
u16_le address;
|
||||
u16_le byteSize;
|
||||
u16_le readPointer;
|
||||
u16_le writePointer;
|
||||
u8 slot;
|
||||
u8 flags;
|
||||
|
||||
static constexpr u16 wrapBit = 0x8000;
|
||||
static constexpr u16 pointerMask = 0x7FFF;
|
||||
|
||||
bool isFull() const { return (readPointer ^ writePointer) == wrapBit; }
|
||||
bool isEmpty() const { return (readPointer ^ writePointer) == 0; }
|
||||
|
||||
// isWrapped: Are read and write pointers in different memory passes.
|
||||
// true: xxxx]----[xxxx (data is wrapping around the end of memory)
|
||||
// false: ----[xxxx]----
|
||||
bool isWrapped() const { return (readPointer ^ writePointer) >= wrapBit; }
|
||||
};
|
||||
static_assert(sizeof(PipeStatus) == 10, "Teakra: Pipe Status size is wrong");
|
||||
static constexpr u8 pipeToSlotIndex(u8 pipe, PipeDirection direction) { return (pipe * 2) + u8(direction); }
|
||||
|
||||
PipeStatus getPipeStatus(u8 pipe, PipeDirection direction) {
|
||||
PipeStatus ret;
|
||||
const u8 index = pipeToSlotIndex(pipe, direction);
|
||||
|
||||
std::memcpy(&ret, getDataPointer(pipeBaseAddr * 2 + index * sizeof(PipeStatus)), sizeof(PipeStatus));
|
||||
return ret;
|
||||
}
|
||||
|
||||
void updatePipeStatus(const PipeStatus& status) {
|
||||
u8 slot = status.slot;
|
||||
u8* statusAddress = getDataPointer(pipeBaseAddr * 2 + slot * sizeof(PipeStatus));
|
||||
|
||||
if (slot % 2 == 0) {
|
||||
std::memcpy(statusAddress + 4, &status.readPointer, sizeof(u16));
|
||||
} else {
|
||||
std::memcpy(statusAddress + 6, &status.writePointer, sizeof(u16));
|
||||
}
|
||||
}
|
||||
// Run 1 slice of DSP instructions
|
||||
void runSlice() {
|
||||
if (running) {
|
||||
teakra.Run(Audio::lleSlice);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
TeakraDSP(Memory& mem, Scheduler& scheduler, DSPService& dspService, EmulatorConfig& config);
|
||||
~TeakraDSP() override {}
|
||||
|
||||
void reset() override;
|
||||
|
||||
// Run 1 slice of DSP instructions and schedule the next audio frame
|
||||
void runAudioFrame(u64 eventTimestamp) override {
|
||||
runSlice();
|
||||
scheduler.addEvent(Scheduler::EventType::RunDSP, scheduler.currentTimestamp + Audio::lleSlice * 2);
|
||||
}
|
||||
|
||||
void setAudioEnabled(bool enable) override;
|
||||
u8* getDspMemory() override { return teakra.GetDspMemory().data(); }
|
||||
|
||||
u16 recvData(u32 regId) override { return teakra.RecvData(regId); }
|
||||
bool recvDataIsReady(u32 regId) override { return teakra.RecvDataIsReady(regId); }
|
||||
void setSemaphore(u16 value) override { teakra.SetSemaphore(value); }
|
||||
void setSemaphoreMask(u16 value) override { teakra.MaskSemaphore(value); }
|
||||
|
||||
void writeProcessPipe(u32 channel, u32 size, u32 buffer) override;
|
||||
std::vector<u8> readPipe(u32 channel, u32 peer, u32 size, u32 buffer) override;
|
||||
void loadComponent(std::vector<u8>& data, u32 programMask, u32 dataMask) override;
|
||||
void unloadComponent() override;
|
||||
};
|
||||
} // namespace Audio
|
413
include/bitfield.hpp
Normal file
|
@ -0,0 +1,413 @@
|
|||
// Copyright 2014 Dolphin Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
// Copyright 2014 Tony Wasserka
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// * Neither the name of the owner nor the names of its contributors may
|
||||
// be used to endorse or promote products derived from this software
|
||||
// without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
|
||||
#include "compiler_builtins.hpp"
|
||||
/*
|
||||
* Abstract bitfield class
|
||||
*
|
||||
* Allows endianness-independent access to individual bitfields within some raw
|
||||
* integer value. The assembly generated by this class is identical to the
|
||||
* usage of raw bitfields, so it's a perfectly fine replacement.
|
||||
*
|
||||
* For BitField<X,Y,Z>, X is the distance of the bitfield to the LSB of the
|
||||
* raw value, Y is the length in bits of the bitfield. Z is an integer type
|
||||
* which determines the sign of the bitfield. Z must have the same size as the
|
||||
* raw integer.
|
||||
*
|
||||
*
|
||||
* General usage:
|
||||
*
|
||||
* Create a new union with the raw integer value as a member.
|
||||
* Then for each bitfield you want to expose, add a BitField member
|
||||
* in the union. The template parameters are the bit offset and the number
|
||||
* of desired bits.
|
||||
*
|
||||
* Changes in the bitfield members will then get reflected in the raw integer
|
||||
* value and vice-versa.
|
||||
*
|
||||
*
|
||||
* Sample usage:
|
||||
*
|
||||
* union SomeRegister
|
||||
* {
|
||||
* u32 hex;
|
||||
*
|
||||
* BitField<0,7,u32> first_seven_bits; // unsigned
|
||||
* BitField<7,8,u32> next_eight_bits; // unsigned
|
||||
* BitField<3,15,s32> some_signed_fields; // signed
|
||||
* };
|
||||
*
|
||||
* This is equivalent to the little-endian specific code:
|
||||
*
|
||||
* union SomeRegister
|
||||
* {
|
||||
* u32 hex;
|
||||
*
|
||||
* struct
|
||||
* {
|
||||
* u32 first_seven_bits : 7;
|
||||
* u32 next_eight_bits : 8;
|
||||
* };
|
||||
* struct
|
||||
* {
|
||||
* u32 : 3; // padding
|
||||
* s32 some_signed_fields : 15;
|
||||
* };
|
||||
* };
|
||||
*
|
||||
*
|
||||
* Caveats:
|
||||
*
|
||||
* 1)
|
||||
* BitField provides automatic casting from and to the storage type where
|
||||
* appropriate. However, when using non-typesafe functions like printf, an
|
||||
* explicit cast must be performed on the BitField object to make sure it gets
|
||||
* passed correctly, e.g.:
|
||||
* printf("Value: %d", (s32)some_register.some_signed_fields);
|
||||
* Note that this does not apply when using fmt, as a formatter is provided that
|
||||
* handles this conversion automatically.
|
||||
*
|
||||
* 2)
|
||||
* Not really a caveat, but potentially irritating: This class is used in some
|
||||
* packed structures that do not guarantee proper alignment. Therefore we have
|
||||
* to use #pragma pack here not to pack the members of the class, but instead
|
||||
* to break GCC's assumption that the members of the class are aligned on
|
||||
* sizeof(StorageType).
|
||||
* TODO(neobrain): Confirm that this is a proper fix and not just masking
|
||||
* symptoms.
|
||||
*/
|
||||
#pragma pack(1)
|
||||
template <
|
||||
std::size_t position, std::size_t bits, typename T,
|
||||
// StorageType is T for non-enum types and the underlying type of T if
|
||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||
// former case to workaround compile errors which arise when using
|
||||
// std::underlying_type<T>::type directly.
|
||||
typename StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>, std::enable_if<true, T>>::type>
|
||||
struct BitField {
|
||||
private:
|
||||
// This constructor might be considered ambiguous:
|
||||
// Would it initialize the storage or just the bitfield?
|
||||
// Hence, delete it. Use the assignment operator to set bitfield values!
|
||||
BitField(T val) = delete;
|
||||
|
||||
public:
|
||||
// Force default constructor to be created
|
||||
// so that we can use this within unions
|
||||
constexpr BitField() = default;
|
||||
|
||||
// We explicitly delete the copy assignment operator here, because the
|
||||
// default copy assignment would copy the full storage value, rather than
|
||||
// just the bits relevant to this particular bit field.
|
||||
// Ideally, we would just implement the copy assignment to copy only the
|
||||
// relevant bits, but we're prevented from doing that because the savestate
|
||||
// code expects that this class is trivially copyable.
|
||||
BitField& operator=(const BitField&) = delete;
|
||||
|
||||
ALWAYS_INLINE BitField& operator=(T val) {
|
||||
storage = (storage & ~GetMask()) | ((static_cast<StorageType>(val) << position) & GetMask());
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr T Value() const { return Value(std::is_signed<T>()); }
|
||||
constexpr operator T() const { return Value(); }
|
||||
static constexpr bool IsSigned() { return std::is_signed<T>(); }
|
||||
static constexpr std::size_t StartBit() { return position; }
|
||||
static constexpr std::size_t NumBits() { return bits; }
|
||||
|
||||
private:
|
||||
// Unsigned version of StorageType
|
||||
using StorageTypeU = std::make_unsigned_t<StorageType>;
|
||||
|
||||
constexpr T Value(std::true_type) const {
|
||||
const size_t shift_amount = 8 * sizeof(StorageType) - bits;
|
||||
return static_cast<T>((storage << (shift_amount - position)) >> shift_amount);
|
||||
}
|
||||
|
||||
constexpr T Value(std::false_type) const { return static_cast<T>((storage & GetMask()) >> position); }
|
||||
|
||||
static constexpr StorageType GetMask() { return (std::numeric_limits<StorageTypeU>::max() >> (8 * sizeof(StorageType) - bits)) << position; }
|
||||
|
||||
StorageType storage;
|
||||
|
||||
static_assert(bits + position <= 8 * sizeof(StorageType), "Bitfield out of range");
|
||||
static_assert(sizeof(T) <= sizeof(StorageType), "T must fit in StorageType");
|
||||
|
||||
// And, you know, just in case people specify something stupid like bits=position=0x80000000
|
||||
static_assert(position < 8 * sizeof(StorageType), "Invalid position");
|
||||
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
|
||||
static_assert(bits > 0, "Invalid number of bits");
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
// Language limitations require the following to make these formattable
|
||||
// (formatter<BitFieldArray<position, bits, size, T>::Ref> is not legal)
|
||||
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||
class BitFieldArrayConstRef;
|
||||
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||
class BitFieldArrayRef;
|
||||
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||
class BitFieldArrayConstIterator;
|
||||
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||
class BitFieldArrayIterator;
|
||||
|
||||
#pragma pack(1)
|
||||
template <
|
||||
std::size_t position, std::size_t bits, std::size_t size, typename T,
|
||||
// StorageType is T for non-enum types and the underlying type of T if
|
||||
// T is an enumeration. Note that T is wrapped within an enable_if in the
|
||||
// former case to workaround compile errors which arise when using
|
||||
// std::underlying_type<T>::type directly.
|
||||
typename StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>, std::enable_if<true, T>>::type>
|
||||
struct BitFieldArray {
|
||||
using Ref = BitFieldArrayRef<position, bits, size, T, StorageType>;
|
||||
using ConstRef = BitFieldArrayConstRef<position, bits, size, T, StorageType>;
|
||||
using Iterator = BitFieldArrayIterator<position, bits, size, T, StorageType>;
|
||||
using ConstIterator = BitFieldArrayConstIterator<position, bits, size, T, StorageType>;
|
||||
|
||||
private:
|
||||
// This constructor might be considered ambiguous:
|
||||
// Would it initialize the storage or just the bitfield?
|
||||
// Hence, delete it. Use the assignment operator to set bitfield values!
|
||||
BitFieldArray(T val) = delete;
|
||||
|
||||
public:
|
||||
// Force default constructor to be created
|
||||
// so that we can use this within unions
|
||||
constexpr BitFieldArray() = default;
|
||||
|
||||
// We explicitly delete the copy assignment operator here, because the
|
||||
// default copy assignment would copy the full storage value, rather than
|
||||
// just the bits relevant to this particular bit field.
|
||||
// Ideally, we would just implement the copy assignment to copy only the
|
||||
// relevant bits, but we're prevented from doing that because the savestate
|
||||
// code expects that this class is trivially copyable.
|
||||
BitFieldArray& operator=(const BitFieldArray&) = delete;
|
||||
|
||||
public:
|
||||
constexpr bool IsSigned() const { return std::is_signed<T>(); }
|
||||
constexpr std::size_t StartBit() const { return position; }
|
||||
constexpr std::size_t NumBits() const { return bits; }
|
||||
constexpr std::size_t Size() const { return size; }
|
||||
constexpr std::size_t TotalNumBits() const { return bits * size; }
|
||||
|
||||
constexpr T Value(size_t index) const { return Value(std::is_signed<T>(), index); }
|
||||
void SetValue(size_t index, T value) {
|
||||
const size_t pos = position + bits * index;
|
||||
storage = (storage & ~GetElementMask(index)) | ((static_cast<StorageType>(value) << pos) & GetElementMask(index));
|
||||
}
|
||||
Ref operator[](size_t index) { return Ref(this, index); }
|
||||
constexpr const ConstRef operator[](size_t index) const { return ConstRef(this, index); }
|
||||
|
||||
constexpr Iterator begin() { return Iterator(this, 0); }
|
||||
constexpr Iterator end() { return Iterator(this, size); }
|
||||
constexpr ConstIterator begin() const { return ConstIterator(this, 0); }
|
||||
constexpr ConstIterator end() const { return ConstIterator(this, size); }
|
||||
constexpr ConstIterator cbegin() const { return begin(); }
|
||||
constexpr ConstIterator cend() const { return end(); }
|
||||
|
||||
private:
|
||||
// Unsigned version of StorageType
|
||||
using StorageTypeU = std::make_unsigned_t<StorageType>;
|
||||
|
||||
constexpr T Value(std::true_type, size_t index) const {
|
||||
const size_t pos = position + bits * index;
|
||||
const size_t shift_amount = 8 * sizeof(StorageType) - bits;
|
||||
return static_cast<T>((storage << (shift_amount - pos)) >> shift_amount);
|
||||
}
|
||||
|
||||
constexpr T Value(std::false_type, size_t index) const {
|
||||
const size_t pos = position + bits * index;
|
||||
return static_cast<T>((storage & GetElementMask(index)) >> pos);
|
||||
}
|
||||
|
||||
static constexpr StorageType GetElementMask(size_t index) {
|
||||
const size_t pos = position + bits * index;
|
||||
return (std::numeric_limits<StorageTypeU>::max() >> (8 * sizeof(StorageType) - bits)) << pos;
|
||||
}
|
||||
|
||||
StorageType storage;
|
||||
|
||||
static_assert(bits * size + position <= 8 * sizeof(StorageType), "Bitfield array out of range");
|
||||
static_assert(sizeof(T) <= sizeof(StorageType), "T must fit in StorageType");
|
||||
|
||||
// And, you know, just in case people specify something stupid like bits=position=0x80000000
|
||||
static_assert(position < 8 * sizeof(StorageType), "Invalid position");
|
||||
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
|
||||
static_assert(bits > 0, "Invalid number of bits");
|
||||
static_assert(size <= 8 * sizeof(StorageType), "Invalid size");
|
||||
static_assert(size > 0, "Invalid size");
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||
class BitFieldArrayConstRef {
|
||||
friend struct BitFieldArray<position, bits, size, T, S>;
|
||||
friend class BitFieldArrayConstIterator<position, bits, size, T, S>;
|
||||
|
||||
public:
|
||||
constexpr T Value() const { return m_array->Value(m_index); };
|
||||
constexpr operator T() const { return Value(); }
|
||||
|
||||
private:
|
||||
constexpr BitFieldArrayConstRef(const BitFieldArray<position, bits, size, T, S>* array, size_t index) : m_array(array), m_index(index) {}
|
||||
|
||||
const BitFieldArray<position, bits, size, T, S>* const m_array;
|
||||
const size_t m_index;
|
||||
};
|
||||
|
||||
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||
class BitFieldArrayRef {
|
||||
friend struct BitFieldArray<position, bits, size, T, S>;
|
||||
friend class BitFieldArrayIterator<position, bits, size, T, S>;
|
||||
|
||||
public:
|
||||
constexpr T Value() const { return m_array->Value(m_index); };
|
||||
constexpr operator T() const { return Value(); }
|
||||
T operator=(const BitFieldArrayRef<position, bits, size, T, S>& value) const {
|
||||
m_array->SetValue(m_index, value);
|
||||
return value;
|
||||
}
|
||||
T operator=(T value) const {
|
||||
m_array->SetValue(m_index, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
constexpr BitFieldArrayRef(BitFieldArray<position, bits, size, T, S>* array, size_t index) : m_array(array), m_index(index) {}
|
||||
|
||||
BitFieldArray<position, bits, size, T, S>* const m_array;
|
||||
const size_t m_index;
|
||||
};
|
||||
|
||||
// Satisfies LegacyOutputIterator / std::output_iterator.
|
||||
// Does not satisfy LegacyInputIterator / std::input_iterator as std::output_iterator_tag does not
|
||||
// extend std::input_iterator_tag.
|
||||
// Does not satisfy LegacyForwardIterator / std::forward_iterator, as that requires use of real
|
||||
// references instead of proxy objects.
|
||||
// This iterator allows use of BitFieldArray in range-based for loops, and with fmt::join.
|
||||
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||
class BitFieldArrayIterator {
|
||||
friend struct BitFieldArray<position, bits, size, T, S>;
|
||||
|
||||
public:
|
||||
using iterator_category = std::output_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = void;
|
||||
using reference = BitFieldArrayRef<position, bits, size, T, S>;
|
||||
|
||||
private:
|
||||
constexpr BitFieldArrayIterator(BitFieldArray<position, bits, size, T, S>* array, size_t index) : m_array(array), m_index(index) {}
|
||||
|
||||
public:
|
||||
// Required by std::input_or_output_iterator
|
||||
constexpr BitFieldArrayIterator() = default;
|
||||
// Required by LegacyIterator
|
||||
constexpr BitFieldArrayIterator(const BitFieldArrayIterator& other) = default;
|
||||
// Required by LegacyIterator
|
||||
BitFieldArrayIterator& operator=(const BitFieldArrayIterator& other) = default;
|
||||
// Move constructor and assignment operators, explicitly defined for completeness
|
||||
constexpr BitFieldArrayIterator(BitFieldArrayIterator&& other) = default;
|
||||
BitFieldArrayIterator& operator=(BitFieldArrayIterator&& other) = default;
|
||||
|
||||
public:
|
||||
BitFieldArrayIterator& operator++() {
|
||||
m_index++;
|
||||
return *this;
|
||||
}
|
||||
BitFieldArrayIterator operator++(int) {
|
||||
BitFieldArrayIterator other(*this);
|
||||
++*this;
|
||||
return other;
|
||||
}
|
||||
constexpr reference operator*() const { return reference(m_array, m_index); }
|
||||
constexpr bool operator==(BitFieldArrayIterator other) const { return m_index == other.m_index; }
|
||||
constexpr bool operator!=(BitFieldArrayIterator other) const { return m_index != other.m_index; }
|
||||
|
||||
private:
|
||||
BitFieldArray<position, bits, size, T, S>* m_array;
|
||||
size_t m_index;
|
||||
};
|
||||
|
||||
// Satisfies LegacyInputIterator / std::input_iterator.
|
||||
// Does not satisfy LegacyForwardIterator / std::forward_iterator, as that requires use of real
|
||||
// references instead of proxy objects.
|
||||
// This iterator allows use of BitFieldArray in range-based for loops, and with fmt::join.
|
||||
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||
class BitFieldArrayConstIterator {
|
||||
friend struct BitFieldArray<position, bits, size, T, S>;
|
||||
|
||||
public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using value_type = T;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = void;
|
||||
using reference = BitFieldArrayConstRef<position, bits, size, T, S>;
|
||||
|
||||
private:
|
||||
constexpr BitFieldArrayConstIterator(const BitFieldArray<position, bits, size, T, S>* array, size_t index) : m_array(array), m_index(index) {}
|
||||
|
||||
public:
|
||||
// Required by std::input_or_output_iterator
|
||||
constexpr BitFieldArrayConstIterator() = default;
|
||||
// Required by LegacyIterator
|
||||
constexpr BitFieldArrayConstIterator(const BitFieldArrayConstIterator& other) = default;
|
||||
// Required by LegacyIterator
|
||||
BitFieldArrayConstIterator& operator=(const BitFieldArrayConstIterator& other) = default;
|
||||
// Move constructor and assignment operators, explicitly defined for completeness
|
||||
constexpr BitFieldArrayConstIterator(BitFieldArrayConstIterator&& other) = default;
|
||||
BitFieldArrayConstIterator& operator=(BitFieldArrayConstIterator&& other) = default;
|
||||
|
||||
public:
|
||||
BitFieldArrayConstIterator& operator++() {
|
||||
m_index++;
|
||||
return *this;
|
||||
}
|
||||
BitFieldArrayConstIterator operator++(int) {
|
||||
BitFieldArrayConstIterator other(*this);
|
||||
++*this;
|
||||
return other;
|
||||
}
|
||||
constexpr reference operator*() const { return reference(m_array, m_index); }
|
||||
constexpr bool operator==(BitFieldArrayConstIterator other) const { return m_index == other.m_index; }
|
||||
constexpr bool operator!=(BitFieldArrayConstIterator other) const { return m_index != other.m_index; }
|
||||
|
||||
private:
|
||||
const BitFieldArray<position, bits, size, T, S>* m_array;
|
||||
size_t m_index;
|
||||
};
|
53
include/capstone.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
#include <capstone/capstone.h>
|
||||
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include "helpers.hpp"
|
||||
|
||||
namespace Common {
|
||||
class CapstoneDisassembler {
|
||||
csh handle; // Handle to our disassembler object
|
||||
cs_insn* instructions = nullptr; // Pointer to instruction object
|
||||
bool initialized = false;
|
||||
|
||||
public:
|
||||
bool isInitialized() { return initialized; }
|
||||
void init(cs_arch arch, cs_mode mode) { initialized = (cs_open(arch, mode, &handle) == CS_ERR_OK); }
|
||||
|
||||
CapstoneDisassembler() {}
|
||||
CapstoneDisassembler(cs_arch arch, cs_mode mode) { init(arch, mode); }
|
||||
|
||||
// Returns the number of instructions successfully disassembled
|
||||
// pc: program counter of the instruction to disassemble
|
||||
// bytes: Byte representation of instruction
|
||||
// buffer: text buffer to output the disassembly too
|
||||
usize disassemble(std::string& buffer, u32 pc, std::span<u8> bytes, u64 offset = 0) {
|
||||
if (!initialized) {
|
||||
buffer = "Capstone was not properly initialized";
|
||||
return 0;
|
||||
}
|
||||
|
||||
usize count = cs_disasm(handle, bytes.data(), bytes.size(), pc, offset, &instructions);
|
||||
if (count == 0) {
|
||||
// Error in disassembly, quit early and return empty string
|
||||
buffer = "Error disassembling instructions with Capstone";
|
||||
return 0;
|
||||
}
|
||||
|
||||
buffer = "";
|
||||
for (usize i = 0; i < count; i++) {
|
||||
buffer += std::string(instructions[i].mnemonic) + " " + std::string(instructions[i].op_str);
|
||||
|
||||
if (i < count - 1) {
|
||||
// Append newlines between instructions, sans the final instruction
|
||||
buffer += "\n";
|
||||
}
|
||||
}
|
||||
|
||||
cs_free(instructions, count);
|
||||
return count;
|
||||
}
|
||||
};
|
||||
} // namespace Common
|
|
@ -12,25 +12,31 @@ class Memory;
|
|||
class Cheats {
|
||||
public:
|
||||
enum class CheatType {
|
||||
None, // Cheat has been removed by the frontend or is invalid
|
||||
ActionReplay, // CTRPF cheats
|
||||
// TODO: Other cheat devices and standards?
|
||||
};
|
||||
|
||||
struct Cheat {
|
||||
CheatType type;
|
||||
bool enabled = true;
|
||||
CheatType type = CheatType::ActionReplay;
|
||||
std::vector<u32> instructions;
|
||||
};
|
||||
|
||||
Cheats(Memory& mem, HIDService& hid);
|
||||
void addCheat(const Cheat& cheat);
|
||||
u32 addCheat(const Cheat& cheat);
|
||||
u32 addCheat(const u8* data, size_t size);
|
||||
void removeCheat(u32 id);
|
||||
void enableCheat(u32 id);
|
||||
void disableCheat(u32 id);
|
||||
void reset();
|
||||
void run();
|
||||
|
||||
void clear();
|
||||
bool haveCheats() const { return cheatsLoaded; }
|
||||
static constexpr u32 badCheatHandle = 0xFFFFFFFF;
|
||||
|
||||
private:
|
||||
ActionReplay ar; // An ActionReplay cheat machine for executing CTRPF codes
|
||||
std::vector<Cheat> cheats;
|
||||
bool cheatsLoaded = false;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,23 +1,126 @@
|
|||
#pragma once
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include "audio/dsp_core.hpp"
|
||||
#include "frontend_settings.hpp"
|
||||
#include "renderer.hpp"
|
||||
#include "services/region_codes.hpp"
|
||||
|
||||
struct AudioDeviceConfig {
|
||||
// Audio curve to use for volumes between 0-100
|
||||
enum class VolumeCurve : int {
|
||||
Cubic = 0, // Samples are scaled by volume ^ 3
|
||||
Linear = 1, // Samples are scaled by volume
|
||||
};
|
||||
|
||||
float volumeRaw = 1.0f;
|
||||
VolumeCurve volumeCurve = VolumeCurve::Cubic;
|
||||
|
||||
bool muteAudio = false;
|
||||
|
||||
float getVolume() const {
|
||||
if (muteAudio) {
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
return volumeRaw;
|
||||
}
|
||||
|
||||
static VolumeCurve volumeCurveFromString(std::string inString);
|
||||
static const char* volumeCurveToString(VolumeCurve curve);
|
||||
};
|
||||
|
||||
// Remember to initialize every field here to its default value otherwise bad things will happen
|
||||
struct EmulatorConfig {
|
||||
bool shaderJitEnabled = true;
|
||||
// Only enable the shader JIT by default on platforms where it's completely tested
|
||||
#if defined(PANDA3DS_X64_HOST) || defined(PANDA3DS_ARM64_HOST)
|
||||
static constexpr bool shaderJitDefault = true;
|
||||
#else
|
||||
static constexpr bool shaderJitDefault = false;
|
||||
#endif
|
||||
|
||||
// For now, use specialized shaders by default on MacOS as M1 drivers are buggy when using the ubershader, and on Android since mobile GPUs are
|
||||
// horrible. On other platforms we default to ubershader + shadergen fallback for lights
|
||||
#if defined(__ANDROID__) || defined(__APPLE__)
|
||||
static constexpr bool ubershaderDefault = false;
|
||||
#else
|
||||
static constexpr bool ubershaderDefault = true;
|
||||
#endif
|
||||
static constexpr bool accelerateShadersDefault = true;
|
||||
|
||||
#if defined(__LIBRETRO__)
|
||||
static constexpr bool audioEnabledDefault = true;
|
||||
#else
|
||||
static constexpr bool audioEnabledDefault = false;
|
||||
#endif
|
||||
|
||||
// We default to OpenGL on all platforms other than iOS
|
||||
#if defined(PANDA3DS_IOS)
|
||||
static constexpr RendererType rendererDefault = RendererType::Metal;
|
||||
#else
|
||||
static constexpr RendererType rendererDefault = RendererType::OpenGL;
|
||||
#endif
|
||||
|
||||
bool shaderJitEnabled = shaderJitDefault;
|
||||
bool useUbershaders = ubershaderDefault;
|
||||
bool accelerateShaders = accelerateShadersDefault;
|
||||
bool accurateShaderMul = false;
|
||||
bool discordRpcEnabled = false;
|
||||
RendererType rendererType = RendererType::OpenGL;
|
||||
|
||||
// Toggles whether to force shadergen when there's more than N lights active and we're using the ubershader, for better performance
|
||||
bool forceShadergenForLights = true;
|
||||
int lightShadergenThreshold = 1;
|
||||
|
||||
RendererType rendererType = rendererDefault;
|
||||
Audio::DSPCore::Type dspType = Audio::DSPCore::Type::HLE;
|
||||
|
||||
bool sdCardInserted = true;
|
||||
bool sdWriteProtected = false;
|
||||
bool usePortableBuild = false;
|
||||
|
||||
bool audioEnabled = audioEnabledDefault;
|
||||
bool vsyncEnabled = true;
|
||||
bool aacEnabled = true; // Enable AAC audio?
|
||||
|
||||
bool enableRenderdoc = false;
|
||||
bool printAppVersion = true;
|
||||
bool printDSPFirmware = false;
|
||||
|
||||
bool chargerPlugged = true;
|
||||
// Default to 3% battery to make users suffer
|
||||
int batteryPercentage = 3;
|
||||
|
||||
LanguageCodes systemLanguage = LanguageCodes::English;
|
||||
|
||||
// Default ROM path to open in Qt and misc frontends
|
||||
std::filesystem::path defaultRomPath = "";
|
||||
std::filesystem::path filePath;
|
||||
|
||||
// Frontend window settings
|
||||
struct WindowSettings {
|
||||
static constexpr int defaultX = 200;
|
||||
static constexpr int defaultY = 200;
|
||||
static constexpr int defaultWidth = 800;
|
||||
static constexpr int defaultHeight = 240 * 2;
|
||||
|
||||
bool rememberPosition = false; // Remember window position & size
|
||||
bool showAppVersion = false;
|
||||
|
||||
int x = defaultX;
|
||||
int y = defaultY;
|
||||
int width = defaultHeight;
|
||||
int height = defaultHeight;
|
||||
};
|
||||
|
||||
WindowSettings windowSettings;
|
||||
AudioDeviceConfig audioDeviceConfig;
|
||||
FrontendSettings frontendSettings;
|
||||
|
||||
EmulatorConfig(const std::filesystem::path& path);
|
||||
void load(const std::filesystem::path& path);
|
||||
void save(const std::filesystem::path& path);
|
||||
void load();
|
||||
void save();
|
||||
|
||||
static LanguageCodes languageCodeFromString(std::string inString);
|
||||
static const char* languageCodeToString(LanguageCodes code);
|
||||
};
|
|
@ -9,15 +9,17 @@
|
|||
#include "helpers.hpp"
|
||||
#include "kernel.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
class Emulator;
|
||||
class CPU;
|
||||
|
||||
class MyEnvironment final : public Dynarmic::A32::UserCallbacks {
|
||||
public:
|
||||
u64 ticksLeft = 0;
|
||||
u64 totalTicks = 0;
|
||||
Memory& mem;
|
||||
Kernel& kernel;
|
||||
public:
|
||||
u64 ticksLeft = 0;
|
||||
Memory& mem;
|
||||
Kernel& kernel;
|
||||
Scheduler& scheduler;
|
||||
|
||||
u64 getCyclesForInstruction(bool isThumb, u32 instruction);
|
||||
|
||||
|
@ -76,54 +78,56 @@ public:
|
|||
std::terminate();
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
kernel.serviceSVC(swi);
|
||||
}
|
||||
void CallSVC(u32 swi) override {
|
||||
kernel.serviceSVC(swi);
|
||||
}
|
||||
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||
switch (exception) {
|
||||
case Dynarmic::A32::Exception::UnpredictableInstruction:
|
||||
Helpers::panic("Unpredictable instruction at pc = %08X", pc);
|
||||
break;
|
||||
void ExceptionRaised(u32 pc, Dynarmic::A32::Exception exception) override {
|
||||
switch (exception) {
|
||||
case Dynarmic::A32::Exception::UnpredictableInstruction:
|
||||
Helpers::panic("Unpredictable instruction at pc = %08X", pc);
|
||||
break;
|
||||
|
||||
default: Helpers::panic("Fired exception oops");
|
||||
}
|
||||
}
|
||||
default: Helpers::panic("Fired exception oops");
|
||||
}
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) override {
|
||||
totalTicks += ticks;
|
||||
void AddTicks(u64 ticks) override {
|
||||
scheduler.currentTimestamp += ticks;
|
||||
|
||||
if (ticks > ticksLeft) {
|
||||
ticksLeft = 0;
|
||||
return;
|
||||
}
|
||||
ticksLeft -= ticks;
|
||||
}
|
||||
if (ticks > ticksLeft) {
|
||||
ticksLeft = 0;
|
||||
return;
|
||||
}
|
||||
ticksLeft -= ticks;
|
||||
}
|
||||
|
||||
u64 GetTicksRemaining() override {
|
||||
return ticksLeft;
|
||||
}
|
||||
u64 GetTicksRemaining() override {
|
||||
return ticksLeft;
|
||||
}
|
||||
|
||||
u64 GetTicksForCode(bool isThumb, u32 vaddr, u32 instruction) override {
|
||||
return getCyclesForInstruction(isThumb, instruction);
|
||||
}
|
||||
u64 GetTicksForCode(bool isThumb, u32 vaddr, u32 instruction) override {
|
||||
return getCyclesForInstruction(isThumb, instruction);
|
||||
}
|
||||
|
||||
MyEnvironment(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
MyEnvironment(Memory& mem, Kernel& kernel, Scheduler& scheduler) : mem(mem), kernel(kernel), scheduler(scheduler) {}
|
||||
};
|
||||
|
||||
class CPU {
|
||||
std::unique_ptr<Dynarmic::A32::Jit> jit;
|
||||
std::shared_ptr<CP15> cp15;
|
||||
std::unique_ptr<Dynarmic::A32::Jit> jit;
|
||||
std::shared_ptr<CP15> cp15;
|
||||
|
||||
// Make exclusive monitor with only 1 CPU core
|
||||
Dynarmic::ExclusiveMonitor exclusiveMonitor{1};
|
||||
MyEnvironment env;
|
||||
Memory& mem;
|
||||
// Make exclusive monitor with only 1 CPU core
|
||||
Dynarmic::ExclusiveMonitor exclusiveMonitor{1};
|
||||
MyEnvironment env;
|
||||
Memory& mem;
|
||||
Scheduler& scheduler;
|
||||
Emulator& emu;
|
||||
|
||||
public:
|
||||
static constexpr u64 ticksPerSec = 268111856;
|
||||
public:
|
||||
static constexpr u64 ticksPerSec = Scheduler::arm11Clock;
|
||||
|
||||
CPU(Memory& mem, Kernel& kernel);
|
||||
CPU(Memory& mem, Kernel& kernel, Emulator& emu);
|
||||
void reset();
|
||||
|
||||
void setReg(int index, u32 value) {
|
||||
|
@ -162,29 +166,22 @@ public:
|
|||
}
|
||||
|
||||
u64 getTicks() {
|
||||
return env.totalTicks;
|
||||
return scheduler.currentTimestamp;
|
||||
}
|
||||
|
||||
// Get reference to tick count. Memory needs access to this
|
||||
u64& getTicksRef() {
|
||||
return env.totalTicks;
|
||||
return scheduler.currentTimestamp;
|
||||
}
|
||||
|
||||
void clearCache() { jit->ClearCache(); }
|
||||
|
||||
void runFrame() {
|
||||
env.ticksLeft = ticksPerSec / 60;
|
||||
execute:
|
||||
const auto exitReason = jit->Run();
|
||||
|
||||
if (static_cast<u32>(exitReason) != 0) [[unlikely]] {
|
||||
// Cache invalidation needs to exit the JIT so it returns a CacheInvalidation HaltReason. In our case, we just go back to executing
|
||||
// The goto might be terrible but it does guarantee that this does not recursively call run and crash, instead getting optimized to a jump
|
||||
if (Dynarmic::Has(exitReason, Dynarmic::HaltReason::CacheInvalidation)) {
|
||||
goto execute;
|
||||
} else {
|
||||
Helpers::panic("Exit reason: %d\nPC: %08X", static_cast<u32>(exitReason), getReg(15));
|
||||
}
|
||||
}
|
||||
Scheduler& getScheduler() {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
void addTicks(u64 ticks) { env.AddTicks(ticks); }
|
||||
|
||||
void clearCache() { jit->ClearCache(); }
|
||||
void clearCacheRange(u32 start, u32 size) { jit->InvalidateCacheRange(start, size); }
|
||||
|
||||
void runFrame();
|
||||
};
|
|
@ -1,20 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <cstdint>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "swap.hpp"
|
||||
|
||||
namespace Crypto {
|
||||
constexpr std::size_t AesKeySize = 0x10;
|
||||
constexpr usize AesKeySize = 0x10;
|
||||
using AESKey = std::array<u8, AesKeySize>;
|
||||
|
||||
template <std::size_t N>
|
||||
static std::array<u8, N> rolArray(const std::array<u8, N>& value, std::size_t bits) {
|
||||
struct Seed {
|
||||
u64_le titleID;
|
||||
AESKey seed;
|
||||
std::array<u8, 8> pad;
|
||||
};
|
||||
|
||||
template <usize N>
|
||||
static std::array<u8, N> rolArray(const std::array<u8, N>& value, usize bits) {
|
||||
const auto bitWidth = N * CHAR_BIT;
|
||||
|
||||
bits %= bitWidth;
|
||||
|
@ -24,18 +33,18 @@ namespace Crypto {
|
|||
|
||||
std::array<u8, N> result;
|
||||
|
||||
for (std::size_t i = 0; i < N; i++) {
|
||||
for (usize i = 0; i < N; i++) {
|
||||
result[i] = ((value[(i + byteShift) % N] << bitShift) | (value[(i + byteShift + 1) % N] >> (CHAR_BIT - bitShift))) & UINT8_MAX;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
template <usize N>
|
||||
static std::array<u8, N> addArray(const std::array<u8, N>& a, const std::array<u8, N>& b) {
|
||||
std::array<u8, N> result;
|
||||
std::size_t sum = 0;
|
||||
std::size_t carry = 0;
|
||||
usize sum = 0;
|
||||
usize carry = 0;
|
||||
|
||||
for (std::int64_t i = N - 1; i >= 0; i--) {
|
||||
sum = a[i] + b[i] + carry;
|
||||
|
@ -46,11 +55,11 @@ namespace Crypto {
|
|||
return result;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
template <usize N>
|
||||
static std::array<u8, N> xorArray(const std::array<u8, N>& a, const std::array<u8, N>& b) {
|
||||
std::array<u8, N> result;
|
||||
|
||||
for (std::size_t i = 0; i < N; i++) {
|
||||
for (usize i = 0; i < N; i++) {
|
||||
result[i] = a[i] ^ b[i];
|
||||
}
|
||||
|
||||
|
@ -63,7 +72,7 @@ namespace Crypto {
|
|||
}
|
||||
|
||||
AESKey rawKey;
|
||||
for (std::size_t i = 0; i < rawKey.size(); i++) {
|
||||
for (usize i = 0; i < rawKey.size(); i++) {
|
||||
rawKey[i] = static_cast<u8>(std::stoi(hex.substr(i * 2, 2), 0, 16));
|
||||
}
|
||||
|
||||
|
@ -76,7 +85,7 @@ namespace Crypto {
|
|||
std::optional<AESKey> normalKey = std::nullopt;
|
||||
};
|
||||
|
||||
enum KeySlotId : std::size_t {
|
||||
enum KeySlotId : usize {
|
||||
NCCHKey0 = 0x2C,
|
||||
NCCHKey1 = 0x25,
|
||||
NCCHKey2 = 0x18,
|
||||
|
@ -84,14 +93,17 @@ namespace Crypto {
|
|||
};
|
||||
|
||||
class AESEngine {
|
||||
private:
|
||||
constexpr static std::size_t AesKeySlotCount = 0x40;
|
||||
private:
|
||||
constexpr static usize AesKeySlotCount = 0x40;
|
||||
|
||||
std::optional<AESKey> m_generator = std::nullopt;
|
||||
std::array<AESKeySlot, AesKeySlotCount> m_slots;
|
||||
bool keysLoaded = false;
|
||||
|
||||
constexpr void updateNormalKey(std::size_t slotId) {
|
||||
std::vector<Seed> seeds;
|
||||
IOFile seedDatabase;
|
||||
|
||||
constexpr void updateNormalKey(usize slotId) {
|
||||
if (m_generator.has_value() && hasKeyX(slotId) && hasKeyY(slotId)) {
|
||||
auto& keySlot = m_slots.at(slotId);
|
||||
AESKey keyX = keySlot.keyX.value();
|
||||
|
@ -101,13 +113,17 @@ namespace Crypto {
|
|||
}
|
||||
}
|
||||
|
||||
public:
|
||||
public:
|
||||
AESEngine() {}
|
||||
void loadKeys(const std::filesystem::path& path);
|
||||
void setSeedPath(const std::filesystem::path& path);
|
||||
// Returns true on success, false on failure
|
||||
bool loadSeeds();
|
||||
|
||||
bool haveKeys() { return keysLoaded; }
|
||||
bool haveGenerator() { return m_generator.has_value(); }
|
||||
|
||||
constexpr bool hasKeyX(std::size_t slotId) {
|
||||
constexpr bool hasKeyX(usize slotId) {
|
||||
if (slotId >= AesKeySlotCount) {
|
||||
return false;
|
||||
}
|
||||
|
@ -115,18 +131,16 @@ namespace Crypto {
|
|||
return m_slots.at(slotId).keyX.has_value();
|
||||
}
|
||||
|
||||
constexpr AESKey getKeyX(std::size_t slotId) {
|
||||
return m_slots.at(slotId).keyX.value_or(AESKey{});
|
||||
}
|
||||
constexpr AESKey getKeyX(usize slotId) { return m_slots.at(slotId).keyX.value_or(AESKey{}); }
|
||||
|
||||
constexpr void setKeyX(std::size_t slotId, const AESKey &key) {
|
||||
constexpr void setKeyX(usize slotId, const AESKey& key) {
|
||||
if (slotId < AesKeySlotCount) {
|
||||
m_slots.at(slotId).keyX = key;
|
||||
updateNormalKey(slotId);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool hasKeyY(std::size_t slotId) {
|
||||
constexpr bool hasKeyY(usize slotId) {
|
||||
if (slotId >= AesKeySlotCount) {
|
||||
return false;
|
||||
}
|
||||
|
@ -134,18 +148,16 @@ namespace Crypto {
|
|||
return m_slots.at(slotId).keyY.has_value();
|
||||
}
|
||||
|
||||
constexpr AESKey getKeyY(std::size_t slotId) {
|
||||
return m_slots.at(slotId).keyY.value_or(AESKey{});
|
||||
}
|
||||
constexpr AESKey getKeyY(usize slotId) { return m_slots.at(slotId).keyY.value_or(AESKey{}); }
|
||||
|
||||
constexpr void setKeyY(std::size_t slotId, const AESKey &key) {
|
||||
constexpr void setKeyY(usize slotId, const AESKey& key) {
|
||||
if (slotId < AesKeySlotCount) {
|
||||
m_slots.at(slotId).keyY = key;
|
||||
updateNormalKey(slotId);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool hasNormalKey(std::size_t slotId) {
|
||||
constexpr bool hasNormalKey(usize slotId) {
|
||||
if (slotId >= AesKeySlotCount) {
|
||||
return false;
|
||||
}
|
||||
|
@ -153,14 +165,14 @@ namespace Crypto {
|
|||
return m_slots.at(slotId).normalKey.has_value();
|
||||
}
|
||||
|
||||
constexpr AESKey getNormalKey(std::size_t slotId) {
|
||||
return m_slots.at(slotId).normalKey.value_or(AESKey{});
|
||||
}
|
||||
constexpr AESKey getNormalKey(usize slotId) { return m_slots.at(slotId).normalKey.value_or(AESKey{}); }
|
||||
|
||||
constexpr void setNormalKey(std::size_t slotId, const AESKey &key) {
|
||||
constexpr void setNormalKey(usize slotId, const AESKey& key) {
|
||||
if (slotId < AesKeySlotCount) {
|
||||
m_slots.at(slotId).normalKey = key;
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<AESKey> getSeedFromDB(u64 titleID);
|
||||
};
|
||||
}
|
||||
} // namespace Crypto
|
||||
|
|
|
@ -17,6 +17,8 @@ namespace Discord {
|
|||
void init();
|
||||
void update(RPCStatus status, const std::string& title);
|
||||
void stop();
|
||||
|
||||
bool running() const { return enabled; }
|
||||
};
|
||||
} // namespace Discord
|
||||
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include "PICA/gpu.hpp"
|
||||
#include "audio/audio_device.hpp"
|
||||
#include "audio/dsp_core.hpp"
|
||||
#include "cheats.hpp"
|
||||
#include "config.hpp"
|
||||
#include "cpu.hpp"
|
||||
|
@ -16,6 +18,7 @@
|
|||
#include "io_file.hpp"
|
||||
#include "lua_manager.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
|
||||
#include "http_server.hpp"
|
||||
|
@ -25,6 +28,8 @@
|
|||
#include "gl/context.h"
|
||||
#endif
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
enum class ROMType {
|
||||
None,
|
||||
ELF,
|
||||
|
@ -39,36 +44,20 @@ class Emulator {
|
|||
GPU gpu;
|
||||
Memory memory;
|
||||
Kernel kernel;
|
||||
std::unique_ptr<Audio::DSPCore> dsp;
|
||||
Scheduler scheduler;
|
||||
|
||||
Crypto::AESEngine aesEngine;
|
||||
AudioDevice audioDevice;
|
||||
Cheats cheats;
|
||||
|
||||
#ifdef PANDA3DS_FRONTEND_SDL
|
||||
SDL_Window* window;
|
||||
|
||||
#ifdef PANDA3DS_ENABLE_OPENGL
|
||||
SDL_GLContext glContext;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SDL_GameController* gameController = nullptr;
|
||||
int gameControllerID;
|
||||
|
||||
// Variables to keep track of whether the user is controlling the 3DS analog stick with their keyboard
|
||||
// This is done so when a gamepad is connected, we won't automatically override the 3DS analog stick settings with the gamepad's state
|
||||
// And so the user can still use the keyboard to control the analog
|
||||
bool keyboardAnalogX = false;
|
||||
bool keyboardAnalogY = false;
|
||||
|
||||
// For tracking whether to update gyroscope
|
||||
// We bind gyro to right click + mouse movement
|
||||
bool holdingRightClick = false;
|
||||
|
||||
public:
|
||||
static constexpr u32 width = 400;
|
||||
static constexpr u32 height = 240 * 2; // * 2 because 2 screens
|
||||
ROMType romType = ROMType::None;
|
||||
bool running = false; // Is the emulator running a game?
|
||||
bool programRunning = false; // Is the emulator program itself running?
|
||||
bool running = false; // Is the emulator running a game?
|
||||
|
||||
private:
|
||||
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
|
||||
HttpServer httpServer;
|
||||
friend struct HttpServer;
|
||||
|
@ -93,19 +82,22 @@ class Emulator {
|
|||
// change ROMs. If Reload is selected, the emulator will reload its selected ROM. This is useful for eg a "reset" button that keeps the current
|
||||
// ROM and just resets the emu
|
||||
enum class ReloadOption { NoReload, Reload };
|
||||
// Used in CPU::runFrame
|
||||
bool frameDone = false;
|
||||
|
||||
Emulator();
|
||||
~Emulator();
|
||||
|
||||
void step();
|
||||
void render();
|
||||
void reset(ReloadOption reload);
|
||||
void run();
|
||||
void runFrame();
|
||||
// Poll the scheduler for events
|
||||
void pollScheduler();
|
||||
|
||||
void resume(); // Resume the emulator
|
||||
void pause(); // Pause the emulator
|
||||
void togglePause();
|
||||
void setAudioEnabled(bool enable);
|
||||
|
||||
bool loadAmiibo(const std::filesystem::path& path);
|
||||
bool loadROM(const std::filesystem::path& path);
|
||||
|
@ -118,11 +110,35 @@ class Emulator {
|
|||
// For passing the GL context from Qt to the renderer
|
||||
void initGraphicsContext(GL::Context* glContext) { gpu.initGraphicsContext(nullptr); }
|
||||
#else
|
||||
void initGraphicsContext() { gpu.initGraphicsContext(window); }
|
||||
void initGraphicsContext(SDL_Window* window) { gpu.initGraphicsContext(window); }
|
||||
#endif
|
||||
|
||||
RomFS::DumpingResult dumpRomFS(const std::filesystem::path& path);
|
||||
void setOutputSize(u32 width, u32 height) { gpu.setOutputSize(width, height); }
|
||||
void deinitGraphicsContext() { gpu.deinitGraphicsContext(); }
|
||||
|
||||
// Reloads some settings that require special handling, such as audio enable
|
||||
void reloadSettings();
|
||||
|
||||
EmulatorConfig& getConfig() { return config; }
|
||||
Cheats& getCheats() { return cheats; }
|
||||
ServiceManager& getServiceManager() { return kernel.getServiceManager(); }
|
||||
LuaManager& getLua() { return lua; }
|
||||
Scheduler& getScheduler() { return scheduler; }
|
||||
Memory& getMemory() { return memory; }
|
||||
AudioDeviceInterface& getAudioDevice() { return audioDevice; }
|
||||
|
||||
RendererType getRendererType() const { return config.rendererType; }
|
||||
Renderer* getRenderer() { return gpu.getRenderer(); }
|
||||
u64 getTicks() { return cpu.getTicks(); }
|
||||
|
||||
std::filesystem::path getConfigPath();
|
||||
std::filesystem::path getAndroidAppPath();
|
||||
// Get the root path for the emulator's app data
|
||||
std::filesystem::path getAppDataRoot();
|
||||
|
||||
std::span<u8> getSMDH();
|
||||
|
||||
private:
|
||||
void loadRenderdoc();
|
||||
};
|
||||
|
|
35
include/frontend_settings.hpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
// Some UI settings that aren't fully frontend-dependent. Note: Not all frontends will support the same settings.
|
||||
// Note: Any enums should ideally be ordered in the same order we want to show them in UI dropdown menus, so that we can cast indices to enums
|
||||
// directly.
|
||||
struct FrontendSettings {
|
||||
enum class Theme : int {
|
||||
System = 0,
|
||||
Light = 1,
|
||||
Dark = 2,
|
||||
GreetingsCat = 3,
|
||||
Cream = 4,
|
||||
Oled = 5,
|
||||
};
|
||||
|
||||
// Different panda-themed window icons
|
||||
enum class WindowIcon : int {
|
||||
Rpog = 0,
|
||||
Rsyn = 1,
|
||||
Rnap = 2,
|
||||
Rcow = 3,
|
||||
SkyEmu = 4,
|
||||
};
|
||||
|
||||
Theme theme = Theme::Dark;
|
||||
WindowIcon icon = WindowIcon::Rpog;
|
||||
std::string language = "en";
|
||||
|
||||
static Theme themeFromString(std::string inString);
|
||||
static const char* themeToString(Theme theme);
|
||||
|
||||
static WindowIcon iconFromString(std::string inString);
|
||||
static const char* iconToString(WindowIcon icon);
|
||||
};
|
|
@ -7,6 +7,7 @@
|
|||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result.hpp"
|
||||
|
@ -15,13 +16,13 @@
|
|||
using Result::HorizonResult;
|
||||
|
||||
namespace PathType {
|
||||
enum : u32 {
|
||||
Invalid = 0,
|
||||
Empty = 1,
|
||||
Binary = 2,
|
||||
ASCII = 3,
|
||||
UTF16 = 4,
|
||||
};
|
||||
enum : u32 {
|
||||
Invalid = 0,
|
||||
Empty = 1,
|
||||
Binary = 2,
|
||||
ASCII = 3,
|
||||
UTF16 = 4,
|
||||
};
|
||||
}
|
||||
|
||||
namespace ArchiveID {
|
||||
|
@ -34,91 +35,103 @@ namespace ArchiveID {
|
|||
SDMC = 9,
|
||||
SDMCWriteOnly = 0xA,
|
||||
|
||||
CardSPI = 0x12345679,
|
||||
SavedataAndNcch = 0x2345678A,
|
||||
// 3DBrew: This is the same as the regular SaveData archive, except with this the savedata ID and mediatype is loaded from the input archive
|
||||
// lowpath.
|
||||
UserSaveData1 = 0x567890B2,
|
||||
// 3DBrew: Similar to 0x567890B2 but can only access Accessible Save specified in exheader?
|
||||
UserSaveData2 = 0x567890B4,
|
||||
|
||||
TwlPhoto = 0x567890AC,
|
||||
TwlSound = 0x567890AD,
|
||||
};
|
||||
|
||||
static std::string toString(u32 id) {
|
||||
switch (id) {
|
||||
case SelfNCCH: return "SelfNCCH";
|
||||
case SaveData: return "SaveData";
|
||||
case ExtSaveData: return "ExtSaveData";
|
||||
case SharedExtSaveData: return "SharedExtSaveData";
|
||||
case SystemSaveData: return "SystemSaveData";
|
||||
case SDMC: return "SDMC";
|
||||
case SDMCWriteOnly: return "SDMC (Write-only)";
|
||||
case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)";
|
||||
default: return "Unknown archive";
|
||||
}
|
||||
}
|
||||
}
|
||||
static std::string toString(u32 id) {
|
||||
switch (id) {
|
||||
case SelfNCCH: return "SelfNCCH";
|
||||
case SaveData: return "SaveData";
|
||||
case ExtSaveData: return "ExtSaveData";
|
||||
case SharedExtSaveData: return "SharedExtSaveData";
|
||||
case SystemSaveData: return "SystemSaveData";
|
||||
case SDMC: return "SDMC";
|
||||
case SDMCWriteOnly: return "SDMC (Write-only)";
|
||||
case SavedataAndNcch: return "Savedata & NCCH (archive 0x2345678A)";
|
||||
case TwlPhoto: return "TWL_PHOTO";
|
||||
case TwlSound: return "TWL_SOUND";
|
||||
default: return "Unknown archive";
|
||||
}
|
||||
}
|
||||
} // namespace ArchiveID
|
||||
|
||||
struct FSPath {
|
||||
u32 type = PathType::Invalid;
|
||||
u32 type = PathType::Invalid;
|
||||
|
||||
std::vector<u8> binary; // Path data for binary paths
|
||||
std::string string; // Path data for ASCII paths
|
||||
std::u16string utf16_string;
|
||||
std::vector<u8> binary; // Path data for binary paths
|
||||
std::string string; // Path data for ASCII paths
|
||||
std::u16string utf16_string;
|
||||
|
||||
FSPath() {}
|
||||
FSPath() {}
|
||||
|
||||
FSPath(u32 type, const std::vector<u8>& vec) : type(type) {
|
||||
switch (type) {
|
||||
case PathType::Binary:
|
||||
binary = std::move(vec);
|
||||
break;
|
||||
FSPath(u32 type, const std::vector<u8>& vec) : type(type) {
|
||||
switch (type) {
|
||||
case PathType::Binary: binary = std::move(vec); break;
|
||||
|
||||
case PathType::ASCII:
|
||||
string.resize(vec.size() - 1); // -1 because of the null terminator
|
||||
std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data
|
||||
break;
|
||||
case PathType::ASCII:
|
||||
string.resize(vec.size() - 1); // -1 because of the null terminator
|
||||
std::memcpy(string.data(), vec.data(), vec.size() - 1); // Copy string data
|
||||
break;
|
||||
|
||||
case PathType::UTF16: {
|
||||
const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too
|
||||
utf16_string.resize(size);
|
||||
std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16));
|
||||
break;
|
||||
}
|
||||
; }
|
||||
}
|
||||
case PathType::UTF16: {
|
||||
const size_t size = vec.size() / sizeof(u16) - 1; // Character count. -1 because null terminator here too
|
||||
utf16_string.resize(size);
|
||||
std::memcpy(utf16_string.data(), vec.data(), size * sizeof(u16));
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
bool isUTF16() const { return type == PathType::UTF16; }
|
||||
bool isASCII() const { return type == PathType::ASCII; }
|
||||
bool isBinary() const { return type == PathType::Binary; }
|
||||
// This is not called "isEmpty()" to make obvious that we're talking about an empty-type path, NOT an empty text path
|
||||
bool isEmptyType() const { return type == PathType::Empty; }
|
||||
|
||||
bool isTextPath() const { return isUTF16() || isASCII(); }
|
||||
};
|
||||
|
||||
struct FilePerms {
|
||||
u32 raw;
|
||||
u32 raw;
|
||||
|
||||
FilePerms(u32 val) : raw(val) {}
|
||||
bool read() const { return (raw & 1) != 0; }
|
||||
bool write() const { return (raw & 2) != 0; }
|
||||
bool create() const { return (raw & 4) != 0; }
|
||||
FilePerms(u32 val) : raw(val) {}
|
||||
bool read() const { return (raw & 1) != 0; }
|
||||
bool write() const { return (raw & 2) != 0; }
|
||||
bool create() const { return (raw & 4) != 0; }
|
||||
};
|
||||
|
||||
class ArchiveBase;
|
||||
struct FileSession {
|
||||
ArchiveBase* archive = nullptr;
|
||||
FILE* fd = nullptr; // File descriptor for file sessions that require them.
|
||||
FSPath path;
|
||||
FSPath archivePath;
|
||||
u32 priority = 0; // TODO: What does this even do
|
||||
bool isOpen;
|
||||
ArchiveBase* archive = nullptr;
|
||||
FILE* fd = nullptr; // File descriptor for file sessions that require them.
|
||||
FSPath path;
|
||||
FSPath archivePath;
|
||||
u32 priority = 0; // TODO: What does this even do
|
||||
bool isOpen;
|
||||
|
||||
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true) :
|
||||
archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
|
||||
FileSession(ArchiveBase* archive, const FSPath& filePath, const FSPath& archivePath, FILE* fd, bool isOpen = true)
|
||||
: archive(archive), path(filePath), archivePath(archivePath), fd(fd), isOpen(isOpen), priority(0) {}
|
||||
|
||||
// For cloning a file session
|
||||
FileSession(const FileSession& other) : archive(other.archive), path(other.path),
|
||||
archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {}
|
||||
// For cloning a file session
|
||||
FileSession(const FileSession& other)
|
||||
: archive(other.archive), path(other.path), archivePath(other.archivePath), fd(other.fd), isOpen(other.isOpen), priority(other.priority) {}
|
||||
};
|
||||
|
||||
struct ArchiveSession {
|
||||
ArchiveBase* archive = nullptr;
|
||||
FSPath path;
|
||||
bool isOpen;
|
||||
ArchiveBase* archive = nullptr;
|
||||
FSPath path;
|
||||
bool isOpen;
|
||||
|
||||
ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {}
|
||||
ArchiveSession(ArchiveBase* archive, const FSPath& filePath, bool isOpen = true) : archive(archive), path(filePath), isOpen(isOpen) {}
|
||||
};
|
||||
|
||||
struct DirectoryEntry {
|
||||
|
@ -156,106 +169,125 @@ struct DirectorySession {
|
|||
using FileDescriptor = std::optional<FILE*>;
|
||||
|
||||
class ArchiveBase {
|
||||
public:
|
||||
struct FormatInfo {
|
||||
u32 size; // Archive size
|
||||
u32 numOfDirectories; // Number of directories
|
||||
u32 numOfFiles; // Number of files
|
||||
bool duplicateData; // Whether to duplicate data or not
|
||||
};
|
||||
public:
|
||||
struct FormatInfo {
|
||||
u32 size; // Archive size
|
||||
u32 numOfDirectories; // Number of directories
|
||||
u32 numOfFiles; // Number of files
|
||||
bool duplicateData; // Whether to duplicate data or not
|
||||
};
|
||||
|
||||
protected:
|
||||
using Handle = u32;
|
||||
protected:
|
||||
using Handle = u32;
|
||||
|
||||
static constexpr FileDescriptor NoFile = nullptr;
|
||||
static constexpr FileDescriptor FileError = std::nullopt;
|
||||
Memory& mem;
|
||||
static constexpr FileDescriptor NoFile = nullptr;
|
||||
static constexpr FileDescriptor FileError = std::nullopt;
|
||||
Memory& mem;
|
||||
|
||||
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not
|
||||
// A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs
|
||||
// And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory tree
|
||||
// And access files outside of the emulator's app data folder
|
||||
template <u32 format>
|
||||
bool isPathSafe(const FSPath& path) {
|
||||
static_assert(format == PathType::ASCII || format == PathType::UTF16);
|
||||
using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
|
||||
using Char = typename String::value_type; // Char type for the path
|
||||
// Returns if a specified 3DS path in UTF16 or ASCII format is safe or not
|
||||
// A 3DS path is considered safe if its first character is '/' which means we're not trying to access anything outside the root of the fs
|
||||
// And if it doesn't contain enough instances of ".." (Indicating "climb up a folder" in filesystems) to let the software climb up the directory
|
||||
// tree And access files outside of the emulator's app data folder
|
||||
template <u32 format>
|
||||
bool isPathSafe(const FSPath& path) {
|
||||
static_assert(format == PathType::ASCII || format == PathType::UTF16);
|
||||
using String = typename std::conditional<format == PathType::UTF16, std::u16string, std::string>::type; // String type for the path
|
||||
using Char = typename String::value_type; // Char type for the path
|
||||
|
||||
String pathString, dots;
|
||||
if constexpr (std::is_same<String, std::u16string>()) {
|
||||
pathString = path.utf16_string;
|
||||
dots = u"..";
|
||||
} else {
|
||||
pathString = path.string;
|
||||
dots = "..";
|
||||
}
|
||||
String pathString, dots;
|
||||
if constexpr (std::is_same<String, std::u16string>()) {
|
||||
pathString = path.utf16_string;
|
||||
dots = u"..";
|
||||
} else {
|
||||
pathString = path.string;
|
||||
dots = "..";
|
||||
}
|
||||
|
||||
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
|
||||
if (pathString[0] != Char('/')) return false;
|
||||
// If the path string doesn't begin with / then that means it's accessing outside the FS root, which is invalid & unsafe
|
||||
if (pathString[0] != Char('/')) return false;
|
||||
|
||||
// Counts how many folders sans the root our file is nested under.
|
||||
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
|
||||
// If it's 0 then this is the FS root.
|
||||
// If it's > 0 then we're in a subdirectory of the root.
|
||||
int level = 0;
|
||||
// Counts how many folders sans the root our file is nested under.
|
||||
// If it's < 0 at any point of parsing, then the path is unsafe and tries to crawl outside our file sandbox.
|
||||
// If it's 0 then this is the FS root.
|
||||
// If it's > 0 then we're in a subdirectory of the root.
|
||||
int level = 0;
|
||||
|
||||
// Split the string on / characters and see how many of the substrings are ".."
|
||||
size_t pos = 0;
|
||||
while ((pos = pathString.find(Char('/'))) != String::npos) {
|
||||
String token = pathString.substr(0, pos);
|
||||
pathString.erase(0, pos + 1);
|
||||
// Split the string on / characters and see how many of the substrings are ".."
|
||||
size_t pos = 0;
|
||||
while ((pos = pathString.find(Char('/'))) != String::npos) {
|
||||
String token = pathString.substr(0, pos);
|
||||
pathString.erase(0, pos + 1);
|
||||
|
||||
if (token == dots) {
|
||||
level--;
|
||||
if (level < 0) return false;
|
||||
} else {
|
||||
level++;
|
||||
}
|
||||
}
|
||||
if (token == dots) {
|
||||
level--;
|
||||
if (level < 0) return false;
|
||||
} else {
|
||||
level++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual std::string name() = 0;
|
||||
virtual u64 getFreeBytes() = 0;
|
||||
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
|
||||
virtual HorizonResult deleteFile(const FSPath& path) = 0;
|
||||
public:
|
||||
virtual std::string name() = 0;
|
||||
virtual u64 getFreeBytes() = 0;
|
||||
virtual HorizonResult createFile(const FSPath& path, u64 size) = 0;
|
||||
virtual HorizonResult deleteFile(const FSPath& path) = 0;
|
||||
|
||||
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
|
||||
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
|
||||
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
|
||||
return Ok(FormatInfo{ .size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false });
|
||||
}
|
||||
virtual Rust::Result<FormatInfo, HorizonResult> getFormatInfo(const FSPath& path) {
|
||||
Helpers::panic("Unimplemented GetFormatInfo for %s archive", name().c_str());
|
||||
// Return a dummy struct just to avoid the UB of not returning anything, even if we panic
|
||||
return Ok(FormatInfo{.size = 0, .numOfDirectories = 0, .numOfFiles = 0, .duplicateData = false});
|
||||
}
|
||||
|
||||
virtual HorizonResult createDirectory(const FSPath& path) {
|
||||
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
|
||||
return Result::FS::AlreadyExists;
|
||||
}
|
||||
virtual HorizonResult createDirectory(const FSPath& path) {
|
||||
Helpers::panic("Unimplemented CreateDirectory for %s archive", name().c_str());
|
||||
return Result::FS::AlreadyExists;
|
||||
}
|
||||
|
||||
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
|
||||
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
|
||||
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
|
||||
// Returns nullopt if opening the file failed, otherwise returns a file descriptor to it (nullptr if none is needed)
|
||||
virtual FileDescriptor openFile(const FSPath& path, const FilePerms& perms) = 0;
|
||||
virtual Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) = 0;
|
||||
|
||||
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
|
||||
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
|
||||
return Err(Result::FS::FileNotFoundAlt);
|
||||
}
|
||||
virtual Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) {
|
||||
Helpers::panic("Unimplemented OpenDirectory for %s archive", name().c_str());
|
||||
return Err(Result::FS::FileNotFoundAlt);
|
||||
}
|
||||
|
||||
virtual void format(const FSPath& path, const FormatInfo& info) {
|
||||
Helpers::panic("Unimplemented Format for %s archive", name().c_str());
|
||||
}
|
||||
virtual void format(const FSPath& path, const FormatInfo& info) { Helpers::panic("Unimplemented Format for %s archive", name().c_str()); }
|
||||
|
||||
virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
|
||||
virtual HorizonResult renameFile(const FSPath& oldPath, const FSPath& newPath) {
|
||||
Helpers::panic("Unimplemented RenameFile for %s archive", name().c_str());
|
||||
return Result::Success;
|
||||
}
|
||||
}
|
||||
|
||||
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
|
||||
// Returns the number of bytes read, or nullopt if the read failed
|
||||
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
|
||||
// Read size bytes from a file starting at offset "offset" into a certain buffer in memory
|
||||
// Returns the number of bytes read, or nullopt if the read failed
|
||||
virtual std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) = 0;
|
||||
|
||||
ArchiveBase(Memory& mem) : mem(mem) {}
|
||||
ArchiveBase(Memory& mem) : mem(mem) {}
|
||||
|
||||
bool isSafeTextPath(const FSPath& path) {
|
||||
if (path.type == PathType::UTF16) {
|
||||
return isPathSafe<PathType::UTF16>(path);
|
||||
} else if (path.type == PathType::ASCII){
|
||||
return isPathSafe<PathType::ASCII>(path);
|
||||
}
|
||||
|
||||
Helpers::panic("ArchiveBase::IsSafeTextPath: Invalid path");
|
||||
}
|
||||
|
||||
// Appends a 3DS path to an std::filesystem::path
|
||||
void appendPath(std::filesystem::path& diskPath, const FSPath& guestPath) {
|
||||
if (guestPath.type == PathType::UTF16) {
|
||||
diskPath += std::filesystem::path(guestPath.utf16_string).make_preferred();
|
||||
} else if (guestPath.type == PathType::ASCII) {
|
||||
diskPath += std::filesystem::path(guestPath.string).make_preferred();
|
||||
} else [[unlikely]] {
|
||||
Helpers::panic("ArchiveBase::AppendPath: Invalid 3DS path");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ArchiveResource {
|
||||
|
|
30
include/fs/archive_card_spi.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
#include "archive_base.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
using Result::HorizonResult;
|
||||
|
||||
class CardSPIArchive : public ArchiveBase {
|
||||
public:
|
||||
CardSPIArchive(Memory& mem) : ArchiveBase(mem) {}
|
||||
std::string name() override { return "Card SPI"; }
|
||||
|
||||
u64 getFreeBytes() override {
|
||||
Helpers::warn("Unimplemented GetFreeBytes for Card SPI archive");
|
||||
return 0_MB;
|
||||
}
|
||||
|
||||
HorizonResult createDirectory(const FSPath& path) override;
|
||||
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||
HorizonResult deleteFile(const FSPath& path) override;
|
||||
|
||||
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
|
||||
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
|
||||
|
||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||
|
||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
|
||||
Helpers::panic("Unimplemented ReadFile for Card SPI archive");
|
||||
return {};
|
||||
};
|
||||
};
|
|
@ -5,8 +5,10 @@
|
|||
using Result::HorizonResult;
|
||||
|
||||
class SDMCArchive : public ArchiveBase {
|
||||
public:
|
||||
SDMCArchive(Memory& mem) : ArchiveBase(mem) {}
|
||||
bool isWriteOnly = false; // There's 2 variants of the SDMC archive: Regular one (Read/Write) and write-only
|
||||
|
||||
public:
|
||||
SDMCArchive(Memory& mem, bool writeOnly = false) : ArchiveBase(mem), isWriteOnly(writeOnly) {}
|
||||
|
||||
u64 getFreeBytes() override { return 1_GB; }
|
||||
std::string name() override { return "SDMC"; }
|
||||
|
|
30
include/fs/archive_twl_photo.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
#include "archive_base.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
using Result::HorizonResult;
|
||||
|
||||
class TWLPhotoArchive : public ArchiveBase {
|
||||
public:
|
||||
TWLPhotoArchive(Memory& mem) : ArchiveBase(mem) {}
|
||||
std::string name() override { return "TWL_PHOTO"; }
|
||||
|
||||
u64 getFreeBytes() override {
|
||||
Helpers::warn("Unimplemented GetFreeBytes for TWLPhoto archive");
|
||||
return 32_MB;
|
||||
}
|
||||
|
||||
HorizonResult createDirectory(const FSPath& path) override;
|
||||
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||
HorizonResult deleteFile(const FSPath& path) override;
|
||||
|
||||
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
|
||||
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
|
||||
|
||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||
|
||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
|
||||
Helpers::panic("Unimplemented ReadFile for TWL_PHOTO archive");
|
||||
return {};
|
||||
};
|
||||
};
|
30
include/fs/archive_twl_sound.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
#include "archive_base.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
using Result::HorizonResult;
|
||||
|
||||
class TWLSoundArchive : public ArchiveBase {
|
||||
public:
|
||||
TWLSoundArchive(Memory& mem) : ArchiveBase(mem) {}
|
||||
std::string name() override { return "TWL_SOUND"; }
|
||||
|
||||
u64 getFreeBytes() override {
|
||||
Helpers::warn("Unimplemented GetFreeBytes for TWLSound archive");
|
||||
return 32_MB;
|
||||
}
|
||||
|
||||
HorizonResult createDirectory(const FSPath& path) override;
|
||||
HorizonResult createFile(const FSPath& path, u64 size) override;
|
||||
HorizonResult deleteFile(const FSPath& path) override;
|
||||
|
||||
Rust::Result<ArchiveBase*, HorizonResult> openArchive(const FSPath& path) override;
|
||||
Rust::Result<DirectorySession, HorizonResult> openDirectory(const FSPath& path) override;
|
||||
|
||||
FileDescriptor openFile(const FSPath& path, const FilePerms& perms) override;
|
||||
|
||||
std::optional<u32> readFile(FileSession* file, u64 offset, u32 size, u32 dataPointer) override {
|
||||
Helpers::panic("Unimplemented ReadFile for TWL_SOUND archive");
|
||||
return {};
|
||||
};
|
||||
};
|
|
@ -4,7 +4,6 @@
|
|||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
@ -83,6 +82,20 @@ namespace Helpers {
|
|||
return false;
|
||||
}
|
||||
|
||||
static constexpr bool isHydraCore() {
|
||||
#ifdef PANDA3DS_HYDRA_CORE
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr bool isAndroid() {
|
||||
#ifdef __ANDROID__
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
static void debug_printf(const char* fmt, ...) {
|
||||
if constexpr (buildingInDebugMode()) {
|
||||
std::va_list args;
|
||||
|
@ -148,19 +161,6 @@ namespace Helpers {
|
|||
return std::bit_cast<To, From>(from);
|
||||
}
|
||||
#endif
|
||||
|
||||
static std::vector<std::string> split(const std::string& s, const char c) {
|
||||
std::istringstream tmp(s);
|
||||
std::vector<std::string> result(1);
|
||||
|
||||
while (std::getline(tmp, *result.rbegin(), c)) {
|
||||
result.emplace_back();
|
||||
}
|
||||
|
||||
// Remove temporary slot
|
||||
result.pop_back();
|
||||
return result;
|
||||
}
|
||||
}; // namespace Helpers
|
||||
|
||||
// UDLs for memory size values
|
||||
|
|