Compare commits
1355 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e6311bfd2 | ||
|
|
6cc7656e81 | ||
|
|
efd956e6ff | ||
|
|
b8b90ce6e6 | ||
|
|
095c8d999b | ||
|
|
6ddf8f34db | ||
|
|
e6ee31a8e9 | ||
|
|
d3bfb102d8 | ||
|
|
d43769f93f | ||
|
|
1a5d6de0d4 | ||
|
|
e51d715700 | ||
|
|
fe292573de | ||
|
|
6f16826260 | ||
|
|
30dfd89126 | ||
|
|
f85f2b3728 | ||
|
|
baed7e1fba | ||
|
|
3e2380327a | ||
|
|
cf3a6dd4a1 | ||
|
|
af672d8abf | ||
|
|
839dbd9710 | ||
|
|
39f08e551d | ||
|
|
5ae5610c13 | ||
|
|
15b2e2ec13 | ||
|
|
e7e347a828 | ||
|
|
137d43fa2f | ||
|
|
8679934693 | ||
|
|
024eec02a5 | ||
|
|
dade709f63 | ||
|
|
02841052aa | ||
|
|
37ee05f7c0 | ||
|
|
ccf0a9cb38 | ||
|
|
fd312abedd | ||
|
|
4f18d35888 | ||
|
|
0d83f8f255 | ||
|
|
cf463a9b67 | ||
|
|
9aaf1c0df8 | ||
|
|
cba78dc70a | ||
|
|
d80dd2b812 | ||
|
|
61b144bbf3 | ||
|
|
1c7c798e9e | ||
|
|
57e47bae32 | ||
|
|
226dc914b3 | ||
|
|
8f5e2a2b83 | ||
|
|
215b65fe75 | ||
|
|
fdb35760a7 | ||
|
|
bfe84f06f2 | ||
|
|
a4595bb939 | ||
|
|
e4daf4bee5 | ||
|
|
ac06105dfe | ||
|
|
aa48468862 | ||
|
|
62f9409ba3 | ||
|
|
120d8f3bf7 | ||
|
|
b4ace6ec6f | ||
|
|
4d139943f2 | ||
|
|
5f30f95e94 | ||
|
|
6d441828e6 | ||
|
|
003b44822a | ||
|
|
42fb4e82d3 | ||
|
|
4c2a94fa94 | ||
|
|
21b2411c44 | ||
|
|
3c9e70fefa | ||
|
|
bd14f397ce | ||
|
|
393042c09c | ||
|
|
1835911425 | ||
|
|
bc679c9b8c | ||
|
|
7c61f0c322 | ||
|
|
8391048a83 | ||
|
|
decc319634 | ||
|
|
f72046099a | ||
|
|
7d86a008e2 | ||
|
|
62225ae050 | ||
|
|
aa0c82e405 | ||
|
|
32fc31fb13 | ||
|
|
8f2ad3a66d | ||
|
|
5e2f23e2b1 | ||
|
|
df3799a008 | ||
|
|
3031aa9d27 | ||
|
|
29782273ec | ||
|
|
f543b43fd0 | ||
|
|
15cc729ebd | ||
|
|
ed2e0e85c9 | ||
|
|
871580dcd8 | ||
|
|
a9aa1db552 | ||
|
|
2e1cdde994 | ||
|
|
fefb003b23 | ||
|
|
ce452049d3 | ||
|
|
b92b4bbeaf | ||
|
|
f8e46d335f | ||
|
|
56901912cb | ||
|
|
541c550753 | ||
|
|
dccfe193a9 | ||
|
|
cf9d6c6f52 | ||
|
|
16145e2f21 | ||
|
|
4092907687 | ||
|
|
334090fe6f | ||
|
|
a63e6f9dfd | ||
|
|
0921558a9f | ||
|
|
97c0ac3545 | ||
|
|
fe5962e073 | ||
|
|
8d8366b602 | ||
|
|
f7b69d61f2 | ||
|
|
f4c24d0832 | ||
|
|
e3e51d3ddb | ||
|
|
b8f1506aa5 | ||
|
|
f7da74d18e | ||
|
|
fc2419e441 | ||
|
|
29ff84ea99 | ||
|
|
a75740b298 | ||
|
|
62048edc15 | ||
|
|
8731ac87fa | ||
|
|
70e86248fd | ||
|
|
861580f6d2 | ||
|
|
7ecdaaf189 | ||
|
|
c0445006af | ||
|
|
efcb83fb41 | ||
|
|
cc866d1384 | ||
|
|
92dd496fb9 | ||
|
|
9d163c00cc | ||
|
|
11104b4883 | ||
|
|
ab65fde9f4 | ||
|
|
e3b2ef9170 | ||
|
|
4654f89618 | ||
|
|
91b56c4928 | ||
|
|
1f92cbc059 | ||
|
|
fa9e0f9c8b | ||
|
|
f646ca874d | ||
|
|
cbb146069a | ||
|
|
57616f9758 | ||
|
|
28bef31ea8 | ||
|
|
14e2df5610 | ||
|
|
e42bb5e003 | ||
|
|
7b81e1e525 | ||
|
|
598e4d2f6c | ||
|
|
a58eefa7e4 | ||
|
|
5873b14efd | ||
|
|
1008be9b67 | ||
|
|
b67c1fdf38 | ||
|
|
83377113bf | ||
|
|
6c6f95d071 | ||
|
|
7fd598636e | ||
|
|
75603b005b | ||
|
|
367c52ff0d | ||
|
|
2513e086ab | ||
|
|
f2c1fd08f9 | ||
|
|
b3c2ec362b | ||
|
|
2f6a611311 | ||
|
|
d42424ace0 | ||
|
|
9f3fc067bf | ||
|
|
6b05f71b67 | ||
|
|
5d9f001bb5 | ||
|
|
d373a65d26 | ||
|
|
c6f5c7d291 | ||
|
|
b91e2d55f3 | ||
|
|
c461188f51 | ||
|
|
fb6bc2c495 | ||
|
|
93fea4e179 | ||
|
|
05db48b3f8 | ||
|
|
e0ce07aa7d | ||
|
|
5659d5d258 | ||
|
|
b23b30f9ef | ||
|
|
f90b75cc4c | ||
|
|
fb9f273e90 | ||
|
|
73eea61614 | ||
|
|
0246db9ea2 | ||
|
|
2d5f51bb9b | ||
|
|
37bb2c45ee | ||
|
|
4f183123f5 | ||
|
|
ba0873d33c | ||
|
|
050547b801 | ||
|
|
6eada3c57d | ||
|
|
940a711caf | ||
|
|
50a470eab8 | ||
|
|
16188acb50 | ||
|
|
44fdac334c | ||
|
|
3e5c3d0f16 | ||
|
|
b52343a428 | ||
|
|
c65d4d119f | ||
|
|
f68e324672 | ||
|
|
d6cbb3a3e0 | ||
|
|
bd8db3f7f8 | ||
|
|
90746c33c7 | ||
|
|
19d9b0778a | ||
|
|
a8f5fd787f | ||
|
|
272517cf7e | ||
|
|
4f186de069 | ||
|
|
44228ee3ed | ||
|
|
57b44200a2 | ||
|
|
d2dd1289bd | ||
|
|
41c6c4593a | ||
|
|
f020319a45 | ||
|
|
68aaa83836 | ||
|
|
ba02dd9ebc | ||
|
|
48b2eda492 | ||
|
|
acfc801d14 | ||
|
|
1db1e013e0 | ||
|
|
8606995515 | ||
|
|
000da01c1d | ||
|
|
0285ddfbd4 | ||
|
|
072053ab95 | ||
|
|
a373dddbd9 | ||
|
|
8ba21e28cf | ||
|
|
9cd5c61fcf | ||
|
|
c1c59617ad | ||
|
|
cf97d00eb6 | ||
|
|
b0b57c21e6 | ||
|
|
a0db7e2cf9 | ||
|
|
bf2f2a715f | ||
|
|
9f92533cc2 | ||
|
|
b918925bd5 | ||
|
|
05aa4aa01a | ||
|
|
9b8fc2b689 | ||
|
|
f237265187 | ||
|
|
1e35d8fa8f | ||
|
|
74f966404d | ||
|
|
ef31487624 | ||
|
|
3f49725a51 | ||
|
|
cfc9fe4460 | ||
|
|
8fe9572271 | ||
|
|
0931a711de | ||
|
|
b02a1e38fa | ||
|
|
e57aa62b11 | ||
|
|
e980e90d6e | ||
|
|
583137709f | ||
|
|
335e9d18ae | ||
|
|
05ef9dfc10 | ||
|
|
3146502a12 | ||
|
|
05d49962b6 | ||
|
|
3b4a158230 | ||
|
|
321eb0b6b0 | ||
|
|
2e5f0e5024 | ||
|
|
2752183883 | ||
|
|
45195a51a7 | ||
|
|
c8c4105659 | ||
|
|
f272261c21 | ||
|
|
92a98a8b19 | ||
|
|
189cf0da7b | ||
|
|
a8f54f96fc | ||
|
|
0432af5ad1 | ||
|
|
52fb86c65c | ||
|
|
8dff92c5f6 | ||
|
|
5fbc354261 | ||
|
|
cbc7ad8f6d | ||
|
|
9bba103791 | ||
|
|
dfaea55be5 | ||
|
|
c8f9bbbf85 | ||
|
|
f465e4aaf2 | ||
|
|
d06f4cfc63 | ||
|
|
08819ec70a | ||
|
|
6a0612f2bf | ||
|
|
ab6dfa4fa5 | ||
|
|
c51f8563a6 | ||
|
|
b33ce787b7 | ||
|
|
d85130d7be | ||
|
|
c9942fe46e | ||
|
|
bf957d5345 | ||
|
|
c768535463 | ||
|
|
e9abbcae85 | ||
|
|
bd88d4108f | ||
|
|
0284cbe7ec | ||
|
|
6415f81bb8 | ||
|
|
9155c8daeb | ||
|
|
733c47623b | ||
|
|
c2cf784376 | ||
|
|
c7a4bf5074 | ||
|
|
de9604d63e | ||
|
|
1a2d33eeb4 | ||
|
|
a8b1c7763b | ||
|
|
717889e93c | ||
|
|
528e5cee67 | ||
|
|
f85ab0a123 | ||
|
|
ead2a4eeb4 | ||
|
|
b51e7e0288 | ||
|
|
71b48cb00f | ||
|
|
b6867602ca | ||
|
|
567a5524b9 | ||
|
|
9a8dbba1e5 | ||
|
|
fafc80d72e | ||
|
|
4a84986bc3 | ||
|
|
e4bb759c4b | ||
|
|
e2ac8fb36d | ||
|
|
aac77bbd18 | ||
|
|
f7308a6c25 | ||
|
|
2fd124bc93 | ||
|
|
1adbcd54fe | ||
|
|
31e52113b3 | ||
|
|
55a4756766 | ||
|
|
a94b623dfb | ||
|
|
2c9c0d70a3 | ||
|
|
2bfb9fd0e6 | ||
|
|
692e7cee4f | ||
|
|
27fe8159c5 | ||
|
|
fc46183e03 | ||
|
|
59beb540ae | ||
|
|
b91f7d5d67 | ||
|
|
e6367ab955 | ||
|
|
3476ba2aee | ||
|
|
e561afdcd5 | ||
|
|
e7af0f69da | ||
|
|
076add4ccd | ||
|
|
3be048e50a | ||
|
|
e833301e4c | ||
|
|
08af788a57 | ||
|
|
240e756962 | ||
|
|
9dbe9d4291 | ||
|
|
13930f0c33 | ||
|
|
51b5619079 | ||
|
|
2845348608 | ||
|
|
ba480ea2fb | ||
|
|
daee15b058 | ||
|
|
0019a36b41 | ||
|
|
c878a819d7 | ||
|
|
54ddb37b3c | ||
|
|
3bd8563f2d | ||
|
|
63c2e32e20 | ||
|
|
f8e994354f | ||
|
|
fc669a97d4 | ||
|
|
b5218d9986 | ||
|
|
da64da367b | ||
|
|
4f8756edd0 | ||
|
|
accd1f17e4 | ||
|
|
ae128f0375 | ||
|
|
496c67fd73 | ||
|
|
7bd2faad9a | ||
|
|
df5a44a40b | ||
|
|
fb65076b0f | ||
|
|
9969a5db1e | ||
|
|
3ef134a092 | ||
|
|
50a5d09d32 | ||
|
|
0258b444ef | ||
|
|
2ea45fe75b | ||
|
|
a0e51d8b98 | ||
|
|
8e7497d5bb | ||
|
|
2ec9fbc2d4 | ||
|
|
a42376dfad | ||
|
|
ce97d8ef6c | ||
|
|
60899b80f0 | ||
|
|
938aa5779c | ||
|
|
926dd41587 | ||
|
|
49c4fe1f2f | ||
|
|
4a43fb7e1d | ||
|
|
d9e21eebe8 | ||
|
|
cc50857460 | ||
|
|
bb5eb4f20a | ||
|
|
957ddab679 | ||
|
|
a99d9db32f | ||
|
|
79217f9870 | ||
|
|
0821a210c4 | ||
|
|
44fac34697 | ||
|
|
9f2bcdbb76 | ||
|
|
fbe462099b | ||
|
|
3c5c292592 | ||
|
|
9b3bc0b282 | ||
|
|
04d723baf9 | ||
|
|
e89c22c147 | ||
|
|
a9b953e6d4 | ||
|
|
3a2567c97c | ||
|
|
475222a496 | ||
|
|
3ee4fa557f | ||
|
|
c245150439 | ||
|
|
89825766ee | ||
|
|
522a11a11f | ||
|
|
4a9acc87f9 | ||
|
|
7bb226f22d | ||
|
|
0e61e8362f | ||
|
|
7fe10dea3e | ||
|
|
c243bc09d4 | ||
|
|
bad035e9a3 | ||
|
|
c061e27155 | ||
|
|
429217248f | ||
|
|
325c259fc5 | ||
|
|
cdddd71d08 | ||
|
|
3e973bc4c6 | ||
|
|
5c676dc884 | ||
|
|
3f0922715a | ||
|
|
2b48cfd44b | ||
|
|
e926757c8f | ||
|
|
1470b85af9 | ||
|
|
2f0ff4d25b | ||
|
|
143525dcb9 | ||
|
|
46ba1bc40f | ||
|
|
6ac955a0b4 | ||
|
|
4c3bd33be2 | ||
|
|
3bac3051fc | ||
|
|
804115b2a4 | ||
|
|
d6e8e16a66 | ||
|
|
12445b476d | ||
|
|
d884e805c5 | ||
|
|
4bea6657ef | ||
|
|
ae0c95efcc | ||
|
|
c1b8cd9058 | ||
|
|
0cfb0bacb2 | ||
|
|
d2f788762a | ||
|
|
17f8059fea | ||
|
|
c560043581 | ||
|
|
4c0b1cc1ae | ||
|
|
035e6bd407 | ||
|
|
1c34498368 | ||
|
|
ac959799e4 | ||
|
|
49b15af054 | ||
|
|
f9e468d891 | ||
|
|
7ddd5b765d | ||
|
|
50c191439d | ||
|
|
3b8a0bc146 | ||
|
|
136040ee15 | ||
|
|
e58855c7a4 | ||
|
|
00131e752d | ||
|
|
223ddb2008 | ||
|
|
fcf81147e7 | ||
|
|
73a2d71f44 | ||
|
|
bd8065295c | ||
|
|
0acf9b351f | ||
|
|
073a21ac0b | ||
|
|
64e45b04e0 | ||
|
|
55af5bda55 | ||
|
|
3dcedb36b4 | ||
|
|
6d64ecf359 | ||
|
|
500e81429a | ||
|
|
5ff72a48a7 | ||
|
|
82a313a14c | ||
|
|
fdb199290b | ||
|
|
af074ee422 | ||
|
|
deff28d3c0 | ||
|
|
3d9776f36a | ||
|
|
1aa195a9c0 | ||
|
|
e51bd49f87 | ||
|
|
7e697ab7ff | ||
|
|
6d9dd1dc6d | ||
|
|
112351d557 | ||
|
|
a6efff8b02 | ||
|
|
a76f0d5d06 | ||
|
|
4048b54ef7 | ||
|
|
9cd79c25ed | ||
|
|
2515d2433b | ||
|
|
8b08cb925b | ||
|
|
975226e7ff | ||
|
|
a8974f0556 | ||
|
|
23ae7cf9db | ||
|
|
fdd5c97a14 | ||
|
|
f165a85398 | ||
|
|
0731383124 | ||
|
|
05f6f59ffb | ||
|
|
ce8291f6c5 | ||
|
|
9dccf7e1fa | ||
|
|
030676b95d | ||
|
|
a439f7b6e1 | ||
|
|
b56e5edafc | ||
|
|
460ebc8187 | ||
|
|
6ac1bd9f5d | ||
|
|
7e9b79955f | ||
|
|
564b7fdc9c | ||
|
|
c08c5d346a | ||
|
|
9382414b20 | ||
|
|
e3af341d5b | ||
|
|
3f17fe7133 | ||
|
|
a164b413fa | ||
|
|
9273c02427 | ||
|
|
b89dda2b98 | ||
|
|
9947c6ad59 | ||
|
|
9b50dca2bb | ||
|
|
009a2cc9cc | ||
|
|
6faf1b0972 | ||
|
|
820f646458 | ||
|
|
948f6c0738 | ||
|
|
ddcdbce067 | ||
|
|
8d685a29bc | ||
|
|
14230fe2af | ||
|
|
68296d9474 | ||
|
|
8f4e09ba07 | ||
|
|
56ab608044 | ||
|
|
54724fe918 | ||
|
|
b155b3ef81 | ||
|
|
a859a35ec8 | ||
|
|
fbaefc47a0 | ||
|
|
742f895f8b | ||
|
|
a781042700 | ||
|
|
77554ac773 | ||
|
|
6f09c5b128 | ||
|
|
e63b229f4a | ||
|
|
94f193af65 | ||
|
|
a6ae765410 | ||
|
|
aba988f71c | ||
|
|
7f15306f78 | ||
|
|
6bd6beee20 | ||
|
|
d3ad9469a1 | ||
|
|
c0b7ed8b58 | ||
|
|
527e362a83 | ||
|
|
50a806ea67 | ||
|
|
a1ef02c3e6 | ||
|
|
c913136eb2 | ||
|
|
7d5d781b20 | ||
|
|
23a16c1720 | ||
|
|
92e26df00f | ||
|
|
c91b60a421 | ||
|
|
cbd517d8cc | ||
|
|
2814ca3624 | ||
|
|
a6e75cd45b | ||
|
|
9664ce255d | ||
|
|
f92b3512e0 | ||
|
|
8e150c46b9 | ||
|
|
f5e03b9173 | ||
|
|
08fcb4694f | ||
|
|
97bf83bc56 | ||
|
|
8e900a301a | ||
|
|
54e7ddb93a | ||
|
|
1efe5a76b1 | ||
|
|
9951f6d054 | ||
|
|
d2caf4af7d | ||
|
|
99fbcb3bf2 | ||
|
|
faa9e066ab | ||
|
|
99a71580c4 | ||
|
|
87be4bc283 | ||
|
|
e973cceadd | ||
|
|
23d2c50479 | ||
|
|
1280061725 | ||
|
|
8974771334 | ||
|
|
e4e55d064e | ||
|
|
58473309a0 | ||
|
|
f7eaea424d | ||
|
|
d7518cf6e0 | ||
|
|
5c8aff984e | ||
|
|
93703431e2 | ||
|
|
a040929c90 | ||
|
|
b555311438 | ||
|
|
d770c60205 | ||
|
|
dda4b5e89e | ||
|
|
9a07e9f805 | ||
|
|
ed37b68fb5 | ||
|
|
26e96d16d0 | ||
|
|
8ec1e16867 | ||
|
|
5a29b358aa | ||
|
|
c156ee8eb8 | ||
|
|
6ef84f1c4c | ||
|
|
2949d9552c | ||
|
|
978f3a3282 | ||
|
|
dce624e3f1 | ||
|
|
2081ed7db2 | ||
|
|
10bc725944 | ||
|
|
a813c10e1c | ||
|
|
1c5636e690 | ||
|
|
2afe8ac4a7 | ||
|
|
04397cd185 | ||
|
|
1ff3318458 | ||
|
|
b2268f1f8d | ||
|
|
9cfe2414cb | ||
|
|
c6fd56b00f | ||
|
|
18a89931a9 | ||
|
|
a405373144 | ||
|
|
1242c1ec0a | ||
|
|
41cd766438 | ||
|
|
325f3e0693 | ||
|
|
89be49d2f3 | ||
|
|
2714d9e64c | ||
|
|
d2ade27c3f | ||
|
|
177c45e97d | ||
|
|
9c206fe94d | ||
|
|
1ccc0457d5 | ||
|
|
7a439630bb | ||
|
|
fda8f1da20 | ||
|
|
0f453488e2 | ||
|
|
a0e1fbfe14 | ||
|
|
60754b4728 | ||
|
|
2edab4e840 | ||
|
|
2bc6abb9a1 | ||
|
|
6f8ed9508d | ||
|
|
b89fc407d7 | ||
|
|
948bc87a59 | ||
|
|
ad3dca7e62 | ||
|
|
f32e28c7b8 | ||
|
|
19d0951ae6 | ||
|
|
7939ea18e8 | ||
|
|
c69dc5acf9 | ||
|
|
1c05c06e04 | ||
|
|
4a587b81b2 | ||
|
|
dc3cc0002c | ||
|
|
42588493d5 | ||
|
|
7f7eb29323 | ||
|
|
123c065086 | ||
|
|
9bc71fcc5f | ||
|
|
d647d9550c | ||
|
|
16d65182f9 | ||
|
|
06578e89b2 | ||
|
|
f08d24e9c0 | ||
|
|
6683bf50b5 | ||
|
|
e205e74e1f | ||
|
|
e2457418da | ||
|
|
5be8b7a362 | ||
|
|
d626bc8c62 | ||
|
|
26aaa86ece | ||
|
|
915ab81ec2 | ||
|
|
d6accf96ff | ||
|
|
5094dfa081 | ||
|
|
42ef40884f | ||
|
|
6e73039eb5 | ||
|
|
a6dd577d02 | ||
|
|
78653f7339 | ||
|
|
02dfbf961e | ||
|
|
a2c97de929 | ||
|
|
b1ccd88434 | ||
|
|
0cbcd6ec9a | ||
|
|
4d7e1662c8 | ||
|
|
eb4f2d5596 | ||
|
|
d8ba202070 | ||
|
|
72e4499a9e | ||
|
|
2e7dc4cac9 | ||
|
|
45fb74d262 | ||
|
|
6771a18c6c | ||
|
|
0d2435343a | ||
|
|
f1bc62bb4c | ||
|
|
4a56931703 | ||
|
|
ffe2336136 | ||
|
|
b4ac8218d0 | ||
|
|
9a6bfc55f3 | ||
|
|
a409d49bbd | ||
|
|
b55d8111e6 | ||
|
|
a0e1566dc5 | ||
|
|
382852418b | ||
|
|
2f5ed3877c | ||
|
|
90fd03015a | ||
|
|
2562fe4a16 | ||
|
|
62edc01525 | ||
|
|
5d2043598e | ||
|
|
c6024379a4 | ||
|
|
d3934d7da7 | ||
|
|
887a9c5c29 | ||
|
|
af59d4bff0 | ||
|
|
f96ded9815 | ||
|
|
8c66a5a9a5 | ||
|
|
34a447d24e | ||
|
|
8d86747514 | ||
|
|
43a2598e26 | ||
|
|
d26a46feed | ||
|
|
be2f1eabd7 | ||
|
|
23b86fd3ea | ||
|
|
f708207ae6 | ||
|
|
bfb0c87b7b | ||
|
|
81ca46dd17 | ||
|
|
b8be5524bc | ||
|
|
2fd45093f2 | ||
|
|
f170159fde | ||
|
|
e81354ae38 | ||
|
|
6426b0f551 | ||
|
|
6314a799aa | ||
|
|
43e0d865fa | ||
|
|
c65713832c | ||
|
|
1e6a209649 | ||
|
|
b6425c0511 | ||
|
|
20800f2df7 | ||
|
|
f09da5d1c9 | ||
|
|
8492ec1669 | ||
|
|
c74b7ee204 | ||
|
|
36093a3e4d | ||
|
|
ec59e4a6c5 | ||
|
|
8fd9eb71b4 | ||
|
|
018c25e123 | ||
|
|
f6f5c2e4d8 | ||
|
|
165c23c848 | ||
|
|
d1a6dd61d1 | ||
|
|
4f18c17df7 | ||
|
|
5049ca5d8c | ||
|
|
ba2972bc64 | ||
|
|
06487c2c8d | ||
|
|
67fa51ea2f | ||
|
|
78b109d195 | ||
|
|
0dce6d7008 | ||
|
|
3ed0115e36 | ||
|
|
ccfd176382 | ||
|
|
119ab308b5 | ||
|
|
a7e8d10969 | ||
|
|
42dc856ce1 | ||
|
|
61a5b56abd | ||
|
|
f26fc64cb4 | ||
|
|
cde665c565 | ||
|
|
60b7a3b904 | ||
|
|
ab44192ab0 | ||
|
|
8b52d6682a | ||
|
|
13524578b6 | ||
|
|
4112dd6b4e | ||
|
|
bf33f80fae | ||
|
|
ef3768f323 | ||
|
|
410062031b | ||
|
|
b247e0cab0 | ||
|
|
2164702cf7 | ||
|
|
c4845df3d4 | ||
|
|
10e5356e9a | ||
|
|
6dd369ab88 | ||
|
|
a9dc5a3c10 | ||
|
|
d65f079cc1 | ||
|
|
fee8bdd90c | ||
|
|
fde2017a3f | ||
|
|
ebf5768340 | ||
|
|
a4ac3bed6c | ||
|
|
da3da6be90 | ||
|
|
2a472ff54d | ||
|
|
c4ed0b16b1 | ||
|
|
c7f2fb2151 | ||
|
|
232b0d9d2a | ||
|
|
eccc77a8c8 | ||
|
|
74e08b4800 | ||
|
|
a1bdc597e9 | ||
|
|
c5ea6db02d | ||
|
|
c7c4e6dcba | ||
|
|
c5c0da41b4 | ||
|
|
f835349364 | ||
|
|
12ba80a86c | ||
|
|
1fd979f50a | ||
|
|
b2ca8089ce | ||
|
|
e70a3c5a5d | ||
|
|
d1b1c42c07 | ||
|
|
dd35b4b18a | ||
|
|
4877e6c2f6 | ||
|
|
8e8326595f | ||
|
|
8ce02d85e9 | ||
|
|
b38d67d940 | ||
|
|
cea627b0fc | ||
|
|
5abf71fe65 | ||
|
|
eef0c93643 | ||
|
|
125d7122ac | ||
|
|
ad1220e1b3 | ||
|
|
92b85fad70 | ||
|
|
cb8b371570 | ||
|
|
38517241ec | ||
|
|
15cc34b93e | ||
|
|
99fc32428a | ||
|
|
d63b1d21f1 | ||
|
|
ac68c8a605 | ||
|
|
c2695aa2eb | ||
|
|
16b83fac9b | ||
|
|
a769d8c913 | ||
|
|
a0e2bd85a5 | ||
|
|
29ac15d1b8 | ||
|
|
0057a47e41 | ||
|
|
5a53d75313 | ||
|
|
8dd9cb98ce | ||
|
|
c95c4442e9 | ||
|
|
37f2ec6fc2 | ||
|
|
624239ed6b | ||
|
|
5678ec0dd0 | ||
|
|
3f4fb4b037 | ||
|
|
bfb28c5b3f | ||
|
|
f2d5b100c2 | ||
|
|
6923ecee3a | ||
|
|
36090521ce | ||
|
|
cc71832b19 | ||
|
|
bf89a99839 | ||
|
|
79243b6fa0 | ||
|
|
b0f7713fce | ||
|
|
8c9abe1d41 | ||
|
|
ca58929eb0 | ||
|
|
523e4be02c | ||
|
|
fde3b1b6f2 | ||
|
|
477eee3993 | ||
|
|
93a4097e9d | ||
|
|
e3bddf8137 | ||
|
|
c4ce7e456a | ||
|
|
e33452f7e8 | ||
|
|
5aaee2ff8d | ||
|
|
2ae88feea7 | ||
|
|
16db8b9d9f | ||
|
|
948002635f | ||
|
|
ea99819f37 | ||
|
|
eac3cf301c | ||
|
|
fc5b489b0f | ||
|
|
19b05c3f55 | ||
|
|
2788144f46 | ||
|
|
96463d0a55 | ||
|
|
dd70ddad7e | ||
|
|
c0fb321935 | ||
|
|
34f3d58470 | ||
|
|
b5fb246a99 | ||
|
|
c6fda4c758 | ||
|
|
609cb04f3f | ||
|
|
eb88fedc5d | ||
|
|
f5b132676f | ||
|
|
0fcdf37917 | ||
|
|
350f6e0aa4 | ||
|
|
9d8f19d7bf | ||
|
|
38cd4e9c61 | ||
|
|
f9a26d468c | ||
|
|
1277556c69 | ||
|
|
dfdf4a46fe | ||
|
|
69dd37d874 | ||
|
|
f13a66b963 | ||
|
|
2b9eee4d1e | ||
|
|
b1d238bbb8 | ||
|
|
f24ab6d9e6 | ||
|
|
46ef072cf9 | ||
|
|
6bcdf37d4f | ||
|
|
bc16f7f3cc | ||
|
|
ba8ff096fd | ||
|
|
7784ce1854 | ||
|
|
e8cb6f5c9b | ||
|
|
9e9a4bb3a7 | ||
|
|
d7c68fbb12 | ||
|
|
3fe77be392 | ||
|
|
028d90eb79 | ||
|
|
296e57fa0e | ||
|
|
b20ed93884 | ||
|
|
185b35bfcd | ||
|
|
943771e703 | ||
|
|
ce4b77bd7d | ||
|
|
6ee8b15abe | ||
|
|
23d45715dc | ||
|
|
ffd60ee476 | ||
|
|
8a88110060 | ||
|
|
6cf719a4ab | ||
|
|
51ddb130c5 | ||
|
|
9b17486be6 | ||
|
|
0a1d4fbc5c | ||
|
|
f7edbcd7a3 | ||
|
|
b0eb580931 | ||
|
|
85da529f15 | ||
|
|
7fb406c3fc | ||
|
|
3ef4b3d4b4 | ||
|
|
73b937b190 | ||
|
|
656758fd81 | ||
|
|
29d4f8c2dd | ||
|
|
9baf5de90c | ||
|
|
d6cb22b0df | ||
|
|
1b92ae136f | ||
|
|
731701a2d2 | ||
|
|
706fc5d2d6 | ||
|
|
27da7bc9da | ||
|
|
9b1c49a9cf | ||
|
|
e0f66c1fbf | ||
|
|
8335b2f115 | ||
|
|
367feaefa0 | ||
|
|
71cc482bbd | ||
|
|
ff358d97e8 | ||
|
|
2e95ba2e9c | ||
|
|
6eba539f4a | ||
|
|
63dff47e22 | ||
|
|
504cff2b7a | ||
|
|
804aebf7c7 | ||
|
|
2003771789 | ||
|
|
f246fd778d | ||
|
|
1db7839f11 | ||
|
|
224be09d66 | ||
|
|
e341d868ee | ||
|
|
da7226442f | ||
|
|
727136a9c9 | ||
|
|
0d9b3e425e | ||
|
|
ce56b8e1fa | ||
|
|
cef35e7c9c | ||
|
|
928e78dced | ||
|
|
3fd78f4d24 | ||
|
|
9791f0d590 | ||
|
|
2a3d7128d1 | ||
|
|
aac807fd3a | ||
|
|
a0ce6de913 | ||
|
|
94329038b6 | ||
|
|
24a759de4a | ||
|
|
b39cd70cd4 | ||
|
|
c594ec3417 | ||
|
|
89c3d6a2a3 | ||
|
|
c00b374e78 | ||
|
|
69236e5aff | ||
|
|
1dd27aff47 | ||
|
|
cee6a7ab55 | ||
|
|
a2fa37b499 | ||
|
|
f96de510ee | ||
|
|
91140f6c0a | ||
|
|
38592a3b5e | ||
|
|
40ecdda19e | ||
|
|
5ef447cc0e | ||
|
|
11c221cc62 | ||
|
|
40f83fee6a | ||
|
|
0bca5743ab | ||
|
|
aac5792a2b | ||
|
|
5752454629 | ||
|
|
87d8a9c986 | ||
|
|
0769aa594e | ||
|
|
c8e3f98c27 | ||
|
|
b74df62959 | ||
|
|
e0b0f4eece | ||
|
|
b1148d269d | ||
|
|
c5284efd4f | ||
|
|
8599e1e4fc | ||
|
|
3aad82b1a3 | ||
|
|
2a42dea568 | ||
|
|
c8cd1785e6 | ||
|
|
991eb4824c | ||
|
|
301baaa942 | ||
|
|
3db1b8e0cd | ||
|
|
8f9c49f7ee | ||
|
|
f009ed63f3 | ||
|
|
18dfa99030 | ||
|
|
6eda9ebbdb | ||
|
|
ad7815a28d | ||
|
|
409d2e07c2 | ||
|
|
8dc4407586 | ||
|
|
96c0b81a51 | ||
|
|
25d71454d1 | ||
|
|
5e66a24423 | ||
|
|
290439a6a5 | ||
|
|
dc876fd63a | ||
|
|
d8fd3ef4fe | ||
|
|
1c31cbad72 | ||
|
|
2e715ef70d | ||
|
|
319dbc5843 | ||
|
|
60f476cd8f | ||
|
|
6d549abb4e | ||
|
|
0ce0905380 | ||
|
|
11895d54af | ||
|
|
d1520410a3 | ||
|
|
4dacb8a4b1 | ||
|
|
5b32594fbe | ||
|
|
882ce44986 | ||
|
|
bc7bfd96f0 | ||
|
|
6bfcf13187 | ||
|
|
309564abe3 | ||
|
|
b6c47b578f | ||
|
|
9d09d92c56 | ||
|
|
57d007e545 | ||
|
|
6e52f37d5b | ||
|
|
59d18ef55b | ||
|
|
46fbf6dd92 | ||
|
|
f19b4fab5f | ||
|
|
875d52a81f | ||
|
|
9bf9c71c88 | ||
|
|
fcc5155601 | ||
|
|
45cc022ea9 | ||
|
|
eab35c8235 | ||
|
|
01d199965a | ||
|
|
4b44b8b4fb | ||
|
|
56300f2928 | ||
|
|
e67630b51e | ||
|
|
bd14653417 | ||
|
|
2e89719d3e | ||
|
|
41b77c4e0a | ||
|
|
baaafbd5ea | ||
|
|
3476f5b4d3 | ||
|
|
bdf17fe0cc | ||
|
|
54ef9302a2 | ||
|
|
76fad8410d | ||
|
|
92492ee23b | ||
|
|
e56a444da9 | ||
|
|
8fe118bcaa | ||
|
|
c56a0e3c34 | ||
|
|
fecffeb0dd | ||
|
|
9608f51cde | ||
|
|
e4ed5bc836 | ||
|
|
de5d431eec | ||
|
|
8da753ab81 | ||
|
|
d923766042 | ||
|
|
a9877c8f65 | ||
|
|
2e7802ad7d | ||
|
|
3a338d9286 | ||
|
|
84b542c386 | ||
|
|
0135b328ed | ||
|
|
a970709d5d | ||
|
|
534abf9d97 | ||
|
|
5224cc49c4 | ||
|
|
b82b093108 | ||
|
|
cf0a7cd1c1 | ||
|
|
424e90f0f5 | ||
|
|
e12a07079e | ||
|
|
35e4a47be0 | ||
|
|
fcc5ffdfdd | ||
|
|
4cafc24a4e | ||
|
|
68c44ca0ee | ||
|
|
e858a72a22 | ||
|
|
4db8acd30a | ||
|
|
b8c1dca62f | ||
|
|
e850ff63bc | ||
|
|
11470f331a | ||
|
|
55c73e10a7 | ||
|
|
0eb39922f6 | ||
|
|
0af7e93763 | ||
|
|
6ff7906ddc | ||
|
|
ce722e317b | ||
|
|
6f6bba3ff1 | ||
|
|
d7298ec262 | ||
|
|
66f4f86a82 | ||
|
|
63a70c253e | ||
|
|
9e74d6238e | ||
|
|
5926fbd3d7 | ||
|
|
75bba25009 | ||
|
|
7b6519741b | ||
|
|
d6a1a43854 | ||
|
|
eb2633f3ef | ||
|
|
639ebb39f6 | ||
|
|
cb3c50eacc | ||
|
|
094f6003e0 | ||
|
|
98b940052c | ||
|
|
e5ee0afe6f | ||
|
|
a70ad9b5bb | ||
|
|
3f81c38c6d | ||
|
|
c68aa65226 | ||
|
|
ecfbe7d9c8 | ||
|
|
a1fb8a331f | ||
|
|
6b76b77400 | ||
|
|
fdf27bf390 | ||
|
|
8f06a0f898 | ||
|
|
dda8ef11c7 | ||
|
|
149bda980a | ||
|
|
893447b6b0 | ||
|
|
22bdddd6f0 | ||
|
|
62e859c6c7 | ||
|
|
f78a6e752f | ||
|
|
3b3c919e20 | ||
|
|
10812f8407 | ||
|
|
e5504a060d | ||
|
|
167bfddafa | ||
|
|
bfb945c243 | ||
|
|
b67e751ccb | ||
|
|
a91983b11c | ||
|
|
9aab787122 | ||
|
|
ab8acce645 | ||
|
|
9b0e3556ed | ||
|
|
c0257cf52f | ||
|
|
70a510bd8f | ||
|
|
95bb1067c1 | ||
|
|
5b4119fa7f | ||
|
|
42114e1df4 | ||
|
|
a27ec24c0f | ||
|
|
b70a831608 | ||
|
|
10aac376d1 | ||
|
|
a921d22545 | ||
|
|
ee07041b3a | ||
|
|
9c977d2215 | ||
|
|
f2c7b5dcd6 | ||
|
|
d37da52cb3 | ||
|
|
969326bd58 | ||
|
|
224071a652 | ||
|
|
2dad1204e8 | ||
|
|
249341d08f | ||
|
|
261a4f0311 | ||
|
|
ca4bf671ce | ||
|
|
28e90fa0e0 | ||
|
|
0a93b45b6a | ||
|
|
403dfd68fc | ||
|
|
c519354506 | ||
|
|
3d486fffed | ||
|
|
436acbb630 | ||
|
|
0b668d5ff3 | ||
|
|
bc286c169f | ||
|
|
670a2c1f80 | ||
|
|
88ffa422d4 | ||
|
|
0471976b48 | ||
|
|
c1ad973881 | ||
|
|
305a05f820 | ||
|
|
d64303d185 | ||
|
|
b8b9f41b6b | ||
|
|
dfcde52f39 | ||
|
|
10f494eefe | ||
|
|
448290bee4 | ||
|
|
2592e41301 | ||
|
|
0b6f8ba51e | ||
|
|
d0b2950434 | ||
|
|
42431d2aa6 | ||
|
|
b8e70faa2d | ||
|
|
662218e997 | ||
|
|
c3013c7c9c | ||
|
|
acff922762 | ||
|
|
dfea525cbe | ||
|
|
82fa0bcea7 | ||
|
|
4fa4d80c68 | ||
|
|
6aa8ee6943 | ||
|
|
4f0818144e | ||
|
|
b76ddb7647 | ||
|
|
2a3b335b15 | ||
|
|
20c2928c2b | ||
|
|
f380496728 | ||
|
|
2e80e7480d | ||
|
|
8eb97706b8 | ||
|
|
aaf671a309 | ||
|
|
be53097577 | ||
|
|
0a003efde4 | ||
|
|
2e8620c877 | ||
|
|
6b0bc48a42 | ||
|
|
a5b65df9cf | ||
|
|
57626fda7b | ||
|
|
6313d54cef | ||
|
|
7e6a73963e | ||
|
|
2156cb3cbe | ||
|
|
ef41983c84 | ||
|
|
8069fbd37f | ||
|
|
ec3bef7b4c | ||
|
|
0e1510ac29 | ||
|
|
b8c43b6080 | ||
|
|
3a67876252 | ||
|
|
6828c25498 | ||
|
|
5191c20b71 | ||
|
|
a91bb7080d | ||
|
|
69cd213fac | ||
|
|
05c33d89a1 | ||
|
|
96ef22d3d0 | ||
|
|
e8c52d4c89 | ||
|
|
37e1ed3744 | ||
|
|
75e12a33ae | ||
|
|
e58f7ff843 | ||
|
|
6ef027b958 | ||
|
|
59ea37daa7 | ||
|
|
0bfe974281 | ||
|
|
88b18b9ba4 | ||
|
|
b125137493 | ||
|
|
f765a6b902 | ||
|
|
5cb6eceecf | ||
|
|
b46a5c42ff | ||
|
|
ff5024ee2a | ||
|
|
5c908c0373 | ||
|
|
c333bfc193 | ||
|
|
e831b80d69 | ||
|
|
434f352eb3 | ||
|
|
4e1471ef21 | ||
|
|
5b1b758326 | ||
|
|
dfc3eed0cb | ||
|
|
06d0b96ca9 | ||
|
|
4283019aa0 | ||
|
|
efe6b473c5 | ||
|
|
557c466994 | ||
|
|
25ba4d1b68 | ||
|
|
668458525e | ||
|
|
2f4f4f147f | ||
|
|
94cf327e77 | ||
|
|
2b6128fe0b | ||
|
|
dad2ae1ee0 | ||
|
|
656e97df16 | ||
|
|
52a2e42cb9 | ||
|
|
4b471f0554 | ||
|
|
aaa8fdea52 | ||
|
|
2de2ec25d6 | ||
|
|
3f82dad1e4 | ||
|
|
3bf488ce52 | ||
|
|
b36dee364e | ||
|
|
ddec200290 | ||
|
|
cf917a5e93 | ||
|
|
9ceceb212f | ||
|
|
9f48454ea9 | ||
|
|
7353cfc781 | ||
|
|
4afb05d0cc | ||
|
|
cc2526dd51 | ||
|
|
096b04f1a4 | ||
|
|
756e1e6f9b | ||
|
|
6e90f0bf6a | ||
|
|
599c011f40 | ||
|
|
5a9c00ea04 | ||
|
|
76197a4be9 | ||
|
|
cc9d7bbf01 | ||
|
|
d224eb7c39 | ||
|
|
fd9da4232b | ||
|
|
507e6ae100 | ||
|
|
6a2f415298 | ||
|
|
448264e719 | ||
|
|
e9978fd4f5 | ||
|
|
75169c7570 | ||
|
|
03d7faf583 | ||
|
|
6f691e71bf | ||
|
|
c0d44d3b2a | ||
|
|
5f8d253ce0 | ||
|
|
f156a45c01 | ||
|
|
6a5d032809 | ||
|
|
4941e3d412 | ||
|
|
7bf422d58c | ||
|
|
9fde7d739a | ||
|
|
3c498189b6 | ||
|
|
7f0d0a93f7 | ||
|
|
57982df105 | ||
|
|
8c6338b6f9 | ||
|
|
c120ed7d18 | ||
|
|
0f834e2284 | ||
|
|
aaf8d9ac2f | ||
|
|
e542356d0c | ||
|
|
b7fb9f2071 | ||
|
|
934a2b9604 | ||
|
|
2bc296801a | ||
|
|
80cfd88e4e | ||
|
|
d378d98e26 | ||
|
|
766c1a2d50 | ||
|
|
5c323d96e0 | ||
|
|
4e3bc37791 | ||
|
|
a7d6efc520 | ||
|
|
cd1a96f389 | ||
|
|
62b0b83fd8 | ||
|
|
825e8cb925 | ||
|
|
1fbb7aff1e | ||
|
|
4fa3511a63 | ||
|
|
7139f05fc5 | ||
|
|
e530f82dd3 | ||
|
|
1abfd4166e | ||
|
|
b9829a05be | ||
|
|
7ed8565978 | ||
|
|
6576bc8927 | ||
|
|
2d57cbaec1 | ||
|
|
3242e7c164 | ||
|
|
c707240685 | ||
|
|
573a66c23d | ||
|
|
97c6f984dc | ||
|
|
07595987ac | ||
|
|
b09c4f45c7 | ||
|
|
0735a0c8a1 | ||
|
|
bc6d91a103 | ||
|
|
c392650e21 | ||
|
|
8f73f41824 | ||
|
|
c4397ec77e | ||
|
|
438d9aa407 | ||
|
|
3e81c09094 | ||
|
|
12ab5a0547 | ||
|
|
d3f64785d1 | ||
|
|
300ab211e8 | ||
|
|
fa8017295b | ||
|
|
e40b0cf437 | ||
|
|
7e49881b7f | ||
|
|
0f5c4615ae | ||
|
|
890e543304 | ||
|
|
20c976ff2a | ||
|
|
45bc449ff9 | ||
|
|
c8f6754417 | ||
|
|
0db0e4c8f3 | ||
|
|
91cfe70301 | ||
|
|
e4422b09b6 | ||
|
|
5927cf0e17 | ||
|
|
9e88f03e75 | ||
|
|
da2f00ab7d | ||
|
|
826b1394e8 | ||
|
|
0c3c91e41c | ||
|
|
e2b74f6354 | ||
|
|
e218d79cc2 | ||
|
|
75df8a3969 | ||
|
|
645d35ac32 | ||
|
|
168958f8e2 | ||
|
|
e3321c2e00 | ||
|
|
f179e87864 | ||
|
|
cf82358ee6 | ||
|
|
83ef37ca37 | ||
|
|
bf51bbffcb | ||
|
|
96b6ad11c1 | ||
|
|
10d693b9c2 | ||
|
|
a5ac53dd4c | ||
|
|
251e92513a | ||
|
|
cf983888cc | ||
|
|
2b2dc00bfd | ||
|
|
d33f641912 | ||
|
|
9764b4ec0e | ||
|
|
7846295a8f | ||
|
|
00a68c5eea | ||
|
|
2feb1a8ba6 | ||
|
|
1ac45342dd | ||
|
|
37adf04dcd | ||
|
|
2ea0f0fd16 | ||
|
|
904d7eaa94 | ||
|
|
57eb936200 | ||
|
|
0b80bbeb95 | ||
|
|
f1b93d63d1 | ||
|
|
03b7ebbc08 | ||
|
|
bb21c2198a | ||
|
|
6c56754322 | ||
|
|
a0c3a46aa9 | ||
|
|
2a7a2b739b | ||
|
|
4aa31b0618 | ||
|
|
2fc5c783ed | ||
|
|
6edd828101 | ||
|
|
d9815b523b | ||
|
|
7f9430f7ae | ||
|
|
c8e5c74092 | ||
|
|
00f7e584ce | ||
|
|
89c076b4b1 | ||
|
|
c0af42d6eb | ||
|
|
7a77d0a71e | ||
|
|
ca96f8db4e | ||
|
|
e06953626c | ||
|
|
e816745b19 | ||
|
|
fd715e54a1 | ||
|
|
ce46fb27ca | ||
|
|
b46df98e93 | ||
|
|
b96010bfa9 | ||
|
|
6d1e30e041 | ||
|
|
a0a605df06 | ||
|
|
cd96c04339 | ||
|
|
3f4fcd582e | ||
|
|
1dee8ceda1 | ||
|
|
f1cb3903ac | ||
|
|
de72956181 | ||
|
|
df51207ed2 | ||
|
|
0d04ee97dc | ||
|
|
64c8212ae1 | ||
|
|
b25468b498 | ||
|
|
8da651ac4d | ||
|
|
c1f76abfaf | ||
|
|
02fccc0940 | ||
|
|
2cc962e171 | ||
|
|
34b3f83498 | ||
|
|
9f846d3aa4 | ||
|
|
2b06301dbf | ||
|
|
13d6593753 | ||
|
|
3b678b9e8e | ||
|
|
dde5dce736 | ||
|
|
2665457f4a | ||
|
|
6030c5ce41 | ||
|
|
762fcaf5de | ||
|
|
b0129489ea | ||
|
|
206f2e3436 | ||
|
|
d43dad001e | ||
|
|
e93fa7f2cc | ||
|
|
29f31356d8 | ||
|
|
e649db8c6b | ||
|
|
2beda7c2b3 | ||
|
|
59b04c0df6 | ||
|
|
40e63ede6d | ||
|
|
64806a8397 | ||
|
|
b4e050e6c4 | ||
|
|
b45e5c2399 | ||
|
|
555d76d065 | ||
|
|
26de4bb521 | ||
|
|
c1d54f4aea | ||
|
|
b6d61abd78 | ||
|
|
40e78b9a89 | ||
|
|
ef9433411d | ||
|
|
00ba704a7f | ||
|
|
4c3c608d59 | ||
|
|
4b84d5bcec | ||
|
|
9e48ca23b2 | ||
|
|
49d817134a | ||
|
|
61ed68f3d0 | ||
|
|
291ccf7257 | ||
|
|
52da0ce399 | ||
|
|
db340f6402 | ||
|
|
684fc2c320 | ||
|
|
29b6afb82f | ||
|
|
4de18e054b | ||
|
|
bae1822aed | ||
|
|
e79e967151 | ||
|
|
fe64e1d38e | ||
|
|
d0bd01146e | ||
|
|
6058c84b79 | ||
|
|
fac0e42b2f | ||
|
|
9a50a4f2cc | ||
|
|
42a4c6b79e | ||
|
|
cec9e9b811 | ||
|
|
6b32e24161 | ||
|
|
d92e8ab062 | ||
|
|
f2a03468b1 | ||
|
|
c6db1c390b | ||
|
|
7469e26e5e | ||
|
|
2bc4ab3958 | ||
|
|
89ebef6571 | ||
|
|
bf45092c61 | ||
|
|
42c5171322 | ||
|
|
5233040ab4 | ||
|
|
0f2ac928f2 | ||
|
|
48acb764a4 | ||
|
|
13cdf1f159 | ||
|
|
0497bb5528 | ||
|
|
187d8e215f | ||
|
|
9d59b96ef9 | ||
|
|
a9c921a41d | ||
|
|
03149d3e4a | ||
|
|
150527ec19 | ||
|
|
cc8234fa89 | ||
|
|
239a3113e4 | ||
|
|
22342487e8 | ||
|
|
83c3ae8be8 | ||
|
|
c54a10cb4f | ||
|
|
df5b75694f | ||
|
|
ed8b2a0254 |
4
.gitattributes
vendored
Normal file
4
.gitattributes
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
dist/languages/* linguist-vendored
|
||||
dist/qt_themes/* linguist-vendored
|
||||
externals/* linguist-vendored
|
||||
*.h linguist-language=cpp
|
||||
16
.gitmodules
vendored
16
.gitmodules
vendored
@@ -25,6 +25,18 @@
|
||||
[submodule "unicorn"]
|
||||
path = externals/unicorn
|
||||
url = https://github.com/yuzu-emu/unicorn
|
||||
[submodule "mbedtls"]
|
||||
path = externals/mbedtls
|
||||
url = https://github.com/DarkLordZach/mbedtls
|
||||
[submodule "opus"]
|
||||
path = externals/opus
|
||||
url = https://github.com/ogniK5377/opus.git
|
||||
path = externals/opus
|
||||
url = https://github.com/ogniK5377/opus.git
|
||||
[submodule "soundtouch"]
|
||||
path = externals/soundtouch
|
||||
url = https://github.com/citra-emu/ext-soundtouch.git
|
||||
[submodule "libressl"]
|
||||
path = externals/libressl
|
||||
url = https://github.com/citra-emu/ext-libressl-portable.git
|
||||
[submodule "discord-rpc"]
|
||||
path = externals/discord-rpc
|
||||
url = https://github.com/discordapp/discord-rpc.git
|
||||
|
||||
21
.travis.yml
21
.travis.yml
@@ -20,13 +20,28 @@ matrix:
|
||||
install: "./.travis/linux/deps.sh"
|
||||
script: "./.travis/linux/build.sh"
|
||||
after_success: "./.travis/linux/upload.sh"
|
||||
cache: ccache
|
||||
- os: osx
|
||||
env: NAME="macos build"
|
||||
sudo: false
|
||||
osx_image: xcode9.3
|
||||
osx_image: xcode10
|
||||
install: "./.travis/macos/deps.sh"
|
||||
script: "./.travis/macos/build.sh"
|
||||
after_success: "./.travis/macos/upload.sh"
|
||||
cache: ccache
|
||||
- os: linux
|
||||
env: NAME="MinGW build"
|
||||
sudo: required
|
||||
dist: trusty
|
||||
services: docker
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- p7zip-full
|
||||
install: "./.travis/linux-mingw/deps.sh"
|
||||
script: "./.travis/linux-mingw/build.sh"
|
||||
after_success: "./.travis/linux-mingw/upload.sh"
|
||||
cache: ccache
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
@@ -42,7 +57,3 @@ notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://api.yuzu-emu.org/code/travis/notify
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.ccache
|
||||
|
||||
@@ -11,6 +11,9 @@ if [ -z $TRAVIS_TAG ]; then
|
||||
RELEASE_NAME=head
|
||||
else
|
||||
RELEASE_NAME=$(echo $TRAVIS_TAG | cut -d- -f1)
|
||||
if [ "$NAME" = "MinGW build" ]; then
|
||||
RELEASE_NAME="${RELEASE_NAME}-mingw"
|
||||
fi
|
||||
fi
|
||||
|
||||
mv "$REV_NAME" $RELEASE_NAME
|
||||
|
||||
16
.travis/common/travis-ci.env
Normal file
16
.travis/common/travis-ci.env
Normal file
@@ -0,0 +1,16 @@
|
||||
# List of environment variables to be shared with Docker containers
|
||||
CI
|
||||
TRAVIS
|
||||
CONTINUOUS_INTEGRATION
|
||||
TRAVIS_BRANCH
|
||||
TRAVIS_BUILD_ID
|
||||
TRAVIS_BUILD_NUMBER
|
||||
TRAVIS_COMMIT
|
||||
TRAVIS_JOB_ID
|
||||
TRAVIS_JOB_NUMBER
|
||||
TRAVIS_REPO_SLUG
|
||||
TRAVIS_TAG
|
||||
|
||||
# yuzu specific flags
|
||||
ENABLE_COMPATIBILITY_REPORTING
|
||||
USE_DISCORD_PRESENCE
|
||||
3
.travis/linux-mingw/build.sh
Executable file
3
.travis/linux-mingw/build.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash -ex
|
||||
mkdir "$HOME/.ccache" || true
|
||||
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh
|
||||
3
.travis/linux-mingw/deps.sh
Executable file
3
.travis/linux-mingw/deps.sh
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
docker pull ubuntu:18.04
|
||||
59
.travis/linux-mingw/docker.sh
Executable file
59
.travis/linux-mingw/docker.sh
Executable file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
cd /yuzu
|
||||
MINGW_PACKAGES="sdl2-mingw-w64 qt5base-mingw-w64 qt5tools-mingw-w64 libsamplerate-mingw-w64 qt5multimedia-mingw-w64"
|
||||
apt-get update
|
||||
apt-get install -y gpg wget git python3-pip python ccache g++-mingw-w64-x86-64 gcc-mingw-w64-x86-64 mingw-w64-tools cmake
|
||||
echo 'deb http://ppa.launchpad.net/tobydox/mingw-w64/ubuntu bionic main ' > /etc/apt/sources.list.d/extras.list
|
||||
apt-key adv --keyserver keyserver.ubuntu.com --recv '72931B477E22FEFD47F8DECE02FE5F12ADDE29B2'
|
||||
apt-get update
|
||||
apt-get install -y ${MINGW_PACKAGES}
|
||||
|
||||
# fix a problem in current MinGW headers
|
||||
wget -q https://raw.githubusercontent.com/Alexpux/mingw-w64/d0d7f784833bbb0b2d279310ddc6afb52fe47a46/mingw-w64-headers/crt/errno.h -O /usr/x86_64-w64-mingw32/include/errno.h
|
||||
# override Travis CI unreasonable ccache size
|
||||
echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
|
||||
|
||||
# Dirty hack to trick unicorn makefile into believing we are in a MINGW system
|
||||
mv /bin/uname /bin/uname1 && echo -e '#!/bin/sh\necho MINGW64' >> /bin/uname
|
||||
chmod +x /bin/uname
|
||||
|
||||
# Dirty hack to trick unicorn makefile into believing we have cmd
|
||||
echo '' >> /bin/cmd
|
||||
chmod +x /bin/cmd
|
||||
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j4
|
||||
|
||||
# Clean up the dirty hacks
|
||||
rm /bin/uname && mv /bin/uname1 /bin/uname
|
||||
rm /bin/cmd
|
||||
|
||||
ccache -s
|
||||
|
||||
echo "Tests skipped"
|
||||
#ctest -VV -C Release
|
||||
|
||||
echo 'Prepare binaries...'
|
||||
cd ..
|
||||
mkdir package
|
||||
|
||||
QT_PLATFORM_DLL_PATH='/usr/x86_64-w64-mingw32/lib/qt5/plugins/platforms/'
|
||||
find build/ -name "yuzu*.exe" -exec cp {} 'package' \;
|
||||
|
||||
# copy Qt plugins
|
||||
mkdir package/platforms
|
||||
cp "${QT_PLATFORM_DLL_PATH}/qwindows.dll" package/platforms/
|
||||
cp -rv "${QT_PLATFORM_DLL_PATH}/../mediaservice/" package/
|
||||
cp -rv "${QT_PLATFORM_DLL_PATH}/../imageformats/" package/
|
||||
rm -f package/mediaservice/*d.dll
|
||||
|
||||
for i in package/*.exe; do
|
||||
# we need to process pdb here, however, cv2pdb
|
||||
# does not work here, so we just simply strip all the debug symbols
|
||||
x86_64-w64-mingw32-strip "${i}"
|
||||
done
|
||||
|
||||
pip3 install pefile
|
||||
python3 .travis/linux-mingw/scan_dll.py package/*.exe "package/"
|
||||
106
.travis/linux-mingw/scan_dll.py
Normal file
106
.travis/linux-mingw/scan_dll.py
Normal file
@@ -0,0 +1,106 @@
|
||||
import pefile
|
||||
import sys
|
||||
import re
|
||||
import os
|
||||
import queue
|
||||
import shutil
|
||||
|
||||
# constant definitions
|
||||
KNOWN_SYS_DLLS = ['WINMM.DLL', 'MSVCRT.DLL', 'VERSION.DLL', 'MPR.DLL',
|
||||
'DWMAPI.DLL', 'UXTHEME.DLL', 'DNSAPI.DLL', 'IPHLPAPI.DLL']
|
||||
# below is for Ubuntu 18.04 with specified PPA enabled, if you are using
|
||||
# other distro or different repositories, change the following accordingly
|
||||
DLL_PATH = [
|
||||
'/usr/x86_64-w64-mingw32/bin/',
|
||||
'/usr/x86_64-w64-mingw32/lib/',
|
||||
'/usr/lib/gcc/x86_64-w64-mingw32/7.3-posix/'
|
||||
]
|
||||
|
||||
missing = []
|
||||
|
||||
|
||||
def parse_imports(file_name):
|
||||
results = []
|
||||
pe = pefile.PE(file_name, fast_load=True)
|
||||
pe.parse_data_directories()
|
||||
|
||||
for entry in pe.DIRECTORY_ENTRY_IMPORT:
|
||||
current = entry.dll.decode()
|
||||
current_u = current.upper() # b/c Windows is often case insensitive
|
||||
# here we filter out system dlls
|
||||
# dll w/ names like *32.dll are likely to be system dlls
|
||||
if current_u.upper() not in KNOWN_SYS_DLLS and not re.match(string=current_u, pattern=r'.*32\.DLL'):
|
||||
results.append(current)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def parse_imports_recursive(file_name, path_list=[]):
|
||||
q = queue.Queue() # create a FIFO queue
|
||||
# file_name can be a string or a list for the convience
|
||||
if isinstance(file_name, str):
|
||||
q.put(file_name)
|
||||
elif isinstance(file_name, list):
|
||||
for i in file_name:
|
||||
q.put(i)
|
||||
full_list = []
|
||||
while q.qsize():
|
||||
current = q.get_nowait()
|
||||
print('> %s' % current)
|
||||
deps = parse_imports(current)
|
||||
# if this dll does not have any import, ignore it
|
||||
if not deps:
|
||||
continue
|
||||
for dep in deps:
|
||||
# the dependency already included in the list, skip
|
||||
if dep in full_list:
|
||||
continue
|
||||
# find the requested dll in the provided paths
|
||||
full_path = find_dll(dep)
|
||||
if not full_path:
|
||||
missing.append(dep)
|
||||
continue
|
||||
full_list.append(dep)
|
||||
q.put(full_path)
|
||||
path_list.append(full_path)
|
||||
return full_list
|
||||
|
||||
|
||||
def find_dll(name):
|
||||
for path in DLL_PATH:
|
||||
for root, _, files in os.walk(path):
|
||||
for f in files:
|
||||
if name.lower() == f.lower():
|
||||
return os.path.join(root, f)
|
||||
|
||||
|
||||
def deploy(name, dst, dry_run=False):
|
||||
dlls_path = []
|
||||
parse_imports_recursive(name, dlls_path)
|
||||
for dll_entry in dlls_path:
|
||||
if not dry_run:
|
||||
shutil.copy(dll_entry, dst)
|
||||
else:
|
||||
print('[Dry-Run] Copy %s to %s' % (dll_entry, dst))
|
||||
print('Deploy completed.')
|
||||
return dlls_path
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print('Usage: %s [files to examine ...] [target deploy directory]')
|
||||
return 1
|
||||
to_deploy = sys.argv[1:-1]
|
||||
tgt_dir = sys.argv[-1]
|
||||
if not os.path.isdir(tgt_dir):
|
||||
print('%s is not a directory.' % tgt_dir)
|
||||
return 1
|
||||
print('Scanning dependencies...')
|
||||
deploy(to_deploy, tgt_dir)
|
||||
if missing:
|
||||
print('Following DLLs are not found: %s' % ('\n'.join(missing)))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
13
.travis/linux-mingw/upload.sh
Executable file
13
.travis/linux-mingw/upload.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
. .travis/common/pre-upload.sh
|
||||
|
||||
REV_NAME="yuzu-windows-mingw-${GITDATE}-${GITREV}"
|
||||
ARCHIVE_NAME="${REV_NAME}.tar.gz"
|
||||
COMPRESSION_FLAGS="-czvf"
|
||||
|
||||
mkdir "$REV_NAME"
|
||||
# get around the permission issues
|
||||
cp -r package/* "$REV_NAME"
|
||||
|
||||
. .travis/common/post-upload.sh
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
docker run -e CCACHE_DIR=/ccache -v $HOME/.ccache:/ccache -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
mkdir -p "$HOME/.ccache"
|
||||
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
|
||||
@@ -5,14 +5,10 @@ apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev
|
||||
|
||||
cd /yuzu
|
||||
|
||||
export PATH=/usr/lib/ccache:$PATH
|
||||
ln -sf /usr/bin/ccache /usr/lib/ccache/cc
|
||||
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
|
||||
mkdir build && cd build
|
||||
ccache --show-stats > ccache_before
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -G Ninja
|
||||
ninja
|
||||
ccache --show-stats > ccache_after
|
||||
diff -U100 ccache_before ccache_after || true
|
||||
|
||||
ccache -s
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
@@ -2,17 +2,16 @@
|
||||
|
||||
set -o pipefail
|
||||
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.12
|
||||
export MACOSX_DEPLOYMENT_TARGET=10.13
|
||||
export Qt5_DIR=$(brew --prefix)/opt/qt5
|
||||
export UNICORNDIR=$(pwd)/externals/unicorn
|
||||
export PATH="/usr/local/opt/ccache/libexec:$PATH"
|
||||
|
||||
mkdir build && cd build
|
||||
export PATH=/usr/local/opt/ccache/libexec:$PATH
|
||||
ccache --show-stats > ccache_before
|
||||
cmake --version
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
|
||||
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
|
||||
make -j4
|
||||
ccache --show-stats > ccache_after
|
||||
diff -U100 ccache_before ccache_after || true
|
||||
|
||||
ccache -s
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# CMake 3.6 required for FindBoost to define IMPORTED libs properly on unknown Boost versions
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
cmake_minimum_required(VERSION 3.7)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modules")
|
||||
include(DownloadExternals)
|
||||
@@ -15,10 +15,14 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON
|
||||
option(ENABLE_QT "Enable the Qt frontend" ON)
|
||||
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
|
||||
|
||||
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
|
||||
|
||||
option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
|
||||
|
||||
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
|
||||
|
||||
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
|
||||
|
||||
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
message(STATUS "Copying pre-commit hook")
|
||||
file(COPY hooks/pre-commit
|
||||
@@ -41,6 +45,19 @@ function(check_submodules_present)
|
||||
endfunction()
|
||||
check_submodules_present()
|
||||
|
||||
configure_file(${CMAKE_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
COPYONLY)
|
||||
if (ENABLE_COMPATIBILITY_LIST_DOWNLOAD AND NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
message(STATUS "Downloading compatibility list for yuzu...")
|
||||
file(DOWNLOAD
|
||||
https://api.yuzu-emu.org/gamedb/
|
||||
"${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json" SHOW_PROGRESS)
|
||||
endif()
|
||||
if (NOT EXISTS ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json)
|
||||
file(WRITE ${CMAKE_BINARY_DIR}/dist/compatibility_list/compatibility_list.json "")
|
||||
endif()
|
||||
|
||||
# Detect current compilation architecture and create standard definitions
|
||||
# =======================================================================
|
||||
|
||||
@@ -66,10 +83,12 @@ if (NOT ENABLE_GENERIC)
|
||||
detect_architecture("_M_AMD64" x86_64)
|
||||
detect_architecture("_M_IX86" x86)
|
||||
detect_architecture("_M_ARM" ARM)
|
||||
detect_architecture("_M_ARM64" ARM64)
|
||||
else()
|
||||
detect_architecture("__x86_64__" x86_64)
|
||||
detect_architecture("__i386__" x86)
|
||||
detect_architecture("__arm__" ARM)
|
||||
detect_architecture("__aarch64__" ARM64)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -108,8 +127,6 @@ else()
|
||||
# Avoid windows.h from including some usually unused libs like winsocks.h, since this might cause some redefinition errors.
|
||||
add_definitions(/DWIN32_LEAN_AND_MEAN)
|
||||
|
||||
# set up output paths for executable binaries (.exe-files, and .dll-files on DLL-capable platforms)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
set(CMAKE_CONFIGURATION_TYPES Debug Release CACHE STRING "" FORCE)
|
||||
|
||||
# Tweak optimization settings
|
||||
@@ -187,8 +204,8 @@ find_package(Threads REQUIRED)
|
||||
if (ENABLE_SDL2)
|
||||
if (YUZU_USE_BUNDLED_SDL2)
|
||||
# Detect toolchain and platform
|
||||
if (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
set(SDL2_VER "SDL2-2.0.5")
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
|
||||
set(SDL2_VER "SDL2-2.0.8")
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled SDL2 binaries for your toolchain. Disable YUZU_USE_BUNDLED_SDL2 and provide your own.")
|
||||
endif()
|
||||
@@ -220,7 +237,7 @@ if (YUZU_USE_BUNDLED_UNICORN)
|
||||
if (MSVC)
|
||||
message(STATUS "unicorn not found, falling back to bundled")
|
||||
# Detect toolchain and platform
|
||||
if (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) 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.")
|
||||
@@ -254,10 +271,18 @@ if (YUZU_USE_BUNDLED_UNICORN)
|
||||
|
||||
find_package(PythonInterp 2.7 REQUIRED)
|
||||
|
||||
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}
|
||||
)
|
||||
if (MINGW)
|
||||
add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
|
||||
COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh cross-win64
|
||||
WORKING_DIRECTORY ${UNICORN_PREFIX}
|
||||
)
|
||||
else()
|
||||
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}
|
||||
)
|
||||
endif()
|
||||
|
||||
# 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
|
||||
@@ -271,6 +296,7 @@ endif()
|
||||
|
||||
if (UNICORN_FOUND)
|
||||
add_library(unicorn INTERFACE)
|
||||
add_dependencies(unicorn unicorn-build)
|
||||
target_link_libraries(unicorn INTERFACE "${LIBUNICORN_LIBRARY}")
|
||||
target_include_directories(unicorn INTERFACE "${LIBUNICORN_INCLUDE_DIR}")
|
||||
else()
|
||||
@@ -279,7 +305,7 @@ endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
if (YUZU_USE_BUNDLED_QT)
|
||||
if (MSVC14 AND ARCHITECTURE_x86_64)
|
||||
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1920) AND ARCHITECTURE_x86_64)
|
||||
set(QT_VER qt-5.10.0-msvc2015_64)
|
||||
else()
|
||||
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
|
||||
@@ -303,7 +329,7 @@ endif()
|
||||
# ======================================
|
||||
|
||||
IF (APPLE)
|
||||
FIND_LIBRARY(COCOA_LIBRARY Cocoa) # Umbrella framework for everything GUI-related
|
||||
find_library(COCOA_LIBRARY Cocoa) # Umbrella framework for everything GUI-related
|
||||
set(PLATFORM_LIBRARIES ${COCOA_LIBRARY} ${IOKIT_LIBRARY} ${COREVIDEO_LIBRARY})
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||
@@ -322,14 +348,6 @@ ELSEIF (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")
|
||||
set(PLATFORM_LIBRARIES rt)
|
||||
ENDIF (APPLE)
|
||||
|
||||
# MINGW: GCC does not support codecvt, so use iconv instead
|
||||
if (UNIX OR MINGW)
|
||||
find_library(ICONV_LIBRARY NAMES iconv)
|
||||
if (ICONV_LIBRARY)
|
||||
list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY})
|
||||
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.
|
||||
# =======================================================================
|
||||
@@ -416,6 +434,13 @@ enable_testing()
|
||||
add_subdirectory(externals)
|
||||
add_subdirectory(src)
|
||||
|
||||
# Set yuzu project or yuzu-cmd project as default StartUp Project in Visual Studio depending on whether QT is enabled or not
|
||||
if(ENABLE_QT)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu)
|
||||
else()
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT yuzu-cmd)
|
||||
endif()
|
||||
|
||||
|
||||
# Installation instructions
|
||||
# =========================
|
||||
|
||||
@@ -4,8 +4,10 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
set(Qt5_DLL_DIR "${Qt5_DIR}/../../../bin")
|
||||
set(Qt5_PLATFORMS_DIR "${Qt5_DIR}/../../../plugins/platforms/")
|
||||
set(Qt5_STYLES_DIR "${Qt5_DIR}/../../../plugins/styles/")
|
||||
set(Qt5_IMAGEFORMATS_DIR "${Qt5_DIR}/../../../plugins/imageformats/")
|
||||
set(PLATFORMS ${DLL_DEST}platforms/)
|
||||
set(STYLES ${DLL_DEST}styles/)
|
||||
set(IMAGEFORMATS ${DLL_DEST}imageformats/)
|
||||
windows_copy_files(${target_dir} ${Qt5_DLL_DIR} ${DLL_DEST}
|
||||
icudt*.dll
|
||||
icuin*.dll
|
||||
@@ -17,4 +19,5 @@ function(copy_yuzu_Qt5_deps target_dir)
|
||||
)
|
||||
windows_copy_files(yuzu ${Qt5_PLATFORMS_DIR} ${PLATFORMS} qwindows$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_STYLES_DIR} ${STYLES} qwindowsvistastyle$<$<CONFIG:Debug>:d>.*)
|
||||
windows_copy_files(yuzu ${Qt5_IMAGEFORMATS_DIR} ${IMAGEFORMATS} qjpeg$<$<CONFIG:Debug>:d>.*)
|
||||
endfunction(copy_yuzu_Qt5_deps)
|
||||
|
||||
54
CMakeModules/MinGWCross.cmake
Normal file
54
CMakeModules/MinGWCross.cmake
Normal file
@@ -0,0 +1,54 @@
|
||||
set(MINGW_PREFIX /usr/x86_64-w64-mingw32/)
|
||||
set(CMAKE_SYSTEM_NAME Windows)
|
||||
set(CMAKE_SYSTEM_PROCESSOR x86_64)
|
||||
# Actually a hack, w/o this will cause some strange errors
|
||||
set(CMAKE_HOST_WIN32 TRUE)
|
||||
|
||||
|
||||
set(CMAKE_FIND_ROOT_PATH ${MINGW_PREFIX})
|
||||
set(SDL2_PATH ${MINGW_PREFIX})
|
||||
set(MINGW_TOOL_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32-)
|
||||
|
||||
# Specify the cross compiler
|
||||
set(CMAKE_C_COMPILER ${MINGW_TOOL_PREFIX}gcc-posix)
|
||||
set(CMAKE_CXX_COMPILER ${MINGW_TOOL_PREFIX}g++-posix)
|
||||
set(CMAKE_RC_COMPILER ${MINGW_TOOL_PREFIX}windres)
|
||||
|
||||
# Mingw tools
|
||||
set(STRIP ${MINGW_TOOL_PREFIX}strip)
|
||||
set(WINDRES ${MINGW_TOOL_PREFIX}windres)
|
||||
set(ENV{PKG_CONFIG} ${MINGW_TOOL_PREFIX}pkg-config)
|
||||
|
||||
# ccache wrapper
|
||||
option(USE_CCACHE "Use ccache for compilation" OFF)
|
||||
if(USE_CCACHE)
|
||||
find_program(CCACHE ccache)
|
||||
if(CCACHE)
|
||||
message(STATUS "Using ccache found in PATH")
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ${CCACHE})
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ${CCACHE})
|
||||
else(CCACHE)
|
||||
message(WARNING "USE_CCACHE enabled, but no ccache found")
|
||||
endif(CCACHE)
|
||||
endif(USE_CCACHE)
|
||||
|
||||
# Search for programs in the build host directories
|
||||
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
|
||||
|
||||
# Echo modified cmake vars to screen for debugging purposes
|
||||
if(NOT DEFINED ENV{MINGW_DEBUG_INFO})
|
||||
message("")
|
||||
message("Custom cmake vars: (blank = system default)")
|
||||
message("-----------------------------------------")
|
||||
message("* CMAKE_C_COMPILER : ${CMAKE_C_COMPILER}")
|
||||
message("* CMAKE_CXX_COMPILER : ${CMAKE_CXX_COMPILER}")
|
||||
message("* CMAKE_RC_COMPILER : ${CMAKE_RC_COMPILER}")
|
||||
message("* WINDRES : ${WINDRES}")
|
||||
message("* ENV{PKG_CONFIG} : $ENV{PKG_CONFIG}")
|
||||
message("* STRIP : ${STRIP}")
|
||||
message("* USE_CCACHE : ${USE_CCACHE}")
|
||||
message("")
|
||||
# So that the debug info only appears once
|
||||
set(ENV{MINGW_DEBUG_INFO} SHOWN)
|
||||
endif()
|
||||
13
appveyor.yml
13
appveyor.yml
@@ -39,11 +39,12 @@ before_build:
|
||||
- mkdir %BUILD_TYPE%_build
|
||||
- cd %BUILD_TYPE%_build
|
||||
- ps: |
|
||||
$COMPAT = if ($env:ENABLE_COMPATIBILITY_REPORTING -eq $null) {0} else {$env:ENABLE_COMPATIBILITY_REPORTING}
|
||||
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 .. 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 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1 && exit 0'
|
||||
} else {
|
||||
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=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 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1"
|
||||
}
|
||||
- cd ..
|
||||
|
||||
@@ -117,6 +118,7 @@ after_build:
|
||||
mkdir $RELEASE_DIST
|
||||
mkdir $RELEASE_DIST/platforms
|
||||
mkdir $RELEASE_DIST/styles
|
||||
mkdir $RELEASE_DIST/imageformats
|
||||
|
||||
# copy the compiled binaries and other release files to the release folder
|
||||
Get-ChildItem "$CMAKE_BINARY_DIR" -Filter "yuzu*.exe" | Copy-Item -destination $RELEASE_DIST
|
||||
@@ -140,6 +142,9 @@ after_build:
|
||||
# copy the qt windows vista style dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/styles/qwindowsvistastyle.dll" -force -destination "$RELEASE_DIST/styles"
|
||||
|
||||
# copy the qt jpeg imageformat dll to platforms
|
||||
Copy-Item -path "C:/msys64/mingw64/share/qt5/plugins/imageformats/qjpeg.dll" -force -destination "$RELEASE_DIST/imageformats"
|
||||
|
||||
7z a -tzip $MINGW_BUILD_ZIP $RELEASE_DIST\*
|
||||
7z a $MINGW_SEVENZIP $RELEASE_DIST
|
||||
}
|
||||
@@ -158,10 +163,6 @@ artifacts:
|
||||
- path: $(BUILD_ZIP)
|
||||
name: build
|
||||
type: zip
|
||||
- path: $(BUILD_SYMBOLS)
|
||||
name: debugsymbols
|
||||
- path: $(BUILD_UPDATE)
|
||||
name: update
|
||||
|
||||
deploy:
|
||||
provider: GitHub
|
||||
|
||||
5
dist/compatibility_list/compatibility_list.qrc
vendored
Normal file
5
dist/compatibility_list/compatibility_list.qrc
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="compatibility_list">
|
||||
<file>compatibility_list.json</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
39
externals/CMakeLists.txt
vendored
39
externals/CMakeLists.txt
vendored
@@ -32,17 +32,27 @@ add_subdirectory(inih)
|
||||
|
||||
# lz4
|
||||
set(LZ4_BUNDLED_MODE ON)
|
||||
add_subdirectory(lz4/contrib/cmake_unofficial)
|
||||
add_subdirectory(lz4/contrib/cmake_unofficial EXCLUDE_FROM_ALL)
|
||||
target_include_directories(lz4_static INTERFACE ./lz4/lib)
|
||||
|
||||
# mbedtls
|
||||
add_subdirectory(mbedtls EXCLUDE_FROM_ALL)
|
||||
target_include_directories(mbedtls PUBLIC ./mbedtls/include)
|
||||
|
||||
# MicroProfile
|
||||
add_library(microprofile INTERFACE)
|
||||
target_include_directories(microprofile INTERFACE ./microprofile)
|
||||
|
||||
# Open Source Archives
|
||||
add_subdirectory(open_source_archives EXCLUDE_FROM_ALL)
|
||||
|
||||
# Unicorn
|
||||
add_library(unicorn-headers INTERFACE)
|
||||
target_include_directories(unicorn-headers INTERFACE ./unicorn/include)
|
||||
|
||||
# SoundTouch
|
||||
add_subdirectory(soundtouch)
|
||||
|
||||
# Xbyak
|
||||
if (ARCHITECTURE_x86_64)
|
||||
# Defined before "dynarmic" above
|
||||
@@ -58,5 +68,30 @@ target_include_directories(opus INTERFACE ./opus/include)
|
||||
# Cubeb
|
||||
if(ENABLE_CUBEB)
|
||||
set(BUILD_TESTS OFF CACHE BOOL "")
|
||||
add_subdirectory(cubeb)
|
||||
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
|
||||
# DiscordRPC
|
||||
if (USE_DISCORD_PRESENCE)
|
||||
add_subdirectory(discord-rpc EXCLUDE_FROM_ALL)
|
||||
target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
|
||||
endif()
|
||||
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
# LibreSSL
|
||||
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
|
||||
add_subdirectory(libressl EXCLUDE_FROM_ALL)
|
||||
target_include_directories(ssl INTERFACE ./libressl/include)
|
||||
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
|
||||
|
||||
# lurlparser
|
||||
add_subdirectory(lurlparser EXCLUDE_FROM_ALL)
|
||||
|
||||
# httplib
|
||||
add_library(httplib INTERFACE)
|
||||
target_include_directories(httplib INTERFACE ./httplib)
|
||||
|
||||
# JSON
|
||||
add_library(json-headers INTERFACE)
|
||||
target_include_directories(json-headers INTERFACE ./json)
|
||||
endif()
|
||||
|
||||
2
externals/boost
vendored
2
externals/boost
vendored
Submodule externals/boost updated: d80e506e17...0b920df1c9
2
externals/catch
vendored
2
externals/catch
vendored
Submodule externals/catch updated: d2a130f243...15cf3caace
1
externals/discord-rpc
vendored
Submodule
1
externals/discord-rpc
vendored
Submodule
Submodule externals/discord-rpc added at e32d001809
2
externals/dynarmic
vendored
2
externals/dynarmic
vendored
Submodule externals/dynarmic updated: 73d3efc3e0...4e6848d1c9
2
externals/fmt
vendored
2
externals/fmt
vendored
Submodule externals/fmt updated: c2ce7e4f07...62010520ed
23
externals/glad/include/glad/glad.h
vendored
23
externals/glad/include/glad/glad.h
vendored
File diff suppressed because one or more lines are too long
6385
externals/glad/src/glad.c
vendored
6385
externals/glad/src/glad.c
vendored
File diff suppressed because one or more lines are too long
15
externals/httplib/README.md
vendored
Normal file
15
externals/httplib/README.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
From https://github.com/yhirose/cpp-httplib/commit/d9479bc0b12e8a1e8bce2d34da4feeef488581f3
|
||||
|
||||
MIT License
|
||||
|
||||
===
|
||||
|
||||
cpp-httplib
|
||||
|
||||
A C++11 header-only HTTP library.
|
||||
|
||||
It's extremely easy to setup. Just include httplib.h file in your code!
|
||||
|
||||
Inspired by Sinatra and express.
|
||||
|
||||
© 2017 Yuji Hirose
|
||||
2344
externals/httplib/httplib.h
vendored
Normal file
2344
externals/httplib/httplib.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
9
externals/json/README.md
vendored
Normal file
9
externals/json/README.md
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
JSON for Modern C++
|
||||
===================
|
||||
|
||||
v3.1.2
|
||||
|
||||
This is a mirror providing the single required header file.
|
||||
|
||||
The original repository can be found at:
|
||||
https://github.com/nlohmann/json/commit/d2dd27dc3b8472dbaa7d66f83619b3ebcd9185fe
|
||||
17300
externals/json/json.hpp
vendored
Normal file
17300
externals/json/json.hpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
externals/libressl
vendored
Submodule
1
externals/libressl
vendored
Submodule
Submodule externals/libressl added at 7d01cb01cb
8
externals/lurlparser/CMakeLists.txt
vendored
Normal file
8
externals/lurlparser/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
add_library(lurlparser
|
||||
LUrlParser.cpp
|
||||
LUrlParser.h
|
||||
)
|
||||
|
||||
create_target_directory_groups(lurlparser)
|
||||
|
||||
target_include_directories(lurlparser INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
265
externals/lurlparser/LUrlParser.cpp
vendored
Normal file
265
externals/lurlparser/LUrlParser.cpp
vendored
Normal file
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||
* https://github.com/corporateshark/LUrlParser
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "LUrlParser.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <stdlib.h>
|
||||
|
||||
// check if the scheme name is valid
|
||||
static bool IsSchemeValid( const std::string& SchemeName )
|
||||
{
|
||||
for ( auto c : SchemeName )
|
||||
{
|
||||
if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
|
||||
{
|
||||
if ( !IsValid() ) { return false; }
|
||||
|
||||
int Port = atoi( m_Port.c_str() );
|
||||
|
||||
if ( Port <= 0 || Port > 65535 ) { return false; }
|
||||
|
||||
if ( OutPort ) { *OutPort = Port; }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// based on RFC 1738 and RFC 3986
|
||||
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
|
||||
{
|
||||
LUrlParser::clParseURL Result;
|
||||
|
||||
const char* CurrentString = URL.c_str();
|
||||
|
||||
/*
|
||||
* <scheme>:<scheme-specific-part>
|
||||
* <scheme> := [a-z\+\-\.]+
|
||||
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
|
||||
*/
|
||||
|
||||
// try to read scheme
|
||||
{
|
||||
const char* LocalString = strchr( CurrentString, ':' );
|
||||
|
||||
if ( !LocalString )
|
||||
{
|
||||
return clParseURL( LUrlParserError_NoUrlCharacter );
|
||||
}
|
||||
|
||||
// save the scheme name
|
||||
Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
if ( !IsSchemeValid( Result.m_Scheme ) )
|
||||
{
|
||||
return clParseURL( LUrlParserError_InvalidSchemeName );
|
||||
}
|
||||
|
||||
// scheme should be lowercase
|
||||
std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
|
||||
|
||||
// skip ':'
|
||||
CurrentString = LocalString+1;
|
||||
}
|
||||
|
||||
/*
|
||||
* //<user>:<password>@<host>:<port>/<url-path>
|
||||
* any ":", "@" and "/" must be normalized
|
||||
*/
|
||||
|
||||
// skip "//"
|
||||
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
||||
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
||||
|
||||
// check if the user name and password are specified
|
||||
bool bHasUserName = false;
|
||||
|
||||
const char* LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString )
|
||||
{
|
||||
if ( *LocalString == '@' )
|
||||
{
|
||||
// user name and password are specified
|
||||
bHasUserName = true;
|
||||
break;
|
||||
}
|
||||
else if ( *LocalString == '/' )
|
||||
{
|
||||
// end of <host>:<port> specification
|
||||
bHasUserName = false;
|
||||
break;
|
||||
}
|
||||
|
||||
LocalString++;
|
||||
}
|
||||
|
||||
// user name and password
|
||||
LocalString = CurrentString;
|
||||
|
||||
if ( bHasUserName )
|
||||
{
|
||||
// read user name
|
||||
while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
|
||||
|
||||
Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
// proceed with the current pointer
|
||||
CurrentString = LocalString;
|
||||
|
||||
if ( *CurrentString == ':' )
|
||||
{
|
||||
// skip ':'
|
||||
CurrentString++;
|
||||
|
||||
// read password
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '@' ) LocalString++;
|
||||
|
||||
Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
// skip '@'
|
||||
if ( *CurrentString != '@' )
|
||||
{
|
||||
return clParseURL( LUrlParserError_NoAtSign );
|
||||
}
|
||||
|
||||
CurrentString++;
|
||||
}
|
||||
|
||||
bool bHasBracket = ( *CurrentString == '[' );
|
||||
|
||||
// go ahead, read the host name
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString )
|
||||
{
|
||||
if ( bHasBracket && *LocalString == ']' )
|
||||
{
|
||||
// end of IPv6 address
|
||||
LocalString++;
|
||||
break;
|
||||
}
|
||||
else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
|
||||
{
|
||||
// port number is specified
|
||||
break;
|
||||
}
|
||||
|
||||
LocalString++;
|
||||
}
|
||||
|
||||
Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
|
||||
// is port number specified?
|
||||
if ( *CurrentString == ':' )
|
||||
{
|
||||
CurrentString++;
|
||||
|
||||
// read port number
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '/' ) LocalString++;
|
||||
|
||||
Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
// end of string
|
||||
if ( !*CurrentString )
|
||||
{
|
||||
Result.m_ErrorCode = LUrlParserError_Ok;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
// skip '/'
|
||||
if ( *CurrentString != '/' )
|
||||
{
|
||||
return clParseURL( LUrlParserError_NoSlash );
|
||||
}
|
||||
|
||||
CurrentString++;
|
||||
|
||||
// parse the path
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
|
||||
|
||||
Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
|
||||
// check for query
|
||||
if ( *CurrentString == '?' )
|
||||
{
|
||||
// skip '?'
|
||||
CurrentString++;
|
||||
|
||||
// read query
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString && *LocalString != '#' ) LocalString++;
|
||||
|
||||
Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
// check for fragment
|
||||
if ( *CurrentString == '#' )
|
||||
{
|
||||
// skip '#'
|
||||
CurrentString++;
|
||||
|
||||
// read fragment
|
||||
LocalString = CurrentString;
|
||||
|
||||
while ( *LocalString ) LocalString++;
|
||||
|
||||
Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
|
||||
|
||||
CurrentString = LocalString;
|
||||
}
|
||||
|
||||
Result.m_ErrorCode = LUrlParserError_Ok;
|
||||
|
||||
return Result;
|
||||
}
|
||||
78
externals/lurlparser/LUrlParser.h
vendored
Normal file
78
externals/lurlparser/LUrlParser.h
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||
* https://github.com/corporateshark/LUrlParser
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace LUrlParser
|
||||
{
|
||||
enum LUrlParserError
|
||||
{
|
||||
LUrlParserError_Ok = 0,
|
||||
LUrlParserError_Uninitialized = 1,
|
||||
LUrlParserError_NoUrlCharacter = 2,
|
||||
LUrlParserError_InvalidSchemeName = 3,
|
||||
LUrlParserError_NoDoubleSlash = 4,
|
||||
LUrlParserError_NoAtSign = 5,
|
||||
LUrlParserError_UnexpectedEndOfLine = 6,
|
||||
LUrlParserError_NoSlash = 7,
|
||||
};
|
||||
|
||||
class clParseURL
|
||||
{
|
||||
public:
|
||||
LUrlParserError m_ErrorCode;
|
||||
std::string m_Scheme;
|
||||
std::string m_Host;
|
||||
std::string m_Port;
|
||||
std::string m_Path;
|
||||
std::string m_Query;
|
||||
std::string m_Fragment;
|
||||
std::string m_UserName;
|
||||
std::string m_Password;
|
||||
|
||||
clParseURL()
|
||||
: m_ErrorCode( LUrlParserError_Uninitialized )
|
||||
{}
|
||||
|
||||
/// return 'true' if the parsing was successful
|
||||
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
|
||||
|
||||
/// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
|
||||
bool GetPort( int* OutPort ) const;
|
||||
|
||||
/// parse the URL
|
||||
static clParseURL ParseURL( const std::string& URL );
|
||||
|
||||
private:
|
||||
explicit clParseURL( LUrlParserError ErrorCode )
|
||||
: m_ErrorCode( ErrorCode )
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace LUrlParser
|
||||
19
externals/lurlparser/README.md
vendored
Normal file
19
externals/lurlparser/README.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
From https://github.com/corporateshark/LUrlParser/commit/455d5e2d27e3946f11ad0328fee9ee2628e6a8e2
|
||||
|
||||
MIT License
|
||||
|
||||
===
|
||||
|
||||
Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||
|
||||
(C) Sergey Kosarevsky, 2015
|
||||
|
||||
@corporateshark sk@linderdaum.com
|
||||
|
||||
http://www.linderdaum.com
|
||||
|
||||
http://blog.linderdaum.com
|
||||
|
||||
=============================
|
||||
|
||||
A tiny and lightweight URL & URI parser (RFC 1738, RFC 3986) written in C++.
|
||||
1
externals/mbedtls
vendored
Submodule
1
externals/mbedtls
vendored
Submodule
Submodule externals/mbedtls added at d409b75a4c
16
externals/open_source_archives/CMakeLists.txt
vendored
Normal file
16
externals/open_source_archives/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
add_library(open_source_archives
|
||||
src/FontChineseSimplified.cpp
|
||||
src/FontChineseTraditional.cpp
|
||||
src/FontExtendedChineseSimplified.cpp
|
||||
src/FontKorean.cpp
|
||||
src/FontNintendoExtended.cpp
|
||||
src/FontStandard.cpp
|
||||
include/FontChineseSimplified.h
|
||||
include/FontChineseTraditional.h
|
||||
include/FontExtendedChineseSimplified.h
|
||||
include/FontKorean.h
|
||||
include/FontNintendoExtended.h
|
||||
include/FontStandard.h
|
||||
)
|
||||
|
||||
target_include_directories(open_source_archives PUBLIC include)
|
||||
4
externals/open_source_archives/Readme.md
vendored
Normal file
4
externals/open_source_archives/Readme.md
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
These files were generated by https://github.com/FearlessTobi/yuzu_system_archives at git commit 0a24b0c9f38d71fb2c4bba5645a39029e539a5ec. To generate the files use the run.sh inside that repository.
|
||||
|
||||
The follwing system archives are currently included:
|
||||
- JPN/EUR/USA System Font
|
||||
6
externals/open_source_archives/include/FontChineseSimplified.h
vendored
Normal file
6
externals/open_source_archives/include/FontChineseSimplified.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 217276> FontChineseSimplified;
|
||||
|
||||
6
externals/open_source_archives/include/FontChineseTraditional.h
vendored
Normal file
6
externals/open_source_archives/include/FontChineseTraditional.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 222236> FontChineseTraditional;
|
||||
|
||||
6
externals/open_source_archives/include/FontExtendedChineseSimplified.h
vendored
Normal file
6
externals/open_source_archives/include/FontExtendedChineseSimplified.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 293516> FontExtendedChineseSimplified;
|
||||
|
||||
6
externals/open_source_archives/include/FontKorean.h
vendored
Normal file
6
externals/open_source_archives/include/FontKorean.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 217276> FontKorean;
|
||||
|
||||
6
externals/open_source_archives/include/FontNintendoExtended.h
vendored
Normal file
6
externals/open_source_archives/include/FontNintendoExtended.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 172064> FontNintendoExtended;
|
||||
|
||||
6
externals/open_source_archives/include/FontStandard.h
vendored
Normal file
6
externals/open_source_archives/include/FontStandard.h
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern const std::array<unsigned char, 217276> FontStandard;
|
||||
|
||||
18112
externals/open_source_archives/src/FontChineseSimplified.cpp
vendored
Normal file
18112
externals/open_source_archives/src/FontChineseSimplified.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18525
externals/open_source_archives/src/FontChineseTraditional.cpp
vendored
Normal file
18525
externals/open_source_archives/src/FontChineseTraditional.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
24465
externals/open_source_archives/src/FontExtendedChineseSimplified.cpp
vendored
Normal file
24465
externals/open_source_archives/src/FontExtendedChineseSimplified.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18112
externals/open_source_archives/src/FontKorean.cpp
vendored
Normal file
18112
externals/open_source_archives/src/FontKorean.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
14344
externals/open_source_archives/src/FontNintendoExtended.cpp
vendored
Normal file
14344
externals/open_source_archives/src/FontNintendoExtended.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
18112
externals/open_source_archives/src/FontStandard.cpp
vendored
Normal file
18112
externals/open_source_archives/src/FontStandard.cpp
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
externals/soundtouch
vendored
Submodule
1
externals/soundtouch
vendored
Submodule
Submodule externals/soundtouch added at 060181eaf2
2
externals/xbyak
vendored
2
externals/xbyak
vendored
Submodule externals/xbyak updated: 71b75f653f...1de435ed04
@@ -13,3 +13,6 @@ endif()
|
||||
if (ENABLE_QT)
|
||||
add_subdirectory(yuzu)
|
||||
endif()
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
add_subdirectory(web_service)
|
||||
endif()
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
add_library(audio_core STATIC
|
||||
algorithm/filter.cpp
|
||||
algorithm/filter.h
|
||||
algorithm/interpolate.cpp
|
||||
algorithm/interpolate.h
|
||||
audio_out.cpp
|
||||
audio_out.h
|
||||
audio_renderer.cpp
|
||||
audio_renderer.h
|
||||
buffer.h
|
||||
cubeb_sink.cpp
|
||||
cubeb_sink.h
|
||||
codec.cpp
|
||||
codec.h
|
||||
null_sink.h
|
||||
stream.cpp
|
||||
stream.h
|
||||
sink.h
|
||||
sink_details.cpp
|
||||
sink_details.h
|
||||
sink_stream.h
|
||||
stream.cpp
|
||||
stream.h
|
||||
time_stretch.cpp
|
||||
time_stretch.h
|
||||
|
||||
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
|
||||
)
|
||||
|
||||
create_target_directory_groups(audio_core)
|
||||
|
||||
target_link_libraries(audio_core PUBLIC common core)
|
||||
target_link_libraries(audio_core PRIVATE SoundTouch)
|
||||
|
||||
if(ENABLE_CUBEB)
|
||||
target_link_libraries(audio_core PRIVATE cubeb)
|
||||
|
||||
79
src/audio_core/algorithm/filter.cpp
Normal file
79
src/audio_core/algorithm/filter.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include "audio_core/algorithm/filter.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
Filter Filter::LowPass(double cutoff, double Q) {
|
||||
const double w0 = 2.0 * M_PI * cutoff;
|
||||
const double sin_w0 = std::sin(w0);
|
||||
const double cos_w0 = std::cos(w0);
|
||||
const double alpha = sin_w0 / (2 * Q);
|
||||
|
||||
const double a0 = 1 + alpha;
|
||||
const double a1 = -2.0 * cos_w0;
|
||||
const double a2 = 1 - alpha;
|
||||
const double b0 = 0.5 * (1 - cos_w0);
|
||||
const double b1 = 1.0 * (1 - cos_w0);
|
||||
const double b2 = 0.5 * (1 - cos_w0);
|
||||
|
||||
return {a0, a1, a2, b0, b1, b2};
|
||||
}
|
||||
|
||||
Filter::Filter() : Filter(1.0, 0.0, 0.0, 1.0, 0.0, 0.0) {}
|
||||
|
||||
Filter::Filter(double a0, double a1, double a2, double b0, double b1, double b2)
|
||||
: a1(a1 / a0), a2(a2 / a0), b0(b0 / a0), b1(b1 / a0), b2(b2 / a0) {}
|
||||
|
||||
void Filter::Process(std::vector<s16>& signal) {
|
||||
const std::size_t num_frames = signal.size() / 2;
|
||||
for (std::size_t i = 0; i < num_frames; i++) {
|
||||
std::rotate(in.begin(), in.end() - 1, in.end());
|
||||
std::rotate(out.begin(), out.end() - 1, out.end());
|
||||
|
||||
for (std::size_t ch = 0; ch < channel_count; ch++) {
|
||||
in[0][ch] = signal[i * channel_count + ch];
|
||||
|
||||
out[0][ch] = b0 * in[0][ch] + b1 * in[1][ch] + b2 * in[2][ch] - a1 * out[1][ch] -
|
||||
a2 * out[2][ch];
|
||||
|
||||
signal[i * 2 + ch] = static_cast<s16>(std::clamp(out[0][ch], -32768.0, 32767.0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the appropriate Q for each biquad in a cascading filter.
|
||||
/// @param total_count The total number of biquads to be cascaded.
|
||||
/// @param index 0-index of the biquad to calculate the Q value for.
|
||||
static double CascadingBiquadQ(std::size_t total_count, std::size_t index) {
|
||||
const double pole = M_PI * (2 * index + 1) / (4.0 * total_count);
|
||||
return 1.0 / (2.0 * std::cos(pole));
|
||||
}
|
||||
|
||||
CascadingFilter CascadingFilter::LowPass(double cutoff, std::size_t cascade_size) {
|
||||
std::vector<Filter> cascade(cascade_size);
|
||||
for (std::size_t i = 0; i < cascade_size; i++) {
|
||||
cascade[i] = Filter::LowPass(cutoff, CascadingBiquadQ(cascade_size, i));
|
||||
}
|
||||
return CascadingFilter{std::move(cascade)};
|
||||
}
|
||||
|
||||
CascadingFilter::CascadingFilter() = default;
|
||||
CascadingFilter::CascadingFilter(std::vector<Filter> filters) : filters(std::move(filters)) {}
|
||||
|
||||
void CascadingFilter::Process(std::vector<s16>& signal) {
|
||||
for (auto& filter : filters) {
|
||||
filter.Process(signal);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
62
src/audio_core/algorithm/filter.h
Normal file
62
src/audio_core/algorithm/filter.h
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
/// Digital biquad filter:
|
||||
///
|
||||
/// b0 + b1 z^-1 + b2 z^-2
|
||||
/// H(z) = ------------------------
|
||||
/// a0 + a1 z^-1 + b2 z^-2
|
||||
class Filter {
|
||||
public:
|
||||
/// Creates a low-pass filter.
|
||||
/// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0.
|
||||
/// @param Q Determines the quality factor of this filter.
|
||||
static Filter LowPass(double cutoff, double Q = 0.7071);
|
||||
|
||||
/// Passthrough filter.
|
||||
Filter();
|
||||
|
||||
Filter(double a0, double a1, double a2, double b0, double b1, double b2);
|
||||
|
||||
void Process(std::vector<s16>& signal);
|
||||
|
||||
private:
|
||||
static constexpr std::size_t channel_count = 2;
|
||||
|
||||
/// Coefficients are in normalized form (a0 = 1.0).
|
||||
double a1, a2, b0, b1, b2;
|
||||
/// Input History
|
||||
std::array<std::array<double, channel_count>, 3> in;
|
||||
/// Output History
|
||||
std::array<std::array<double, channel_count>, 3> out;
|
||||
};
|
||||
|
||||
/// Cascade filters to build up higher-order filters from lower-order ones.
|
||||
class CascadingFilter {
|
||||
public:
|
||||
/// Creates a cascading low-pass filter.
|
||||
/// @param cutoff Determines the cutoff frequency. A value from 0.0 to 1.0.
|
||||
/// @param cascade_size Number of biquads in cascade.
|
||||
static CascadingFilter LowPass(double cutoff, std::size_t cascade_size);
|
||||
|
||||
/// Passthrough.
|
||||
CascadingFilter();
|
||||
|
||||
explicit CascadingFilter(std::vector<Filter> filters);
|
||||
|
||||
void Process(std::vector<s16>& signal);
|
||||
|
||||
private:
|
||||
std::vector<Filter> filters;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
71
src/audio_core/algorithm/interpolate.cpp
Normal file
71
src/audio_core/algorithm/interpolate.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include "audio_core/algorithm/interpolate.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
/// The Lanczos kernel
|
||||
static double Lanczos(std::size_t a, double x) {
|
||||
if (x == 0.0)
|
||||
return 1.0;
|
||||
const double px = M_PI * x;
|
||||
return a * std::sin(px) * std::sin(px / a) / (px * px);
|
||||
}
|
||||
|
||||
std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio) {
|
||||
if (input.size() < 2)
|
||||
return {};
|
||||
|
||||
if (ratio <= 0) {
|
||||
LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio);
|
||||
ratio = 1.0;
|
||||
}
|
||||
|
||||
if (ratio != state.current_ratio) {
|
||||
const double cutoff_frequency = std::min(0.5 / ratio, 0.5 * ratio);
|
||||
state.nyquist = CascadingFilter::LowPass(std::clamp(cutoff_frequency, 0.0, 0.4), 3);
|
||||
state.current_ratio = ratio;
|
||||
}
|
||||
state.nyquist.Process(input);
|
||||
|
||||
constexpr std::size_t taps = InterpolationState::lanczos_taps;
|
||||
const std::size_t num_frames = input.size() / 2;
|
||||
|
||||
std::vector<s16> output;
|
||||
output.reserve(static_cast<std::size_t>(input.size() / ratio + 4));
|
||||
|
||||
double& pos = state.position;
|
||||
auto& h = state.history;
|
||||
for (std::size_t i = 0; i < num_frames; ++i) {
|
||||
std::rotate(h.begin(), h.end() - 1, h.end());
|
||||
h[0][0] = input[i * 2 + 0];
|
||||
h[0][1] = input[i * 2 + 1];
|
||||
|
||||
while (pos <= 1.0) {
|
||||
double l = 0.0;
|
||||
double r = 0.0;
|
||||
for (std::size_t j = 0; j < h.size(); j++) {
|
||||
l += Lanczos(taps, pos + j - taps + 1) * h[j][0];
|
||||
r += Lanczos(taps, pos + j - taps + 1) * h[j][1];
|
||||
}
|
||||
output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0)));
|
||||
output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0)));
|
||||
|
||||
pos += ratio;
|
||||
}
|
||||
pos -= 1.0;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
43
src/audio_core/algorithm/interpolate.h
Normal file
43
src/audio_core/algorithm/interpolate.h
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include "audio_core/algorithm/filter.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
struct InterpolationState {
|
||||
static constexpr std::size_t lanczos_taps = 4;
|
||||
static constexpr std::size_t history_size = lanczos_taps * 2 - 1;
|
||||
|
||||
double current_ratio = 0.0;
|
||||
CascadingFilter nyquist;
|
||||
std::array<std::array<s16, 2>, history_size> history = {};
|
||||
double position = 0;
|
||||
};
|
||||
|
||||
/// Interpolates input signal to produce output signal.
|
||||
/// @param input The signal to interpolate.
|
||||
/// @param ratio Interpolation ratio.
|
||||
/// ratio > 1.0 results in fewer output samples.
|
||||
/// ratio < 1.0 results in more output samples.
|
||||
/// @returns Output signal.
|
||||
std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, double ratio);
|
||||
|
||||
/// Interpolates input signal to produce output signal.
|
||||
/// @param input The signal to interpolate.
|
||||
/// @param input_rate The sample rate of input.
|
||||
/// @param output_rate The desired sample rate of the output.
|
||||
/// @returns Output signal.
|
||||
inline std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
|
||||
u32 input_rate, u32 output_rate) {
|
||||
const double ratio = static_cast<double>(input_rate) / static_cast<double>(output_rate);
|
||||
return Interpolate(state, std::move(input), ratio);
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -27,19 +27,20 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) {
|
||||
return {};
|
||||
}
|
||||
|
||||
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels,
|
||||
StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
|
||||
Stream::ReleaseCallback&& release_callback) {
|
||||
if (!sink) {
|
||||
const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id);
|
||||
sink = sink_details.factory(Settings::values.audio_device_id);
|
||||
}
|
||||
|
||||
return std::make_shared<Stream>(sample_rate, ChannelsToStreamFormat(num_channels),
|
||||
std::move(release_callback),
|
||||
sink->AcquireSinkStream(sample_rate, num_channels));
|
||||
return std::make_shared<Stream>(
|
||||
sample_rate, ChannelsToStreamFormat(num_channels), std::move(release_callback),
|
||||
sink->AcquireSinkStream(sample_rate, num_channels, name), std::move(name));
|
||||
}
|
||||
|
||||
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count) {
|
||||
std::vector<Buffer::Tag> AudioOut::GetTagsAndReleaseBuffers(StreamPtr stream,
|
||||
std::size_t max_count) {
|
||||
return stream->GetTagsAndReleaseBuffers(max_count);
|
||||
}
|
||||
|
||||
@@ -51,7 +52,7 @@ void AudioOut::StopStream(StreamPtr stream) {
|
||||
stream->Stop();
|
||||
}
|
||||
|
||||
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data) {
|
||||
bool AudioOut::QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data) {
|
||||
return stream->QueueBuffer(std::make_shared<Buffer>(tag, std::move(data)));
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/buffer.h"
|
||||
@@ -20,11 +21,11 @@ namespace AudioCore {
|
||||
class AudioOut {
|
||||
public:
|
||||
/// Opens a new audio stream
|
||||
StreamPtr OpenStream(u32 sample_rate, u32 num_channels,
|
||||
StreamPtr OpenStream(u32 sample_rate, u32 num_channels, std::string&& name,
|
||||
Stream::ReleaseCallback&& release_callback);
|
||||
|
||||
/// Returns a vector of recently released buffers specified by tag for the specified stream
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, size_t max_count);
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(StreamPtr stream, std::size_t max_count);
|
||||
|
||||
/// Starts an audio stream for playback
|
||||
void StartStream(StreamPtr stream);
|
||||
@@ -33,7 +34,7 @@ public:
|
||||
void StopStream(StreamPtr stream);
|
||||
|
||||
/// Queues a buffer into the specified audio stream, returns true on success
|
||||
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<u8>&& data);
|
||||
bool QueueBuffer(StreamPtr stream, Buffer::Tag tag, std::vector<s16>&& data);
|
||||
|
||||
private:
|
||||
SinkPtr sink;
|
||||
|
||||
293
src/audio_core/audio_renderer.cpp
Normal file
293
src/audio_core/audio_renderer.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "audio_core/algorithm/interpolate.h"
|
||||
#include "audio_core/audio_out.h"
|
||||
#include "audio_core/audio_renderer.h"
|
||||
#include "audio_core/codec.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/hle/kernel/event.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
constexpr u32 STREAM_SAMPLE_RATE{48000};
|
||||
constexpr u32 STREAM_NUM_CHANNELS{2};
|
||||
|
||||
class AudioRenderer::VoiceState {
|
||||
public:
|
||||
bool IsPlaying() const {
|
||||
return is_in_use && info.play_state == PlayState::Started;
|
||||
}
|
||||
|
||||
const VoiceOutStatus& GetOutStatus() const {
|
||||
return out_status;
|
||||
}
|
||||
|
||||
const VoiceInfo& GetInfo() const {
|
||||
return info;
|
||||
}
|
||||
|
||||
VoiceInfo& Info() {
|
||||
return info;
|
||||
}
|
||||
|
||||
void SetWaveIndex(std::size_t index);
|
||||
std::vector<s16> DequeueSamples(std::size_t sample_count);
|
||||
void UpdateState();
|
||||
void RefreshBuffer();
|
||||
|
||||
private:
|
||||
bool is_in_use{};
|
||||
bool is_refresh_pending{};
|
||||
std::size_t wave_index{};
|
||||
std::size_t offset{};
|
||||
Codec::ADPCMState adpcm_state{};
|
||||
InterpolationState interp_state{};
|
||||
std::vector<s16> samples;
|
||||
VoiceOutStatus out_status{};
|
||||
VoiceInfo info{};
|
||||
};
|
||||
|
||||
AudioRenderer::AudioRenderer(AudioRendererParameter params,
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_event)
|
||||
: worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count) {
|
||||
|
||||
audio_out = std::make_unique<AudioCore::AudioOut>();
|
||||
stream = audio_out->OpenStream(STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS, "AudioRenderer",
|
||||
[=]() { buffer_event->Signal(); });
|
||||
audio_out->StartStream(stream);
|
||||
|
||||
QueueMixedBuffer(0);
|
||||
QueueMixedBuffer(1);
|
||||
QueueMixedBuffer(2);
|
||||
}
|
||||
|
||||
AudioRenderer::~AudioRenderer() = default;
|
||||
|
||||
u32 AudioRenderer::GetSampleRate() const {
|
||||
return worker_params.sample_rate;
|
||||
}
|
||||
|
||||
u32 AudioRenderer::GetSampleCount() const {
|
||||
return worker_params.sample_count;
|
||||
}
|
||||
|
||||
u32 AudioRenderer::GetMixBufferCount() const {
|
||||
return worker_params.mix_buffer_count;
|
||||
}
|
||||
|
||||
Stream::State AudioRenderer::GetStreamState() const {
|
||||
return stream->GetState();
|
||||
}
|
||||
|
||||
std::vector<u8> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
|
||||
// Copy UpdateDataHeader struct
|
||||
UpdateDataHeader config{};
|
||||
std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
|
||||
u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
|
||||
|
||||
// Copy MemoryPoolInfo structs
|
||||
std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
|
||||
std::memcpy(mem_pool_info.data(),
|
||||
input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
|
||||
memory_pool_count * sizeof(MemoryPoolInfo));
|
||||
|
||||
// Copy VoiceInfo structs
|
||||
std::size_t offset{sizeof(UpdateDataHeader) + config.behavior_size + config.memory_pools_size +
|
||||
config.voice_resource_size};
|
||||
for (auto& voice : voices) {
|
||||
std::memcpy(&voice.Info(), input_params.data() + offset, sizeof(VoiceInfo));
|
||||
offset += sizeof(VoiceInfo);
|
||||
}
|
||||
|
||||
// Update voices
|
||||
for (auto& voice : voices) {
|
||||
voice.UpdateState();
|
||||
if (!voice.GetInfo().is_in_use) {
|
||||
continue;
|
||||
}
|
||||
if (voice.GetInfo().is_new) {
|
||||
voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
|
||||
}
|
||||
}
|
||||
|
||||
// Update memory pool state
|
||||
std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
|
||||
for (std::size_t index = 0; index < memory_pool.size(); ++index) {
|
||||
if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
|
||||
memory_pool[index].state = MemoryPoolStates::Attached;
|
||||
} else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
|
||||
memory_pool[index].state = MemoryPoolStates::Detached;
|
||||
}
|
||||
}
|
||||
|
||||
// Release previous buffers and queue next ones for playback
|
||||
ReleaseAndQueueBuffers();
|
||||
|
||||
// Copy output header
|
||||
UpdateDataHeader response_data{worker_params};
|
||||
std::vector<u8> output_params(response_data.total_size);
|
||||
std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
|
||||
|
||||
// Copy output memory pool entries
|
||||
std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
|
||||
response_data.memory_pools_size);
|
||||
|
||||
// Copy output voice status
|
||||
std::size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
|
||||
for (const auto& voice : voices) {
|
||||
std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
|
||||
sizeof(VoiceOutStatus));
|
||||
voice_out_status_offset += sizeof(VoiceOutStatus);
|
||||
}
|
||||
|
||||
return output_params;
|
||||
}
|
||||
|
||||
void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
|
||||
wave_index = index & 3;
|
||||
is_refresh_pending = true;
|
||||
}
|
||||
|
||||
std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(std::size_t sample_count) {
|
||||
if (!IsPlaying()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (is_refresh_pending) {
|
||||
RefreshBuffer();
|
||||
}
|
||||
|
||||
const std::size_t max_size{samples.size() - offset};
|
||||
const std::size_t dequeue_offset{offset};
|
||||
std::size_t size{sample_count * STREAM_NUM_CHANNELS};
|
||||
if (size > max_size) {
|
||||
size = max_size;
|
||||
}
|
||||
|
||||
out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
|
||||
offset += size;
|
||||
|
||||
const auto& wave_buffer{info.wave_buffer[wave_index]};
|
||||
if (offset == samples.size()) {
|
||||
offset = 0;
|
||||
|
||||
if (!wave_buffer.is_looping) {
|
||||
SetWaveIndex(wave_index + 1);
|
||||
}
|
||||
|
||||
out_status.wave_buffer_consumed++;
|
||||
|
||||
if (wave_buffer.end_of_stream) {
|
||||
info.play_state = PlayState::Paused;
|
||||
}
|
||||
}
|
||||
|
||||
return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
|
||||
}
|
||||
|
||||
void AudioRenderer::VoiceState::UpdateState() {
|
||||
if (is_in_use && !info.is_in_use) {
|
||||
// No longer in use, reset state
|
||||
is_refresh_pending = true;
|
||||
wave_index = 0;
|
||||
offset = 0;
|
||||
out_status = {};
|
||||
}
|
||||
is_in_use = info.is_in_use;
|
||||
}
|
||||
|
||||
void AudioRenderer::VoiceState::RefreshBuffer() {
|
||||
std::vector<s16> new_samples(info.wave_buffer[wave_index].buffer_sz / sizeof(s16));
|
||||
Memory::ReadBlock(info.wave_buffer[wave_index].buffer_addr, new_samples.data(),
|
||||
info.wave_buffer[wave_index].buffer_sz);
|
||||
|
||||
switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
|
||||
case Codec::PcmFormat::Int16: {
|
||||
// PCM16 is played as-is
|
||||
break;
|
||||
}
|
||||
case Codec::PcmFormat::Adpcm: {
|
||||
// Decode ADPCM to PCM16
|
||||
Codec::ADPCM_Coeff coeffs;
|
||||
Memory::ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
|
||||
new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
|
||||
new_samples.size() * sizeof(s16), coeffs, adpcm_state);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(Audio, "Unimplemented sample_format={}", info.sample_format);
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (info.channel_count) {
|
||||
case 1:
|
||||
// 1 channel is upsampled to 2 channel
|
||||
samples.resize(new_samples.size() * 2);
|
||||
for (std::size_t index = 0; index < new_samples.size(); ++index) {
|
||||
samples[index * 2] = new_samples[index];
|
||||
samples[index * 2 + 1] = new_samples[index];
|
||||
}
|
||||
break;
|
||||
case 2: {
|
||||
// 2 channel is played as is
|
||||
samples = std::move(new_samples);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOG_CRITICAL(Audio, "Unimplemented channel_count={}", info.channel_count);
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
samples = Interpolate(interp_state, std::move(samples), Info().sample_rate, STREAM_SAMPLE_RATE);
|
||||
|
||||
is_refresh_pending = false;
|
||||
}
|
||||
|
||||
static constexpr s16 ClampToS16(s32 value) {
|
||||
return static_cast<s16>(std::clamp(value, -32768, 32767));
|
||||
}
|
||||
|
||||
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
|
||||
constexpr std::size_t BUFFER_SIZE{512};
|
||||
std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
|
||||
|
||||
for (auto& voice : voices) {
|
||||
if (!voice.IsPlaying()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::size_t offset{};
|
||||
s64 samples_remaining{BUFFER_SIZE};
|
||||
while (samples_remaining > 0) {
|
||||
const std::vector<s16> samples{voice.DequeueSamples(samples_remaining)};
|
||||
|
||||
if (samples.empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
samples_remaining -= samples.size() / stream->GetNumChannels();
|
||||
|
||||
for (const auto& sample : samples) {
|
||||
const s32 buffer_sample{buffer[offset]};
|
||||
buffer[offset++] =
|
||||
ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
|
||||
}
|
||||
}
|
||||
}
|
||||
audio_out->QueueBuffer(stream, tag, std::move(buffer));
|
||||
}
|
||||
|
||||
void AudioRenderer::ReleaseAndQueueBuffers() {
|
||||
const auto released_buffers{audio_out->GetTagsAndReleaseBuffers(stream, 2)};
|
||||
for (const auto& tag : released_buffers) {
|
||||
QueueMixedBuffer(tag);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
185
src/audio_core/audio_renderer.h
Normal file
185
src/audio_core/audio_renderer.h
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "audio_core/stream.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/hle/kernel/object.h"
|
||||
|
||||
namespace Kernel {
|
||||
class Event;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class AudioOut;
|
||||
|
||||
enum class PlayState : u8 {
|
||||
Started = 0,
|
||||
Stopped = 1,
|
||||
Paused = 2,
|
||||
};
|
||||
|
||||
struct AudioRendererParameter {
|
||||
u32_le sample_rate;
|
||||
u32_le sample_count;
|
||||
u32_le mix_buffer_count;
|
||||
u32_le unknown_c;
|
||||
u32_le voice_count;
|
||||
u32_le sink_count;
|
||||
u32_le effect_count;
|
||||
u32_le unknown_1c;
|
||||
u8 unknown_20;
|
||||
INSERT_PADDING_BYTES(3);
|
||||
u32_le splitter_count;
|
||||
u32_le unknown_2c;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u32_le revision;
|
||||
};
|
||||
static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
|
||||
|
||||
enum class MemoryPoolStates : u32 { // Should be LE
|
||||
Invalid = 0x0,
|
||||
Unknown = 0x1,
|
||||
RequestDetach = 0x2,
|
||||
Detached = 0x3,
|
||||
RequestAttach = 0x4,
|
||||
Attached = 0x5,
|
||||
Released = 0x6,
|
||||
};
|
||||
|
||||
struct MemoryPoolEntry {
|
||||
MemoryPoolStates state;
|
||||
u32_le unknown_4;
|
||||
u32_le unknown_8;
|
||||
u32_le unknown_c;
|
||||
};
|
||||
static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
|
||||
|
||||
struct MemoryPoolInfo {
|
||||
u64_le pool_address;
|
||||
u64_le pool_size;
|
||||
MemoryPoolStates pool_state;
|
||||
INSERT_PADDING_WORDS(3); // Unknown
|
||||
};
|
||||
static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
|
||||
struct BiquadFilter {
|
||||
u8 enable;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
std::array<s16_le, 3> numerator;
|
||||
std::array<s16_le, 2> denominator;
|
||||
};
|
||||
static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
|
||||
|
||||
struct WaveBuffer {
|
||||
u64_le buffer_addr;
|
||||
u64_le buffer_sz;
|
||||
s32_le start_sample_offset;
|
||||
s32_le end_sample_offset;
|
||||
u8 is_looping;
|
||||
u8 end_of_stream;
|
||||
u8 sent_to_server;
|
||||
INSERT_PADDING_BYTES(5);
|
||||
u64 context_addr;
|
||||
u64 context_sz;
|
||||
INSERT_PADDING_BYTES(8);
|
||||
};
|
||||
static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
|
||||
|
||||
struct VoiceInfo {
|
||||
u32_le id;
|
||||
u32_le node_id;
|
||||
u8 is_new;
|
||||
u8 is_in_use;
|
||||
PlayState play_state;
|
||||
u8 sample_format;
|
||||
u32_le sample_rate;
|
||||
u32_le priority;
|
||||
u32_le sorting_order;
|
||||
u32_le channel_count;
|
||||
float_le pitch;
|
||||
float_le volume;
|
||||
std::array<BiquadFilter, 2> biquad_filter;
|
||||
u32_le wave_buffer_count;
|
||||
u32_le wave_buffer_head;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
u64_le additional_params_addr;
|
||||
u64_le additional_params_sz;
|
||||
u32_le mix_id;
|
||||
u32_le splitter_info_id;
|
||||
std::array<WaveBuffer, 4> wave_buffer;
|
||||
std::array<u32_le, 6> voice_channel_resource_ids;
|
||||
INSERT_PADDING_BYTES(24);
|
||||
};
|
||||
static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
|
||||
|
||||
struct VoiceOutStatus {
|
||||
u64_le played_sample_count;
|
||||
u32_le wave_buffer_consumed;
|
||||
u32_le voice_drops_count;
|
||||
};
|
||||
static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
|
||||
|
||||
struct UpdateDataHeader {
|
||||
UpdateDataHeader() {}
|
||||
|
||||
explicit UpdateDataHeader(const AudioRendererParameter& config) {
|
||||
revision = Common::MakeMagic('R', 'E', 'V', '4'); // 5.1.0 Revision
|
||||
behavior_size = 0xb0;
|
||||
memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
|
||||
voices_size = config.voice_count * 0x10;
|
||||
voice_resource_size = 0x0;
|
||||
effects_size = config.effect_count * 0x10;
|
||||
mixes_size = 0x0;
|
||||
sinks_size = config.sink_count * 0x20;
|
||||
performance_manager_size = 0x10;
|
||||
total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
|
||||
effects_size + sinks_size + performance_manager_size;
|
||||
}
|
||||
|
||||
u32_le revision;
|
||||
u32_le behavior_size;
|
||||
u32_le memory_pools_size;
|
||||
u32_le voices_size;
|
||||
u32_le voice_resource_size;
|
||||
u32_le effects_size;
|
||||
u32_le mixes_size;
|
||||
u32_le sinks_size;
|
||||
u32_le performance_manager_size;
|
||||
INSERT_PADDING_WORDS(6);
|
||||
u32_le total_size;
|
||||
};
|
||||
static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
|
||||
|
||||
class AudioRenderer {
|
||||
public:
|
||||
AudioRenderer(AudioRendererParameter params, Kernel::SharedPtr<Kernel::Event> buffer_event);
|
||||
~AudioRenderer();
|
||||
|
||||
std::vector<u8> UpdateAudioRenderer(const std::vector<u8>& input_params);
|
||||
void QueueMixedBuffer(Buffer::Tag tag);
|
||||
void ReleaseAndQueueBuffers();
|
||||
u32 GetSampleRate() const;
|
||||
u32 GetSampleCount() const;
|
||||
u32 GetMixBufferCount() const;
|
||||
Stream::State GetStreamState() const;
|
||||
|
||||
private:
|
||||
class VoiceState;
|
||||
|
||||
AudioRendererParameter worker_params;
|
||||
Kernel::SharedPtr<Kernel::Event> buffer_event;
|
||||
std::vector<VoiceState> voices;
|
||||
std::unique_ptr<AudioOut> audio_out;
|
||||
AudioCore::StreamPtr stream;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -18,11 +18,16 @@ class Buffer {
|
||||
public:
|
||||
using Tag = u64;
|
||||
|
||||
Buffer(Tag tag, std::vector<u8>&& data) : tag{tag}, data{std::move(data)} {}
|
||||
Buffer(Tag tag, std::vector<s16>&& samples) : tag{tag}, samples{std::move(samples)} {}
|
||||
|
||||
/// Returns the raw audio data for the buffer
|
||||
const std::vector<u8>& GetData() const {
|
||||
return data;
|
||||
std::vector<s16>& Samples() {
|
||||
return samples;
|
||||
}
|
||||
|
||||
/// Returns the raw audio data for the buffer
|
||||
const std::vector<s16>& GetSamples() const {
|
||||
return samples;
|
||||
}
|
||||
|
||||
/// Returns the buffer tag, this is provided by the game to the audout service
|
||||
@@ -32,7 +37,7 @@ public:
|
||||
|
||||
private:
|
||||
Tag tag;
|
||||
std::vector<u8> data;
|
||||
std::vector<s16> samples;
|
||||
};
|
||||
|
||||
using BufferPtr = std::shared_ptr<Buffer>;
|
||||
|
||||
77
src/audio_core/codec.cpp
Normal file
77
src/audio_core/codec.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "audio_core/codec.h"
|
||||
|
||||
namespace AudioCore::Codec {
|
||||
|
||||
std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff,
|
||||
ADPCMState& state) {
|
||||
// GC-ADPCM with scale factor and variable coefficients.
|
||||
// Frames are 8 bytes long containing 14 samples each.
|
||||
// Samples are 4 bits (one nibble) long.
|
||||
|
||||
constexpr std::size_t FRAME_LEN = 8;
|
||||
constexpr std::size_t SAMPLES_PER_FRAME = 14;
|
||||
constexpr std::array<int, 16> SIGNED_NIBBLES = {
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
|
||||
|
||||
const std::size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
|
||||
const std::size_t ret_size =
|
||||
sample_count % 2 == 0 ? sample_count : sample_count + 1; // Ensure multiple of two.
|
||||
std::vector<s16> ret(ret_size);
|
||||
|
||||
int yn1 = state.yn1, yn2 = state.yn2;
|
||||
|
||||
const std::size_t NUM_FRAMES =
|
||||
(sample_count + (SAMPLES_PER_FRAME - 1)) / SAMPLES_PER_FRAME; // Round up.
|
||||
for (std::size_t framei = 0; framei < NUM_FRAMES; framei++) {
|
||||
const int frame_header = data[framei * FRAME_LEN];
|
||||
const int scale = 1 << (frame_header & 0xF);
|
||||
const int idx = (frame_header >> 4) & 0x7;
|
||||
|
||||
// Coefficients are fixed point with 11 bits fractional part.
|
||||
const int coef1 = coeff[idx * 2 + 0];
|
||||
const int coef2 = coeff[idx * 2 + 1];
|
||||
|
||||
// Decodes an audio sample. One nibble produces one sample.
|
||||
const auto decode_sample = [&](const int nibble) -> s16 {
|
||||
const int xn = nibble * scale;
|
||||
// We first transform everything into 11 bit fixed point, perform the second order
|
||||
// digital filter, then transform back.
|
||||
// 0x400 == 0.5 in 11 bit fixed point.
|
||||
// Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
|
||||
int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
|
||||
// Clamp to output range.
|
||||
val = std::clamp<s32>(val, -32768, 32767);
|
||||
// Advance output feedback.
|
||||
yn2 = yn1;
|
||||
yn1 = val;
|
||||
return static_cast<s16>(val);
|
||||
};
|
||||
|
||||
std::size_t outputi = framei * SAMPLES_PER_FRAME;
|
||||
std::size_t datai = framei * FRAME_LEN + 1;
|
||||
for (std::size_t i = 0; i < SAMPLES_PER_FRAME && outputi < sample_count; i += 2) {
|
||||
const s16 sample1 = decode_sample(SIGNED_NIBBLES[data[datai] >> 4]);
|
||||
ret[outputi] = sample1;
|
||||
outputi++;
|
||||
|
||||
const s16 sample2 = decode_sample(SIGNED_NIBBLES[data[datai] & 0xF]);
|
||||
ret[outputi] = sample2;
|
||||
outputi++;
|
||||
|
||||
datai++;
|
||||
}
|
||||
}
|
||||
|
||||
state.yn1 = yn1;
|
||||
state.yn2 = yn2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace AudioCore::Codec
|
||||
44
src/audio_core/codec.h
Normal file
44
src/audio_core/codec.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore::Codec {
|
||||
|
||||
enum class PcmFormat : u32 {
|
||||
Invalid = 0,
|
||||
Int8 = 1,
|
||||
Int16 = 2,
|
||||
Int24 = 3,
|
||||
Int32 = 4,
|
||||
PcmFloat = 5,
|
||||
Adpcm = 6,
|
||||
};
|
||||
|
||||
/// See: Codec::DecodeADPCM
|
||||
struct ADPCMState {
|
||||
// Two historical samples from previous processed buffer,
|
||||
// required for ADPCM decoding
|
||||
s16 yn1; ///< y[n-1]
|
||||
s16 yn2; ///< y[n-2]
|
||||
};
|
||||
|
||||
using ADPCM_Coeff = std::array<s16, 16>;
|
||||
|
||||
/**
|
||||
* @param data Pointer to buffer that contains ADPCM data to decode
|
||||
* @param size Size of buffer in bytes
|
||||
* @param coeff ADPCM coefficients
|
||||
* @param state ADPCM state, this is updated with new state
|
||||
* @return Decoded stereo signed PCM16 data, sample_count in length
|
||||
*/
|
||||
std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff,
|
||||
ADPCMState& state);
|
||||
|
||||
}; // namespace AudioCore::Codec
|
||||
@@ -3,31 +3,38 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
|
||||
#include "audio_core/cubeb_sink.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "audio_core/time_stretch.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/ring_buffer.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class SinkStreamImpl final : public SinkStream {
|
||||
class CubebSinkStream final : public SinkStream {
|
||||
public:
|
||||
SinkStreamImpl(cubeb* ctx, cubeb_devid output_device) : ctx{ctx} {
|
||||
cubeb_stream_params params;
|
||||
params.rate = 48000;
|
||||
params.channels = GetNumChannels();
|
||||
params.format = CUBEB_SAMPLE_S16NE;
|
||||
params.layout = CUBEB_LAYOUT_STEREO;
|
||||
CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
|
||||
const std::string& name)
|
||||
: ctx{ctx}, num_channels{std::min(num_channels_, 2u)}, time_stretch{sample_rate,
|
||||
num_channels} {
|
||||
|
||||
u32 minimum_latency = 0;
|
||||
cubeb_stream_params params{};
|
||||
params.rate = sample_rate;
|
||||
params.channels = num_channels;
|
||||
params.format = CUBEB_SAMPLE_S16NE;
|
||||
params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
|
||||
|
||||
u32 minimum_latency{};
|
||||
if (cubeb_get_min_latency(ctx, ¶ms, &minimum_latency) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error getting minimum latency");
|
||||
}
|
||||
|
||||
if (cubeb_stream_init(ctx, &stream_backend, "yuzu Audio Output", nullptr, nullptr,
|
||||
output_device, ¶ms, std::max(512u, minimum_latency),
|
||||
&SinkStreamImpl::DataCallback, &SinkStreamImpl::StateCallback,
|
||||
if (cubeb_stream_init(ctx, &stream_backend, name.c_str(), nullptr, nullptr, output_device,
|
||||
¶ms, std::max(512u, minimum_latency),
|
||||
&CubebSinkStream::DataCallback, &CubebSinkStream::StateCallback,
|
||||
this) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "Error initializing cubeb stream");
|
||||
return;
|
||||
@@ -39,7 +46,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~SinkStreamImpl() {
|
||||
~CubebSinkStream() {
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
@@ -51,33 +58,36 @@ public:
|
||||
cubeb_stream_destroy(stream_backend);
|
||||
}
|
||||
|
||||
void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) override {
|
||||
if (!ctx) {
|
||||
void EnqueueSamples(u32 source_num_channels, const std::vector<s16>& samples) override {
|
||||
if (source_num_channels > num_channels) {
|
||||
// Downsample 6 channels to 2
|
||||
std::vector<s16> buf;
|
||||
buf.reserve(samples.size() * num_channels / source_num_channels);
|
||||
for (std::size_t i = 0; i < samples.size(); i += source_num_channels) {
|
||||
for (std::size_t ch = 0; ch < num_channels; ch++) {
|
||||
buf.push_back(samples[i + ch]);
|
||||
}
|
||||
}
|
||||
queue.Push(buf);
|
||||
return;
|
||||
}
|
||||
|
||||
queue.reserve(queue.size() + sample_count * GetNumChannels());
|
||||
queue.Push(samples);
|
||||
}
|
||||
|
||||
if (num_channels == 2) {
|
||||
// Copy as-is
|
||||
std::copy(samples, samples + sample_count * GetNumChannels(),
|
||||
std::back_inserter(queue));
|
||||
} else if (num_channels == 6) {
|
||||
// Downsample 6 channels to 2
|
||||
const size_t sample_count_copy_size = sample_count * num_channels * 2;
|
||||
queue.reserve(sample_count_copy_size);
|
||||
for (size_t i = 0; i < sample_count * num_channels; i += num_channels) {
|
||||
queue.push_back(samples[i]);
|
||||
queue.push_back(samples[i + 1]);
|
||||
}
|
||||
} else {
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
}
|
||||
std::size_t SamplesInQueue(u32 num_channels) const override {
|
||||
if (!ctx)
|
||||
return 0;
|
||||
|
||||
return queue.Size() / num_channels;
|
||||
}
|
||||
|
||||
void Flush() override {
|
||||
should_flush = true;
|
||||
}
|
||||
|
||||
u32 GetNumChannels() const {
|
||||
// Only support 2-channel stereo output for now
|
||||
return 2;
|
||||
return num_channels;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -85,8 +95,12 @@ private:
|
||||
|
||||
cubeb* ctx{};
|
||||
cubeb_stream* stream_backend{};
|
||||
u32 num_channels{};
|
||||
|
||||
std::vector<s16> queue;
|
||||
Common::RingBuffer<s16, 0x10000> queue;
|
||||
std::array<s16, 2> last_frame;
|
||||
std::atomic<bool> should_flush{};
|
||||
TimeStretcher time_stretch;
|
||||
|
||||
static long DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames);
|
||||
@@ -105,10 +119,10 @@ CubebSink::CubebSink(std::string target_device_name) {
|
||||
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
|
||||
} else {
|
||||
const auto collection_end{collection.device + collection.count};
|
||||
const auto device{std::find_if(collection.device, collection_end,
|
||||
[&](const cubeb_device_info& device) {
|
||||
return target_device_name == device.friendly_name;
|
||||
})};
|
||||
const auto device{
|
||||
std::find_if(collection.device, collection_end, [&](const cubeb_device_info& info) {
|
||||
return target_device_name == info.friendly_name;
|
||||
})};
|
||||
if (device != collection_end) {
|
||||
output_device = device->devid;
|
||||
}
|
||||
@@ -129,43 +143,62 @@ CubebSink::~CubebSink() {
|
||||
cubeb_destroy(ctx);
|
||||
}
|
||||
|
||||
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels) {
|
||||
sink_streams.push_back(std::make_unique<SinkStreamImpl>(ctx, output_device));
|
||||
SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
|
||||
const std::string& name) {
|
||||
sink_streams.push_back(
|
||||
std::make_unique<CubebSinkStream>(ctx, sample_rate, num_channels, output_device, name));
|
||||
return *sink_streams.back();
|
||||
}
|
||||
|
||||
long SinkStreamImpl::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames) {
|
||||
SinkStreamImpl* impl = static_cast<SinkStreamImpl*>(user_data);
|
||||
long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
|
||||
void* output_buffer, long num_frames) {
|
||||
CubebSinkStream* impl = static_cast<CubebSinkStream*>(user_data);
|
||||
u8* buffer = reinterpret_cast<u8*>(output_buffer);
|
||||
|
||||
if (!impl) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const size_t frames_to_write{
|
||||
std::min(impl->queue.size() / impl->GetNumChannels(), static_cast<size_t>(num_frames))};
|
||||
const std::size_t num_channels = impl->GetNumChannels();
|
||||
const std::size_t samples_to_write = num_channels * num_frames;
|
||||
std::size_t samples_written;
|
||||
|
||||
memcpy(buffer, impl->queue.data(), frames_to_write * sizeof(s16) * impl->GetNumChannels());
|
||||
impl->queue.erase(impl->queue.begin(),
|
||||
impl->queue.begin() + frames_to_write * impl->GetNumChannels());
|
||||
if (Settings::values.enable_audio_stretching) {
|
||||
const std::vector<s16> in{impl->queue.Pop()};
|
||||
const std::size_t num_in{in.size() / num_channels};
|
||||
s16* const out{reinterpret_cast<s16*>(buffer)};
|
||||
const std::size_t out_frames =
|
||||
impl->time_stretch.Process(in.data(), num_in, out, num_frames);
|
||||
samples_written = out_frames * num_channels;
|
||||
|
||||
if (frames_to_write < num_frames) {
|
||||
// Fill the rest of the frames with silence
|
||||
memset(buffer + frames_to_write * sizeof(s16) * impl->GetNumChannels(), 0,
|
||||
(num_frames - frames_to_write) * sizeof(s16) * impl->GetNumChannels());
|
||||
if (impl->should_flush) {
|
||||
impl->time_stretch.Flush();
|
||||
impl->should_flush = false;
|
||||
}
|
||||
} else {
|
||||
samples_written = impl->queue.Pop(buffer, samples_to_write);
|
||||
}
|
||||
|
||||
if (samples_written >= num_channels) {
|
||||
std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
|
||||
num_channels * sizeof(s16));
|
||||
}
|
||||
|
||||
// Fill the rest of the frames with last_frame
|
||||
for (std::size_t i = samples_written; i < samples_to_write; i += num_channels) {
|
||||
std::memcpy(buffer + i * sizeof(s16), &impl->last_frame[0], num_channels * sizeof(s16));
|
||||
}
|
||||
|
||||
return num_frames;
|
||||
}
|
||||
|
||||
void SinkStreamImpl::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
|
||||
void CubebSinkStream::StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state) {}
|
||||
|
||||
std::vector<std::string> ListCubebSinkDevices() {
|
||||
std::vector<std::string> device_list;
|
||||
cubeb* ctx;
|
||||
|
||||
if (cubeb_init(&ctx, "Citra Device Enumerator", nullptr) != CUBEB_OK) {
|
||||
if (cubeb_init(&ctx, "yuzu Device Enumerator", nullptr) != CUBEB_OK) {
|
||||
LOG_CRITICAL(Audio_Sink, "cubeb_init failed");
|
||||
return {};
|
||||
}
|
||||
@@ -174,7 +207,7 @@ std::vector<std::string> ListCubebSinkDevices() {
|
||||
if (cubeb_enumerate_devices(ctx, CUBEB_DEVICE_TYPE_OUTPUT, &collection) != CUBEB_OK) {
|
||||
LOG_WARNING(Audio_Sink, "Audio output device enumeration not supported");
|
||||
} else {
|
||||
for (size_t i = 0; i < collection.count; i++) {
|
||||
for (std::size_t i = 0; i < collection.count; i++) {
|
||||
const cubeb_device_info& device = collection.device[i];
|
||||
if (device.friendly_name) {
|
||||
device_list.emplace_back(device.friendly_name);
|
||||
|
||||
@@ -18,7 +18,8 @@ public:
|
||||
explicit CubebSink(std::string device_id);
|
||||
~CubebSink() override;
|
||||
|
||||
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) override;
|
||||
SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
|
||||
const std::string& name) override;
|
||||
|
||||
private:
|
||||
cubeb* ctx{};
|
||||
|
||||
@@ -13,14 +13,20 @@ public:
|
||||
explicit NullSink(std::string){};
|
||||
~NullSink() override = default;
|
||||
|
||||
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/) override {
|
||||
SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/,
|
||||
const std::string& /*name*/) override {
|
||||
return null_sink_stream;
|
||||
}
|
||||
|
||||
private:
|
||||
struct NullSinkStreamImpl final : SinkStream {
|
||||
void EnqueueSamples(u32 /*num_channels*/, const s16* /*samples*/,
|
||||
size_t /*sample_count*/) override {}
|
||||
void EnqueueSamples(u32 /*num_channels*/, const std::vector<s16>& /*samples*/) override {}
|
||||
|
||||
std::size_t SamplesInQueue(u32 /*num_channels*/) const override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Flush() override {}
|
||||
} null_sink_stream;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "audio_core/sink_stream.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -21,7 +22,8 @@ constexpr char auto_device_name[] = "auto";
|
||||
class Sink {
|
||||
public:
|
||||
virtual ~Sink() = default;
|
||||
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels) = 0;
|
||||
virtual SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels,
|
||||
const std::string& name) = 0;
|
||||
};
|
||||
|
||||
using SinkPtr = std::unique_ptr<Sink>;
|
||||
|
||||
@@ -24,7 +24,7 @@ const std::vector<SinkDetails> g_sink_details = {
|
||||
[] { return std::vector<std::string>{"null"}; }},
|
||||
};
|
||||
|
||||
const SinkDetails& GetSinkDetails(std::string sink_id) {
|
||||
const SinkDetails& GetSinkDetails(std::string_view sink_id) {
|
||||
auto iter =
|
||||
std::find_if(g_sink_details.begin(), g_sink_details.end(),
|
||||
[sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; });
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace AudioCore {
|
||||
@@ -13,20 +16,22 @@ namespace AudioCore {
|
||||
class Sink;
|
||||
|
||||
struct SinkDetails {
|
||||
SinkDetails(const char* id_, std::function<std::unique_ptr<Sink>(std::string)> factory_,
|
||||
std::function<std::vector<std::string>()> list_devices_)
|
||||
: id(id_), factory(factory_), list_devices(list_devices_) {}
|
||||
using FactoryFn = std::function<std::unique_ptr<Sink>(std::string)>;
|
||||
using ListDevicesFn = std::function<std::vector<std::string>()>;
|
||||
|
||||
SinkDetails(const char* id_, FactoryFn factory_, ListDevicesFn list_devices_)
|
||||
: id(id_), factory(std::move(factory_)), list_devices(std::move(list_devices_)) {}
|
||||
|
||||
/// Name for this sink.
|
||||
const char* id;
|
||||
/// A method to call to construct an instance of this type of sink.
|
||||
std::function<std::unique_ptr<Sink>(std::string device_id)> factory;
|
||||
FactoryFn factory;
|
||||
/// A method to call to list available devices.
|
||||
std::function<std::vector<std::string>()> list_devices;
|
||||
ListDevicesFn list_devices;
|
||||
};
|
||||
|
||||
extern const std::vector<SinkDetails> g_sink_details;
|
||||
|
||||
const SinkDetails& GetSinkDetails(std::string sink_id);
|
||||
const SinkDetails& GetSinkDetails(std::string_view sink_id);
|
||||
|
||||
} // namespace AudioCore
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
@@ -22,9 +23,12 @@ public:
|
||||
* Feed stereo samples to sink.
|
||||
* @param num_channels Number of channels used.
|
||||
* @param samples Samples in interleaved stereo PCM16 format.
|
||||
* @param sample_count Number of samples.
|
||||
*/
|
||||
virtual void EnqueueSamples(u32 num_channels, const s16* samples, size_t sample_count) = 0;
|
||||
virtual void EnqueueSamples(u32 num_channels, const std::vector<s16>& samples) = 0;
|
||||
|
||||
virtual std::size_t SamplesInQueue(u32 num_channels) const = 0;
|
||||
|
||||
virtual void Flush() = 0;
|
||||
};
|
||||
|
||||
using SinkStreamPtr = std::unique_ptr<SinkStream>;
|
||||
|
||||
@@ -7,16 +7,18 @@
|
||||
|
||||
#include "audio_core/sink.h"
|
||||
#include "audio_core/sink_details.h"
|
||||
#include "audio_core/sink_stream.h"
|
||||
#include "audio_core/stream.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/core_timing_util.h"
|
||||
#include "core/settings.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
constexpr size_t MaxAudioBufferCount{32};
|
||||
constexpr std::size_t MaxAudioBufferCount{32};
|
||||
|
||||
u32 Stream::GetNumChannels() const {
|
||||
switch (format) {
|
||||
@@ -32,17 +34,13 @@ u32 Stream::GetNumChannels() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
u32 Stream::GetSampleSize() const {
|
||||
return GetNumChannels() * 2;
|
||||
}
|
||||
|
||||
Stream::Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
|
||||
SinkStream& sink_stream)
|
||||
SinkStream& sink_stream, std::string&& name_)
|
||||
: sample_rate{sample_rate}, format{format}, release_callback{std::move(release_callback)},
|
||||
sink_stream{sink_stream} {
|
||||
sink_stream{sink_stream}, name{std::move(name_)} {
|
||||
|
||||
release_event = CoreTiming::RegisterEvent(
|
||||
"Stream::Release", [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
|
||||
name, [this](u64 userdata, int cycles_late) { ReleaseActiveBuffer(); });
|
||||
}
|
||||
|
||||
void Stream::Play() {
|
||||
@@ -51,21 +49,24 @@ void Stream::Play() {
|
||||
}
|
||||
|
||||
void Stream::Stop() {
|
||||
state = State::Stopped;
|
||||
ASSERT_MSG(false, "Unimplemented");
|
||||
}
|
||||
|
||||
Stream::State Stream::GetState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||
const size_t num_samples{buffer.GetData().size() / GetSampleSize()};
|
||||
const std::size_t num_samples{buffer.GetSamples().size() / GetNumChannels()};
|
||||
return CoreTiming::usToCycles((static_cast<u64>(num_samples) * 1000000) / sample_rate);
|
||||
}
|
||||
|
||||
static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
|
||||
std::vector<s16> samples(data.size() / sizeof(s16));
|
||||
std::memcpy(samples.data(), data.data(), data.size());
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
|
||||
|
||||
if (volume == 1.0f) {
|
||||
return samples;
|
||||
return;
|
||||
}
|
||||
|
||||
// Implementation of a volume slider with a dynamic range of 60 dB
|
||||
@@ -73,13 +74,12 @@ static std::vector<s16> GetVolumeAdjustedSamples(const std::vector<u8>& data) {
|
||||
for (auto& sample : samples) {
|
||||
sample = static_cast<s16>(sample * volume_scale_factor);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
void Stream::PlayNextBuffer() {
|
||||
if (!IsPlaying()) {
|
||||
// Ensure we are in playing state before playing the next buffer
|
||||
sink_stream.Flush();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -90,20 +90,25 @@ void Stream::PlayNextBuffer() {
|
||||
|
||||
if (queued_buffers.empty()) {
|
||||
// No queued buffers - we are effectively paused
|
||||
sink_stream.Flush();
|
||||
return;
|
||||
}
|
||||
|
||||
active_buffer = queued_buffers.front();
|
||||
queued_buffers.pop();
|
||||
|
||||
const size_t sample_count{active_buffer->GetData().size() / GetSampleSize()};
|
||||
sink_stream.EnqueueSamples(
|
||||
GetNumChannels(), GetVolumeAdjustedSamples(active_buffer->GetData()).data(), sample_count);
|
||||
VolumeAdjustSamples(active_buffer->Samples());
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
CoreTiming::ScheduleEventThreadsafe(GetBufferReleaseCycles(*active_buffer), release_event, {});
|
||||
}
|
||||
|
||||
MICROPROFILE_DEFINE(AudioOutput, "Audio", "ReleaseActiveBuffer", MP_RGB(100, 100, 255));
|
||||
|
||||
void Stream::ReleaseActiveBuffer() {
|
||||
MICROPROFILE_SCOPE(AudioOutput);
|
||||
ASSERT(active_buffer);
|
||||
released_buffers.push(std::move(active_buffer));
|
||||
release_callback();
|
||||
PlayNextBuffer();
|
||||
@@ -123,9 +128,9 @@ bool Stream::ContainsBuffer(Buffer::Tag tag) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(size_t max_count) {
|
||||
std::vector<Buffer::Tag> Stream::GetTagsAndReleaseBuffers(std::size_t max_count) {
|
||||
std::vector<Buffer::Tag> tags;
|
||||
for (size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
||||
for (std::size_t count = 0; count < max_count && !released_buffers.empty(); ++count) {
|
||||
tags.push_back(released_buffers.front()->GetTag());
|
||||
released_buffers.pop();
|
||||
}
|
||||
|
||||
@@ -6,17 +6,21 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include "audio_core/buffer.h"
|
||||
#include "audio_core/sink_stream.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "core/core_timing.h"
|
||||
|
||||
namespace CoreTiming {
|
||||
struct EventType;
|
||||
}
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class SinkStream;
|
||||
|
||||
/**
|
||||
* Represents an audio stream, which is a sequence of queued buffers, to be outputed by AudioOut
|
||||
*/
|
||||
@@ -29,11 +33,17 @@ public:
|
||||
Multi51Channel16,
|
||||
};
|
||||
|
||||
/// Current state of the stream
|
||||
enum class State {
|
||||
Stopped,
|
||||
Playing,
|
||||
};
|
||||
|
||||
/// Callback function type, used to change guest state on a buffer being released
|
||||
using ReleaseCallback = std::function<void()>;
|
||||
|
||||
Stream(u32 sample_rate, Format format, ReleaseCallback&& release_callback,
|
||||
SinkStream& sink_stream);
|
||||
SinkStream& sink_stream, std::string&& name_);
|
||||
|
||||
/// Plays the audio stream
|
||||
void Play();
|
||||
@@ -48,7 +58,7 @@ public:
|
||||
bool ContainsBuffer(Buffer::Tag tag) const;
|
||||
|
||||
/// Returns a vector of recently released buffers specified by tag
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(size_t max_count);
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
|
||||
|
||||
/// Returns true if the stream is currently playing
|
||||
bool IsPlaying() const {
|
||||
@@ -56,7 +66,7 @@ public:
|
||||
}
|
||||
|
||||
/// Returns the number of queued buffers
|
||||
size_t GetQueueSize() const {
|
||||
std::size_t GetQueueSize() const {
|
||||
return queued_buffers.size();
|
||||
}
|
||||
|
||||
@@ -68,16 +78,10 @@ public:
|
||||
/// Gets the number of channels
|
||||
u32 GetNumChannels() const;
|
||||
|
||||
/// Gets the sample size in bytes
|
||||
u32 GetSampleSize() const;
|
||||
/// Get the state
|
||||
State GetState() const;
|
||||
|
||||
private:
|
||||
/// Current state of the stream
|
||||
enum class State {
|
||||
Stopped,
|
||||
Playing,
|
||||
};
|
||||
|
||||
/// Plays the next queued buffer in the audio stream, starting playback if necessary
|
||||
void PlayNextBuffer();
|
||||
|
||||
@@ -96,6 +100,7 @@ private:
|
||||
std::queue<BufferPtr> queued_buffers; ///< Buffers queued to be played in the stream
|
||||
std::queue<BufferPtr> released_buffers; ///< Buffers recently released from the stream
|
||||
SinkStream& sink_stream; ///< Output sink for the stream
|
||||
std::string name; ///< Name of the stream, must be unique
|
||||
};
|
||||
|
||||
using StreamPtr = std::shared_ptr<Stream>;
|
||||
|
||||
69
src/audio_core/time_stretch.cpp
Normal file
69
src/audio_core/time_stretch.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include "audio_core/time_stretch.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count)
|
||||
: m_sample_rate(sample_rate), m_channel_count(channel_count) {
|
||||
m_sound_touch.setChannels(channel_count);
|
||||
m_sound_touch.setSampleRate(sample_rate);
|
||||
m_sound_touch.setPitch(1.0);
|
||||
m_sound_touch.setTempo(1.0);
|
||||
}
|
||||
|
||||
void TimeStretcher::Clear() {
|
||||
m_sound_touch.clear();
|
||||
}
|
||||
|
||||
void TimeStretcher::Flush() {
|
||||
m_sound_touch.flush();
|
||||
}
|
||||
|
||||
std::size_t TimeStretcher::Process(const s16* in, std::size_t num_in, s16* out,
|
||||
std::size_t num_out) {
|
||||
const double time_delta = static_cast<double>(num_out) / m_sample_rate; // seconds
|
||||
|
||||
// We were given actual_samples number of samples, and num_samples were requested from us.
|
||||
double current_ratio = static_cast<double>(num_in) / static_cast<double>(num_out);
|
||||
|
||||
const double max_latency = 1.0; // seconds
|
||||
const double max_backlog = m_sample_rate * max_latency;
|
||||
const double backlog_fullness = m_sound_touch.numSamples() / max_backlog;
|
||||
if (backlog_fullness > 5.0) {
|
||||
// Too many samples in backlog: Don't push anymore on
|
||||
num_in = 0;
|
||||
}
|
||||
|
||||
// We ideally want the backlog to be about 50% full.
|
||||
// This gives some headroom both ways to prevent underflow and overflow.
|
||||
// We tweak current_ratio to encourage this.
|
||||
constexpr double tweak_time_scale = 0.05; // seconds
|
||||
const double tweak_correction = (backlog_fullness - 0.5) * (time_delta / tweak_time_scale);
|
||||
current_ratio *= std::pow(1.0 + 2.0 * tweak_correction, tweak_correction < 0 ? 3.0 : 1.0);
|
||||
|
||||
// This low-pass filter smoothes out variance in the calculated stretch ratio.
|
||||
// The time-scale determines how responsive this filter is.
|
||||
constexpr double lpf_time_scale = 2.0; // seconds
|
||||
const double lpf_gain = 1.0 - std::exp(-time_delta / lpf_time_scale);
|
||||
m_stretch_ratio += lpf_gain * (current_ratio - m_stretch_ratio);
|
||||
|
||||
// Place a lower limit of 5% speed. When a game boots up, there will be
|
||||
// many silence samples. These do not need to be timestretched.
|
||||
m_stretch_ratio = std::max(m_stretch_ratio, 0.05);
|
||||
m_sound_touch.setTempo(m_stretch_ratio);
|
||||
|
||||
LOG_TRACE(Audio, "{:5}/{:5} ratio:{:0.6f} backlog:{:0.6f}", num_in, num_out, m_stretch_ratio,
|
||||
backlog_fullness);
|
||||
|
||||
m_sound_touch.putSamples(in, static_cast<u32>(num_in));
|
||||
return m_sound_touch.receiveSamples(out, static_cast<u32>(num_out));
|
||||
}
|
||||
|
||||
} // namespace AudioCore
|
||||
35
src/audio_core/time_stretch.h
Normal file
35
src/audio_core/time_stretch.h
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <SoundTouch.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace AudioCore {
|
||||
|
||||
class TimeStretcher {
|
||||
public:
|
||||
TimeStretcher(u32 sample_rate, u32 channel_count);
|
||||
|
||||
/// @param in Input sample buffer
|
||||
/// @param num_in Number of input frames in `in`
|
||||
/// @param out Output sample buffer
|
||||
/// @param num_out Desired number of output frames in `out`
|
||||
/// @returns Actual number of frames written to `out`
|
||||
std::size_t Process(const s16* in, std::size_t num_in, s16* out, std::size_t num_out);
|
||||
|
||||
void Clear();
|
||||
|
||||
void Flush();
|
||||
|
||||
private:
|
||||
u32 m_sample_rate;
|
||||
u32 m_channel_count;
|
||||
soundtouch::SoundTouch m_sound_touch;
|
||||
double m_stretch_ratio = 1.0;
|
||||
};
|
||||
|
||||
} // namespace AudioCore
|
||||
@@ -1,13 +1,16 @@
|
||||
# Generate cpp with Git revision from template
|
||||
# Also if this is a CI build, add the build name (ie: Nightly, Bleeding Edge) to the scm_rev file as well
|
||||
# Also if this is a CI build, add the build name (ie: Nightly, Canary) to the scm_rev file as well
|
||||
set(REPO_NAME "")
|
||||
set(BUILD_VERSION "0")
|
||||
if ($ENV{CI})
|
||||
if ($ENV{TRAVIS})
|
||||
set(BUILD_REPOSITORY $ENV{TRAVIS_REPO_SLUG})
|
||||
set(BUILD_TAG $ENV{TRAVIS_TAG})
|
||||
elseif($ENV{APPVEYOR})
|
||||
set(BUILD_REPOSITORY $ENV{APPVEYOR_REPO_NAME})
|
||||
set(BUILD_TAG $ENV{APPVEYOR_REPO_TAG_NAME})
|
||||
endif()
|
||||
# regex capture the string nightly or bleeding-edge into CMAKE_MATCH_1
|
||||
# regex capture the string nightly or canary into CMAKE_MATCH_1
|
||||
string(REGEX MATCH "yuzu-emu/yuzu-?(.*)" OUTVAR ${BUILD_REPOSITORY})
|
||||
if (${CMAKE_MATCH_COUNT} GREATER 0)
|
||||
# capitalize the first letter of each word in the repo name.
|
||||
@@ -16,10 +19,21 @@ if ($ENV{CI})
|
||||
string(SUBSTRING ${WORD} 0 1 FIRST_LETTER)
|
||||
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 it's styled in the title bar.
|
||||
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER} ")
|
||||
set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER}")
|
||||
endforeach()
|
||||
if (BUILD_TAG)
|
||||
string(REGEX MATCH "${CMAKE_MATCH_1}-([0-9]+)" OUTVAR ${BUILD_TAG})
|
||||
if (${CMAKE_MATCH_COUNT} GREATER 0)
|
||||
set(BUILD_VERSION ${CMAKE_MATCH_1})
|
||||
endif()
|
||||
if (BUILD_VERSION)
|
||||
# This leaves a trailing space on the last word, but we actually want that
|
||||
# because of how it's styled in the title bar.
|
||||
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
|
||||
else()
|
||||
set(BUILD_FULLNAME "")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp" @ONLY)
|
||||
@@ -27,10 +41,10 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU
|
||||
add_library(common STATIC
|
||||
alignment.h
|
||||
assert.h
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
bit_field.h
|
||||
bit_set.h
|
||||
break_points.cpp
|
||||
break_points.h
|
||||
cityhash.cpp
|
||||
cityhash.h
|
||||
color.h
|
||||
@@ -40,6 +54,8 @@ add_library(common STATIC
|
||||
file_util.cpp
|
||||
file_util.h
|
||||
hash.h
|
||||
hex_util.cpp
|
||||
hex_util.h
|
||||
logging/backend.cpp
|
||||
logging/backend.h
|
||||
logging/filter.cpp
|
||||
@@ -57,6 +73,7 @@ add_library(common STATIC
|
||||
param_package.cpp
|
||||
param_package.h
|
||||
quaternion.h
|
||||
ring_buffer.h
|
||||
scm_rev.cpp
|
||||
scm_rev.h
|
||||
scope_exit.h
|
||||
@@ -72,6 +89,7 @@ add_library(common STATIC
|
||||
timer.cpp
|
||||
timer.h
|
||||
vector_math.h
|
||||
web_result.h
|
||||
)
|
||||
|
||||
if(ARCHITECTURE_x86_64)
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
namespace Common {
|
||||
|
||||
template <typename T>
|
||||
constexpr T AlignUp(T value, size_t size) {
|
||||
static_assert(std::is_unsigned<T>::value, "T must be an unsigned value.");
|
||||
constexpr T AlignUp(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value + (size - value % size) % size);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
constexpr T AlignDown(T value, size_t size) {
|
||||
static_assert(std::is_unsigned<T>::value, "T must be an unsigned value.");
|
||||
constexpr T AlignDown(T value, std::size_t size) {
|
||||
static_assert(std::is_unsigned_v<T>, "T must be an unsigned value.");
|
||||
return static_cast<T>(value - value % size);
|
||||
}
|
||||
|
||||
|
||||
@@ -129,8 +129,8 @@ private:
|
||||
|
||||
public:
|
||||
/// Constants to allow limited introspection of fields if needed
|
||||
static constexpr size_t position = Position;
|
||||
static constexpr size_t bits = Bits;
|
||||
static constexpr std::size_t position = Position;
|
||||
static constexpr std::size_t bits = Bits;
|
||||
static constexpr StorageType mask = (((StorageTypeU)~0) >> (8 * sizeof(T) - bits)) << position;
|
||||
|
||||
/**
|
||||
@@ -178,8 +178,7 @@ public:
|
||||
return ExtractValue(storage);
|
||||
}
|
||||
|
||||
// TODO: we may want to change this to explicit operator bool() if it's bug-free in VS2015
|
||||
constexpr FORCE_INLINE bool ToBool() const {
|
||||
constexpr explicit operator bool() const {
|
||||
return Value() != 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ static inline int LeastSignificantSetBit(u64 val) {
|
||||
|
||||
template <typename IntTy>
|
||||
class BitSet {
|
||||
static_assert(!std::is_signed<IntTy>::value, "BitSet should not be used with signed types");
|
||||
static_assert(!std::is_signed_v<IntTy>, "BitSet should not be used with signed types");
|
||||
|
||||
public:
|
||||
// A reference to a particular bit, returned from operator[].
|
||||
@@ -170,14 +170,14 @@ public:
|
||||
m_val |= (IntTy)1 << bit;
|
||||
}
|
||||
|
||||
static BitSet AllTrue(size_t count) {
|
||||
static BitSet AllTrue(std::size_t count) {
|
||||
return BitSet(count == sizeof(IntTy) * 8 ? ~(IntTy)0 : (((IntTy)1 << count) - 1));
|
||||
}
|
||||
|
||||
Ref operator[](size_t bit) {
|
||||
Ref operator[](std::size_t bit) {
|
||||
return Ref(this, (IntTy)1 << bit);
|
||||
}
|
||||
const Ref operator[](size_t bit) const {
|
||||
const Ref operator[](std::size_t bit) const {
|
||||
return (*const_cast<BitSet*>(this))[bit];
|
||||
}
|
||||
bool operator==(BitSet other) const {
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include "common/break_points.h"
|
||||
|
||||
bool BreakPoints::IsAddressBreakPoint(u32 iAddress) const {
|
||||
auto cond = [&iAddress](const TBreakPoint& bp) { return bp.iAddress == iAddress; };
|
||||
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
|
||||
return it != m_BreakPoints.end();
|
||||
}
|
||||
|
||||
bool BreakPoints::IsTempBreakPoint(u32 iAddress) const {
|
||||
auto cond = [&iAddress](const TBreakPoint& bp) {
|
||||
return bp.iAddress == iAddress && bp.bTemporary;
|
||||
};
|
||||
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
|
||||
return it != m_BreakPoints.end();
|
||||
}
|
||||
|
||||
BreakPoints::TBreakPointsStr BreakPoints::GetStrings() const {
|
||||
TBreakPointsStr bps;
|
||||
for (auto breakpoint : m_BreakPoints) {
|
||||
if (!breakpoint.bTemporary) {
|
||||
std::stringstream bp;
|
||||
bp << std::hex << breakpoint.iAddress << " " << (breakpoint.bOn ? "n" : "");
|
||||
bps.push_back(bp.str());
|
||||
}
|
||||
}
|
||||
|
||||
return bps;
|
||||
}
|
||||
|
||||
void BreakPoints::AddFromStrings(const TBreakPointsStr& bps) {
|
||||
for (auto bps_item : bps) {
|
||||
TBreakPoint bp;
|
||||
std::stringstream bpstr;
|
||||
bpstr << std::hex << bps_item;
|
||||
bpstr >> bp.iAddress;
|
||||
bp.bOn = bps_item.find("n") != bps_item.npos;
|
||||
bp.bTemporary = false;
|
||||
Add(bp);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Add(const TBreakPoint& bp) {
|
||||
if (!IsAddressBreakPoint(bp.iAddress)) {
|
||||
m_BreakPoints.push_back(bp);
|
||||
// if (jit)
|
||||
// jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Add(u32 em_address, bool temp) {
|
||||
if (!IsAddressBreakPoint(em_address)) // only add new addresses
|
||||
{
|
||||
TBreakPoint pt; // breakpoint settings
|
||||
pt.bOn = true;
|
||||
pt.bTemporary = temp;
|
||||
pt.iAddress = em_address;
|
||||
|
||||
m_BreakPoints.push_back(pt);
|
||||
|
||||
// if (jit)
|
||||
// jit->GetBlockCache()->InvalidateICache(em_address, 4);
|
||||
}
|
||||
}
|
||||
|
||||
void BreakPoints::Remove(u32 em_address) {
|
||||
auto cond = [&em_address](const TBreakPoint& bp) { return bp.iAddress == em_address; };
|
||||
auto it = std::find_if(m_BreakPoints.begin(), m_BreakPoints.end(), cond);
|
||||
if (it != m_BreakPoints.end())
|
||||
m_BreakPoints.erase(it);
|
||||
}
|
||||
|
||||
void BreakPoints::Clear() {
|
||||
// if (jit)
|
||||
//{
|
||||
// std::for_each(m_BreakPoints.begin(), m_BreakPoints.end(),
|
||||
// [](const TBreakPoint& bp)
|
||||
// {
|
||||
// jit->GetBlockCache()->InvalidateICache(bp.iAddress, 4);
|
||||
// }
|
||||
// );
|
||||
//}
|
||||
|
||||
m_BreakPoints.clear();
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 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"
|
||||
|
||||
class DebugInterface;
|
||||
|
||||
struct TBreakPoint {
|
||||
u32 iAddress;
|
||||
bool bOn;
|
||||
bool bTemporary;
|
||||
};
|
||||
|
||||
// Code breakpoints.
|
||||
class BreakPoints {
|
||||
public:
|
||||
typedef std::vector<TBreakPoint> TBreakPoints;
|
||||
typedef std::vector<std::string> TBreakPointsStr;
|
||||
|
||||
const TBreakPoints& GetBreakPoints() {
|
||||
return m_BreakPoints;
|
||||
}
|
||||
|
||||
TBreakPointsStr GetStrings() const;
|
||||
void AddFromStrings(const TBreakPointsStr& bps);
|
||||
|
||||
// is address breakpoint
|
||||
bool IsAddressBreakPoint(u32 iAddress) const;
|
||||
bool IsTempBreakPoint(u32 iAddress) const;
|
||||
|
||||
// Add BreakPoint
|
||||
void Add(u32 em_address, bool temp = false);
|
||||
void Add(const TBreakPoint& bp);
|
||||
|
||||
// Remove Breakpoint
|
||||
void Remove(u32 iAddress);
|
||||
void Clear();
|
||||
|
||||
void DeleteByAddress(u32 Address);
|
||||
|
||||
private:
|
||||
TBreakPoints m_BreakPoints;
|
||||
u32 m_iBreakOnCount;
|
||||
};
|
||||
@@ -114,7 +114,7 @@ static uint64 HashLen16(uint64 u, uint64 v, uint64 mul) {
|
||||
return b;
|
||||
}
|
||||
|
||||
static uint64 HashLen0to16(const char* s, size_t len) {
|
||||
static uint64 HashLen0to16(const char* s, std::size_t len) {
|
||||
if (len >= 8) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) + k2;
|
||||
@@ -141,7 +141,7 @@ static uint64 HashLen0to16(const char* s, size_t len) {
|
||||
|
||||
// This probably works well for 16-byte strings as well, but it may be overkill
|
||||
// in that case.
|
||||
static uint64 HashLen17to32(const char* s, size_t len) {
|
||||
static uint64 HashLen17to32(const char* s, std::size_t len) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) * k1;
|
||||
uint64 b = Fetch64(s + 8);
|
||||
@@ -170,7 +170,7 @@ static pair<uint64, uint64> WeakHashLen32WithSeeds(const char* s, uint64 a, uint
|
||||
}
|
||||
|
||||
// Return an 8-byte hash for 33 to 64 bytes.
|
||||
static uint64 HashLen33to64(const char* s, size_t len) {
|
||||
static uint64 HashLen33to64(const char* s, std::size_t len) {
|
||||
uint64 mul = k2 + len * 2;
|
||||
uint64 a = Fetch64(s) * k2;
|
||||
uint64 b = Fetch64(s + 8);
|
||||
@@ -191,7 +191,7 @@ static uint64 HashLen33to64(const char* s, size_t len) {
|
||||
return b + x;
|
||||
}
|
||||
|
||||
uint64 CityHash64(const char* s, size_t len) {
|
||||
uint64 CityHash64(const char* s, std::size_t len) {
|
||||
if (len <= 32) {
|
||||
if (len <= 16) {
|
||||
return HashLen0to16(s, len);
|
||||
@@ -212,7 +212,7 @@ uint64 CityHash64(const char* s, size_t len) {
|
||||
x = x * k1 + Fetch64(s);
|
||||
|
||||
// Decrease len to the nearest multiple of 64, and operate on 64-byte chunks.
|
||||
len = (len - 1) & ~static_cast<size_t>(63);
|
||||
len = (len - 1) & ~static_cast<std::size_t>(63);
|
||||
do {
|
||||
x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1;
|
||||
y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1;
|
||||
@@ -229,17 +229,17 @@ uint64 CityHash64(const char* s, size_t len) {
|
||||
HashLen16(v.second, w.second) + x);
|
||||
}
|
||||
|
||||
uint64 CityHash64WithSeed(const char* s, size_t len, uint64 seed) {
|
||||
uint64 CityHash64WithSeed(const char* s, std::size_t len, uint64 seed) {
|
||||
return CityHash64WithSeeds(s, len, k2, seed);
|
||||
}
|
||||
|
||||
uint64 CityHash64WithSeeds(const char* s, size_t len, uint64 seed0, uint64 seed1) {
|
||||
uint64 CityHash64WithSeeds(const char* s, std::size_t len, uint64 seed0, uint64 seed1) {
|
||||
return HashLen16(CityHash64(s, len) - seed0, seed1);
|
||||
}
|
||||
|
||||
// A subroutine for CityHash128(). Returns a decent 128-bit hash for strings
|
||||
// of any length representable in signed long. Based on City and Murmur.
|
||||
static uint128 CityMurmur(const char* s, size_t len, uint128 seed) {
|
||||
static uint128 CityMurmur(const char* s, std::size_t len, uint128 seed) {
|
||||
uint64 a = Uint128Low64(seed);
|
||||
uint64 b = Uint128High64(seed);
|
||||
uint64 c = 0;
|
||||
@@ -269,7 +269,7 @@ static uint128 CityMurmur(const char* s, size_t len, uint128 seed) {
|
||||
return uint128(a ^ b, HashLen16(b, a));
|
||||
}
|
||||
|
||||
uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) {
|
||||
uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed) {
|
||||
if (len < 128) {
|
||||
return CityMurmur(s, len, seed);
|
||||
}
|
||||
@@ -313,7 +313,7 @@ uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) {
|
||||
w.first *= 9;
|
||||
v.first *= k0;
|
||||
// If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s.
|
||||
for (size_t tail_done = 0; tail_done < len;) {
|
||||
for (std::size_t tail_done = 0; tail_done < len;) {
|
||||
tail_done += 32;
|
||||
y = Rotate(x + y, 42) * k0 + v.second;
|
||||
w.first += Fetch64(s + len - tail_done + 16);
|
||||
@@ -331,7 +331,7 @@ uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed) {
|
||||
return uint128(HashLen16(x + v.second, w.second) + y, HashLen16(x + w.second, y + v.second));
|
||||
}
|
||||
|
||||
uint128 CityHash128(const char* s, size_t len) {
|
||||
uint128 CityHash128(const char* s, std::size_t len) {
|
||||
return len >= 16
|
||||
? CityHash128WithSeed(s + 16, len - 16, uint128(Fetch64(s), Fetch64(s + 8) + k0))
|
||||
: CityHash128WithSeed(s, len, uint128(k0, k1));
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
#include <utility>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h> // for size_t.
|
||||
#include <stdlib.h> // for std::size_t.
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -77,22 +77,22 @@ inline uint64_t Uint128High64(const uint128& x) {
|
||||
}
|
||||
|
||||
// Hash function for a byte array.
|
||||
uint64_t CityHash64(const char* buf, size_t len);
|
||||
uint64_t CityHash64(const char* buf, std::size_t len);
|
||||
|
||||
// Hash function for a byte array. For convenience, a 64-bit seed is also
|
||||
// hashed into the result.
|
||||
uint64_t CityHash64WithSeed(const char* buf, size_t len, uint64_t seed);
|
||||
uint64_t CityHash64WithSeed(const char* buf, std::size_t len, uint64_t seed);
|
||||
|
||||
// Hash function for a byte array. For convenience, two seeds are also
|
||||
// hashed into the result.
|
||||
uint64_t CityHash64WithSeeds(const char* buf, size_t len, uint64_t seed0, uint64_t seed1);
|
||||
uint64_t CityHash64WithSeeds(const char* buf, std::size_t len, uint64_t seed0, uint64_t seed1);
|
||||
|
||||
// Hash function for a byte array.
|
||||
uint128 CityHash128(const char* s, size_t len);
|
||||
uint128 CityHash128(const char* s, std::size_t len);
|
||||
|
||||
// Hash function for a byte array. For convenience, a 128-bit seed is also
|
||||
// hashed into the result.
|
||||
uint128 CityHash128WithSeed(const char* s, size_t len, uint128 seed);
|
||||
uint128 CityHash128WithSeed(const char* s, std::size_t len, uint128 seed);
|
||||
|
||||
// Hash 128 input bits down to 64 bits of output.
|
||||
// This is intended to be a reasonably good hash function.
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "common/vector_math.h"
|
||||
@@ -55,7 +57,7 @@ constexpr u8 Convert8To6(u8 value) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
|
||||
inline Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
|
||||
return {bytes[3], bytes[2], bytes[1], bytes[0]};
|
||||
}
|
||||
|
||||
@@ -64,7 +66,7 @@ inline const Math::Vec4<u8> DecodeRGBA8(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
|
||||
inline Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
|
||||
return {bytes[2], bytes[1], bytes[0], 255};
|
||||
}
|
||||
|
||||
@@ -73,7 +75,7 @@ inline const Math::Vec4<u8> DecodeRGB8(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
|
||||
inline Math::Vec4<u8> DecodeRG8(const u8* bytes) {
|
||||
return {bytes[1], bytes[0], 0, 255};
|
||||
}
|
||||
|
||||
@@ -82,8 +84,9 @@ inline const Math::Vec4<u8> DecodeRG8(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
|
||||
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
|
||||
inline Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
|
||||
u16_le pixel;
|
||||
std::memcpy(&pixel, bytes, sizeof(pixel));
|
||||
return {Convert5To8((pixel >> 11) & 0x1F), Convert6To8((pixel >> 5) & 0x3F),
|
||||
Convert5To8(pixel & 0x1F), 255};
|
||||
}
|
||||
@@ -93,8 +96,9 @@ inline const Math::Vec4<u8> DecodeRGB565(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
|
||||
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
|
||||
inline Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
|
||||
u16_le pixel;
|
||||
std::memcpy(&pixel, bytes, sizeof(pixel));
|
||||
return {Convert5To8((pixel >> 11) & 0x1F), Convert5To8((pixel >> 6) & 0x1F),
|
||||
Convert5To8((pixel >> 1) & 0x1F), Convert1To8(pixel & 0x1)};
|
||||
}
|
||||
@@ -104,8 +108,9 @@ inline const Math::Vec4<u8> DecodeRGB5A1(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source color
|
||||
* @return Result color decoded as Math::Vec4<u8>
|
||||
*/
|
||||
inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
|
||||
const u16_le pixel = *reinterpret_cast<const u16_le*>(bytes);
|
||||
inline Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
|
||||
u16_le pixel;
|
||||
std::memcpy(&pixel, bytes, sizeof(pixel));
|
||||
return {Convert4To8((pixel >> 12) & 0xF), Convert4To8((pixel >> 8) & 0xF),
|
||||
Convert4To8((pixel >> 4) & 0xF), Convert4To8(pixel & 0xF)};
|
||||
}
|
||||
@@ -116,7 +121,9 @@ inline const Math::Vec4<u8> DecodeRGBA4(const u8* bytes) {
|
||||
* @return Depth value as an u32
|
||||
*/
|
||||
inline u32 DecodeD16(const u8* bytes) {
|
||||
return *reinterpret_cast<const u16_le*>(bytes);
|
||||
u16_le data;
|
||||
std::memcpy(&data, bytes, sizeof(data));
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -133,7 +140,7 @@ inline u32 DecodeD24(const u8* bytes) {
|
||||
* @param bytes Pointer to encoded source values
|
||||
* @return Resulting values stored as a Math::Vec2
|
||||
*/
|
||||
inline const Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
|
||||
inline Math::Vec2<u32> DecodeD24S8(const u8* bytes) {
|
||||
return {static_cast<u32>((bytes[2] << 16) | (bytes[1] << 8) | bytes[0]), bytes[3]};
|
||||
}
|
||||
|
||||
@@ -175,8 +182,10 @@ inline void EncodeRG8(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
*reinterpret_cast<u16_le*>(bytes) =
|
||||
const u16_le data =
|
||||
(Convert8To5(color.r()) << 11) | (Convert8To6(color.g()) << 5) | Convert8To5(color.b());
|
||||
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -185,9 +194,10 @@ inline void EncodeRGB565(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
*reinterpret_cast<u16_le*>(bytes) = (Convert8To5(color.r()) << 11) |
|
||||
(Convert8To5(color.g()) << 6) |
|
||||
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
|
||||
const u16_le data = (Convert8To5(color.r()) << 11) | (Convert8To5(color.g()) << 6) |
|
||||
(Convert8To5(color.b()) << 1) | Convert8To1(color.a());
|
||||
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,9 +206,10 @@ inline void EncodeRGB5A1(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
* @param bytes Destination pointer to store encoded color
|
||||
*/
|
||||
inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
*reinterpret_cast<u16_le*>(bytes) = (Convert8To4(color.r()) << 12) |
|
||||
(Convert8To4(color.g()) << 8) |
|
||||
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
|
||||
const u16 data = (Convert8To4(color.r()) << 12) | (Convert8To4(color.g()) << 8) |
|
||||
(Convert8To4(color.b()) << 4) | Convert8To4(color.a());
|
||||
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -207,7 +218,8 @@ inline void EncodeRGBA4(const Math::Vec4<u8>& color, u8* bytes) {
|
||||
* @param bytes Pointer where to store the encoded value
|
||||
*/
|
||||
inline void EncodeD16(u32 value, u8* bytes) {
|
||||
*reinterpret_cast<u16_le*>(bytes) = value & 0xFFFF;
|
||||
const u16_le data = static_cast<u16>(value);
|
||||
std::memcpy(bytes, &data, sizeof(data));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -32,6 +32,9 @@
|
||||
#define SDMC_DIR "sdmc"
|
||||
#define NAND_DIR "nand"
|
||||
#define SYSDATA_DIR "sysdata"
|
||||
#define KEYS_DIR "keys"
|
||||
#define LOAD_DIR "load"
|
||||
#define DUMP_DIR "dump"
|
||||
#define LOG_DIR "log"
|
||||
|
||||
// Filenames
|
||||
|
||||
41
src/common/detached_tasks.cpp
Normal file
41
src/common/detached_tasks.cpp
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <thread>
|
||||
#include "common/assert.h"
|
||||
#include "common/detached_tasks.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
DetachedTasks* DetachedTasks::instance = nullptr;
|
||||
|
||||
DetachedTasks::DetachedTasks() {
|
||||
ASSERT(instance == nullptr);
|
||||
instance = this;
|
||||
}
|
||||
|
||||
void DetachedTasks::WaitForAllTasks() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
cv.wait(lock, [this]() { return count == 0; });
|
||||
}
|
||||
|
||||
DetachedTasks::~DetachedTasks() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
ASSERT(count == 0);
|
||||
instance = nullptr;
|
||||
}
|
||||
|
||||
void DetachedTasks::AddTask(std::function<void()> task) {
|
||||
std::unique_lock<std::mutex> lock(instance->mutex);
|
||||
++instance->count;
|
||||
std::thread([task{std::move(task)}]() {
|
||||
task();
|
||||
std::unique_lock<std::mutex> lock(instance->mutex);
|
||||
--instance->count;
|
||||
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
40
src/common/detached_tasks.h
Normal file
40
src/common/detached_tasks.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright 2018 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
|
||||
namespace Common {
|
||||
|
||||
/**
|
||||
* A background manager which ensures that all detached task is finished before program exits.
|
||||
*
|
||||
* Some tasks, telemetry submission for example, prefer executing asynchronously and don't care
|
||||
* about the result. These tasks are suitable for std::thread::detach(). However, this is unsafe if
|
||||
* the task is launched just before the program exits (which is a common case for telemetry), so we
|
||||
* need to block on these tasks on program exit.
|
||||
*
|
||||
* To make detached task safe, a single DetachedTasks object should be placed in the main(), and
|
||||
* call WaitForAllTasks() after all program execution but before global/static variable destruction.
|
||||
* Any potentially unsafe detached task should be executed via DetachedTasks::AddTask.
|
||||
*/
|
||||
class DetachedTasks {
|
||||
public:
|
||||
DetachedTasks();
|
||||
~DetachedTasks();
|
||||
void WaitForAllTasks();
|
||||
|
||||
static void AddTask(std::function<void()> task);
|
||||
|
||||
private:
|
||||
static DetachedTasks* instance;
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex mutex;
|
||||
int count = 0;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
@@ -76,7 +76,7 @@ namespace FileUtil {
|
||||
// Modifies argument.
|
||||
static void StripTailDirSlashes(std::string& fname) {
|
||||
if (fname.length() > 1) {
|
||||
size_t i = fname.length();
|
||||
std::size_t i = fname.length();
|
||||
while (i > 0 && fname[i - 1] == DIR_SEP_CHR)
|
||||
--i;
|
||||
fname.resize(i);
|
||||
@@ -201,7 +201,7 @@ bool CreateFullPath(const std::string& fullPath) {
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t position = 0;
|
||||
std::size_t position = 0;
|
||||
while (true) {
|
||||
// Find next sub path
|
||||
position = fullPath.find(DIR_SEP_CHR, position);
|
||||
@@ -299,7 +299,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
|
||||
std::array<char, 1024> buffer;
|
||||
while (!feof(input.get())) {
|
||||
// read input
|
||||
size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get());
|
||||
std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get());
|
||||
if (rnum != buffer.size()) {
|
||||
if (ferror(input.get()) != 0) {
|
||||
LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}",
|
||||
@@ -309,7 +309,7 @@ bool Copy(const std::string& srcFilename, const std::string& destFilename) {
|
||||
}
|
||||
|
||||
// write output
|
||||
size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get());
|
||||
std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get());
|
||||
if (wnum != rnum) {
|
||||
LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename,
|
||||
destFilename, GetLastErrorMsg());
|
||||
@@ -705,7 +705,10 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
|
||||
#endif
|
||||
paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
|
||||
paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
|
||||
// TODO: Put the logs in a better location for each OS
|
||||
paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP);
|
||||
}
|
||||
@@ -736,15 +739,34 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
|
||||
return paths[path];
|
||||
}
|
||||
|
||||
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
|
||||
std::string GetHactoolConfigurationPath() {
|
||||
#ifdef _WIN32
|
||||
PWSTR pw_local_path = nullptr;
|
||||
if (SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &pw_local_path) != S_OK)
|
||||
return "";
|
||||
std::string local_path = Common::UTF16ToUTF8(pw_local_path);
|
||||
CoTaskMemFree(pw_local_path);
|
||||
return local_path + "\\.switch";
|
||||
#else
|
||||
return GetHomeDirectory() + "/.switch";
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string GetNANDRegistrationDir(bool system) {
|
||||
if (system)
|
||||
return GetUserPath(UserPath::NANDDir) + "system/Contents/registered/";
|
||||
return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/";
|
||||
}
|
||||
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
|
||||
return FileUtil::IOFile(filename, text_file ? "w" : "wb").WriteBytes(str.data(), str.size());
|
||||
}
|
||||
|
||||
size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
|
||||
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str) {
|
||||
IOFile file(filename, text_file ? "r" : "rb");
|
||||
|
||||
if (!file.IsOpen())
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
str.resize(static_cast<u32>(file.GetSize()));
|
||||
return file.ReadArray(&str[0], str.size());
|
||||
@@ -809,7 +831,7 @@ std::vector<std::string> SplitPathComponents(std::string_view filename) {
|
||||
std::string_view GetParentPath(std::string_view path) {
|
||||
const auto name_bck_index = path.rfind('\\');
|
||||
const auto name_fwd_index = path.rfind('/');
|
||||
size_t name_index;
|
||||
std::size_t name_index;
|
||||
|
||||
if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) {
|
||||
name_index = std::min(name_bck_index, name_fwd_index);
|
||||
@@ -848,7 +870,7 @@ std::string_view GetFilename(std::string_view path) {
|
||||
}
|
||||
|
||||
std::string_view GetExtensionFromFilename(std::string_view name) {
|
||||
const size_t index = name.rfind('.');
|
||||
const std::size_t index = name.rfind('.');
|
||||
|
||||
if (index == std::string_view::npos) {
|
||||
return {};
|
||||
@@ -870,11 +892,21 @@ std::string_view RemoveTrailingSlash(std::string_view path) {
|
||||
return path;
|
||||
}
|
||||
|
||||
std::string SanitizePath(std::string_view path_) {
|
||||
std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) {
|
||||
std::string path(path_);
|
||||
std::replace(path.begin(), path.end(), '\\', '/');
|
||||
char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\';
|
||||
char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/';
|
||||
|
||||
if (directory_separator == DirectorySeparator::PlatformDefault) {
|
||||
#ifdef _WIN32
|
||||
type1 = '/';
|
||||
type2 = '\\';
|
||||
#endif
|
||||
}
|
||||
|
||||
std::replace(path.begin(), path.end(), type1, type2);
|
||||
path.erase(std::unique(path.begin(), path.end(),
|
||||
[](char c1, char c2) { return c1 == '/' && c2 == '/'; }),
|
||||
[type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
|
||||
path.end());
|
||||
return std::string(RemoveTrailingSlash(path));
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
@@ -23,10 +24,13 @@ namespace FileUtil {
|
||||
enum class UserPath {
|
||||
CacheDir,
|
||||
ConfigDir,
|
||||
KeysDir,
|
||||
LogDir,
|
||||
NANDDir,
|
||||
RootDir,
|
||||
SDMCDir,
|
||||
LoadDir,
|
||||
DumpDir,
|
||||
SysDataDir,
|
||||
UserDir,
|
||||
};
|
||||
@@ -125,6 +129,10 @@ bool SetCurrentDir(const std::string& directory);
|
||||
// directory. To be used in "multi-user" mode (that is, installed).
|
||||
const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
|
||||
|
||||
std::string GetHactoolConfigurationPath();
|
||||
|
||||
std::string GetNANDRegistrationDir(bool system = false);
|
||||
|
||||
// Returns the path to where the sys file are
|
||||
std::string GetSysDirectory();
|
||||
|
||||
@@ -137,8 +145,9 @@ const std::string& GetExeDirectory();
|
||||
std::string AppDataRoamingDirectory();
|
||||
#endif
|
||||
|
||||
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
|
||||
size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
|
||||
std::size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename);
|
||||
|
||||
std::size_t ReadFileToString(bool text_file, const char* filename, std::string& str);
|
||||
|
||||
/**
|
||||
* Splits the filename into 8.3 format
|
||||
@@ -171,15 +180,19 @@ std::string_view RemoveTrailingSlash(std::string_view path);
|
||||
|
||||
// Creates a new vector containing indices [first, last) from the original.
|
||||
template <typename T>
|
||||
std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) {
|
||||
std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, std::size_t last) {
|
||||
if (first >= last)
|
||||
return {};
|
||||
last = std::min<size_t>(last, vector.size());
|
||||
last = std::min<std::size_t>(last, vector.size());
|
||||
return std::vector<T>(vector.begin() + first, vector.begin() + first + last);
|
||||
}
|
||||
|
||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'.
|
||||
std::string SanitizePath(std::string_view path);
|
||||
enum class DirectorySeparator { ForwardSlash, BackwardSlash, PlatformDefault };
|
||||
|
||||
// Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\'
|
||||
// depending if directory_separator is BackwardSlash or PlatformDefault and running on windows
|
||||
std::string SanitizePath(std::string_view path,
|
||||
DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash);
|
||||
|
||||
// simple wrapper for cstdlib file functions to
|
||||
// hopefully will make error checking easier
|
||||
@@ -203,44 +216,47 @@ public:
|
||||
bool Close();
|
||||
|
||||
template <typename T>
|
||||
size_t ReadArray(T* data, size_t length) const {
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
std::size_t ReadArray(T* data, std::size_t length) const {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Given array does not consist of trivially copyable objects");
|
||||
|
||||
if (!IsOpen())
|
||||
return -1;
|
||||
if (!IsOpen()) {
|
||||
return std::numeric_limits<std::size_t>::max();
|
||||
}
|
||||
|
||||
return std::fread(data, sizeof(T), length, m_file);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t WriteArray(const T* data, size_t length) {
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
std::size_t WriteArray(const T* data, std::size_t length) {
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Given array does not consist of trivially copyable objects");
|
||||
if (!IsOpen())
|
||||
return -1;
|
||||
if (!IsOpen()) {
|
||||
return std::numeric_limits<std::size_t>::max();
|
||||
}
|
||||
|
||||
return std::fwrite(data, sizeof(T), length, m_file);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t ReadBytes(T* data, size_t length) const {
|
||||
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
|
||||
std::size_t ReadBytes(T* data, std::size_t length) const {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
||||
return ReadArray(reinterpret_cast<char*>(data), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t WriteBytes(const T* data, size_t length) {
|
||||
static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable");
|
||||
std::size_t WriteBytes(const T* data, std::size_t length) {
|
||||
static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable");
|
||||
return WriteArray(reinterpret_cast<const char*>(data), length);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t WriteObject(const T& object) {
|
||||
static_assert(!std::is_pointer<T>::value, "Given object is a pointer");
|
||||
std::size_t WriteObject(const T& object) {
|
||||
static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer");
|
||||
return WriteArray(&object, 1);
|
||||
}
|
||||
|
||||
size_t WriteString(const std::string& str) {
|
||||
std::size_t WriteString(const std::string& str) {
|
||||
return WriteArray(str.c_str(), str.length());
|
||||
}
|
||||
|
||||
@@ -269,7 +285,7 @@ private:
|
||||
template <typename T>
|
||||
void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) {
|
||||
#ifdef _MSC_VER
|
||||
fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode);
|
||||
fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode);
|
||||
#else
|
||||
fstream.open(filename.c_str(), openmode);
|
||||
#endif
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Common {
|
||||
* @param len Length of data (in bytes) to compute hash over
|
||||
* @returns 64-bit hash value that was computed over the data block
|
||||
*/
|
||||
static inline u64 ComputeHash64(const void* data, size_t len) {
|
||||
static inline u64 ComputeHash64(const void* data, std::size_t len) {
|
||||
return CityHash64(static_cast<const char*>(data), len);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ static inline u64 ComputeHash64(const void* data, size_t len) {
|
||||
*/
|
||||
template <typename T>
|
||||
static inline u64 ComputeStructHash64(const T& data) {
|
||||
static_assert(std::is_trivially_copyable<T>(),
|
||||
static_assert(std::is_trivially_copyable_v<T>,
|
||||
"Type passed to ComputeStructHash64 must be trivially copyable");
|
||||
return ComputeHash64(&data, sizeof(data));
|
||||
}
|
||||
@@ -38,7 +38,7 @@ template <typename T>
|
||||
struct HashableStruct {
|
||||
// In addition to being trivially copyable, T must also have a trivial default constructor,
|
||||
// because any member initialization would be overridden by memset
|
||||
static_assert(std::is_trivial<T>(), "Type passed to HashableStruct must be trivial");
|
||||
static_assert(std::is_trivial_v<T>, "Type passed to HashableStruct must be trivial");
|
||||
/*
|
||||
* We use a union because "implicitly-defined copy/move constructor for a union X copies the
|
||||
* object representation of X." and "implicitly-defined copy assignment operator for a union X
|
||||
@@ -63,7 +63,7 @@ struct HashableStruct {
|
||||
return !(*this == o);
|
||||
};
|
||||
|
||||
size_t Hash() const {
|
||||
std::size_t Hash() const {
|
||||
return Common::ComputeStructHash64(state);
|
||||
}
|
||||
};
|
||||
|
||||
43
src/common/hex_util.cpp
Normal file
43
src/common/hex_util.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/hex_util.h"
|
||||
#include "common/logging/log.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
u8 ToHexNibble(char c1) {
|
||||
if (c1 >= 65 && c1 <= 70)
|
||||
return c1 - 55;
|
||||
if (c1 >= 97 && c1 <= 102)
|
||||
return c1 - 87;
|
||||
if (c1 >= 48 && c1 <= 57)
|
||||
return c1 - 48;
|
||||
LOG_ERROR(Common, "Invalid hex digit: 0x{:02X}", c1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
|
||||
if (len != 32) {
|
||||
LOG_ERROR(Common,
|
||||
"Attempting to parse string to array that is not of correct size (expected=32, "
|
||||
"actual={}).",
|
||||
len);
|
||||
return {};
|
||||
}
|
||||
return HexStringToArray<16>(str);
|
||||
}
|
||||
|
||||
std::array<u8, 32> operator""_array32(const char* str, std::size_t len) {
|
||||
if (len != 64) {
|
||||
LOG_ERROR(Common,
|
||||
"Attempting to parse string to array that is not of correct size (expected=64, "
|
||||
"actual={}).",
|
||||
len);
|
||||
return {};
|
||||
}
|
||||
return HexStringToArray<32>(str);
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
41
src/common/hex_util.h
Normal file
41
src/common/hex_util.h
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Common {
|
||||
|
||||
u8 ToHexNibble(char c1);
|
||||
|
||||
template <std::size_t Size, bool le = false>
|
||||
std::array<u8, Size> HexStringToArray(std::string_view str) {
|
||||
std::array<u8, Size> out{};
|
||||
if constexpr (le) {
|
||||
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2)
|
||||
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
|
||||
} else {
|
||||
for (std::size_t i = 0; i < 2 * Size; i += 2)
|
||||
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <std::size_t Size>
|
||||
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
|
||||
std::string out;
|
||||
for (u8 c : array)
|
||||
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::array<u8, 0x10> operator"" _array16(const char* str, std::size_t len);
|
||||
std::array<u8, 0x20> operator"" _array32(const char* str, std::size_t len);
|
||||
|
||||
} // namespace Common
|
||||
@@ -135,7 +135,7 @@ FileBackend::FileBackend(const std::string& filename)
|
||||
void FileBackend::Write(const Entry& entry) {
|
||||
// prevent logs from going over the maximum size (in case its spamming and the user doesn't
|
||||
// know)
|
||||
constexpr size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
|
||||
constexpr std::size_t MAX_BYTES_WRITTEN = 50 * 1024L * 1024L;
|
||||
if (!file.IsOpen() || bytes_written > MAX_BYTES_WRITTEN) {
|
||||
return;
|
||||
}
|
||||
@@ -168,35 +168,48 @@ void FileBackend::Write(const Entry& entry) {
|
||||
SUB(Service, AM) \
|
||||
SUB(Service, AOC) \
|
||||
SUB(Service, APM) \
|
||||
SUB(Service, ARP) \
|
||||
SUB(Service, BCAT) \
|
||||
SUB(Service, BPC) \
|
||||
SUB(Service, BTDRV) \
|
||||
SUB(Service, BTM) \
|
||||
SUB(Service, Capture) \
|
||||
SUB(Service, ERPT) \
|
||||
SUB(Service, ETicket) \
|
||||
SUB(Service, EUPLD) \
|
||||
SUB(Service, Fatal) \
|
||||
SUB(Service, FGM) \
|
||||
SUB(Service, Friend) \
|
||||
SUB(Service, FS) \
|
||||
SUB(Service, GRC) \
|
||||
SUB(Service, HID) \
|
||||
SUB(Service, IRS) \
|
||||
SUB(Service, LBL) \
|
||||
SUB(Service, LDN) \
|
||||
SUB(Service, LDR) \
|
||||
SUB(Service, LM) \
|
||||
SUB(Service, Migration) \
|
||||
SUB(Service, Mii) \
|
||||
SUB(Service, MM) \
|
||||
SUB(Service, NCM) \
|
||||
SUB(Service, NFC) \
|
||||
SUB(Service, NFP) \
|
||||
SUB(Service, NIFM) \
|
||||
SUB(Service, NIM) \
|
||||
SUB(Service, NS) \
|
||||
SUB(Service, NVDRV) \
|
||||
SUB(Service, PCIE) \
|
||||
SUB(Service, PCTL) \
|
||||
SUB(Service, PCV) \
|
||||
SUB(Service, PM) \
|
||||
SUB(Service, PREPO) \
|
||||
SUB(Service, PSC) \
|
||||
SUB(Service, SET) \
|
||||
SUB(Service, SM) \
|
||||
SUB(Service, SPL) \
|
||||
SUB(Service, SSL) \
|
||||
SUB(Service, Time) \
|
||||
SUB(Service, USB) \
|
||||
SUB(Service, VI) \
|
||||
SUB(Service, WLAN) \
|
||||
CLS(HW) \
|
||||
@@ -215,6 +228,7 @@ void FileBackend::Write(const Entry& entry) {
|
||||
CLS(Input) \
|
||||
CLS(Network) \
|
||||
CLS(Loader) \
|
||||
CLS(Crypto) \
|
||||
CLS(WebService)
|
||||
|
||||
// GetClassName is a macro defined by Windows.h, grrr...
|
||||
@@ -289,13 +303,14 @@ Backend* GetBackend(std::string_view backend_name) {
|
||||
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
|
||||
unsigned int line_num, const char* function, const char* format,
|
||||
const fmt::format_args& args) {
|
||||
auto filter = Impl::Instance().GetGlobalFilter();
|
||||
auto& instance = Impl::Instance();
|
||||
const auto& filter = instance.GetGlobalFilter();
|
||||
if (!filter.CheckMessage(log_class, log_level))
|
||||
return;
|
||||
|
||||
Entry entry =
|
||||
CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args));
|
||||
|
||||
Impl::Instance().PushEntry(std::move(entry));
|
||||
instance.PushEntry(std::move(entry));
|
||||
}
|
||||
} // namespace Log
|
||||
|
||||
@@ -100,7 +100,7 @@ public:
|
||||
|
||||
private:
|
||||
FileUtil::IOFile file;
|
||||
size_t bytes_written;
|
||||
std::size_t bytes_written;
|
||||
};
|
||||
|
||||
void AddBackend(std::unique_ptr<Backend> backend);
|
||||
|
||||
@@ -71,7 +71,7 @@ void Filter::ResetAll(Level level) {
|
||||
}
|
||||
|
||||
void Filter::SetClassLevel(Class log_class, Level level) {
|
||||
class_levels[static_cast<size_t>(log_class)] = level;
|
||||
class_levels[static_cast<std::size_t>(log_class)] = level;
|
||||
}
|
||||
|
||||
void Filter::ParseFilterString(std::string_view filter_view) {
|
||||
@@ -93,7 +93,8 @@ void Filter::ParseFilterString(std::string_view filter_view) {
|
||||
}
|
||||
|
||||
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)]);
|
||||
return static_cast<u8>(level) >=
|
||||
static_cast<u8>(class_levels[static_cast<std::size_t>(log_class)]);
|
||||
}
|
||||
|
||||
bool Filter::IsDebug() const {
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Log {
|
||||
class Filter {
|
||||
public:
|
||||
/// Initializes the filter with all classes having `default_level` as the minimum level.
|
||||
Filter(Level default_level = Level::Info);
|
||||
explicit Filter(Level default_level = Level::Info);
|
||||
|
||||
/// Resets the filter so that all classes have `level` as the minimum displayed level.
|
||||
void ResetAll(Level level);
|
||||
@@ -49,6 +49,6 @@ public:
|
||||
bool IsDebug() const;
|
||||
|
||||
private:
|
||||
std::array<Level, (size_t)Class::Count> class_levels;
|
||||
std::array<Level, static_cast<std::size_t>(Class::Count)> class_levels;
|
||||
};
|
||||
} // namespace Log
|
||||
|
||||
@@ -12,14 +12,14 @@ namespace Log {
|
||||
/// Specifies the severity or level of detail of the log message.
|
||||
enum class Level : u8 {
|
||||
Trace, ///< Extremely detailed and repetitive debugging information that is likely to
|
||||
/// pollute logs.
|
||||
///< pollute logs.
|
||||
Debug, ///< Less detailed debugging information.
|
||||
Info, ///< Status information from important points during execution.
|
||||
Warning, ///< Minor or potential problems found during execution of a task.
|
||||
Error, ///< Major problems found during execution of a task that prevent it from being
|
||||
/// completed.
|
||||
Critical, ///< Major problems during execution that threathen the stability of the entire
|
||||
/// application.
|
||||
///< completed.
|
||||
Critical, ///< Major problems during execution that threaten the stability of the entire
|
||||
///< application.
|
||||
|
||||
Count ///< Total number of logging levels
|
||||
};
|
||||
@@ -49,41 +49,54 @@ enum class Class : ClassType {
|
||||
Kernel, ///< The HLE implementation of the CTR kernel
|
||||
Kernel_SVC, ///< Kernel system calls
|
||||
Service, ///< HLE implementation of system services. Each major service
|
||||
/// should have its own subclass.
|
||||
///< should have its own subclass.
|
||||
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_ARP, ///< The ARP service
|
||||
Service_Audio, ///< The Audio (Audio control) service
|
||||
Service_BCAT, ///< The BCAT service
|
||||
Service_BPC, ///< The BPC service
|
||||
Service_BTDRV, ///< The Bluetooth driver service
|
||||
Service_BTM, ///< The BTM service
|
||||
Service_Capture, ///< The capture service
|
||||
Service_ERPT, ///< The error reporting service
|
||||
Service_ETicket, ///< The ETicket service
|
||||
Service_EUPLD, ///< The error upload service
|
||||
Service_Fatal, ///< The Fatal service
|
||||
Service_FGM, ///< The FGM service
|
||||
Service_Friend, ///< The friend service
|
||||
Service_FS, ///< The FS (Filesystem) service
|
||||
Service_GRC, ///< The game recording service
|
||||
Service_HID, ///< The HID (Human interface device) service
|
||||
Service_IRS, ///< The IRS service
|
||||
Service_LBL, ///< The LBL (LCD backlight) service
|
||||
Service_LDN, ///< The LDN (Local domain network) service
|
||||
Service_LDR, ///< The loader service
|
||||
Service_LM, ///< The LM (Logger) service
|
||||
Service_Migration, ///< The migration service
|
||||
Service_Mii, ///< The Mii service
|
||||
Service_MM, ///< The MM (Multimedia) service
|
||||
Service_NCM, ///< The NCM service
|
||||
Service_NFC, ///< The NFC (Near-field communication) service
|
||||
Service_NFP, ///< The NFP service
|
||||
Service_NIFM, ///< The NIFM (Network interface) service
|
||||
Service_NIM, ///< The NIM service
|
||||
Service_NS, ///< The NS services
|
||||
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
|
||||
Service_PCIE, ///< The PCIe service
|
||||
Service_PCTL, ///< The PCTL (Parental control) service
|
||||
Service_PCV, ///< The PCV (Parental control) service
|
||||
Service_PCV, ///< The PCV service
|
||||
Service_PM, ///< The PM service
|
||||
Service_PREPO, ///< The PREPO (Play report) service
|
||||
Service_PSC, ///< The PSC service
|
||||
Service_SET, ///< The SET (Settings) service
|
||||
Service_SM, ///< The SM (Service manager) service
|
||||
Service_SPL, ///< The SPL service
|
||||
Service_SSL, ///< The SSL service
|
||||
Service_Time, ///< The time service
|
||||
Service_USB, ///< The USB (Universal Serial Bus) service
|
||||
Service_VI, ///< The VI (Video interface) service
|
||||
Service_WLAN, ///< The WLAN (Wireless local area network) service
|
||||
HW, ///< Low-level hardware emulation
|
||||
@@ -100,6 +113,7 @@ enum class Class : ClassType {
|
||||
Audio_DSP, ///< The HLE implementation of the DSP
|
||||
Audio_Sink, ///< Emulator audio output backend
|
||||
Loader, ///< ROM loader
|
||||
Crypto, ///< Cryptographic engine/functions
|
||||
Input, ///< Input emulation
|
||||
Network, ///< Network emulation
|
||||
WebService, ///< Interface to yuzu Web Services
|
||||
|
||||
@@ -31,7 +31,7 @@ std::string FormatLogMessage(const Entry& entry) {
|
||||
}
|
||||
|
||||
void PrintMessage(const Entry& entry) {
|
||||
auto str = FormatLogMessage(entry) + '\n';
|
||||
const auto str = FormatLogMessage(entry).append(1, '\n');
|
||||
fputs(str.c_str(), stderr);
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ void PrintColoredMessage(const Entry& entry) {
|
||||
return;
|
||||
}
|
||||
|
||||
CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
|
||||
CONSOLE_SCREEN_BUFFER_INFO original_info = {};
|
||||
GetConsoleScreenBufferInfo(console_handle, &original_info);
|
||||
|
||||
WORD color = 0;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user