Compare commits
577 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ded39f5d8 | ||
|
|
633abd5a94 | ||
|
|
ee5f5a2c2d | ||
|
|
47cac816f6 | ||
|
|
4daf91fc69 | ||
|
|
8afdbf6a1f | ||
|
|
b5c03088bc | ||
|
|
95e747cd06 | ||
|
|
ec5ede68e7 | ||
|
|
2ab021da14 | ||
|
|
7fa3bf02ca | ||
|
|
34b733e70e | ||
|
|
3cdf6c536d | ||
|
|
4721ea4523 | ||
|
|
c450d264eb | ||
|
|
6673ed1274 | ||
|
|
ae28a52277 | ||
|
|
0b3ab30762 | ||
|
|
8c8de2efe9 | ||
|
|
dca7cfb9cf | ||
|
|
6e3222363c | ||
|
|
9c468e0c55 | ||
|
|
d7b1ebe4a8 | ||
|
|
f4700ccabf | ||
|
|
4bdb46e4c2 | ||
|
|
a3e10b1a72 | ||
|
|
db0cfb8e8b | ||
|
|
0e4b9cdde4 | ||
|
|
a1cf5020e6 | ||
|
|
6a0902e56d | ||
|
|
7a27a11770 | ||
|
|
21d9519032 | ||
|
|
a90ab1dec7 | ||
|
|
1d8b6ad13b | ||
|
|
d16e08454d | ||
|
|
0e72d0d826 | ||
|
|
ef875d6a35 | ||
|
|
b2d7c92cae | ||
|
|
dcae0c9a4f | ||
|
|
cff7b29bba | ||
|
|
23a0d2d7b7 | ||
|
|
2dc3a56e96 | ||
|
|
2332a44b68 | ||
|
|
c1c92c30f9 | ||
|
|
c86af6939c | ||
|
|
2faa83ca13 | ||
|
|
019f1a0cf0 | ||
|
|
e353b9fb3d | ||
|
|
03156d0c9a | ||
|
|
6317a0b2ca | ||
|
|
7b6868e908 | ||
|
|
85d820b1b4 | ||
|
|
a64b936cbe | ||
|
|
46f9d4b4a3 | ||
|
|
aa586fa268 | ||
|
|
7ac8657432 | ||
|
|
29981fa2eb | ||
|
|
ccb8da1512 | ||
|
|
88698c156f | ||
|
|
66dae22790 | ||
|
|
1d9d9c16e8 | ||
|
|
579000e747 | ||
|
|
516ef4f19f | ||
|
|
c286921739 | ||
|
|
f93d769a1c | ||
|
|
d2888f7e90 | ||
|
|
cd4e8a989c | ||
|
|
29feece4b8 | ||
|
|
0eff775264 | ||
|
|
e453b09a61 | ||
|
|
bf310a41b8 | ||
|
|
cbec739e7b | ||
|
|
494275fd38 | ||
|
|
e7ba2a4447 | ||
|
|
cc6f22e0e4 | ||
|
|
e9a857ce82 | ||
|
|
403f8e79ea | ||
|
|
34a29ad051 | ||
|
|
8581404482 | ||
|
|
69ee9edd8d | ||
|
|
3923b0f589 | ||
|
|
8be7131033 | ||
|
|
07ae1f972d | ||
|
|
fbfa7ddd62 | ||
|
|
9289255314 | ||
|
|
ec030a542f | ||
|
|
cde9386e0f | ||
|
|
7d6653268f | ||
|
|
8538e0bc3d | ||
|
|
5750f6f046 | ||
|
|
5fb4c718cc | ||
|
|
80562aaf64 | ||
|
|
84e1c0a430 | ||
|
|
e4b7a1d160 | ||
|
|
0eefe6e4d1 | ||
|
|
3c3d5eeddf | ||
|
|
248881fa7f | ||
|
|
7e7110b3b9 | ||
|
|
aef7a15b93 | ||
|
|
656e7aab29 | ||
|
|
28669872d9 | ||
|
|
46fc7d8502 | ||
|
|
3209cff530 | ||
|
|
827f8ca3c7 | ||
|
|
cc6e4ae6cf | ||
|
|
d140c8ecf7 | ||
|
|
b1709410dd | ||
|
|
c74af07c49 | ||
|
|
6e38081165 | ||
|
|
65f3119074 | ||
|
|
926604fc14 | ||
|
|
f1beb69899 | ||
|
|
7e45669ccb | ||
|
|
e29710818f | ||
|
|
487f8bc018 | ||
|
|
fd3806fd30 | ||
|
|
2b28fd7809 | ||
|
|
08c6ac02cf | ||
|
|
bc88cae0c7 | ||
|
|
2b41c6e573 | ||
|
|
c45173c9a6 | ||
|
|
32c509b82d | ||
|
|
6bf7108545 | ||
|
|
a63d4fa5b4 | ||
|
|
e4f94ee30b | ||
|
|
910198a29a | ||
|
|
7f0ecbf859 | ||
|
|
fa28dbe0f3 | ||
|
|
944132dbe5 | ||
|
|
32d127ad3e | ||
|
|
e8b9731af3 | ||
|
|
cc368de1a0 | ||
|
|
1cdc74c5e9 | ||
|
|
1289a3c3c1 | ||
|
|
827152d1fd | ||
|
|
1572c45aa0 | ||
|
|
6a2197806e | ||
|
|
587f22b610 | ||
|
|
46931a9566 | ||
|
|
678574972a | ||
|
|
7bee3427d0 | ||
|
|
1d491d636d | ||
|
|
23fe6f5be3 | ||
|
|
8db80d8389 | ||
|
|
ad55d22130 | ||
|
|
5ab285f1f9 | ||
|
|
ac81c02ed9 | ||
|
|
c78d495161 | ||
|
|
2d4a6883bc | ||
|
|
cec0d4f191 | ||
|
|
de43db3400 | ||
|
|
ec39c9eb32 | ||
|
|
5babad5de5 | ||
|
|
94ee8fc97b | ||
|
|
b36ce74d18 | ||
|
|
f6e548fbc0 | ||
|
|
416f692f6e | ||
|
|
d758332425 | ||
|
|
2662de6e52 | ||
|
|
1b64160d83 | ||
|
|
bd6432f1ff | ||
|
|
3388208597 | ||
|
|
947831ff1e | ||
|
|
6797d4a907 | ||
|
|
98ffad4303 | ||
|
|
7a1917e0fd | ||
|
|
35d0d06885 | ||
|
|
d18446f63a | ||
|
|
b78ffc4abf | ||
|
|
8dee5663b3 | ||
|
|
df008a159b | ||
|
|
42c062c620 | ||
|
|
fa58d95027 | ||
|
|
e017184445 | ||
|
|
db873a232c | ||
|
|
88bfec37ce | ||
|
|
756e9f1484 | ||
|
|
a8d8c21e00 | ||
|
|
61c39f0fdd | ||
|
|
b65c096be5 | ||
|
|
d939792b9b | ||
|
|
fe0775d2f4 | ||
|
|
e6bf72877f | ||
|
|
2ade136ff4 | ||
|
|
eba57fce88 | ||
|
|
ee170cbcea | ||
|
|
983777a317 | ||
|
|
2d388a75f0 | ||
|
|
ce1fe0387f | ||
|
|
dc97117a0b | ||
|
|
c1146d2a5f | ||
|
|
1e33db8573 | ||
|
|
51ce224a96 | ||
|
|
f1b82634bc | ||
|
|
6fd19f05f1 | ||
|
|
c85e3a2234 | ||
|
|
516a95721c | ||
|
|
d6e52581ac | ||
|
|
f16bb1dfcf | ||
|
|
d42e77797e | ||
|
|
8f84665775 | ||
|
|
fc1b425520 | ||
|
|
490d0e36a0 | ||
|
|
1ce6fff064 | ||
|
|
4f8ee5e456 | ||
|
|
af8ae770ef | ||
|
|
0a55eb588b | ||
|
|
826e9c9782 | ||
|
|
87c3c93464 | ||
|
|
55de13efcc | ||
|
|
91e19deb39 | ||
|
|
a9e4e8294a | ||
|
|
4f969e2271 | ||
|
|
0a87eb71ba | ||
|
|
6085d32cf5 | ||
|
|
ce8006e851 | ||
|
|
3160f83607 | ||
|
|
be5ba4d952 | ||
|
|
ac61a7d1e6 | ||
|
|
6cddf9d88e | ||
|
|
e01a8f2187 | ||
|
|
890e98a33e | ||
|
|
ba2426aa3f | ||
|
|
deadcb39c2 | ||
|
|
6fce1414c3 | ||
|
|
068744db1b | ||
|
|
b26cdf1fe5 | ||
|
|
8e7da73214 | ||
|
|
0532de6559 | ||
|
|
c83a1b2320 | ||
|
|
725304094e | ||
|
|
63de56ee0f | ||
|
|
309276a317 | ||
|
|
1add3b20c4 | ||
|
|
3b35202280 | ||
|
|
45e5a67676 | ||
|
|
22caeee64f | ||
|
|
576f0cf027 | ||
|
|
ca99063600 | ||
|
|
205daab50d | ||
|
|
d3bbed5e78 | ||
|
|
dc0a137e5b | ||
|
|
db11c9a0b9 | ||
|
|
a39a65cbe0 | ||
|
|
c711253798 | ||
|
|
196f8dff08 | ||
|
|
703880c9ab | ||
|
|
869d65e923 | ||
|
|
335096e19a | ||
|
|
2b75b52489 | ||
|
|
8d300b2d7e | ||
|
|
1cd9438945 | ||
|
|
903beb43a8 | ||
|
|
1963222933 | ||
|
|
d129905a66 | ||
|
|
c83f69841f | ||
|
|
294b2b2c17 | ||
|
|
e33117c00a | ||
|
|
22bc951d7e | ||
|
|
f9ba5a7e11 | ||
|
|
35517ca92c | ||
|
|
1d51b25ed1 | ||
|
|
fe99052599 | ||
|
|
8e1dbb26bd | ||
|
|
c689fe8424 | ||
|
|
fc1359dc03 | ||
|
|
649960b4eb | ||
|
|
8d2e4c3d39 | ||
|
|
69697535bf | ||
|
|
485c6541cf | ||
|
|
8a5833f7ad | ||
|
|
119f02a439 | ||
|
|
ad97414057 | ||
|
|
ea615ef5a4 | ||
|
|
764bbaa19c | ||
|
|
65cfe09b62 | ||
|
|
a947f16b63 | ||
|
|
6674e8e048 | ||
|
|
1ddc18454e | ||
|
|
42fc437268 | ||
|
|
0b6b147939 | ||
|
|
eaf75ea970 | ||
|
|
272058d7d9 | ||
|
|
647364db8f | ||
|
|
f9c9ce2005 | ||
|
|
abc4be8e0f | ||
|
|
b5bdaf3441 | ||
|
|
72c5bfb1fa | ||
|
|
f67a8d87a0 | ||
|
|
1a8f5bfb8e | ||
|
|
5ad9b3e19d | ||
|
|
1909802156 | ||
|
|
2e6776909b | ||
|
|
524c12a5f8 | ||
|
|
58601abd1c | ||
|
|
96c444d1ff | ||
|
|
eaa9f968a6 | ||
|
|
449e32bb81 | ||
|
|
090da0b5c1 | ||
|
|
c1a8e4bfe4 | ||
|
|
81be2027ad | ||
|
|
6755c0d1cf | ||
|
|
738f91a57d | ||
|
|
12d95f0214 | ||
|
|
c93136a2bf | ||
|
|
4cd2b475cb | ||
|
|
e26e95fc37 | ||
|
|
9a005d5239 | ||
|
|
9e41053ead | ||
|
|
767ce8abc8 | ||
|
|
3258db29da | ||
|
|
748c0de539 | ||
|
|
de177f6692 | ||
|
|
714a576113 | ||
|
|
f0b6baf3ad | ||
|
|
7588b24f46 | ||
|
|
1b1d399e5f | ||
|
|
f9dae99006 | ||
|
|
f0035420d7 | ||
|
|
27bad0598a | ||
|
|
67758857e4 | ||
|
|
932fa94af7 | ||
|
|
44eb840232 | ||
|
|
b35cf672c0 | ||
|
|
703be1931a | ||
|
|
a93ff5ed0f | ||
|
|
ad64e7e86d | ||
|
|
337664ae7c | ||
|
|
afa1ed6ad9 | ||
|
|
d1b64cdc07 | ||
|
|
b0489c9a64 | ||
|
|
d8bd70d396 | ||
|
|
7efa6e8801 | ||
|
|
1003996e80 | ||
|
|
1a9c96e4de | ||
|
|
67c43e9200 | ||
|
|
de2e5a0855 | ||
|
|
832009bfdb | ||
|
|
10c67bf395 | ||
|
|
eb58f852f8 | ||
|
|
42859461f3 | ||
|
|
7f19a7d305 | ||
|
|
8d7686ff8e | ||
|
|
07355cf7cc | ||
|
|
fdbb039427 | ||
|
|
32d91fa6d2 | ||
|
|
eeb3b5eed7 | ||
|
|
2403143ff1 | ||
|
|
06d2e1bd23 | ||
|
|
687a17acae | ||
|
|
e7cb20fbf0 | ||
|
|
ab8525705b | ||
|
|
5035d18baa | ||
|
|
8e50d6002b | ||
|
|
d9a91d7678 | ||
|
|
d64b7d7dfd | ||
|
|
00851a5ef4 | ||
|
|
1c06c918af | ||
|
|
7988f02489 | ||
|
|
2f71a32363 | ||
|
|
4c07dde472 | ||
|
|
ee024eb0a2 | ||
|
|
1bcc233245 | ||
|
|
425a78ec1b | ||
|
|
749043c809 | ||
|
|
add8d40f3f | ||
|
|
bba785d643 | ||
|
|
af871f8966 | ||
|
|
d904b0db58 | ||
|
|
defaaf4519 | ||
|
|
602cd3886d | ||
|
|
9d6339b887 | ||
|
|
07cfab72e0 | ||
|
|
3315af8f09 | ||
|
|
557b2496d7 | ||
|
|
96f446ff65 | ||
|
|
425b3923d2 | ||
|
|
096be16636 | ||
|
|
1e662e6e9a | ||
|
|
1a4e429d9e | ||
|
|
c3e22a2f6c | ||
|
|
6cccbf0eb3 | ||
|
|
ff883cc563 | ||
|
|
de7aa3106a | ||
|
|
386df282a3 | ||
|
|
e75aba3ed0 | ||
|
|
023aef053c | ||
|
|
2dafd0d287 | ||
|
|
c8a094e164 | ||
|
|
0b6da0c1ab | ||
|
|
dd62f125c3 | ||
|
|
7b219539a9 | ||
|
|
c457f34eb2 | ||
|
|
d9ca9d3472 | ||
|
|
0f363d37e6 | ||
|
|
c3005ee4d1 | ||
|
|
deec326ddf | ||
|
|
1d49680613 | ||
|
|
e27accc15d | ||
|
|
de646cef2d | ||
|
|
e1ee8f4657 | ||
|
|
d80991977a | ||
|
|
c85d04ebe1 | ||
|
|
8f13499bb8 | ||
|
|
952dba9c2b | ||
|
|
1a5098e8b8 | ||
|
|
6b4f37b544 | ||
|
|
5e46a9bb2b | ||
|
|
cb3ab6ec77 | ||
|
|
da3e13fea5 | ||
|
|
3a409d5c8f | ||
|
|
0e7749a500 | ||
|
|
59575d5cae | ||
|
|
463356f0a7 | ||
|
|
195b4b5129 | ||
|
|
378cea2ae2 | ||
|
|
c7c180fdf1 | ||
|
|
2ff1cebfbe | ||
|
|
b5bc94bce0 | ||
|
|
da21545a77 | ||
|
|
be0e14ab3e | ||
|
|
4ca5f0c145 | ||
|
|
22465c8722 | ||
|
|
ed788742bf | ||
|
|
cf0daed0b8 | ||
|
|
32eb620ef4 | ||
|
|
01b3bf119e | ||
|
|
3ebe56524c | ||
|
|
138c03d565 | ||
|
|
5d38bb36c3 | ||
|
|
36dc44fb22 | ||
|
|
e710a1b989 | ||
|
|
6a36ffb86c | ||
|
|
18507b09b2 | ||
|
|
73c0ef7453 | ||
|
|
4497eb4528 | ||
|
|
c81ed5eb33 | ||
|
|
62a8c61e36 | ||
|
|
3d19102c48 | ||
|
|
14069e6ec4 | ||
|
|
b16c89bf65 | ||
|
|
01f379baa7 | ||
|
|
501d1cc33d | ||
|
|
94a6515b71 | ||
|
|
15318b6601 | ||
|
|
33eba9b96e | ||
|
|
ed296e47f1 | ||
|
|
2b3ee30a46 | ||
|
|
e50188374f | ||
|
|
ee08c39b72 | ||
|
|
d3e63e4220 | ||
|
|
e2f06dbc17 | ||
|
|
b17763e3d4 | ||
|
|
5fcf8d530a | ||
|
|
6f22471a72 | ||
|
|
c65ac49238 | ||
|
|
c7452bab90 | ||
|
|
f2b4b668e3 | ||
|
|
0568346cc3 | ||
|
|
8bff9c9152 | ||
|
|
10955d72ef | ||
|
|
66388f7576 | ||
|
|
7fa32af1c7 | ||
|
|
30d2ba9de3 | ||
|
|
1998a16557 | ||
|
|
7172ff4d9a | ||
|
|
b2386fc712 | ||
|
|
b588cbcb1d | ||
|
|
1c98f3a9b3 | ||
|
|
60d650cc4e | ||
|
|
d92636d424 | ||
|
|
d05dc3f4dd | ||
|
|
008823724f | ||
|
|
9ae55884d2 | ||
|
|
30cb98f874 | ||
|
|
d15cadd760 | ||
|
|
a66eb7351b | ||
|
|
dc905463dc | ||
|
|
4b156d2e64 | ||
|
|
eff90550a1 | ||
|
|
36b89787ce | ||
|
|
8c05e935bd | ||
|
|
f621310da2 | ||
|
|
9a9d33a741 | ||
|
|
cb75b56e45 | ||
|
|
30657f9ca1 | ||
|
|
f827b17dd4 | ||
|
|
c5a0408ccc | ||
|
|
f7dc637a61 | ||
|
|
bf0e20c571 | ||
|
|
3442f4b96a | ||
|
|
5bc14e791a | ||
|
|
1aa4cdc3c8 | ||
|
|
cb8d5328d5 | ||
|
|
0f6e3421c8 | ||
|
|
7338f089f8 | ||
|
|
4dc989f073 | ||
|
|
5f6e29831f | ||
|
|
07b465d24e | ||
|
|
bb7221c5d5 | ||
|
|
52fe15278b | ||
|
|
104dd867c4 | ||
|
|
68143af636 | ||
|
|
c903372111 | ||
|
|
aec3b28547 | ||
|
|
5d5f1dfb68 | ||
|
|
f4f64cc197 | ||
|
|
f10a7081e3 | ||
|
|
33778e4984 | ||
|
|
e35644c005 | ||
|
|
153205edbb | ||
|
|
af9a4539b1 | ||
|
|
1de165506f | ||
|
|
c2e4666579 | ||
|
|
09bcc2042c | ||
|
|
f53dc4c9d6 | ||
|
|
7c6c8da218 | ||
|
|
0a8d13801a | ||
|
|
2d7a85f7af | ||
|
|
8b097aa17e | ||
|
|
d818791866 | ||
|
|
3b28d382d0 | ||
|
|
8cdc1be0df | ||
|
|
f473780c52 | ||
|
|
d20a883194 | ||
|
|
b5b0d4e7c3 | ||
|
|
e026b66bbb | ||
|
|
713c1ed203 | ||
|
|
21f5161ad8 | ||
|
|
eaff98dbb3 | ||
|
|
20a745aa71 | ||
|
|
6f904bb622 | ||
|
|
e9cf161917 | ||
|
|
1a3b3e9100 | ||
|
|
1148e2ce7b | ||
|
|
5bc14ab0de | ||
|
|
8e2f62c6a5 | ||
|
|
67c8b671d2 | ||
|
|
054d3e5fc3 | ||
|
|
325f72aceb | ||
|
|
a4f5ccbf0a | ||
|
|
8d968780c1 | ||
|
|
58ee23f16d | ||
|
|
27960d8c2c | ||
|
|
af6e0faf0a | ||
|
|
8a440faa69 | ||
|
|
9fba2d68fe | ||
|
|
1ea49442f9 | ||
|
|
bb1fcfac33 | ||
|
|
aa4fa8bded | ||
|
|
801d6c1b6f | ||
|
|
cdb43e64c1 | ||
|
|
74aa14c9b4 | ||
|
|
e08c132175 | ||
|
|
f2d55bb881 | ||
|
|
22347e8844 | ||
|
|
af1ca87be1 | ||
|
|
2678dbfbe6 | ||
|
|
b1712b8312 | ||
|
|
92801b1c34 | ||
|
|
f3264dd78d | ||
|
|
ebd613c2cc | ||
|
|
2adde6ae8b | ||
|
|
554cc9488f | ||
|
|
71eeab257f | ||
|
|
7bedea73a8 | ||
|
|
d81a984d4a | ||
|
|
2e8246a02c | ||
|
|
7a50d56d0e | ||
|
|
f4a3d28224 | ||
|
|
979483c7dd | ||
|
|
b20dfb357b | ||
|
|
115d3e133f | ||
|
|
22659afcd0 | ||
|
|
974362bc12 | ||
|
|
bd58c5470c | ||
|
|
bf1084ff6e |
5
.gitmodules
vendored
5
.gitmodules
vendored
@@ -18,4 +18,7 @@
|
||||
url = https://github.com/fmtlib/fmt.git
|
||||
[submodule "lz4"]
|
||||
path = externals/lz4
|
||||
url = http://github.com/lz4/lz4.git
|
||||
url = https://github.com/lz4/lz4.git
|
||||
[submodule "unicorn"]
|
||||
path = externals/unicorn
|
||||
url = https://github.com/yuzu-emu/unicorn
|
||||
|
||||
24
.travis.yml
24
.travis.yml
@@ -1,6 +1,13 @@
|
||||
language: cpp
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
env: NAME="clang-format"
|
||||
sudo: required
|
||||
dist: trusty
|
||||
services: docker
|
||||
install: "./.travis/clang-format/deps.sh"
|
||||
script: "./.travis/clang-format/build.sh"
|
||||
- os: linux
|
||||
env: NAME="linux build"
|
||||
sudo: required
|
||||
@@ -16,25 +23,22 @@ matrix:
|
||||
- os: osx
|
||||
env: NAME="macos build"
|
||||
sudo: false
|
||||
osx_image: xcode7.3
|
||||
osx_image: xcode9.2
|
||||
install: "./.travis/macos/deps.sh"
|
||||
script: "./.travis/macos/build.sh"
|
||||
after_success: "./.travis/macos/upload.sh"
|
||||
- os: linux
|
||||
env: NAME="clang-format"
|
||||
dist: trusty
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- clang-format-3.9
|
||||
script: "./.travis/clang-format/script.sh"
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: Mck15DIWaJdxDiS3aYVlM9N3G6y8VKUI1rnwII7/iolfm1s94U+tgvbheZDmT7SSbFyaGaYO/E8HrV/uZR9Vvs7ev20sHsTN1u60OTWfDIIyHs9SqjhcGbtq95m9/dMFschOYqTOR+gAs5BsxjuoeAotHdhpQEwvkO2oo5oR0zhGy45gjFnVvtcxT/IfpZBIpVgcK3aLb9zT6ekcJbSiPmEB15iLq3xXd0nFUNtEZdX3D6Veye4n5jB6n72qN8JVoKvPZAwaC2K0pZxpcGJaXDchLsw1q+4eCvdz6UJfUemeQ/uMAmjfeQ3wrzYGXe3nCM3WmX5wosCsB0mw4zYatzl3si6CZ1W+0GkV4Rwlx03dfp7v3EeFhTsXYCaXqhwuLZnWOLUik8t9vaSoFUx4nUIRwfO9kAMUJQSpLuHNO2nT01s3GxvqxzczuLQ9he5nGSi0RRodUzDwek1qUp6I4uV3gRHKz4B07YIc1i2fK88NLXjyQ0uLVZ+7Oq1+kgDp6+N7vvXXZ5qZ17tdaysSbKEE0Y8zsoXw7Rk1tPN19vrCS+TSpomNMyQyne1k+I5iZ/qkxPTLAS5qI6Utc2dL3GJdxWRAEfGNO9AIX3GV/jmmKfdcvwGsCYP8hxqs5vLYfgacw3D8NLf1941lQUwavC17jm9EV9g5G3Pn1Cp516E=
|
||||
secure: ElsIAlbvVXBNKsP31nVPysh+mf0GQA4DiL/y5iJeQxKQYR6iRoNo+RfzOBmdswdo0bE/PGeBAlfzCkp15gjhWf6Je0N6dRpczmcmLq6SSQFn1Mpq00xMJB2AgQIlaHs6KFgoUA173EBKbPwgU/NubTFpJFm/Wa+NcSWAHQXKL9KT2M3qKpxNkPl3mKEVsbch4REP+T/46vsa+ikw0VE0kIs6V93LqUQZpI2F0Dhihx8Cxr5iedkE1QsNK+QSX9iItMHbfek9OH980gP7L3lkZltyAA1Pk0c37OAgz2PwczwNKwCT8jg9PMzdcKmWouvLyAkZFuA806ElzwHY3oEd91Zm6+Bk5n24yBKZ9027AZzw38NK2Z2m9Akb8+ar8PdsKU6N5pDutX9qSLayr0oMgJ0s7/xnGBGdL3gfkPCFc50xO/2DxlsOR+zAhPNM9Y76hhGy6A7/40+9uzrJvd4nAuDvIXRzi2Yl2L7mKBE4suMKbFLtk2LlgM0qY5JMVTQ8NliaEtqopfPur2KWFVJUpWDNLtNX8xGqhfwg7cLjIiGmnxSaJBTDuZI6dpEjkWkU0n1xYhGqEqit8DbehYzazozMJ+Vsr8hku7jGlUtlw+U6HG1e19O2y4aGeSwYPROcCNz+BLwmVM8oZE3Roy3qoaa2yiFf+sy6rUHznrhsfEM=
|
||||
file_glob: true
|
||||
file: "artifacts/*"
|
||||
skip_cleanup: true
|
||||
on:
|
||||
tags: true
|
||||
|
||||
notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://api.yuzu-emu.org/code/travis/notify
|
||||
|
||||
3
.travis/clang-format/build.sh
Executable file
3
.travis/clang-format/build.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash -ex /yuzu/.travis/clang-format/docker.sh
|
||||
3
.travis/clang-format/deps.sh
Executable file
3
.travis/clang-format/deps.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
docker pull ubuntu:18.04
|
||||
8
.travis/clang-format/docker.sh
Executable file
8
.travis/clang-format/docker.sh
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
apt-get update
|
||||
apt-get install -y clang-format-6.0
|
||||
|
||||
# Run clang-format
|
||||
cd /yuzu
|
||||
./.travis/clang-format/script.sh
|
||||
@@ -7,7 +7,7 @@ if grep -nr '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .travis*
|
||||
fi
|
||||
|
||||
# Default clang-format points to default 3.5 version one
|
||||
CLANG_FORMAT=clang-format-3.9
|
||||
CLANG_FORMAT=clang-format-6.0
|
||||
$CLANG_FORMAT --version
|
||||
|
||||
if [ "$TRAVIS_EVENT_TYPE" = "pull_request" ]; then
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
apt-get update
|
||||
apt-get install -y build-essential git libcurl4-openssl-dev libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev wget
|
||||
apt-get install -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev wget
|
||||
|
||||
# Get a recent version of CMake
|
||||
wget https://cmake.org/files/v3.10/cmake-3.10.1-Linux-x86_64.sh
|
||||
sh cmake-3.10.1-Linux-x86_64.sh --exclude-subdir --prefix=/ --skip-license
|
||||
|
||||
mkdir /unicorn
|
||||
cd /unicorn
|
||||
git clone https://github.com/yuzu-emu/unicorn .
|
||||
UNICORN_ARCHS=aarch64 ./make.sh
|
||||
./make.sh install
|
||||
|
||||
cd /yuzu
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -DUSE_SYSTEM_CURL=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j4
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
@@ -8,7 +8,7 @@ COMPRESSION_FLAGS="-cJvf"
|
||||
|
||||
mkdir "$REV_NAME"
|
||||
|
||||
cp build/src/yuzu_cmd/yuzu-cmd "$REV_NAME"
|
||||
cp build/src/yuzu/yuzu "$REV_NAME"
|
||||
cp build/bin/yuzu-cmd "$REV_NAME"
|
||||
cp build/bin/yuzu "$REV_NAME"
|
||||
|
||||
. .travis/common/post-upload.sh
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
set -o pipefail
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.9
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.12
|
||||
export Qt5_DIR=$(brew --prefix)/opt/qt5
|
||||
export UNICORNDIR=$(pwd)/externals/unicorn
|
||||
|
||||
mkdir build && cd build
|
||||
cmake --version
|
||||
cmake .. -DUSE_SYSTEM_CURL=ON -DCMAKE_OSX_ARCHITECTURES="x86_64;x86_64h" -DCMAKE_BUILD_TYPE=Release
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j4
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
@@ -2,9 +2,4 @@
|
||||
|
||||
brew update
|
||||
brew install dylibbundler p7zip qt5 sdl2
|
||||
|
||||
mkdir externals/unicorn
|
||||
pushd externals/unicorn
|
||||
git clone https://github.com/yuzu-emu/unicorn .
|
||||
UNICORN_ARCHS=aarch64 ./make.sh macos-universal-no
|
||||
popd
|
||||
brew outdated cmake || brew upgrade cmake
|
||||
|
||||
@@ -8,8 +8,8 @@ COMPRESSION_FLAGS="-czvf"
|
||||
|
||||
mkdir "$REV_NAME"
|
||||
|
||||
cp build/src/yuzu_cmd/yuzu-cmd "$REV_NAME"
|
||||
cp -r build/src/yuzu/yuzu.app "$REV_NAME"
|
||||
cp build/bin/yuzu-cmd "$REV_NAME"
|
||||
cp -r build/bin/yuzu.app "$REV_NAME"
|
||||
|
||||
# move qt libs into app bundle for deployment
|
||||
$(brew --prefix)/opt/qt5/bin/macdeployqt "${REV_NAME}/yuzu.app"
|
||||
|
||||
167
CMakeLists.txt
167
CMakeLists.txt
@@ -3,16 +3,19 @@ cmake_minimum_required(VERSION 3.6)
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
|
||||
include(DownloadExternals)
|
||||
include(CMakeDependentOption)
|
||||
|
||||
project(yuzu)
|
||||
|
||||
# Set bundled sdl2/qt as dependent options.
|
||||
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
|
||||
option(ENABLE_SDL2 "Enable the SDL2 frontend" ON)
|
||||
option(CITRA_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON "ENABLE_SDL2;MSVC" OFF)
|
||||
|
||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||
option(CITRA_USE_BUNDLED_QT "Download bundled Qt binaries" OFF)
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
|
||||
|
||||
option(YUZU_USE_BUNDLED_UNICORN "Download bundled Unicorn binaries" OFF)
|
||||
option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
|
||||
|
||||
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
message(STATUS "Copying pre-commit hook")
|
||||
@@ -29,7 +32,7 @@ function(check_submodules_present)
|
||||
foreach(module ${gitmodules})
|
||||
string(REGEX REPLACE "path *= *" "" module ${module})
|
||||
if (NOT EXISTS "${CMAKE_SOURCE_DIR}/${module}/.git")
|
||||
message(SEND_ERROR "Git submodule ${module} not found."
|
||||
message(FATAL_ERROR "Git submodule ${module} not found. "
|
||||
"Please run: git submodule update --init --recursive")
|
||||
endif()
|
||||
endforeach()
|
||||
@@ -56,15 +59,18 @@ function(detect_architecture symbol arch)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
if (MSVC)
|
||||
detect_architecture("_M_AMD64" x86_64)
|
||||
detect_architecture("_M_IX86" x86)
|
||||
detect_architecture("_M_ARM" ARM)
|
||||
else()
|
||||
detect_architecture("__x86_64__" x86_64)
|
||||
detect_architecture("__i386__" x86)
|
||||
detect_architecture("__arm__" ARM)
|
||||
if (NOT ENABLE_GENERIC)
|
||||
if (MSVC)
|
||||
detect_architecture("_M_AMD64" x86_64)
|
||||
detect_architecture("_M_IX86" x86)
|
||||
detect_architecture("_M_ARM" ARM)
|
||||
else()
|
||||
detect_architecture("__x86_64__" x86_64)
|
||||
detect_architecture("__i386__" x86)
|
||||
detect_architecture("__arm__" ARM)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT DEFINED ARCHITECTURE)
|
||||
set(ARCHITECTURE "GENERIC")
|
||||
set(ARCHITECTURE_GENERIC 1)
|
||||
@@ -134,6 +140,12 @@ else()
|
||||
set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE)
|
||||
endif()
|
||||
|
||||
# Fix GCC C++17 and Boost.ICL incompatibility (needed to build dynarmic)
|
||||
# See https://bugzilla.redhat.com/show_bug.cgi?id=1485641#c1
|
||||
if (CMAKE_COMPILER_IS_GNUCC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-new-ttp-matching")
|
||||
endif()
|
||||
|
||||
# Set file offset size to 64 bits.
|
||||
#
|
||||
# On modern Unixes, this is typically already the case. The lone exception is
|
||||
@@ -148,6 +160,9 @@ set_property(DIRECTORY APPEND PROPERTY
|
||||
COMPILE_DEFINITIONS $<$<CONFIG:Debug>:_DEBUG> $<$<NOT:$<CONFIG:Debug>>:NDEBUG>)
|
||||
|
||||
|
||||
math(EXPR EMU_ARCH_BITS ${CMAKE_SIZEOF_VOID_P}*8)
|
||||
add_definitions(-DEMU_ARCH_BITS=${EMU_ARCH_BITS})
|
||||
|
||||
# System imported libraries
|
||||
# ======================
|
||||
|
||||
@@ -160,17 +175,20 @@ if (NOT Boost_FOUND)
|
||||
find_package(Boost QUIET REQUIRED)
|
||||
endif()
|
||||
|
||||
# Output binaries to bin/
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
# Prefer the -pthread flag on Linux.
|
||||
set(THREADS_PREFER_PTHREAD_FLAG ON)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
if (ENABLE_SDL2)
|
||||
if (CITRA_USE_BUNDLED_SDL2)
|
||||
if (YUZU_USE_BUNDLED_SDL2)
|
||||
# Detect toolchain and platform
|
||||
if (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
set(SDL2_VER "SDL2-2.0.5")
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable CITRA_USE_BUNDLED_SDL2 and provide your own.")
|
||||
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
|
||||
endif()
|
||||
|
||||
if (DEFINED SDL2_VER)
|
||||
@@ -195,26 +213,56 @@ else()
|
||||
set(SDL2_FOUND NO)
|
||||
endif()
|
||||
|
||||
# If unicorn isn't found, msvc -> download bundled unicorn; everyone else -> build external
|
||||
if (YUZU_USE_BUNDLED_UNICORN)
|
||||
# Detect toolchain and platform
|
||||
if (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
set(UNICORN_VER "unicorn-yuzu")
|
||||
if (MSVC)
|
||||
message(STATUS "unicorn not found, falling back to bundled")
|
||||
# Detect toolchain and platform
|
||||
if (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
set(UNICORN_VER "unicorn-yuzu")
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Unicorn binaries for your toolchain. Disable YUZU_USE_BUNDLED_UNICORN and provide your own.")
|
||||
endif()
|
||||
|
||||
if (DEFINED UNICORN_VER)
|
||||
download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
|
||||
endif()
|
||||
|
||||
if (DEFINED UNICORN_VER)
|
||||
download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
|
||||
endif()
|
||||
|
||||
set(UNICORN_FOUND YES)
|
||||
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
|
||||
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/lib/x64/unicorn_dynload.lib" CACHE PATH "Path to Unicorn library" FORCE)
|
||||
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/lib/x64/" CACHE PATH "Path to unicorn.dll" FORCE)
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Unicorn binaries for your toolchain. Disable YUZU_USE_BUNDLED_UNICORN and provide your own.")
|
||||
endif()
|
||||
message(STATUS "unicorn not found, falling back to externals")
|
||||
if (MINGW)
|
||||
set(UNICORN_LIB_NAME "unicorn.a")
|
||||
else()
|
||||
set(UNICORN_LIB_NAME "libunicorn.a")
|
||||
endif()
|
||||
|
||||
if (DEFINED UNICORN_VER)
|
||||
download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
|
||||
endif()
|
||||
set(UNICORN_FOUND YES)
|
||||
set(UNICORN_PREFIX ${CMAKE_SOURCE_DIR}/externals/unicorn)
|
||||
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE)
|
||||
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
|
||||
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE)
|
||||
|
||||
if (DEFINED UNICORN_VER)
|
||||
download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
|
||||
endif()
|
||||
find_package(PythonInterp 2.7 REQUIRED)
|
||||
|
||||
set(UNICORN_FOUND YES)
|
||||
set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers")
|
||||
set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/lib/x64/unicorn_dynload.lib" CACHE PATH "Path to Unicorn library")
|
||||
set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/lib/x64/" CACHE PATH "Path to unicorn.dll")
|
||||
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
|
||||
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no
|
||||
WORKING_DIRECTORY ${UNICORN_PREFIX}
|
||||
)
|
||||
# ALL makes this custom target build every time
|
||||
# but it won't actually build if LIBUNICORN_LIBRARY is up to date
|
||||
add_custom_target(unicorn-build ALL
|
||||
DEPENDS ${LIBUNICORN_LIBRARY}
|
||||
)
|
||||
unset(UNICORN_LIB_NAME)
|
||||
endif()
|
||||
else()
|
||||
find_package(Unicorn REQUIRED)
|
||||
endif()
|
||||
@@ -223,14 +271,16 @@ if (UNICORN_FOUND)
|
||||
add_library(unicorn INTERFACE)
|
||||
target_link_libraries(unicorn INTERFACE "${LIBUNICORN_LIBRARY}")
|
||||
target_include_directories(unicorn INTERFACE "${LIBUNICORN_INCLUDE_DIR}")
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find or build unicorn which is required.")
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
if (CITRA_USE_BUNDLED_QT)
|
||||
if (YUZU_USE_BUNDLED_QT)
|
||||
if (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
set(QT_VER qt-5.7-msvc2015_64)
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable CITRA_USE_BUNDLED_QT and provide your own.")
|
||||
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
|
||||
endif()
|
||||
|
||||
if (DEFINED QT_VER)
|
||||
@@ -278,6 +328,53 @@ if (UNIX OR MINGW)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Setup a custom clang-format target (if clang-format can be found) that will run
|
||||
# against all the src files. This should be used before making a pull request.
|
||||
# =======================================================================
|
||||
|
||||
set(CLANG_FORMAT_POSTFIX "-6.0")
|
||||
find_program(CLANG_FORMAT
|
||||
NAMES clang-format${CLANG_FORMAT_POSTFIX}
|
||||
clang-format
|
||||
PATHS ${CMAKE_BINARY_DIR}/externals)
|
||||
# if find_program doesn't find it, try to download from externals
|
||||
if (NOT CLANG_FORMAT)
|
||||
if (WIN32)
|
||||
message(STATUS "Clang format not found! Downloading...")
|
||||
set(CLANG_FORMAT "${CMAKE_BINARY_DIR}/externals/clang-format${CLANG_FORMAT_POSTFIX}.exe")
|
||||
file(DOWNLOAD
|
||||
https://github.com/yuzu-emu/ext-windows-bin/raw/master/clang-format${CLANG_FORMAT_POSTFIX}.exe
|
||||
"${CLANG_FORMAT}" SHOW_PROGRESS
|
||||
STATUS DOWNLOAD_SUCCESS)
|
||||
if (NOT DOWNLOAD_SUCCESS EQUAL 0)
|
||||
message(WARNING "Could not download clang format! Disabling the clang format target")
|
||||
file(REMOVE ${CLANG_FORMAT})
|
||||
unset(CLANG_FORMAT)
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "Clang format not found! Disabling the clang format target")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (CLANG_FORMAT)
|
||||
set(SRCS ${CMAKE_SOURCE_DIR}/src)
|
||||
set(CCOMMENT "Running clang format against all the .h and .cpp files in src/")
|
||||
if (WIN32)
|
||||
add_custom_target(clang-format
|
||||
COMMAND powershell.exe -Command "${CLANG_FORMAT} -i @(Get-ChildItem -Recurse ${SRCS}/* -Include \'*.h\', \'*.cpp\')"
|
||||
COMMENT ${CCOMMENT})
|
||||
elseif(MINGW)
|
||||
add_custom_target(clang-format
|
||||
COMMAND find `cygpath -u ${SRCS}` -iname *.h -o -iname *.cpp | xargs `cygpath -u ${CLANG_FORMAT}` -i
|
||||
COMMENT ${CCOMMENT})
|
||||
else()
|
||||
add_custom_target(clang-format
|
||||
COMMAND find ${SRCS} -iname *.h -o -iname *.cpp | xargs ${CLANG_FORMAT} -i
|
||||
COMMENT ${CCOMMENT})
|
||||
endif()
|
||||
unset(SRCS)
|
||||
unset(CCOMMENT)
|
||||
endif()
|
||||
|
||||
# Include source code
|
||||
# ===================
|
||||
@@ -285,12 +382,14 @@ endif()
|
||||
# This function should be passed a list of all files in a target. It will automatically generate
|
||||
# file groups following the directory hierarchy, so that the layout of the files in IDEs matches the
|
||||
# one in the filesystem.
|
||||
function(create_directory_groups)
|
||||
function(create_target_directory_groups target_name)
|
||||
# Place any files that aren't in the source list in a separate group so that they don't get in
|
||||
# the way.
|
||||
source_group("Other Files" REGULAR_EXPRESSION ".")
|
||||
|
||||
foreach(file_name ${ARGV})
|
||||
get_target_property(target_sources "${target_name}" SOURCES)
|
||||
|
||||
foreach(file_name IN LISTS target_sources)
|
||||
get_filename_component(dir_name "${file_name}" PATH)
|
||||
# Group names use '\' as a separator even though the entire rest of CMake uses '/'...
|
||||
string(REPLACE "/" "\\" group_name "${dir_name}")
|
||||
@@ -327,7 +426,7 @@ if(ENABLE_QT AND UNIX AND NOT APPLE)
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.desktop"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.svg"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/pixmaps")
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/icons/hicolor/scalable/apps")
|
||||
install(FILES "${CMAKE_SOURCE_DIR}/dist/yuzu.xml"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/mime/packages")
|
||||
endif()
|
||||
|
||||
@@ -5,7 +5,16 @@
|
||||
If you believe you have a valid issue report, please post text or a screenshot from the log (the console window that opens alongside yuzu) and build version (hex string visible in the titlebar and zip filename), as well as your hardware and software information if applicable.
|
||||
|
||||
# Contributing
|
||||
yuzu is a brand new project, so we have a great opportunity to keep things clean and well organized early on. As such, coding style is very important when making commits. We run clang-format on our CI to check the code. Please use it to format your code when contributing. However, it doesn't cover all the rules below. Some of them aren't very strict rules since we want to be flexible and we understand that under certain circumstances some of them can be counterproductive. Just try to follow as many of them as possible:
|
||||
yuzu is a brand new project, so we have a great opportunity to keep things clean and well organized early on. As such, coding style is very important when making commits. We run clang-format on our CI to check the code. Please use it to format your code when contributing. However, it doesn't cover all the rules below. Some of them aren't very strict rules since we want to be flexible and we understand that under certain circumstances some of them can be counterproductive. Just try to follow as many of them as possible.
|
||||
|
||||
# Using clang format (version 6.0)
|
||||
When generating the native build script for your toolset, cmake will try to find the correct version of clang format (or will download it on windows). Before running cmake, please install clang format version 6.0 for your platform as follows:
|
||||
|
||||
* Windows: do nothing; cmake will download a pre built binary for MSVC and MINGW. MSVC users can additionally install a clang format Visual Studio extension to add features like format on save.
|
||||
* OSX: run `brew install clang-format`.
|
||||
* Linux: use your package manager to get an appropriate binary.
|
||||
|
||||
If clang format is found, then cmake will add a custom build target that can be run at any time to run clang format against *all* source files and update the formatting in them. This should be used before making a pull request so that the reviewers can spend more time reviewing the code instead of having to worry about minor style violations. On MSVC, you can run clang format by building the clang-format project in the solution. On OSX, you can either use the Makefile target `make clang-format` or by building the clang-format target in XCode. For Makefile builds, you can use the clang-format target with `make clang-format`
|
||||
|
||||
### General Rules
|
||||
* A lot of code was taken from other projects (e.g. Citra, Dolphin, PPSSPP, Gekko). In general, when editing other people's code, follow the style of the module you're in (or better yet, fix the style if it drastically differs from our guide).
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
yuzu emulator
|
||||
=============
|
||||
[](https://travis-ci.org/yuzu-emu/yuzu)
|
||||
[](https://ci.appveyor.com/project/bunnei/yuzu)
|
||||
|
||||
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
|
||||
|
||||
It is written in C++ with portability in mind, with builds actively maintained for Windows, Linux and macOS. The emulator is currently only useful for homebrew development and research purposes.
|
||||
@@ -10,7 +13,7 @@ yuzu is licensed under the GPLv2 (or any later version). Refer to the license.tx
|
||||
|
||||
Check out our [website](https://yuzu-emu.org/)!
|
||||
|
||||
For development discussion, please join us on [Discord](https://discord.gg/VXqngT3).
|
||||
For development discussion, please join us on [Discord](https://discord.gg/XQV6dn9).
|
||||
|
||||
### Development
|
||||
|
||||
|
||||
31
appveyor.yml
31
appveyor.yml
@@ -13,8 +13,8 @@ environment:
|
||||
# Tell msys2 to inherit the current directory when starting the shell
|
||||
CHERE_INVOKING: 1
|
||||
matrix:
|
||||
- BUILD_TYPE: mingw
|
||||
- BUILD_TYPE: msvc
|
||||
- BUILD_TYPE: mingw
|
||||
|
||||
platform:
|
||||
- x64
|
||||
@@ -28,7 +28,6 @@ install:
|
||||
if ($env:BUILD_TYPE -eq 'mingw') {
|
||||
$dependencies = "mingw64/mingw-w64-x86_64-cmake",
|
||||
"mingw64/mingw-w64-x86_64-qt5",
|
||||
"mingw64/mingw-w64-x86_64-curl",
|
||||
"mingw64/mingw-w64-x86_64-SDL2"
|
||||
# redirect err to null to prevent warnings from becoming errors
|
||||
# workaround to prevent pacman from failing due to cyclical dependencies
|
||||
@@ -42,9 +41,9 @@ before_build:
|
||||
- ps: |
|
||||
if ($env:BUILD_TYPE -eq 'msvc') {
|
||||
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
|
||||
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DCMAKE_USE_OPENSSL=0 .. 2>&1 && exit 0'
|
||||
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 .. 2>&1 && exit 0'
|
||||
} else {
|
||||
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DUSE_SYSTEM_CURL=1 -DYUZU_USE_BUNDLED_CURL=1 -DCMAKE_BUILD_TYPE=Release .. 2>&1"
|
||||
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release .. 2>&1"
|
||||
}
|
||||
- cd ..
|
||||
|
||||
@@ -81,11 +80,19 @@ after_build:
|
||||
$env:BUILD_SYMBOLS = $MSVC_BUILD_PDB
|
||||
$env:BUILD_UPDATE = $MSVC_SEVENZIP
|
||||
|
||||
7z a -tzip $MSVC_BUILD_PDB .\msvc_build\bin\release\*.pdb
|
||||
rm .\msvc_build\bin\release\*.pdb
|
||||
$BUILD_DIR = ".\msvc_build\bin\Release"
|
||||
|
||||
# Make a debug symbol upload
|
||||
mkdir pdb
|
||||
Get-ChildItem "$BUILD_DIR\" -Recurse -Filter "*.pdb" | Copy-Item -destination .\pdb
|
||||
7z a -tzip $MSVC_BUILD_PDB .\pdb\*.pdb
|
||||
rm "$BUILD_DIR\*.pdb"
|
||||
|
||||
mkdir $RELEASE_DIST
|
||||
Copy-Item .\msvc_build\bin\release\* -Destination $RELEASE_DIST -Recurse
|
||||
# get rid of extra exes by copying everything over, then deleting all the exes, then copying just the exes we want
|
||||
Copy-Item "$BUILD_DIR\*" -Destination $RELEASE_DIST -Recurse
|
||||
rm "$RELEASE_DIST\*.exe"
|
||||
Get-ChildItem "$BUILD_DIR" -Recurse -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
Copy-Item .\license.txt -Destination $RELEASE_DIST
|
||||
Copy-Item .\README.md -Destination $RELEASE_DIST
|
||||
7z a -tzip $MSVC_BUILD_ZIP $RELEASE_DIST\*
|
||||
@@ -104,16 +111,14 @@ after_build:
|
||||
$env:BUILD_UPDATE = $MINGW_SEVENZIP
|
||||
|
||||
$CMAKE_SOURCE_DIR = "$env:APPVEYOR_BUILD_FOLDER"
|
||||
$CMAKE_BINARY_DIR = "$CMAKE_SOURCE_DIR/mingw_build"
|
||||
$CMAKE_BINARY_DIR = "$CMAKE_SOURCE_DIR/mingw_build/bin"
|
||||
$RELEASE_DIST = $RELEASE_DIST + "-mingw"
|
||||
|
||||
mkdir $RELEASE_DIST
|
||||
mkdir $RELEASE_DIST/platforms
|
||||
|
||||
# copy the compiled binaries and other release files to the release folder
|
||||
Get-ChildItem "$CMAKE_BINARY_DIR" -Recurse -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
# copy the libcurl dll
|
||||
Get-ChildItem "$CMAKE_BINARY_DIR" -Recurse -Filter "libcurl.dll" | Copy-Item -destination $RELEASE_DIST
|
||||
Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
Copy-Item -path "$CMAKE_SOURCE_DIR/license.txt" -destination $RELEASE_DIST
|
||||
Copy-Item -path "$CMAKE_SOURCE_DIR/README.md" -destination $RELEASE_DIST
|
||||
# copy all the dll dependencies to the release folder
|
||||
@@ -122,7 +127,7 @@ after_build:
|
||||
# QT dll dependencies
|
||||
"libbz2-*.dll","libicudt*.dll","libicuin*.dll","libicuuc*.dll","libffi-*.dll",
|
||||
"libfreetype-*.dll","libglib-*.dll","libgobject-*.dll","libgraphite2.dll","libiconv-*.dll",
|
||||
"libharfbuzz-*.dll","libintl-*.dll","libpcre-*.dll","libpcre16-*.dll","libpng16-*.dll",
|
||||
"libharfbuzz-*.dll","libintl-*.dll","libpcre-*.dll","libpcre2-16-*.dll","libpcre16-*.dll","libpng16-*.dll",
|
||||
# Runtime/Other dependencies
|
||||
"libgcc_s_seh-*.dll","libstdc++-*.dll","libwinpthread-*.dll","SDL2.dll","zlib1.dll"
|
||||
foreach ($file in $MingwDLLs) {
|
||||
@@ -165,7 +170,7 @@ deploy:
|
||||
provider: GitHub
|
||||
release: $(appveyor_repo_tag_name)
|
||||
auth_token:
|
||||
secure: "dbpsMC/MgPKWFNJCXpQl4cR8FYhepkPLjgNp/pRMktZ8oLKTqPYErfreaIxb/4P1"
|
||||
secure: QqePPnXbkzmXct5c8hZ2X5AbsthbI6cS1Sr+VBzcD8oUOIjfWJJKXVAQGUbQAbb0
|
||||
artifact: update,build
|
||||
draft: false
|
||||
prerelease: false
|
||||
|
||||
2
externals/catch
vendored
2
externals/catch
vendored
Submodule externals/catch updated: 3dcc923351...cd76f5730c
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: bc73004dd5...6b4c6b06a9
14
externals/getopt/CMakeLists.txt
vendored
14
externals/getopt/CMakeLists.txt
vendored
@@ -1,11 +1,9 @@
|
||||
set(SRCS
|
||||
getopt.c
|
||||
)
|
||||
set(HEADERS
|
||||
getopt.h
|
||||
)
|
||||
add_library(getopt
|
||||
getopt.c
|
||||
getopt.h
|
||||
)
|
||||
|
||||
create_target_directory_groups(getopt)
|
||||
|
||||
create_directory_groups(${SRCS} ${HEADERS})
|
||||
add_library(getopt ${SRCS} ${HEADERS})
|
||||
target_compile_definitions(getopt PUBLIC STATIC_GETOPT)
|
||||
target_include_directories(getopt INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
15
externals/glad/CMakeLists.txt
vendored
15
externals/glad/CMakeLists.txt
vendored
@@ -1,13 +1,10 @@
|
||||
set(SRCS
|
||||
src/glad.c
|
||||
)
|
||||
set(HEADERS
|
||||
include/KHR/khrplatform.h
|
||||
include/glad/glad.h
|
||||
)
|
||||
add_library(glad STATIC
|
||||
src/glad.c
|
||||
include/KHR/khrplatform.h
|
||||
include/glad/glad.h
|
||||
)
|
||||
|
||||
create_directory_groups(${SRCS} ${HEADERS})
|
||||
add_library(glad STATIC ${SRCS} ${HEADERS})
|
||||
create_target_directory_groups(glad)
|
||||
target_include_directories(glad PUBLIC "include/")
|
||||
|
||||
if ("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
|
||||
|
||||
2
externals/glad/Readme.md
vendored
2
externals/glad/Readme.md
vendored
@@ -1,5 +1,5 @@
|
||||
These files were generated by the [glad](https://github.com/Dav1dde/glad) OpenGL loader generator and have been checked in as-is. You can re-generate them using glad with the following command:
|
||||
|
||||
```
|
||||
python -m glad --profile core --out-path glad/ --api gl=3.3,gles=3.0
|
||||
python -m glad --profile core --out-path glad/ --api gl=3.3 --generator=c
|
||||
```
|
||||
|
||||
6
externals/glad/include/KHR/khrplatform.h
vendored
6
externals/glad/include/KHR/khrplatform.h
vendored
@@ -26,7 +26,7 @@
|
||||
|
||||
/* Khronos platform-specific types and definitions.
|
||||
*
|
||||
* $Revision: 23298 $ on $Date: 2013-09-30 17:07:13 -0700 (Mon, 30 Sep 2013) $
|
||||
* $Revision: 32517 $ on $Date: 2016-03-11 02:41:19 -0800 (Fri, 11 Mar 2016) $
|
||||
*
|
||||
* Adopters may modify this file to suit their platform. Adopters are
|
||||
* encouraged to submit platform specific modifications to the Khronos
|
||||
@@ -101,6 +101,8 @@
|
||||
# define KHRONOS_APICALL __declspec(dllimport)
|
||||
#elif defined (__SYMBIAN32__)
|
||||
# define KHRONOS_APICALL IMPORT_C
|
||||
#elif defined(__ANDROID__)
|
||||
# define KHRONOS_APICALL __attribute__((visibility("default")))
|
||||
#else
|
||||
# define KHRONOS_APICALL
|
||||
#endif
|
||||
@@ -223,7 +225,7 @@ typedef signed short int khronos_int16_t;
|
||||
typedef unsigned short int khronos_uint16_t;
|
||||
|
||||
/*
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* Types that differ between LLP64 and LP64 architectures - in LLP64,
|
||||
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
|
||||
* to be the only LLP64 architecture in current use.
|
||||
*/
|
||||
|
||||
13755
externals/glad/include/glad/glad.h
vendored
13755
externals/glad/include/glad/glad.h
vendored
File diff suppressed because one or more lines are too long
8704
externals/glad/src/glad.c
vendored
8704
externals/glad/src/glad.c
vendored
File diff suppressed because one or more lines are too long
17
externals/inih/CMakeLists.txt
vendored
17
externals/inih/CMakeLists.txt
vendored
@@ -1,12 +1,9 @@
|
||||
set(SRCS
|
||||
inih/ini.c
|
||||
inih/cpp/INIReader.cpp
|
||||
)
|
||||
set(HEADERS
|
||||
inih/ini.h
|
||||
inih/cpp/INIReader.h
|
||||
)
|
||||
add_library(inih
|
||||
inih/ini.c
|
||||
inih/ini.h
|
||||
inih/cpp/INIReader.cpp
|
||||
inih/cpp/INIReader.h
|
||||
)
|
||||
|
||||
create_directory_groups(${SRCS} ${HEADERS})
|
||||
add_library(inih ${SRCS} ${HEADERS})
|
||||
create_target_directory_groups(inih)
|
||||
target_include_directories(inih INTERFACE .)
|
||||
|
||||
14
externals/microprofile/microprofileui.h
vendored
14
externals/microprofile/microprofileui.h
vendored
@@ -1853,7 +1853,7 @@ void MicroProfileDrawBarView(uint32_t nScreenWidth, uint32_t nScreenHeight)
|
||||
{
|
||||
if(nMetaIndex < MICROPROFILE_META_MAX && S.MetaCounters[nMetaIndex].pName)
|
||||
{
|
||||
uint32_t nStrWidth = strlen(S.MetaCounters[nMetaIndex].pName);
|
||||
uint32_t nStrWidth = static_cast<uint32_t>(strlen(S.MetaCounters[nMetaIndex].pName));
|
||||
if(S.nBars & MP_DRAW_TIMERS)
|
||||
nWidth += 6 + (1+MICROPROFILE_TEXT_WIDTH) * (nStrWidth);
|
||||
if(S.nBars & MP_DRAW_AVERAGE)
|
||||
@@ -1907,7 +1907,7 @@ void MicroProfileDrawBarView(uint32_t nScreenWidth, uint32_t nScreenHeight)
|
||||
{
|
||||
if(0 != (S.nBars & (MP_DRAW_META_FIRST<<i)) && S.MetaCounters[i].pName)
|
||||
{
|
||||
uint32_t nBufferSize = strlen(S.MetaCounters[i].pName) + 32;
|
||||
uint32_t nBufferSize = static_cast<uint32_t>(strlen(S.MetaCounters[i].pName) + 32);
|
||||
char* buffer = (char*)alloca(nBufferSize);
|
||||
if(S.nBars & MP_DRAW_TIMERS)
|
||||
nX += MicroProfileDrawBarMetaCount(nX, nY, &S.MetaCounters[i].nCounters[0], S.MetaCounters[i].pName, nTotalHeight) + 1;
|
||||
@@ -1991,7 +1991,7 @@ const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected)
|
||||
else
|
||||
{
|
||||
nIndex = nIndex-1;
|
||||
if(nIndex < UI.GroupMenuCount)
|
||||
if(static_cast<uint32_t>(nIndex) < UI.GroupMenuCount)
|
||||
{
|
||||
MicroProfileGroupMenuItem& Item = UI.GroupMenu[nIndex];
|
||||
static char buffer[MICROPROFILE_NAME_MAX_LEN+32];
|
||||
@@ -2134,7 +2134,7 @@ const char* MicroProfileUIMenuCustom(int nIndex, bool* bSelected)
|
||||
case 1: return "--";
|
||||
default:
|
||||
nIndex -= 2;
|
||||
if(nIndex < UI.nCustomCount)
|
||||
if(static_cast<uint32_t>(nIndex) < UI.nCustomCount)
|
||||
{
|
||||
return UI.Custom[nIndex].pName;
|
||||
}
|
||||
@@ -2184,7 +2184,7 @@ void MicroProfileUIClickGroups(int nIndex)
|
||||
else
|
||||
{
|
||||
nIndex -= 1;
|
||||
if(nIndex < UI.GroupMenuCount)
|
||||
if(static_cast<uint32_t>(nIndex) < UI.GroupMenuCount)
|
||||
{
|
||||
MicroProfileGroupMenuItem& Item = UI.GroupMenu[nIndex];
|
||||
if(Item.nIsCategory)
|
||||
@@ -2599,7 +2599,7 @@ void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
|
||||
nOffsetY = nOffsetYBase;
|
||||
float* pMs = pCustom->nFlags & MICROPROFILE_CUSTOM_BAR_SOURCE_MAX ? pTimeMax : pTimeAvg;
|
||||
const char* pString = pCustom->nFlags & MICROPROFILE_CUSTOM_BAR_SOURCE_MAX ? "Max" : "Avg";
|
||||
MicroProfileDrawText(nMaxOffsetX, nOffsetY, (uint32_t)-1, pString, strlen(pString));
|
||||
MicroProfileDrawText(nMaxOffsetX, nOffsetY, (uint32_t)-1, pString, static_cast<uint32_t>(strlen(pString)));
|
||||
int nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2fms", fReference);
|
||||
MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, (uint32_t)-1, Buffer, nSize);
|
||||
for(uint32_t i = 0; i < nCount; ++i)
|
||||
@@ -2613,7 +2613,7 @@ void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
|
||||
{
|
||||
nOffsetY += 2*(1+MICROPROFILE_TEXT_HEIGHT);
|
||||
const char* pString = pCustom->nFlags & MICROPROFILE_CUSTOM_STACK_SOURCE_MAX ? "Max" : "Avg";
|
||||
MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING, nOffsetY, (uint32_t)-1, pString, strlen(pString));
|
||||
MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING, nOffsetY, (uint32_t)-1, pString, static_cast<uint32_t>(strlen(pString)));
|
||||
int nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2fms", fReference);
|
||||
MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, (uint32_t)-1, Buffer, nSize);
|
||||
nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
|
||||
|
||||
1
externals/unicorn
vendored
Submodule
1
externals/unicorn
vendored
Submodule
Submodule externals/unicorn added at 73f4573535
2
externals/xbyak
vendored
2
externals/xbyak
vendored
Submodule externals/xbyak updated: d512551e91...2794cde79e
@@ -17,85 +17,78 @@ if ($ENV{CI})
|
||||
string(SUBSTRING ${WORD} 1 -1 REMAINDER)
|
||||
string(TOUPPER ${FIRST_LETTER} FIRST_LETTER)
|
||||
# this leaves a trailing space on the last word, but we actually want that
|
||||
# because of how its styled in the title bar.
|
||||
# because of how it's styled in the title bar.
|
||||
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER} ")
|
||||
endforeach()
|
||||
endif()
|
||||
endif()
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)
|
||||
|
||||
set(SRCS
|
||||
break_points.cpp
|
||||
file_util.cpp
|
||||
hash.cpp
|
||||
logging/filter.cpp
|
||||
logging/text_formatter.cpp
|
||||
logging/backend.cpp
|
||||
memory_util.cpp
|
||||
microprofile.cpp
|
||||
misc.cpp
|
||||
param_package.cpp
|
||||
scm_rev.cpp
|
||||
string_util.cpp
|
||||
telemetry.cpp
|
||||
thread.cpp
|
||||
timer.cpp
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
alignment.h
|
||||
assert.h
|
||||
bit_field.h
|
||||
bit_set.h
|
||||
break_points.h
|
||||
chunk_file.h
|
||||
code_block.h
|
||||
color.h
|
||||
common_funcs.h
|
||||
common_paths.h
|
||||
common_types.h
|
||||
file_util.h
|
||||
hash.h
|
||||
linear_disk_cache.h
|
||||
logging/text_formatter.h
|
||||
logging/filter.h
|
||||
logging/log.h
|
||||
logging/backend.h
|
||||
math_util.h
|
||||
memory_util.h
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
param_package.h
|
||||
platform.h
|
||||
quaternion.h
|
||||
scm_rev.h
|
||||
scope_exit.h
|
||||
string_util.h
|
||||
swap.h
|
||||
synchronized_wrapper.h
|
||||
telemetry.h
|
||||
thread.h
|
||||
thread_queue_list.h
|
||||
threadsafe_queue.h
|
||||
timer.h
|
||||
vector_math.h
|
||||
)
|
||||
add_library(common STATIC
|
||||
alignment.h
|
||||
assert.h
|
||||
bit_field.h
|
||||
bit_set.h
|
||||
break_points.cpp
|
||||
break_points.h
|
||||
chunk_file.h
|
||||
code_block.h
|
||||
color.h
|
||||
common_funcs.h
|
||||
common_paths.h
|
||||
common_types.h
|
||||
file_util.cpp
|
||||
file_util.h
|
||||
hash.cpp
|
||||
hash.h
|
||||
linear_disk_cache.h
|
||||
logging/backend.cpp
|
||||
logging/backend.h
|
||||
logging/filter.cpp
|
||||
logging/filter.h
|
||||
logging/log.h
|
||||
logging/text_formatter.cpp
|
||||
logging/text_formatter.h
|
||||
math_util.h
|
||||
memory_util.cpp
|
||||
memory_util.h
|
||||
microprofile.cpp
|
||||
microprofile.h
|
||||
microprofileui.h
|
||||
misc.cpp
|
||||
param_package.cpp
|
||||
param_package.h
|
||||
quaternion.h
|
||||
scm_rev.cpp
|
||||
scm_rev.h
|
||||
scope_exit.h
|
||||
string_util.cpp
|
||||
string_util.h
|
||||
swap.h
|
||||
synchronized_wrapper.h
|
||||
telemetry.cpp
|
||||
telemetry.h
|
||||
thread.cpp
|
||||
thread.h
|
||||
thread_queue_list.h
|
||||
threadsafe_queue.h
|
||||
timer.cpp
|
||||
timer.h
|
||||
vector_math.h
|
||||
)
|
||||
|
||||
if(ARCHITECTURE_x86_64)
|
||||
set(SRCS ${SRCS}
|
||||
target_sources(common
|
||||
PRIVATE
|
||||
x64/cpu_detect.cpp
|
||||
)
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
x64/cpu_detect.h
|
||||
x64/xbyak_abi.h
|
||||
x64/xbyak_util.h
|
||||
)
|
||||
)
|
||||
endif()
|
||||
|
||||
create_directory_groups(${SRCS} ${HEADERS})
|
||||
create_target_directory_groups(common)
|
||||
|
||||
add_library(common STATIC ${SRCS} ${HEADERS})
|
||||
target_link_libraries(common PUBLIC Boost::boost microprofile)
|
||||
if (ARCHITECTURE_x86_64)
|
||||
target_link_libraries(common PRIVATE xbyak)
|
||||
|
||||
@@ -236,7 +236,7 @@ public:
|
||||
IntTy m_val;
|
||||
};
|
||||
|
||||
} // Common
|
||||
} // namespace Common
|
||||
|
||||
typedef Common::BitSet<u8> BitSet8;
|
||||
typedef Common::BitSet<u16> BitSet16;
|
||||
|
||||
@@ -607,8 +607,9 @@ public:
|
||||
u32 cookie = arbitraryNumber;
|
||||
Do(cookie);
|
||||
if (mode == PointerWrap::MODE_READ && cookie != arbitraryNumber) {
|
||||
LOG_ERROR(Common, "After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). "
|
||||
"Aborting savestate load...",
|
||||
LOG_ERROR(Common,
|
||||
"After \"%s\", found %d (0x%X) instead of save marker %d (0x%X). "
|
||||
"Aborting savestate load...",
|
||||
prevName, cookie, cookie, arbitraryNumber, arbitraryNumber);
|
||||
SetError(ERROR_FAILURE);
|
||||
}
|
||||
|
||||
@@ -256,4 +256,4 @@ inline void EncodeX24S8(u8 stencil, u8* bytes) {
|
||||
bytes[3] = stencil;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Color
|
||||
|
||||
@@ -873,20 +873,19 @@ bool IOFile::Flush() {
|
||||
}
|
||||
|
||||
bool IOFile::Resize(u64 size) {
|
||||
if (!IsOpen() ||
|
||||
0 !=
|
||||
if (!IsOpen() || 0 !=
|
||||
#ifdef _WIN32
|
||||
// ector: _chsize sucks, not 64-bit safe
|
||||
// F|RES: changed to _chsize_s. i think it is 64-bit safe
|
||||
_chsize_s(_fileno(m_file), size)
|
||||
// ector: _chsize sucks, not 64-bit safe
|
||||
// F|RES: changed to _chsize_s. i think it is 64-bit safe
|
||||
_chsize_s(_fileno(m_file), size)
|
||||
#else
|
||||
// TODO: handle 64bit and growing
|
||||
ftruncate(fileno(m_file), size)
|
||||
// TODO: handle 64bit and growing
|
||||
ftruncate(fileno(m_file), size)
|
||||
#endif
|
||||
)
|
||||
)
|
||||
m_good = false;
|
||||
|
||||
return m_good;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace FileUtil
|
||||
|
||||
@@ -253,7 +253,7 @@ private:
|
||||
bool m_good = true;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace FileUtil
|
||||
|
||||
// To deal with Windows being dumb at unicode:
|
||||
template <typename T>
|
||||
|
||||
@@ -32,17 +32,31 @@ namespace Log {
|
||||
CLS(Kernel) \
|
||||
SUB(Kernel, SVC) \
|
||||
CLS(Service) \
|
||||
SUB(Service, SM) \
|
||||
SUB(Service, ACC) \
|
||||
SUB(Service, Audio) \
|
||||
SUB(Service, AM) \
|
||||
SUB(Service, AOC) \
|
||||
SUB(Service, APM) \
|
||||
SUB(Service, Fatal) \
|
||||
SUB(Service, Friend) \
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, GSP) \
|
||||
SUB(Service, CFG) \
|
||||
SUB(Service, DSP) \
|
||||
SUB(Service, HID) \
|
||||
SUB(Service, LM) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NS) \
|
||||
SUB(Service, NVDRV) \
|
||||
SUB(Service, PCTL) \
|
||||
SUB(Service, SET) \
|
||||
SUB(Service, SM) \
|
||||
SUB(Service, SPL) \
|
||||
SUB(Service, Time) \
|
||||
SUB(Service, VI) \
|
||||
CLS(HW) \
|
||||
SUB(HW, Memory) \
|
||||
SUB(HW, LCD) \
|
||||
SUB(HW, GPU) \
|
||||
SUB(HW, AES) \
|
||||
CLS(IPC) \
|
||||
CLS(Frontend) \
|
||||
CLS(Render) \
|
||||
SUB(Render, Software) \
|
||||
@@ -91,8 +105,8 @@ const char* GetLevelName(Level log_level) {
|
||||
|
||||
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
|
||||
const char* function, const char* format, va_list args) {
|
||||
using std::chrono::steady_clock;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::steady_clock;
|
||||
|
||||
static steady_clock::time_point time_origin = steady_clock::now();
|
||||
|
||||
@@ -131,4 +145,4 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned
|
||||
|
||||
PrintColoredMessage(entry);
|
||||
}
|
||||
}
|
||||
} // namespace Log
|
||||
|
||||
@@ -47,4 +47,4 @@ Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsign
|
||||
const char* function, const char* format, va_list args);
|
||||
|
||||
void SetFilter(Filter* filter);
|
||||
}
|
||||
} // namespace Log
|
||||
|
||||
@@ -94,4 +94,4 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
|
||||
bool Filter::CheckMessage(Class log_class, Level level) const {
|
||||
return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
|
||||
}
|
||||
}
|
||||
} // namespace Log
|
||||
|
||||
@@ -50,4 +50,4 @@ public:
|
||||
private:
|
||||
std::array<Level, (size_t)Class::Count> class_levels;
|
||||
};
|
||||
}
|
||||
} // namespace Log
|
||||
|
||||
@@ -49,17 +49,31 @@ enum class Class : ClassType {
|
||||
Kernel_SVC, ///< Kernel system calls
|
||||
Service, ///< HLE implementation of system services. Each major service
|
||||
/// should have its own subclass.
|
||||
Service_SM, ///< The SRV (Service Directory) implementation
|
||||
Service_FS, ///< The FS (Filesystem) service implementation
|
||||
Service_GSP, ///< The GSP (GPU control) service
|
||||
Service_CFG, ///< The CFG (Configuration) service
|
||||
Service_DSP, ///< The DSP (DSP control) service
|
||||
Service_ACC, ///< The ACC (Accounts) service
|
||||
Service_AM, ///< The AM (Applet manager) service
|
||||
Service_AOC, ///< The AOC (AddOn Content) service
|
||||
Service_APM, ///< The APM (Performance) service
|
||||
Service_Audio, ///< The Audio (Audio control) service
|
||||
Service_Fatal, ///< The Fatal service
|
||||
Service_Friend, ///< The friend service
|
||||
Service_FS, ///< The FS (Filesystem) service
|
||||
Service_HID, ///< The HID (Human interface device) service
|
||||
Service_LM, ///< The LM (Logger) service
|
||||
Service_NIFM, ///< The NIFM (Network interface) service
|
||||
Service_NS, ///< The NS services
|
||||
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
||||
Service_PCTL, ///< The PCTL (Parental control) service
|
||||
Service_SET, ///< The SET (Settings) service
|
||||
Service_SM, ///< The SM (Service manager) service
|
||||
Service_SPL, ///< The SPL service
|
||||
Service_Time, ///< The time service
|
||||
Service_VI, ///< The VI (Video interface) service
|
||||
HW, ///< Low-level hardware emulation
|
||||
HW_Memory, ///< Memory-map and address translation
|
||||
HW_LCD, ///< LCD register emulation
|
||||
HW_GPU, ///< GPU control emulation
|
||||
HW_AES, ///< AES engine emulation
|
||||
IPC, ///< IPC interface
|
||||
Frontend, ///< Emulator UI
|
||||
Render, ///< Emulator video output and hardware acceleration
|
||||
Render_Software, ///< Software renderer backend
|
||||
|
||||
@@ -129,4 +129,4 @@ void PrintColoredMessage(const Entry& entry) {
|
||||
#undef ESC
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace Log
|
||||
|
||||
@@ -28,4 +28,4 @@ void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
|
||||
void PrintMessage(const Entry& entry);
|
||||
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.
|
||||
void PrintColoredMessage(const Entry& entry);
|
||||
}
|
||||
} // namespace Log
|
||||
|
||||
@@ -40,11 +40,12 @@ void* AllocateExecutableMemory(size_t size, bool low) {
|
||||
if (low && (!map_hint))
|
||||
map_hint = (char*)round_page(512 * 1024 * 1024); /* 0.5 GB rounded up to the next page */
|
||||
#endif
|
||||
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_ANON | MAP_PRIVATE
|
||||
void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC,
|
||||
MAP_ANON | MAP_PRIVATE
|
||||
#if defined(ARCHITECTURE_X64) && defined(MAP_32BIT)
|
||||
| (low ? MAP_32BIT : 0)
|
||||
| (low ? MAP_32BIT : 0)
|
||||
#endif
|
||||
,
|
||||
,
|
||||
-1, 0);
|
||||
#endif /* defined(_WIN32) */
|
||||
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2005-2012 Gekko Emulator
|
||||
*
|
||||
* @file platform.h
|
||||
* @author ShizZy <shizzy247@gmail.com>
|
||||
* @date 2012-02-11
|
||||
* @brief Platform detection macros for portable compilation
|
||||
*
|
||||
* @section LICENSE
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details at
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* Official project repository can be found at:
|
||||
* http://code.google.com/p/gekko-gc-emu/
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Platform detection
|
||||
|
||||
#if defined(ARCHITECTURE_x86_64) || defined(__aarch64__)
|
||||
#define EMU_ARCH_BITS 64
|
||||
#elif defined(__i386) || defined(_M_IX86) || defined(__arm__) || defined(_M_ARM)
|
||||
#define EMU_ARCH_BITS 32
|
||||
#endif
|
||||
@@ -46,4 +46,4 @@ inline Quaternion<float> MakeQuaternion(const Math::Vec3<float>& axis, float ang
|
||||
return {axis * std::sin(angle / 2), std::cos(angle / 2)};
|
||||
}
|
||||
|
||||
} // namspace Math
|
||||
} // namespace Math
|
||||
|
||||
@@ -12,4 +12,4 @@ extern const char g_scm_desc[];
|
||||
extern const char g_build_name[];
|
||||
extern const char g_build_date[];
|
||||
|
||||
} // namespace
|
||||
} // namespace Common
|
||||
|
||||
@@ -22,7 +22,7 @@ template <typename Func>
|
||||
ScopeExitHelper<Func> ScopeExit(Func&& func) {
|
||||
return ScopeExitHelper<Func>(std::move(func));
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
* This macro allows you to conveniently specify a block of code that will run on scope exit. Handy
|
||||
|
||||
@@ -202,7 +202,7 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _
|
||||
#ifdef _WIN32
|
||||
":"
|
||||
#endif
|
||||
);
|
||||
);
|
||||
if (std::string::npos == dir_end)
|
||||
dir_end = 0;
|
||||
else
|
||||
@@ -462,4 +462,4 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_l
|
||||
|
||||
return std::string(buffer, len);
|
||||
}
|
||||
}
|
||||
} // namespace Common
|
||||
|
||||
@@ -134,4 +134,4 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
|
||||
* NUL-terminated then the string ends at max_len characters.
|
||||
*/
|
||||
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len);
|
||||
}
|
||||
} // namespace Common
|
||||
|
||||
@@ -53,10 +53,10 @@ template <typename T>
|
||||
class Field : public FieldInterface {
|
||||
public:
|
||||
Field(FieldType type, std::string name, const T& value)
|
||||
: type(type), name(std::move(name)), value(value) {}
|
||||
: name(std::move(name)), type(type), value(value) {}
|
||||
|
||||
Field(FieldType type, std::string name, T&& value)
|
||||
: type(type), name(std::move(name)), value(std::move(value)) {}
|
||||
: name(std::move(name)), type(type), value(std::move(value)) {}
|
||||
|
||||
Field(const Field& other) : Field(other.type, other.name, other.value) {}
|
||||
|
||||
|
||||
@@ -158,4 +158,4 @@ private:
|
||||
std::array<Queue, NUM_QUEUES> queues;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace Common
|
||||
|
||||
@@ -60,20 +60,41 @@ const Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
|
||||
|
||||
const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rcx, Xbyak::util::rdx, Xbyak::util::r8, Xbyak::util::r9, Xbyak::util::r10,
|
||||
Xbyak::util::rcx,
|
||||
Xbyak::util::rdx,
|
||||
Xbyak::util::r8,
|
||||
Xbyak::util::r9,
|
||||
Xbyak::util::r10,
|
||||
Xbyak::util::r11,
|
||||
// XMMs
|
||||
Xbyak::util::xmm0, Xbyak::util::xmm1, Xbyak::util::xmm2, Xbyak::util::xmm3, Xbyak::util::xmm4,
|
||||
Xbyak::util::xmm0,
|
||||
Xbyak::util::xmm1,
|
||||
Xbyak::util::xmm2,
|
||||
Xbyak::util::xmm3,
|
||||
Xbyak::util::xmm4,
|
||||
Xbyak::util::xmm5,
|
||||
});
|
||||
|
||||
const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rbx, Xbyak::util::rsi, Xbyak::util::rdi, Xbyak::util::rbp, Xbyak::util::r12,
|
||||
Xbyak::util::r13, Xbyak::util::r14, Xbyak::util::r15,
|
||||
Xbyak::util::rbx,
|
||||
Xbyak::util::rsi,
|
||||
Xbyak::util::rdi,
|
||||
Xbyak::util::rbp,
|
||||
Xbyak::util::r12,
|
||||
Xbyak::util::r13,
|
||||
Xbyak::util::r14,
|
||||
Xbyak::util::r15,
|
||||
// XMMs
|
||||
Xbyak::util::xmm6, Xbyak::util::xmm7, Xbyak::util::xmm8, Xbyak::util::xmm9, Xbyak::util::xmm10,
|
||||
Xbyak::util::xmm11, Xbyak::util::xmm12, Xbyak::util::xmm13, Xbyak::util::xmm14,
|
||||
Xbyak::util::xmm6,
|
||||
Xbyak::util::xmm7,
|
||||
Xbyak::util::xmm8,
|
||||
Xbyak::util::xmm9,
|
||||
Xbyak::util::xmm10,
|
||||
Xbyak::util::xmm11,
|
||||
Xbyak::util::xmm12,
|
||||
Xbyak::util::xmm13,
|
||||
Xbyak::util::xmm14,
|
||||
Xbyak::util::xmm15,
|
||||
});
|
||||
|
||||
@@ -90,18 +111,40 @@ const Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
|
||||
|
||||
const BitSet32 ABI_ALL_CALLER_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rcx, Xbyak::util::rdx, Xbyak::util::rdi, Xbyak::util::rsi, Xbyak::util::r8,
|
||||
Xbyak::util::r9, Xbyak::util::r10, Xbyak::util::r11,
|
||||
Xbyak::util::rcx,
|
||||
Xbyak::util::rdx,
|
||||
Xbyak::util::rdi,
|
||||
Xbyak::util::rsi,
|
||||
Xbyak::util::r8,
|
||||
Xbyak::util::r9,
|
||||
Xbyak::util::r10,
|
||||
Xbyak::util::r11,
|
||||
// XMMs
|
||||
Xbyak::util::xmm0, Xbyak::util::xmm1, Xbyak::util::xmm2, Xbyak::util::xmm3, Xbyak::util::xmm4,
|
||||
Xbyak::util::xmm5, Xbyak::util::xmm6, Xbyak::util::xmm7, Xbyak::util::xmm8, Xbyak::util::xmm9,
|
||||
Xbyak::util::xmm10, Xbyak::util::xmm11, Xbyak::util::xmm12, Xbyak::util::xmm13,
|
||||
Xbyak::util::xmm14, Xbyak::util::xmm15,
|
||||
Xbyak::util::xmm0,
|
||||
Xbyak::util::xmm1,
|
||||
Xbyak::util::xmm2,
|
||||
Xbyak::util::xmm3,
|
||||
Xbyak::util::xmm4,
|
||||
Xbyak::util::xmm5,
|
||||
Xbyak::util::xmm6,
|
||||
Xbyak::util::xmm7,
|
||||
Xbyak::util::xmm8,
|
||||
Xbyak::util::xmm9,
|
||||
Xbyak::util::xmm10,
|
||||
Xbyak::util::xmm11,
|
||||
Xbyak::util::xmm12,
|
||||
Xbyak::util::xmm13,
|
||||
Xbyak::util::xmm14,
|
||||
Xbyak::util::xmm15,
|
||||
});
|
||||
|
||||
const BitSet32 ABI_ALL_CALLEE_SAVED = BuildRegSet({
|
||||
// GPRs
|
||||
Xbyak::util::rbx, Xbyak::util::rbp, Xbyak::util::r12, Xbyak::util::r13, Xbyak::util::r14,
|
||||
Xbyak::util::rbx,
|
||||
Xbyak::util::rbp,
|
||||
Xbyak::util::r12,
|
||||
Xbyak::util::r13,
|
||||
Xbyak::util::r14,
|
||||
Xbyak::util::r15,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,163 +1,260 @@
|
||||
set(SRCS
|
||||
arm/dynarmic/arm_dynarmic.cpp
|
||||
arm/unicorn/arm_unicorn.cpp
|
||||
core.cpp
|
||||
core_timing.cpp
|
||||
file_sys/archive_backend.cpp
|
||||
file_sys/disk_archive.cpp
|
||||
file_sys/ivfc_archive.cpp
|
||||
file_sys/path_parser.cpp
|
||||
file_sys/savedata_archive.cpp
|
||||
file_sys/title_metadata.cpp
|
||||
frontend/emu_window.cpp
|
||||
frontend/framebuffer_layout.cpp
|
||||
gdbstub/gdbstub.cpp
|
||||
hle/config_mem.cpp
|
||||
hle/kernel/address_arbiter.cpp
|
||||
hle/kernel/client_port.cpp
|
||||
hle/kernel/client_session.cpp
|
||||
hle/kernel/condition_variable.cpp
|
||||
hle/kernel/domain.cpp
|
||||
hle/kernel/event.cpp
|
||||
hle/kernel/handle_table.cpp
|
||||
hle/kernel/hle_ipc.cpp
|
||||
hle/kernel/kernel.cpp
|
||||
hle/kernel/memory.cpp
|
||||
hle/kernel/mutex.cpp
|
||||
hle/kernel/object_address_table.cpp
|
||||
hle/kernel/process.cpp
|
||||
hle/kernel/resource_limit.cpp
|
||||
hle/kernel/server_port.cpp
|
||||
hle/kernel/server_session.cpp
|
||||
hle/kernel/shared_memory.cpp
|
||||
hle/kernel/svc.cpp
|
||||
hle/kernel/thread.cpp
|
||||
hle/kernel/timer.cpp
|
||||
hle/kernel/vm_manager.cpp
|
||||
hle/kernel/wait_object.cpp
|
||||
hle/lock.cpp
|
||||
hle/romfs.cpp
|
||||
hle/service/am/am.cpp
|
||||
hle/service/am/applet_oe.cpp
|
||||
hle/service/aoc/aoc_u.cpp
|
||||
hle/service/apm/apm.cpp
|
||||
hle/service/hid/hid.cpp
|
||||
hle/service/lm/lm.cpp
|
||||
hle/service/nvdrv/devices/nvdisp_disp0.cpp
|
||||
hle/service/nvdrv/devices/nvhost_as_gpu.cpp
|
||||
hle/service/nvdrv/devices/nvmap.cpp
|
||||
hle/service/nvdrv/nvdrv.cpp
|
||||
hle/service/nvdrv/nvdrv_a.cpp
|
||||
hle/service/pctl/pctl.cpp
|
||||
hle/service/pctl/pctl_a.cpp
|
||||
hle/service/service.cpp
|
||||
hle/service/sm/controller.cpp
|
||||
hle/service/sm/sm.cpp
|
||||
hle/service/vi/vi.cpp
|
||||
hle/service/vi/vi_m.cpp
|
||||
hle/shared_page.cpp
|
||||
hw/hw.cpp
|
||||
hw/lcd.cpp
|
||||
loader/elf.cpp
|
||||
loader/linker.cpp
|
||||
loader/loader.cpp
|
||||
loader/nro.cpp
|
||||
loader/nso.cpp
|
||||
tracer/recorder.cpp
|
||||
memory.cpp
|
||||
perf_stats.cpp
|
||||
settings.cpp
|
||||
telemetry_session.cpp
|
||||
)
|
||||
add_library(core STATIC
|
||||
arm/arm_interface.h
|
||||
arm/unicorn/arm_unicorn.cpp
|
||||
arm/unicorn/arm_unicorn.h
|
||||
core.cpp
|
||||
core.h
|
||||
core_timing.cpp
|
||||
core_timing.h
|
||||
file_sys/directory.h
|
||||
file_sys/disk_filesystem.cpp
|
||||
file_sys/disk_filesystem.h
|
||||
file_sys/errors.h
|
||||
file_sys/filesystem.cpp
|
||||
file_sys/filesystem.h
|
||||
file_sys/path_parser.cpp
|
||||
file_sys/path_parser.h
|
||||
file_sys/program_metadata.cpp
|
||||
file_sys/program_metadata.h
|
||||
file_sys/romfs_factory.cpp
|
||||
file_sys/romfs_factory.h
|
||||
file_sys/romfs_filesystem.cpp
|
||||
file_sys/romfs_filesystem.h
|
||||
file_sys/savedata_factory.cpp
|
||||
file_sys/savedata_factory.h
|
||||
file_sys/storage.h
|
||||
frontend/emu_window.cpp
|
||||
frontend/emu_window.h
|
||||
frontend/framebuffer_layout.cpp
|
||||
frontend/framebuffer_layout.h
|
||||
frontend/input.h
|
||||
gdbstub/gdbstub.cpp
|
||||
gdbstub/gdbstub.h
|
||||
hle/config_mem.cpp
|
||||
hle/config_mem.h
|
||||
hle/ipc.h
|
||||
hle/ipc_helpers.h
|
||||
hle/kernel/client_port.cpp
|
||||
hle/kernel/client_port.h
|
||||
hle/kernel/client_session.cpp
|
||||
hle/kernel/client_session.h
|
||||
hle/kernel/condition_variable.cpp
|
||||
hle/kernel/condition_variable.h
|
||||
hle/kernel/errors.h
|
||||
hle/kernel/event.cpp
|
||||
hle/kernel/event.h
|
||||
hle/kernel/handle_table.cpp
|
||||
hle/kernel/handle_table.h
|
||||
hle/kernel/hle_ipc.cpp
|
||||
hle/kernel/hle_ipc.h
|
||||
hle/kernel/kernel.cpp
|
||||
hle/kernel/kernel.h
|
||||
hle/kernel/memory.cpp
|
||||
hle/kernel/memory.h
|
||||
hle/kernel/mutex.cpp
|
||||
hle/kernel/mutex.h
|
||||
hle/kernel/object_address_table.cpp
|
||||
hle/kernel/object_address_table.h
|
||||
hle/kernel/process.cpp
|
||||
hle/kernel/process.h
|
||||
hle/kernel/resource_limit.cpp
|
||||
hle/kernel/resource_limit.h
|
||||
hle/kernel/scheduler.cpp
|
||||
hle/kernel/scheduler.h
|
||||
hle/kernel/server_port.cpp
|
||||
hle/kernel/server_port.h
|
||||
hle/kernel/server_session.cpp
|
||||
hle/kernel/server_session.h
|
||||
hle/kernel/session.h
|
||||
hle/kernel/shared_memory.cpp
|
||||
hle/kernel/shared_memory.h
|
||||
hle/kernel/svc.cpp
|
||||
hle/kernel/svc.h
|
||||
hle/kernel/svc_wrap.h
|
||||
hle/kernel/thread.cpp
|
||||
hle/kernel/thread.h
|
||||
hle/kernel/timer.cpp
|
||||
hle/kernel/timer.h
|
||||
hle/kernel/vm_manager.cpp
|
||||
hle/kernel/vm_manager.h
|
||||
hle/kernel/wait_object.cpp
|
||||
hle/kernel/wait_object.h
|
||||
hle/lock.cpp
|
||||
hle/lock.h
|
||||
hle/result.h
|
||||
hle/romfs.cpp
|
||||
hle/romfs.h
|
||||
hle/service/acc/acc.cpp
|
||||
hle/service/acc/acc.h
|
||||
hle/service/acc/acc_u0.cpp
|
||||
hle/service/acc/acc_u0.h
|
||||
hle/service/am/am.cpp
|
||||
hle/service/am/am.h
|
||||
hle/service/am/applet_ae.cpp
|
||||
hle/service/am/applet_ae.h
|
||||
hle/service/am/applet_oe.cpp
|
||||
hle/service/am/applet_oe.h
|
||||
hle/service/aoc/aoc_u.cpp
|
||||
hle/service/aoc/aoc_u.h
|
||||
hle/service/apm/apm.cpp
|
||||
hle/service/apm/apm.h
|
||||
hle/service/apm/interface.cpp
|
||||
hle/service/apm/interface.h
|
||||
hle/service/audio/audio.cpp
|
||||
hle/service/audio/audio.h
|
||||
hle/service/audio/audin_u.cpp
|
||||
hle/service/audio/audin_u.h
|
||||
hle/service/audio/audout_u.cpp
|
||||
hle/service/audio/audout_u.h
|
||||
hle/service/audio/audrec_u.cpp
|
||||
hle/service/audio/audrec_u.h
|
||||
hle/service/audio/audren_u.cpp
|
||||
hle/service/audio/audren_u.h
|
||||
hle/service/audio/audren_u.cpp
|
||||
hle/service/audio/audren_u.h
|
||||
hle/service/audio/codecctl.cpp
|
||||
hle/service/audio/codecctl.h
|
||||
hle/service/fatal/fatal.cpp
|
||||
hle/service/fatal/fatal.h
|
||||
hle/service/fatal/fatal_p.cpp
|
||||
hle/service/fatal/fatal_p.h
|
||||
hle/service/fatal/fatal_u.cpp
|
||||
hle/service/fatal/fatal_u.h
|
||||
hle/service/filesystem/filesystem.cpp
|
||||
hle/service/filesystem/filesystem.h
|
||||
hle/service/filesystem/fsp_srv.cpp
|
||||
hle/service/filesystem/fsp_srv.h
|
||||
hle/service/friend/friend.cpp
|
||||
hle/service/friend/friend.h
|
||||
hle/service/friend/friend_a.cpp
|
||||
hle/service/friend/friend_a.h
|
||||
hle/service/hid/hid.cpp
|
||||
hle/service/hid/hid.h
|
||||
hle/service/lm/lm.cpp
|
||||
hle/service/lm/lm.h
|
||||
hle/service/nifm/nifm.cpp
|
||||
hle/service/nifm/nifm.h
|
||||
hle/service/nifm/nifm_a.cpp
|
||||
hle/service/nifm/nifm_a.h
|
||||
hle/service/nifm/nifm_s.cpp
|
||||
hle/service/nifm/nifm_s.h
|
||||
hle/service/nifm/nifm_u.cpp
|
||||
hle/service/nifm/nifm_u.h
|
||||
hle/service/ns/ns.cpp
|
||||
hle/service/ns/ns.h
|
||||
hle/service/ns/pl_u.cpp
|
||||
hle/service/ns/pl_u.h
|
||||
hle/service/nvdrv/devices/nvdevice.h
|
||||
hle/service/nvdrv/devices/nvdisp_disp0.cpp
|
||||
hle/service/nvdrv/devices/nvdisp_disp0.h
|
||||
hle/service/nvdrv/devices/nvhost_as_gpu.cpp
|
||||
hle/service/nvdrv/devices/nvhost_as_gpu.h
|
||||
hle/service/nvdrv/devices/nvhost_ctrl.cpp
|
||||
hle/service/nvdrv/devices/nvhost_ctrl.h
|
||||
hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
|
||||
hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
|
||||
hle/service/nvdrv/devices/nvhost_gpu.cpp
|
||||
hle/service/nvdrv/devices/nvhost_gpu.h
|
||||
hle/service/nvdrv/devices/nvmap.cpp
|
||||
hle/service/nvdrv/devices/nvmap.h
|
||||
hle/service/nvdrv/interface.cpp
|
||||
hle/service/nvdrv/interface.h
|
||||
hle/service/nvdrv/nvdrv.cpp
|
||||
hle/service/nvdrv/nvdrv.h
|
||||
hle/service/nvdrv/nvmemp.cpp
|
||||
hle/service/nvdrv/nvmemp.h
|
||||
hle/service/nvflinger/buffer_queue.cpp
|
||||
hle/service/nvflinger/buffer_queue.h
|
||||
hle/service/nvflinger/nvflinger.cpp
|
||||
hle/service/nvflinger/nvflinger.h
|
||||
hle/service/pctl/pctl.cpp
|
||||
hle/service/pctl/pctl.h
|
||||
hle/service/pctl/pctl_a.cpp
|
||||
hle/service/pctl/pctl_a.h
|
||||
hle/service/service.cpp
|
||||
hle/service/service.h
|
||||
hle/service/set/set.cpp
|
||||
hle/service/set/set.h
|
||||
hle/service/set/set_cal.cpp
|
||||
hle/service/set/set_cal.h
|
||||
hle/service/set/set_fd.cpp
|
||||
hle/service/set/set_fd.h
|
||||
hle/service/set/set_sys.cpp
|
||||
hle/service/set/set_sys.h
|
||||
hle/service/set/settings.cpp
|
||||
hle/service/set/settings.h
|
||||
hle/service/sm/controller.cpp
|
||||
hle/service/sm/controller.h
|
||||
hle/service/sm/sm.cpp
|
||||
hle/service/sm/sm.h
|
||||
hle/service/sockets/bsd_u.cpp
|
||||
hle/service/sockets/bsd_u.h
|
||||
hle/service/sockets/sfdnsres.cpp
|
||||
hle/service/sockets/sfdnsres.h
|
||||
hle/service/sockets/sockets.cpp
|
||||
hle/service/sockets/sockets.h
|
||||
hle/service/spl/csrng.cpp
|
||||
hle/service/spl/csrng.h
|
||||
hle/service/spl/module.cpp
|
||||
hle/service/spl/module.h
|
||||
hle/service/spl/spl.cpp
|
||||
hle/service/spl/spl.h
|
||||
hle/service/time/time.cpp
|
||||
hle/service/time/time.h
|
||||
hle/service/time/time_s.cpp
|
||||
hle/service/time/time_s.h
|
||||
hle/service/time/time_u.cpp
|
||||
hle/service/time/time_u.h
|
||||
hle/service/vi/vi.cpp
|
||||
hle/service/vi/vi.h
|
||||
hle/service/vi/vi_m.cpp
|
||||
hle/service/vi/vi_m.h
|
||||
hle/service/vi/vi_s.cpp
|
||||
hle/service/vi/vi_s.h
|
||||
hle/service/vi/vi_u.cpp
|
||||
hle/service/vi/vi_u.h
|
||||
hle/shared_page.cpp
|
||||
hle/shared_page.h
|
||||
hw/hw.cpp
|
||||
hw/hw.h
|
||||
hw/lcd.cpp
|
||||
hw/lcd.h
|
||||
loader/deconstructed_rom_directory.cpp
|
||||
loader/deconstructed_rom_directory.h
|
||||
loader/elf.cpp
|
||||
loader/elf.h
|
||||
loader/linker.cpp
|
||||
loader/linker.h
|
||||
loader/loader.cpp
|
||||
loader/loader.h
|
||||
loader/nro.cpp
|
||||
loader/nro.h
|
||||
loader/nso.cpp
|
||||
loader/nso.h
|
||||
memory.cpp
|
||||
memory.h
|
||||
memory_hook.h
|
||||
memory_setup.h
|
||||
perf_stats.cpp
|
||||
perf_stats.h
|
||||
settings.cpp
|
||||
settings.h
|
||||
telemetry_session.cpp
|
||||
telemetry_session.h
|
||||
tracer/citrace.h
|
||||
tracer/recorder.cpp
|
||||
tracer/recorder.h
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
arm/arm_interface.h
|
||||
arm/dynarmic/arm_dynarmic.h
|
||||
arm/unicorn/arm_unicorn.h
|
||||
core.h
|
||||
core_timing.h
|
||||
file_sys/archive_backend.h
|
||||
file_sys/directory_backend.h
|
||||
file_sys/disk_archive.h
|
||||
file_sys/errors.h
|
||||
file_sys/file_backend.h
|
||||
file_sys/ivfc_archive.h
|
||||
file_sys/path_parser.h
|
||||
file_sys/savedata_archive.h
|
||||
frontend/emu_window.h
|
||||
frontend/framebuffer_layout.h
|
||||
frontend/input.h
|
||||
gdbstub/gdbstub.h
|
||||
hle/config_mem.h
|
||||
hle/ipc.h
|
||||
hle/ipc_helpers.h
|
||||
hle/kernel/address_arbiter.h
|
||||
hle/kernel/client_port.h
|
||||
hle/kernel/client_session.h
|
||||
hle/kernel/condition_variable.h
|
||||
hle/kernel/domain.h
|
||||
hle/kernel/errors.h
|
||||
hle/kernel/event.h
|
||||
hle/kernel/handle_table.h
|
||||
hle/kernel/hle_ipc.h
|
||||
hle/kernel/kernel.h
|
||||
hle/kernel/memory.h
|
||||
hle/kernel/mutex.h
|
||||
hle/kernel/object_address_table.h
|
||||
hle/kernel/process.h
|
||||
hle/kernel/resource_limit.h
|
||||
hle/kernel/server_port.h
|
||||
hle/kernel/server_session.h
|
||||
hle/kernel/session.h
|
||||
hle/kernel/shared_memory.h
|
||||
hle/kernel/sync_object.h
|
||||
hle/kernel/svc.h
|
||||
hle/kernel/svc_wrap.h
|
||||
hle/kernel/thread.h
|
||||
hle/kernel/timer.h
|
||||
hle/kernel/vm_manager.h
|
||||
hle/kernel/wait_object.h
|
||||
hle/lock.h
|
||||
hle/result.h
|
||||
hle/romfs.h
|
||||
hle/service/am/am.h
|
||||
hle/service/am/applet_oe.h
|
||||
hle/service/aoc/aoc_u.h
|
||||
hle/service/apm/apm.h
|
||||
hle/service/hid/hid.h
|
||||
hle/service/lm/lm.h
|
||||
hle/service/nvdrv/devices/nvdevice.h
|
||||
hle/service/nvdrv/devices/nvdisp_disp0.h
|
||||
hle/service/nvdrv/devices/nvhost_as_gpu.h
|
||||
hle/service/nvdrv/devices/nvmap.h
|
||||
hle/service/nvdrv/nvdrv.h
|
||||
hle/service/nvdrv/nvdrv_a.h
|
||||
hle/service/pctl/pctl.h
|
||||
hle/service/pctl/pctl_a.h
|
||||
hle/service/service.h
|
||||
hle/service/sm/controller.h
|
||||
hle/service/sm/sm.h
|
||||
hle/service/vi/vi.h
|
||||
hle/service/vi/vi_m.h
|
||||
hle/shared_page.h
|
||||
hw/hw.h
|
||||
hw/lcd.h
|
||||
loader/elf.h
|
||||
loader/linker.h
|
||||
loader/loader.h
|
||||
loader/nro.h
|
||||
loader/nso.h
|
||||
tracer/recorder.h
|
||||
tracer/citrace.h
|
||||
memory.h
|
||||
memory_setup.h
|
||||
mmio.h
|
||||
perf_stats.h
|
||||
settings.h
|
||||
telemetry_session.h
|
||||
)
|
||||
create_target_directory_groups(core)
|
||||
|
||||
create_directory_groups(${SRCS} ${HEADERS})
|
||||
add_library(core STATIC ${SRCS} ${HEADERS})
|
||||
target_link_libraries(core PUBLIC common PRIVATE dynarmic video_core)
|
||||
target_link_libraries(core PUBLIC common PRIVATE video_core)
|
||||
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static unicorn)
|
||||
|
||||
if (ARCHITECTURE_x86_64)
|
||||
target_sources(core PRIVATE
|
||||
arm/dynarmic/arm_dynarmic.cpp
|
||||
arm/dynarmic/arm_dynarmic.h
|
||||
)
|
||||
target_link_libraries(core PRIVATE dynarmic)
|
||||
endif()
|
||||
|
||||
@@ -25,21 +25,18 @@ public:
|
||||
VAddr tls_address;
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs the CPU for the given number of instructions
|
||||
* @param num_instructions Number of instructions to run
|
||||
*/
|
||||
void Run(int num_instructions) {
|
||||
ExecuteInstructions(num_instructions);
|
||||
this->num_instructions += num_instructions;
|
||||
}
|
||||
/// Runs the CPU until an event happens
|
||||
virtual void Run() = 0;
|
||||
|
||||
/// Step CPU by one instruction
|
||||
void Step() {
|
||||
Run(1);
|
||||
}
|
||||
virtual void Step() = 0;
|
||||
|
||||
virtual void MapBackingMemory(VAddr address, size_t size, u8* memory, Kernel::VMAPermission perms) {}
|
||||
/// Maps a backing memory region for the CPU
|
||||
virtual void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) = 0;
|
||||
|
||||
/// Unmaps a region of memory that was previously mapped using MapBackingMemory
|
||||
virtual void UnmapMemory(VAddr address, size_t size) = 0;
|
||||
|
||||
/// Clear all instruction cache
|
||||
virtual void ClearInstructionCache() = 0;
|
||||
@@ -121,19 +118,4 @@ public:
|
||||
|
||||
/// Prepare core for thread reschedule (if needed to correctly handle state)
|
||||
virtual void PrepareReschedule() = 0;
|
||||
|
||||
/// Getter for num_instructions
|
||||
u64 GetNumInstructions() const {
|
||||
return num_instructions;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Executes the given number of instructions
|
||||
* @param num_instructions Number of instructions to executes
|
||||
*/
|
||||
virtual void ExecuteInstructions(int num_instructions) = 0;
|
||||
|
||||
private:
|
||||
u64 num_instructions = 0; ///< Number of instructions executed
|
||||
};
|
||||
|
||||
@@ -6,11 +6,16 @@
|
||||
#include <memory>
|
||||
#include <dynarmic/A64/a64.h>
|
||||
#include <dynarmic/A64/config.h>
|
||||
#include "common/logging/log.h"
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/kernel/memory.h"
|
||||
#include "core/hle/kernel/svc.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
using Vector = Dynarmic::A64::Vector;
|
||||
|
||||
class ARM_Dynarmic_Callbacks : public Dynarmic::A64::UserCallbacks {
|
||||
public:
|
||||
explicit ARM_Dynarmic_Callbacks(ARM_Dynarmic& parent) : parent(parent) {}
|
||||
@@ -28,6 +33,9 @@ public:
|
||||
u64 MemoryRead64(u64 vaddr) override {
|
||||
return Memory::Read64(vaddr);
|
||||
}
|
||||
Vector MemoryRead128(u64 vaddr) override {
|
||||
return {Memory::Read64(vaddr), Memory::Read64(vaddr + 8)};
|
||||
}
|
||||
|
||||
void MemoryWrite8(u64 vaddr, u8 value) override {
|
||||
Memory::Write8(vaddr, value);
|
||||
@@ -41,23 +49,39 @@ public:
|
||||
void MemoryWrite64(u64 vaddr, u64 value) override {
|
||||
Memory::Write64(vaddr, value);
|
||||
}
|
||||
void MemoryWrite128(u64 vaddr, Vector value) override {
|
||||
Memory::Write64(vaddr, value[0]);
|
||||
Memory::Write64(vaddr + 8, value[1]);
|
||||
}
|
||||
|
||||
void InterpreterFallback(u64 pc, size_t num_instructions) override {
|
||||
LOG_INFO(Core_ARM, "Unicorn fallback @ 0x%" PRIx64 " for %zu instructions (instr = %08x)",
|
||||
pc, num_instructions, MemoryReadCode(pc));
|
||||
|
||||
ARM_Interface::ThreadContext ctx;
|
||||
parent.SaveContext(ctx);
|
||||
parent.inner_unicorn.LoadContext(ctx);
|
||||
parent.inner_unicorn.ExecuteInstructions(num_instructions);
|
||||
parent.inner_unicorn.ExecuteInstructions(static_cast<int>(num_instructions));
|
||||
parent.inner_unicorn.SaveContext(ctx);
|
||||
parent.LoadContext(ctx);
|
||||
num_interpreted_instructions += num_instructions;
|
||||
}
|
||||
|
||||
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception /*exception*/) override {
|
||||
ASSERT_MSG(false, "ExceptionRaised(%" PRIx64 ")", pc);
|
||||
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
|
||||
switch (exception) {
|
||||
case Dynarmic::A64::Exception::WaitForInterrupt:
|
||||
case Dynarmic::A64::Exception::WaitForEvent:
|
||||
case Dynarmic::A64::Exception::SendEvent:
|
||||
case Dynarmic::A64::Exception::SendEventLocal:
|
||||
case Dynarmic::A64::Exception::Yield:
|
||||
return;
|
||||
default:
|
||||
ASSERT_MSG(false, "ExceptionRaised(exception = %zu, pc = %" PRIx64 ")",
|
||||
static_cast<size_t>(exception), pc);
|
||||
}
|
||||
}
|
||||
|
||||
void CallSVC(u32 swi) override {
|
||||
printf("svc %x\n", swi);
|
||||
Kernel::CallSVC(swi);
|
||||
}
|
||||
|
||||
@@ -71,19 +95,49 @@ public:
|
||||
u64 GetTicksRemaining() override {
|
||||
return ticks_remaining;
|
||||
}
|
||||
u64 GetCNTPCT() override {
|
||||
return CoreTiming::GetTicks();
|
||||
}
|
||||
|
||||
ARM_Dynarmic& parent;
|
||||
size_t ticks_remaining = 0;
|
||||
size_t num_interpreted_instructions = 0;
|
||||
u64 tpidrr0_el0 = 0;
|
||||
u64 tpidrro_el0 = 0;
|
||||
u64 tpidr_el0 = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<Dynarmic::A64::Jit> MakeJit(const std::unique_ptr<ARM_Dynarmic_Callbacks>& cb) {
|
||||
const auto page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data();
|
||||
|
||||
Dynarmic::A64::UserConfig config;
|
||||
config.callbacks = cb.get();
|
||||
config.tpidrro_el0 = &cb->tpidrro_el0;
|
||||
config.tpidr_el0 = &cb->tpidr_el0;
|
||||
config.dczid_el0 = 4;
|
||||
config.ctr_el0 = 0x8444c004;
|
||||
config.page_table = reinterpret_cast<void**>(page_table);
|
||||
config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS;
|
||||
config.silently_mirror_page_table = false;
|
||||
|
||||
return std::make_unique<Dynarmic::A64::Jit>(config);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::Run() {
|
||||
ASSERT(Memory::GetCurrentPageTable() == current_page_table);
|
||||
|
||||
jit->Run();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::Step() {
|
||||
cb->InterpreterFallback(jit->GetPC(), 1);
|
||||
}
|
||||
|
||||
ARM_Dynarmic::ARM_Dynarmic()
|
||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)),
|
||||
jit(Dynarmic::A64::UserConfig{cb.get()}) {
|
||||
: cb(std::make_unique<ARM_Dynarmic_Callbacks>(*this)), jit(MakeJit(cb)) {
|
||||
ARM_Interface::ThreadContext ctx;
|
||||
inner_unicorn.SaveContext(ctx);
|
||||
LoadContext(ctx);
|
||||
PageTableChanged();
|
||||
}
|
||||
|
||||
ARM_Dynarmic::~ARM_Dynarmic() = default;
|
||||
@@ -93,28 +147,32 @@ void ARM_Dynarmic::MapBackingMemory(u64 address, size_t size, u8* memory,
|
||||
inner_unicorn.MapBackingMemory(address, size, memory, perms);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::UnmapMemory(u64 address, size_t size) {
|
||||
inner_unicorn.UnmapMemory(address, size);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetPC(u64 pc) {
|
||||
jit.SetPC(pc);
|
||||
jit->SetPC(pc);
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic::GetPC() const {
|
||||
return jit.GetPC();
|
||||
return jit->GetPC();
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic::GetReg(int index) const {
|
||||
return jit.GetRegister(index);
|
||||
return jit->GetRegister(index);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetReg(int index, u64 value) {
|
||||
jit.SetRegister(index, value);
|
||||
jit->SetRegister(index, value);
|
||||
}
|
||||
|
||||
u128 ARM_Dynarmic::GetExtReg(int index) const {
|
||||
return jit.GetVector(index);
|
||||
return jit->GetVector(index);
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetExtReg(int index, u128 value) {
|
||||
jit.SetVector(index, value);
|
||||
jit->SetVector(index, value);
|
||||
}
|
||||
|
||||
u32 ARM_Dynarmic::GetVFPReg(int /*index*/) const {
|
||||
@@ -127,58 +185,52 @@ void ARM_Dynarmic::SetVFPReg(int /*index*/, u32 /*value*/) {
|
||||
}
|
||||
|
||||
u32 ARM_Dynarmic::GetCPSR() const {
|
||||
return jit.GetPstate();
|
||||
return jit->GetPstate();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetCPSR(u32 cpsr) {
|
||||
jit.SetPstate(cpsr);
|
||||
jit->SetPstate(cpsr);
|
||||
}
|
||||
|
||||
u64 ARM_Dynarmic::GetTlsAddress() const {
|
||||
return cb->tpidrr0_el0;
|
||||
return cb->tpidrro_el0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SetTlsAddress(u64 address) {
|
||||
cb->tpidrr0_el0 = address;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::ExecuteInstructions(int num_instructions) {
|
||||
cb->ticks_remaining = num_instructions;
|
||||
jit.Run();
|
||||
CoreTiming::AddTicks(num_instructions - cb->num_interpreted_instructions);
|
||||
cb->num_interpreted_instructions = 0;
|
||||
cb->tpidrro_el0 = address;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
|
||||
ctx.cpu_registers = jit.GetRegisters();
|
||||
ctx.sp = jit.GetSP();
|
||||
ctx.pc = jit.GetPC();
|
||||
ctx.cpsr = jit.GetPstate();
|
||||
ctx.fpu_registers = jit.GetVectors();
|
||||
ctx.fpscr = jit.GetFpcr();
|
||||
ctx.tls_address = cb->tpidrr0_el0;
|
||||
ctx.cpu_registers = jit->GetRegisters();
|
||||
ctx.sp = jit->GetSP();
|
||||
ctx.pc = jit->GetPC();
|
||||
ctx.cpsr = jit->GetPstate();
|
||||
ctx.fpu_registers = jit->GetVectors();
|
||||
ctx.fpscr = jit->GetFpcr();
|
||||
ctx.tls_address = cb->tpidrro_el0;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
|
||||
jit.SetRegisters(ctx.cpu_registers);
|
||||
jit.SetSP(ctx.sp);
|
||||
jit.SetPC(ctx.pc);
|
||||
jit.SetPstate(ctx.cpsr);
|
||||
jit.SetVectors(ctx.fpu_registers);
|
||||
jit.SetFpcr(ctx.fpscr);
|
||||
cb->tpidrr0_el0 = ctx.tls_address;
|
||||
jit->SetRegisters(ctx.cpu_registers);
|
||||
jit->SetSP(ctx.sp);
|
||||
jit->SetPC(ctx.pc);
|
||||
jit->SetPstate(static_cast<u32>(ctx.cpsr));
|
||||
jit->SetVectors(ctx.fpu_registers);
|
||||
jit->SetFpcr(static_cast<u32>(ctx.fpscr));
|
||||
cb->tpidrro_el0 = ctx.tls_address;
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PrepareReschedule() {
|
||||
if (jit.IsExecuting()) {
|
||||
jit.HaltExecution();
|
||||
if (jit->IsExecuting()) {
|
||||
jit->HaltExecution();
|
||||
}
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::ClearInstructionCache() {
|
||||
jit.ClearCache();
|
||||
jit->ClearCache();
|
||||
}
|
||||
|
||||
void ARM_Dynarmic::PageTableChanged() {
|
||||
UNIMPLEMENTED();
|
||||
jit = MakeJit(cb);
|
||||
current_page_table = Memory::GetCurrentPageTable();
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public:
|
||||
|
||||
void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) override;
|
||||
|
||||
void UnmapMemory(u64 address, size_t size) override;
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
@@ -29,6 +29,8 @@ public:
|
||||
u32 GetVFPReg(int index) const override;
|
||||
void SetVFPReg(int index, u32 value) override;
|
||||
u32 GetCPSR() const override;
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void SetCPSR(u32 cpsr) override;
|
||||
VAddr GetTlsAddress() const override;
|
||||
void SetTlsAddress(VAddr address) override;
|
||||
@@ -37,7 +39,6 @@ public:
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
|
||||
void PrepareReschedule() override;
|
||||
void ExecuteInstructions(int num_instructions) override;
|
||||
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged() override;
|
||||
@@ -45,6 +46,8 @@ public:
|
||||
private:
|
||||
friend class ARM_Dynarmic_Callbacks;
|
||||
std::unique_ptr<ARM_Dynarmic_Callbacks> cb;
|
||||
Dynarmic::A64::Jit jit;
|
||||
std::unique_ptr<Dynarmic::A64::Jit> jit;
|
||||
ARM_Unicorn inner_unicorn;
|
||||
|
||||
Memory::PageTable* current_page_table = nullptr;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <unicorn/arm64.h>
|
||||
#include "common/assert.h"
|
||||
#include "common/microprofile.h"
|
||||
@@ -11,7 +12,7 @@
|
||||
#include "core/hle/kernel/svc.h"
|
||||
|
||||
// Load Unicorn DLL once on Windows using RAII
|
||||
#ifdef _WIN32
|
||||
#ifdef _MSC_VER
|
||||
#include <unicorn_dynload.h>
|
||||
struct LoadDll {
|
||||
private:
|
||||
@@ -52,7 +53,8 @@ static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int si
|
||||
void* user_data) {
|
||||
ARM_Interface::ThreadContext ctx{};
|
||||
Core::CPU().SaveContext(ctx);
|
||||
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%llx", addr);
|
||||
ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x%lx, pc=0x%lx, lr=0x%lx", addr,
|
||||
ctx.pc, ctx.cpu_registers[30]);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -76,6 +78,10 @@ void ARM_Unicorn::MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
CHECKED(uc_mem_map_ptr(uc, address, size, static_cast<u32>(perms), memory));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::UnmapMemory(VAddr address, size_t size) {
|
||||
CHECKED(uc_mem_unmap(uc, address, size));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::SetPC(u64 pc) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
|
||||
}
|
||||
@@ -148,6 +154,14 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
|
||||
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Run() {
|
||||
ExecuteInstructions(std::max(CoreTiming::GetDowncount(), 0));
|
||||
}
|
||||
|
||||
void ARM_Unicorn::Step() {
|
||||
ExecuteInstructions(1);
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64));
|
||||
|
||||
void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
|
||||
|
||||
@@ -14,6 +14,7 @@ public:
|
||||
~ARM_Unicorn();
|
||||
void MapBackingMemory(VAddr address, size_t size, u8* memory,
|
||||
Kernel::VMAPermission perms) override;
|
||||
void UnmapMemory(VAddr address, size_t size) override;
|
||||
void SetPC(u64 pc) override;
|
||||
u64 GetPC() const override;
|
||||
u64 GetReg(int index) const override;
|
||||
@@ -29,7 +30,9 @@ public:
|
||||
void SaveContext(ThreadContext& ctx) override;
|
||||
void LoadContext(const ThreadContext& ctx) override;
|
||||
void PrepareReschedule() override;
|
||||
void ExecuteInstructions(int num_instructions) override;
|
||||
void ExecuteInstructions(int num_instructions);
|
||||
void Run() override;
|
||||
void Step() override;
|
||||
void ClearInstructionCache() override;
|
||||
void PageTableChanged() override{};
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "common/logging/log.h"
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
#include "core/arm/dynarmic/arm_dynarmic.h"
|
||||
#endif
|
||||
#include "core/arm/unicorn/arm_unicorn.h"
|
||||
#include "core/core.h"
|
||||
#include "core/core_timing.h"
|
||||
@@ -24,7 +26,7 @@ namespace Core {
|
||||
|
||||
/*static*/ System System::s_instance;
|
||||
|
||||
System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
status = ResultStatus::Success;
|
||||
if (!cpu_core) {
|
||||
return ResultStatus::ErrorNotInitialized;
|
||||
@@ -38,7 +40,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
if (GDBStub::GetCpuHaltFlag()) {
|
||||
if (GDBStub::GetCpuStepFlag()) {
|
||||
GDBStub::SetCpuStepFlag(false);
|
||||
tight_loop = 1;
|
||||
tight_loop = false;
|
||||
} else {
|
||||
return ResultStatus::Success;
|
||||
}
|
||||
@@ -54,7 +56,11 @@ System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
PrepareReschedule();
|
||||
} else {
|
||||
CoreTiming::Advance();
|
||||
cpu_core->Run(tight_loop);
|
||||
if (tight_loop) {
|
||||
cpu_core->Run();
|
||||
} else {
|
||||
cpu_core->Step();
|
||||
}
|
||||
}
|
||||
|
||||
HW::Update();
|
||||
@@ -64,7 +70,7 @@ System::ResultStatus System::RunLoop(int tight_loop) {
|
||||
}
|
||||
|
||||
System::ResultStatus System::SingleStep() {
|
||||
return RunLoop(1);
|
||||
return RunLoop(false);
|
||||
}
|
||||
|
||||
System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) {
|
||||
@@ -80,7 +86,6 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
|
||||
if (system_mode.second != Loader::ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to determine system mode (Error %i)!",
|
||||
static_cast<int>(system_mode.second));
|
||||
System::Shutdown();
|
||||
|
||||
switch (system_mode.second) {
|
||||
case Loader::ResultStatus::ErrorEncrypted:
|
||||
@@ -94,14 +99,15 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file
|
||||
|
||||
ResultStatus init_result{Init(emu_window, system_mode.first.get())};
|
||||
if (init_result != ResultStatus::Success) {
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result);
|
||||
LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!",
|
||||
static_cast<int>(init_result));
|
||||
System::Shutdown();
|
||||
return init_result;
|
||||
}
|
||||
|
||||
const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)};
|
||||
const Loader::ResultStatus load_result{app_loader->Load(current_process)};
|
||||
if (Loader::ResultStatus::Success != load_result) {
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result);
|
||||
LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", static_cast<int>(load_result));
|
||||
System::Shutdown();
|
||||
|
||||
switch (load_result) {
|
||||
@@ -132,27 +138,38 @@ void System::Reschedule() {
|
||||
}
|
||||
|
||||
reschedule_pending = false;
|
||||
Kernel::Reschedule();
|
||||
Core::System::GetInstance().Scheduler().Reschedule();
|
||||
}
|
||||
|
||||
System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) {
|
||||
LOG_DEBUG(HW_Memory, "initialized OK");
|
||||
|
||||
CoreTiming::Init();
|
||||
|
||||
current_process = Kernel::Process::Create("main");
|
||||
|
||||
switch (Settings::values.cpu_core) {
|
||||
case Settings::CpuCore::Unicorn:
|
||||
cpu_core = std::make_unique<ARM_Unicorn>();
|
||||
cpu_core = std::make_shared<ARM_Unicorn>();
|
||||
break;
|
||||
case Settings::CpuCore::Dynarmic:
|
||||
default:
|
||||
cpu_core = std::make_unique<ARM_Dynarmic>();
|
||||
#ifdef ARCHITECTURE_x86_64
|
||||
cpu_core = std::make_shared<ARM_Dynarmic>();
|
||||
#else
|
||||
cpu_core = std::make_shared<ARM_Unicorn>();
|
||||
LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
gpu_core = std::make_unique<Tegra::GPU>();
|
||||
|
||||
telemetry_session = std::make_unique<Core::TelemetrySession>();
|
||||
|
||||
CoreTiming::Init();
|
||||
HW::Init();
|
||||
Kernel::Init(system_mode);
|
||||
scheduler = std::make_unique<Kernel::Scheduler>(cpu_core.get());
|
||||
Service::Init();
|
||||
GDBStub::Init();
|
||||
|
||||
@@ -180,15 +197,18 @@ void System::Shutdown() {
|
||||
perf_results.frametime * 1000.0);
|
||||
|
||||
// Shutdown emulation session
|
||||
GDBStub::Shutdown();
|
||||
VideoCore::Shutdown();
|
||||
GDBStub::Shutdown();
|
||||
Service::Shutdown();
|
||||
scheduler = nullptr;
|
||||
Kernel::Shutdown();
|
||||
HW::Shutdown();
|
||||
CoreTiming::Shutdown();
|
||||
cpu_core = nullptr;
|
||||
app_loader = nullptr;
|
||||
telemetry_session = nullptr;
|
||||
gpu_core = nullptr;
|
||||
cpu_core = nullptr;
|
||||
CoreTiming::Shutdown();
|
||||
|
||||
app_loader = nullptr;
|
||||
|
||||
LOG_DEBUG(Core, "Shutdown OK");
|
||||
}
|
||||
|
||||
@@ -7,10 +7,13 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/scheduler.h"
|
||||
#include "core/loader/loader.h"
|
||||
#include "core/memory.h"
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "video_core/gpu.h"
|
||||
|
||||
class EmuWindow;
|
||||
class ARM_Interface;
|
||||
@@ -50,14 +53,14 @@ public:
|
||||
* is not required to do a full dispatch with each instruction. NOTE: the number of instructions
|
||||
* requested is not guaranteed to run, as this will be interrupted preemptively if a hardware
|
||||
* update is requested (e.g. on a thread switch).
|
||||
* @param tight_loop Number of instructions to execute.
|
||||
* @return Result status, indicating whethor or not the operation succeeded.
|
||||
* @param tight_loop If false, the CPU single-steps.
|
||||
* @return Result status, indicating whether or not the operation succeeded.
|
||||
*/
|
||||
ResultStatus RunLoop(int tight_loop = 100000);
|
||||
ResultStatus RunLoop(bool tight_loop = true);
|
||||
|
||||
/**
|
||||
* Step the CPU one instruction
|
||||
* @return Result status, indicating whethor or not the operation succeeded.
|
||||
* @return Result status, indicating whether or not the operation succeeded.
|
||||
*/
|
||||
ResultStatus SingleStep();
|
||||
|
||||
@@ -102,6 +105,18 @@ public:
|
||||
return *cpu_core;
|
||||
}
|
||||
|
||||
Tegra::GPU& GPU() {
|
||||
return *gpu_core;
|
||||
}
|
||||
|
||||
Kernel::Scheduler& Scheduler() {
|
||||
return *scheduler;
|
||||
}
|
||||
|
||||
Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
|
||||
return current_process;
|
||||
}
|
||||
|
||||
PerfStats perf_stats;
|
||||
FrameLimiter frame_limiter;
|
||||
|
||||
@@ -135,8 +150,11 @@ private:
|
||||
/// AppLoader used to load the current executing application
|
||||
std::unique_ptr<Loader::AppLoader> app_loader;
|
||||
|
||||
///< ARM11 CPU core
|
||||
std::unique_ptr<ARM_Interface> cpu_core;
|
||||
std::shared_ptr<ARM_Interface> cpu_core;
|
||||
std::unique_ptr<Kernel::Scheduler> scheduler;
|
||||
std::unique_ptr<Tegra::GPU> gpu_core;
|
||||
|
||||
Kernel::SharedPtr<Kernel::Process> current_process;
|
||||
|
||||
/// When true, signals that a reschedule should happen
|
||||
bool reschedule_pending{};
|
||||
@@ -158,4 +176,8 @@ inline TelemetrySession& Telemetry() {
|
||||
return System::GetInstance().TelemetrySession();
|
||||
}
|
||||
|
||||
inline Kernel::SharedPtr<Kernel::Process>& CurrentProcess() {
|
||||
return System::GetInstance().CurrentProcess();
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -122,7 +122,7 @@ u64 GetTicks() {
|
||||
}
|
||||
|
||||
void AddTicks(u64 ticks) {
|
||||
downcount -= ticks;
|
||||
downcount -= static_cast<int>(ticks);
|
||||
}
|
||||
|
||||
u64 GetIdleTicks() {
|
||||
@@ -208,7 +208,7 @@ void Advance() {
|
||||
Event evt = std::move(event_queue.front());
|
||||
std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<Event>());
|
||||
event_queue.pop_back();
|
||||
evt.type->callback(evt.userdata, global_timer - evt.time);
|
||||
evt.type->callback(evt.userdata, static_cast<int>(global_timer - evt.time));
|
||||
}
|
||||
|
||||
is_global_timer_sane = false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/disk_archive.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const {
|
||||
if (!mode.read_flag)
|
||||
return ERROR_INVALID_OPEN_FLAGS;
|
||||
|
||||
file->Seek(offset, SEEK_SET);
|
||||
return MakeResult<size_t>(file->ReadBytes(buffer, length));
|
||||
}
|
||||
|
||||
ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush,
|
||||
const u8* buffer) const {
|
||||
if (!mode.write_flag)
|
||||
return ERROR_INVALID_OPEN_FLAGS;
|
||||
|
||||
file->Seek(offset, SEEK_SET);
|
||||
size_t written = file->WriteBytes(buffer, length);
|
||||
if (flush)
|
||||
file->Flush();
|
||||
return MakeResult<size_t>(written);
|
||||
}
|
||||
|
||||
u64 DiskFile::GetSize() const {
|
||||
return file->GetSize();
|
||||
}
|
||||
|
||||
bool DiskFile::SetSize(const u64 size) const {
|
||||
file->Resize(size);
|
||||
file->Flush();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskFile::Close() const {
|
||||
return file->Close();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DiskDirectory::DiskDirectory(const std::string& path) : directory() {
|
||||
unsigned size = FileUtil::ScanDirectoryTree(path, directory);
|
||||
directory.size = size;
|
||||
directory.isDirectory = true;
|
||||
children_iterator = directory.children.begin();
|
||||
}
|
||||
|
||||
u32 DiskDirectory::Read(const u32 count, Entry* entries) {
|
||||
u32 entries_read = 0;
|
||||
|
||||
while (entries_read < count && children_iterator != directory.children.cend()) {
|
||||
const FileUtil::FSTEntry& file = *children_iterator;
|
||||
const std::string& filename = file.virtualName;
|
||||
Entry& entry = entries[entries_read];
|
||||
|
||||
LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size,
|
||||
file.isDirectory);
|
||||
|
||||
// TODO(Link Mauve): use a proper conversion to UTF-16.
|
||||
for (size_t j = 0; j < FILENAME_LENGTH; ++j) {
|
||||
entry.filename[j] = filename[j];
|
||||
if (!filename[j])
|
||||
break;
|
||||
}
|
||||
|
||||
FileUtil::SplitFilename83(filename, entry.short_name, entry.extension);
|
||||
|
||||
entry.is_directory = file.isDirectory;
|
||||
entry.is_hidden = (filename[0] == '.');
|
||||
entry.is_read_only = 0;
|
||||
entry.file_size = file.size;
|
||||
|
||||
// We emulate a SD card where the archive bit has never been cleared, as it would be on
|
||||
// most user SD cards.
|
||||
// Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a
|
||||
// file bit.
|
||||
entry.is_archive = !file.isDirectory;
|
||||
|
||||
++entries_read;
|
||||
++children_iterator;
|
||||
}
|
||||
return entries_read;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/directory_backend.h"
|
||||
#include "core/file_sys/file_backend.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class DiskFile : public FileBackend {
|
||||
public:
|
||||
DiskFile(FileUtil::IOFile&& file_, const Mode& mode_)
|
||||
: file(new FileUtil::IOFile(std::move(file_))) {
|
||||
mode.hex = mode_.hex;
|
||||
}
|
||||
|
||||
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
|
||||
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
|
||||
u64 GetSize() const override;
|
||||
bool SetSize(u64 size) const override;
|
||||
bool Close() const override;
|
||||
|
||||
void Flush() const override {
|
||||
file->Flush();
|
||||
}
|
||||
|
||||
protected:
|
||||
Mode mode;
|
||||
std::unique_ptr<FileUtil::IOFile> file;
|
||||
};
|
||||
|
||||
class DiskDirectory : public DirectoryBackend {
|
||||
public:
|
||||
DiskDirectory(const std::string& path);
|
||||
|
||||
~DiskDirectory() override {
|
||||
Close();
|
||||
}
|
||||
|
||||
u32 Read(const u32 count, Entry* entries) override;
|
||||
|
||||
bool Close() const override {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
u32 total_entries_in_directory;
|
||||
FileUtil::FSTEntry directory;
|
||||
|
||||
// We need to remember the last entry we returned, so a subsequent call to Read will continue
|
||||
// from the next one. This iterator will always point to the next unread entry.
|
||||
std::vector<FileUtil::FSTEntry>::iterator children_iterator;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
146
src/core/file_sys/disk_filesystem.cpp
Normal file
146
src/core/file_sys/disk_filesystem.cpp
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/disk_filesystem.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
std::string Disk_FileSystem::GetName() const {
|
||||
return "Disk";
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path,
|
||||
Mode mode) const {
|
||||
ASSERT_MSG(mode == Mode::Read || mode == Mode::Write, "Other file modes are not supported");
|
||||
|
||||
std::string full_path = base_directory + path;
|
||||
auto file = std::make_shared<FileUtil::IOFile>(full_path, mode == Mode::Read ? "rb" : "wb");
|
||||
|
||||
if (!file->IsOpen()) {
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
return MakeResult<std::unique_ptr<StorageBackend>>(
|
||||
std::make_unique<Disk_Storage>(std::move(file)));
|
||||
}
|
||||
|
||||
ResultCode Disk_FileSystem::DeleteFile(const Path& path) const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
// TODO(bunnei): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode Disk_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
|
||||
std::string full_path = base_directory + path;
|
||||
if (size == 0) {
|
||||
FileUtil::CreateEmptyFile(full_path);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
FileUtil::IOFile file(full_path, "wb");
|
||||
// Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
|
||||
// We do this by seeking to the right size, then writing a single null byte.
|
||||
if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_FS, "Too large file");
|
||||
// TODO(Subv): Find out the correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode Disk_FileSystem::CreateDirectory(const Path& path) const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory(
|
||||
const Path& path) const {
|
||||
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<Disk_Directory>());
|
||||
}
|
||||
|
||||
u64 Disk_FileSystem::GetFreeSpaceSize() const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& path) const {
|
||||
std::string full_path = base_directory + path;
|
||||
if (!FileUtil::Exists(full_path)) {
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
}
|
||||
|
||||
// TODO(Subv): Find out the EntryType values
|
||||
UNIMPLEMENTED_MSG("Unimplemented GetEntryType");
|
||||
}
|
||||
|
||||
ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
|
||||
LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
|
||||
file->Seek(offset, SEEK_SET);
|
||||
return MakeResult<size_t>(file->ReadBytes(buffer, length));
|
||||
}
|
||||
|
||||
ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush,
|
||||
const u8* buffer) const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
file->Seek(offset, SEEK_SET);
|
||||
size_t written = file->WriteBytes(buffer, length);
|
||||
if (flush) {
|
||||
file->Flush();
|
||||
}
|
||||
return MakeResult<size_t>(written);
|
||||
}
|
||||
|
||||
u64 Disk_Storage::GetSize() const {
|
||||
return file->GetSize();
|
||||
}
|
||||
|
||||
bool Disk_Storage::SetSize(const u64 size) const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 Disk_Directory::Read(const u32 count, Entry* entries) {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Disk_Directory::Close() const {
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called");
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
66
src/core/file_sys/disk_filesystem.h
Normal file
66
src/core/file_sys/disk_filesystem.h
Normal file
@@ -0,0 +1,66 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/directory.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/file_sys/storage.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class Disk_FileSystem : public FileSystemBackend {
|
||||
public:
|
||||
explicit Disk_FileSystem(std::string base_directory)
|
||||
: base_directory(std::move(base_directory)) {}
|
||||
|
||||
std::string GetName() const override;
|
||||
|
||||
ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
|
||||
Mode mode) const override;
|
||||
ResultCode DeleteFile(const Path& path) const override;
|
||||
ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultCode DeleteDirectory(const Path& path) const override;
|
||||
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
|
||||
ResultCode CreateFile(const std::string& path, u64 size) const override;
|
||||
ResultCode CreateDirectory(const Path& path) const override;
|
||||
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
||||
u64 GetFreeSpaceSize() const override;
|
||||
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
|
||||
|
||||
protected:
|
||||
std::string base_directory;
|
||||
};
|
||||
|
||||
class Disk_Storage : public StorageBackend {
|
||||
public:
|
||||
Disk_Storage(std::shared_ptr<FileUtil::IOFile> file) : file(std::move(file)) {}
|
||||
|
||||
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
|
||||
ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override;
|
||||
u64 GetSize() const override;
|
||||
bool SetSize(u64 size) const override;
|
||||
bool Close() const override {
|
||||
return false;
|
||||
}
|
||||
void Flush() const override {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<FileUtil::IOFile> file;
|
||||
};
|
||||
|
||||
class Disk_Directory : public DirectoryBackend {
|
||||
public:
|
||||
u32 Read(const u32 count, Entry* entries) override;
|
||||
bool Close() const override;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -10,36 +10,17 @@ namespace FileSys {
|
||||
|
||||
namespace ErrCodes {
|
||||
enum {
|
||||
RomFSNotFound = 100,
|
||||
ArchiveNotMounted = 101,
|
||||
FileNotFound = 112,
|
||||
PathNotFound = 113,
|
||||
GameCardNotInserted = 141,
|
||||
NotFound = 120,
|
||||
FileAlreadyExists = 180,
|
||||
DirectoryAlreadyExists = 185,
|
||||
AlreadyExists = 190,
|
||||
InvalidOpenFlags = 230,
|
||||
DirectoryNotEmpty = 240,
|
||||
NotAFile = 250,
|
||||
NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive
|
||||
ExeFSSectionNotFound = 567,
|
||||
CommandNotAllowed = 630,
|
||||
InvalidReadFlag = 700,
|
||||
InvalidPath = 702,
|
||||
WriteBeyondEnd = 705,
|
||||
UnsupportedOpenFlags = 760,
|
||||
IncorrectExeFSReadSize = 761,
|
||||
UnexpectedFileOrDirectory = 770,
|
||||
NotFound = 1,
|
||||
};
|
||||
}
|
||||
|
||||
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
|
||||
|
||||
// TODO(bunnei): Replace these with correct errors for Switch OS
|
||||
constexpr ResultCode ERROR_INVALID_PATH(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_FILE_NOT_FOUND(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_PATH_NOT_FOUND(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ResultCode(-1));
|
||||
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ResultCode(-1));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <sstream>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace FileSys {
|
||||
@@ -119,4 +119,4 @@ std::vector<u8> Path::AsBinary() const {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace FileSys
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class FileBackend;
|
||||
class StorageBackend;
|
||||
class DirectoryBackend;
|
||||
|
||||
// Path string type
|
||||
@@ -27,11 +27,14 @@ enum LowPathType : u32 {
|
||||
Wchar = 4,
|
||||
};
|
||||
|
||||
union Mode {
|
||||
u32 hex;
|
||||
BitField<0, 1, u32> read_flag;
|
||||
BitField<1, 1, u32> write_flag;
|
||||
BitField<2, 1, u32> create_flag;
|
||||
enum EntryType : u32 {
|
||||
Directory = 0,
|
||||
File = 1,
|
||||
};
|
||||
|
||||
enum class Mode : u32 {
|
||||
Read = 1,
|
||||
Write = 2,
|
||||
};
|
||||
|
||||
class Path {
|
||||
@@ -71,9 +74,9 @@ struct ArchiveFormatInfo {
|
||||
};
|
||||
static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD");
|
||||
|
||||
class ArchiveBackend : NonCopyable {
|
||||
class FileSystemBackend : NonCopyable {
|
||||
public:
|
||||
virtual ~ArchiveBackend() {}
|
||||
virtual ~FileSystemBackend() {}
|
||||
|
||||
/**
|
||||
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
|
||||
@@ -81,13 +84,12 @@ public:
|
||||
virtual std::string GetName() const = 0;
|
||||
|
||||
/**
|
||||
* Open a file specified by its path, using the specified mode
|
||||
* @param path Path relative to the archive
|
||||
* @param mode Mode to open the file with
|
||||
* @return Opened file, or error code
|
||||
* Create a file specified by its path
|
||||
* @param path Path relative to the Archive
|
||||
* @param size The size of the new file, filled with zeroes
|
||||
* @return Result of the operation
|
||||
*/
|
||||
virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
|
||||
const Mode& mode) const = 0;
|
||||
virtual ResultCode CreateFile(const std::string& path, u64 size) const = 0;
|
||||
|
||||
/**
|
||||
* Delete a file specified by its path
|
||||
@@ -97,12 +99,11 @@ public:
|
||||
virtual ResultCode DeleteFile(const Path& path) const = 0;
|
||||
|
||||
/**
|
||||
* Rename a File specified by its path
|
||||
* @param src_path Source path relative to the archive
|
||||
* @param dest_path Destination path relative to the archive
|
||||
* Create a directory specified by its path
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0;
|
||||
virtual ResultCode CreateDirectory(const Path& path) const = 0;
|
||||
|
||||
/**
|
||||
* Delete a directory specified by its path
|
||||
@@ -119,19 +120,12 @@ public:
|
||||
virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0;
|
||||
|
||||
/**
|
||||
* Create a file specified by its path
|
||||
* @param path Path relative to the Archive
|
||||
* @param size The size of the new file, filled with zeroes
|
||||
* Rename a File specified by its path
|
||||
* @param src_path Source path relative to the archive
|
||||
* @param dest_path Destination path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
virtual ResultCode CreateFile(const Path& path, u64 size) const = 0;
|
||||
|
||||
/**
|
||||
* Create a directory specified by its path
|
||||
* @param path Path relative to the archive
|
||||
* @return Result of the operation
|
||||
*/
|
||||
virtual ResultCode CreateDirectory(const Path& path) const = 0;
|
||||
virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0;
|
||||
|
||||
/**
|
||||
* Rename a Directory specified by its path
|
||||
@@ -141,6 +135,15 @@ public:
|
||||
*/
|
||||
virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0;
|
||||
|
||||
/**
|
||||
* Open a file specified by its path, using the specified mode
|
||||
* @param path Path relative to the archive
|
||||
* @param mode Mode to open the file with
|
||||
* @return Opened file, or error code
|
||||
*/
|
||||
virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
|
||||
Mode mode) const = 0;
|
||||
|
||||
/**
|
||||
* Open a directory specified by its path
|
||||
* @param path Path relative to the archive
|
||||
@@ -152,12 +155,18 @@ public:
|
||||
* Get the free space
|
||||
* @return The number of free bytes in the archive
|
||||
*/
|
||||
virtual u64 GetFreeBytes() const = 0;
|
||||
virtual u64 GetFreeSpaceSize() const = 0;
|
||||
|
||||
/**
|
||||
* Get the type of the specified path
|
||||
* @return The type of the specified path or error code
|
||||
*/
|
||||
virtual ResultVal<EntryType> GetEntryType(const std::string& path) const = 0;
|
||||
};
|
||||
|
||||
class ArchiveFactory : NonCopyable {
|
||||
class FileSystemFactory : NonCopyable {
|
||||
public:
|
||||
virtual ~ArchiveFactory() {}
|
||||
virtual ~FileSystemFactory() {}
|
||||
|
||||
/**
|
||||
* Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.)
|
||||
@@ -169,15 +178,14 @@ public:
|
||||
* @param path Path to the archive
|
||||
* @return An ArchiveBackend corresponding operating specified archive path.
|
||||
*/
|
||||
virtual ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) = 0;
|
||||
virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0;
|
||||
|
||||
/**
|
||||
* Deletes the archive contents and then re-creates the base folder
|
||||
* @param path Path to the archive
|
||||
* @param format_info Format information for the new archive
|
||||
* @return ResultCode of the operation, 0 on success
|
||||
*/
|
||||
virtual ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) = 0;
|
||||
virtual ResultCode Format(const Path& path) = 0;
|
||||
|
||||
/**
|
||||
* Retrieves the format info about the archive with the specified path
|
||||
@@ -1,110 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/ivfc_archive.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
std::string IVFCArchive::GetName() const {
|
||||
return "IVFC";
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path,
|
||||
const Mode& mode) const {
|
||||
return MakeResult<std::unique_ptr<FileBackend>>(
|
||||
std::make_unique<IVFCFile>(romfs_file, data_offset, data_size));
|
||||
}
|
||||
|
||||
ResultCode IVFCArchive::DeleteFile(const Path& path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(bunnei): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode IVFCArchive::DeleteDirectory(const Path& path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(bunnei): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode IVFCArchive::CreateDirectory(const Path& path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const {
|
||||
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>());
|
||||
}
|
||||
|
||||
u64 IVFCArchive::GetFreeBytes() const {
|
||||
LOG_WARNING(Service_FS, "Attempted to get the free space in an IVFC archive");
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const {
|
||||
LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
|
||||
romfs_file->Seek(data_offset + offset, SEEK_SET);
|
||||
size_t read_length = (size_t)std::min((u64)length, data_size - offset);
|
||||
|
||||
return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length));
|
||||
}
|
||||
|
||||
ResultVal<size_t> IVFCFile::Write(const u64 offset, const size_t length, const bool flush,
|
||||
const u8* buffer) const {
|
||||
LOG_ERROR(Service_FS, "Attempted to write to IVFC file");
|
||||
// TODO(Subv): Find error code
|
||||
return MakeResult<size_t>(0);
|
||||
}
|
||||
|
||||
u64 IVFCFile::GetSize() const {
|
||||
return data_size;
|
||||
}
|
||||
|
||||
bool IVFCFile::SetSize(const u64 size) const {
|
||||
LOG_ERROR(Service_FS, "Attempted to set the size of an IVFC file");
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
|
||||
114
src/core/file_sys/program_metadata.cpp
Normal file
114
src/core/file_sys/program_metadata.cpp
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/program_metadata.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) {
|
||||
FileUtil::IOFile file(file_path, "rb");
|
||||
if (!file.IsOpen())
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
std::vector<u8> file_data(file.GetSize());
|
||||
|
||||
if (!file.ReadBytes(file_data.data(), file_data.size()))
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
Loader::ResultStatus result = Load(file_data);
|
||||
if (result != Loader::ResultStatus::Success)
|
||||
LOG_ERROR(Service_FS, "Failed to load NPDM from file %s!", file_path.c_str());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Loader::ResultStatus ProgramMetadata::Load(const std::vector<u8> file_data, size_t offset) {
|
||||
size_t total_size = static_cast<size_t>(file_data.size() - offset);
|
||||
if (total_size < sizeof(Header))
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
size_t header_offset = offset;
|
||||
memcpy(&npdm_header, &file_data[offset], sizeof(Header));
|
||||
|
||||
size_t aci_offset = header_offset + npdm_header.aci_offset;
|
||||
size_t acid_offset = header_offset + npdm_header.acid_offset;
|
||||
memcpy(&aci_header, &file_data[aci_offset], sizeof(AciHeader));
|
||||
memcpy(&acid_header, &file_data[acid_offset], sizeof(AcidHeader));
|
||||
|
||||
size_t fac_offset = acid_offset + acid_header.fac_offset;
|
||||
size_t fah_offset = aci_offset + aci_header.fah_offset;
|
||||
memcpy(&acid_file_access, &file_data[fac_offset], sizeof(FileAccessControl));
|
||||
memcpy(&aci_file_access, &file_data[fah_offset], sizeof(FileAccessHeader));
|
||||
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
bool ProgramMetadata::Is64BitProgram() const {
|
||||
return npdm_header.has_64_bit_instructions;
|
||||
}
|
||||
|
||||
ProgramAddressSpaceType ProgramMetadata::GetAddressSpaceType() const {
|
||||
return npdm_header.address_space_type;
|
||||
}
|
||||
|
||||
u8 ProgramMetadata::GetMainThreadPriority() const {
|
||||
return npdm_header.main_thread_priority;
|
||||
}
|
||||
|
||||
u8 ProgramMetadata::GetMainThreadCore() const {
|
||||
return npdm_header.main_thread_cpu;
|
||||
}
|
||||
|
||||
u32 ProgramMetadata::GetMainThreadStackSize() const {
|
||||
return npdm_header.main_stack_size;
|
||||
}
|
||||
|
||||
u64 ProgramMetadata::GetTitleID() const {
|
||||
return aci_header.title_id;
|
||||
}
|
||||
|
||||
u64 ProgramMetadata::GetFilesystemPermissions() const {
|
||||
return aci_file_access.permissions;
|
||||
}
|
||||
|
||||
void ProgramMetadata::Print() const {
|
||||
LOG_DEBUG(Service_FS, "Magic: %.4s", npdm_header.magic.data());
|
||||
LOG_DEBUG(Service_FS, "Main thread priority: 0x%02x", npdm_header.main_thread_priority);
|
||||
LOG_DEBUG(Service_FS, "Main thread core: %u", npdm_header.main_thread_cpu);
|
||||
LOG_DEBUG(Service_FS, "Main thread stack size: 0x%x bytes", npdm_header.main_stack_size);
|
||||
LOG_DEBUG(Service_FS, "Process category: %u", npdm_header.process_category);
|
||||
LOG_DEBUG(Service_FS, "Flags: %02x", npdm_header.flags);
|
||||
LOG_DEBUG(Service_FS, " > 64-bit instructions: %s",
|
||||
npdm_header.has_64_bit_instructions ? "YES" : "NO");
|
||||
|
||||
auto address_space = "Unknown";
|
||||
switch (npdm_header.address_space_type) {
|
||||
case ProgramAddressSpaceType::Is64Bit:
|
||||
address_space = "64-bit";
|
||||
break;
|
||||
case ProgramAddressSpaceType::Is32Bit:
|
||||
address_space = "32-bit";
|
||||
break;
|
||||
}
|
||||
|
||||
LOG_DEBUG(Service_FS, " > Address space: %s\n", address_space);
|
||||
|
||||
// Begin ACID printing (potential perms, signed)
|
||||
LOG_DEBUG(Service_FS, "Magic: %.4s", acid_header.magic.data());
|
||||
LOG_DEBUG(Service_FS, "Flags: %02x", acid_header.flags);
|
||||
LOG_DEBUG(Service_FS, " > Is Retail: %s", acid_header.is_retail ? "YES" : "NO");
|
||||
LOG_DEBUG(Service_FS, "Title ID Min: %016" PRIX64, acid_header.title_id_min);
|
||||
LOG_DEBUG(Service_FS, "Title ID Max: %016" PRIX64, acid_header.title_id_max);
|
||||
LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", acid_file_access.permissions);
|
||||
|
||||
// Begin ACI0 printing (actual perms, unsigned)
|
||||
LOG_DEBUG(Service_FS, "Magic: %.4s", aci_header.magic.data());
|
||||
LOG_DEBUG(Service_FS, "Title ID: %016" PRIX64, aci_header.title_id);
|
||||
LOG_DEBUG(Service_FS, "Filesystem Access: %016" PRIX64 "\n", aci_file_access.permissions);
|
||||
}
|
||||
} // namespace FileSys
|
||||
154
src/core/file_sys/program_metadata.h
Normal file
154
src/core/file_sys/program_metadata.h
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus;
|
||||
}
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
enum class ProgramAddressSpaceType : u8 {
|
||||
Is64Bit = 1,
|
||||
Is32Bit = 2,
|
||||
};
|
||||
|
||||
enum class ProgramFilePermission : u64 {
|
||||
MountContent = 1ULL << 0,
|
||||
SaveDataBackup = 1ULL << 5,
|
||||
SdCard = 1ULL << 21,
|
||||
Calibration = 1ULL << 34,
|
||||
Bit62 = 1ULL << 62,
|
||||
Everything = 1ULL << 63,
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper which implements an interface to parse Program Description Metadata (NPDM)
|
||||
* Data can either be loaded from a file path or with data and an offset into it.
|
||||
*/
|
||||
class ProgramMetadata {
|
||||
public:
|
||||
Loader::ResultStatus Load(const std::string& file_path);
|
||||
Loader::ResultStatus Load(const std::vector<u8> file_data, size_t offset = 0);
|
||||
|
||||
bool Is64BitProgram() const;
|
||||
ProgramAddressSpaceType GetAddressSpaceType() const;
|
||||
u8 GetMainThreadPriority() const;
|
||||
u8 GetMainThreadCore() const;
|
||||
u32 GetMainThreadStackSize() const;
|
||||
u64 GetTitleID() const;
|
||||
u64 GetFilesystemPermissions() const;
|
||||
|
||||
void Print() const;
|
||||
|
||||
private:
|
||||
struct Header {
|
||||
std::array<char, 4> magic;
|
||||
std::array<u8, 8> reserved;
|
||||
union {
|
||||
u8 flags;
|
||||
|
||||
BitField<0, 1, u8> has_64_bit_instructions;
|
||||
BitField<1, 3, ProgramAddressSpaceType> address_space_type;
|
||||
BitField<4, 4, u8> reserved_2;
|
||||
};
|
||||
u8 reserved_3;
|
||||
u8 main_thread_priority;
|
||||
u8 main_thread_cpu;
|
||||
std::array<u8, 8> reserved_4;
|
||||
u32_le process_category;
|
||||
u32_le main_stack_size;
|
||||
std::array<u8, 0x10> application_name;
|
||||
std::array<u8, 0x40> reserved_5;
|
||||
u32_le aci_offset;
|
||||
u32_le aci_size;
|
||||
u32_le acid_offset;
|
||||
u32_le acid_size;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong");
|
||||
|
||||
struct AcidHeader {
|
||||
std::array<u8, 0x100> signature;
|
||||
std::array<u8, 0x100> nca_modulus;
|
||||
std::array<char, 4> magic;
|
||||
u32_le nca_size;
|
||||
std::array<u8, 0x4> reserved;
|
||||
union {
|
||||
u32 flags;
|
||||
|
||||
BitField<0, 1, u32> is_retail;
|
||||
BitField<1, 31, u32> flags_unk;
|
||||
};
|
||||
u64_le title_id_min;
|
||||
u64_le title_id_max;
|
||||
u32_le fac_offset;
|
||||
u32_le fac_size;
|
||||
u32_le sac_offset;
|
||||
u32_le sac_size;
|
||||
u32_le kac_offset;
|
||||
u32_le kac_size;
|
||||
INSERT_PADDING_BYTES(0x8);
|
||||
};
|
||||
|
||||
static_assert(sizeof(AcidHeader) == 0x240, "ACID header structure size is wrong");
|
||||
|
||||
struct AciHeader {
|
||||
std::array<char, 4> magic;
|
||||
std::array<u8, 0xC> reserved;
|
||||
u64_le title_id;
|
||||
INSERT_PADDING_BYTES(0x8);
|
||||
u32_le fah_offset;
|
||||
u32_le fah_size;
|
||||
u32_le sac_offset;
|
||||
u32_le sac_size;
|
||||
u32_le kac_offset;
|
||||
u32_le kac_size;
|
||||
INSERT_PADDING_BYTES(0x8);
|
||||
};
|
||||
|
||||
static_assert(sizeof(AciHeader) == 0x40, "ACI0 header structure size is wrong");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct FileAccessControl {
|
||||
u8 version;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
u64_le permissions;
|
||||
std::array<u8, 0x20> unknown;
|
||||
};
|
||||
|
||||
static_assert(sizeof(FileAccessControl) == 0x2C, "FS access control structure size is wrong");
|
||||
|
||||
struct FileAccessHeader {
|
||||
u8 version;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
u64_le permissions;
|
||||
u32_le unk_offset;
|
||||
u32_le unk_size;
|
||||
u32_le unk_offset_2;
|
||||
u32_le unk_size_2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(FileAccessHeader) == 0x1C, "FS access header structure size is wrong");
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
Header npdm_header;
|
||||
AciHeader aci_header;
|
||||
AcidHeader acid_header;
|
||||
|
||||
FileAccessControl acid_file_access;
|
||||
FileAccessHeader aci_file_access;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
38
src/core/file_sys/romfs_factory.cpp
Normal file
38
src/core/file_sys/romfs_factory.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/file_sys/romfs_filesystem.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) {
|
||||
// Load the RomFS from the app
|
||||
if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) {
|
||||
LOG_ERROR(Service_FS, "Unable to read RomFS!");
|
||||
}
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) {
|
||||
auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size);
|
||||
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
|
||||
}
|
||||
|
||||
ResultCode RomFS_Factory::Format(const Path& path) {
|
||||
LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str());
|
||||
// TODO(bunnei): Find the right error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const {
|
||||
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
|
||||
// TODO(bunnei): Find the right error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
35
src/core/file_sys/romfs_factory.h
Normal file
35
src/core/file_sys/romfs_factory.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/hle/result.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
/// File system interface to the RomFS archive
|
||||
class RomFS_Factory final : public FileSystemFactory {
|
||||
public:
|
||||
explicit RomFS_Factory(Loader::AppLoader& app_loader);
|
||||
|
||||
std::string GetName() const override {
|
||||
return "ArchiveFactory_RomFS";
|
||||
}
|
||||
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
|
||||
ResultCode Format(const Path& path) override;
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<FileUtil::IOFile> romfs_file;
|
||||
u64 data_offset;
|
||||
u64 data_size;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
112
src/core/file_sys/romfs_filesystem.cpp
Normal file
112
src/core/file_sys/romfs_filesystem.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/romfs_filesystem.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
std::string RomFS_FileSystem::GetName() const {
|
||||
return "RomFS";
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std::string& path,
|
||||
Mode mode) const {
|
||||
return MakeResult<std::unique_ptr<StorageBackend>>(
|
||||
std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size));
|
||||
}
|
||||
|
||||
ResultCode RomFS_FileSystem::DeleteFile(const Path& path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(bunnei): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode RomFS_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(bunnei): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode RomFS_FileSystem::CreateDirectory(const Path& path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const {
|
||||
LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).",
|
||||
GetName().c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory(
|
||||
const Path& path) const {
|
||||
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>());
|
||||
}
|
||||
|
||||
u64 RomFS_FileSystem::GetFreeSpaceSize() const {
|
||||
LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const {
|
||||
LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path %s).", path.c_str());
|
||||
// TODO(wwylele): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const {
|
||||
LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length);
|
||||
romfs_file->Seek(data_offset + offset, SEEK_SET);
|
||||
size_t read_length = (size_t)std::min((u64)length, data_size - offset);
|
||||
|
||||
return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length));
|
||||
}
|
||||
|
||||
ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush,
|
||||
const u8* buffer) const {
|
||||
LOG_ERROR(Service_FS, "Attempted to write to ROMFS file");
|
||||
// TODO(Subv): Find error code
|
||||
return MakeResult<size_t>(0);
|
||||
}
|
||||
|
||||
u64 RomFS_Storage::GetSize() const {
|
||||
return data_size;
|
||||
}
|
||||
|
||||
bool RomFS_Storage::SetSize(const u64 size) const {
|
||||
LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file");
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -10,39 +10,37 @@
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/directory_backend.h"
|
||||
#include "core/file_sys/file_backend.h"
|
||||
#include "core/file_sys/directory.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/file_sys/storage.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
/**
|
||||
* Helper which implements an interface to deal with IVFC images used in some archives
|
||||
* This should be subclassed by concrete archive types, which will provide the
|
||||
* input data (load the raw IVFC archive) and override any required methods
|
||||
* Helper which implements an interface to deal with Switch .istorage ROMFS images used in some
|
||||
* archives This should be subclassed by concrete archive types, which will provide the input data
|
||||
* (load the raw ROMFS archive) and override any required methods
|
||||
*/
|
||||
class IVFCArchive : public ArchiveBackend {
|
||||
class RomFS_FileSystem : public FileSystemBackend {
|
||||
public:
|
||||
IVFCArchive(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
|
||||
RomFS_FileSystem(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
|
||||
: romfs_file(file), data_offset(offset), data_size(size) {}
|
||||
|
||||
std::string GetName() const override;
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
|
||||
const Mode& mode) const override;
|
||||
ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path,
|
||||
Mode mode) const override;
|
||||
ResultCode DeleteFile(const Path& path) const override;
|
||||
ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultCode DeleteDirectory(const Path& path) const override;
|
||||
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
|
||||
ResultCode CreateFile(const Path& path, u64 size) const override;
|
||||
ResultCode CreateFile(const std::string& path, u64 size) const override;
|
||||
ResultCode CreateDirectory(const Path& path) const override;
|
||||
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
||||
u64 GetFreeBytes() const override;
|
||||
u64 GetFreeSpaceSize() const override;
|
||||
ResultVal<EntryType> GetEntryType(const std::string& path) const override;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<FileUtil::IOFile> romfs_file;
|
||||
@@ -50,9 +48,9 @@ protected:
|
||||
u64 data_size;
|
||||
};
|
||||
|
||||
class IVFCFile : public FileBackend {
|
||||
class RomFS_Storage : public StorageBackend {
|
||||
public:
|
||||
IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
|
||||
RomFS_Storage(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size)
|
||||
: romfs_file(file), data_offset(offset), data_size(size) {}
|
||||
|
||||
ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override;
|
||||
@@ -70,7 +68,7 @@ private:
|
||||
u64 data_size;
|
||||
};
|
||||
|
||||
class IVFCDirectory : public DirectoryBackend {
|
||||
class ROMFSDirectory : public DirectoryBackend {
|
||||
public:
|
||||
u32 Read(const u32 count, Entry* entries) override {
|
||||
return 0;
|
||||
@@ -1,330 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/file_util.h"
|
||||
#include "core/file_sys/disk_archive.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/file_sys/path_parser.h"
|
||||
#include "core/file_sys/savedata_archive.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path,
|
||||
const Mode& mode) const {
|
||||
LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex);
|
||||
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
|
||||
return ERROR_INVALID_PATH;
|
||||
}
|
||||
|
||||
if (mode.hex == 0) {
|
||||
LOG_ERROR(Service_FS, "Empty open mode");
|
||||
return ERROR_UNSUPPORTED_OPEN_FLAGS;
|
||||
}
|
||||
|
||||
if (mode.create_flag && !mode.write_flag) {
|
||||
LOG_ERROR(Service_FS, "Create flag set but write flag not set");
|
||||
return ERROR_UNSUPPORTED_OPEN_FLAGS;
|
||||
}
|
||||
|
||||
const auto full_path = path_parser.BuildHostPath(mount_point);
|
||||
|
||||
switch (path_parser.GetHostStatus(mount_point)) {
|
||||
case PathParser::InvalidMountPoint:
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
case PathParser::PathNotFound:
|
||||
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
case PathParser::FileInPath:
|
||||
case PathParser::DirectoryFound:
|
||||
LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str());
|
||||
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
|
||||
case PathParser::NotFound:
|
||||
if (!mode.create_flag) {
|
||||
LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.",
|
||||
full_path.c_str());
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
} else {
|
||||
// Create the file
|
||||
FileUtil::CreateEmptyFile(full_path);
|
||||
}
|
||||
break;
|
||||
case PathParser::FileFound:
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb");
|
||||
if (!file.IsOpen()) {
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str());
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
auto disk_file = std::make_unique<DiskFile>(std::move(file), mode);
|
||||
return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file));
|
||||
}
|
||||
|
||||
ResultCode SaveDataArchive::DeleteFile(const Path& path) const {
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
|
||||
return ERROR_INVALID_PATH;
|
||||
}
|
||||
|
||||
const auto full_path = path_parser.BuildHostPath(mount_point);
|
||||
|
||||
switch (path_parser.GetHostStatus(mount_point)) {
|
||||
case PathParser::InvalidMountPoint:
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
case PathParser::PathNotFound:
|
||||
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
case PathParser::FileInPath:
|
||||
case PathParser::DirectoryFound:
|
||||
case PathParser::NotFound:
|
||||
LOG_ERROR(Service_FS, "File not found %s", full_path.c_str());
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
case PathParser::FileFound:
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
if (FileUtil::Delete(full_path)) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str());
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
|
||||
ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const {
|
||||
const PathParser path_parser_src(src_path);
|
||||
|
||||
// TODO: Verify these return codes with HW
|
||||
if (!path_parser_src.IsValid()) {
|
||||
LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
|
||||
return ERROR_INVALID_PATH;
|
||||
}
|
||||
|
||||
const PathParser path_parser_dest(dest_path);
|
||||
|
||||
if (!path_parser_dest.IsValid()) {
|
||||
LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
|
||||
return ERROR_INVALID_PATH;
|
||||
}
|
||||
|
||||
const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
|
||||
const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
|
||||
|
||||
if (FileUtil::Rename(src_path_full, dest_path_full)) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// TODO(bunnei): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point,
|
||||
T deleter) {
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
|
||||
return ERROR_INVALID_PATH;
|
||||
}
|
||||
|
||||
if (path_parser.IsRootDirectory())
|
||||
return ERROR_DIRECTORY_NOT_EMPTY;
|
||||
|
||||
const auto full_path = path_parser.BuildHostPath(mount_point);
|
||||
|
||||
switch (path_parser.GetHostStatus(mount_point)) {
|
||||
case PathParser::InvalidMountPoint:
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
case PathParser::PathNotFound:
|
||||
case PathParser::NotFound:
|
||||
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
case PathParser::FileInPath:
|
||||
case PathParser::FileFound:
|
||||
LOG_ERROR(Service_FS, "Unexpected file or directory %s", full_path.c_str());
|
||||
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
|
||||
case PathParser::DirectoryFound:
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
if (deleter(full_path)) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str());
|
||||
return ERROR_DIRECTORY_NOT_EMPTY;
|
||||
}
|
||||
|
||||
ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const {
|
||||
return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir);
|
||||
}
|
||||
|
||||
ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const {
|
||||
return DeleteDirectoryHelper(
|
||||
path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); });
|
||||
}
|
||||
|
||||
ResultCode SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const {
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
|
||||
return ERROR_INVALID_PATH;
|
||||
}
|
||||
|
||||
const auto full_path = path_parser.BuildHostPath(mount_point);
|
||||
|
||||
switch (path_parser.GetHostStatus(mount_point)) {
|
||||
case PathParser::InvalidMountPoint:
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
case PathParser::PathNotFound:
|
||||
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
case PathParser::FileInPath:
|
||||
LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
|
||||
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
|
||||
case PathParser::DirectoryFound:
|
||||
case PathParser::FileFound:
|
||||
LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
|
||||
return ERROR_FILE_ALREADY_EXISTS;
|
||||
case PathParser::NotFound:
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
FileUtil::CreateEmptyFile(full_path);
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
FileUtil::IOFile file(full_path, "wb");
|
||||
// Creates a sparse file (or a normal file on filesystems without the concept of sparse files)
|
||||
// We do this by seeking to the right size, then writing a single null byte.
|
||||
if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
LOG_ERROR(Service_FS, "Too large file");
|
||||
|
||||
// TODO(bunnei): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode SaveDataArchive::CreateDirectory(const Path& path) const {
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
|
||||
return ERROR_INVALID_PATH;
|
||||
}
|
||||
|
||||
const auto full_path = path_parser.BuildHostPath(mount_point);
|
||||
|
||||
switch (path_parser.GetHostStatus(mount_point)) {
|
||||
case PathParser::InvalidMountPoint:
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
case PathParser::PathNotFound:
|
||||
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
case PathParser::FileInPath:
|
||||
LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
|
||||
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
|
||||
case PathParser::DirectoryFound:
|
||||
case PathParser::FileFound:
|
||||
LOG_ERROR(Service_FS, "%s already exists", full_path.c_str());
|
||||
return ERROR_DIRECTORY_ALREADY_EXISTS;
|
||||
case PathParser::NotFound:
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
if (FileUtil::CreateDir(mount_point + path.AsString())) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str());
|
||||
|
||||
// TODO(bunnei): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const {
|
||||
const PathParser path_parser_src(src_path);
|
||||
|
||||
// TODO: Verify these return codes with HW
|
||||
if (!path_parser_src.IsValid()) {
|
||||
LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str());
|
||||
return ERROR_INVALID_PATH;
|
||||
}
|
||||
|
||||
const PathParser path_parser_dest(dest_path);
|
||||
|
||||
if (!path_parser_dest.IsValid()) {
|
||||
LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str());
|
||||
return ERROR_INVALID_PATH;
|
||||
}
|
||||
|
||||
const auto src_path_full = path_parser_src.BuildHostPath(mount_point);
|
||||
const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point);
|
||||
|
||||
if (FileUtil::Rename(src_path_full, dest_path_full)) {
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
// TODO(bunnei): Use correct error code
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory(
|
||||
const Path& path) const {
|
||||
const PathParser path_parser(path);
|
||||
|
||||
if (!path_parser.IsValid()) {
|
||||
LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str());
|
||||
return ERROR_INVALID_PATH;
|
||||
}
|
||||
|
||||
const auto full_path = path_parser.BuildHostPath(mount_point);
|
||||
|
||||
switch (path_parser.GetHostStatus(mount_point)) {
|
||||
case PathParser::InvalidMountPoint:
|
||||
LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str());
|
||||
return ERROR_FILE_NOT_FOUND;
|
||||
case PathParser::PathNotFound:
|
||||
case PathParser::NotFound:
|
||||
LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str());
|
||||
return ERROR_PATH_NOT_FOUND;
|
||||
case PathParser::FileInPath:
|
||||
case PathParser::FileFound:
|
||||
LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str());
|
||||
return ERROR_UNEXPECTED_FILE_OR_DIRECTORY;
|
||||
case PathParser::DirectoryFound:
|
||||
break; // Expected 'success' case
|
||||
}
|
||||
|
||||
auto directory = std::make_unique<DiskDirectory>(full_path);
|
||||
return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory));
|
||||
}
|
||||
|
||||
u64 SaveDataArchive::GetFreeBytes() const {
|
||||
// TODO: Stubbed to return 1GiB
|
||||
return 1024 * 1024 * 1024;
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -1,43 +0,0 @@
|
||||
// Copyright 2016 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "core/file_sys/archive_backend.h"
|
||||
#include "core/file_sys/directory_backend.h"
|
||||
#include "core/file_sys/file_backend.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
/// Archive backend for general save data archive type (SaveData and SystemSaveData)
|
||||
class SaveDataArchive : public ArchiveBackend {
|
||||
public:
|
||||
explicit SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {}
|
||||
|
||||
std::string GetName() const override {
|
||||
return "SaveDataArchive: " + mount_point;
|
||||
}
|
||||
|
||||
ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path,
|
||||
const Mode& mode) const override;
|
||||
ResultCode DeleteFile(const Path& path) const override;
|
||||
ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultCode DeleteDirectory(const Path& path) const override;
|
||||
ResultCode DeleteDirectoryRecursively(const Path& path) const override;
|
||||
ResultCode CreateFile(const Path& path, u64 size) const override;
|
||||
ResultCode CreateDirectory(const Path& path) const override;
|
||||
ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override;
|
||||
ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override;
|
||||
u64 GetFreeBytes() const override;
|
||||
|
||||
protected:
|
||||
std::string mount_point;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
57
src/core/file_sys/savedata_factory.cpp
Normal file
57
src/core/file_sys/savedata_factory.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include <memory>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/disk_filesystem.h"
|
||||
#include "core/file_sys/savedata_factory.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
SaveData_Factory::SaveData_Factory(std::string nand_directory)
|
||||
: nand_directory(std::move(nand_directory)) {}
|
||||
|
||||
ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) {
|
||||
std::string save_directory = GetFullPath();
|
||||
// Return an error if the save data doesn't actually exist.
|
||||
if (!FileUtil::IsDirectory(save_directory)) {
|
||||
// TODO(Subv): Find out correct error code.
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
auto archive = std::make_unique<Disk_FileSystem>(save_directory);
|
||||
return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive));
|
||||
}
|
||||
|
||||
ResultCode SaveData_Factory::Format(const Path& path) {
|
||||
LOG_WARNING(Service_FS, "Format archive %s", GetName().c_str());
|
||||
// Create the save data directory.
|
||||
if (!FileUtil::CreateFullPath(GetFullPath())) {
|
||||
// TODO(Subv): Find the correct error code.
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const {
|
||||
LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str());
|
||||
// TODO(bunnei): Find the right error code for this
|
||||
return ResultCode(-1);
|
||||
}
|
||||
|
||||
std::string SaveData_Factory::GetFullPath() const {
|
||||
u64 title_id = Core::CurrentProcess()->program_id;
|
||||
// TODO(Subv): Somehow obtain this value.
|
||||
u32 user = 0;
|
||||
return Common::StringFromFormat("%ssave/%016" PRIX64 "/%08X/", nand_directory.c_str(), title_id,
|
||||
user);
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
33
src/core/file_sys/savedata_factory.h
Normal file
33
src/core/file_sys/savedata_factory.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
/// File system interface to the SaveData archive
|
||||
class SaveData_Factory final : public FileSystemFactory {
|
||||
public:
|
||||
explicit SaveData_Factory(std::string nand_directory);
|
||||
|
||||
std::string GetName() const override {
|
||||
return "SaveData_Factory";
|
||||
}
|
||||
ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override;
|
||||
ResultCode Format(const Path& path) override;
|
||||
ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override;
|
||||
|
||||
private:
|
||||
std::string nand_directory;
|
||||
|
||||
std::string GetFullPath() const;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
@@ -8,15 +8,12 @@
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
class FileBackend : NonCopyable {
|
||||
class StorageBackend : NonCopyable {
|
||||
public:
|
||||
FileBackend() {}
|
||||
virtual ~FileBackend() {}
|
||||
StorageBackend() {}
|
||||
virtual ~StorageBackend() {}
|
||||
|
||||
/**
|
||||
* Read data from the file
|
||||
@@ -39,10 +36,9 @@ public:
|
||||
const u8* buffer) const = 0;
|
||||
|
||||
/**
|
||||
* Get the size of the file in bytes
|
||||
* @return Size of the file in bytes
|
||||
* Flushes the file
|
||||
*/
|
||||
virtual u64 GetSize() const = 0;
|
||||
virtual void Flush() const = 0;
|
||||
|
||||
/**
|
||||
* Set the size of the file in bytes
|
||||
@@ -51,16 +47,17 @@ public:
|
||||
*/
|
||||
virtual bool SetSize(u64 size) const = 0;
|
||||
|
||||
/**
|
||||
* Get the size of the file in bytes
|
||||
* @return Size of the file in bytes
|
||||
*/
|
||||
virtual u64 GetSize() const = 0;
|
||||
|
||||
/**
|
||||
* Close the file
|
||||
* @return true if the file closed correctly
|
||||
*/
|
||||
virtual bool Close() const = 0;
|
||||
|
||||
/**
|
||||
* Flushes the file
|
||||
*/
|
||||
virtual void Flush() const = 0;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -1,163 +0,0 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cinttypes>
|
||||
#include "common/alignment.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/file_sys/title_metadata.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
static u32 GetSignatureSize(u32 signature_type) {
|
||||
switch (signature_type) {
|
||||
case Rsa4096Sha1:
|
||||
case Rsa4096Sha256:
|
||||
return 0x200;
|
||||
|
||||
case Rsa2048Sha1:
|
||||
case Rsa2048Sha256:
|
||||
return 0x100;
|
||||
|
||||
case EllipticSha1:
|
||||
case EcdsaSha256:
|
||||
return 0x3C;
|
||||
}
|
||||
}
|
||||
|
||||
Loader::ResultStatus TitleMetadata::Load() {
|
||||
FileUtil::IOFile file(filepath, "rb");
|
||||
if (!file.IsOpen())
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
if (!file.ReadBytes(&signature_type, sizeof(u32_be)))
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
// Signature lengths are variable, and the body follows the signature
|
||||
u32 signature_size = GetSignatureSize(signature_type);
|
||||
|
||||
tmd_signature.resize(signature_size);
|
||||
if (!file.ReadBytes(&tmd_signature[0], signature_size))
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
// The TMD body start position is rounded to the nearest 0x40 after the signature
|
||||
size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40);
|
||||
file.Seek(body_start, SEEK_SET);
|
||||
|
||||
// Read our TMD body, then load the amount of ContentChunks specified
|
||||
if (file.ReadBytes(&tmd_body, sizeof(TitleMetadata::Body)) != sizeof(TitleMetadata::Body))
|
||||
return Loader::ResultStatus::Error;
|
||||
|
||||
for (u16 i = 0; i < tmd_body.content_count; i++) {
|
||||
ContentChunk chunk;
|
||||
if (file.ReadBytes(&chunk, sizeof(ContentChunk)) == sizeof(ContentChunk)) {
|
||||
tmd_chunks.push_back(chunk);
|
||||
} else {
|
||||
LOG_ERROR(Service_FS, "Malformed TMD %s, failed to load content chunk index %u!",
|
||||
filepath.c_str(), i);
|
||||
return Loader::ResultStatus::ErrorInvalidFormat;
|
||||
}
|
||||
}
|
||||
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
Loader::ResultStatus TitleMetadata::Save() {
|
||||
UNIMPLEMENTED();
|
||||
return Loader::ResultStatus::Success;
|
||||
}
|
||||
|
||||
u64 TitleMetadata::GetTitleID() const {
|
||||
return tmd_body.title_id;
|
||||
}
|
||||
|
||||
u32 TitleMetadata::GetTitleType() const {
|
||||
return tmd_body.title_type;
|
||||
}
|
||||
|
||||
u16 TitleMetadata::GetTitleVersion() const {
|
||||
return tmd_body.title_version;
|
||||
}
|
||||
|
||||
u64 TitleMetadata::GetSystemVersion() const {
|
||||
return tmd_body.system_version;
|
||||
}
|
||||
|
||||
size_t TitleMetadata::GetContentCount() const {
|
||||
return tmd_chunks.size();
|
||||
}
|
||||
|
||||
u32 TitleMetadata::GetBootContentID() const {
|
||||
return tmd_chunks[TMDContentIndex::Main].id;
|
||||
}
|
||||
|
||||
u32 TitleMetadata::GetManualContentID() const {
|
||||
return tmd_chunks[TMDContentIndex::Manual].id;
|
||||
}
|
||||
|
||||
u32 TitleMetadata::GetDLPContentID() const {
|
||||
return tmd_chunks[TMDContentIndex::DLP].id;
|
||||
}
|
||||
|
||||
void TitleMetadata::SetTitleID(u64 title_id) {
|
||||
tmd_body.title_id = title_id;
|
||||
}
|
||||
|
||||
void TitleMetadata::SetTitleType(u32 type) {
|
||||
tmd_body.title_type = type;
|
||||
}
|
||||
|
||||
void TitleMetadata::SetTitleVersion(u16 version) {
|
||||
tmd_body.title_version = version;
|
||||
}
|
||||
|
||||
void TitleMetadata::SetSystemVersion(u64 version) {
|
||||
tmd_body.system_version = version;
|
||||
}
|
||||
|
||||
void TitleMetadata::AddContentChunk(const ContentChunk& chunk) {
|
||||
tmd_chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
void TitleMetadata::Print() const {
|
||||
LOG_DEBUG(Service_FS, "%s - %u chunks", filepath.c_str(),
|
||||
static_cast<u32>(tmd_body.content_count));
|
||||
|
||||
// Content info describes ranges of content chunks
|
||||
LOG_DEBUG(Service_FS, "Content info:");
|
||||
for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) {
|
||||
if (tmd_body.contentinfo[i].command_count == 0)
|
||||
break;
|
||||
|
||||
LOG_DEBUG(Service_FS, " Index %04X, Command Count %04X",
|
||||
static_cast<u32>(tmd_body.contentinfo[i].index),
|
||||
static_cast<u32>(tmd_body.contentinfo[i].command_count));
|
||||
}
|
||||
|
||||
// For each content info, print their content chunk range
|
||||
for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) {
|
||||
u16 index = static_cast<u16>(tmd_body.contentinfo[i].index);
|
||||
u16 count = static_cast<u16>(tmd_body.contentinfo[i].command_count);
|
||||
|
||||
if (count == 0)
|
||||
continue;
|
||||
|
||||
LOG_DEBUG(Service_FS, "Content chunks for content info index %zu:", i);
|
||||
for (u16 j = index; j < index + count; j++) {
|
||||
// Don't attempt to print content we don't have
|
||||
if (j > tmd_body.content_count)
|
||||
break;
|
||||
|
||||
const ContentChunk& chunk = tmd_chunks[j];
|
||||
LOG_DEBUG(Service_FS, " ID %08X, Index %04X, Type %04x, Size %016" PRIX64,
|
||||
static_cast<u32>(chunk.id), static_cast<u32>(chunk.index),
|
||||
static_cast<u32>(chunk.type), static_cast<u64>(chunk.size));
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace FileSys
|
||||
@@ -1,125 +0,0 @@
|
||||
// Copyright 2017 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
|
||||
namespace Loader {
|
||||
enum class ResultStatus;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// FileSys namespace
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
enum TMDSignatureType : u32 {
|
||||
Rsa4096Sha1 = 0x10000,
|
||||
Rsa2048Sha1 = 0x10001,
|
||||
EllipticSha1 = 0x10002,
|
||||
Rsa4096Sha256 = 0x10003,
|
||||
Rsa2048Sha256 = 0x10004,
|
||||
EcdsaSha256 = 0x10005
|
||||
};
|
||||
|
||||
enum TMDContentTypeFlag : u16 {
|
||||
Encrypted = 1 << 1,
|
||||
Disc = 1 << 2,
|
||||
CFM = 1 << 3,
|
||||
Optional = 1 << 14,
|
||||
Shared = 1 << 15
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper which implements an interface to read and write Title Metadata (TMD) files.
|
||||
* If a file path is provided and the file exists, it can be parsed and used, otherwise
|
||||
* it must be created. The TMD file can then be interpreted, modified and/or saved.
|
||||
*/
|
||||
class TitleMetadata {
|
||||
public:
|
||||
struct ContentChunk {
|
||||
u32_be id;
|
||||
u16_be index;
|
||||
u16_be type;
|
||||
u64_be size;
|
||||
std::array<u8, 0x20> hash;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ContentChunk) == 0x30, "TMD ContentChunk structure size is wrong");
|
||||
|
||||
struct ContentInfo {
|
||||
u16_be index;
|
||||
u16_be command_count;
|
||||
std::array<u8, 0x20> hash;
|
||||
};
|
||||
|
||||
static_assert(sizeof(ContentInfo) == 0x24, "TMD ContentInfo structure size is wrong");
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
struct Body {
|
||||
std::array<u8, 0x40> issuer;
|
||||
u8 version;
|
||||
u8 ca_crl_version;
|
||||
u8 signer_crl_version;
|
||||
u8 reserved;
|
||||
u64_be system_version;
|
||||
u64_be title_id;
|
||||
u32_be title_type;
|
||||
u16_be group_id;
|
||||
u32_be savedata_size;
|
||||
u32_be srl_private_savedata_size;
|
||||
std::array<u8, 4> reserved_2;
|
||||
u8 srl_flag;
|
||||
std::array<u8, 0x31> reserved_3;
|
||||
u32_be access_rights;
|
||||
u16_be title_version;
|
||||
u16_be content_count;
|
||||
u16_be boot_content;
|
||||
std::array<u8, 2> reserved_4;
|
||||
std::array<u8, 0x20> contentinfo_hash;
|
||||
std::array<ContentInfo, 64> contentinfo;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Body) == 0x9C4, "TMD body structure size is wrong");
|
||||
|
||||
#pragma pack(pop)
|
||||
|
||||
explicit TitleMetadata(std::string& path) : filepath(std::move(path)) {}
|
||||
Loader::ResultStatus Load();
|
||||
Loader::ResultStatus Save();
|
||||
|
||||
u64 GetTitleID() const;
|
||||
u32 GetTitleType() const;
|
||||
u16 GetTitleVersion() const;
|
||||
u64 GetSystemVersion() const;
|
||||
size_t GetContentCount() const;
|
||||
u32 GetBootContentID() const;
|
||||
u32 GetManualContentID() const;
|
||||
u32 GetDLPContentID() const;
|
||||
|
||||
void SetTitleID(u64 title_id);
|
||||
void SetTitleType(u32 type);
|
||||
void SetTitleVersion(u16 version);
|
||||
void SetSystemVersion(u64 version);
|
||||
void AddContentChunk(const ContentChunk& chunk);
|
||||
|
||||
void Print() const;
|
||||
|
||||
private:
|
||||
enum TMDContentIndex { Main = 0, Manual = 1, DLP = 2 };
|
||||
|
||||
Body tmd_body;
|
||||
u32_be signature_type;
|
||||
std::vector<u8> tmd_signature;
|
||||
std::vector<ContentChunk> tmd_chunks;
|
||||
|
||||
std::string filepath;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cinttypes>
|
||||
#include <climits>
|
||||
#include <csignal>
|
||||
#include <cstdarg>
|
||||
@@ -57,9 +58,10 @@ const u32 SIGTERM = 15;
|
||||
const u32 MSG_WAITALL = 8;
|
||||
#endif
|
||||
|
||||
const u32 R15_REGISTER = 15;
|
||||
const u32 CPSR_REGISTER = 25;
|
||||
const u32 FPSCR_REGISTER = 58;
|
||||
const u32 X30_REGISTER = 30;
|
||||
const u32 SP_REGISTER = 31;
|
||||
const u32 PC_REGISTER = 32;
|
||||
const u32 CPSR_REGISTER = 33;
|
||||
|
||||
// For sample XML files see the GDB source /gdb/features
|
||||
// GDB also wants the l character at the start
|
||||
@@ -68,48 +70,62 @@ static const char* target_xml =
|
||||
R"(l<?xml version="1.0"?>
|
||||
<!DOCTYPE target SYSTEM "gdb-target.dtd">
|
||||
<target version="1.0">
|
||||
<feature name="org.gnu.gdb.arm.core">
|
||||
<reg name="r0" bitsize="32"/>
|
||||
<reg name="r1" bitsize="32"/>
|
||||
<reg name="r2" bitsize="32"/>
|
||||
<reg name="r3" bitsize="32"/>
|
||||
<reg name="r4" bitsize="32"/>
|
||||
<reg name="r5" bitsize="32"/>
|
||||
<reg name="r6" bitsize="32"/>
|
||||
<reg name="r7" bitsize="32"/>
|
||||
<reg name="r8" bitsize="32"/>
|
||||
<reg name="r9" bitsize="32"/>
|
||||
<reg name="r10" bitsize="32"/>
|
||||
<reg name="r11" bitsize="32"/>
|
||||
<reg name="r12" bitsize="32"/>
|
||||
<reg name="sp" bitsize="32" type="data_ptr"/>
|
||||
<reg name="lr" bitsize="32"/>
|
||||
<reg name="pc" bitsize="32" type="code_ptr"/>
|
||||
<feature name="org.gnu.gdb.aarch64.core">
|
||||
<reg name="x0" bitsize="64"/>
|
||||
<reg name="x1" bitsize="64"/>
|
||||
<reg name="x2" bitsize="64"/>
|
||||
<reg name="x3" bitsize="64"/>
|
||||
<reg name="x4" bitsize="64"/>
|
||||
<reg name="x5" bitsize="64"/>
|
||||
<reg name="x6" bitsize="64"/>
|
||||
<reg name="x7" bitsize="64"/>
|
||||
<reg name="x8" bitsize="64"/>
|
||||
<reg name="x9" bitsize="64"/>
|
||||
<reg name="x10" bitsize="64"/>
|
||||
<reg name="x11" bitsize="64"/>
|
||||
<reg name="x12" bitsize="64"/>
|
||||
<reg name="x13" bitsize="64"/>
|
||||
<reg name="x14" bitsize="64"/>
|
||||
<reg name="x15" bitsize="64"/>
|
||||
<reg name="x16" bitsize="64"/>
|
||||
<reg name="x17" bitsize="64"/>
|
||||
<reg name="x18" bitsize="64"/>
|
||||
<reg name="x19" bitsize="64"/>
|
||||
<reg name="x20" bitsize="64"/>
|
||||
<reg name="x21" bitsize="64"/>
|
||||
<reg name="x22" bitsize="64"/>
|
||||
<reg name="x23" bitsize="64"/>
|
||||
<reg name="x24" bitsize="64"/>
|
||||
<reg name="x25" bitsize="64"/>
|
||||
<reg name="x26" bitsize="64"/>
|
||||
<reg name="x27" bitsize="64"/>
|
||||
<reg name="x28" bitsize="64"/>
|
||||
<reg name="x29" bitsize="64"/>
|
||||
<reg name="x30" bitsize="64"/>
|
||||
<reg name="sp" bitsize="64" type="data_ptr"/>
|
||||
|
||||
<!-- The CPSR is register 25, rather than register 16, because
|
||||
the FPA registers historically were placed between the PC
|
||||
and the CPSR in the "g" packet. -->
|
||||
<reg name="pc" bitsize="64" type="code_ptr"/>
|
||||
|
||||
<reg name="cpsr" bitsize="32" regnum="25"/>
|
||||
</feature>
|
||||
<feature name="org.gnu.gdb.arm.vfp">
|
||||
<reg name="d0" bitsize="64" type="float"/>
|
||||
<reg name="d1" bitsize="64" type="float"/>
|
||||
<reg name="d2" bitsize="64" type="float"/>
|
||||
<reg name="d3" bitsize="64" type="float"/>
|
||||
<reg name="d4" bitsize="64" type="float"/>
|
||||
<reg name="d5" bitsize="64" type="float"/>
|
||||
<reg name="d6" bitsize="64" type="float"/>
|
||||
<reg name="d7" bitsize="64" type="float"/>
|
||||
<reg name="d8" bitsize="64" type="float"/>
|
||||
<reg name="d9" bitsize="64" type="float"/>
|
||||
<reg name="d10" bitsize="64" type="float"/>
|
||||
<reg name="d11" bitsize="64" type="float"/>
|
||||
<reg name="d12" bitsize="64" type="float"/>
|
||||
<reg name="d13" bitsize="64" type="float"/>
|
||||
<reg name="d14" bitsize="64" type="float"/>
|
||||
<reg name="d15" bitsize="64" type="float"/>
|
||||
<reg name="fpscr" bitsize="32" type="int" group="float"/>
|
||||
<flags id="cpsr_flags" size="4">
|
||||
<field name="SP" start="0" end="0"/>
|
||||
<field name="" start="1" end="1"/>
|
||||
<field name="EL" start="2" end="3"/>
|
||||
<field name="nRW" start="4" end="4"/>
|
||||
<field name="" start="5" end="5"/>
|
||||
<field name="F" start="6" end="6"/>
|
||||
<field name="I" start="7" end="7"/>
|
||||
<field name="A" start="8" end="8"/>
|
||||
<field name="D" start="9" end="9"/>
|
||||
|
||||
<field name="IL" start="20" end="20"/>
|
||||
<field name="SS" start="21" end="21"/>
|
||||
|
||||
<field name="V" start="28" end="28"/>
|
||||
<field name="C" start="29" end="29"/>
|
||||
<field name="Z" start="30" end="30"/>
|
||||
<field name="N" start="31" end="31"/>
|
||||
</flags>
|
||||
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
|
||||
</feature>
|
||||
</target>
|
||||
)";
|
||||
@@ -143,12 +159,12 @@ WSADATA InitData;
|
||||
struct Breakpoint {
|
||||
bool active;
|
||||
PAddr addr;
|
||||
u32 len;
|
||||
u64 len;
|
||||
};
|
||||
|
||||
static std::map<u32, Breakpoint> breakpoints_execute;
|
||||
static std::map<u32, Breakpoint> breakpoints_read;
|
||||
static std::map<u32, Breakpoint> breakpoints_write;
|
||||
static std::map<u64, Breakpoint> breakpoints_execute;
|
||||
static std::map<u64, Breakpoint> breakpoints_read;
|
||||
static std::map<u64, Breakpoint> breakpoints_write;
|
||||
|
||||
/**
|
||||
* Turns hex string character into the equivalent byte.
|
||||
@@ -183,11 +199,11 @@ static u8 NibbleToHex(u8 n) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts input hex string characters into an array of equivalent of u8 bytes.
|
||||
*
|
||||
* @param src Pointer to array of output hex string characters.
|
||||
* @param len Length of src array.
|
||||
*/
|
||||
* Converts input hex string characters into an array of equivalent of u8 bytes.
|
||||
*
|
||||
* @param src Pointer to array of output hex string characters.
|
||||
* @param len Length of src array.
|
||||
*/
|
||||
static u32 HexToInt(const u8* src, size_t len) {
|
||||
u32 output = 0;
|
||||
while (len-- > 0) {
|
||||
@@ -197,6 +213,21 @@ static u32 HexToInt(const u8* src, size_t len) {
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts input hex string characters into an array of equivalent of u8 bytes.
|
||||
*
|
||||
* @param src Pointer to array of output hex string characters.
|
||||
* @param len Length of src array.
|
||||
*/
|
||||
static u64 HexToLong(const u8* src, size_t len) {
|
||||
u64 output = 0;
|
||||
while (len-- > 0) {
|
||||
output = (output << 4) | HexCharToValue(src[0]);
|
||||
src++;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts input array of u8 bytes into their equivalent hex string characters.
|
||||
*
|
||||
@@ -234,8 +265,21 @@ static void GdbHexToMem(u8* dest, const u8* src, size_t len) {
|
||||
*/
|
||||
static void IntToGdbHex(u8* dest, u32 v) {
|
||||
for (int i = 0; i < 8; i += 2) {
|
||||
dest[i + 1] = NibbleToHex(v >> (4 * i));
|
||||
dest[i] = NibbleToHex(v >> (4 * (i + 1)));
|
||||
dest[i + 1] = NibbleToHex(static_cast<u8>(v >> (4 * i)));
|
||||
dest[i] = NibbleToHex(static_cast<u8>(v >> (4 * (i + 1))));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a u64 into a gdb-formatted hex string.
|
||||
*
|
||||
* @param dest Pointer to buffer to store output hex string characters.
|
||||
* @param v Value to convert.
|
||||
*/
|
||||
static void LongToGdbHex(u8* dest, u64 v) {
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
dest[i + 1] = NibbleToHex(static_cast<u8>(v >> (4 * i)));
|
||||
dest[i] = NibbleToHex(static_cast<u8>(v >> (4 * (i + 1))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,6 +299,22 @@ static u32 GdbHexToInt(const u8* src) {
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a gdb-formatted hex string into a u64.
|
||||
*
|
||||
* @param src Pointer to hex string.
|
||||
*/
|
||||
static u64 GdbHexToLong(const u8* src) {
|
||||
u64 output = 0;
|
||||
|
||||
for (int i = 0; i < 16; i += 2) {
|
||||
output = (output << 4) | HexCharToValue(src[15 - i - 1]);
|
||||
output = (output << 4) | HexCharToValue(src[15 - i]);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// Read a byte from the gdb client.
|
||||
static u8 ReadByte() {
|
||||
u8 c;
|
||||
@@ -277,7 +337,7 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) {
|
||||
*
|
||||
* @param type Type of breakpoint list.
|
||||
*/
|
||||
static std::map<u32, Breakpoint>& GetBreakpointList(BreakpointType type) {
|
||||
static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) {
|
||||
switch (type) {
|
||||
case BreakpointType::Execute:
|
||||
return breakpoints_execute;
|
||||
@@ -297,19 +357,20 @@ static std::map<u32, Breakpoint>& GetBreakpointList(BreakpointType type) {
|
||||
* @param addr Address of breakpoint.
|
||||
*/
|
||||
static void RemoveBreakpoint(BreakpointType type, PAddr addr) {
|
||||
std::map<u32, Breakpoint>& p = GetBreakpointList(type);
|
||||
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
|
||||
|
||||
auto bp = p.find(addr);
|
||||
auto bp = p.find(static_cast<u64>(addr));
|
||||
if (bp != p.end()) {
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n",
|
||||
bp->second.len, bp->second.addr, type);
|
||||
p.erase(addr);
|
||||
LOG_DEBUG(Debug_GDBStub,
|
||||
"gdb: removed a breakpoint: %016" PRIx64 " bytes at %016" PRIx64 " of type %d\n",
|
||||
bp->second.len, bp->second.addr, static_cast<int>(type));
|
||||
p.erase(static_cast<u64>(addr));
|
||||
}
|
||||
}
|
||||
|
||||
BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) {
|
||||
std::map<u32, Breakpoint>& p = GetBreakpointList(type);
|
||||
auto next_breakpoint = p.lower_bound(addr);
|
||||
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
|
||||
auto next_breakpoint = p.lower_bound(static_cast<u64>(addr));
|
||||
BreakpointAddress breakpoint;
|
||||
|
||||
if (next_breakpoint != p.end()) {
|
||||
@@ -328,11 +389,11 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::map<u32, Breakpoint>& p = GetBreakpointList(type);
|
||||
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
|
||||
|
||||
auto bp = p.find(addr);
|
||||
auto bp = p.find(static_cast<u64>(addr));
|
||||
if (bp != p.end()) {
|
||||
u32 len = bp->second.len;
|
||||
u64 len = bp->second.len;
|
||||
|
||||
// IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints
|
||||
// no matter if it's a 4-byte or 2-byte instruction. When you execute a
|
||||
@@ -348,8 +409,9 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) {
|
||||
|
||||
if (bp->second.active && (addr >= bp->second.addr && addr < bp->second.addr + len)) {
|
||||
LOG_DEBUG(Debug_GDBStub,
|
||||
"Found breakpoint type %d @ %08x, range: %08x - %08x (%d bytes)\n", type,
|
||||
addr, bp->second.addr, bp->second.addr + len, len);
|
||||
"Found breakpoint type %d @ %016" PRIx64 ", range: %016" PRIx64
|
||||
" - %016" PRIx64 " (%" PRIx64 " bytes)\n",
|
||||
static_cast<int>(type), addr, bp->second.addr, bp->second.addr + len, len);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -419,7 +481,7 @@ static void HandleQuery() {
|
||||
SendReply("T0");
|
||||
} else if (strncmp(query, "Supported", strlen("Supported")) == 0) {
|
||||
// PacketSize needs to be large enough for target xml
|
||||
SendReply("PacketSize=800;qXfer:features:read+");
|
||||
SendReply("PacketSize=2000;qXfer:features:read+");
|
||||
} else if (strncmp(query, "Xfer:features:read:target.xml:",
|
||||
strlen("Xfer:features:read:target.xml:")) == 0) {
|
||||
SendReply(target_xml);
|
||||
@@ -450,9 +512,7 @@ static void SendSignal(u32 signal) {
|
||||
|
||||
latest_signal = signal;
|
||||
|
||||
std::string buffer =
|
||||
Common::StringFromFormat("T%02x%02x:%08x;%02x:%08x;", latest_signal, 15,
|
||||
htonl(Core::CPU().GetPC()), 13, htonl(Core::CPU().GetReg(13)));
|
||||
std::string buffer = Common::StringFromFormat("T%02x", latest_signal);
|
||||
LOG_DEBUG(Debug_GDBStub, "Response: %s", buffer.c_str());
|
||||
SendReply(buffer.c_str());
|
||||
}
|
||||
@@ -538,16 +598,12 @@ static void ReadRegister() {
|
||||
id |= HexCharToValue(command_buffer[2]);
|
||||
}
|
||||
|
||||
if (id <= R15_REGISTER) {
|
||||
IntToGdbHex(reply, Core::CPU().GetReg(id));
|
||||
if (id <= SP_REGISTER) {
|
||||
LongToGdbHex(reply, Core::CPU().GetReg(static_cast<int>(id)));
|
||||
} else if (id == PC_REGISTER) {
|
||||
LongToGdbHex(reply, Core::CPU().GetPC());
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
IntToGdbHex(reply, Core::CPU().GetCPSR());
|
||||
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
|
||||
IntToGdbHex(reply, Core::CPU().GetVFPReg(
|
||||
id - CPSR_REGISTER -
|
||||
1)); // VFP registers should start at 26, so one after CSPR_REGISTER
|
||||
} else if (id == FPSCR_REGISTER) {
|
||||
UNIMPLEMENTED();
|
||||
} else {
|
||||
return SendReply("E01");
|
||||
}
|
||||
@@ -562,21 +618,19 @@ static void ReadRegisters() {
|
||||
|
||||
u8* bufptr = buffer;
|
||||
|
||||
for (int reg = 0; reg <= R15_REGISTER; reg++) {
|
||||
IntToGdbHex(bufptr + reg * CHAR_BIT, Core::CPU().GetReg(reg));
|
||||
for (int reg = 0; reg <= SP_REGISTER; reg++) {
|
||||
LongToGdbHex(bufptr + reg * 16, Core::CPU().GetReg(reg));
|
||||
}
|
||||
|
||||
bufptr += (16 * CHAR_BIT);
|
||||
bufptr += (32 * 16);
|
||||
|
||||
LongToGdbHex(bufptr, Core::CPU().GetPC());
|
||||
|
||||
bufptr += 16;
|
||||
|
||||
IntToGdbHex(bufptr, Core::CPU().GetCPSR());
|
||||
|
||||
bufptr += CHAR_BIT;
|
||||
|
||||
for (int reg = 0; reg <= 31; reg++) {
|
||||
IntToGdbHex(bufptr + reg * CHAR_BIT, Core::CPU().GetVFPReg(reg));
|
||||
}
|
||||
|
||||
bufptr += (32 * CHAR_BIT);
|
||||
bufptr += 8;
|
||||
|
||||
SendReply(reinterpret_cast<char*>(buffer));
|
||||
}
|
||||
@@ -592,14 +646,12 @@ static void WriteRegister() {
|
||||
id |= HexCharToValue(command_buffer[2]);
|
||||
}
|
||||
|
||||
if (id <= R15_REGISTER) {
|
||||
Core::CPU().SetReg(id, GdbHexToInt(buffer_ptr));
|
||||
if (id <= SP_REGISTER) {
|
||||
Core::CPU().SetReg(id, GdbHexToLong(buffer_ptr));
|
||||
} else if (id == PC_REGISTER) {
|
||||
Core::CPU().SetPC(GdbHexToLong(buffer_ptr));
|
||||
} else if (id == CPSR_REGISTER) {
|
||||
Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr));
|
||||
} else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) {
|
||||
Core::CPU().SetVFPReg(id - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr));
|
||||
} else if (id == FPSCR_REGISTER) {
|
||||
UNIMPLEMENTED();
|
||||
} else {
|
||||
return SendReply("E01");
|
||||
}
|
||||
@@ -614,20 +666,14 @@ static void WriteRegisters() {
|
||||
if (command_buffer[0] != 'G')
|
||||
return SendReply("E01");
|
||||
|
||||
for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) {
|
||||
if (reg <= R15_REGISTER) {
|
||||
Core::CPU().SetReg(reg, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
|
||||
for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) {
|
||||
if (reg <= SP_REGISTER) {
|
||||
Core::CPU().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16));
|
||||
} else if (reg == PC_REGISTER) {
|
||||
Core::CPU().SetPC(GdbHexToLong(buffer_ptr + i * 16));
|
||||
} else if (reg == CPSR_REGISTER) {
|
||||
Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr + i * CHAR_BIT));
|
||||
} else if (reg == CPSR_REGISTER - 1) {
|
||||
// Dummy FPA register, ignore
|
||||
} else if (reg < CPSR_REGISTER) {
|
||||
// Dummy FPA registers, ignore
|
||||
i += 2;
|
||||
} else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) {
|
||||
Core::CPU().SetVFPReg(reg - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr + i * CHAR_BIT));
|
||||
i++; // Skip padding
|
||||
} else if (reg == FPSCR_REGISTER) {
|
||||
Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr + i * 16));
|
||||
} else {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
}
|
||||
@@ -641,13 +687,13 @@ static void ReadMemory() {
|
||||
|
||||
auto start_offset = command_buffer + 1;
|
||||
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
|
||||
VAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
|
||||
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
|
||||
|
||||
start_offset = addr_pos + 1;
|
||||
u32 len =
|
||||
HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
|
||||
u64 len =
|
||||
HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
|
||||
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %08x len: %08x\n", addr, len);
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016lx len: %016lx\n", addr, len);
|
||||
|
||||
if (len * 2 > sizeof(reply)) {
|
||||
SendReply("E01");
|
||||
@@ -669,11 +715,11 @@ static void ReadMemory() {
|
||||
static void WriteMemory() {
|
||||
auto start_offset = command_buffer + 1;
|
||||
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
|
||||
VAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
|
||||
VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
|
||||
|
||||
start_offset = addr_pos + 1;
|
||||
auto len_pos = std::find(start_offset, command_buffer + command_length, ':');
|
||||
u32 len = HexToInt(start_offset, static_cast<u32>(len_pos - start_offset));
|
||||
u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset));
|
||||
|
||||
if (!Memory::IsValidVirtualAddress(addr)) {
|
||||
return SendReply("E00");
|
||||
@@ -726,8 +772,8 @@ static void Continue() {
|
||||
* @param addr Address of breakpoint.
|
||||
* @param len Length of breakpoint.
|
||||
*/
|
||||
static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
|
||||
std::map<u32, Breakpoint>& p = GetBreakpointList(type);
|
||||
static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) {
|
||||
std::map<u64, Breakpoint>& p = GetBreakpointList(type);
|
||||
|
||||
Breakpoint breakpoint;
|
||||
breakpoint.active = true;
|
||||
@@ -735,8 +781,8 @@ static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) {
|
||||
breakpoint.len = len;
|
||||
p.insert({addr, breakpoint});
|
||||
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %08x bytes at %08x\n", type, breakpoint.len,
|
||||
breakpoint.addr);
|
||||
LOG_DEBUG(Debug_GDBStub, "gdb: added %d breakpoint: %016" PRIx64 " bytes at %016" PRIx64 "\n",
|
||||
static_cast<int>(type), breakpoint.len, breakpoint.addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -766,11 +812,11 @@ static void AddBreakpoint() {
|
||||
|
||||
auto start_offset = command_buffer + 3;
|
||||
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
|
||||
PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
|
||||
PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
|
||||
|
||||
start_offset = addr_pos + 1;
|
||||
u32 len =
|
||||
HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset));
|
||||
u64 len =
|
||||
HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset));
|
||||
|
||||
if (type == BreakpointType::Access) {
|
||||
// Access is made up of Read and Write types, so add both breakpoints
|
||||
@@ -815,7 +861,7 @@ static void RemoveBreakpoint() {
|
||||
|
||||
auto start_offset = command_buffer + 3;
|
||||
auto addr_pos = std::find(start_offset, command_buffer + command_length, ',');
|
||||
PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset));
|
||||
PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset));
|
||||
|
||||
if (type == BreakpointType::Access) {
|
||||
// Access is made up of Read and Write types, so add both breakpoints
|
||||
@@ -905,7 +951,7 @@ void ToggleServer(bool status) {
|
||||
server_enabled = status;
|
||||
|
||||
// Start server
|
||||
if (!IsConnected() && Core::System().GetInstance().IsPoweredOn()) {
|
||||
if (!IsConnected() && Core::System::GetInstance().IsPoweredOn()) {
|
||||
Init();
|
||||
}
|
||||
} else {
|
||||
@@ -1034,4 +1080,4 @@ bool GetCpuStepFlag() {
|
||||
void SetCpuStepFlag(bool is_step) {
|
||||
step_loop = is_step;
|
||||
}
|
||||
};
|
||||
}; // namespace GDBStub
|
||||
|
||||
@@ -91,4 +91,4 @@ bool GetCpuStepFlag();
|
||||
* @param is_step
|
||||
*/
|
||||
void SetCpuStepFlag(bool is_step);
|
||||
}
|
||||
} // namespace GDBStub
|
||||
|
||||
@@ -28,4 +28,4 @@ void Init() {
|
||||
config_mem.firm_ctr_sdk_ver = 0x0000F297;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ConfigMem
|
||||
|
||||
@@ -53,4 +53,4 @@ extern ConfigMemDef config_mem;
|
||||
|
||||
void Init();
|
||||
|
||||
} // namespace
|
||||
} // namespace ConfigMem
|
||||
|
||||
@@ -19,7 +19,6 @@ constexpr size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
|
||||
// TODO(yuriks): These will probably go away once translation is implemented inside the kernel.
|
||||
constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS;
|
||||
|
||||
|
||||
enum class ControlCommand : u32 {
|
||||
ConvertSessionToDomain = 0,
|
||||
ConvertDomainToSession = 1,
|
||||
@@ -81,17 +80,21 @@ struct BufferDescriptorX {
|
||||
u32_le address_bits_0_31;
|
||||
|
||||
u32_le Counter() const {
|
||||
u32_le counter{ counter_bits_0_5 };
|
||||
u32_le counter{counter_bits_0_5};
|
||||
counter |= counter_bits_9_11 << 9;
|
||||
return counter;
|
||||
}
|
||||
|
||||
VAddr Address() const {
|
||||
VAddr address{ address_bits_0_31 };
|
||||
VAddr address{address_bits_0_31};
|
||||
address |= static_cast<VAddr>(address_bits_32_35) << 32;
|
||||
address |= static_cast<VAddr>(address_bits_36_38) << 36;
|
||||
return address;
|
||||
}
|
||||
|
||||
u64 Size() const {
|
||||
return static_cast<u64>(size);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(BufferDescriptorX) == 8, "BufferDescriptorX size is incorrect");
|
||||
|
||||
@@ -107,14 +110,14 @@ struct BufferDescriptorABW {
|
||||
};
|
||||
|
||||
VAddr Address() const {
|
||||
VAddr address{ address_bits_0_31 };
|
||||
VAddr address{address_bits_0_31};
|
||||
address |= static_cast<VAddr>(address_bits_32_35) << 32;
|
||||
address |= static_cast<VAddr>(address_bits_36_38) << 36;
|
||||
return address;
|
||||
}
|
||||
|
||||
u64 Size() const {
|
||||
u64 size{ size_bits_0_31 };
|
||||
u64 size{size_bits_0_31};
|
||||
size |= static_cast<u64>(size_bits_32_35) << 32;
|
||||
return size;
|
||||
}
|
||||
@@ -130,10 +133,14 @@ struct BufferDescriptorC {
|
||||
};
|
||||
|
||||
VAddr Address() const {
|
||||
VAddr address{ address_bits_0_31 };
|
||||
VAddr address{address_bits_0_31};
|
||||
address |= static_cast<VAddr>(address_bits_32_47) << 32;
|
||||
return address;
|
||||
}
|
||||
|
||||
u64 Size() const {
|
||||
return static_cast<u64>(size);
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(BufferDescriptorC) == 8, "BufferDescriptorC size is incorrect");
|
||||
|
||||
@@ -144,6 +151,11 @@ struct DataPayloadHeader {
|
||||
static_assert(sizeof(DataPayloadHeader) == 8, "DataPayloadRequest size is incorrect");
|
||||
|
||||
struct DomainMessageHeader {
|
||||
enum class CommandType : u32_le {
|
||||
SendMessage = 1,
|
||||
CloseVirtualHandle = 2,
|
||||
};
|
||||
|
||||
union {
|
||||
// Used when responding to an IPC request, Server -> Client.
|
||||
struct {
|
||||
@@ -154,7 +166,7 @@ struct DomainMessageHeader {
|
||||
// Used when performing an IPC request, Client -> Server.
|
||||
struct {
|
||||
union {
|
||||
BitField<0, 8, u32_le> command;
|
||||
BitField<0, 8, CommandType> command;
|
||||
BitField<16, 16, u32_le> size;
|
||||
};
|
||||
u32_le object_id;
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include "core/hle/ipc.h"
|
||||
#include "core/hle/kernel/domain.h"
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/client_session.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/server_port.h"
|
||||
|
||||
namespace IPC {
|
||||
|
||||
@@ -28,11 +30,6 @@ public:
|
||||
RequestHelperBase(Kernel::HLERequestContext& context)
|
||||
: context(&context), cmdbuf(context.CommandBuffer()) {}
|
||||
|
||||
void ValidateHeader() {
|
||||
// DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)",
|
||||
// header.raw);
|
||||
}
|
||||
|
||||
void Skip(unsigned size_in_words, bool set_to_null) {
|
||||
if (set_to_null)
|
||||
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
|
||||
@@ -51,25 +48,59 @@ public:
|
||||
unsigned GetCurrentOffset() const {
|
||||
return static_cast<unsigned>(index);
|
||||
}
|
||||
|
||||
void SetCurrentOffset(unsigned offset) {
|
||||
index = static_cast<ptrdiff_t>(offset);
|
||||
}
|
||||
};
|
||||
|
||||
class RequestBuilder : public RequestHelperBase {
|
||||
class ResponseBuilder : public RequestHelperBase {
|
||||
public:
|
||||
RequestBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
|
||||
ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
|
||||
|
||||
u32 normal_params_size{};
|
||||
u32 num_handles_to_copy{};
|
||||
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
|
||||
std::ptrdiff_t datapayload_index{};
|
||||
|
||||
/// Flags used for customizing the behavior of ResponseBuilder
|
||||
enum class Flags : u32 {
|
||||
None = 0,
|
||||
/// Uses move handles to move objects in the response, even when in a domain. This is
|
||||
/// required when PushMoveObjects is used.
|
||||
AlwaysMoveHandles = 1,
|
||||
};
|
||||
|
||||
ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
|
||||
u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
|
||||
Flags flags = Flags::None)
|
||||
|
||||
: RequestHelperBase(context), normal_params_size(normal_params_size),
|
||||
num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) {
|
||||
|
||||
RequestBuilder(Kernel::HLERequestContext& context, unsigned normal_params_size,
|
||||
u32 num_handles_to_copy = 0, u32 num_handles_to_move = 0,
|
||||
u32 num_domain_objects = 0)
|
||||
: RequestHelperBase(context) {
|
||||
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
|
||||
|
||||
context.ClearIncomingObjects();
|
||||
|
||||
IPC::CommandHeader header{};
|
||||
|
||||
// The entire size of the raw data section in u32 units, including the 16 bytes of mandatory
|
||||
// padding.
|
||||
u32 raw_data_size = sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size;
|
||||
if (context.IsDomain())
|
||||
|
||||
u32 num_handles_to_move{};
|
||||
u32 num_domain_objects{};
|
||||
const bool always_move_handles{
|
||||
(static_cast<u32>(flags) & static_cast<u32>(Flags::AlwaysMoveHandles)) != 0};
|
||||
if (!context.Session()->IsDomain() || always_move_handles) {
|
||||
num_handles_to_move = num_objects_to_move;
|
||||
} else {
|
||||
num_domain_objects = num_objects_to_move;
|
||||
}
|
||||
|
||||
if (context.Session()->IsDomain()) {
|
||||
raw_data_size += sizeof(DomainMessageHeader) / 4 + num_domain_objects;
|
||||
}
|
||||
|
||||
header.data_size.Assign(raw_data_size);
|
||||
if (num_handles_to_copy || num_handles_to_move) {
|
||||
@@ -87,7 +118,8 @@ public:
|
||||
|
||||
AlignWithPadding();
|
||||
|
||||
if (context.IsDomain()) {
|
||||
const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr};
|
||||
if (context.Session()->IsDomain() && request_has_domain_header) {
|
||||
IPC::DomainMessageHeader domain_header{};
|
||||
domain_header.num_objects = num_domain_objects;
|
||||
PushRaw(domain_header);
|
||||
@@ -96,15 +128,43 @@ public:
|
||||
IPC::DataPayloadHeader data_payload_header{};
|
||||
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
|
||||
PushRaw(data_payload_header);
|
||||
|
||||
datapayload_index = index;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void PushIpcInterface(std::shared_ptr<T> iface) {
|
||||
if (context->Session()->IsDomain()) {
|
||||
context->AddDomainObject(std::move(iface));
|
||||
} else {
|
||||
auto sessions = Kernel::ServerSession::CreateSessionPair(iface->GetServiceName());
|
||||
auto server = std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions);
|
||||
auto client = std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions);
|
||||
iface->ClientConnected(server);
|
||||
context->AddMoveObject(std::move(client));
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, class... Args>
|
||||
void PushIpcInterface(Args&&... args) {
|
||||
context->AddDomainObject(std::make_shared<T>(std::forward<Args>(args)...));
|
||||
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
void ValidateHeader() {
|
||||
const size_t num_domain_objects = context->NumDomainObjects();
|
||||
const size_t num_move_objects = context->NumMoveObjects();
|
||||
ASSERT_MSG(!num_domain_objects || !num_move_objects,
|
||||
"cannot move normal handles and domain objects");
|
||||
ASSERT_MSG((index - datapayload_index) == normal_params_size,
|
||||
"normal_params_size value is incorrect");
|
||||
ASSERT_MSG((num_domain_objects + num_move_objects) == num_objects_to_move,
|
||||
"num_objects_to_move value is incorrect");
|
||||
ASSERT_MSG(context->NumCopyObjects() == num_handles_to_copy,
|
||||
"num_handles_to_copy value is incorrect");
|
||||
}
|
||||
|
||||
// Validate on destruction, as there shouldn't be any case where we don't want it
|
||||
~RequestBuilder() {
|
||||
~ResponseBuilder() {
|
||||
ValidateHeader();
|
||||
}
|
||||
|
||||
@@ -132,52 +192,52 @@ public:
|
||||
/// Push ///
|
||||
|
||||
template <>
|
||||
inline void RequestBuilder::Push(u32 value) {
|
||||
inline void ResponseBuilder::Push(u32 value) {
|
||||
cmdbuf[index++] = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void RequestBuilder::PushRaw(const T& value) {
|
||||
void ResponseBuilder::PushRaw(const T& value) {
|
||||
std::memcpy(cmdbuf + index, &value, sizeof(T));
|
||||
index += (sizeof(T) + 3) / 4; // round up to word length
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void RequestBuilder::Push(ResultCode value) {
|
||||
inline void ResponseBuilder::Push(ResultCode value) {
|
||||
// Result codes are actually 64-bit in the IPC buffer, but only the high part is discarded.
|
||||
Push(value.raw);
|
||||
Push<u32>(0);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void RequestBuilder::Push(u8 value) {
|
||||
inline void ResponseBuilder::Push(u8 value) {
|
||||
PushRaw(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void RequestBuilder::Push(u16 value) {
|
||||
inline void ResponseBuilder::Push(u16 value) {
|
||||
PushRaw(value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void RequestBuilder::Push(u64 value) {
|
||||
inline void ResponseBuilder::Push(u64 value) {
|
||||
Push(static_cast<u32>(value));
|
||||
Push(static_cast<u32>(value >> 32));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void RequestBuilder::Push(bool value) {
|
||||
inline void ResponseBuilder::Push(bool value) {
|
||||
Push(static_cast<u8>(value));
|
||||
}
|
||||
|
||||
template <typename First, typename... Other>
|
||||
void RequestBuilder::Push(const First& first_value, const Other&... other_values) {
|
||||
void ResponseBuilder::Push(const First& first_value, const Other&... other_values) {
|
||||
Push(first_value);
|
||||
Push(other_values...);
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void RequestBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) {
|
||||
inline void ResponseBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddCopyObject(std::move(object));
|
||||
@@ -185,7 +245,7 @@ inline void RequestBuilder::PushCopyObjects(Kernel::SharedPtr<O>... pointers) {
|
||||
}
|
||||
|
||||
template <typename... O>
|
||||
inline void RequestBuilder::PushMoveObjects(Kernel::SharedPtr<O>... pointers) {
|
||||
inline void ResponseBuilder::PushMoveObjects(Kernel::SharedPtr<O>... pointers) {
|
||||
auto objects = {pointers...};
|
||||
for (auto& object : objects) {
|
||||
context->AddMoveObject(std::move(object));
|
||||
@@ -204,15 +264,10 @@ public:
|
||||
Skip(CommandIdSize, false);
|
||||
}
|
||||
|
||||
RequestBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy,
|
||||
u32 num_handles_to_move, u32 num_domain_objects,
|
||||
bool validate_header = true) {
|
||||
if (validate_header) {
|
||||
ValidateHeader();
|
||||
}
|
||||
|
||||
return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move,
|
||||
num_domain_objects};
|
||||
ResponseBuilder MakeBuilder(u32 normal_params_size, u32 num_handles_to_copy,
|
||||
u32 num_handles_to_move,
|
||||
ResponseBuilder::Flags flags = ResponseBuilder::Flags::None) {
|
||||
return {*context, normal_params_size, num_handles_to_copy, num_handles_to_move, flags};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -282,6 +337,11 @@ inline u64 RequestParser::Pop() {
|
||||
return msw << 32 | lsw;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline s64 RequestParser::Pop() {
|
||||
return static_cast<s64>(Pop<u64>());
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool RequestParser::Pop() {
|
||||
return Pop<u8>() != 0;
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/errors.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Kernel namespace
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
AddressArbiter::AddressArbiter() {}
|
||||
AddressArbiter::~AddressArbiter() {}
|
||||
|
||||
SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) {
|
||||
SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter);
|
||||
|
||||
address_arbiter->name = std::move(name);
|
||||
|
||||
return address_arbiter;
|
||||
}
|
||||
|
||||
ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value,
|
||||
u64 nanoseconds) {
|
||||
switch (type) {
|
||||
|
||||
// Signal thread(s) waiting for arbitrate address...
|
||||
case ArbitrationType::Signal:
|
||||
// Negative value means resume all threads
|
||||
if (value < 0) {
|
||||
ArbitrateAllThreads(address);
|
||||
} else {
|
||||
// Resume first N threads
|
||||
for (int i = 0; i < value; i++)
|
||||
ArbitrateHighestPriorityThread(address);
|
||||
}
|
||||
break;
|
||||
|
||||
// Wait current thread (acquire the arbiter)...
|
||||
case ArbitrationType::WaitIfLessThan:
|
||||
if ((s32)Memory::Read32(address) < value) {
|
||||
Kernel::WaitCurrentThread_ArbitrateAddress(address);
|
||||
}
|
||||
break;
|
||||
case ArbitrationType::WaitIfLessThanWithTimeout:
|
||||
if ((s32)Memory::Read32(address) < value) {
|
||||
Kernel::WaitCurrentThread_ArbitrateAddress(address);
|
||||
GetCurrentThread()->WakeAfterDelay(nanoseconds);
|
||||
}
|
||||
break;
|
||||
case ArbitrationType::DecrementAndWaitIfLessThan: {
|
||||
s32 memory_value = Memory::Read32(address);
|
||||
if (memory_value < value) {
|
||||
// Only change the memory value if the thread should wait
|
||||
Memory::Write32(address, (s32)memory_value - 1);
|
||||
Kernel::WaitCurrentThread_ArbitrateAddress(address);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: {
|
||||
s32 memory_value = Memory::Read32(address);
|
||||
if (memory_value < value) {
|
||||
// Only change the memory value if the thread should wait
|
||||
Memory::Write32(address, (s32)memory_value - 1);
|
||||
Kernel::WaitCurrentThread_ArbitrateAddress(address);
|
||||
GetCurrentThread()->WakeAfterDelay(nanoseconds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
LOG_ERROR(Kernel, "unknown type=%d", type);
|
||||
return ERR_INVALID_ENUM_VALUE_FND;
|
||||
}
|
||||
|
||||
// The calls that use a timeout seem to always return a Timeout error even if they did not put
|
||||
// the thread to sleep
|
||||
if (type == ArbitrationType::WaitIfLessThanWithTimeout ||
|
||||
type == ArbitrationType::DecrementAndWaitIfLessThanWithTimeout) {
|
||||
|
||||
return RESULT_TIMEOUT;
|
||||
}
|
||||
return RESULT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -1,60 +0,0 @@
|
||||
// Copyright 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
// Address arbiters are an underlying kernel synchronization object that can be created/used via
|
||||
// supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR
|
||||
// applications use them as an underlying mechanism to implement thread-safe barriers, events, and
|
||||
// semphores.
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Kernel namespace
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
enum class ArbitrationType : u32 {
|
||||
Signal,
|
||||
WaitIfLessThan,
|
||||
DecrementAndWaitIfLessThan,
|
||||
WaitIfLessThanWithTimeout,
|
||||
DecrementAndWaitIfLessThanWithTimeout,
|
||||
};
|
||||
|
||||
class AddressArbiter final : public Object {
|
||||
public:
|
||||
/**
|
||||
* Creates an address arbiter.
|
||||
*
|
||||
* @param name Optional name used for debugging.
|
||||
* @returns The created AddressArbiter.
|
||||
*/
|
||||
static SharedPtr<AddressArbiter> Create(std::string name = "Unknown");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "Arbiter";
|
||||
}
|
||||
std::string GetName() const override {
|
||||
return name;
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::AddressArbiter;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
std::string name; ///< Name of address arbiter object (optional)
|
||||
|
||||
ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds);
|
||||
|
||||
private:
|
||||
AddressArbiter();
|
||||
~AddressArbiter() override;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
||||
@@ -39,4 +39,4 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
|
||||
return MakeResult(std::get<SharedPtr<ClientSession>>(sessions));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -47,4 +47,4 @@ private:
|
||||
~ClientPort() override;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -48,4 +48,4 @@ ResultCode ClientSession::SendSyncRequest(SharedPtr<Thread> thread) {
|
||||
return server->HandleSyncRequest(std::move(thread));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/sync_object.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
@@ -16,7 +16,7 @@ class ServerSession;
|
||||
class Session;
|
||||
class Thread;
|
||||
|
||||
class ClientSession final : public SyncObject {
|
||||
class ClientSession final : public Object {
|
||||
public:
|
||||
friend class ServerSession;
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
ResultCode SendSyncRequest(SharedPtr<Thread> thread) override;
|
||||
ResultCode SendSyncRequest(SharedPtr<Thread> thread);
|
||||
|
||||
std::string name; ///< Name of client port (optional)
|
||||
|
||||
@@ -45,4 +45,4 @@ private:
|
||||
~ClientSession() override;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -15,13 +15,12 @@ ConditionVariable::ConditionVariable() {}
|
||||
ConditionVariable::~ConditionVariable() {}
|
||||
|
||||
ResultVal<SharedPtr<ConditionVariable>> ConditionVariable::Create(VAddr guest_addr,
|
||||
VAddr mutex_addr,
|
||||
std::string name) {
|
||||
SharedPtr<ConditionVariable> condition_variable(new ConditionVariable);
|
||||
|
||||
condition_variable->name = std::move(name);
|
||||
condition_variable->guest_addr = guest_addr;
|
||||
condition_variable->mutex_addr = mutex_addr;
|
||||
condition_variable->mutex_addr = 0;
|
||||
|
||||
// Condition variables are referenced by guest address, so track this in the kernel
|
||||
g_object_address_table.Insert(guest_addr, condition_variable);
|
||||
@@ -43,7 +42,7 @@ void ConditionVariable::Acquire(Thread* thread) {
|
||||
ResultCode ConditionVariable::Release(s32 target) {
|
||||
if (target == -1) {
|
||||
// When -1, wake up all waiting threads
|
||||
SetAvailableCount(GetWaitingThreads().size());
|
||||
SetAvailableCount(static_cast<s32>(GetWaitingThreads().size()));
|
||||
WakeupAllWaitingThreads();
|
||||
} else {
|
||||
// Otherwise, wake up just a single thread
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <queue>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/kernel.h"
|
||||
#include "core/hle/kernel/wait_object.h"
|
||||
@@ -19,12 +19,10 @@ public:
|
||||
* Creates a condition variable.
|
||||
* @param guest_addr Address of the object tracking the condition variable in guest memory. If
|
||||
* specified, this condition variable will update the guest object when its state changes.
|
||||
* @param mutex_addr Optional address of a guest mutex associated with this condition variable,
|
||||
* used by the OS for implementing events.
|
||||
* @param name Optional name of condition variable.
|
||||
* @return The created condition variable.
|
||||
*/
|
||||
static ResultVal<SharedPtr<ConditionVariable>> Create(VAddr guest_addr, VAddr mutex_addr = 0,
|
||||
static ResultVal<SharedPtr<ConditionVariable>> Create(VAddr guest_addr,
|
||||
std::string name = "Unknown");
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/hle/kernel/client_port.h"
|
||||
#include "core/hle/kernel/domain.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
#include "core/hle/kernel/hle_ipc.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/kernel/session.h"
|
||||
#include "core/hle/kernel/thread.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
ResultVal<SharedPtr<Domain>> Domain::Create(std::string name) {
|
||||
SharedPtr<Domain> domain(new Domain);
|
||||
domain->name = std::move(name);
|
||||
return MakeResult(std::move(domain));
|
||||
}
|
||||
|
||||
ResultVal<SharedPtr<Domain>> Domain::CreateFromSession(const Session& session) {
|
||||
auto res = Create(session.port->GetName() + "_Domain");
|
||||
auto& domain = res.Unwrap();
|
||||
domain->request_handlers.push_back(std::move(session.server->hle_handler));
|
||||
Kernel::g_handle_table.ConvertSessionToDomain(session, domain);
|
||||
return res;
|
||||
}
|
||||
|
||||
ResultCode Domain::SendSyncRequest(SharedPtr<Thread> thread) {
|
||||
Kernel::HLERequestContext context(this);
|
||||
u32* cmd_buf = (u32*)Memory::GetPointer(Kernel::GetCurrentThread()->GetTLSAddress());
|
||||
context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
|
||||
Kernel::g_handle_table);
|
||||
|
||||
auto& domain_message_header = context.GetDomainMessageHeader();
|
||||
if (domain_message_header) {
|
||||
// If there is a DomainMessageHeader, then this is CommandType "Request"
|
||||
const u32 object_id{context.GetDomainMessageHeader()->object_id};
|
||||
return request_handlers[object_id - 1]->HandleSyncRequest(context);
|
||||
}
|
||||
return request_handlers.front()->HandleSyncRequest(context);
|
||||
}
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "core/hle/kernel/sync_object.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
class Session;
|
||||
class SessionRequestHandler;
|
||||
|
||||
class Domain final : public SyncObject {
|
||||
public:
|
||||
std::string GetTypeName() const override {
|
||||
return "Domain";
|
||||
}
|
||||
|
||||
static const HandleType HANDLE_TYPE = HandleType::Domain;
|
||||
HandleType GetHandleType() const override {
|
||||
return HANDLE_TYPE;
|
||||
}
|
||||
|
||||
static ResultVal<SharedPtr<Domain>> CreateFromSession(const Session& server);
|
||||
|
||||
ResultCode SendSyncRequest(SharedPtr<Thread> thread) override;
|
||||
|
||||
/// The name of this domain (optional)
|
||||
std::string name;
|
||||
|
||||
std::vector<std::shared_ptr<SessionRequestHandler>> request_handlers;
|
||||
|
||||
private:
|
||||
Domain() = default;
|
||||
~Domain() override = default;
|
||||
|
||||
static ResultVal<SharedPtr<Domain>> Create(std::string name = "Unknown");
|
||||
};
|
||||
|
||||
} // namespace Kernel
|
||||
@@ -52,4 +52,4 @@ void Event::WakeupAllWaitingThreads() {
|
||||
signaled = false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Kernel
|
||||
|
||||
@@ -49,4 +49,4 @@ private:
|
||||
~Event() override;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace Kernel
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user