mirror of
https://github.com/flyinghead/flycast.git
synced 2026-11-08 08:06:48 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
392a429e8b | ||
|
|
0491f2e6a5 | ||
|
|
33d9a58cf9 | ||
|
|
401332d520 | ||
|
|
8511d1ad3c | ||
|
|
d794b53512 | ||
|
|
594d82b223 | ||
|
|
74cbee908f | ||
|
|
4886dcb1c2 | ||
|
|
16fc172ffc | ||
|
|
dfb2ae8987 | ||
|
|
4529a31372 | ||
|
|
5a29ac489f | ||
|
|
c4c3c2ae93 | ||
|
|
f65907a256 | ||
|
|
37a9077304 | ||
|
|
27a913fc02 | ||
|
|
97442c0411 | ||
|
|
5b12f9ca68 | ||
|
|
6a298289a9 | ||
|
|
59ef8579d2 | ||
|
|
14f759eeb7 | ||
|
|
230d971d4d | ||
|
|
5a150f4121 | ||
|
|
f178e5d76e | ||
|
|
9222fa8442 | ||
|
|
77359cefb2 | ||
|
|
a6dc38a35b | ||
|
|
eab952fcbe | ||
|
|
1666eb0875 | ||
|
|
5bce763f66 | ||
|
|
0f4fca415b | ||
|
|
f2fe918379 | ||
|
|
a9be4d111c | ||
|
|
ce07a7fbf5 | ||
|
|
e44c03fa0e | ||
|
|
1dac3695fd | ||
|
|
997f6179dd | ||
|
|
bed524f0d0 | ||
|
|
410b37b041 | ||
|
|
ead4b51d6f | ||
|
|
043bb5ee99 | ||
|
|
4747e61908 | ||
|
|
548cf6fe2e | ||
|
|
339d288dfb | ||
|
|
8f7e4bf2d3 | ||
|
|
922ca3e54b | ||
|
|
4b09de2fc1 | ||
|
|
98db7a6cd5 | ||
|
|
3405b5b2a6 | ||
|
|
4320685ff8 | ||
|
|
cd3fe7393c | ||
|
|
2da0d392fd | ||
|
|
8d1bf3382c | ||
|
|
b1489d665e | ||
|
|
fc56bd283e | ||
|
|
807e4a20ed | ||
|
|
943311752e | ||
|
|
e7ad12b031 | ||
|
|
b2bc6251c7 | ||
|
|
6f38dc3210 | ||
|
|
4bb32466bd | ||
|
|
e26d356265 | ||
|
|
265c5c6c3d | ||
|
|
2b1eb55daa | ||
|
|
bd136b1856 | ||
|
|
bd8adf1da1 | ||
|
|
11625176c2 | ||
|
|
c64441a02f | ||
|
|
8e9eee246d | ||
|
|
d0ec876936 | ||
|
|
b19cbcbf29 | ||
|
|
bf2bd7efed | ||
|
|
d15d5be44a | ||
|
|
70c8d1d22b | ||
|
|
3917c0181b | ||
|
|
6f58d204b6 | ||
|
|
d450c84a14 | ||
|
|
3b7036d14a | ||
|
|
5d628f8167 | ||
|
|
d1ed878446 | ||
|
|
28ea3dc69c | ||
|
|
031a645ffc | ||
|
|
0a526d7c94 | ||
|
|
3d594054eb | ||
|
|
3b651463dc | ||
|
|
4e6b5b270b | ||
|
|
fd8f2e24b8 | ||
|
|
82b4c55f09 | ||
|
|
8176646c5a | ||
|
|
3f0dd626c1 | ||
|
|
8edd8fc447 | ||
|
|
0c7dca9cb7 | ||
|
|
086e8bc20c | ||
|
|
11880bca30 | ||
|
|
37187467e7 | ||
|
|
7b0c106fc2 | ||
|
|
fd654614ae | ||
|
|
4ee24ca612 | ||
|
|
4b3704953f | ||
|
|
6ea9a34e8a | ||
|
|
c018d46d00 | ||
|
|
400da2bdfe | ||
|
|
3b5536dd43 | ||
|
|
34e2f426ac | ||
|
|
a246964a99 | ||
|
|
b4b4b3ab1e | ||
|
|
af5f67c15d | ||
|
|
909ea496ac | ||
|
|
3347dc1a46 | ||
|
|
dd5e148e2b | ||
|
|
1f818ca750 | ||
|
|
ab6b95ff4d | ||
|
|
760c62a3da | ||
|
|
b2d8ad633d | ||
|
|
f9b47b6f91 | ||
|
|
372ef91211 | ||
|
|
3375f6c476 | ||
|
|
c5396a041e | ||
|
|
851dd22664 | ||
|
|
ac32a80352 | ||
|
|
eff11d6a07 | ||
|
|
b6c135b0c6 | ||
|
|
0fe7073496 | ||
|
|
d5c5839a92 | ||
|
|
7ac45a7e46 | ||
|
|
0243f81c26 | ||
|
|
2dfa647445 | ||
|
|
9c5408a6d3 | ||
|
|
d3dcb13ef9 | ||
|
|
2cb2ac23e0 | ||
|
|
e38b0ac01f | ||
|
|
09b27ddd5c | ||
|
|
33833cfd1e | ||
|
|
3c5c1e0b66 | ||
|
|
3f446a5835 | ||
|
|
df3c676a04 | ||
|
|
9ff3ab618c | ||
|
|
ba33b8c3f7 | ||
|
|
eab13e85bc | ||
|
|
a1f1c9825a | ||
|
|
43a9c1384a | ||
|
|
b4ab21a768 | ||
|
|
30365d13ef | ||
|
|
f7eae0ff9e | ||
|
|
e26f170558 | ||
|
|
40e400ab08 | ||
|
|
5743502cdd | ||
|
|
8f33b63153 | ||
|
|
6fca05f00e | ||
|
|
8c07a2e1d6 | ||
|
|
ffbe266413 | ||
|
|
5d3b7fae6b | ||
|
|
738b95d2be | ||
|
|
48c58dbd18 | ||
|
|
54b06a5c34 | ||
|
|
d3a46a1e33 | ||
|
|
4fe2f4760f | ||
|
|
c0809d771d | ||
|
|
54ed92e2b0 | ||
|
|
6ea61b9084 | ||
|
|
f3e0f09ca9 | ||
|
|
301b1a0f00 | ||
|
|
b787427446 | ||
|
|
5df5c70994 | ||
|
|
75defb590b | ||
|
|
fbeaf8ba16 | ||
|
|
3f79b6baed | ||
|
|
34f095a91e | ||
|
|
09247b4503 | ||
|
|
109e084e18 | ||
|
|
af043c55cd | ||
|
|
04999d8969 | ||
|
|
26b8684e78 | ||
|
|
eefcb0e05d | ||
|
|
449d256995 | ||
|
|
0897f0ed8a | ||
|
|
9bdb90e605 | ||
|
|
5b75975237 | ||
|
|
c73336a86f | ||
|
|
27318443a7 | ||
|
|
df93c069f2 | ||
|
|
7358f29556 | ||
|
|
8801b2ae16 | ||
|
|
b10db5a078 | ||
|
|
71341a27e4 | ||
|
|
7e7f645f37 | ||
|
|
146e90dae6 | ||
|
|
bb5bd71437 | ||
|
|
e8217ca0c5 | ||
|
|
8f033723a1 | ||
|
|
b097aa3331 | ||
|
|
64b9e51af0 | ||
|
|
9b91430513 | ||
|
|
d9ec0cb3ab | ||
|
|
39d68ec7fa | ||
|
|
a1a782df38 | ||
|
|
acc106e988 | ||
|
|
11de0eda13 | ||
|
|
a7148ad7ae | ||
|
|
27f19a5b54 | ||
|
|
b973453317 | ||
|
|
ffaada9e9d | ||
|
|
1953eb0eea | ||
|
|
c3275d3c6f | ||
|
|
5e769d5cd2 | ||
|
|
f26e10e44a | ||
|
|
3d60fc8c60 | ||
|
|
7adeb53aee | ||
|
|
eacadf24a6 | ||
|
|
8e14c7f394 | ||
|
|
a842edb09e | ||
|
|
e2247e4872 | ||
|
|
eb662da389 | ||
|
|
2b79f93d6b | ||
|
|
ffc32d2d86 | ||
|
|
892cdb7cea | ||
|
|
90ed30f70c | ||
|
|
5f4eefab58 | ||
|
|
34f8ab292f | ||
|
|
7538424efb | ||
|
|
3976fe1be2 | ||
|
|
c57b2e1aa7 | ||
|
|
9cc3d1030d | ||
|
|
48e964235c | ||
|
|
e148fabeae | ||
|
|
d3b8a67cba | ||
|
|
03f2ef6df0 | ||
|
|
e1ab9af89d | ||
|
|
3330225de4 | ||
|
|
58ce9a4159 | ||
|
|
1b5c8678a4 | ||
|
|
c20ec680a8 | ||
|
|
57cc7b4a60 | ||
|
|
cdef7f7444 | ||
|
|
f62582a4bd | ||
|
|
30e1a858da | ||
|
|
b04f0eb530 | ||
|
|
ef3d3bd246 | ||
|
|
2ca596ae20 | ||
|
|
1273c06d4b | ||
|
|
25a882341d | ||
|
|
75064f5683 | ||
|
|
93aaf50b4f | ||
|
|
332be9c8aa | ||
|
|
77776be20b | ||
|
|
f7676faa68 | ||
|
|
93f463296d | ||
|
|
aff64d43d4 | ||
|
|
240be07bbb | ||
|
|
786ccf4139 | ||
|
|
10a5ed6cec | ||
|
|
b9ba3f1e63 | ||
|
|
34fda43d9c | ||
|
|
b602cc9c57 | ||
|
|
63534c5d56 | ||
|
|
0267de4997 | ||
|
|
56c00b0eb5 | ||
|
|
3114394019 | ||
|
|
e5f13b6858 | ||
|
|
3193d2cf5d | ||
|
|
0f18e2e167 | ||
|
|
5908b1a84a | ||
|
|
4d6168dd0b | ||
|
|
2d1c602a69 | ||
|
|
130675aae3 | ||
|
|
75d5cc4d63 | ||
|
|
5a110fd666 | ||
|
|
13436a4dda | ||
|
|
2631706fe2 | ||
|
|
ea6d5f8732 | ||
|
|
305cae496f | ||
|
|
416085980f | ||
|
|
8b35fb80b4 | ||
|
|
5c1249c55f | ||
|
|
fac44e4a2f | ||
|
|
4ea317d51f | ||
|
|
310d5e612e | ||
|
|
62778b1a68 | ||
|
|
604415b0d2 | ||
|
|
49ea1135ee | ||
|
|
46e2d52b75 | ||
|
|
44f77408e4 | ||
|
|
2860aa96f4 | ||
|
|
8b0a5fa480 | ||
|
|
b150ba2b62 | ||
|
|
48bce25820 | ||
|
|
562f78cd71 | ||
|
|
9e0573aa98 | ||
|
|
d23b3b6bc7 | ||
|
|
1d9f8ab438 | ||
|
|
df3c075fba | ||
|
|
9cdd0aab9f | ||
|
|
e4d9220f3f | ||
|
|
0bf9c64b1b | ||
|
|
4e74619d9e | ||
|
|
0414a90500 | ||
|
|
5866e97d18 | ||
|
|
387e2427b4 | ||
|
|
5187258f4b | ||
|
|
e1da1c3a55 | ||
|
|
84356e3b26 | ||
|
|
c7499df3a8 | ||
|
|
03f7a0f605 | ||
|
|
f3c838d19d | ||
|
|
d6f3ca986d | ||
|
|
b9df7d3469 | ||
|
|
bf1d4a56ae | ||
|
|
a682e20bbd | ||
|
|
5b9dd96064 | ||
|
|
a99a72589b | ||
|
|
77ed9c20de | ||
|
|
7cb0fe53d7 | ||
|
|
3db0fce9b6 | ||
|
|
6a15e7a3aa | ||
|
|
431d71de5e | ||
|
|
914fa29d31 | ||
|
|
c143cae09b | ||
|
|
a35e8214df | ||
|
|
d84108de32 | ||
|
|
1513c6cab8 | ||
|
|
34104724a6 | ||
|
|
b70c2791b2 | ||
|
|
ccdd53ee0e | ||
|
|
57a3e4ca53 | ||
|
|
b92ac1544e | ||
|
|
26d386f833 | ||
|
|
ce0060126e | ||
|
|
4389a3ffa6 | ||
|
|
b3fa453d75 | ||
|
|
32c274f407 | ||
|
|
cb2d3e3776 | ||
|
|
28f5dd983e | ||
|
|
160828a4c7 | ||
|
|
aee932808e | ||
|
|
77636391fc | ||
|
|
944af44c7f | ||
|
|
5c27648699 | ||
|
|
7ef35eaf68 | ||
|
|
bb733323ce | ||
|
|
1b6bf25e7a | ||
|
|
aa65b81f89 | ||
|
|
da9f030a0e | ||
|
|
a0e1eb0005 | ||
|
|
5cc15429cd | ||
|
|
18656f5986 | ||
|
|
536be34853 | ||
|
|
5b513cac5b | ||
|
|
5efdf58e85 | ||
|
|
a93f3697d6 | ||
|
|
b98e833c57 | ||
|
|
2e89848041 | ||
|
|
bb4ba3ed89 | ||
|
|
a5a338d408 | ||
|
|
3114344414 | ||
|
|
20ba79668f | ||
|
|
7a4f9d8667 | ||
|
|
22bff1b017 | ||
|
|
9344856c7e | ||
|
|
b2c1d134e1 | ||
|
|
160d3e7ef3 | ||
|
|
3cf4808033 | ||
|
|
1bd894f941 | ||
|
|
8dc55721d0 | ||
|
|
c94deaee9d | ||
|
|
95583583ca | ||
|
|
42752acef5 | ||
|
|
45c700ab60 | ||
|
|
a3902fc278 | ||
|
|
0606f3e63e | ||
|
|
0558c957e8 | ||
|
|
2ca9187137 | ||
|
|
5add29cbed | ||
|
|
5a1c3f80f5 | ||
|
|
a0e8e9a9df | ||
|
|
45f4378184 | ||
|
|
c7d030c2e6 | ||
|
|
2bee6b1ce4 | ||
|
|
522505273e | ||
|
|
0481f5464d | ||
|
|
7478476f9c | ||
|
|
76c3695f6e | ||
|
|
e18ada7225 | ||
|
|
f3a33642f8 | ||
|
|
2f03c1b147 | ||
|
|
b7e2f5e489 | ||
|
|
d01cbde41c | ||
|
|
11068012fe | ||
|
|
a06791780d | ||
|
|
ee1a7167f6 | ||
|
|
10aaec1619 | ||
|
|
32d8d9a8c3 | ||
|
|
553f77c675 | ||
|
|
9402981b43 | ||
|
|
9038ef6d8a | ||
|
|
1d290560e0 | ||
|
|
59e844db15 | ||
|
|
6853238c8b | ||
|
|
f0aa1abf89 | ||
|
|
cfd3677746 | ||
|
|
50a10598e4 | ||
|
|
9bfd48cf40 | ||
|
|
b72750c7d5 | ||
|
|
b9fdd5070b | ||
|
|
6c3d77b012 | ||
|
|
7fee0d2d29 | ||
|
|
dfd4dbebc4 | ||
|
|
34a5e7f47d | ||
|
|
3f8d645c42 | ||
|
|
ecd0305a60 | ||
|
|
667e3906d8 | ||
|
|
e5b1d56a7c | ||
|
|
d789b57412 | ||
|
|
825778445a | ||
|
|
20517cf0da | ||
|
|
989d3b1370 | ||
|
|
421245aeba | ||
|
|
686f08e7dd | ||
|
|
4af1d096de | ||
|
|
27a0497dd5 | ||
|
|
9ea0563a7a | ||
|
|
a46677e64e | ||
|
|
a7f2008763 | ||
|
|
53e0f12c22 | ||
|
|
34961daaec | ||
|
|
f5389bcd0b | ||
|
|
67eee05c14 | ||
|
|
bb1b1059d4 | ||
|
|
94f35ca54f | ||
|
|
1507097bcd | ||
|
|
e4599e36ed | ||
|
|
15617c1816 | ||
|
|
fb64fc418e | ||
|
|
5656370388 | ||
|
|
354d649ebd | ||
|
|
fff03f64a3 | ||
|
|
a99aa5217b | ||
|
|
89abb7925b | ||
|
|
dea8e74182 | ||
|
|
92c5892d4c | ||
|
|
21f9e9fbc2 | ||
|
|
78fa3ee09e | ||
|
|
de1584d97c | ||
|
|
a40c51e682 | ||
|
|
c860807fef | ||
|
|
b91f82c7e5 | ||
|
|
d38ce47081 | ||
|
|
5f55a9fd3f | ||
|
|
a085fd2f6d | ||
|
|
36c2f5137c | ||
|
|
5e9c168fc9 | ||
|
|
59c2343cad | ||
|
|
b42387e929 | ||
|
|
df78a781ec | ||
|
|
974dc2e233 | ||
|
|
6bf38e5085 | ||
|
|
792aa38d34 | ||
|
|
516982c66c | ||
|
|
a9fa33c17a | ||
|
|
d0584b8652 | ||
|
|
58c8a33d11 | ||
|
|
b4dcc64ef6 | ||
|
|
7531f5df20 | ||
|
|
c4d77165a6 | ||
|
|
aa95c71ead | ||
|
|
6612e3fef8 | ||
|
|
fbd52a72d4 | ||
|
|
d618abc320 | ||
|
|
b7163dda83 | ||
|
|
d259a2e2f5 | ||
|
|
e2f8de33e8 | ||
|
|
42ad5532b2 | ||
|
|
1e30f483c2 | ||
|
|
bdd2d5af7f | ||
|
|
76638df001 | ||
|
|
3efb87ba6d | ||
|
|
c9f238362d | ||
|
|
129673a84b | ||
|
|
7c1c5817d3 | ||
|
|
e394840344 | ||
|
|
fc33002284 | ||
|
|
dff967f60a | ||
|
|
a3708a7e95 | ||
|
|
1cadeb276e | ||
|
|
2b28e819e5 | ||
|
|
db846ca933 | ||
|
|
9cb7d9e5d5 | ||
|
|
93ae9d0375 | ||
|
|
e03e11b8a9 | ||
|
|
52df0133f0 | ||
|
|
d5aeb482d9 | ||
|
|
7eec648690 | ||
|
|
897e06b887 | ||
|
|
aa38771cd0 | ||
|
|
c77ddba109 | ||
|
|
cd8a88366d | ||
|
|
f892630e4b | ||
|
|
9d4d486524 | ||
|
|
d8a3ee9c3a | ||
|
|
fc6142b332 | ||
|
|
5fc84acdfb | ||
|
|
037dc3b4b9 | ||
|
|
96aac66133 | ||
|
|
4d5ced1ce6 | ||
|
|
da3cfd9d58 | ||
|
|
65aadb9dcb | ||
|
|
cec46990ea | ||
|
|
2e5309bfa3 | ||
|
|
742090bb1f | ||
|
|
f3a64e5891 | ||
|
|
32ccb754ff | ||
|
|
32d11dd89c | ||
|
|
760813e45a | ||
|
|
e221f6fed2 | ||
|
|
8108e63907 | ||
|
|
b0403ed103 | ||
|
|
c86ed291cf | ||
|
|
d981c48a75 | ||
|
|
82925c4e81 | ||
|
|
54e8589f63 | ||
|
|
4750ddb646 | ||
|
|
894ac54b80 | ||
|
|
d4a223656f | ||
|
|
b698cc3d10 | ||
|
|
75af269bb8 | ||
|
|
fd475bdd8e | ||
|
|
f71dd6d4d8 | ||
|
|
5aec6d05b0 | ||
|
|
b4937ef271 | ||
|
|
a50164a91b | ||
|
|
595842f606 | ||
|
|
8f74f7883a | ||
|
|
71dc4c3e39 | ||
|
|
6edad8eaba | ||
|
|
ea1cd9d31b | ||
|
|
bb7ad07701 | ||
|
|
9793e54646 | ||
|
|
6d2ed417be | ||
|
|
d3916f13d7 | ||
|
|
b2c6159b10 | ||
|
|
4d73cc8e13 | ||
|
|
5b343562b9 | ||
|
|
b20db6a8bb | ||
|
|
a8bc1c12f4 | ||
|
|
b94acb70a6 | ||
|
|
0c3f5c7a76 | ||
|
|
03d20f9d5a | ||
|
|
01e5cd882c | ||
|
|
556e2ead56 | ||
|
|
ff6a3119b0 | ||
|
|
86657c0c12 | ||
|
|
d7c168f406 | ||
|
|
f37e43b26f | ||
|
|
e427c4d5c4 | ||
|
|
56fe3b9c9d | ||
|
|
30bb864b37 | ||
|
|
d689c50e21 | ||
|
|
efd71b3a44 | ||
|
|
5450646b5c | ||
|
|
55e0211a57 | ||
|
|
5d719e0b49 | ||
|
|
e10a9929da | ||
|
|
3d41e4ea3e | ||
|
|
2dd4cbe33d | ||
|
|
2653c5ed73 | ||
|
|
343021a8c5 | ||
|
|
7907100b9c | ||
|
|
f179c00bdd | ||
|
|
c83b0e325f | ||
|
|
210e8ca6e3 | ||
|
|
a46dc71156 | ||
|
|
4baf529ff7 | ||
|
|
63ef3aaab4 | ||
|
|
abe71409b5 | ||
|
|
f8c12d2835 | ||
|
|
92555c68c6 | ||
|
|
915532f946 | ||
|
|
079180faab | ||
|
|
38de2868a0 | ||
|
|
0e97f97bb7 | ||
|
|
64d7199107 | ||
|
|
0077b8ecec | ||
|
|
5393235c40 | ||
|
|
a81a02f14b | ||
|
|
480f93e50d | ||
|
|
7174ef1194 | ||
|
|
f039a809da | ||
|
|
1223154665 | ||
|
|
8cb847940b | ||
|
|
9691fe9622 | ||
|
|
c575eac89b | ||
|
|
b468c9bdae | ||
|
|
368bd17ae0 | ||
|
|
757732d0f8 | ||
|
|
0ff9caf656 | ||
|
|
a93bd9e64e | ||
|
|
308d9fc1ac | ||
|
|
7a27a43ba4 | ||
|
|
244bb724c9 | ||
|
|
349083030d | ||
|
|
b99b3450c8 | ||
|
|
6061d402bd | ||
|
|
4d06743d9a | ||
|
|
af7353225b | ||
|
|
cb0b667129 | ||
|
|
00f60a3d62 | ||
|
|
5565b79b9e | ||
|
|
ecb468a723 | ||
|
|
fbe7cc8fe3 | ||
|
|
45bf218df4 | ||
|
|
6b3e86619e | ||
|
|
56b8bb1245 | ||
|
|
cea80560db | ||
|
|
088c1e6b0d | ||
|
|
bb048dea8f | ||
|
|
588dca2bfd | ||
|
|
3afa4322f1 | ||
|
|
0b6b8b3753 | ||
|
|
53d092c326 | ||
|
|
bfba850ee7 | ||
|
|
db5a4d1247 | ||
|
|
3ad00596a1 | ||
|
|
39e04b07b8 | ||
|
|
b9cbf3e47f | ||
|
|
2a4356e72c | ||
|
|
136ce267dc | ||
|
|
c6f50fad9d | ||
|
|
916afeb5ef | ||
|
|
81842ccc23 | ||
|
|
4fdd28fab7 |
No files matched your search
@@ -12,7 +12,7 @@ assignees: ''
|
||||
|
||||
Platform / OS / Hardware:
|
||||
|
||||
Github hash:
|
||||
Flycast version:
|
||||
|
||||
Hardware:
|
||||
|
||||
|
||||
@@ -4,19 +4,20 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Android
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Set up build environment
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ccache libcurl4-openssl-dev ninja-build
|
||||
sudo apt-get -y install ccache cmake libcurl4-openssl-dev ninja-build
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '17'
|
||||
@@ -28,6 +29,10 @@ jobs:
|
||||
path: ~/.ccache
|
||||
key: android-ccache-${{ github.sha }}
|
||||
restore-keys: android-ccache-
|
||||
|
||||
- name: Disable release signing
|
||||
run: sed -i 's/signingConfig signingConfigs.release/signingConfig signingConfigs.debug/g' shell/android-studio/flycast/build.gradle
|
||||
if: github.repository != 'flyinghead/flycast' || github.event_name != 'push'
|
||||
|
||||
- name: Bump version code
|
||||
uses: chkfung/[email protected]
|
||||
@@ -37,19 +42,15 @@ jobs:
|
||||
|
||||
- name: Gradle
|
||||
working-directory: shell/android-studio
|
||||
run: ./gradlew bundleRelease --parallel
|
||||
run: ./gradlew assembleRelease bundleRelease --parallel
|
||||
env:
|
||||
SENTRY_UPLOAD_URL: ${{ secrets.SENTRY_UPLOAD_URL }}
|
||||
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
|
||||
|
||||
- name: Upload to Google Play
|
||||
uses: r0adkll/[email protected]
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
|
||||
packageName: com.flycast.emulator
|
||||
releaseFiles: shell/android-studio/flycast/build/outputs/bundle/release/flycast-release.aab
|
||||
track: internal
|
||||
status: draft
|
||||
name: flycast-release.apk
|
||||
path: shell/android-studio/flycast/build/outputs/apk/release/flycast-release.apk
|
||||
|
||||
- name: Build dump_syms
|
||||
working-directory: core/deps/breakpad
|
||||
@@ -69,6 +70,17 @@ jobs:
|
||||
mv libflycast.so.sym symbols/libflycast.so/$BUILD_ID
|
||||
done
|
||||
|
||||
- name: Setup Rclone
|
||||
uses: AnimMouse/setup-rclone@v1
|
||||
with:
|
||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||
|
||||
- name: Upload to S3
|
||||
run: |
|
||||
cp shell/android-studio/flycast/build/outputs/bundle/release/*.aab shell/android-studio/flycast/build/outputs/apk/release/
|
||||
rclone copy shell/android-studio/flycast/build/outputs/apk/release scaleway:flycast-builds/android/${GITHUB_REF#refs/}-$GITHUB_SHA --exclude '*.json'
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
|
||||
|
||||
- name: Setup Sentry CLI
|
||||
uses: mathieu-bour/setup-sentry-cli@v2
|
||||
env:
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
name: BSD CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
operating_system: [ freebsd, netbsd, openbsd ]
|
||||
architecture: [ arm64, x86-64 ]
|
||||
include:
|
||||
- operating_system: freebsd
|
||||
version: '14.3'
|
||||
pkginstall: sudo pkg update && sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2 libcdio
|
||||
- operating_system: netbsd
|
||||
version: '10.1'
|
||||
pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache clang cmake git libao libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2 libcdio
|
||||
- operating_system: openbsd
|
||||
version: '7.7'
|
||||
pkginstall: sudo pkg_add -u && sudo pkg_add ccache cmake git libao libzip miniupnpc ninja pkgconf pulseaudio sdl2 libcdio
|
||||
exclude:
|
||||
- architecture: arm64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-${{ github.sha }}
|
||||
restore-keys: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-
|
||||
|
||||
- uses: cross-platform-actions/[email protected]
|
||||
with:
|
||||
operating_system: ${{ matrix.operating_system }}
|
||||
architecture: ${{ matrix.architecture }}
|
||||
version: ${{ matrix.version }}
|
||||
environment_variables: CCACHE_DIR
|
||||
run: |
|
||||
${{ matrix.pkginstall }}
|
||||
cmake -B build -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
cmake --build build --config Release
|
||||
@@ -0,0 +1,175 @@
|
||||
name: C/C++ CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: ${{ matrix.config.name }}
|
||||
runs-on: ${{ matrix.config.os }}
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: ${{ matrix.config.shell }}
|
||||
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {name: i686-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x86, cmakeArgs: -G Ninja, buildType: Release}
|
||||
- {name: apple-darwin, os: macos-latest, shell: sh, cmakeArgs: -G Xcode -DUSE_DISCORD=ON, destDir: osx, buildType: RelWithDebInfo}
|
||||
- {name: x86_64-pc-linux-gnu, os: ubuntu-22.04, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: linux, buildType: RelWithDebInfo}
|
||||
- {name: x86_64-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x64, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, buildType: Release}
|
||||
- {name: x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: win, buildType: RelWithDebInfo}
|
||||
- {name: libretro-x86_64-pc-linux-gnu, os: ubuntu-22.04, shell: sh, cmakeArgs: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release}
|
||||
- {name: libretro-x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -G Ninja, buildType: Release}
|
||||
|
||||
steps:
|
||||
- name: Set up build environment (macOS)
|
||||
run: |
|
||||
# Unlink and re-link to prevent errors when github mac runner images install python outside of brew. See https://github.com/actions/setup-python/issues/577
|
||||
brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done
|
||||
brew update || :
|
||||
brew install libao ldid pulseaudio
|
||||
brew uninstall --ignore-dependencies zstd
|
||||
VULKAN_VER=1.3.290.0 && aria2c https://sdk.lunarg.com/sdk/download/$VULKAN_VER/mac/vulkansdk-macos-$VULKAN_VER.dmg
|
||||
hdiutil attach ./vulkansdk-macos-*.dmg -mountpoint /Volumes/VulkanSDK
|
||||
sudo /Volumes/VulkanSDK/InstallVulkan.app/Contents/MacOS/InstallVulkan --root $HOME/VulkanSDK --accept-licenses --default-answer --confirm-command install
|
||||
hdiutil detach /Volumes/VulkanSDK
|
||||
echo "VULKAN_SDK=$HOME/VulkanSDK/macOS" >> $GITHUB_ENV
|
||||
if: runner.os == 'macOS'
|
||||
|
||||
- name: Set up build environment (Linux)
|
||||
run: |
|
||||
sudo add-apt-repository ppa:christianrauch/libdecoration
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ccache libao-dev libasound2-dev libevdev-dev libgl1-mesa-dev liblua5.3-dev libminiupnpc-dev libpulse-dev libsdl2-dev libudev-dev libzip-dev ninja-build libcurl4-openssl-dev libcdio-dev libfuse2
|
||||
sudo apt-get -y install libwayland-dev libdecor-0-dev libaudio-dev libjack-dev libsndio-dev libsamplerate0-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxkbcommon-dev libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev
|
||||
if: runner.os == 'Linux'
|
||||
|
||||
- name: Set up build environment (Windows, MinGW)
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: MINGW64
|
||||
install: git make mingw-w64-x86_64-ccache mingw-w64-x86_64-cmake mingw-w64-x86_64-lua mingw-w64-x86_64-ninja mingw-w64-x86_64-SDL2 mingw-w64-x86_64-toolchain mingw-w64-x86_64-libcdio
|
||||
if: matrix.config.shell == 'msys2 {0}'
|
||||
|
||||
- name: Set up build environment (Windows, Visual Studio)
|
||||
run: |
|
||||
choco install ccache directx-sdk ninja
|
||||
echo DXSDK_DIR=C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)>> %GITHUB_ENV%
|
||||
if: matrix.config.shell == 'cmd'
|
||||
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
with:
|
||||
arch: ${{ matrix.config.arch }}
|
||||
if: matrix.config.shell == 'cmd'
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Compile a universal OpenMP (macOS)
|
||||
run: HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_DEVELOPER=1 brew reinstall --build-from-source --formula ./shell/apple/libomp.rb
|
||||
if: matrix.config.name == 'apple-darwin'
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: ccache-${{ matrix.config.name }}-${{ github.sha }}
|
||||
restore-keys: ccache-${{ matrix.config.name }}-
|
||||
if: runner.os != 'macOS'
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.buildType }} -DCMAKE_INSTALL_PREFIX=artifact ${{ matrix.config.cmakeArgs }} -DSENTRY_UPLOAD_URL=${{ env.SENTRY_UPLOAD_URL }}
|
||||
cmake --build build --config ${{ matrix.config.buildType }} --target install
|
||||
env:
|
||||
SENTRY_UPLOAD_URL: ${{ secrets.SENTRY_UPLOAD_URL }}
|
||||
|
||||
- name: Unit Tests
|
||||
run: |
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.buildType }} -DCMAKE_INSTALL_PREFIX=artifact -DENABLE_CTEST=ON ${{ matrix.config.cmakeArgs }}
|
||||
cmake --build build --config ${{ matrix.config.buildType }}
|
||||
./build/flycast
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
|
||||
- name: Dump symbols
|
||||
run: |
|
||||
mkdir -p symbols
|
||||
core/deps/breakpad/bin/dump_syms artifact/bin/flycast.exe > symbols/flycast.sym 2>/dev/null
|
||||
strip artifact/bin/flycast.exe
|
||||
if: matrix.config.name == 'x86_64-w64-mingw32'
|
||||
|
||||
- name: Build breakpad (linux)
|
||||
working-directory: core/deps/breakpad
|
||||
run: |
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
|
||||
- name: Dump symbols and make AppImage (linux)
|
||||
run: |
|
||||
mkdir -p symbols
|
||||
dump_syms artifact/bin/flycast > symbols/flycast.sym 2>/dev/null
|
||||
strip artifact/bin/flycast
|
||||
shell/linux/make-appimage.sh . artifact/bin appimage
|
||||
mv flycast-x86_64.AppImage artifact/bin
|
||||
rm artifact/bin/flycast
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: flycast-${{ matrix.config.name }}
|
||||
path: |
|
||||
artifact/bin
|
||||
artifact/lib/libretro
|
||||
|
||||
- name: Package app (macos)
|
||||
run: |
|
||||
cd artifact/bin
|
||||
zip -rm flycast.app.zip Flycast.app
|
||||
if: matrix.config.name == 'apple-darwin'
|
||||
|
||||
- name: Package app (windows)
|
||||
run: |
|
||||
powershell Compress-Archive artifact/bin/flycast.exe artifact/bin/flycast.zip
|
||||
rm artifact/bin/flycast.exe
|
||||
if: matrix.config.name == 'x86_64-w64-mingw32'
|
||||
|
||||
- name: Setup Rclone
|
||||
uses: AnimMouse/setup-rclone@v1
|
||||
with:
|
||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||
|
||||
- name: Upload to S3
|
||||
run: rclone copy artifact/bin scaleway:flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA
|
||||
shell: bash
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
|
||||
|
||||
- name: Setup Sentry CLI
|
||||
uses: mathieu-bour/setup-sentry-cli@v2
|
||||
env:
|
||||
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
|
||||
with:
|
||||
url: https://sentry.io
|
||||
token: ${{ env.SENTRY_TOKEN }}
|
||||
organization: flycast
|
||||
project: minidump
|
||||
version: 2.21.2
|
||||
if: ${{ env.SENTRY_TOKEN != '' }}
|
||||
|
||||
- name: Upload symbols to Sentry (Windows, macOS, Linux)
|
||||
run: |
|
||||
VERSION=$(git describe --tags --always)
|
||||
sentry-cli releases new "$VERSION"
|
||||
sentry-cli releases set-commits "$VERSION" --auto
|
||||
sentry-cli debug-files upload symbols
|
||||
shell: bash
|
||||
env:
|
||||
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
|
||||
if: ${{ env.SENTRY_TOKEN != '' && (matrix.config.name == 'x86_64-w64-mingw32' || matrix.config.name == 'apple-darwin' || matrix.config.name == 'x86_64-pc-linux-gnu') }}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Prepare source texts & upload them to Crowdin
|
||||
|
||||
name: Crowdin Source Texts Upload
|
||||
|
||||
# on change to the English texts
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'shell/libretro/libretro_core_options.h'
|
||||
|
||||
jobs:
|
||||
upload_source_file:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Java JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 18
|
||||
distribution: zulu
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Upload Source
|
||||
shell: bash
|
||||
env:
|
||||
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
|
||||
run: |
|
||||
python3 intl/upload_workflow.py $CROWDIN_API_KEY "flyinghead-flycast" "shell/libretro/libretro_core_options.h"
|
||||
@@ -0,0 +1,49 @@
|
||||
# Download translations form Crowdin & Recreate libretro_core_options_intl.h
|
||||
|
||||
name: Crowdin Translation Integration
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# please choose a random time & weekday to avoid all repos synching at the same time
|
||||
- cron: '15 22 * * 5' # Fridays at 10:15 PM, UTC
|
||||
|
||||
jobs:
|
||||
create_intl_file:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Setup Java JDK
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: 18
|
||||
distribution: zulu
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false # otherwise, the token used is the GITHUB_TOKEN, instead of your personal access token.
|
||||
fetch-depth: 0 # otherwise, there would be errors pushing refs to the destination repository.
|
||||
|
||||
- name: Create intl file
|
||||
shell: bash
|
||||
env:
|
||||
CROWDIN_API_KEY: ${{ secrets.CROWDIN_API_KEY }}
|
||||
run: |
|
||||
python3 intl/download_workflow.py $CROWDIN_API_KEY "flyinghead-flycast" "shell/libretro/libretro_core_options_intl.h"
|
||||
|
||||
- name: Commit files
|
||||
run: |
|
||||
git config --local user.email "[email protected]"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add intl/*_workflow.py "shell/libretro/libretro_core_options_intl.h"
|
||||
git commit -m "Fetch translations & Recreate libretro_core_options_intl.h"
|
||||
|
||||
- name: GitHub Push
|
||||
uses: ad-m/[email protected]
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: ${{ github.ref }}
|
||||
@@ -0,0 +1,61 @@
|
||||
name: Nintendo Switch CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Switch ${{ matrix.config.name }}
|
||||
runs-on: ubuntu-latest
|
||||
container: devkitpro/devkita64:latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
config:
|
||||
- {name: standalone, destDir: switch}
|
||||
- {name: libretro, cmakeArgs: -DLIBRETRO=ON}
|
||||
|
||||
steps:
|
||||
- name: Set up build environment
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install ccache cmake
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- name: Mark git repository as safe
|
||||
run: git config --global --add safe.directory $PWD
|
||||
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.ccache
|
||||
key: ccache-switch-${{ matrix.config.name }}-${{ github.sha }}
|
||||
restore-keys: ccache-switch-${{ matrix.config.name }}-
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
$DEVKITPRO/portlibs/switch/bin/aarch64-none-elf-cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=artifact -G Ninja ${{ matrix.config.cmakeArgs }}
|
||||
cmake --build build --config Release --target install
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: flycast-switch-${{ matrix.config.name }}
|
||||
path: |
|
||||
artifact/bin
|
||||
artifact/lib/libretro
|
||||
|
||||
- name: Package app
|
||||
run: zip -m artifact/bin/flycast.nro.zip artifact/bin/flycast.nro
|
||||
if: hashFiles('artifact/bin/flycast.nro') != ''
|
||||
|
||||
- name: Setup Rclone
|
||||
uses: AnimMouse/setup-rclone@v1
|
||||
with:
|
||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||
version: v1.69.1
|
||||
|
||||
- name: Upload to S3
|
||||
run: rclone copy artifact/bin scaleway:flycast-builds/${{ matrix.config.destDir }}/${GITHUB_REF#refs/}-$GITHUB_SHA
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.config.destDir != ''
|
||||
@@ -0,0 +1,55 @@
|
||||
name: Windows UWP CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: UWP
|
||||
runs-on: windows-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
arch: [ Win32, x64, ARM64 ]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
submodules: true
|
||||
|
||||
- name: CMake
|
||||
run: |
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -G "Visual Studio 17 2022" -A ${{ matrix.arch }} -DCMAKE_SYSTEM_NAME=WindowsStore -DCMAKE_SYSTEM_VERSION=10.0.22000.0
|
||||
cmake --build build --config Release -- /m
|
||||
shell: cmd
|
||||
|
||||
- uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Package app
|
||||
run: |
|
||||
mkdir build\artifact
|
||||
cd build\AppPackages\flycast\flycast_*_Test
|
||||
mkdir tmp
|
||||
ren *.msix flycast.msix
|
||||
makeappx.exe unpack /p .\flycast.msix /d tmp
|
||||
copy ..\..\..\Release\*.dll tmp
|
||||
makeappx pack /d tmp /p ..\..\..\artifact\flycast.appx
|
||||
|
||||
- name: Sign app
|
||||
run: signtool sign /f shell\uwp\sign_cert.pfx /p '${{ secrets.SIGN_CERT_PWD }}' /v /fd SHA256 build\artifact\flycast.appx
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push'
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: flycast-uwp-${{ matrix.arch }}
|
||||
path: build/artifact
|
||||
|
||||
- name: Setup Rclone
|
||||
uses: AnimMouse/setup-rclone@v1
|
||||
with:
|
||||
rclone_config: ${{ secrets.RCLONE_CONFIG }}
|
||||
|
||||
- name: Upload to S3
|
||||
run: rclone copy build/artifact scaleway:flycast-builds/xbox/${GITHUB_REF#refs/}-$GITHUB_SHA
|
||||
shell: bash
|
||||
if: github.repository == 'flyinghead/flycast' && github.event_name == 'push' && matrix.arch == 'x64'
|
||||
@@ -17,3 +17,6 @@ cmake-build-*/
|
||||
# Dependencies from .gitmodules
|
||||
core/deps/breakpad/
|
||||
core/deps/glslang/
|
||||
.cache/
|
||||
compile_commands.json
|
||||
CMakeUserPresets.json
|
||||
+15
-6
@@ -9,34 +9,38 @@
|
||||
variables:
|
||||
GIT_SUBMODULE_STRATEGY: recursive
|
||||
CORENAME: flycast
|
||||
CORE_ARGS: -DLIBRETRO=ON -DCMAKE_BUILD_TYPE=Release
|
||||
CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON
|
||||
|
||||
.core-defs-linux:
|
||||
extends: .core-defs
|
||||
variables:
|
||||
CORE_ARGS: -DLIBRETRO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -DCMAKE_BUILD_TYPE=Release
|
||||
CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE
|
||||
CC: /usr/bin/gcc-12
|
||||
CXX: /usr/bin/g++-12
|
||||
|
||||
.core-defs-osx-x64:
|
||||
extends: .core-defs
|
||||
variables:
|
||||
CORE_ARGS: -DLIBRETRO=ON -G Xcode -DCMAKE_BUILD_TYPE=Release -DCMAKE_OSX_ARCHITECTURES=x86_64
|
||||
CORE_ARGS: -DLIBRETRO=ON -G Xcode -DCMAKE_OSX_ARCHITECTURES=x86_64
|
||||
EXTRA_PATH: Release
|
||||
|
||||
.core-defs-osx-arm64:
|
||||
extends: .core-defs
|
||||
variables:
|
||||
CORE_ARGS: -DLIBRETRO=ON -G Xcode -DCMAKE_BUILD_TYPE=Release
|
||||
CORE_ARGS: -DLIBRETRO=ON -G Xcode
|
||||
EXTRA_PATH: Release
|
||||
|
||||
.core-defs-ios-arm64:
|
||||
extends: .core-defs
|
||||
variables:
|
||||
CORE_ARGS: -DLIBRETRO=ON -DCMAKE_BUILD_TYPE=Release
|
||||
CORE_ARGS: -DLIBRETRO=ON -DUSE_OPENMP=OFF -DCMAKE_OSX_DEPLOYMENT_TARGET=13.0
|
||||
IOS_MINVER: 13.0
|
||||
MINVER: 13.0
|
||||
|
||||
.core-defs-android:
|
||||
extends: .core-defs
|
||||
script:
|
||||
- cmake $CORE_ARGS -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI -DANDROID_ARM_MODE=arm "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR
|
||||
- cmake -DLIBRETRO=ON -DUSE_OPENMP=OFF -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI -DANDROID_ARM_MODE=arm "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR
|
||||
- cmake --build $BUILD_DIR --target ${CORENAME}_libretro --config Release -- -j $NUMPROC
|
||||
- mv $BUILD_DIR/${CORENAME}_libretro.so $LIBNAME
|
||||
- if [ $STRIP_CORE_LIB -eq 1 ]; then $NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip $LIBNAME; fi
|
||||
@@ -108,18 +112,22 @@ libretro-build-linux-x64:
|
||||
extends:
|
||||
- .libretro-linux-cmake-x86_64
|
||||
- .core-defs-linux
|
||||
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-amd64-ubuntu:backports
|
||||
|
||||
# Linux 32-bit
|
||||
libretro-build-linux-i686:
|
||||
extends:
|
||||
- .libretro-linux-cmake-x86
|
||||
- .core-defs-linux
|
||||
image: $CI_SERVER_HOST:5050/libretro-infrastructure/libretro-build-i386-ubuntu:backports
|
||||
|
||||
# MacOS 64-bit
|
||||
libretro-build-osx-x64:
|
||||
extends:
|
||||
- .libretro-osx-cmake-x86
|
||||
- .core-defs-osx-x64
|
||||
tags:
|
||||
- mac-apple-silicon
|
||||
|
||||
# MacOS arm 64-bit
|
||||
libretro-build-osx-arm64:
|
||||
@@ -169,3 +177,4 @@ libretro-build-tvos-arm64:
|
||||
extends:
|
||||
- .libretro-tvos-cmake-arm64
|
||||
- .core-defs-ios-arm64
|
||||
|
||||
+20
-2
@@ -3,7 +3,7 @@
|
||||
url = https://github.com/libsdl-org/SDL.git
|
||||
[submodule "core/deps/libchdr"]
|
||||
path = core/deps/libchdr
|
||||
url = https://github.com/rtissera/libchdr.git
|
||||
url = https://github.com/flyinghead/libchdr.git
|
||||
ignore = dirty
|
||||
[submodule "core/deps/luabridge"]
|
||||
path = core/deps/luabridge
|
||||
@@ -31,7 +31,7 @@
|
||||
url = https://github.com/vkedwardli/Spout2.git
|
||||
[submodule "core/deps/discord-rpc"]
|
||||
path = core/deps/discord-rpc
|
||||
url = https://github.com/flyinghead/discord-rpc
|
||||
url = https://github.com/harmonytf/discord-rpc
|
||||
[submodule "core/deps/libadrenotools"]
|
||||
path = core/deps/libadrenotools
|
||||
url = https://github.com/bylaws/libadrenotools
|
||||
@@ -41,3 +41,21 @@
|
||||
[submodule "core/deps/googletest"]
|
||||
path = core/deps/googletest
|
||||
url = https://github.com/google/googletest.git
|
||||
[submodule "core/deps/asio"]
|
||||
path = core/deps/asio
|
||||
url = https://github.com/flyinghead/asio.git
|
||||
[submodule "core/deps/xbyak"]
|
||||
path = core/deps/xbyak
|
||||
url = https://github.com/herumi/xbyak.git
|
||||
[submodule "core/deps/libjuice"]
|
||||
path = core/deps/libjuice
|
||||
url = https://github.com/paullouisageneau/libjuice.git
|
||||
[submodule "core/deps/websocketpp"]
|
||||
path = core/deps/websocketpp
|
||||
url = https://github.com/zaphoyd/websocketpp.git
|
||||
[submodule "core/deps/DreamPicoPort-API"]
|
||||
path = core/deps/DreamPicoPort-API
|
||||
url = https://github.com/OrangeFox86/DreamPicoPort-API.git
|
||||
[submodule "core/deps/libusb-cmake"]
|
||||
path = core/deps/libusb-cmake
|
||||
url = https://github.com/libusb/libusb-cmake.git
|
||||
+247
-526
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"version": 2,
|
||||
"configurePresets": [
|
||||
{
|
||||
"name": "clang",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "build-clang",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "gcc",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "build-gcc",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
[](https://github.com/flyinghead/flycast/actions/workflows/uwp.yml)
|
||||
[](https://github.com/flyinghead/flycast/actions/workflows/bsd.yml)
|
||||
|
||||

|
||||
<img src="shell/linux/flycast.png" alt="flycast logo" width="150"/>
|
||||
|
||||
**Flycast** is a multi-platform Sega Dreamcast, Naomi, Naomi 2, and Atomiswave emulator derived from [**reicast**](https://github.com/skmp/reicast-emulator).
|
||||
|
||||
@@ -16,6 +16,8 @@ Join us on our [**Discord server**](https://discord.gg/X8YWP8w) for a chat.
|
||||
|
||||
## Install
|
||||
|
||||
### Android 
|
||||
Install Flycast from [**Google Play**](https://play.google.com/store/apps/details?id=com.flycast.emulator).
|
||||
### Flatpak (Linux )
|
||||
|
||||
1. [Set up Flatpak](https://www.flatpak.org/setup/).
|
||||
@@ -36,6 +38,10 @@ Join us on our [**Discord server**](https://discord.gg/X8YWP8w) for a chat.
|
||||
|
||||
`brew install --cask flycast`
|
||||
|
||||
### iOS
|
||||
|
||||
Due to persistent harassment from an iOS user, support for this platform has been dropped.
|
||||
|
||||
### Xbox One/Series 
|
||||
|
||||
Grab the latest build from [**the builds page**](https://flyinghead.github.io/flycast-builds/), or the [**GitHub Actions**](https://github.com/flyinghead/flycast/actions/workflows/uwp.yml). Then install it using the **Xbox Device Portal**.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
cmrc_add_resource_library(flycast-resources ALIAS flycast::res NAMESPACE flycast)
|
||||
|
||||
cmrc_add_resources(flycast-resources
|
||||
WHENCE resources
|
||||
resources/flash/alienfnt.nvmem.zip # network settings
|
||||
resources/flash/gunsur2.nvmem.zip
|
||||
resources/flash/mazan.nvmem.zip # free play, calibrated
|
||||
resources/flash/otrigger.nvmem.zip
|
||||
resources/flash/wldkicks.nvmem.zip # free play
|
||||
resources/flash/wldkicksj.nvmem.zip # free play
|
||||
resources/flash/wldkicksu.nvmem.zip # free play
|
||||
resources/flash/f355.nvmem.zip # printer on
|
||||
resources/flash/f355twin.nvmem.zip # printer on
|
||||
resources/flash/f355twn2.nvmem.zip # printer on
|
||||
resources/flash/dirtypig.nvmem.zip # 4 players
|
||||
resources/flash/dirtypig.nvmem2.zip
|
||||
resources/flash/vf4.nvmem.zip # card all day, stage select
|
||||
resources/flash/vf4evob.nvmem.zip # card all day, stage select
|
||||
resources/flash/vf4tuned.nvmem.zip # card all day, stage select, 45 sec time limit, 3 match
|
||||
resources/flash/magicpop.nvmem.zip # debug: comm and all errors disabled
|
||||
resources/flash/ochaken.nvmem.zip # debug: comm and all errors disabled
|
||||
resources/flash/puyomedal.nvmem.zip # debug: comm and all errors disabled
|
||||
resources/flash/unomedal.nvmem.zip # debug: comm and all errors disabled
|
||||
resources/flash/westdrmg.nvmem.zip # debug: comm and all errors disabled
|
||||
resources/flash/smarinef.nvmem.zip # standard cabinet
|
||||
resources/picture/f355_print_template.png)
|
||||
|
||||
cmrc_add_resources(flycast-resources
|
||||
fonts/printer_ascii8x16.bin.zip
|
||||
fonts/printer_ascii12x24.bin.zip
|
||||
fonts/printer_kanji16x16.bin.zip
|
||||
fonts/printer_kanji24x24.bin.zip)
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
cmrc_add_resources(flycast-resources
|
||||
fonts/Roboto-Medium.ttf.zip
|
||||
fonts/Roboto-Regular.ttf.zip
|
||||
fonts/fa-solid-900.ttf.zip)
|
||||
if(ANDROID OR IOS)
|
||||
cmrc_add_resources(flycast-resources
|
||||
WHENCE resources
|
||||
resources/picture/buttons.png
|
||||
resources/picture/buttons-arcade.png)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
cmrc_add_resources(flycast-resources fonts/biosfont.bin.zip)
|
||||
@@ -0,0 +1,3 @@
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
achievements.cpp
|
||||
achievements.h)
|
||||
+103
-120
@@ -37,8 +37,9 @@
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
#include <xxhash.h>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
#include "util/worker_thread.h"
|
||||
#include "util/periodic_thread.h"
|
||||
|
||||
namespace achievements
|
||||
{
|
||||
@@ -74,10 +75,8 @@ private:
|
||||
std::string getOrDownloadImage(const char *url);
|
||||
std::pair<std::string, bool> getCachedImage(const char *url);
|
||||
void diskChange();
|
||||
void asyncTask(std::function<void()> f);
|
||||
void startThread();
|
||||
void stopThread();
|
||||
void backgroundThread();
|
||||
void asyncTask(std::function<void()>&& f);
|
||||
void stopThreads();
|
||||
|
||||
static void clientLoginWithTokenCallback(int result, const char *error_message, rc_client_t *client, void *userdata);
|
||||
static void clientLoginWithPasswordCallback(int result, const char *error_message, rc_client_t *client, void *userdata);
|
||||
@@ -112,11 +111,12 @@ private:
|
||||
std::string cachePath;
|
||||
std::unordered_map<u64, std::string> cacheMap;
|
||||
std::mutex cacheMutex;
|
||||
std::vector<std::function<void()>> tasks;
|
||||
std::mutex taskMutex;
|
||||
std::thread taskThread;
|
||||
cResetEvent resetEvent;
|
||||
bool threadRunning = false;
|
||||
WorkerThread taskThread {"RA-background"};
|
||||
|
||||
PeriodicThread idleThread { "RA-idle", [this]() {
|
||||
if (active)
|
||||
rc_client_idle(rc_client);
|
||||
}};
|
||||
};
|
||||
|
||||
bool init() {
|
||||
@@ -176,6 +176,7 @@ Achievements::Achievements()
|
||||
EventManager::listen(Event::Pause, emuEventCallback, this);
|
||||
EventManager::listen(Event::Resume, emuEventCallback, this);
|
||||
EventManager::listen(Event::DiskChange, emuEventCallback, this);
|
||||
idleThread.setPeriod(1000);
|
||||
}
|
||||
|
||||
Achievements::~Achievements()
|
||||
@@ -188,44 +189,13 @@ Achievements::~Achievements()
|
||||
term();
|
||||
}
|
||||
|
||||
void Achievements::asyncTask(std::function<void()> f)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> _(taskMutex);
|
||||
tasks.emplace_back(f);
|
||||
}
|
||||
resetEvent.Set();
|
||||
void Achievements::asyncTask(std::function<void()>&& f) {
|
||||
taskThread.run(std::move(f));
|
||||
}
|
||||
|
||||
void Achievements::startThread()
|
||||
{
|
||||
threadRunning = true;
|
||||
taskThread = std::thread(&Achievements::backgroundThread, this);
|
||||
}
|
||||
|
||||
void Achievements::stopThread()
|
||||
{
|
||||
threadRunning = false;
|
||||
resetEvent.Set();
|
||||
if (taskThread.joinable())
|
||||
taskThread.join();
|
||||
}
|
||||
|
||||
void Achievements::backgroundThread()
|
||||
{
|
||||
ThreadName _("RA-background");
|
||||
while (threadRunning)
|
||||
{
|
||||
if (!resetEvent.Wait(1000) && active && paused)
|
||||
rc_client_idle(rc_client);
|
||||
std::vector<std::function<void()>> localTasks;
|
||||
{
|
||||
std::lock_guard<std::mutex> _(taskMutex);
|
||||
std::swap(tasks, localTasks);
|
||||
}
|
||||
for (auto& f : localTasks)
|
||||
f();
|
||||
}
|
||||
void Achievements::stopThreads() {
|
||||
taskThread.stop();
|
||||
idleThread.stop();
|
||||
}
|
||||
|
||||
bool Achievements::init()
|
||||
@@ -243,7 +213,6 @@ bool Achievements::init()
|
||||
//rc_client_set_unofficial_enabled(rc_client, 0);
|
||||
//rc_client_set_spectator_mode_enabled(rc_client, 0);
|
||||
loadCache();
|
||||
startThread();
|
||||
|
||||
if (!config::AchievementsUserName.get().empty() && !config::AchievementsToken.get().empty())
|
||||
{
|
||||
@@ -301,7 +270,7 @@ void Achievements::loadCache()
|
||||
}
|
||||
|
||||
static u64 hashUrl(const char *url) {
|
||||
return XXH64(url, strlen(url), 13);
|
||||
return XXH3_64bits(url, strlen(url));
|
||||
}
|
||||
|
||||
std::pair<std::string, bool> Achievements::getCachedImage(const char *url)
|
||||
@@ -357,7 +326,7 @@ void Achievements::term()
|
||||
if (rc_client == nullptr)
|
||||
return;
|
||||
unloadGame();
|
||||
stopThread();
|
||||
stopThreads();
|
||||
rc_client_destroy(rc_client);
|
||||
rc_client = nullptr;
|
||||
}
|
||||
@@ -365,16 +334,16 @@ void Achievements::term()
|
||||
void Achievements::authenticationSuccess(const rc_client_user_t *user)
|
||||
{
|
||||
NOTICE_LOG(COMMON, "RA Login successful");
|
||||
asyncTask([this, user]() {
|
||||
char url[512];
|
||||
int rc = rc_client_user_get_image_url(user, url, sizeof(url));
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
std::string image = getOrDownloadImage(url);
|
||||
std::string url(512, '\0');
|
||||
int rc = rc_client_user_get_image_url(user, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url]() {
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
std::string text = "User " + config::AchievementsUserName.get() + " authenticated";
|
||||
notifier.notify(Notification::Login, image, text);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
loggedOn = true;
|
||||
if (!settings.content.fileName.empty()) // TODO better test?
|
||||
loadGame();
|
||||
@@ -600,46 +569,48 @@ void Achievements::handleUnlockEvent(const rc_client_event_t *event)
|
||||
assert(cheevo != nullptr);
|
||||
|
||||
INFO_LOG(COMMON, "RA: Achievement %s (%u) for game %s unlocked", cheevo->title, cheevo->id, settings.content.title.c_str());
|
||||
asyncTask([this, cheevo]() {
|
||||
char url[512];
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, cheevo->state, url, sizeof(url));
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
std::string image = getOrDownloadImage(url);
|
||||
std::string text = "Achievement " + std::string(cheevo->title) + " unlocked!";
|
||||
notifier.notify(Notification::Login, image, text, cheevo->description);
|
||||
}
|
||||
});
|
||||
std::string title(cheevo->title);
|
||||
std::string description(cheevo->description);
|
||||
std::string url(512, '\0');
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, cheevo->state, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url, title, description]() {
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
std::string text = "Achievement " + title + " unlocked!";
|
||||
notifier.notify(Notification::Login, image, text, description);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::handleAchievementChallengeIndicatorShowEvent(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_achievement_t* cheevo = event->achievement;
|
||||
INFO_LOG(COMMON, "RA: Challenge: %s", cheevo->title);
|
||||
asyncTask([this, cheevo]() {
|
||||
char url[128];
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url, sizeof(url));
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
std::string image = getOrDownloadImage(url);
|
||||
std::string url(512, '\0');
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url]() {
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
notifier.showChallenge(image);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::handleAchievementChallengeIndicatorHideEvent(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_achievement_t* cheevo = event->achievement;
|
||||
INFO_LOG(COMMON, "RA: Challenge hidden: %s", cheevo->title);
|
||||
asyncTask([this, cheevo]() {
|
||||
char url[128];
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url, sizeof(url));
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
std::string image = getOrDownloadImage(url);
|
||||
std::string url(512, '\0');
|
||||
int rc = rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_UNLOCKED, url.data(), url.size());
|
||||
if (rc == RC_OK)
|
||||
{
|
||||
asyncTask([this, url]() {
|
||||
std::string image = getOrDownloadImage(url.c_str());
|
||||
notifier.hideChallenge(image);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Achievements::handleLeaderboardStarted(const rc_client_event_t *event)
|
||||
@@ -685,18 +656,21 @@ void Achievements::handleUpdateLeaderboardTracker(const rc_client_event_t *event
|
||||
void Achievements::handleGameCompleted(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_game_t* game = rc_client_get_game_info(rc_client);
|
||||
asyncTask([this, game]() {
|
||||
std::string text1 = (rc_client_get_hardcore_enabled(rc_client) ? "Mastered " : "Completed ") + std::string(game->title);
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(rc_client, &summary);
|
||||
std::stringstream ss;
|
||||
ss << summary.num_unlocked_achievements << " achievements, " << summary.points_unlocked << " points";
|
||||
std::string text2(ss.str());
|
||||
std::string text3 = rc_client_get_user_info(rc_client)->display_name;
|
||||
std::string url(512, '\0');
|
||||
if (rc_client_game_get_image_url(game, url.data(), url.size()) != RC_OK)
|
||||
url.clear();
|
||||
asyncTask([this, url, text1, text2, text3]() {
|
||||
std::string image;
|
||||
char url[128];
|
||||
if (rc_client_game_get_image_url(game, url, sizeof(url)) == RC_OK)
|
||||
image = getOrDownloadImage(url);
|
||||
std::string text1 = (rc_client_get_hardcore_enabled(rc_client) ? "Mastered " : "Completed ") + std::string(game->title);
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(rc_client, &summary);
|
||||
std::stringstream ss;
|
||||
ss << summary.num_unlocked_achievements << " achievements, " << summary.points_unlocked << " points";
|
||||
std::string text3 = rc_client_get_user_info(rc_client)->display_name;
|
||||
notifier.notify(Notification::Mastery, image, text1, ss.str(), text3);
|
||||
if (!url.empty())
|
||||
image = getOrDownloadImage(url.c_str());
|
||||
notifier.notify(Notification::Mastery, image, text1, text2, text3);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -711,12 +685,15 @@ void Achievements::handleHideAchievementProgress(const rc_client_event_t *event)
|
||||
void Achievements::handleUpdateAchievementProgress(const rc_client_event_t *event)
|
||||
{
|
||||
const rc_client_achievement_t* cheevo = event->achievement;
|
||||
asyncTask([this, cheevo]() {
|
||||
char url[128];
|
||||
std::string url(512, '\0');
|
||||
if (rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE, url.data(), url.size()) != RC_OK)
|
||||
url.clear();
|
||||
std::string progress(cheevo->measured_progress);
|
||||
asyncTask([this, url, progress]() {
|
||||
std::string image;
|
||||
if (rc_client_achievement_get_image_url(cheevo, RC_CLIENT_ACHIEVEMENT_STATE_ACTIVE, url, sizeof(url)) == RC_OK)
|
||||
image = getOrDownloadImage(url);
|
||||
notifier.notify(Notification::Progress, image, cheevo->measured_progress);
|
||||
if (!url.empty())
|
||||
image = getOrDownloadImage(url.c_str());
|
||||
notifier.notify(Notification::Progress, image, progress);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -771,8 +748,7 @@ std::string Achievements::getGameHash()
|
||||
{
|
||||
if (settings.platform.isConsole())
|
||||
{
|
||||
const u32 diskType = libGDR_GetDiscType();
|
||||
if (diskType == NoDisk || diskType == Open)
|
||||
if (!gdr::isLoaded())
|
||||
return {};
|
||||
// Reopen the disk locally to avoid threading issues (CHD)
|
||||
try {
|
||||
@@ -806,15 +782,19 @@ std::string Achievements::getGameHash()
|
||||
return hash;
|
||||
}
|
||||
|
||||
void Achievements::pauseGame() {
|
||||
void Achievements::pauseGame()
|
||||
{
|
||||
paused = true;
|
||||
if (active)
|
||||
idleThread.start();
|
||||
}
|
||||
|
||||
void Achievements::resumeGame()
|
||||
{
|
||||
paused = false;
|
||||
if (config::EnableAchievements)
|
||||
if (config::EnableAchievements && !settings.naomi.slave)
|
||||
{
|
||||
idleThread.stop();
|
||||
loadGame();
|
||||
if (settings.raHardcoreMode && !config::AchievementsHardcoreMode)
|
||||
{
|
||||
@@ -919,21 +899,24 @@ void Achievements::gameLoaded(int result, const char *errorMessage)
|
||||
settings.raHardcoreMode = false;
|
||||
else
|
||||
settings.raHardcoreMode = (bool)rc_client_get_hardcore_enabled(rc_client);
|
||||
asyncTask([this, info]() {
|
||||
std::string url(512, '\0');
|
||||
if (rc_client_game_get_image_url(info, url.data(), url.size()) != RC_OK)
|
||||
url.clear();
|
||||
std::string text1(info->title);
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(rc_client, &summary);
|
||||
std::string text2;
|
||||
if (summary.num_core_achievements > 0)
|
||||
text2 = "You have " + std::to_string(summary.num_unlocked_achievements)
|
||||
+ " of " + std::to_string(summary.num_core_achievements) + " achievements unlocked.";
|
||||
else
|
||||
text2 = "This game has no achievements.";
|
||||
asyncTask([this, url, text1, text2]() {
|
||||
std::string image;
|
||||
char url[512];
|
||||
if (rc_client_game_get_image_url(info, url, sizeof(url)) == RC_OK)
|
||||
image = getOrDownloadImage(url);
|
||||
rc_client_user_game_summary_t summary;
|
||||
rc_client_get_user_game_summary(rc_client, &summary);
|
||||
std::string text;
|
||||
if (summary.num_core_achievements > 0)
|
||||
text = "You have " + std::to_string(summary.num_unlocked_achievements)
|
||||
+ " of " + std::to_string(summary.num_core_achievements) + " achievements unlocked.";
|
||||
else
|
||||
text = "This game has no achievements.";
|
||||
std::string text2 = settings.raHardcoreMode ? "Hardcore Mode" : "";
|
||||
notifier.notify(Notification::Login, image, info->title, text, text2);
|
||||
if (!url.empty())
|
||||
image = getOrDownloadImage(url.c_str());
|
||||
std::string text3 = settings.raHardcoreMode ? "Hardcore Mode" : "";
|
||||
notifier.notify(Notification::Login, image, text1, text2, text3);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -945,15 +928,15 @@ void Achievements::unloadGame()
|
||||
paused = false;
|
||||
EventManager::unlisten(Event::VBlank, emuEventCallback, this);
|
||||
// wait for all async tasks before unloading the game
|
||||
stopThread();
|
||||
startThread();
|
||||
stopThreads();
|
||||
rc_client_unload_game(rc_client);
|
||||
settings.raHardcoreMode = false;
|
||||
}
|
||||
|
||||
void Achievements::diskChange()
|
||||
{
|
||||
if (!active)
|
||||
if (!active || settings.content.path.empty())
|
||||
// Don't unload the game when the lid is open while swapping disks
|
||||
return;
|
||||
std::string hash = getGameHash();
|
||||
if (hash == "") {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
7zArchive.cpp
|
||||
7zArchive.h
|
||||
archive.cpp
|
||||
archive.h
|
||||
rzip.cpp
|
||||
rzip.h
|
||||
ZipArchive.cpp
|
||||
ZipArchive.h)
|
||||
@@ -0,0 +1,17 @@
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
audiostream.h)
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
audiobackend_alsa.cpp
|
||||
audiobackend_coreaudio.cpp
|
||||
audiobackend_directsound.cpp
|
||||
audiobackend_libao.cpp
|
||||
audiobackend_null.cpp
|
||||
audiobackend_oboe.cpp
|
||||
audiobackend_omx.cpp
|
||||
audiobackend_oss.cpp
|
||||
audiobackend_pulseaudio.cpp
|
||||
audiobackend_sdl2.cpp
|
||||
audiostream.cpp)
|
||||
endif()
|
||||
@@ -19,17 +19,20 @@ public:
|
||||
|
||||
u32 push(const void* frame, u32 samples, bool wait) override
|
||||
{
|
||||
if (wait)
|
||||
if (wait && last_time.time_since_epoch() != the_clock::duration::zero())
|
||||
{
|
||||
if (last_time.time_since_epoch() != the_clock::duration::zero())
|
||||
{
|
||||
auto fduration = std::chrono::nanoseconds(1000000000L * samples / 44100);
|
||||
auto duration = fduration - (the_clock::now() - last_time);
|
||||
auto fduration = std::chrono::nanoseconds(1'000'000'000LL * samples / 44100);
|
||||
auto duration = fduration - (the_clock::now() - last_time);
|
||||
if (duration > std::chrono::nanoseconds::zero())
|
||||
std::this_thread::sleep_for(duration);
|
||||
last_time += fduration;
|
||||
}
|
||||
else
|
||||
if (duration < -std::chrono::milliseconds(67))
|
||||
// if ~4 frames ahead, reset time (fast forward detection)
|
||||
last_time = the_clock::now();
|
||||
else
|
||||
last_time += fduration;
|
||||
}
|
||||
else {
|
||||
last_time = the_clock::now();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -10,29 +10,87 @@ class OSSAudioBackend : public AudioBackend
|
||||
int audioFD = -1;
|
||||
int recordFD = -1;
|
||||
|
||||
static int openDevice(int flags)
|
||||
{
|
||||
const char* path = "/dev/dsp";
|
||||
int fd = open(path, flags);
|
||||
|
||||
if (fd < 0)
|
||||
ERROR_LOG(AUDIO, "OSS: open(%s) failed: %s", path, strerror(errno));
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static bool setRate(int fd, int rate)
|
||||
{
|
||||
int tmp = rate;
|
||||
|
||||
if (ioctl(fd, SNDCTL_DSP_SPEED, &tmp) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: ioctl(SNDCTL_DSP_SPEED) failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tmp != rate)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: sample rate unsupported: %d => %d", rate, tmp);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setChannels(int fd, int channels)
|
||||
{
|
||||
int tmp = channels;
|
||||
|
||||
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &tmp) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: ioctl(SNDCTL_DSP_CHANNELS) failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tmp != channels)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: channels unsupported: %d => %d", channels, tmp);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool setFormat(int fd, int format)
|
||||
{
|
||||
int tmp = format;
|
||||
|
||||
if (ioctl(fd, SNDCTL_DSP_SETFMT, &tmp) < 0)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: ioctl(SNDCTL_DSP_SETFMT) failed: %s", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tmp != format)
|
||||
{
|
||||
ERROR_LOG(AUDIO, "OSS: sample format unsupported: %#.8x => %#.8x", format, tmp);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
OSSAudioBackend()
|
||||
: AudioBackend("oss", "Open Sound System") {}
|
||||
|
||||
bool init() override
|
||||
{
|
||||
audioFD = open("/dev/dsp", O_WRONLY);
|
||||
if (audioFD < 0)
|
||||
audioFD = openDevice(O_WRONLY);
|
||||
|
||||
if (audioFD < 0 || !setRate(audioFD, 44100) || !setChannels(audioFD, 2) || !setFormat(audioFD, AFMT_S16_LE))
|
||||
{
|
||||
WARN_LOG(AUDIO, "Couldn't open /dev/dsp.");
|
||||
term();
|
||||
return false;
|
||||
}
|
||||
INFO_LOG(AUDIO, "sound enabled, dsp opened for write");
|
||||
int tmp=44100;
|
||||
int err_ret;
|
||||
err_ret=ioctl(audioFD,SNDCTL_DSP_SPEED,&tmp);
|
||||
INFO_LOG(AUDIO, "set Frequency to %i, return %i (rate=%i)", 44100, err_ret, tmp);
|
||||
int channels=2;
|
||||
err_ret=ioctl(audioFD, SNDCTL_DSP_CHANNELS, &channels);
|
||||
INFO_LOG(AUDIO, "set dsp to stereo (%i => %i)", channels, err_ret);
|
||||
int format=AFMT_S16_LE;
|
||||
err_ret=ioctl(audioFD, SNDCTL_DSP_SETFMT, &format);
|
||||
INFO_LOG(AUDIO, "set dsp to %s audio (%i/%i => %i)", "16bits signed", AFMT_S16_LE, format, err_ret);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -54,34 +112,11 @@ public:
|
||||
|
||||
bool initRecord(u32 sampling_freq) override
|
||||
{
|
||||
recordFD = open("/dev/dsp", O_RDONLY);
|
||||
if (recordFD < 0)
|
||||
recordFD = openDevice(O_RDONLY);
|
||||
|
||||
if (recordFD < 0 || !setRate(recordFD, sampling_freq) || !setChannels(recordFD, 1) || !setFormat(recordFD, AFMT_S16_NE /* Native 16 bits */ ))
|
||||
{
|
||||
INFO_LOG(AUDIO, "OSS: can't open default audio capture device");
|
||||
return false;
|
||||
}
|
||||
int tmp = AFMT_S16_NE; // Native 16 bits
|
||||
if (ioctl(recordFD, SNDCTL_DSP_SETFMT, &tmp) == -1 || tmp != AFMT_S16_NE)
|
||||
{
|
||||
INFO_LOG(AUDIO, "OSS: can't set sample format");
|
||||
close(recordFD);
|
||||
recordFD = -1;
|
||||
return false;
|
||||
}
|
||||
tmp = 1;
|
||||
if (ioctl(recordFD, SNDCTL_DSP_CHANNELS, &tmp) == -1)
|
||||
{
|
||||
INFO_LOG(AUDIO, "OSS: can't set channel count");
|
||||
close(recordFD);
|
||||
recordFD = -1;
|
||||
return false;
|
||||
}
|
||||
tmp = sampling_freq;
|
||||
if (ioctl(recordFD, SNDCTL_DSP_SPEED, &tmp) == -1)
|
||||
{
|
||||
INFO_LOG(AUDIO, "OSS: can't set sample rate");
|
||||
close(recordFD);
|
||||
recordFD = -1;
|
||||
termRecord();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "audiostream.h"
|
||||
#include "cfg/option.h"
|
||||
#include "emulator.h"
|
||||
|
||||
static void registerForEvents();
|
||||
|
||||
struct SoundFrame { s16 l; s16 r; };
|
||||
|
||||
@@ -60,6 +63,7 @@ void WriteSample(s16 r, s16 l)
|
||||
|
||||
void InitAudio()
|
||||
{
|
||||
registerForEvents();
|
||||
TermAudio();
|
||||
|
||||
std::string slug = config::AudioBackend;
|
||||
@@ -138,3 +142,19 @@ void StopAudioRecording()
|
||||
currentBackend->termRecord();
|
||||
audio_recording_started = false;
|
||||
}
|
||||
|
||||
static void registerForEvents()
|
||||
{
|
||||
static bool done;
|
||||
if (done)
|
||||
return;
|
||||
done = true;
|
||||
// Empty the audio buffer when loading a state or terminating the game
|
||||
const auto& callback = [](Event, void *) {
|
||||
writePtr = 0;
|
||||
};
|
||||
EventManager::listen(Event::Terminate, callback);
|
||||
EventManager::listen(Event::LoadState, callback);
|
||||
}
|
||||
|
||||
|
||||
+15
-10
@@ -15,12 +15,8 @@
|
||||
//HOST_CPU
|
||||
#define CPU_X86 0x20000001
|
||||
#define CPU_ARM 0x20000002
|
||||
#define CPU_MIPS 0x20000003
|
||||
#define CPU_ARM64 0x20000003
|
||||
#define CPU_X64 0x20000004
|
||||
#define CPU_GENERIC 0x20000005 //used for pnacl, emscripten, etc
|
||||
#define CPU_PPC 0x20000006
|
||||
#define CPU_PPC64 0x20000007
|
||||
#define CPU_ARM64 0x20000008
|
||||
|
||||
//FEAT_SHREC, FEAT_AREC, FEAT_DSPREC
|
||||
#define DYNAREC_NONE 0x40000001
|
||||
@@ -36,10 +32,19 @@
|
||||
#define HOST_CPU CPU_ARM
|
||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||
#define HOST_CPU CPU_ARM64
|
||||
#elif defined(__mips__)
|
||||
#define HOST_CPU CPU_MIPS
|
||||
#else
|
||||
#define HOST_CPU CPU_GENERIC
|
||||
#error Unsupported architecture
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include "TargetConditionals.h"
|
||||
#if TARGET_OS_SIMULATOR
|
||||
// iOS simulator
|
||||
#define TARGET_NO_REC
|
||||
#endif
|
||||
#if defined(TARGET_MAC) && HOST_CPU == CPU_ARM64
|
||||
#define TARGET_ARM_MAC
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(TARGET_NO_REC)
|
||||
@@ -93,14 +98,14 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
|
||||
#define TARGET_UWP
|
||||
#define TARGET_UWP 1
|
||||
#endif
|
||||
#ifdef HAVE_D3D11
|
||||
#define USE_DX11
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(LIBRETRO) && !defined(TARGET_NO_EXCEPTIONS)
|
||||
#if !defined(LIBRETRO)
|
||||
#define USE_GGPO
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
option.h)
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
cfg.cpp
|
||||
cfg.h
|
||||
cl.cpp
|
||||
ini.cpp
|
||||
ini.h
|
||||
option.cpp)
|
||||
endif()
|
||||
@@ -130,3 +130,12 @@ void cfgSetAutoSave(bool autoSave)
|
||||
if (autoSave)
|
||||
saveConfigFile();
|
||||
}
|
||||
|
||||
void cfgSaveFloat(const std::string& section, const std::string& key, float value)
|
||||
{
|
||||
cfgdb.set_float(section, key, value);
|
||||
}
|
||||
float cfgLoadFloat(const std::string& section, const std::string& key, float def)
|
||||
{
|
||||
return cfgdb.get_float(section, key, def);
|
||||
}
|
||||
@@ -11,6 +11,8 @@ std::string cfgLoadStr(const std::string& section, const std::string& key, const
|
||||
void cfgSaveStr(const std::string& section, const std::string& key, const std::string& value);
|
||||
void cfgSaveBool(const std::string& section, const std::string& key, bool value);
|
||||
bool cfgLoadBool(const std::string& section, const std::string& key, bool def);
|
||||
void cfgSaveFloat(const std::string& section, const std::string& key, float value);
|
||||
float cfgLoadFloat(const std::string& section, const std::string& key, float def);
|
||||
void cfgSetVirtual(const std::string& section, const std::string& key, const std::string& value);
|
||||
bool cfgIsVirtual(const std::string& section, const std::string& key);
|
||||
|
||||
|
||||
@@ -47,6 +47,11 @@ bool ConfigEntry::get_bool()
|
||||
}
|
||||
}
|
||||
|
||||
float ConfigEntry::get_float()
|
||||
{
|
||||
return atof(this->value.c_str());
|
||||
}
|
||||
|
||||
/* ConfigSection */
|
||||
|
||||
bool ConfigSection::has_entry(const std::string& name)
|
||||
@@ -192,6 +197,15 @@ bool ConfigFile::get_bool(const std::string& section_name, const std::string& en
|
||||
}
|
||||
}
|
||||
|
||||
float ConfigFile::get_float(const std::string& section_name, const std::string& entry_name, float default_value)
|
||||
{
|
||||
ConfigEntry* entry = get_entry(section_name, entry_name);
|
||||
if (entry == nullptr)
|
||||
return default_value;
|
||||
else
|
||||
return entry->get_float();
|
||||
}
|
||||
|
||||
void ConfigFile::set(const std::string& section_name, const std::string& entry_name, const std::string& value, bool is_virtual)
|
||||
{
|
||||
ConfigSection* section = this->get_section(section_name, is_virtual);
|
||||
@@ -222,6 +236,13 @@ void ConfigFile::set_bool(const std::string& section_name, const std::string& en
|
||||
this->set(section_name, entry_name, str_value, is_virtual);
|
||||
}
|
||||
|
||||
void ConfigFile::set_float(const std::string& section_name, const std::string& entry_name, float value, bool is_virtual)
|
||||
{
|
||||
std::stringstream str_value;
|
||||
str_value << value;
|
||||
this->set(section_name, entry_name, str_value.str(), is_virtual);
|
||||
}
|
||||
|
||||
void ConfigFile::parse(FILE* file)
|
||||
{
|
||||
if (file == nullptr)
|
||||
|
||||
@@ -13,6 +13,7 @@ struct ConfigEntry {
|
||||
int get_int();
|
||||
bool get_bool();
|
||||
int64_t get_int64();
|
||||
float get_float();
|
||||
};
|
||||
|
||||
struct ConfigSection {
|
||||
@@ -45,11 +46,13 @@ struct ConfigFile {
|
||||
int get_int(const std::string& section_name, const std::string& entry_name, int default_value = 0);
|
||||
int64_t get_int64(const std::string& section_name, const std::string& entry_name, int64_t default_value = 0);
|
||||
bool get_bool(const std::string& section_name, const std::string& entry_name, bool default_value = false);
|
||||
float get_float(const std::string& section_name, const std::string& entry_name, float default_value = 0.f);
|
||||
/* setting values */
|
||||
void set(const std::string& section_name, const std::string& entry_name, const std::string& value, bool is_virtual = false);
|
||||
void set_int(const std::string& section_name, const std::string& entry_name, int value, bool is_virtual = false);
|
||||
void set_int64(const std::string& section_name, const std::string& entry_name, int64_t value, bool is_virtual = false);
|
||||
void set_bool(const std::string& section_name, const std::string& entry_name, bool value, bool is_virtual = false);
|
||||
void set_float(const std::string& section_name, const std::string& entry_name, float value, bool is_virtual = false);
|
||||
|
||||
void delete_section(const std::string& section_name);
|
||||
void delete_entry(const std::string& section_name, const std::string& entry_name);
|
||||
|
||||
+45
-11
@@ -40,6 +40,7 @@ Option<bool> ForceFreePlay("ForceFreePlay", true);
|
||||
Option<bool, false> FetchBoxart("FetchBoxart", true);
|
||||
Option<bool, false> BoxartDisplayMode("BoxartDisplayMode", true);
|
||||
Option<int, false> UIScaling("UIScaling", 100);
|
||||
Option<int, false> UITheme("UITheme", 0);
|
||||
|
||||
// Sound
|
||||
|
||||
@@ -71,11 +72,13 @@ Option<bool> ShowFPS("rend.ShowFPS");
|
||||
Option<bool> RenderToTextureBuffer("rend.RenderToTextureBuffer");
|
||||
Option<bool> TranslucentPolygonDepthMask("rend.TranslucentPolygonDepthMask");
|
||||
Option<bool> ModifierVolumes("rend.ModifierVolumes", true);
|
||||
Option<int> TextureUpscale("rend.TextureUpscale", 1);
|
||||
Option<int> TextureUpscale("rend.TextureUpscale2", 1);
|
||||
Option<int> MaxFilteredTextureSize("rend.MaxFilteredTextureSize", 256);
|
||||
Option<float> ExtraDepthScale("rend.ExtraDepthScale", 1.f);
|
||||
Option<bool> CustomTextures("rend.CustomTextures");
|
||||
Option<bool> PreloadCustomTextures("rend.PreloadCustomTextures");
|
||||
Option<bool> DumpTextures("rend.DumpTextures");
|
||||
Option<bool> DumpReplacedTextures("rend.DumpReplacedTextures");
|
||||
Option<int> ScreenStretching("rend.ScreenStretching", 100);
|
||||
Option<bool> Fog("rend.Fog", true);
|
||||
Option<bool> FloatVMUs("rend.FloatVMUs");
|
||||
@@ -98,6 +101,8 @@ Option<int> SkipFrame("ta.skip");
|
||||
Option<int> MaxThreads("pvr.MaxThreads", 3);
|
||||
Option<int> AutoSkipFrame("pvr.AutoSkipFrame", 0);
|
||||
Option<int> RenderResolution("rend.Resolution", 480);
|
||||
Option<bool> IntegerScale("rend.IntegerScale", false);
|
||||
Option<bool> LinearInterpolation("rend.LinearInterpolation", true);
|
||||
Option<bool> VSync("rend.vsync", true);
|
||||
Option<int64_t> PixelBufferSize("rend.PixelBufferSize", 512_MB);
|
||||
Option<int> AnisotropicFiltering("rend.AnisotropicFiltering", 1);
|
||||
@@ -105,7 +110,11 @@ Option<int> TextureFiltering("rend.TextureFiltering", 0); // Default
|
||||
Option<bool> ThreadedRendering("rend.ThreadedRendering", true);
|
||||
Option<bool> DupeFrames("rend.DupeFrames", false);
|
||||
Option<int> PerPixelLayers("rend.PerPixelLayers", 32);
|
||||
#ifdef TARGET_UWP
|
||||
Option<bool> NativeDepthInterpolation("rend.NativeDepthInterpolation", true);
|
||||
#else
|
||||
Option<bool> NativeDepthInterpolation("rend.NativeDepthInterpolation", false);
|
||||
#endif
|
||||
Option<bool> EmulateFramebuffer("rend.EmulateFramebuffer", false);
|
||||
Option<bool> FixUpscaleBleedingEdge("rend.FixUpscaleBleedingEdge", true);
|
||||
Option<bool> CustomGpuDriver("rend.CustomGpuDriver", false);
|
||||
@@ -129,9 +138,22 @@ Option<bool> RamMod32MB("Dreamcast.RamMod32MB", false);
|
||||
Option<bool> OpenGlChecks("OpenGlChecks", false, "validate");
|
||||
|
||||
Option<std::vector<std::string>, false> ContentPath("Dreamcast.ContentPath");
|
||||
Option<std::vector<std::string>, false> BiosPath("Dreamcast.BiosPath");
|
||||
Option<std::string, false> VMUPath("Dreamcast.VMUPath");
|
||||
Option<std::vector<std::string>, false> SavestatePath("Dreamcast.SavestatePath");
|
||||
Option<std::string, false> SavePath("Dreamcast.SavePath");
|
||||
Option<std::vector<std::string>, false> TexturePath("Dreamcast.TexturePath");
|
||||
Option<std::string, false> TextureDumpPath("Dreamcast.TextureDumpPath");
|
||||
Option<std::string, false> BoxartPath("Dreamcast.BoxartPath");
|
||||
Option<std::vector<std::string>, false> MappingsPath("Dreamcast.MappingsPath");
|
||||
Option<std::vector<std::string>, false> CheatPath("Dreamcast.CheatPath");
|
||||
Option<bool, false> HideLegacyNaomiRoms("Dreamcast.HideLegacyNaomiRoms", true);
|
||||
Option<bool, false> UploadCrashLogs("UploadCrashLogs", true);
|
||||
Option<bool, false> DiscordPresence("DiscordPresence", true);
|
||||
#if defined(__ANDROID__) && !defined(LIBRETRO)
|
||||
Option<bool, false> UseSafFilePicker("UseSafFilePicker", true);
|
||||
#endif
|
||||
OptionString LogServer("LogServer", "", "log");
|
||||
|
||||
// Profiler
|
||||
Option<bool> ProfilerEnabled("Profiler.Enabled");
|
||||
@@ -143,7 +165,7 @@ Option<float> ProfilerFrameWarningTime("Profiler.FrameWarningTime", 1.0f / 55.0f
|
||||
|
||||
Option<bool> NetworkEnable("Enable", false, "network");
|
||||
Option<bool> ActAsServer("ActAsServer", false, "network");
|
||||
OptionString DNS("DNS", "46.101.91.123", "network");
|
||||
OptionString DNS("DNS", "dns.flyca.st", "network");
|
||||
OptionString NetworkServer("server", "", "network");
|
||||
Option<int> LocalPort("LocalPort", NaomiNetwork::SERVER_PORT, "network");
|
||||
Option<bool> EmulateBBA("EmulateBBA", false, "network");
|
||||
@@ -158,6 +180,8 @@ Option<int> GGPOChatTimeout("GGPOChatTimeout", 10, "network");
|
||||
Option<bool> NetworkOutput("NetworkOutput", false, "network");
|
||||
Option<int> MultiboardSlaves("MultiboardSlaves", 1, "network");
|
||||
Option<bool> BattleCableEnable("BattleCable", false, "network");
|
||||
Option<bool> UseDCNet("DCNet", false, "network");
|
||||
OptionString ISPUsername("ISPUsername", "flycast1", "network");
|
||||
|
||||
#ifdef USE_OMX
|
||||
Option<int> OmxAudioLatency("audio_latency", 100, "omx");
|
||||
@@ -168,6 +192,7 @@ Option<bool> OmxAudioHdmi("audio_hdmi", true, "omx");
|
||||
|
||||
Option<int> MouseSensitivity("MouseSensitivity", 100, "input");
|
||||
Option<int> VirtualGamepadVibration("VirtualGamepadVibration", 20, "input");
|
||||
Option<int> VirtualGamepadTransparency("VirtualGamepadTransparency", 37, "input");
|
||||
|
||||
std::array<Option<MapleDeviceType>, 4> MapleMainDevices {
|
||||
Option<MapleDeviceType>("device1", MDT_SegaController, "input"),
|
||||
@@ -175,23 +200,32 @@ std::array<Option<MapleDeviceType>, 4> MapleMainDevices {
|
||||
Option<MapleDeviceType>("device3", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device4", MDT_None, "input"),
|
||||
};
|
||||
std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices {
|
||||
Option<MapleDeviceType>("device1.1", MDT_SegaVMU, "input"),
|
||||
Option<MapleDeviceType>("device1.2", MDT_SegaVMU, "input"),
|
||||
std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices {{
|
||||
{{Option<MapleDeviceType>("device1.1", MDT_SegaVMU, "input"),
|
||||
Option<MapleDeviceType>("device1.2", MDT_SegaVMU, "input")}},
|
||||
|
||||
Option<MapleDeviceType>("device2.1", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device2.2", MDT_None, "input"),
|
||||
{{Option<MapleDeviceType>("device2.1", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device2.2", MDT_None, "input")}},
|
||||
|
||||
Option<MapleDeviceType>("device3.1", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device3.2", MDT_None, "input"),
|
||||
{{Option<MapleDeviceType>("device3.1", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device3.2", MDT_None, "input")}},
|
||||
|
||||
Option<MapleDeviceType>("device4.1", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device4.2", MDT_None, "input"),
|
||||
{{Option<MapleDeviceType>("device4.1", MDT_None, "input"),
|
||||
Option<MapleDeviceType>("device4.2", MDT_None, "input")}},
|
||||
}};
|
||||
|
||||
std::array<Option<bool>, 4> UseNetworkExpansionDevices{
|
||||
Option<bool>("device1.UseNetworkExpansionDevices", false, "input"),
|
||||
Option<bool>("device2.UseNetworkExpansionDevices", false, "input"),
|
||||
Option<bool>("device3.UseNetworkExpansionDevices", false, "input"),
|
||||
Option<bool>("device4.UseNetworkExpansionDevices", false, "input"),
|
||||
};
|
||||
|
||||
Option<bool> PerGameVmu("PerGameVmu", false, "config");
|
||||
#ifdef _WIN32
|
||||
Option<bool, false> UseRawInput("RawInput", false, "input");
|
||||
#endif
|
||||
Option<bool> UsePhysicalVmuMemory("UsePhysicalVmuMemory", true);
|
||||
|
||||
#ifdef USE_LUA
|
||||
Option<std::string, false> LuaFileName("LuaFileName", "flycast.lua");
|
||||
|
||||
+25
-8
@@ -202,11 +202,7 @@ protected:
|
||||
std::enable_if_t<std::is_same_v<float, U>, T>
|
||||
doLoad(const std::string& section, const std::string& name) const
|
||||
{
|
||||
std::string strValue = cfgLoadStr(section, name, "");
|
||||
if (strValue.empty())
|
||||
return value;
|
||||
else
|
||||
return (float)atof(strValue.c_str());
|
||||
return cfgLoadFloat(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
@@ -301,9 +297,7 @@ protected:
|
||||
std::enable_if_t<std::is_same_v<float, U>>
|
||||
doSave(const std::string& section, const std::string& name) const
|
||||
{
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "%f", value);
|
||||
cfgSaveStr(section, name, buf);
|
||||
cfgSaveFloat(section, name, value);
|
||||
}
|
||||
|
||||
template <typename U = T>
|
||||
@@ -373,6 +367,7 @@ extern Option<bool> ForceFreePlay;
|
||||
extern Option<bool, false> FetchBoxart;
|
||||
extern Option<bool, false> BoxartDisplayMode;
|
||||
extern Option<int, false> UIScaling;
|
||||
extern Option<int, false> UITheme; // 0 -> Dark, 1 -> Light, 2 -> Dreamcast, 3 -> High Contrast, 4 -> Nintendo, 5 -> Aqua Chill
|
||||
|
||||
// Sound
|
||||
|
||||
@@ -451,7 +446,9 @@ extern Option<int> PerPixelLayers;
|
||||
#endif
|
||||
extern Option<float> ExtraDepthScale;
|
||||
extern Option<bool> CustomTextures;
|
||||
extern Option<bool> PreloadCustomTextures;
|
||||
extern Option<bool> DumpTextures;
|
||||
extern Option<bool> DumpReplacedTextures;
|
||||
extern Option<int> ScreenStretching; // in percent. 150 means stretch from 4/3 to 6/3
|
||||
extern Option<bool> Fog;
|
||||
extern Option<bool> FloatVMUs;
|
||||
@@ -465,6 +462,8 @@ extern Option<int> SkipFrame;
|
||||
extern Option<int> MaxThreads;
|
||||
extern Option<int> AutoSkipFrame; // 0: none, 1: some, 2: more
|
||||
extern Option<int> RenderResolution;
|
||||
extern Option<bool> IntegerScale;
|
||||
extern Option<bool> LinearInterpolation;
|
||||
extern Option<bool> VSync;
|
||||
extern Option<int64_t> PixelBufferSize;
|
||||
extern Option<int> AnisotropicFiltering;
|
||||
@@ -495,9 +494,22 @@ extern Option<bool> RamMod32MB;
|
||||
extern Option<bool> OpenGlChecks;
|
||||
|
||||
extern Option<std::vector<std::string>, false> ContentPath;
|
||||
extern Option<std::vector<std::string>, false> BiosPath;
|
||||
extern Option<std::string, false> VMUPath;
|
||||
extern Option<std::vector<std::string>, false> SavestatePath;
|
||||
extern Option<std::string, false> SavePath;
|
||||
extern Option<std::vector<std::string>, false> TexturePath;
|
||||
extern Option<std::string, false> TextureDumpPath;
|
||||
extern Option<std::string, false> BoxartPath;
|
||||
extern Option<std::vector<std::string>, false> MappingsPath;
|
||||
extern Option<std::vector<std::string>, false> CheatPath;
|
||||
extern Option<bool, false> HideLegacyNaomiRoms;
|
||||
extern Option<bool, false> UploadCrashLogs;
|
||||
extern Option<bool, false> DiscordPresence;
|
||||
#if defined(__ANDROID__) && !defined(LIBRETRO)
|
||||
extern Option<bool, false> UseSafFilePicker;
|
||||
#endif
|
||||
extern OptionString LogServer;
|
||||
|
||||
// Profiling
|
||||
extern Option<bool> ProfilerEnabled;
|
||||
@@ -524,6 +536,8 @@ extern Option<int> GGPOChatTimeout;
|
||||
extern Option<bool> NetworkOutput;
|
||||
extern Option<int> MultiboardSlaves;
|
||||
extern Option<bool> BattleCableEnable;
|
||||
extern Option<bool> UseDCNet;
|
||||
extern OptionString ISPUsername;
|
||||
|
||||
#ifdef USE_OMX
|
||||
extern Option<int> OmxAudioLatency;
|
||||
@@ -534,14 +548,17 @@ extern Option<bool> OmxAudioHdmi;
|
||||
|
||||
extern Option<int> MouseSensitivity;
|
||||
extern Option<int> VirtualGamepadVibration;
|
||||
extern Option<int> VirtualGamepadTransparency;
|
||||
extern std::array<Option<MapleDeviceType>, 4> MapleMainDevices;
|
||||
extern std::array<std::array<Option<MapleDeviceType>, 2>, 4> MapleExpansionDevices;
|
||||
extern std::array<Option<bool>, 4> UseNetworkExpansionDevices;
|
||||
extern Option<bool> PerGameVmu;
|
||||
#ifdef _WIN32
|
||||
extern Option<bool, false> UseRawInput;
|
||||
#else
|
||||
constexpr bool UseRawInput = false;
|
||||
#endif
|
||||
extern Option<bool> UsePhysicalVmuMemory;
|
||||
|
||||
#ifdef USE_LUA
|
||||
extern Option<std::string, false> LuaFileName;
|
||||
|
||||
+224
-32
@@ -83,7 +83,7 @@ const WidescreenCheat CheatManager::widescreen_cheats[] =
|
||||
{ "T30006M", nullptr, { 0x4CF42C, 0x4CF45C, 0x3E1A36, 0x3E1A34, 0x3E1A3C, 0x3E1A54, 0x3E1A5C },
|
||||
{ 0x43F00000, 0x3F400000, 0x08010000, 0, 0, 0, 0 } },
|
||||
{ "MK-5103750", nullptr, { 0x1FE270 }, { 0x43700000 } }, // Daytona USA (PAL)
|
||||
{ "MK-51037", nullptr, { 0x1FC6D0 }, { 0x43700000 } }, // Daytona USA (USA)
|
||||
// breaks online connection { "MK-51037", nullptr, { 0x1FC6D0 }, { 0x43700000 } }, // Daytona USA (USA)
|
||||
{ "T9501N-50", nullptr, { 0x9821D4 }, { 0x3F400000 } }, // Deadly Skies (PAL)
|
||||
{ "T8116D 50", nullptr, { 0x2E5530 }, { 0x43700000 } }, // Dead or Alive 2 (PAL)
|
||||
{ "T3601N", nullptr, { 0x2F0670 }, { 0x43700000 } }, // Dead or Alive 2 (USA)
|
||||
@@ -165,10 +165,11 @@ const WidescreenCheat CheatManager::widescreen_cheats[] =
|
||||
{ 0x43F00000, 0x3F400000, 0x3F800000 } }, // Napple Tale: Arsia in Daydream (JP)
|
||||
//crash { "MK-51178", nullptr, { 0x23AF00, 0x23B160, 0x144D40, 0x2105B4, 0x705B40 },
|
||||
// { 0xBFAAAAAB, 0xBFAAAAAB, 0xBFAAAAAB, 0xBFAAAAAB, 0x44558000 } }, // NBA 2K2
|
||||
{ "T9504M", nullptr, { 0xCDE848, 0xCDE844 }, { 0x3F400000, 0x3FA00000 } }, // Nightmare Creatures II (USA)
|
||||
{ "T-9502D-50", nullptr, { 0xBDE9B0, 0xBDE9C4 }, { 0x3F400000, 0x3FA00000 } }, // Nightmare Creatures II (PAL)
|
||||
// { "T9504M", nullptr, { 0xCDE848, 0xCDE844 }, { 0x3F400000, 0x3FA00000 } }, // Nightmare Creatures II (USA)
|
||||
// { "T-9502D-50", nullptr, { 0xBDE9B0, 0xBDE9C4 }, { 0x3F400000, 0x3FA00000 } }, // Nightmare Creatures II (PAL)
|
||||
{ "MK-5110250", nullptr, { 0x87B5A4 }, { 0x43700000 } }, // Outtrigger (PAL)
|
||||
{ "HDR-0118", nullptr, { 0x83E284 }, { 0x43700000 } }, // Outtrigger (JP)
|
||||
{ "MK-51102", nullptr, { 0x83E284 }, { 0x43700000 } }, // Outtrigger (US)
|
||||
{ "T15103D 50", nullptr, { 0x1EEE78 }, { 0x3F400000 } }, // PenPen (PAL)
|
||||
{ "T17001M", nullptr, { 0x1C3828 }, { 0x3F400000 } }, // PenPen TriIcelon (JP)
|
||||
{ "MK-5110050", nullptr, { 0x548E04, 0x0923C0 }, { 0x43E80000, 0x3F966666 } }, // Phantasy Star Online (PAL) TODO
|
||||
@@ -377,6 +378,17 @@ void CheatManager::setActive(bool active)
|
||||
void CheatManager::loadCheatFile(const std::string& filename)
|
||||
{
|
||||
#ifndef LIBRETRO
|
||||
try {
|
||||
hostfs::FileInfo fileInfo = hostfs::storage().getFileInfo(filename);
|
||||
if (fileInfo.size >= 1_MB) {
|
||||
WARN_LOG(COMMON, "Cheat file '%s' is too big", filename.c_str());
|
||||
return;
|
||||
}
|
||||
} catch (const hostfs::StorageException& e) {
|
||||
WARN_LOG(COMMON, "Cannot find cheat file '%s': %s", filename.c_str(), e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
FILE* cheatfile = hostfs::storage().openFile(filename, "r");
|
||||
if (cheatfile == nullptr)
|
||||
{
|
||||
@@ -421,7 +433,8 @@ void CheatManager::loadCheatFile(const std::string& filename)
|
||||
}
|
||||
setActive(!cheats.empty());
|
||||
INFO_LOG(COMMON, "%d cheats loaded", (int)cheats.size());
|
||||
cfgSaveStr("cheats", gameId, filename);
|
||||
if (!cheats.empty())
|
||||
cfgSaveStr("cheats", gameId, filename);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -433,13 +446,42 @@ void CheatManager::reset(const std::string& gameId)
|
||||
cheats.clear();
|
||||
setActive(false);
|
||||
this->gameId = gameId;
|
||||
|
||||
#ifndef LIBRETRO
|
||||
if (!settings.raHardcoreMode)
|
||||
{
|
||||
std::string cheatFile = cfgLoadStr("cheats", gameId, "");
|
||||
if (!cheatFile.empty())
|
||||
loadCheatFile(cheatFile);
|
||||
else
|
||||
{
|
||||
// Try to auto-locate a cheat file in user-defined CheatPath
|
||||
std::string romName = settings.content.fileName;
|
||||
const char* exts[] = { ".cht", ".txt" };
|
||||
for (const auto& base : config::CheatPath.get())
|
||||
{
|
||||
if (base.empty())
|
||||
continue;
|
||||
for (const char* ext : exts)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::string candidate = hostfs::storage().getSubPath(base, romName + ext);
|
||||
if (hostfs::storage().exists(candidate))
|
||||
{
|
||||
loadCheatFile(candidate);
|
||||
cfgSaveStr("cheats", gameId, candidate);
|
||||
goto found_cheats;
|
||||
}
|
||||
}
|
||||
catch (const hostfs::StorageException&)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
found_cheats:
|
||||
#endif
|
||||
size_t cheatCount = cheats.size();
|
||||
if (gameId == "Fixed BOOT strapper") // Extreme Hunting 2
|
||||
@@ -454,31 +496,6 @@ void CheatManager::reset(const std::string& gameId)
|
||||
else if (gameId == "THE KING OF ROUTE66") {
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ignore drive error", true, 32, 0x00023ee0, 0x0009000B, true); // rts, nop
|
||||
}
|
||||
else if (gameId.substr(0, 8) == "MKG TKOB")
|
||||
{
|
||||
const auto& setMushikingCheats = [this](u32 addr) {
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid1 error", true, 32, addr, 0, true); // rfid[0].error = 0
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid2 error", true, 32, addr + 0x48, 0, true); // rfid[1].error = 0
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid1 status", true, 32, addr + 8, 0, true); // rfid[0].data18 = 0
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ignore rfid2 status", true, 32, addr + 0x50, 0, true); // rfid[1].data18 = 0
|
||||
};
|
||||
if (gameId == "MKG TKOB 2 EXP VER1.001-") // mushi2eo
|
||||
setMushikingCheats(0x6fe1bc);
|
||||
else if (gameId == "MKG TKOB 2 JPN VER2.001-") // mushik2e
|
||||
setMushikingCheats(0x6ffe54);
|
||||
else if (gameId == "MKG TKOB 2K3 2ND VER1.003-")// mushike
|
||||
setMushikingCheats(0x4ad7ec);
|
||||
else if (gameId == "MKG TKOB 4 JPN VER2.000-") // mushik4e
|
||||
setMushikingCheats(0xb0e538);
|
||||
else if (gameId == "MKG TKOB 2K3 2ND VER1.002-")// mushikeo
|
||||
setMushikingCheats(0x4ad56c);
|
||||
else if (gameId == "MKG TKOB 2K3 2ND KOR VER1.000-") // mushikk
|
||||
setMushikingCheats(0x4ac9b8);
|
||||
else if (gameId == "MKG TKOB 2K3 2ND CHN VER1.000-") // mushikc
|
||||
setMushikingCheats(0x4aa9b8);
|
||||
else if (gameId == "MKG TKOB 2 KOR VER1.000-") // mushik2k
|
||||
setMushikingCheats(0x706084);
|
||||
}
|
||||
else if (gameId == "T-8120N") { // Dave Mirra BMX (US)
|
||||
cheats.emplace_back(Cheat::Type::setValue, "fix main loop time", true, 32, 0x0030b8cc, 0x42040000, true); // 33.0 ms
|
||||
}
|
||||
@@ -494,6 +511,117 @@ void CheatManager::reset(const std::string& gameId)
|
||||
else if (gameId == "SAMURAI SPIRITS 6" || gameId == "T0002M") {
|
||||
cheats.emplace_back(Cheat::Type::setValue, "fix depth", true, 16, 0x0003e602, 0x0009, true); // nop (shift by 8 bits instead of 10)
|
||||
}
|
||||
else if (gameId == "T-8107N") { // Fur Fighters (US)
|
||||
// force logging on to use more cycles
|
||||
cheats.emplace_back(Cheat::Type::setValue, "enable logging", true, 32, 0x00314248, 1, true);
|
||||
}
|
||||
else if (gameId == "T-8113D-50") { // Fur Fighters (EU)
|
||||
// force logging on to use more cycles
|
||||
cheats.emplace_back(Cheat::Type::setValue, "enable logging", true, 32, 0x00314228, 1, true);
|
||||
}
|
||||
// Dricas auth bypass
|
||||
else if (gameId == "T6807M") // Aero Dancing i
|
||||
{
|
||||
// modem
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x0004b7a0, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x0004b7a0, 0xe000000b, true); // rts, _mov #0, r0
|
||||
// BBA
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bba bypass auth ifeq", true, 32, 0x0004af5c, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bba bypass dricas auth", true, 32, 0x0004af5c, 0xe000000b, true);
|
||||
// IP check
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "ip check ifeq", true, 32, 0x00020860, 0x4f222fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ip check ok", true, 32, 0x00020860, 0xe000000b, true);
|
||||
}
|
||||
else if (gameId == "T6809M") // Aero Dancing i - Jikai Saku Made Matemasen
|
||||
{
|
||||
// modem
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x0004b940, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x0004b940, 0xe000000b, true);
|
||||
// BBA
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bba bypass auth ifeq", true, 32, 0x0004f848, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bba bypass dricas auth", true, 32, 0x0004f848, 0xe000000b, true);
|
||||
// IP check
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "ip check ifeq", true, 32, 0x00020980, 0x4f222fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "ip check ok", true, 32, 0x00020980, 0xe000000b, true);
|
||||
}
|
||||
else if (gameId == "T6805M") { // Aero Dancing F - Todoroki Tsubasa no Hatsu Hikou
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x0003ed10, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x0003ed10, 0xe000000b, true);
|
||||
}
|
||||
else if (gameId == "HDR-0106") { // Daytona USA (JP)
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x0003ad30, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x0003ad30, 0xe000000b, true);
|
||||
}
|
||||
else if (gameId == "HDR-0073") { // Sega Tetris
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x000a56f8, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x000a56f8, 0xe000000b, true);
|
||||
}
|
||||
else if (gameId == "T44501M") { // Golf Shiyou Yo 2
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x0013f150, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x0013f150, 0xe000000b, true);
|
||||
}
|
||||
else if (gameId == "HDR-0124") { // Hundred Swords
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x006558ac, 0x1f414f22, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x006558ac, 0xe000000b, true);
|
||||
}
|
||||
else if (gameId == "T43903M") { // Culdcept II
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x00800524, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x00800524, 0xe000000b, true);
|
||||
}
|
||||
else if (gameId == "T40214N") { // The Next Tetris (US)
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass speed ifeq", true, 32, 0x0016d5d4, 0x2f862fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass speed check", true, 32, 0x0016d5d4, 0xe001000b, true);
|
||||
}
|
||||
else if (gameId == "MK-51065") { // Bomberman Online
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode ifeq", true, 32, 0x00196da8, 0x2c302c30, true); // 0,0,
|
||||
cheats.emplace_back(Cheat::Type::setValue, "modem automode set", true, 32, 0x00196da8, 0x2c302c31, true); // 1,0,
|
||||
}
|
||||
else if (gameId == "MK-51102") { // Outtrigger (US)
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode ifeq", true, 32, 0x001bdb48, 0x2c302c30, true); // 0,0,
|
||||
cheats.emplace_back(Cheat::Type::setValue, "modem automode set", true, 32, 0x001bdb48, 0x2c302c31, true); // 1,0,
|
||||
}
|
||||
else if (gameId == "HDR-0118") { // Outtrigger (JP)
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "bypass auth ifeq", true, 32, 0x00139f54, 0x2fd62fe6, true);
|
||||
cheats.emplace_back(Cheat::Type::setValue, "bypass dricas auth", true, 32, 0x00139f54, 0xe000000b, true);
|
||||
}
|
||||
else if (gameId == "T13306M") // Mobile Suit Gundam: Federation vs. Zeon
|
||||
{
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem rxspeed ifeq", true, 32, 0x0016e710, 0x3434312c, true); // ",144"
|
||||
cheats.emplace_back(Cheat::Type::setValue, "modem rxspeed set", true, 32, 0x0016e710, 0x2020202c, true); // ", "
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem txspeed ifeq", true, 32, 0x0016e71c, 0x3434312c, true); // ",144"
|
||||
cheats.emplace_back(Cheat::Type::setValue, "modem txspeed set", true, 32, 0x0016e71c, 0x2020202c, true); // ", "
|
||||
}
|
||||
else if (gameId == "MK-51162") // Propeller Arena
|
||||
{
|
||||
// 0c1f56f0: ,0,28800,28800,28800,28800\\r"\n
|
||||
// 0c1f5770: ,0,28800,28800,28800,28800\\r"\n
|
||||
for (u32 addr = 0x001f56f0; addr <= 0x001f5770; addr += 0x80)
|
||||
{
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode1 ifeq", true, 32, addr + 0x00, 0x322c302c, true); // ",0,2"
|
||||
cheats.emplace_back(Cheat::Type::setValue, "modem automode1 set", true, 32, addr + 0x00, 0x202c312c, true); // ",1, "
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode2 ifeq", true, 32, addr + 0x04, 0x30303838, true); // "8800"
|
||||
cheats.emplace_back(Cheat::Type::setValue, "modem automode2 set", true, 32, addr + 0x04, 0x30202020, true); // " 0"
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode3 ifeq", true, 32, addr + 0x0c, 0x322c3030, true); // "00,2"
|
||||
cheats.emplace_back(Cheat::Type::setValue, "modem automode3 set", true, 32, addr + 0x0c, 0x202c3030, true); // "00, "
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "modem automode4 ifeq", true, 32, addr + 0x10, 0x30303838, true); // "8800"
|
||||
cheats.emplace_back(Cheat::Type::setValue, "modem automode4 set", true, 32, addr + 0x10, 0x30202020, true); // " 0"
|
||||
}
|
||||
}
|
||||
else if (gameId == "HDR-0113") // Power Smash
|
||||
{
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe SYN ifeq", true, 16, 0x14d258, 0xbbce, true); // bsr SendNormalSYN
|
||||
cheats.emplace_back(Cheat::Type::setValue, "no dupe SYN set", true, 16, 0x14d258, 0x0009, true); // nop
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe ACK ifeq", true, 16, 0x14af42, 0x430b, true); // jsr TCPInternalSendPacket
|
||||
cheats.emplace_back(Cheat::Type::setValue, "no dupe ACK set", true, 16, 0x14af42, 0x0009, true); // nop
|
||||
}
|
||||
else if (gameId == "HDR-0091") // Pro Yakyuu Team de Asobou Net!
|
||||
{
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe SYN ifeq", true, 16, 0xe13e0c, 0xbbce, true); // bsr SendNormalSYN
|
||||
cheats.emplace_back(Cheat::Type::setValue, "no dupe SYN set", true, 16, 0xe13e0c, 0x0009, true); // nop
|
||||
cheats.emplace_back(Cheat::Type::runNextIfEq, "no dupe ACK ifeq", true, 16, 0xe11af6, 0x430b, true); // jsr TCPInternalSendPacket
|
||||
cheats.emplace_back(Cheat::Type::setValue, "no dupe ACK set", true, 16, 0xe11af6, 0x0009, true); // nop
|
||||
}
|
||||
|
||||
if (cheats.size() > cheatCount)
|
||||
setActive(true);
|
||||
}
|
||||
@@ -685,6 +813,42 @@ static std::vector<u32> parseCodes(const std::string& s)
|
||||
return codes;
|
||||
}
|
||||
|
||||
// Decrypt (some) Action Replay/Gameshark/Codebreaker/Xploder encrypted codes
|
||||
// Master codes change the encryption method and thus are not supported (07xxxxxx)
|
||||
// Taken from DCcrypt
|
||||
constexpr u32 Seeds[16] = {
|
||||
0xA53A8888,
|
||||
0xA1427921, 0xAC9528B1, 0xC5892354, 0x49671B12,
|
||||
0xACC56121, 0xACB5381E, 0x765436E1, 0x9F2C3E54,
|
||||
0x1133E312, 0xAC5E7894, 0xE9F208B1, 0x4E87DCFE,
|
||||
0x43174312, 0x1D7A6C99, 0x874224A2
|
||||
};
|
||||
|
||||
static u32 decryptCode(u32 v)
|
||||
{
|
||||
constexpr u32 something = 6;
|
||||
|
||||
if ((v & 0xf0000000) == 0)
|
||||
return v;
|
||||
u32 seed = Seeds[v >> 28];
|
||||
if (something & 4)
|
||||
v = (((v << 1) & 0x0FFFFFFE) | ((v >> 27) & 1));
|
||||
if (something & 2) {
|
||||
v = (((v << 1) & 0x0FFFFFFE) | ((v >> 27) & 1));
|
||||
v = (((v << 8) & 0x0FFFFF00) | ((v >> 20) & 0xFF));
|
||||
}
|
||||
if (something & 1)
|
||||
seed >>= 4;
|
||||
u32 ret = (seed & 0x0FFFFFFF) ^ v;
|
||||
//if ((ret & 0x0FF00000) != 0x07100000)
|
||||
// something = (ret & 0x0f) + 6;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u32 decryptArg(u32 v) {
|
||||
return (((v + 0x543700D0) >> 29) | ((v + 0x543700D0) * 8)) ^ Seeds[0];
|
||||
}
|
||||
|
||||
void CheatManager::addGameSharkCheat(const std::string& name, const std::string& s)
|
||||
{
|
||||
std::vector<u32> codes = parseCodes(s);
|
||||
@@ -700,6 +864,22 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
Cheat cheat{};
|
||||
cheat.description = name;
|
||||
u32 code = (codes[i] & 0xff000000) >> 24;
|
||||
if (code & 0xf0)
|
||||
{
|
||||
// encrypted code
|
||||
codes[i] = decryptCode(codes[i]);
|
||||
code = (codes[i] & 0xff000000) >> 24;
|
||||
if (code == 4 && i + 2 < codes.size()) {
|
||||
// following args are also encrypted
|
||||
codes[i + 1] = decryptArg(codes[i + 1]);
|
||||
codes[i + 2] = decryptArg(codes[i + 2]);
|
||||
}
|
||||
else if (code == 0xd && i + 2 < codes.size()) {
|
||||
// following arg is also encrypted
|
||||
codes[i + 1] = decryptArg(codes[i + 1]);
|
||||
}
|
||||
// TODO Which ops have encrypted args (3, 5, E, F)? Couldn't find any
|
||||
}
|
||||
switch (code)
|
||||
{
|
||||
case 0:
|
||||
@@ -813,9 +993,17 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
cheats.push_back(cheat);
|
||||
}
|
||||
break;
|
||||
// TODO 7 change decryption type
|
||||
// TODO 0xb delay applying codes
|
||||
// TODO 0xc global enable test
|
||||
|
||||
case 7:
|
||||
// change decryption type: 071000XX (example: 07100005)
|
||||
throw FlycastException("Master codes aren't supported");
|
||||
|
||||
// TODO 0xb delay applying codes: 0b0xxxxx
|
||||
// Delay putting on codes for xxxxx cycles. Default 1000 (0x3e7)
|
||||
// TODO 0xc global enable test: 0cxxxxxx vvvvvvvv
|
||||
// If the value at address 8Cxxxxxx is equal to vvvvvvvv, execute ALL codes;
|
||||
// otherwise no codes are executed. Useful for waiting until game has loaded.
|
||||
|
||||
case 0xd:
|
||||
{
|
||||
// enable next code if eq/neq/lt/gt
|
||||
@@ -873,6 +1061,10 @@ void CheatManager::addGameSharkCheat(const std::string& name, const std::string&
|
||||
conditionCheat = cheat;
|
||||
}
|
||||
break;
|
||||
|
||||
// TODO 0xF: 0F-XXXXXX 0000YYYY
|
||||
// 16-Bit Write Once Immediately. (Activator code)
|
||||
|
||||
default:
|
||||
throw FlycastException("Unsupported cheat type");
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include "emulator.h"
|
||||
#include "hw/sh4/sh4_if.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include "hw/sh4/sh4_interpreter.h"
|
||||
#include "hw/sh4/dyna/shil.h"
|
||||
#include "cfg/option.h"
|
||||
#include <array>
|
||||
#include <signal.h>
|
||||
@@ -122,9 +122,9 @@ public:
|
||||
for (u32 i = 0; i < Sh4RegList.size(); i++)
|
||||
{
|
||||
if (Sh4RegList[i] == reg_sr_status)
|
||||
allregs[i] = sh4_sr_GetFull();
|
||||
allregs[i] = Sh4cntx.sr.getFull();
|
||||
else if (Sh4RegList[i] != NoReg)
|
||||
allregs[i] = *GetRegPtr(Sh4RegList[i]);
|
||||
allregs[i] = *GetRegPtr(Sh4cntx, Sh4RegList[i]);
|
||||
}
|
||||
*regs = &allregs[0];
|
||||
return allregs.size();
|
||||
@@ -134,7 +134,7 @@ public:
|
||||
{
|
||||
for (u32 i = 0; i < Sh4RegList.size(); i++)
|
||||
if (Sh4RegList[i] != NoReg)
|
||||
*GetRegPtr(Sh4RegList[i]) = regs[i];
|
||||
*GetRegPtr(Sh4cntx, Sh4RegList[i]) = regs[i];
|
||||
}
|
||||
|
||||
u32 readReg(u32 regNum)
|
||||
@@ -143,9 +143,9 @@ public:
|
||||
return 0;
|
||||
Sh4RegType reg = Sh4RegList[regNum];
|
||||
if (reg == reg_sr_status)
|
||||
return sh4_sr_GetFull();
|
||||
return Sh4cntx.sr.getFull();
|
||||
if (reg != NoReg)
|
||||
return *GetRegPtr(reg);
|
||||
return *GetRegPtr(Sh4cntx, reg);
|
||||
return 0;
|
||||
}
|
||||
void writeReg(u32 regNum, u32 value)
|
||||
@@ -154,9 +154,9 @@ public:
|
||||
return;
|
||||
Sh4RegType reg = Sh4RegList[regNum];
|
||||
if (reg == reg_sr_status)
|
||||
sh4_sr_SetFull(value);
|
||||
Sh4cntx.sr.setFull(value);
|
||||
else if (reg != NoReg)
|
||||
*GetRegPtr(reg) = value;
|
||||
*GetRegPtr(Sh4cntx, reg) = value;
|
||||
}
|
||||
|
||||
const u8 *readMem(u32 addr, u32 len)
|
||||
|
||||
+403
-390
File diff suppressed because it is too large.
Load diff
Submodule
+1
Submodule core/deps/DreamPicoPort-API added at 0f6a902bc8.
+1
-1
Submodule core/deps/SDL updated: 92fe3b19c8...00d4f1c2c4.
+1
-1
Submodule core/deps/Syphon updated: 486dd8e4ef...53ec412a78.
Submodule
+1
Submodule core/deps/asio added at d3402006e8.
+26
-14
@@ -38,7 +38,7 @@ unsigned long track_mode;
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void CDI_read_track (FILE *fsource, image_s *image, track_s *track)
|
||||
bool CDI_read_track (FILE *fsource, image_s *image, track_s *track)
|
||||
{
|
||||
|
||||
unsigned char TRACK_START_MARK[10] = { 0, 0, 0x01, 0, 0, 0, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
@@ -49,10 +49,16 @@ void CDI_read_track (FILE *fsource, image_s *image, track_s *track)
|
||||
fseek(fsource, 8, SEEK_CUR); // extra data (DJ 3.00.780 and up)
|
||||
|
||||
fread(¤t_start_mark, 10, 1, fsource);
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) printf( "Unsupported format: Could not find the track start mark");
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) {
|
||||
printf("CDI_read_track: Unsupported format: Could not find the track start mark\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fread(¤t_start_mark, 10, 1, fsource);
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) printf( "Unsupported format: Could not find the track start mark");
|
||||
if (memcmp(TRACK_START_MARK, current_start_mark, 10)) {
|
||||
printf("CDI_read_track: Unsupported format: Could not find the track start mark\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(fsource, 4, SEEK_CUR);
|
||||
fread(&track->filename_length, 1, 1, fsource);
|
||||
@@ -80,20 +86,25 @@ void CDI_read_track (FILE *fsource, image_s *image, track_s *track)
|
||||
case 1 : track->sector_size = 2336; break;
|
||||
case 2 : track->sector_size = 2352; break;
|
||||
case 4 : track->sector_size = 2448; break;
|
||||
default: printf("Unsupported sector size. value %ld\n", track->sector_size_value);
|
||||
break;
|
||||
default:
|
||||
printf("CDI_read_track: Unsupported sector size. value %ld\n", track->sector_size_value);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (track->mode > 2) printf( "Unsupported format: Track mode not supported");
|
||||
if (track->mode > 2) {
|
||||
printf("CDI_read_track: Unsupported format: Track mode not supported\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(fsource, 29, SEEK_CUR);
|
||||
if (image->version != CDI_V2)
|
||||
{
|
||||
{
|
||||
fseek(fsource, 5, SEEK_CUR);
|
||||
fread(&temp_value, 4, 1, fsource);
|
||||
if (temp_value == 0xffffffff)
|
||||
fseek(fsource, 78, SEEK_CUR); // extra data (DJ 3.00.780 and up)
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -104,9 +115,9 @@ void CDI_skip_next_session (FILE *fsource, image_s *image)
|
||||
if (image->version != CDI_V2) fseek(fsource, 1, SEEK_CUR);
|
||||
}
|
||||
|
||||
void CDI_get_tracks (FILE *fsource, image_s *image)
|
||||
bool CDI_get_tracks (FILE *fsource, image_s *image)
|
||||
{
|
||||
fread(&image->tracks, 2, 1, fsource);
|
||||
return fread(&image->tracks, 2, 1, fsource) == 1;
|
||||
}
|
||||
|
||||
bool CDI_init (FILE *fsource, image_s *image, const char *fsourcename)
|
||||
@@ -121,8 +132,9 @@ bool CDI_init (FILE *fsource, image_s *image, const char *fsourcename)
|
||||
}
|
||||
|
||||
fseek(fsource, image->length-8, SEEK_SET);
|
||||
fread(&image->version, 4, 1, fsource);
|
||||
fread(&image->header_offset, 4, 1, fsource);
|
||||
if (fread(&image->version, 4, 1, fsource) != 1
|
||||
|| fread(&image->header_offset, 4, 1, fsource) != 1)
|
||||
return false;
|
||||
|
||||
if ((image->version != CDI_V2 && image->version != CDI_V3 && image->version != CDI_V35)
|
||||
|| image->header_offset == 0)
|
||||
@@ -133,7 +145,7 @@ bool CDI_init (FILE *fsource, image_s *image, const char *fsourcename)
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDI_get_sessions (FILE *fsource, image_s *image)
|
||||
bool CDI_get_sessions (FILE *fsource, image_s *image)
|
||||
{
|
||||
#ifndef DEBUG_CDI
|
||||
if (image->version == CDI_V35)
|
||||
@@ -144,6 +156,6 @@ void CDI_get_sessions (FILE *fsource, image_s *image)
|
||||
#else
|
||||
fseek(fsource, 0L, SEEK_SET);
|
||||
#endif
|
||||
fread(&image->sessions, 2, 1, fsource);
|
||||
return fread(&image->sessions, 2, 1, fsource) == 1;
|
||||
}
|
||||
|
||||
@@ -38,9 +38,9 @@ typedef struct track_s
|
||||
|
||||
unsigned long ask_type(FILE *fsource, long header_position);
|
||||
bool CDI_init(FILE *fsource, image_s *image, const char *fsourcename);
|
||||
void CDI_get_sessions(FILE *fsource, image_s *image);
|
||||
void CDI_get_tracks(FILE *fsource, image_s *image);
|
||||
void CDI_read_track(FILE *fsource, image_s *image, track_s *track);
|
||||
bool CDI_get_sessions(FILE *fsource, image_s *image);
|
||||
bool CDI_get_tracks(FILE *fsource, image_s *image);
|
||||
bool CDI_read_track(FILE *fsource, image_s *image, track_s *track);
|
||||
void CDI_skip_next_session(FILE *fsource, image_s *image);
|
||||
|
||||
#endif
|
||||
+1
-1
Submodule core/deps/discord-rpc updated: 6cae2bd07a...c62c34d37c.
@@ -13,7 +13,7 @@
|
||||
template<class T, int N> class RingBuffer
|
||||
{
|
||||
public:
|
||||
RingBuffer<T, N>() :
|
||||
RingBuffer() :
|
||||
_head(0),
|
||||
_tail(0),
|
||||
_size(0) {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
template<class T, int N> class StaticBuffer
|
||||
{
|
||||
public:
|
||||
StaticBuffer<T, N>() :
|
||||
StaticBuffer() :
|
||||
_size(0) {
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.2 FATAL_ERROR)
|
||||
cmake_policy(VERSION 3.2)
|
||||
cmake_minimum_required(VERSION 3.6 FATAL_ERROR)
|
||||
cmake_policy(VERSION 3.6)
|
||||
|
||||
set(GLM_VERSION "0.9.9")
|
||||
project(glm VERSION ${GLM_VERSION} LANGUAGES CXX)
|
||||
|
||||
+1
-1
Submodule core/deps/glslang updated: 76b52ebf77...fc9889c889.
+1
-1
Submodule core/deps/googletest updated: f8d7d77c06...e397860881.
@@ -222,7 +222,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
glcache.Scissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
|
||||
|
||||
// Bind texture, Draw
|
||||
glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
|
||||
glcache.BindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID());
|
||||
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
|
||||
}
|
||||
}
|
||||
@@ -255,7 +255,7 @@ static bool ImGui_ImplOpenGL3_CreateFontsTexture()
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
|
||||
io.Fonts->SetTexID((ImTextureID)g_FontTexture);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -266,7 +266,6 @@ static void ImGui_ImplOpenGL3_DestroyFontsTexture()
|
||||
{
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
glcache.DeleteTextures(1, &g_FontTexture);
|
||||
io.Fonts->TexID = 0;
|
||||
g_FontTexture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -586,14 +586,14 @@ void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer comm
|
||||
vkCmdSetScissor(command_buffer, 0, 1, &scissor);
|
||||
|
||||
// Bind DescriptorSet with font or user texture
|
||||
VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId };
|
||||
VkDescriptorSet desc_set = (VkDescriptorSet)pcmd->GetTexID();
|
||||
if (sizeof(ImTextureID) < sizeof(ImU64))
|
||||
{
|
||||
// We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used.
|
||||
IM_ASSERT(pcmd->TextureId == (ImTextureID)bd->FontDescriptorSet);
|
||||
desc_set[0] = bd->FontDescriptorSet;
|
||||
IM_ASSERT(pcmd->GetTexID() == (ImTextureID)bd->FontDescriptorSet);
|
||||
desc_set = bd->FontDescriptorSet;
|
||||
}
|
||||
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, nullptr);
|
||||
vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, &desc_set, 0, nullptr);
|
||||
|
||||
// Draw
|
||||
vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0);
|
||||
@@ -820,7 +820,6 @@ void ImGui_ImplVulkan_DestroyFontsTexture()
|
||||
{
|
||||
ImGui_ImplVulkan_RemoveTexture(bd->FontDescriptorSet);
|
||||
bd->FontDescriptorSet = VK_NULL_HANDLE;
|
||||
io.Fonts->SetTexID(0);
|
||||
}
|
||||
|
||||
if (bd->FontView) { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; }
|
||||
|
||||
@@ -21,14 +21,14 @@
|
||||
|
||||
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
|
||||
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec( dllexport )
|
||||
//#define IMGUI_API __declspec( dllimport )
|
||||
// - Windows DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||
//#define IMGUI_API __declspec(dllexport) // MSVC Windows: DLL export
|
||||
//#define IMGUI_API __declspec(dllimport) // MSVC Windows: DLL import
|
||||
//#define IMGUI_API __attribute__((visibility("default"))) // GCC/Clang: override visibility when set is hidden
|
||||
|
||||
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
||||
|
||||
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||
@@ -42,21 +42,29 @@
|
||||
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||
//#define IMGUI_DISABLE_DEFAULT_SHELL_FUNCTIONS // Don't implement default platform_io.Platform_OpenInShellFn() handler (Win32: ShellExecute(), require shell32.lib/.a, Mac/Linux: use system("")).
|
||||
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||
//#define IMGUI_DISABLE_DEFAULT_FONT // Disable default embedded font (ProggyClean.ttf), remove ~9.5 KB from output binary. AddFontDefault() will assert.
|
||||
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||
|
||||
//---- Enable Test Engine / Automation features.
|
||||
//#define IMGUI_ENABLE_TEST_ENGINE // Enable imgui_test_engine hooks. Generally set automatically by include "imgui_te_config.h", see Test Engine for details.
|
||||
|
||||
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||
|
||||
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||
//---- Pack vertex colors as BGRA8 instead of RGBA8 (to avoid converting from one to another). Need dedicated backend support.
|
||||
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||
|
||||
//---- Use legacy CRC32-adler tables (used before 1.91.6), in order to preserve old .ini data that you cannot afford to invalidate.
|
||||
//#define IMGUI_USE_LEGACY_CRC32_ADLER
|
||||
|
||||
//---- Use 32-bit for ImWchar (default is 16-bit) to support Unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||
//#define IMGUI_USE_WCHAR32
|
||||
|
||||
@@ -78,10 +86,12 @@
|
||||
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||
//#define IMGUI_ENABLE_FREETYPE
|
||||
|
||||
//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
|
||||
// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
|
||||
//---- Use FreeType + plutosvg or lunasvg to render OpenType SVG fonts (SVGinOT)
|
||||
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||
// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
// - plutosvg is currently easier to install, as e.g. it is part of vcpkg. It will support more fonts and may load them faster. See misc/freetype/README for instructions.
|
||||
// - Both require headers to be available in the include path + program to be linked with the library code (not provided).
|
||||
// - (note: lunasvg implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||
//#define IMGUI_ENABLE_FREETYPE_PLUTOSVG
|
||||
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||
|
||||
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||
@@ -119,6 +129,10 @@
|
||||
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||
//#define IM_DEBUG_BREAK __debugbreak()
|
||||
|
||||
//---- Debug Tools: Enable highlight ID conflicts _before_ hovering items. When io.ConfigDebugHighlightIdConflicts is set.
|
||||
// (THIS WILL SLOW DOWN DEAR IMGUI. Only use occasionally and disable after use)
|
||||
//#define IMGUI_DEBUG_HIGHLIGHT_ALL_ID_CONFLICTS
|
||||
|
||||
//---- Debug Tools: Enable slower asserts
|
||||
//#define IMGUI_DEBUG_PARANOID
|
||||
|
||||
|
||||
+3374
-1632
File diff suppressed because it is too large.
Load diff
+1202
-466
File diff suppressed because it is too large.
Load diff
+3855
-1783
File diff suppressed because it is too large.
Load diff
+2736
-1051
File diff suppressed because it is too large.
Load diff
+816
-519
File diff suppressed because it is too large.
Load diff
@@ -8,6 +8,7 @@
|
||||
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_stdlib.h"
|
||||
|
||||
// Clang warnings with -Weverything
|
||||
@@ -83,3 +84,5 @@ bool ImGui::InputTextWithHint(const char* label, const char* hint, std::string*
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
@@ -9,6 +9,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef IMGUI_DISABLE
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace ImGui
|
||||
@@ -19,3 +21,5 @@ namespace ImGui
|
||||
IMGUI_API bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, std::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = nullptr, void* user_data = nullptr);
|
||||
}
|
||||
|
||||
#endif // #ifndef IMGUI_DISABLE
|
||||
+212
-101
@@ -1,4 +1,4 @@
|
||||
// dear imgui, v1.90.8
|
||||
// dear imgui, v1.92.1
|
||||
// (tables and columns code)
|
||||
|
||||
/*
|
||||
@@ -221,6 +221,7 @@ Index of this file:
|
||||
#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx'
|
||||
#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse.
|
||||
#pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok.
|
||||
#pragma clang diagnostic ignored "-Wformat" // warning: format specifies type 'int' but the argument has type 'unsigned int'
|
||||
#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning: format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code.
|
||||
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||
#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning: zero as null pointer constant // some standard header variations use #define NULL 0
|
||||
@@ -229,9 +230,15 @@ Index of this file:
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-enum-enum-conversion"// warning: bitwise operation between different enumeration types ('XXXFlags_' and 'XXXFlagsPrivate_') is deprecated
|
||||
#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
|
||||
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage" // warning: 'xxx' is an unsafe pointer used for buffer access
|
||||
#pragma clang diagnostic ignored "-Wnontrivial-memaccess" // warning: first argument in call to 'memset' is a pointer to non-trivially copyable type
|
||||
#pragma clang diagnostic ignored "-Wswitch-default" // warning: 'switch' missing 'default' label
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||
#pragma GCC diagnostic ignored "-Wfloat-equal" // warning: comparing floating-point with '==' or '!=' is unsafe
|
||||
#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked
|
||||
#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
|
||||
#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'int'/'void*', but argument X has type 'unsigned int'/'ImGuiWindow*'
|
||||
#pragma GCC diagnostic ignored "-Wstrict-overflow"
|
||||
#pragma GCC diagnostic ignored "-Wclass-memaccess" // [__GNUC__ >= 8] warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead
|
||||
#endif
|
||||
|
||||
@@ -328,13 +335,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
// - always performing the GetOrAddByKey() O(log N) query in g.Tables.Map[].
|
||||
const bool use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0;
|
||||
const ImVec2 avail_size = GetContentRegionAvail();
|
||||
const ImVec2 actual_outer_size = CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f);
|
||||
const ImVec2 actual_outer_size = ImTrunc(CalcItemSize(outer_size, ImMax(avail_size.x, 1.0f), use_child_window ? ImMax(avail_size.y, 1.0f) : 0.0f));
|
||||
const ImRect outer_rect(outer_window->DC.CursorPos, outer_window->DC.CursorPos + actual_outer_size);
|
||||
const bool outer_window_is_measuring_size = (outer_window->AutoFitFramesX > 0) || (outer_window->AutoFitFramesY > 0); // Doesn't apply to AlwaysAutoResize windows!
|
||||
if (use_child_window && IsClippedEx(outer_rect, 0) && !outer_window_is_measuring_size)
|
||||
{
|
||||
ItemSize(outer_rect);
|
||||
ItemAdd(outer_rect, id);
|
||||
g.NextWindowData.ClearFlags();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -369,6 +377,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->ColumnsCount = columns_count;
|
||||
table->IsLayoutLocked = false;
|
||||
table->InnerWidth = inner_width;
|
||||
table->NavLayer = (ImS8)outer_window->DC.NavLayerCurrent;
|
||||
temp_data->UserOuterSize = outer_size;
|
||||
|
||||
// Instance data (for instance 0, TableID == TableInstanceID)
|
||||
@@ -409,11 +418,15 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
|
||||
// Reset scroll if we are reactivating it
|
||||
if ((previous_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0)
|
||||
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
|
||||
if ((g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasScroll) == 0)
|
||||
SetNextWindowScroll(ImVec2(0.0f, 0.0f));
|
||||
|
||||
// Create scrolling region (without border and zero window padding)
|
||||
ImGuiWindowFlags child_flags = (flags & ImGuiTableFlags_ScrollX) ? ImGuiWindowFlags_HorizontalScrollbar : ImGuiWindowFlags_None;
|
||||
BeginChildEx(name, instance_id, outer_rect.GetSize(), false, child_flags);
|
||||
ImGuiChildFlags child_child_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasChildFlags) ? g.NextWindowData.ChildFlags : ImGuiChildFlags_None;
|
||||
ImGuiWindowFlags child_window_flags = (g.NextWindowData.HasFlags & ImGuiNextWindowDataFlags_HasWindowFlags) ? g.NextWindowData.WindowFlags : ImGuiWindowFlags_None;
|
||||
if (flags & ImGuiTableFlags_ScrollX)
|
||||
child_window_flags |= ImGuiWindowFlags_HorizontalScrollbar;
|
||||
BeginChildEx(name, instance_id, outer_rect.GetSize(), child_child_flags, child_window_flags);
|
||||
table->InnerWindow = g.CurrentWindow;
|
||||
table->WorkRect = table->InnerWindow->WorkRect;
|
||||
table->OuterRect = table->InnerWindow->Rect();
|
||||
@@ -438,6 +451,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
// But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
|
||||
table->WorkRect = table->OuterRect = table->InnerRect = outer_rect;
|
||||
table->HasScrollbarYPrev = table->HasScrollbarYCurr = false;
|
||||
table->InnerWindow->DC.TreeDepth++; // This is designed to always linking ImGuiTreeNodeFlags_DrawLines linking accross a table
|
||||
}
|
||||
|
||||
// Push a standardized ID for both child-using and not-child-using tables
|
||||
@@ -460,16 +474,27 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
temp_data->HostBackupItemWidthStackSize = outer_window->DC.ItemWidthStack.Size;
|
||||
inner_window->DC.PrevLineSize = inner_window->DC.CurrLineSize = ImVec2(0.0f, 0.0f);
|
||||
|
||||
// Make left and top borders not overlap our contents by offsetting HostClipRect (#6765)
|
||||
// Make borders not overlap our contents by offsetting HostClipRect (#6765, #7428, #3752)
|
||||
// (we normally shouldn't alter HostClipRect as we rely on TableMergeDrawChannels() expanding non-clipped column toward the
|
||||
// limits of that rectangle, in order for ImDrawListSplitter::Merge() to merge the draw commands. However since the overlap
|
||||
// problem only affect scrolling tables in this case we can get away with doing it without extra cost).
|
||||
if (inner_window != outer_window)
|
||||
{
|
||||
// FIXME: Because inner_window's Scrollbar doesn't know about border size, since it's not encoded in window->WindowBorderSize,
|
||||
// it already overlaps it and doesn't need an extra offset. Ideally we should be able to pass custom border size with
|
||||
// different x/y values to BeginChild().
|
||||
if (flags & ImGuiTableFlags_BordersOuterV)
|
||||
{
|
||||
table->HostClipRect.Min.x = ImMin(table->HostClipRect.Min.x + TABLE_BORDER_SIZE, table->HostClipRect.Max.x);
|
||||
if (inner_window->DecoOuterSizeX2 == 0.0f)
|
||||
table->HostClipRect.Max.x = ImMax(table->HostClipRect.Max.x - TABLE_BORDER_SIZE, table->HostClipRect.Min.x);
|
||||
}
|
||||
if (flags & ImGuiTableFlags_BordersOuterH)
|
||||
{
|
||||
table->HostClipRect.Min.y = ImMin(table->HostClipRect.Min.y + TABLE_BORDER_SIZE, table->HostClipRect.Max.y);
|
||||
if (inner_window->DecoOuterSizeY2 == 0.0f)
|
||||
table->HostClipRect.Max.y = ImMax(table->HostClipRect.Max.y - TABLE_BORDER_SIZE, table->HostClipRect.Min.y);
|
||||
}
|
||||
}
|
||||
|
||||
// Padding and Spacing
|
||||
@@ -497,7 +522,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
table->InnerClipRect = (inner_window == outer_window) ? table->WorkRect : inner_window->ClipRect;
|
||||
table->InnerClipRect.ClipWith(table->WorkRect); // We need this to honor inner_width
|
||||
table->InnerClipRect.ClipWithFull(table->HostClipRect);
|
||||
table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : inner_window->ClipRect.Max.y;
|
||||
table->InnerClipRect.Max.y = (flags & ImGuiTableFlags_NoHostExtendY) ? ImMin(table->InnerClipRect.Max.y, inner_window->WorkRect.Max.y) : table->HostClipRect.Max.y;
|
||||
|
||||
table->RowPosY1 = table->RowPosY2 = table->WorkRect.Min.y; // This is needed somehow
|
||||
table->RowTextBaseline = 0.0f; // This will be cleared again by TableBeginRow()
|
||||
@@ -555,6 +580,7 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG
|
||||
// Initialize
|
||||
table->SettingsOffset = -1;
|
||||
table->IsSortSpecsDirty = true;
|
||||
table->IsSettingsDirty = true; // Records itself into .ini file even when in default state (#7934)
|
||||
table->InstanceInteracted = -1;
|
||||
table->ContextPopupColumn = -1;
|
||||
table->ReorderColumn = table->ResizedColumn = table->LastResizedColumn = -1;
|
||||
@@ -855,7 +881,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
|
||||
// Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)
|
||||
// Combine width from regular rows + width from headers unless requested not to.
|
||||
if (!column->IsPreserveWidthAuto)
|
||||
if (!column->IsPreserveWidthAuto && table->InstanceCurrent == 0)
|
||||
column->WidthAuto = TableGetColumnWidthAuto(table, column);
|
||||
|
||||
// Non-resizable columns keep their requested width (apply user value regardless of IsPreserveWidthAuto)
|
||||
@@ -954,7 +980,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
// [Part 4] Apply final widths based on requested widths
|
||||
const ImRect work_rect = table->WorkRect;
|
||||
const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1);
|
||||
const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synched tables with mismatching scrollbar state (#5920)
|
||||
const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synced tables with mismatching scrollbar state (#5920)
|
||||
const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed);
|
||||
const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests;
|
||||
float width_remaining_for_stretched_columns = width_avail_for_stretched_columns;
|
||||
@@ -1033,7 +1059,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
const int column_n = table->DisplayOrderToIndex[order_n];
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
|
||||
column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen
|
||||
// Initial nav layer: using FreezeRowsCount, NOT FreezeRowsRequest, so Header line changes layer when frozen
|
||||
column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : (ImGuiNavLayer)table->NavLayer);
|
||||
|
||||
if (offset_x_frozen && table->FreezeColumnsCount == visible_n)
|
||||
{
|
||||
@@ -1059,16 +1086,12 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Detect hovered column
|
||||
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
|
||||
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
|
||||
|
||||
// Lock start position
|
||||
column->MinX = offset_x;
|
||||
|
||||
// Lock width based on start position and minimum/maximum width for this position
|
||||
float max_width = TableGetMaxColumnWidth(table, column_n);
|
||||
column->WidthGiven = ImMin(column->WidthGiven, max_width);
|
||||
column->WidthMax = TableCalcMaxColumnWidth(table, column_n);
|
||||
column->WidthGiven = ImMin(column->WidthGiven, column->WidthMax);
|
||||
column->WidthGiven = ImMax(column->WidthGiven, ImMin(column->WidthRequest, table->MinColumnWidth));
|
||||
column->MaxX = offset_x + column->WidthGiven + table->CellSpacingX1 + table->CellSpacingX2 + table->CellPaddingX * 2.0f;
|
||||
|
||||
@@ -1117,8 +1140,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
column->Flags |= ImGuiTableColumnFlags_IsVisible;
|
||||
if (column->SortOrder != -1)
|
||||
column->Flags |= ImGuiTableColumnFlags_IsSorted;
|
||||
if (table->HoveredColumnBody == column_n)
|
||||
|
||||
// Detect hovered column
|
||||
if (is_hovering_table && mouse_skewed_x >= column->ClipRect.Min.x && mouse_skewed_x < column->ClipRect.Max.x)
|
||||
{
|
||||
column->Flags |= ImGuiTableColumnFlags_IsHovered;
|
||||
table->HoveredColumnBody = (ImGuiTableColumnIdx)column_n;
|
||||
}
|
||||
|
||||
// Alignment
|
||||
// FIXME-TABLE: This align based on the whole column width, not per-cell, and therefore isn't useful in
|
||||
@@ -1148,7 +1176,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
}
|
||||
|
||||
// Don't decrement auto-fit counters until container window got a chance to submit its items
|
||||
if (table->HostSkipItems == false)
|
||||
if (table->HostSkipItems == false && table->InstanceCurrent == 0)
|
||||
{
|
||||
column->AutoFitQueue >>= 1;
|
||||
column->CannotSkipItemsQueue >>= 1;
|
||||
@@ -1223,7 +1251,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
|
||||
// [Part 11] Default context menu
|
||||
// - To append to this menu: you can call TableBeginContextMenuPopup()/.../EndPopup().
|
||||
// - To modify or replace this: set table->IsContextPopupNoDefaultContents = true, then call TableBeginContextMenuPopup()/.../EndPopup().
|
||||
// - To modify or replace this: set table->DisableDefaultContextMenu = true, then call TableBeginContextMenuPopup()/.../EndPopup().
|
||||
// - You may call TableDrawDefaultContextMenu() with selected flags to display specific sections of the default menu,
|
||||
// e.g. TableDrawDefaultContextMenu(table, table->Flags & ~ImGuiTableFlags_Hideable) will display everything EXCEPT columns visibility options.
|
||||
if (table->DisableDefaultContextMenu == false && TableBeginContextMenuPopup(table))
|
||||
@@ -1249,7 +1277,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table)
|
||||
if (table->Flags & ImGuiTableFlags_NoClip)
|
||||
table->DrawSplitter->SetCurrentChannel(inner_window->DrawList, TABLE_DRAW_CHANNEL_NOCLIP);
|
||||
else
|
||||
inner_window->DrawList->PushClipRect(inner_window->ClipRect.Min, inner_window->ClipRect.Max, false);
|
||||
inner_window->DrawList->PushClipRect(inner_window->InnerClipRect.Min, inner_window->InnerClipRect.Max, false); // FIXME: use table->InnerClipRect?
|
||||
}
|
||||
|
||||
// Process hit-testing on resizing borders. Actual size change will be applied in EndTable()
|
||||
@@ -1319,7 +1347,11 @@ void ImGui::EndTable()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL && "Only call EndTable() if BeginTable() returns true!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "EndTable() call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
|
||||
// This assert would be very useful to catch a common error... unfortunately it would probably trigger in some
|
||||
// cases, and for consistency user may sometimes output empty tables (and still benefit from e.g. outer border)
|
||||
@@ -1361,7 +1393,7 @@ void ImGui::EndTable()
|
||||
|
||||
// Setup inner scrolling range
|
||||
// FIXME: This ideally should be done earlier, in BeginTable() SetNextWindowContentSize call, just like writing to inner_window->DC.CursorMaxPos.y,
|
||||
// but since the later is likely to be impossible to do we'd rather update both axises together.
|
||||
// but since the later is likely to be impossible to do we'd rather update both axes together.
|
||||
if (table->Flags & ImGuiTableFlags_ScrollX)
|
||||
{
|
||||
const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f;
|
||||
@@ -1471,12 +1503,15 @@ void ImGui::EndTable()
|
||||
if (inner_window != outer_window)
|
||||
{
|
||||
short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask;
|
||||
inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently.
|
||||
inner_window->DC.NavLayersActiveMask |= 1 << table->NavLayer; // So empty table don't appear to navigate differently.
|
||||
g.CurrentTable = NULL; // To avoid error recovery recursing
|
||||
EndChild();
|
||||
g.CurrentTable = table;
|
||||
inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
table->InnerWindow->DC.TreeDepth--;
|
||||
ItemSize(table->OuterRect.GetSize());
|
||||
ItemAdd(table->OuterRect, 0);
|
||||
}
|
||||
@@ -1534,14 +1569,43 @@ void ImGui::EndTable()
|
||||
NavUpdateCurrentWindowIsScrollPushableX();
|
||||
}
|
||||
|
||||
// Called in TableSetupColumn() when initializing and in TableLoadSettings() for defaults before applying stored settings.
|
||||
// 'init_mask' specify which fields to initialize.
|
||||
static void TableInitColumnDefaults(ImGuiTable* table, ImGuiTableColumn* column, ImGuiTableColumnFlags init_mask)
|
||||
{
|
||||
ImGuiTableColumnFlags flags = column->Flags;
|
||||
if (init_mask & ImGuiTableFlags_Resizable)
|
||||
{
|
||||
float init_width_or_weight = column->InitStretchWeightOrWidth;
|
||||
column->WidthRequest = ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;
|
||||
column->StretchWeight = (init_width_or_weight > 0.0f && (flags & ImGuiTableColumnFlags_WidthStretch)) ? init_width_or_weight : -1.0f;
|
||||
if (init_width_or_weight > 0.0f) // Disable auto-fit if an explicit width/weight has been specified
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
if (init_mask & ImGuiTableFlags_Reorderable)
|
||||
column->DisplayOrder = (ImGuiTableColumnIdx)table->Columns.index_from_ptr(column);
|
||||
if (init_mask & ImGuiTableFlags_Hideable)
|
||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = (flags & ImGuiTableColumnFlags_DefaultHide) ? 0 : 1;
|
||||
if (init_mask & ImGuiTableFlags_Sortable)
|
||||
{
|
||||
// Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
|
||||
column->SortOrder = (flags & ImGuiTableColumnFlags_DefaultSort) ? 0 : -1;
|
||||
column->SortDirection = (flags & ImGuiTableColumnFlags_DefaultSort) ? ((flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending)) : (ImS8)ImGuiSortDirection_None;
|
||||
}
|
||||
}
|
||||
|
||||
// See "COLUMNS SIZING POLICIES" comments at the top of this file
|
||||
// If (init_width_or_weight <= 0.0f) it is ignored
|
||||
void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, float init_width_or_weight, ImGuiID user_id)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!");
|
||||
IM_ASSERT(table->IsLayoutLocked == false && "Need to call call TableSetupColumn() before first row!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
|
||||
IM_ASSERT((flags & ImGuiTableColumnFlags_StatusMask_) == 0 && "Illegal to pass StatusMask values to TableSetupColumn()");
|
||||
if (table->DeclColumnsCount >= table->ColumnsCount)
|
||||
{
|
||||
@@ -1558,7 +1622,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
|
||||
IM_ASSERT(init_width_or_weight <= 0.0f && "Can only specify width/weight if sizing policy is set explicitly in either Table or Column.");
|
||||
|
||||
// When passing a width automatically enforce WidthFixed policy
|
||||
// (whereas TableSetupColumnFlags would default to WidthAuto if table is not Resizable)
|
||||
// (whereas TableSetupColumnFlags would default to WidthAuto if table is not resizable)
|
||||
if ((flags & ImGuiTableColumnFlags_WidthMask_) == 0 && init_width_or_weight > 0.0f)
|
||||
if ((table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedFit || (table->Flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame)
|
||||
flags |= ImGuiTableColumnFlags_WidthFixed;
|
||||
@@ -1576,27 +1640,10 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
|
||||
column->InitStretchWeightOrWidth = init_width_or_weight;
|
||||
if (table->IsInitializing)
|
||||
{
|
||||
// Init width or weight
|
||||
ImGuiTableFlags init_flags = ~table->SettingsLoadedFlags;
|
||||
if (column->WidthRequest < 0.0f && column->StretchWeight < 0.0f)
|
||||
{
|
||||
if ((flags & ImGuiTableColumnFlags_WidthFixed) && init_width_or_weight > 0.0f)
|
||||
column->WidthRequest = init_width_or_weight;
|
||||
if (flags & ImGuiTableColumnFlags_WidthStretch)
|
||||
column->StretchWeight = (init_width_or_weight > 0.0f) ? init_width_or_weight : -1.0f;
|
||||
|
||||
// Disable auto-fit if an explicit width/weight has been specified
|
||||
if (init_width_or_weight > 0.0f)
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
|
||||
// Init default visibility/sort state
|
||||
if ((flags & ImGuiTableColumnFlags_DefaultHide) && (table->SettingsLoadedFlags & ImGuiTableFlags_Hideable) == 0)
|
||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = false;
|
||||
if (flags & ImGuiTableColumnFlags_DefaultSort && (table->SettingsLoadedFlags & ImGuiTableFlags_Sortable) == 0)
|
||||
{
|
||||
column->SortOrder = 0; // Multiple columns using _DefaultSort will be reassigned unique SortOrder values when building the sort specs.
|
||||
column->SortDirection = (column->Flags & ImGuiTableColumnFlags_PreferSortDescending) ? (ImS8)ImGuiSortDirection_Descending : (ImU8)(ImGuiSortDirection_Ascending);
|
||||
}
|
||||
init_flags |= ImGuiTableFlags_Resizable;
|
||||
TableInitColumnDefaults(table, column, init_flags);
|
||||
}
|
||||
|
||||
// Store name (append with zero-terminator in contiguous buffer)
|
||||
@@ -1605,7 +1652,7 @@ void ImGui::TableSetupColumn(const char* label, ImGuiTableColumnFlags flags, flo
|
||||
if (label != NULL && label[0] != 0)
|
||||
{
|
||||
column->NameOffset = (ImS16)table->ColumnsNames.size();
|
||||
table->ColumnsNames.append(label, label + strlen(label) + 1);
|
||||
table->ColumnsNames.append(label, label + ImStrlen(label) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1614,7 +1661,11 @@ void ImGui::TableSetupScrollFreeze(int columns, int rows)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL && "Need to call TableSetupColumn() after BeginTable()!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(table->IsLayoutLocked == false && "Need to call TableSetupColumn() before first row!");
|
||||
IM_ASSERT(columns >= 0 && columns < IMGUI_TABLE_MAX_COLUMNS);
|
||||
IM_ASSERT(rows >= 0 && rows < 128); // Arbitrary limit
|
||||
@@ -1691,9 +1742,11 @@ void ImGui::TableSetColumnEnabled(int column_n, bool enabled)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL);
|
||||
if (!table)
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(table->Flags & ImGuiTableFlags_Hideable); // See comments above
|
||||
if (column_n < 0)
|
||||
column_n = table->CurrentColumn;
|
||||
@@ -1900,7 +1953,10 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
IM_ASSERT(table->IsInsideRow);
|
||||
|
||||
if (table->CurrentColumn != -1)
|
||||
{
|
||||
TableEndCell(table);
|
||||
table->CurrentColumn = -1;
|
||||
}
|
||||
|
||||
// Logging
|
||||
if (g.LogEnabled)
|
||||
@@ -1996,34 +2052,37 @@ void ImGui::TableEndRow(ImGuiTable* table)
|
||||
// We need to do that in TableEndRow() instead of TableBeginRow() so the list clipper can mark end of row and
|
||||
// get the new cursor position.
|
||||
if (unfreeze_rows_request)
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main;
|
||||
if (unfreeze_rows_actual)
|
||||
{
|
||||
IM_ASSERT(table->IsUnfrozenRows == false);
|
||||
const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y);
|
||||
table->IsUnfrozenRows = true;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
table->Columns[column_n].NavLayerCurrent = table->NavLayer;
|
||||
const float y0 = ImMax(table->RowPosY2 + 1, table->InnerClipRect.Min.y);
|
||||
table_instance->LastFrozenHeight = y0 - table->OuterRect.Min.y;
|
||||
|
||||
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
||||
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y);
|
||||
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y;
|
||||
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
|
||||
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
|
||||
|
||||
float row_height = table->RowPosY2 - table->RowPosY1;
|
||||
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
|
||||
table->RowPosY1 = table->RowPosY2 - row_height;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
if (unfreeze_rows_actual)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
|
||||
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
|
||||
}
|
||||
IM_ASSERT(table->IsUnfrozenRows == false);
|
||||
table->IsUnfrozenRows = true;
|
||||
|
||||
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
|
||||
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
|
||||
// BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect
|
||||
table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, table->InnerClipRect.Max.y);
|
||||
table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = table->InnerClipRect.Max.y;
|
||||
table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen;
|
||||
IM_ASSERT(table->Bg2ClipRectForDrawCmd.Min.y <= table->Bg2ClipRectForDrawCmd.Max.y);
|
||||
|
||||
float row_height = table->RowPosY2 - table->RowPosY1;
|
||||
table->RowPosY2 = window->DC.CursorPos.y = table->WorkRect.Min.y + table->RowPosY2 - table->OuterRect.Min.y;
|
||||
table->RowPosY1 = table->RowPosY2 - row_height;
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
column->DrawChannelCurrent = column->DrawChannelUnfrozen;
|
||||
column->ClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y;
|
||||
}
|
||||
|
||||
// Update cliprect ahead of TableBeginCell() so clipper can access to new ClipRect->Min.y
|
||||
SetWindowClipRectBeforeSetChannel(window, table->Columns[0].ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[0].DrawChannelCurrent);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(table->RowFlags & ImGuiTableRowFlags_Headers))
|
||||
@@ -2062,7 +2121,11 @@ bool ImGui::TableSetColumnIndex(int column_n)
|
||||
{
|
||||
if (table->CurrentColumn != -1)
|
||||
TableEndCell(table);
|
||||
IM_ASSERT(column_n >= 0 && table->ColumnsCount);
|
||||
if ((column_n >= 0 && column_n < table->ColumnsCount) == false)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(column_n >= 0 && column_n < table->ColumnsCount, "TableSetColumnIndex() invalid column index!");
|
||||
return false;
|
||||
}
|
||||
TableBeginCell(table, column_n);
|
||||
}
|
||||
|
||||
@@ -2133,6 +2196,7 @@ void ImGui::TableBeginCell(ImGuiTable* table, int column_n)
|
||||
g.LastItemData.StatusFlags = 0;
|
||||
}
|
||||
|
||||
// Also see TablePushColumnChannel()
|
||||
if (table->Flags & ImGuiTableFlags_NoClip)
|
||||
{
|
||||
// FIXME: if we end up drawing all borders/bg in EndTable, could remove this and just assert that channel hasn't changed.
|
||||
@@ -2192,8 +2256,8 @@ void ImGui::TableEndCell(ImGuiTable* table)
|
||||
// Note that actual columns widths are computed in TableUpdateLayout().
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
// Maximum column content width given current layout. Use column->MinX so this value on a per-column basis.
|
||||
float ImGui::TableGetMaxColumnWidth(const ImGuiTable* table, int column_n)
|
||||
// Maximum column content width given current layout. Use column->MinX so this value differs on a per-column basis.
|
||||
float ImGui::TableCalcMaxColumnWidth(const ImGuiTable* table, int column_n)
|
||||
{
|
||||
const ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
float max_width = FLT_MAX;
|
||||
@@ -2255,7 +2319,7 @@ void ImGui::TableSetColumnWidth(int column_n, float width)
|
||||
// Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
|
||||
IM_ASSERT(table->MinColumnWidth > 0.0f);
|
||||
const float min_width = table->MinColumnWidth;
|
||||
const float max_width = ImMax(min_width, TableGetMaxColumnWidth(table, column_n));
|
||||
const float max_width = ImMax(min_width, column_0->WidthMax); // Don't use TableCalcMaxColumnWidth() here as it would rely on MinX from last instance (#7933)
|
||||
column_0_width = ImClamp(column_0_width, min_width, max_width);
|
||||
if (column_0->WidthGiven == column_0_width || column_0->WidthRequest == column_0_width)
|
||||
return;
|
||||
@@ -2406,10 +2470,38 @@ void ImGui::TablePopBackgroundChannel()
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
|
||||
|
||||
// Optimization: avoid PopClipRect() + SetCurrentChannel()
|
||||
SetWindowClipRectBeforeSetChannel(window, table->HostBackupInnerClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, table->Columns[table->CurrentColumn].DrawChannelCurrent);
|
||||
}
|
||||
|
||||
// Also see TableBeginCell()
|
||||
void ImGui::TablePushColumnChannel(int column_n)
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
|
||||
// Optimization: avoid SetCurrentChannel() + PushClipRect()
|
||||
if (table->Flags & ImGuiTableFlags_NoClip)
|
||||
return;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
const ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
||||
}
|
||||
|
||||
void ImGui::TablePopColumnChannel()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
|
||||
// Optimization: avoid PopClipRect() + SetCurrentChannel()
|
||||
if ((table->Flags & ImGuiTableFlags_NoClip) || (table->CurrentColumn == -1)) // Calling TreePop() after TableNextRow() is supported.
|
||||
return;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
const ImGuiTableColumn* column = &table->Columns[table->CurrentColumn];
|
||||
SetWindowClipRectBeforeSetChannel(window, column->ClipRect);
|
||||
table->DrawSplitter->SetCurrentChannel(window->DrawList, column->DrawChannelCurrent);
|
||||
}
|
||||
|
||||
@@ -2740,7 +2832,7 @@ void ImGui::TableDrawBorders(ImGuiTable* table)
|
||||
const ImU32 outer_col = table->BorderColorStrong;
|
||||
if ((table->Flags & ImGuiTableFlags_BordersOuter) == ImGuiTableFlags_BordersOuter)
|
||||
{
|
||||
inner_drawlist->AddRect(outer_border.Min, outer_border.Max + ImVec2(1, 1), outer_col, 0.0f, 0, border_size);
|
||||
inner_drawlist->AddRect(outer_border.Min, outer_border.Max, outer_col, 0.0f, 0, border_size);
|
||||
}
|
||||
else if (table->Flags & ImGuiTableFlags_BordersOuterV)
|
||||
{
|
||||
@@ -3004,15 +3096,21 @@ float ImGui::TableGetHeaderAngledMaxLabelWidth()
|
||||
// The intent is that advanced users willing to create customized headers would not need to use this helper
|
||||
// and can create their own! For example: TableHeader() may be preceded by Checkbox() or other custom widgets.
|
||||
// See 'Demo->Tables->Custom headers' for a demonstration of implementing a custom version of this.
|
||||
// This code is constructed to not make much use of internal functions, as it is intended to be a template to copy.
|
||||
// This code is intentionally written to not make much use of internal functions, to give you better direction
|
||||
// if you need to write your own.
|
||||
// FIXME-TABLE: TableOpenContextMenu() and TableGetHeaderRowHeight() are not public.
|
||||
void ImGui::TableHeadersRow()
|
||||
{
|
||||
ImGuiContext& g = *GImGui;
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Layout if not already done (this is automatically done by TableNextRow, we do it here solely to facilitate stepping in debugger as it is frequent to step in TableUpdateLayout)
|
||||
// Call layout if not already done. This is automatically done by TableNextRow: we do it here _only_ to make
|
||||
// it easier to debug-step in TableUpdateLayout(). Your own version of this function doesn't need this.
|
||||
if (!table->IsLayoutLocked)
|
||||
TableUpdateLayout(table);
|
||||
|
||||
@@ -3029,8 +3127,7 @@ void ImGui::TableHeadersRow()
|
||||
if (!TableSetColumnIndex(column_n))
|
||||
continue;
|
||||
|
||||
// Push an id to allow unnamed labels (generally accidental, but let's behave nicely with them)
|
||||
// In your own code you may omit the PushID/PopID all-together, provided you know they won't collide.
|
||||
// Push an id to allow empty/unnamed headers. This is also idiomatic as it ensure there is a consistent ID path to access columns (for e.g. automation)
|
||||
const char* name = (TableGetColumnFlags(column_n) & ImGuiTableColumnFlags_NoHeaderLabel) ? "" : TableGetColumnName(column_n);
|
||||
PushID(column_n);
|
||||
TableHeader(name);
|
||||
@@ -3055,7 +3152,12 @@ void ImGui::TableHeader(const char* label)
|
||||
return;
|
||||
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
IM_ASSERT(table != NULL && "Need to call TableHeader() after BeginTable()!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
|
||||
IM_ASSERT(table->CurrentColumn != -1);
|
||||
const int column_n = table->CurrentColumn;
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
@@ -3121,7 +3223,7 @@ void ImGui::TableHeader(const char* label)
|
||||
if ((table->RowFlags & ImGuiTableRowFlags_Headers) == 0)
|
||||
TableSetBgColor(ImGuiTableBgTarget_CellBg, GetColorU32(ImGuiCol_TableHeaderBg), table->CurrentColumn);
|
||||
}
|
||||
RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_Compact | ImGuiNavHighlightFlags_NoRounding);
|
||||
RenderNavCursor(bb, id, ImGuiNavRenderCursorFlags_Compact | ImGuiNavRenderCursorFlags_NoRounding);
|
||||
if (held)
|
||||
table->HeldHeaderColumn = (ImGuiTableColumnIdx)column_n;
|
||||
window->DC.CursorPos.y -= g.Style.ItemSpacing.y * 0.5f;
|
||||
@@ -3176,7 +3278,7 @@ void ImGui::TableHeader(const char* label)
|
||||
// Render clipped label. Clipping here ensure that in the majority of situations, all our header cells will
|
||||
// be merged into a single draw call.
|
||||
//window->DrawList->AddCircleFilled(ImVec2(ellipsis_max, label_pos.y), 40, IM_COL32_WHITE);
|
||||
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size);
|
||||
RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, bb.Max.y), ellipsis_max, label, label_end, &label_size);
|
||||
|
||||
const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x);
|
||||
if (text_clipped && hovered && g.ActiveId == 0)
|
||||
@@ -3230,7 +3332,11 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
|
||||
ImGuiTable* table = g.CurrentTable;
|
||||
ImGuiWindow* window = g.CurrentWindow;
|
||||
ImDrawList* draw_list = window->DrawList;
|
||||
IM_ASSERT(table != NULL && "Need to call TableHeadersRow() after BeginTable()!");
|
||||
if (table == NULL)
|
||||
{
|
||||
IM_ASSERT_USER_ERROR(table != NULL, "Call should only be done while in BeginTable() scope!");
|
||||
return;
|
||||
}
|
||||
IM_ASSERT(table->CurrentRow == -1 && "Must be first row");
|
||||
|
||||
if (max_label_width == 0.0f)
|
||||
@@ -3269,13 +3375,13 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
|
||||
ButtonBehavior(row_r, row_id, NULL, NULL);
|
||||
KeepAliveID(row_id);
|
||||
|
||||
const float ascent_scaled = g.Font->Ascent * (g.FontSize / g.Font->FontSize); // FIXME: Standardize those scaling factors better
|
||||
const float ascent_scaled = g.FontBaked->Ascent * g.FontBakedScale; // FIXME: Standardize those scaling factors better
|
||||
const float line_off_for_ascent_x = (ImMax((g.FontSize - ascent_scaled) * 0.5f, 0.0f) / -sin_a) * (flip_label ? -1.0f : 1.0f);
|
||||
const ImVec2 padding = g.Style.CellPadding; // We will always use swapped component
|
||||
const ImVec2 align = g.Style.TableAngledHeadersTextAlign;
|
||||
|
||||
// Draw background and labels in first pass, then all borders.
|
||||
float max_x = 0.0f;
|
||||
float max_x = -FLT_MAX;
|
||||
for (int pass = 0; pass < 2; pass++)
|
||||
for (int order_n = 0; order_n < data_count; order_n++)
|
||||
{
|
||||
@@ -3324,7 +3430,7 @@ void ImGui::TableAngledHeadersRowEx(ImGuiID row_id, float angle, float max_label
|
||||
ImRect clip_r(window->ClipRect.Min, window->ClipRect.Min + ImVec2(clip_width, clip_height));
|
||||
int vtx_idx_begin = draw_list->_VtxCurrentIdx;
|
||||
PushStyleColor(ImGuiCol_Text, request->TextColor);
|
||||
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, clip_r.Max.x, label_name, label_name_eol, &label_size);
|
||||
RenderTextEllipsis(draw_list, clip_r.Min, clip_r.Max, clip_r.Max.x, label_name, label_name_eol, &label_size);
|
||||
PopStyleColor();
|
||||
int vtx_idx_end = draw_list->_VtxCurrentIdx;
|
||||
|
||||
@@ -3471,7 +3577,7 @@ void ImGui::TableDrawDefaultContextMenu(ImGuiTable* table, ImGuiTableFlags flags
|
||||
Separator();
|
||||
want_separator = true;
|
||||
|
||||
PushItemFlag(ImGuiItemFlags_SelectableDontClosePopup, true);
|
||||
PushItemFlag(ImGuiItemFlags_AutoClosePopups, false);
|
||||
for (int other_column_n = 0; other_column_n < table->ColumnsCount; other_column_n++)
|
||||
{
|
||||
ImGuiTableColumn* other_column = &table->Columns[other_column_n];
|
||||
@@ -3661,6 +3767,14 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
|
||||
table->SettingsLoadedFlags = settings->SaveFlags;
|
||||
table->RefScale = settings->RefScale;
|
||||
|
||||
// Initialize default columns settings
|
||||
for (int column_n = 0; column_n < table->ColumnsCount; column_n++)
|
||||
{
|
||||
ImGuiTableColumn* column = &table->Columns[column_n];
|
||||
TableInitColumnDefaults(table, column, ~0);
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
|
||||
// Serialize ImGuiTableSettings/ImGuiTableColumnSettings into ImGuiTable/ImGuiTableColumn
|
||||
ImGuiTableColumnSettings* column_settings = settings->GetColumnSettings();
|
||||
ImU64 display_order_mask = 0;
|
||||
@@ -3677,14 +3791,12 @@ void ImGui::TableLoadSettings(ImGuiTable* table)
|
||||
column->StretchWeight = column_settings->WidthOrWeight;
|
||||
else
|
||||
column->WidthRequest = column_settings->WidthOrWeight;
|
||||
column->AutoFitQueue = 0x00;
|
||||
}
|
||||
if (settings->SaveFlags & ImGuiTableFlags_Reorderable)
|
||||
column->DisplayOrder = column_settings->DisplayOrder;
|
||||
else
|
||||
column->DisplayOrder = (ImGuiTableColumnIdx)column_n;
|
||||
display_order_mask |= (ImU64)1 << column->DisplayOrder;
|
||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled;
|
||||
if ((settings->SaveFlags & ImGuiTableFlags_Hideable) && column_settings->IsEnabled != -1)
|
||||
column->IsUserEnabled = column->IsUserEnabledNextFrame = column_settings->IsEnabled == 1;
|
||||
column->SortOrder = column_settings->SortOrder;
|
||||
column->SortDirection = column_settings->SortDirection;
|
||||
}
|
||||
@@ -3780,8 +3892,7 @@ static void TableSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandle
|
||||
const bool save_visible = (settings->SaveFlags & ImGuiTableFlags_Hideable) != 0;
|
||||
const bool save_order = (settings->SaveFlags & ImGuiTableFlags_Reorderable) != 0;
|
||||
const bool save_sort = (settings->SaveFlags & ImGuiTableFlags_Sortable) != 0;
|
||||
if (!save_size && !save_visible && !save_order && !save_sort)
|
||||
continue;
|
||||
// We need to save the [Table] entry even if all the bools are false, since this records a table with "default settings".
|
||||
|
||||
buf->reserve(buf->size() + 30 + settings->ColumnsCount * 50); // ballpark reserve
|
||||
buf->appendf("[%s][0x%08X,%d]\n", handler->TypeName, settings->ID, settings->ColumnsCount);
|
||||
@@ -4393,7 +4504,7 @@ void ImGui::EndColumns()
|
||||
{
|
||||
ButtonBehavior(column_hit_rect, column_id, &hovered, &held);
|
||||
if (hovered || held)
|
||||
g.MouseCursor = ImGuiMouseCursor_ResizeEW;
|
||||
SetMouseCursor(ImGuiMouseCursor_ResizeEW);
|
||||
if (held && !(column->Flags & ImGuiOldColumnFlags_NoResize))
|
||||
dragging_column = n;
|
||||
}
|
||||
@@ -4425,12 +4536,12 @@ void ImGui::EndColumns()
|
||||
NavUpdateCurrentWindowIsScrollPushableX();
|
||||
}
|
||||
|
||||
void ImGui::Columns(int columns_count, const char* id, bool border)
|
||||
void ImGui::Columns(int columns_count, const char* id, bool borders)
|
||||
{
|
||||
ImGuiWindow* window = GetCurrentWindow();
|
||||
IM_ASSERT(columns_count >= 1);
|
||||
|
||||
ImGuiOldColumnFlags flags = (border ? 0 : ImGuiOldColumnFlags_NoBorder);
|
||||
ImGuiOldColumnFlags flags = (borders ? 0 : ImGuiOldColumnFlags_NoBorder);
|
||||
//flags |= ImGuiOldColumnFlags_NoPreserveWidths; // NB: Legacy behavior
|
||||
ImGuiOldColumns* columns = window->DC.CurrentColumns;
|
||||
if (columns != NULL && columns->Count == columns_count && columns->Flags == flags)
|
||||
|
||||
+2342
-764
File diff suppressed because it is too large.
Load diff
@@ -3,6 +3,8 @@
|
||||
// Those changes would need to be pushed into nothings/stb:
|
||||
// - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321)
|
||||
// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000 + #6783)
|
||||
// - Added name to struct or it may be forward declared in our code.
|
||||
// - Added UTF-8 support (see https://github.com/nothings/stb/issues/188 + https://github.com/ocornut/imgui/pull/7925)
|
||||
// Grep for [DEAR IMGUI] to find the changes.
|
||||
// - Also renamed macros used or defined outside of IMSTB_TEXTEDIT_IMPLEMENTATION block from STB_TEXTEDIT_* to IMSTB_TEXTEDIT_*
|
||||
|
||||
@@ -139,6 +141,7 @@
|
||||
// with previous char)
|
||||
// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character
|
||||
// (return type is int, -1 means not valid to insert)
|
||||
// (not supported if you want to use UTF-8, see below)
|
||||
// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based
|
||||
// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize
|
||||
// as manually wordwrapping for end-of-line positioning
|
||||
@@ -176,6 +179,13 @@
|
||||
// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text
|
||||
// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text
|
||||
//
|
||||
// To support UTF-8:
|
||||
//
|
||||
// STB_TEXTEDIT_GETPREVCHARINDEX returns index of previous character
|
||||
// STB_TEXTEDIT_GETNEXTCHARINDEX returns index of next character
|
||||
// Do NOT define STB_TEXTEDIT_KEYTOTEXT.
|
||||
// Instead, call stb_textedit_text() directly for text contents.
|
||||
//
|
||||
// Keyboard input must be encoded as a single integer value; e.g. a character code
|
||||
// and some bitflags that represent shift states. to simplify the interface, SHIFT must
|
||||
// be a bitflag, so we can test the shifted state of cursor movements to allow selection,
|
||||
@@ -209,6 +219,7 @@
|
||||
// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state)
|
||||
// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len)
|
||||
// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key)
|
||||
// void stb_textedit_text(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int text_len)
|
||||
//
|
||||
// Each of these functions potentially updates the string and updates the
|
||||
// state.
|
||||
@@ -243,7 +254,14 @@
|
||||
// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit
|
||||
// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is
|
||||
// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to
|
||||
// anything other type you wante before including.
|
||||
// anything other type you want before including.
|
||||
// if the STB_TEXTEDIT_KEYTOTEXT function is defined, selected keys are
|
||||
// transformed into text and stb_textedit_text() is automatically called.
|
||||
//
|
||||
// text: (added 2025)
|
||||
// call this to directly send text input the textfield, which is required
|
||||
// for UTF-8 support, because stb_textedit_key() + STB_TEXTEDIT_KEYTOTEXT()
|
||||
// cannot infer text length.
|
||||
//
|
||||
//
|
||||
// When rendering, you can read the cursor position and selection state from
|
||||
@@ -318,7 +336,7 @@ typedef struct
|
||||
int undo_char_point, redo_char_point;
|
||||
} StbUndoState;
|
||||
|
||||
typedef struct
|
||||
typedef struct STB_TexteditState
|
||||
{
|
||||
/////////////////////
|
||||
//
|
||||
@@ -392,6 +410,16 @@ typedef struct
|
||||
#define IMSTB_TEXTEDIT_memmove memmove
|
||||
#endif
|
||||
|
||||
// [DEAR IMGUI]
|
||||
// Functions must be implemented for UTF8 support
|
||||
// Code in this file that uses those functions is modified for [DEAR IMGUI] and deviates from the original stb_textedit.
|
||||
// There is not necessarily a '[DEAR IMGUI]' at the usage sites.
|
||||
#ifndef IMSTB_TEXTEDIT_GETPREVCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETPREVCHARINDEX(OBJ, IDX) ((IDX) - 1)
|
||||
#endif
|
||||
#ifndef IMSTB_TEXTEDIT_GETNEXTCHARINDEX
|
||||
#define IMSTB_TEXTEDIT_GETNEXTCHARINDEX(OBJ, IDX) ((IDX) + 1)
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -438,13 +466,13 @@ static int stb_text_locate_coord(IMSTB_TEXTEDIT_STRING *str, float x, float y)
|
||||
if (x < r.x1) {
|
||||
// search characters in row for one that straddles 'x'
|
||||
prev_x = r.x0;
|
||||
for (k=0; k < r.num_chars; ++k) {
|
||||
for (k=0; k < r.num_chars; k = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k) - i) {
|
||||
float w = STB_TEXTEDIT_GETWIDTH(str, i, k);
|
||||
if (x < prev_x+w) {
|
||||
if (x < prev_x+w/2)
|
||||
return k+i;
|
||||
else
|
||||
return k+i+1;
|
||||
return IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, i + k);
|
||||
}
|
||||
prev_x += w;
|
||||
}
|
||||
@@ -563,7 +591,7 @@ static void stb_textedit_find_charpos(StbFindState *find, IMSTB_TEXTEDIT_STRING
|
||||
|
||||
// now scan to find xpos
|
||||
find->x = r.x0;
|
||||
for (i=0; first+i < n; ++i)
|
||||
for (i=0; first+i < n; i = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, first + i) - first)
|
||||
find->x += STB_TEXTEDIT_GETWIDTH(str, first, i);
|
||||
}
|
||||
|
||||
@@ -649,9 +677,9 @@ static int is_word_boundary( IMSTB_TEXTEDIT_STRING *str, int idx )
|
||||
#ifndef STB_TEXTEDIT_MOVEWORDLEFT
|
||||
static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
--c; // always move at least one character
|
||||
while( c >= 0 && !is_word_boundary( str, c ) )
|
||||
--c;
|
||||
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX( str, c ); // always move at least one character
|
||||
while (c >= 0 && !is_word_boundary(str, c))
|
||||
c = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, c);
|
||||
|
||||
if( c < 0 )
|
||||
c = 0;
|
||||
@@ -665,9 +693,9 @@ static int stb_textedit_move_to_word_previous( IMSTB_TEXTEDIT_STRING *str, int c
|
||||
static int stb_textedit_move_to_word_next( IMSTB_TEXTEDIT_STRING *str, int c )
|
||||
{
|
||||
const int len = STB_TEXTEDIT_STRINGLEN(str);
|
||||
++c; // always move at least one character
|
||||
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c); // always move at least one character
|
||||
while( c < len && !is_word_boundary( str, c ) )
|
||||
++c;
|
||||
c = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, c);
|
||||
|
||||
if( c > len )
|
||||
c = len;
|
||||
@@ -720,36 +748,45 @@ static int stb_textedit_paste_internal(IMSTB_TEXTEDIT_STRING *str, STB_TexteditS
|
||||
#define STB_TEXTEDIT_KEYTYPE int
|
||||
#endif
|
||||
|
||||
// API key: process text input
|
||||
// [DEAR IMGUI] Added stb_textedit_text(), extracted out and called by stb_textedit_key() for backward compatibility.
|
||||
static void stb_textedit_text(IMSTB_TEXTEDIT_STRING* str, STB_TexteditState* state, const IMSTB_TEXTEDIT_CHARTYPE* text, int text_len)
|
||||
{
|
||||
// can't add newline in single-line mode
|
||||
if (text[0] == '\n' && state->single_line)
|
||||
return;
|
||||
|
||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
} else {
|
||||
stb_textedit_delete_selection(str, state); // implicitly clamps
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, text_len)) {
|
||||
stb_text_makeundo_insert(state, state->cursor, text_len);
|
||||
state->cursor += text_len;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// API key: process a keyboard input
|
||||
static void stb_textedit_key(IMSTB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key)
|
||||
{
|
||||
retry:
|
||||
switch (key) {
|
||||
default: {
|
||||
#ifdef STB_TEXTEDIT_KEYTOTEXT
|
||||
// This is not suitable for UTF-8 support.
|
||||
int c = STB_TEXTEDIT_KEYTOTEXT(key);
|
||||
if (c > 0) {
|
||||
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE) c;
|
||||
|
||||
// can't add newline in single-line mode
|
||||
if (c == '\n' && state->single_line)
|
||||
break;
|
||||
|
||||
if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) {
|
||||
stb_text_makeundo_replace(str, state, state->cursor, 1, 1);
|
||||
STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1);
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
|
||||
++state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
} else {
|
||||
stb_textedit_delete_selection(str,state); // implicitly clamps
|
||||
if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) {
|
||||
stb_text_makeundo_insert(state, state->cursor, 1);
|
||||
++state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
}
|
||||
}
|
||||
IMSTB_TEXTEDIT_CHARTYPE ch = (IMSTB_TEXTEDIT_CHARTYPE)c;
|
||||
stb_textedit_text(str, state, &ch, 1);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -775,7 +812,7 @@ retry:
|
||||
stb_textedit_move_to_first(state);
|
||||
else
|
||||
if (state->cursor > 0)
|
||||
--state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
||||
@@ -784,7 +821,7 @@ retry:
|
||||
if (STB_TEXT_HAS_SELECTION(state))
|
||||
stb_textedit_move_to_last(str, state);
|
||||
else
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
stb_textedit_clamp(str, state);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -794,7 +831,7 @@ retry:
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
// move selection left
|
||||
if (state->select_end > 0)
|
||||
--state->select_end;
|
||||
state->select_end = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->select_end);
|
||||
state->cursor = state->select_end;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -844,7 +881,7 @@ retry:
|
||||
case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT:
|
||||
stb_textedit_prep_selection_at_cursor(state);
|
||||
// move selection right
|
||||
++state->select_end;
|
||||
state->select_end = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->select_end);
|
||||
stb_textedit_clamp(str, state);
|
||||
state->cursor = state->select_end;
|
||||
state->has_preferred_x = 0;
|
||||
@@ -891,8 +928,9 @@ retry:
|
||||
state->cursor = start;
|
||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
for (i=0; i < row.num_chars; ) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, start, i);
|
||||
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
@@ -900,7 +938,8 @@ retry:
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
++state->cursor;
|
||||
i += next - state->cursor;
|
||||
state->cursor = next;
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
@@ -953,8 +992,9 @@ retry:
|
||||
state->cursor = find.prev_first;
|
||||
STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor);
|
||||
x = row.x0;
|
||||
for (i=0; i < row.num_chars; ++i) {
|
||||
for (i=0; i < row.num_chars; ) {
|
||||
float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i);
|
||||
int next = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
#ifdef IMSTB_TEXTEDIT_GETWIDTH_NEWLINE
|
||||
if (dx == IMSTB_TEXTEDIT_GETWIDTH_NEWLINE)
|
||||
break;
|
||||
@@ -962,7 +1002,8 @@ retry:
|
||||
x += dx;
|
||||
if (x > goal_x)
|
||||
break;
|
||||
++state->cursor;
|
||||
i += next - state->cursor;
|
||||
state->cursor = next;
|
||||
}
|
||||
stb_textedit_clamp(str, state);
|
||||
|
||||
@@ -975,8 +1016,13 @@ retry:
|
||||
// go to previous line
|
||||
// (we need to scan previous line the hard way. maybe we could expose this as a new API function?)
|
||||
prev_scan = find.prev_first > 0 ? find.prev_first - 1 : 0;
|
||||
while (prev_scan > 0 && STB_TEXTEDIT_GETCHAR(str, prev_scan - 1) != STB_TEXTEDIT_NEWLINE)
|
||||
--prev_scan;
|
||||
while (prev_scan > 0)
|
||||
{
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, prev_scan);
|
||||
if (STB_TEXTEDIT_GETCHAR(str, prev) == STB_TEXTEDIT_NEWLINE)
|
||||
break;
|
||||
prev_scan = prev;
|
||||
}
|
||||
find.first_char = find.prev_first;
|
||||
find.prev_first = prev_scan;
|
||||
}
|
||||
@@ -990,7 +1036,7 @@ retry:
|
||||
else {
|
||||
int n = STB_TEXTEDIT_STRINGLEN(str);
|
||||
if (state->cursor < n)
|
||||
stb_textedit_delete(str, state, state->cursor, 1);
|
||||
stb_textedit_delete(str, state, state->cursor, IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor) - state->cursor);
|
||||
}
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -1002,8 +1048,9 @@ retry:
|
||||
else {
|
||||
stb_textedit_clamp(str, state);
|
||||
if (state->cursor > 0) {
|
||||
stb_textedit_delete(str, state, state->cursor-1, 1);
|
||||
--state->cursor;
|
||||
int prev = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
stb_textedit_delete(str, state, prev, state->cursor - prev);
|
||||
state->cursor = prev;
|
||||
}
|
||||
}
|
||||
state->has_preferred_x = 0;
|
||||
@@ -1054,7 +1101,7 @@ retry:
|
||||
if (state->single_line)
|
||||
state->cursor = 0;
|
||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
||||
--state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
||||
@@ -1066,9 +1113,9 @@ retry:
|
||||
stb_textedit_clamp(str, state);
|
||||
stb_textedit_move_to_first(state);
|
||||
if (state->single_line)
|
||||
state->cursor = n;
|
||||
state->cursor = n;
|
||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
}
|
||||
@@ -1082,7 +1129,7 @@ retry:
|
||||
if (state->single_line)
|
||||
state->cursor = 0;
|
||||
else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE)
|
||||
--state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETPREVCHARINDEX(str, state->cursor);
|
||||
state->select_end = state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
@@ -1097,7 +1144,7 @@ retry:
|
||||
if (state->single_line)
|
||||
state->cursor = n;
|
||||
else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE)
|
||||
++state->cursor;
|
||||
state->cursor = IMSTB_TEXTEDIT_GETNEXTCHARINDEX(str, state->cursor);
|
||||
state->select_end = state->cursor;
|
||||
state->has_preferred_x = 0;
|
||||
break;
|
||||
|
||||
@@ -4516,8 +4516,8 @@ static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex
|
||||
q2[0] = (float)x2;
|
||||
q2[1] = (float)y2;
|
||||
if (equal(q0,q1) || equal(q1,q2)) {
|
||||
x0 = (int)verts[i-1].x;
|
||||
y0 = (int)verts[i-1].y;
|
||||
x0 = (int)verts[i-1].x; //-V1048
|
||||
y0 = (int)verts[i-1].y; //-V1048
|
||||
x1 = (int)verts[i ].x;
|
||||
y1 = (int)verts[i ].y;
|
||||
if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) {
|
||||
|
||||
+1
-1
Submodule core/deps/libadrenotools updated: 5deac9f1ab...8fae8ce254.
+1
-1
Submodule core/deps/libchdr updated: 86b272076d...5f82799f2c.
Submodule
+1
Submodule core/deps/libjuice added at 77daa8befd.
@@ -31,7 +31,7 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined(_WIN32) && !defined(USE_LIBCDIO) // libcdio has its equivalent version
|
||||
#undef fopen
|
||||
|
||||
void *fopen_utf8(const char * filename, const char * mode)
|
||||
|
||||
Submodule
+1
Submodule core/deps/libusb-cmake added at 000e06eb24.
@@ -190,7 +190,9 @@ int main(int argc, char *argv[]) { }" HAVE_NULLABLE)
|
||||
|
||||
test_big_endian(WORDS_BIGENDIAN)
|
||||
|
||||
find_package(ZLIB 1.1.2 REQUIRED)
|
||||
if(NOT TARGET zlibstatic)
|
||||
find_package(ZLIB 1.1.2 REQUIRED)
|
||||
endif()
|
||||
# so developers on systems where zlib is named differently (Windows, sometimes)
|
||||
# can override the name used in the pkg-config file
|
||||
if (NOT ZLIB_LINK_LIBRARY_NAME)
|
||||
@@ -239,17 +241,22 @@ if(ENABLE_LZMA)
|
||||
endif(ENABLE_LZMA)
|
||||
|
||||
if(ENABLE_ZSTD)
|
||||
find_package(zstd 1.3.6)
|
||||
if(zstd_FOUND)
|
||||
if(TARGET libzstd_static)
|
||||
set(zstd_TARGET libzstd_static)
|
||||
set(HAVE_LIBZSTD 1)
|
||||
if(TARGET zstd::libzstd_shared)
|
||||
set(zstd_TARGET zstd::libzstd_shared)
|
||||
else()
|
||||
set(zstd_TARGET zstd::libzstd_static)
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "-- zstd library not found; zstandard support disabled")
|
||||
endif(zstd_FOUND)
|
||||
find_package(zstd 1.3.6)
|
||||
if(zstd_FOUND)
|
||||
set(HAVE_LIBZSTD 1)
|
||||
if(TARGET zstd::libzstd_shared)
|
||||
set(zstd_TARGET zstd::libzstd_shared)
|
||||
else()
|
||||
set(zstd_TARGET zstd::libzstd_static)
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "-- zstd library not found; zstandard support disabled")
|
||||
endif()
|
||||
endif()
|
||||
endif(ENABLE_ZSTD)
|
||||
|
||||
if (COMMONCRYPTO_FOUND)
|
||||
|
||||
@@ -175,7 +175,11 @@ if(SHARED_LIB_VERSIONNING)
|
||||
set_target_properties(zip PROPERTIES VERSION 5.5 SOVERSION 5)
|
||||
endif()
|
||||
|
||||
target_link_libraries(zip PRIVATE ZLIB::ZLIB)
|
||||
if (TARGET zlibstatic)
|
||||
target_link_libraries(zip PRIVATE zlibstatic)
|
||||
else()
|
||||
target_link_libraries(zip PRIVATE ZLIB::ZLIB)
|
||||
endif()
|
||||
target_include_directories(zip
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/lib>
|
||||
|
||||
@@ -76,6 +76,9 @@ int _zip_win32_error_to_errno(DWORD win32err);
|
||||
#ifdef __clang__
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wincompatible-function-pointer-types\"")
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_END _Pragma("GCC diagnostic pop")
|
||||
#elif defined(__GNUC__) && __GNUC__ > 13
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wincompatible-pointer-types\"")
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_END _Pragma("GCC diagnostic pop")
|
||||
#else
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_BEGIN
|
||||
#define DONT_WARN_INCOMPATIBLE_FN_PTR_END
|
||||
|
||||
+1
-1
Submodule core/deps/luabridge updated: fab7b33b89...5d21e35633.
@@ -338,7 +338,7 @@ receiveDevicesFromMiniSSDPD(int s, int * error)
|
||||
#ifdef DEBUG
|
||||
printf(" usnsize=%u\n", usnsize);
|
||||
#endif /* DEBUG */
|
||||
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
|
||||
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize+3);
|
||||
if(tmp == NULL) {
|
||||
if (error)
|
||||
*error = MINISSDPC_MEMORY_ERROR;
|
||||
|
||||
+1
-1
Submodule core/deps/oboe updated: 8740d0fc32...a81bb9f87d.
@@ -7,6 +7,8 @@
|
||||
#include <windows.h>
|
||||
#pragma pack(pop)
|
||||
|
||||
#define PICO_SUPPORT_THREADING
|
||||
|
||||
#if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
|
||||
#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
|
||||
#else
|
||||
@@ -54,6 +56,15 @@ static inline void PICO_IDLE(void)
|
||||
|
||||
#define alloca _alloca
|
||||
|
||||
#ifdef PICO_SUPPORT_THREADING
|
||||
#define PICO_SUPPORT_MUTEX
|
||||
/* mutex implementations */
|
||||
extern void* pico_mutex_init(void);
|
||||
extern void pico_mutex_lock(void* mux);
|
||||
extern void pico_mutex_unlock(void* mux);
|
||||
extern void pico_mutex_deinit(void* mux);
|
||||
#endif
|
||||
|
||||
#endif /* PICO_SUPPORT_MSVC */
|
||||
|
||||
|
||||
@@ -170,6 +170,7 @@ struct pico_ip_mreq_source {
|
||||
#define PICO_SOCK_EV_CLOSE 8u
|
||||
#define PICO_SOCK_EV_FIN 0x10u
|
||||
#define PICO_SOCK_EV_ERR 0x80u
|
||||
#define PICO_SOCK_EV_DEL 0x100u
|
||||
|
||||
struct pico_msginfo {
|
||||
struct pico_device *dev;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "pico_frame.h"
|
||||
#include "pico_constants.h"
|
||||
|
||||
#define PICO_MAX_TIMERS 20
|
||||
#define PICO_MAX_TIMERS 50
|
||||
|
||||
#define PICO_ETH_MRU (1514u)
|
||||
#define PICO_IP_MRU (1500u)
|
||||
|
||||
@@ -319,7 +319,7 @@ static void pppdump(uint8_t *buf, int len, int egress)
|
||||
last_time_ms = PICO_TIME_MS();
|
||||
}
|
||||
|
||||
uint32_t delta = (PICO_TIME_MS() - last_time_ms) / 100;
|
||||
uint32_t delta = PICO_TIME_MS() / 100 - last_time_ms / 100;
|
||||
if (delta < 256)
|
||||
{
|
||||
fputc(6, dumpfp); // Time step (short)
|
||||
|
||||
@@ -295,7 +295,7 @@ static int pico_ethernet_ipv6_dst(struct pico_frame *f, struct pico_eth *const d
|
||||
|
||||
/* Ethernet send, first attempt: try our own address.
|
||||
* Returns 0 if the packet is not for us.
|
||||
* Returns 1 if the packet is cloned to our own receive queue, so the caller can discard the original frame.
|
||||
* Returns 1 if the packet is cloned to our own receive queue and the original frame is dicarded.
|
||||
* */
|
||||
static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr)
|
||||
{
|
||||
@@ -308,7 +308,9 @@ static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr
|
||||
dbg("sending out packet destined for our own mac\n");
|
||||
if (pico_ethernet_receive(clone) < 0) {
|
||||
dbg("pico_ethernet_receive() failed\n");
|
||||
return 0;
|
||||
}
|
||||
pico_frame_discard(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -317,13 +319,12 @@ static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr
|
||||
|
||||
/* Ethernet send, second attempt: try bcast.
|
||||
* Returns 0 if the packet is not bcast, so it will be handled somewhere else.
|
||||
* Returns 1 if the packet is handled by the pico_device_broadcast() function, so it can be discarded.
|
||||
* Returns 1 if the packet is handled by the pico_device_broadcast() function and is discarded.
|
||||
* */
|
||||
static int32_t pico_ethsend_bcast(struct pico_frame *f)
|
||||
{
|
||||
if (IS_LIMITED_BCAST(f)) {
|
||||
(void)pico_device_broadcast(f); /* We can discard broadcast even if it's not sent. */
|
||||
return 1;
|
||||
return (pico_device_broadcast(f) > 0); // Return 1 on success, ret > 0
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -242,6 +242,10 @@ static int8_t pico_icmp4_send_echo(struct pico_icmp4_ping_cookie *cookie)
|
||||
if (!dev)
|
||||
return -1;
|
||||
|
||||
// prevent overflow
|
||||
if (cookie->size > PICO_ICMP_MAXCOOKIE)
|
||||
return -1;
|
||||
|
||||
echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size));
|
||||
if (!echo)
|
||||
return -1;
|
||||
|
||||
@@ -92,7 +92,7 @@ PACKED_STRUCT_DEF pico_icmp4_hdr {
|
||||
#define PICO_ICMP_MASKREPLY 18
|
||||
|
||||
#define PICO_ICMP_MAXTYPE 18
|
||||
|
||||
#define PICO_ICMP_MAXCOOKIE 65528
|
||||
|
||||
#define PICO_ICMP_UNREACH_NET 0
|
||||
#define PICO_ICMP_UNREACH_HOST 1
|
||||
|
||||
@@ -190,7 +190,6 @@ static int socket_tcp_do_deliver(struct pico_socket *s, struct pico_frame *f)
|
||||
return 0;
|
||||
}
|
||||
|
||||
dbg("TCP SOCKET> Not s.\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -826,6 +826,8 @@ static void tcp_rcv_sack(struct pico_socket_tcp *t, uint8_t *opt, int len)
|
||||
static int tcpopt_len_check(uint32_t *idx, uint8_t len, uint8_t expected)
|
||||
{
|
||||
if (len != expected) {
|
||||
if (len < 2)
|
||||
return -1;
|
||||
*idx = *idx + len - 2;
|
||||
return -1;
|
||||
}
|
||||
@@ -880,7 +882,7 @@ static inline void tcp_parse_option_timestamp(struct pico_socket_tcp *t, struct
|
||||
t->ts_nxt = long_be(tsval);
|
||||
}
|
||||
|
||||
static void tcp_parse_options(struct pico_frame *f)
|
||||
static int tcp_parse_options(struct pico_frame *f)
|
||||
{
|
||||
struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock;
|
||||
uint8_t *opt = f->transport_hdr + PICO_SIZE_TCPHDR;
|
||||
@@ -897,6 +899,10 @@ static void tcp_parse_options(struct pico_frame *f)
|
||||
if (f->payload && ((opt + i) > f->payload))
|
||||
break;
|
||||
|
||||
if (len == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
tcp_dbg_options("Received option '%d', len = %d \n", type, len);
|
||||
switch (type) {
|
||||
case PICO_TCP_OPTION_NOOP:
|
||||
@@ -924,6 +930,7 @@ static void tcp_parse_options(struct pico_frame *f)
|
||||
i = i + len - 2;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void tcp_send_add_tcpflags(struct pico_socket_tcp *ts, struct pico_frame *f)
|
||||
@@ -1755,7 +1762,8 @@ static int tcp_data_in(struct pico_socket *s, struct pico_frame *f)
|
||||
(void)hdr;
|
||||
|
||||
if (((hdr->len & 0xf0u) >> 2u) <= f->transport_len) {
|
||||
tcp_parse_options(f);
|
||||
if (tcp_parse_options(f) < 0)
|
||||
return -1;
|
||||
f->payload = f->transport_hdr + ((hdr->len & 0xf0u) >> 2u);
|
||||
f->payload_len = payload_len;
|
||||
tcp_dbg("TCP> Received segment. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f));
|
||||
@@ -2130,7 +2138,8 @@ static int tcp_ack(struct pico_socket *s, struct pico_frame *f)
|
||||
tcp_ack_dbg(s, f);
|
||||
#endif
|
||||
|
||||
tcp_parse_options(f);
|
||||
if (tcp_parse_options(f) < 0)
|
||||
return -1;
|
||||
t->recv_wnd = short_be(hdr->rwnd);
|
||||
|
||||
acked = (uint16_t)tcp_ack_advance_una(t, f, &acked_timestamp);
|
||||
@@ -2446,7 +2455,8 @@ static int tcp_syn(struct pico_socket *s, struct pico_frame *f)
|
||||
f->sock = &new->sock;
|
||||
mtu = (uint16_t)pico_socket_get_mss(&new->sock);
|
||||
new->mss = (uint16_t)(mtu - PICO_SIZE_TCPHDR);
|
||||
tcp_parse_options(f);
|
||||
if (tcp_parse_options(f) < 0)
|
||||
return -1;
|
||||
new->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ;
|
||||
new->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ;
|
||||
new->tcpq_hold.max_size = 2u * mtu;
|
||||
@@ -2793,7 +2803,7 @@ static uint8_t invalid_flags(struct pico_socket *s, uint8_t flags)
|
||||
{ /* PICO_SOCKET_STATE_TCP_UNDEF */ 0, },
|
||||
{ /* PICO_SOCKET_STATE_TCP_CLOSED */ 0, },
|
||||
{ /* PICO_SOCKET_STATE_TCP_LISTEN */ PICO_TCP_SYN, PICO_TCP_SYN | PICO_TCP_PSH },
|
||||
{ /* PICO_SOCKET_STATE_TCP_SYN_SENT */ PICO_TCP_SYNACK, PICO_TCP_RST, PICO_TCP_RSTACK},
|
||||
{ /* PICO_SOCKET_STATE_TCP_SYN_SENT */ PICO_TCP_SYNACK, PICO_TCP_RST, PICO_TCP_RSTACK, PICO_TCP_SYNACK | PICO_TCP_PSH },
|
||||
{ /* PICO_SOCKET_STATE_TCP_SYN_RECV */ PICO_TCP_SYN, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
|
||||
{ /* PICO_SOCKET_STATE_TCP_ESTABLISHED*/ PICO_TCP_SYN, PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST, PICO_TCP_RSTACK},
|
||||
{ /* PICO_SOCKET_STATE_TCP_CLOSE_WAIT */ PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST},
|
||||
@@ -2864,6 +2874,11 @@ int pico_tcp_input(struct pico_socket *s, struct pico_frame *f)
|
||||
// tcp_dbg("[sam] TCP> s->state >> 8 = %u\n", s->state >> 8);
|
||||
tcp_dbg("[tcp input] socket: %p state: %d <-- local port:%u remote port: %u seq: 0x%08x ack: 0x%08x flags: 0x%02x t_len: %u, hdr: %u payload: %d\n", s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len );
|
||||
|
||||
if ((f->payload + f->payload_len) > (f->buffer + f->buffer_len)) {
|
||||
tcp_dbg("TCP> Invalid payload len %04x\n", f->payload_len);
|
||||
pico_frame_discard(f);
|
||||
return -1;
|
||||
}
|
||||
/* This copy of the frame has the current socket as owner */
|
||||
f->sock = s;
|
||||
s->timestamp = TCP_TIME;
|
||||
@@ -2874,7 +2889,9 @@ int pico_tcp_input(struct pico_socket *s, struct pico_frame *f)
|
||||
}
|
||||
else if (flags == PICO_TCP_SYN || flags == (PICO_TCP_SYN | PICO_TCP_PSH)) {
|
||||
tcp_action_call(action->syn, s, f);
|
||||
} else if (flags == (PICO_TCP_SYN | PICO_TCP_ACK)) {
|
||||
} else if (flags == (PICO_TCP_SYN | PICO_TCP_ACK)
|
||||
// Windows CE / DirectPlay sets the PSH flag on SYN|ACK packets, which is normally invalid
|
||||
|| flags == (PICO_TCP_SYN | PICO_TCP_ACK | PICO_TCP_PSH)) {
|
||||
tcp_action_call(action->synack, s, f);
|
||||
} else {
|
||||
ret = tcp_action_by_flags(action, s, f, flags);
|
||||
|
||||
@@ -451,6 +451,7 @@ int32_t pico_device_broadcast(struct pico_frame *f)
|
||||
{
|
||||
struct pico_tree_node *index;
|
||||
int32_t ret = -1;
|
||||
int sent = 0;
|
||||
|
||||
pico_tree_foreach(index, &Device_tree)
|
||||
{
|
||||
@@ -463,14 +464,28 @@ int32_t pico_device_broadcast(struct pico_frame *f)
|
||||
break;
|
||||
|
||||
copy->dev = dev;
|
||||
copy->dev->send(copy->dev, copy->start, (int)copy->len);
|
||||
ret = copy->dev->send(copy->dev, copy->start, (int)copy->len);
|
||||
/* FIXME: If a device driver returns zero (which means the device
|
||||
* driver is currently busy) there is no means to retry the
|
||||
* broadcast operation later. */
|
||||
pico_frame_discard(copy);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = f->dev->send(f->dev, f->start, (int)f->len);
|
||||
/* FIXME: If a device driver returns zero (which means the device
|
||||
* driver is currently busy) there is no means to retry the
|
||||
* broadcast operation later. */
|
||||
}
|
||||
|
||||
/* FIXME: If at least one device driver was able to sent the frame on
|
||||
* the wire, the broadcast operation will be considered successful. */
|
||||
if (ret > 0) {
|
||||
sent = 1;
|
||||
}
|
||||
}
|
||||
ret = sent ? f->len : -1;
|
||||
pico_frame_discard(f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -473,6 +473,8 @@ static void socket_garbage_collect(pico_time now, void *arg)
|
||||
struct pico_socket *s = (struct pico_socket *) arg;
|
||||
IGNORE_PARAMETER(now);
|
||||
|
||||
if (s->wakeup)
|
||||
s->wakeup(PICO_SOCK_EV_DEL, s);
|
||||
socket_clean_queues(s);
|
||||
PICO_FREE(s);
|
||||
}
|
||||
|
||||
+1
-1
Submodule core/deps/rcheevos updated: e88f749925...3106e6d3d2.
Submodule
+1
Submodule core/deps/websocketpp added at 1b11fd3015.
@@ -1,6 +1,21 @@
|
||||
===========
|
||||
|Changelog|
|
||||
===========
|
||||
xBRZ 1.8 [2019-11-28]
|
||||
---------------------
|
||||
Consider ARGB outside area as transparent
|
||||
Fixed ARGB scaling issue on image borders
|
||||
|
||||
|
||||
xBRZ 1.7 [2019-07-04]
|
||||
---------------------
|
||||
Fixed asymmetric color distance
|
||||
New parameter: "Center direction bias"
|
||||
|
||||
|
||||
xBRZ 1.6 [2018-02-27]
|
||||
---------------------
|
||||
Added bilinear scaling
|
||||
Option to skip color buffer creation
|
||||
Updated license info
|
||||
|
||||
|
||||
xBRZ 1.5 [2017-08-07]
|
||||
---------------------
|
||||
|
||||
+439
-204
@@ -1,24 +1,25 @@
|
||||
// ****************************************************************************
|
||||
// * This file is part of the HqMAME project. It is distributed under *
|
||||
// * This file is part of the xBRZ project. It is distributed under *
|
||||
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
|
||||
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
|
||||
// * *
|
||||
// * Additionally and as a special exception, the author gives permission *
|
||||
// * to link the code of this program with the MAME library (or with modified *
|
||||
// * versions of MAME that use the same license as MAME), and distribute *
|
||||
// * linked combinations including the two. You must obey the GNU General *
|
||||
// * Public License in all respects for all of the code used other than MAME. *
|
||||
// * to link the code of this program with the following libraries *
|
||||
// * (or with modified versions that use the same licenses), and distribute *
|
||||
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
|
||||
// * You must obey the GNU General Public License in all respects for all of *
|
||||
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
|
||||
// * If you modify this file, you may extend this exception to your version *
|
||||
// * of the file, but you are not obligated to do so. If you do not wish to *
|
||||
// * do so, delete this exception statement from your version. *
|
||||
// ****************************************************************************
|
||||
|
||||
#include "xbrz.h"
|
||||
#include "xbrz_tools.h"
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
#include <cmath> //std::sqrt
|
||||
#include "xbrz_tools.h"
|
||||
|
||||
using namespace xbrz;
|
||||
|
||||
@@ -26,9 +27,9 @@ using namespace xbrz;
|
||||
namespace
|
||||
{
|
||||
template <unsigned int M, unsigned int N> inline
|
||||
uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: http://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
|
||||
uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color with opacity M / N over opaque background: https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending
|
||||
{
|
||||
static_assert(0 < M && M < N && N <= 1000, "");
|
||||
static_assert(0 < M && M < N && N <= 1000);
|
||||
|
||||
auto calcColor = [](unsigned char colFront, unsigned char colBack) -> unsigned char { return (colFront * M + colBack * (N - M)) / N; };
|
||||
|
||||
@@ -41,7 +42,7 @@ uint32_t gradientRGB(uint32_t pixFront, uint32_t pixBack) //blend front color wi
|
||||
template <unsigned int M, unsigned int N> inline
|
||||
uint32_t gradientARGB(uint32_t pixFront, uint32_t pixBack) //find intermediate color between two colors with alpha channels (=> NO alpha blending!!!)
|
||||
{
|
||||
static_assert(0 < M && M < N && N <= 1000, "");
|
||||
static_assert(0 < M && M < N && N <= 1000);
|
||||
|
||||
const unsigned int weightFront = getAlpha(pixFront) * M;
|
||||
const unsigned int weightBack = getAlpha(pixBack) * (N - M);
|
||||
@@ -135,7 +136,7 @@ template <class T> inline
|
||||
T square(T value) { return value * value; }
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
inline
|
||||
double distRGB(uint32_t pix1, uint32_t pix2)
|
||||
{
|
||||
@@ -146,12 +147,13 @@ double distRGB(uint32_t pix1, uint32_t pix2)
|
||||
//euklidean RGB distance
|
||||
return std::sqrt(square(r_diff) + square(g_diff) + square(b_diff));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
inline
|
||||
double distYCbCr(uint32_t pix1, uint32_t pix2, double lumaWeight)
|
||||
{
|
||||
//http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
|
||||
//https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
|
||||
//YCbCr conversion is a matrix multiplication => take advantage of linearity by subtracting first!
|
||||
const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2); //we may delay division by 255 to after matrix multiplication
|
||||
const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2); //
|
||||
@@ -186,9 +188,9 @@ double distYCbCrBuffered(uint32_t pix1, uint32_t pix2)
|
||||
|
||||
for (uint32_t i = 0; i < 256 * 256 * 256; ++i) //startup time: 114 ms on Intel Core i5 (four cores)
|
||||
{
|
||||
const int r_diff = getByte<2>(i) * 2 - 0xFF;
|
||||
const int g_diff = getByte<1>(i) * 2 - 0xFF;
|
||||
const int b_diff = getByte<0>(i) * 2 - 0xFF;
|
||||
const int r_diff = static_cast<signed char>(getByte<2>(i)) * 2;
|
||||
const int g_diff = static_cast<signed char>(getByte<1>(i)) * 2;
|
||||
const int b_diff = static_cast<signed char>(getByte<0>(i)) * 2;
|
||||
|
||||
const double k_b = 0.0593; //ITU-R BT.2020 conversion
|
||||
const double k_r = 0.2627; //
|
||||
@@ -210,24 +212,32 @@ double distYCbCrBuffered(uint32_t pix1, uint32_t pix2)
|
||||
// return 0;
|
||||
//if (pix1 < pix2)
|
||||
// std::swap(pix1, pix2); -> 30% perf degradation!!!
|
||||
#if 1
|
||||
|
||||
const int r_diff = static_cast<int>(getRed (pix1)) - getRed (pix2);
|
||||
const int g_diff = static_cast<int>(getGreen(pix1)) - getGreen(pix2);
|
||||
const int b_diff = static_cast<int>(getBlue (pix1)) - getBlue (pix2);
|
||||
|
||||
return diffToDist[(((r_diff + 0xFF) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
|
||||
(((g_diff + 0xFF) / 2) << 8) |
|
||||
(( b_diff + 0xFF) / 2)];
|
||||
#else //not noticeably faster:
|
||||
const int r_diff_tmp = ((pix1 & 0xFF0000) + 0xFF0000 - (pix2 & 0xFF0000)) / 2;
|
||||
const int g_diff_tmp = ((pix1 & 0x00FF00) + 0x00FF00 - (pix2 & 0x00FF00)) / 2; //slightly reduce precision (division by 2) to squeeze value into single byte
|
||||
const int b_diff_tmp = ((pix1 & 0x0000FF) + 0x0000FF - (pix2 & 0x0000FF)) / 2;
|
||||
const size_t index = (static_cast<unsigned char>(r_diff / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
|
||||
(static_cast<unsigned char>(g_diff / 2) << 8) |
|
||||
(static_cast<unsigned char>(b_diff / 2));
|
||||
|
||||
return diffToDist[(r_diff_tmp & 0xFF0000) | (g_diff_tmp & 0x00FF00) | (b_diff_tmp & 0x0000FF)];
|
||||
#if 0 //attention: the following calculation creates an asymmetric color distance!!! (e.g. r_diff=46 will be unpacked as 45, but r_diff=-46 unpacks to -47
|
||||
const size_t index = (((r_diff + 0xFF) / 2) << 16) | //slightly reduce precision (division by 2) to squeeze value into single byte
|
||||
(((g_diff + 0xFF) / 2) << 8) |
|
||||
(( b_diff + 0xFF) / 2);
|
||||
#endif
|
||||
return diffToDist[index];
|
||||
}
|
||||
|
||||
|
||||
#if defined _MSC_VER && !defined NDEBUG
|
||||
const int debugPixelX = -1;
|
||||
const int debugPixelY = 58;
|
||||
|
||||
thread_local bool breakIntoDebugger = false;
|
||||
#endif
|
||||
|
||||
|
||||
enum BlendType
|
||||
{
|
||||
BLEND_NONE = 0,
|
||||
@@ -244,24 +254,32 @@ struct BlendResult
|
||||
};
|
||||
|
||||
|
||||
struct Kernel_3x3
|
||||
{
|
||||
uint32_t
|
||||
a, b, c,
|
||||
d, e, f,
|
||||
g, h, i;
|
||||
};
|
||||
|
||||
struct Kernel_4x4 //kernel for preprocessing step
|
||||
{
|
||||
uint32_t
|
||||
/**/a, b, c, d,
|
||||
/**/e, f, g, h,
|
||||
/**/i, j, k, l,
|
||||
/**/m, n, o, p;
|
||||
a, b, c, //
|
||||
e, f, g, // support reinterpret_cast from Kernel_4x4 => Kernel_3x3
|
||||
i, j, k, //
|
||||
m, n, o,
|
||||
d, h, l, p;
|
||||
};
|
||||
|
||||
/*
|
||||
input kernel area naming convention:
|
||||
/* input kernel area naming convention:
|
||||
-----------------
|
||||
| A | B | C | D |
|
||||
----|---|---|---|
|
||||
| E | F | G | H | //evaluate the four corners between F, G, J, K
|
||||
----|---|---|---| //input pixel is at position F
|
||||
|---|---|---|---|
|
||||
| E | F | G | H | evaluate the four corners between F, G, J, K
|
||||
|---|---|---|---| input pixel is at position F
|
||||
| I | J | K | L |
|
||||
----|---|---|---|
|
||||
|---|---|---|---|
|
||||
| M | N | O | P |
|
||||
-----------------
|
||||
*/
|
||||
@@ -269,6 +287,11 @@ template <class ColorDistance>
|
||||
FORCE_INLINE //detect blend direction
|
||||
BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg) //result: F, G, J, K corners of "GradientType"
|
||||
{
|
||||
#if defined _MSC_VER && !defined NDEBUG
|
||||
if (breakIntoDebugger)
|
||||
__debugbreak(); //__asm int 3;
|
||||
#endif
|
||||
|
||||
BlendResult result = {};
|
||||
|
||||
if ((ker.f == ker.g &&
|
||||
@@ -279,9 +302,8 @@ BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg)
|
||||
|
||||
auto dist = [&](uint32_t pix1, uint32_t pix2) { return ColorDistance::dist(pix1, pix2, cfg.luminanceWeight); };
|
||||
|
||||
const int weight = 4;
|
||||
double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + weight * dist(ker.j, ker.g);
|
||||
double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + weight * dist(ker.f, ker.k);
|
||||
double jg = dist(ker.i, ker.f) + dist(ker.f, ker.c) + dist(ker.n, ker.k) + dist(ker.k, ker.h) + cfg.centerDirectionBias * dist(ker.j, ker.g);
|
||||
double fk = dist(ker.e, ker.j) + dist(ker.j, ker.o) + dist(ker.b, ker.g) + dist(ker.g, ker.l) + cfg.centerDirectionBias * dist(ker.f, ker.k);
|
||||
|
||||
if (jg < fk) //test sample: 70% of values max(jg, fk) / min(jg, fk) are between 1.1 and 3.7 with median being 1.8
|
||||
{
|
||||
@@ -304,14 +326,6 @@ BlendResult preProcessCorners(const Kernel_4x4& ker, const xbrz::ScalerCfg& cfg)
|
||||
return result;
|
||||
}
|
||||
|
||||
struct Kernel_3x3
|
||||
{
|
||||
uint32_t
|
||||
/**/a, b, c,
|
||||
/**/d, e, f,
|
||||
/**/g, h, i;
|
||||
};
|
||||
|
||||
#define DEF_GETTER(x) template <RotationDegree rotDeg> uint32_t inline get_##x(const Kernel_3x3& ker) { return ker.x; }
|
||||
//we cannot and NEED NOT write "ker.##x" since ## concatenates preprocessor tokens but "." is not a token
|
||||
DEF_GETTER(a) DEF_GETTER(b) DEF_GETTER(c)
|
||||
@@ -339,17 +353,21 @@ DEF_GETTER(g, a) DEF_GETTER(h, d) DEF_GETTER(i, g)
|
||||
|
||||
|
||||
//compress four blend types into a single byte
|
||||
inline BlendType getTopL (unsigned char b) { return static_cast<BlendType>(0x3 & b); }
|
||||
//inline BlendType getTopL (unsigned char b) { return static_cast<BlendType>(0x3 & b); }
|
||||
inline BlendType getTopR (unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 2)); }
|
||||
inline BlendType getBottomR(unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 4)); }
|
||||
inline BlendType getBottomL(unsigned char b) { return static_cast<BlendType>(0x3 & (b >> 6)); }
|
||||
|
||||
inline void setTopL (unsigned char& b, BlendType bt) { b |= bt; } //buffer is assumed to be initialized before preprocessing!
|
||||
inline void setTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); }
|
||||
inline void setBottomR(unsigned char& b, BlendType bt) { b |= (bt << 4); }
|
||||
inline void setBottomL(unsigned char& b, BlendType bt) { b |= (bt << 6); }
|
||||
inline void clearAddTopL(unsigned char& b, BlendType bt) { b = static_cast<unsigned char>(bt); }
|
||||
inline void addTopR (unsigned char& b, BlendType bt) { b |= (bt << 2); } //buffer is assumed to be initialized before preprocessing!
|
||||
inline void addBottomR (unsigned char& b, BlendType bt) { b |= (bt << 4); } //e.g. via clearAddTopL()
|
||||
inline void addBottomL (unsigned char& b, BlendType bt) { b |= (bt << 6); } //
|
||||
|
||||
inline bool blendingNeeded(unsigned char b) { return b != 0; }
|
||||
inline bool blendingNeeded(unsigned char b)
|
||||
{
|
||||
static_assert(BLEND_NONE == 0);
|
||||
return b != 0;
|
||||
}
|
||||
|
||||
template <RotationDegree rotDeg> inline
|
||||
unsigned char rotateBlendInfo(unsigned char b) { return b; }
|
||||
@@ -358,20 +376,12 @@ template <> inline unsigned char rotateBlendInfo<ROT_180>(unsigned char b) { ret
|
||||
template <> inline unsigned char rotateBlendInfo<ROT_270>(unsigned char b) { return ((b << 6) | (b >> 2)) & 0xff; }
|
||||
|
||||
|
||||
#if defined _MSC_VER && !defined NDEBUG
|
||||
int debugPixelX = -1;
|
||||
int debugPixelY = 12;
|
||||
__declspec(thread) bool breakIntoDebugger = false;
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
input kernel area naming convention:
|
||||
/* input kernel area naming convention:
|
||||
-------------
|
||||
| A | B | C |
|
||||
----|---|---|
|
||||
| D | E | F | //input pixel is at position E
|
||||
----|---|---|
|
||||
|---|---|---|
|
||||
| D | E | F | input pixel is at position E
|
||||
|---|---|---|
|
||||
| G | H | I |
|
||||
-------------
|
||||
*/
|
||||
@@ -382,7 +392,7 @@ void blendPixel(const Kernel_3x3& ker,
|
||||
unsigned char blendInfo, //result of preprocessing all four corners of pixel "e"
|
||||
const xbrz::ScalerCfg& cfg)
|
||||
{
|
||||
#define a get_a<rotDeg>(ker)
|
||||
//#define a get_a<rotDeg>(ker)
|
||||
#define b get_b<rotDeg>(ker)
|
||||
#define c get_c<rotDeg>(ker)
|
||||
#define d get_d<rotDeg>(ker)
|
||||
@@ -410,7 +420,7 @@ void blendPixel(const Kernel_3x3& ker,
|
||||
return true;
|
||||
|
||||
//make sure there is no second blending in an adjacent rotation for this pixel: handles insular pixels, mario eyes
|
||||
if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90� corners
|
||||
if (getTopR(blend) != BLEND_NONE && !eq(e, g)) //but support double-blending for 90° corners
|
||||
return false;
|
||||
if (getBottomL(blend) != BLEND_NONE && !eq(e, c))
|
||||
return false;
|
||||
@@ -453,7 +463,7 @@ void blendPixel(const Kernel_3x3& ker,
|
||||
Scaler::blendCorner(px, out);
|
||||
}
|
||||
|
||||
#undef a
|
||||
//#undef a
|
||||
#undef b
|
||||
#undef c
|
||||
#undef d
|
||||
@@ -465,7 +475,72 @@ void blendPixel(const Kernel_3x3& ker,
|
||||
}
|
||||
|
||||
|
||||
template <class Scaler, class ColorDistance> //scaler policy: see "Scaler2x" reference implementation
|
||||
class OobReaderTransparent
|
||||
{
|
||||
public:
|
||||
OobReaderTransparent(const uint32_t* src, int srcWidth, int srcHeight, int y) :
|
||||
s_m1(0 <= y - 1 && y - 1 < srcHeight ? src + srcWidth * (y - 1) : nullptr),
|
||||
s_0 (0 <= y && y < srcHeight ? src + srcWidth * y : nullptr),
|
||||
s_p1(0 <= y + 1 && y + 1 < srcHeight ? src + srcWidth * (y + 1) : nullptr),
|
||||
s_p2(0 <= y + 2 && y + 2 < srcHeight ? src + srcWidth * (y + 2) : nullptr),
|
||||
srcWidth_(srcWidth) {}
|
||||
|
||||
void readDhlp(Kernel_4x4& ker, int x) const //(x, y) is at kernel position F
|
||||
{
|
||||
[[likely]] if (const int x_p2 = x + 2; 0 <= x_p2 && x_p2 < srcWidth_)
|
||||
{
|
||||
ker.d = s_m1 ? s_m1[x_p2] : 0;
|
||||
ker.h = s_0 ? s_0 [x_p2] : 0;
|
||||
ker.l = s_p1 ? s_p1[x_p2] : 0;
|
||||
ker.p = s_p2 ? s_p2[x_p2] : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ker.d = 0;
|
||||
ker.h = 0;
|
||||
ker.l = 0;
|
||||
ker.p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t* const s_m1;
|
||||
const uint32_t* const s_0;
|
||||
const uint32_t* const s_p1;
|
||||
const uint32_t* const s_p2;
|
||||
const int srcWidth_;
|
||||
};
|
||||
|
||||
|
||||
class OobReaderDuplicate
|
||||
{
|
||||
public:
|
||||
OobReaderDuplicate(const uint32_t* src, int srcWidth, int srcHeight, int y) :
|
||||
s_m1(src + srcWidth * std::clamp(y - 1, 0, srcHeight - 1)),
|
||||
s_0 (src + srcWidth * std::clamp(y, 0, srcHeight - 1)),
|
||||
s_p1(src + srcWidth * std::clamp(y + 1, 0, srcHeight - 1)),
|
||||
s_p2(src + srcWidth * std::clamp(y + 2, 0, srcHeight - 1)),
|
||||
srcWidth_(srcWidth) {}
|
||||
|
||||
void readDhlp(Kernel_4x4& ker, int x) const //(x, y) is at kernel position F
|
||||
{
|
||||
const int x_p2 = std::clamp(x + 2, 0, srcWidth_ - 1);
|
||||
ker.d = s_m1[x_p2];
|
||||
ker.h = s_0 [x_p2];
|
||||
ker.l = s_p1[x_p2];
|
||||
ker.p = s_p2[x_p2];
|
||||
}
|
||||
|
||||
private:
|
||||
const uint32_t* const s_m1;
|
||||
const uint32_t* const s_0;
|
||||
const uint32_t* const s_p1;
|
||||
const uint32_t* const s_p2;
|
||||
const int srcWidth_;
|
||||
};
|
||||
|
||||
|
||||
template <class Scaler, class ColorDistance, class OobReader> //scaler policy: see "Scaler2x" reference implementation
|
||||
void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
|
||||
{
|
||||
yFirst = std::max(yFirst, 0);
|
||||
@@ -475,64 +550,72 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
|
||||
|
||||
const int trgWidth = srcWidth * Scaler::scale;
|
||||
|
||||
//"use" space at the end of the image as temporary buffer for "on the fly preprocessing": we even could use larger area of
|
||||
//"sizeof(uint32_t) * srcWidth * (yLast - yFirst)" bytes without risk of accidental overwriting before accessing
|
||||
const int bufferSize = srcWidth;
|
||||
unsigned char* preProcBuffer = reinterpret_cast<unsigned char*>(trg + yLast * Scaler::scale * trgWidth) - bufferSize;
|
||||
std::fill(preProcBuffer, preProcBuffer + bufferSize, '\0');
|
||||
static_assert(BLEND_NONE == 0, "");
|
||||
//(ab)use space of "sizeof(uint32_t) * srcWidth * Scaler::scale" at the end of the image as temporary
|
||||
//buffer for "on the fly preprocessing" without risk of accidental overwriting before accessing
|
||||
unsigned char* const preProcBuf = reinterpret_cast<unsigned char*>(trg + yLast * Scaler::scale * trgWidth) - srcWidth;
|
||||
|
||||
//initialize preprocessing buffer for first row of current stripe: detect upper left and right corner blending
|
||||
//this cannot be optimized for adjacent processing stripes; we must not allow for a memory race condition!
|
||||
if (yFirst > 0)
|
||||
{
|
||||
const int y = yFirst - 1;
|
||||
const OobReader oobReader(src, srcWidth, srcHeight, yFirst - 1);
|
||||
|
||||
const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0);
|
||||
const uint32_t* s_0 = src + srcWidth * y; //center line
|
||||
const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1);
|
||||
const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1);
|
||||
//initialize at position x = -1
|
||||
Kernel_4x4 ker4 = {};
|
||||
oobReader.readDhlp(ker4, -4); //hack: read a, e, i, m at x = -1
|
||||
ker4.a = ker4.d;
|
||||
ker4.e = ker4.h;
|
||||
ker4.i = ker4.l;
|
||||
ker4.m = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -3);
|
||||
ker4.b = ker4.d;
|
||||
ker4.f = ker4.h;
|
||||
ker4.j = ker4.l;
|
||||
ker4.n = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -2);
|
||||
ker4.c = ker4.d;
|
||||
ker4.g = ker4.h;
|
||||
ker4.k = ker4.l;
|
||||
ker4.o = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -1);
|
||||
|
||||
{
|
||||
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
|
||||
clearAddTopL(preProcBuf[0], res.blend_k); //set 1st known corner for (0, yFirst)
|
||||
}
|
||||
|
||||
for (int x = 0; x < srcWidth; ++x)
|
||||
{
|
||||
const int x_m1 = std::max(x - 1, 0);
|
||||
const int x_p1 = std::min(x + 1, srcWidth - 1);
|
||||
const int x_p2 = std::min(x + 2, srcWidth - 1);
|
||||
ker4.a = ker4.b; //shift previous kernel to the left
|
||||
ker4.e = ker4.f; // -----------------
|
||||
ker4.i = ker4.j; // | A | B | C | D |
|
||||
ker4.m = ker4.n; // |---|---|---|---|
|
||||
/**/ // | E | F | G | H | (x, yFirst - 1) is at position F
|
||||
ker4.b = ker4.c; // |---|---|---|---|
|
||||
ker4.f = ker4.g; // | I | J | K | L |
|
||||
ker4.j = ker4.k; // |---|---|---|---|
|
||||
ker4.n = ker4.o; // | M | N | O | P |
|
||||
/**/ // -----------------
|
||||
ker4.c = ker4.d;
|
||||
ker4.g = ker4.h;
|
||||
ker4.k = ker4.l;
|
||||
ker4.o = ker4.p;
|
||||
|
||||
Kernel_4x4 ker = {}; //perf: initialization is negligible
|
||||
ker.a = s_m1[x_m1]; //read sequentially from memory as far as possible
|
||||
ker.b = s_m1[x];
|
||||
ker.c = s_m1[x_p1];
|
||||
ker.d = s_m1[x_p2];
|
||||
oobReader.readDhlp(ker4, x);
|
||||
|
||||
ker.e = s_0[x_m1];
|
||||
ker.f = s_0[x];
|
||||
ker.g = s_0[x_p1];
|
||||
ker.h = s_0[x_p2];
|
||||
/* preprocessing blend result:
|
||||
---------
|
||||
| F | G | evaluate corner between F, G, J, K
|
||||
|---+---| current input pixel is at position F
|
||||
| J | K |
|
||||
--------- */
|
||||
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
|
||||
addTopR(preProcBuf[x], res.blend_j); //set 2nd known corner for (x, yFirst)
|
||||
|
||||
ker.i = s_p1[x_m1];
|
||||
ker.j = s_p1[x];
|
||||
ker.k = s_p1[x_p1];
|
||||
ker.l = s_p1[x_p2];
|
||||
|
||||
ker.m = s_p2[x_m1];
|
||||
ker.n = s_p2[x];
|
||||
ker.o = s_p2[x_p1];
|
||||
ker.p = s_p2[x_p2];
|
||||
|
||||
const BlendResult res = preProcessCorners<ColorDistance>(ker, cfg);
|
||||
/*
|
||||
preprocessing blend result:
|
||||
---------
|
||||
| F | G | //evalute corner between F, G, J, K
|
||||
----|---| //input pixel is at position F
|
||||
| J | K |
|
||||
---------
|
||||
*/
|
||||
setTopR(preProcBuffer[x], res.blend_j);
|
||||
|
||||
if (x + 1 < bufferSize)
|
||||
setTopL(preProcBuffer[x + 1], res.blend_k);
|
||||
if (x + 1 < srcWidth)
|
||||
clearAddTopL(preProcBuf[x + 1], res.blend_k); //set 1st known corner for (x + 1, yFirst)
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------------
|
||||
@@ -541,91 +624,92 @@ void scaleImage(const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
|
||||
{
|
||||
uint32_t* out = trg + Scaler::scale * y * trgWidth; //consider MT "striped" access
|
||||
|
||||
const uint32_t* s_m1 = src + srcWidth * std::max(y - 1, 0);
|
||||
const uint32_t* s_0 = src + srcWidth * y; //center line
|
||||
const uint32_t* s_p1 = src + srcWidth * std::min(y + 1, srcHeight - 1);
|
||||
const uint32_t* s_p2 = src + srcWidth * std::min(y + 2, srcHeight - 1);
|
||||
const OobReader oobReader(src, srcWidth, srcHeight, y);
|
||||
|
||||
//initialize at position x = -1
|
||||
Kernel_4x4 ker4 = {};
|
||||
oobReader.readDhlp(ker4, -4); //hack: read a, e, i, m at x = -1
|
||||
ker4.a = ker4.d;
|
||||
ker4.e = ker4.h;
|
||||
ker4.i = ker4.l;
|
||||
ker4.m = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -3);
|
||||
ker4.b = ker4.d;
|
||||
ker4.f = ker4.h;
|
||||
ker4.j = ker4.l;
|
||||
ker4.n = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -2);
|
||||
ker4.c = ker4.d;
|
||||
ker4.g = ker4.h;
|
||||
ker4.k = ker4.l;
|
||||
ker4.o = ker4.p;
|
||||
|
||||
oobReader.readDhlp(ker4, -1);
|
||||
|
||||
unsigned char blend_xy1 = 0; //corner blending for current (x, y + 1) position
|
||||
{
|
||||
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
|
||||
clearAddTopL(blend_xy1, res.blend_k); //set 1st known corner for (0, y + 1) and buffer for use on next column
|
||||
|
||||
addBottomL(preProcBuf[0], res.blend_g); //set 3rd known corner for (0, y)
|
||||
}
|
||||
|
||||
for (int x = 0; x < srcWidth; ++x, out += Scaler::scale)
|
||||
{
|
||||
#if defined _MSC_VER && !defined NDEBUG
|
||||
breakIntoDebugger = debugPixelX == x && debugPixelY == y;
|
||||
#endif
|
||||
//all those bounds checks have only insignificant impact on performance!
|
||||
const int x_m1 = std::max(x - 1, 0); //perf: prefer array indexing to additional pointers!
|
||||
const int x_p1 = std::min(x + 1, srcWidth - 1);
|
||||
const int x_p2 = std::min(x + 2, srcWidth - 1);
|
||||
ker4.a = ker4.b; //shift previous kernel to the left
|
||||
ker4.e = ker4.f; // -----------------
|
||||
ker4.i = ker4.j; // | A | B | C | D |
|
||||
ker4.m = ker4.n; // |---|---|---|---|
|
||||
/**/ // | E | F | G | H | (x, y) is at position F
|
||||
ker4.b = ker4.c; // |---|---|---|---|
|
||||
ker4.f = ker4.g; // | I | J | K | L |
|
||||
ker4.j = ker4.k; // |---|---|---|---|
|
||||
ker4.n = ker4.o; // | M | N | O | P |
|
||||
/**/ // -----------------
|
||||
ker4.c = ker4.d;
|
||||
ker4.g = ker4.h;
|
||||
ker4.k = ker4.l;
|
||||
ker4.o = ker4.p;
|
||||
|
||||
Kernel_4x4 ker4 = {}; //perf: initialization is negligible
|
||||
|
||||
ker4.a = s_m1[x_m1]; //read sequentially from memory as far as possible
|
||||
ker4.b = s_m1[x];
|
||||
ker4.c = s_m1[x_p1];
|
||||
ker4.d = s_m1[x_p2];
|
||||
|
||||
ker4.e = s_0[x_m1];
|
||||
ker4.f = s_0[x];
|
||||
ker4.g = s_0[x_p1];
|
||||
ker4.h = s_0[x_p2];
|
||||
|
||||
ker4.i = s_p1[x_m1];
|
||||
ker4.j = s_p1[x];
|
||||
ker4.k = s_p1[x_p1];
|
||||
ker4.l = s_p1[x_p2];
|
||||
|
||||
ker4.m = s_p2[x_m1];
|
||||
ker4.n = s_p2[x];
|
||||
ker4.o = s_p2[x_p1];
|
||||
ker4.p = s_p2[x_p2];
|
||||
oobReader.readDhlp(ker4, x);
|
||||
|
||||
//evaluate the four corners on bottom-right of current pixel
|
||||
unsigned char blend_xy = 0; //for current (x, y) position
|
||||
unsigned char blend_xy = preProcBuf[x]; //for current (x, y) position
|
||||
{
|
||||
/* preprocessing blend result:
|
||||
---------
|
||||
| F | G | evaluate corner between F, G, J, K
|
||||
|---+---| current input pixel is at position F
|
||||
| J | K |
|
||||
--------- */
|
||||
const BlendResult res = preProcessCorners<ColorDistance>(ker4, cfg);
|
||||
/*
|
||||
preprocessing blend result:
|
||||
---------
|
||||
| F | G | //evalute corner between F, G, J, K
|
||||
----|---| //current input pixel is at position F
|
||||
| J | K |
|
||||
---------
|
||||
*/
|
||||
blend_xy = preProcBuffer[x];
|
||||
setBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence!
|
||||
addBottomR(blend_xy, res.blend_f); //all four corners of (x, y) have been determined at this point due to processing sequence!
|
||||
|
||||
setTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1)
|
||||
preProcBuffer[x] = blend_xy1; //store on current buffer position for use on next row
|
||||
addTopR(blend_xy1, res.blend_j); //set 2nd known corner for (x, y + 1)
|
||||
preProcBuf[x] = blend_xy1; //store on current buffer position for use on next row
|
||||
|
||||
blend_xy1 = 0;
|
||||
setTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column
|
||||
[[likely]] if (x + 1 < srcWidth)
|
||||
{
|
||||
//blend_xy1 -> blend_x1y1
|
||||
clearAddTopL(blend_xy1, res.blend_k); //set 1st known corner for (x + 1, y + 1) and buffer for use on next column
|
||||
|
||||
if (x + 1 < bufferSize) //set 3rd known corner for (x + 1, y)
|
||||
setBottomL(preProcBuffer[x + 1], res.blend_g);
|
||||
addBottomL(preProcBuf[x + 1], res.blend_g); //set 3rd known corner for (x + 1, y)
|
||||
}
|
||||
}
|
||||
|
||||
//fill block of size scale * scale with the given color
|
||||
fillBlock(out, trgWidth * sizeof(uint32_t), ker4.f, Scaler::scale, Scaler::scale);
|
||||
//place *after* preprocessing step, to not overwrite the results while processing the the last pixel!
|
||||
//place *after* preprocessing step, to not overwrite the results while processing the last pixel!
|
||||
|
||||
//blend four corners of current pixel
|
||||
if (blendingNeeded(blend_xy)) //good 5% perf-improvement
|
||||
//blend all four corners of current pixel
|
||||
if (blendingNeeded(blend_xy))
|
||||
{
|
||||
Kernel_3x3 ker3 = {}; //perf: initialization is negligible
|
||||
|
||||
ker3.a = ker4.a;
|
||||
ker3.b = ker4.b;
|
||||
ker3.c = ker4.c;
|
||||
|
||||
ker3.d = ker4.e;
|
||||
ker3.e = ker4.f;
|
||||
ker3.f = ker4.g;
|
||||
|
||||
ker3.g = ker4.i;
|
||||
ker3.h = ker4.j;
|
||||
ker3.i = ker4.k;
|
||||
|
||||
const auto& ker3 = reinterpret_cast<const Kernel_3x3&>(ker4); //"The Things We Do for Perf"
|
||||
blendPixel<Scaler, ColorDistance, ROT_0 >(ker3, out, trgWidth, blend_xy, cfg);
|
||||
blendPixel<Scaler, ColorDistance, ROT_90 >(ker3, out, trgWidth, blend_xy, cfg);
|
||||
blendPixel<Scaler, ColorDistance, ROT_180>(ker3, out, trgWidth, blend_xy, cfg);
|
||||
@@ -1037,6 +1121,22 @@ struct ColorDistanceARGB
|
||||
};
|
||||
|
||||
|
||||
struct ColorDistanceUnbufferedARGB
|
||||
{
|
||||
static double dist(uint32_t pix1, uint32_t pix2, double luminanceWeight)
|
||||
{
|
||||
const double a1 = getAlpha(pix1) / 255.0 ;
|
||||
const double a2 = getAlpha(pix2) / 255.0 ;
|
||||
|
||||
const double d = distYCbCr(pix1, pix2, luminanceWeight);
|
||||
if (a1 < a2)
|
||||
return a1 * d + 255 * (a2 - a1);
|
||||
else
|
||||
return a2 * d + 255 * (a1 - a2);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct ColorGradientRGB
|
||||
{
|
||||
template <unsigned int M, unsigned int N>
|
||||
@@ -1059,37 +1159,60 @@ struct ColorGradientARGB
|
||||
|
||||
void xbrz::scale(size_t factor, const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight, ColorFormat colFmt, const xbrz::ScalerCfg& cfg, int yFirst, int yLast)
|
||||
{
|
||||
if (factor == 1)
|
||||
{
|
||||
std::copy(src + yFirst * srcWidth, src + yLast * srcWidth, trg);
|
||||
return;
|
||||
}
|
||||
|
||||
static_assert(SCALE_FACTOR_MAX == 6);
|
||||
switch (colFmt)
|
||||
{
|
||||
case ColorFormat::ARGB:
|
||||
switch (factor)
|
||||
{
|
||||
case 2:
|
||||
return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 3:
|
||||
return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 4:
|
||||
return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 5:
|
||||
return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 6:
|
||||
return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceARGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
}
|
||||
break;
|
||||
|
||||
case ColorFormat::RGB:
|
||||
switch (factor)
|
||||
{
|
||||
case 2:
|
||||
return scaleImage<Scaler2x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
return scaleImage<Scaler2x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 3:
|
||||
return scaleImage<Scaler3x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
return scaleImage<Scaler3x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 4:
|
||||
return scaleImage<Scaler4x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
return scaleImage<Scaler4x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 5:
|
||||
return scaleImage<Scaler5x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
return scaleImage<Scaler5x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 6:
|
||||
return scaleImage<Scaler6x<ColorGradientRGB>, ColorDistanceRGB>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
return scaleImage<Scaler6x<ColorGradientRGB>, ColorDistanceRGB, OobReaderDuplicate>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
}
|
||||
break;
|
||||
|
||||
case ColorFormat::ARGB:
|
||||
switch (factor)
|
||||
{
|
||||
case 2:
|
||||
return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 3:
|
||||
return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 4:
|
||||
return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 5:
|
||||
return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 6:
|
||||
return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
}
|
||||
break;
|
||||
|
||||
case ColorFormat::ARGB_UNBUFFERED:
|
||||
switch (factor)
|
||||
{
|
||||
case 2:
|
||||
return scaleImage<Scaler2x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 3:
|
||||
return scaleImage<Scaler3x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 4:
|
||||
return scaleImage<Scaler4x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 5:
|
||||
return scaleImage<Scaler5x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
case 6:
|
||||
return scaleImage<Scaler6x<ColorGradientARGB>, ColorDistanceUnbufferedARGB, OobReaderTransparent>(src, trg, srcWidth, srcHeight, cfg, yFirst, yLast);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1101,21 +1224,133 @@ bool xbrz::equalColorTest(uint32_t col1, uint32_t col2, ColorFormat colFmt, doub
|
||||
{
|
||||
switch (colFmt)
|
||||
{
|
||||
case ColorFormat::ARGB:
|
||||
return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
|
||||
|
||||
case ColorFormat::RGB:
|
||||
return ColorDistanceRGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
|
||||
case ColorFormat::ARGB:
|
||||
return ColorDistanceARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
|
||||
case ColorFormat::ARGB_UNBUFFERED:
|
||||
return ColorDistanceUnbufferedARGB::dist(col1, col2, luminanceWeight) < equalColorTolerance;
|
||||
}
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void xbrz::bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight)
|
||||
{
|
||||
bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
|
||||
trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
|
||||
0, trgHeight, [](uint32_t pix) { return pix; });
|
||||
}
|
||||
|
||||
|
||||
void xbrz::nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
|
||||
uint32_t* trg, int trgWidth, int trgHeight)
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight)
|
||||
{
|
||||
nearestNeighborScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
|
||||
trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
|
||||
SliceType::TARGET, 0, trgHeight, [](uint32_t pix) { return pix; });
|
||||
0, trgHeight, [](uint32_t pix) { return pix; });
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
//#include <ppl.h>
|
||||
void bilinearScaleCpu(const uint32_t* src, int srcWidth, int srcHeight,
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight)
|
||||
{
|
||||
const int TASK_GRANULARITY = 16;
|
||||
|
||||
concurrency::task_group tg;
|
||||
|
||||
for (int i = 0; i < trgHeight; i += TASK_GRANULARITY)
|
||||
tg.run([=]
|
||||
{
|
||||
const int iLast = std::min(i + TASK_GRANULARITY, trgHeight);
|
||||
xbrz::bilinearScale(src, srcWidth, srcHeight, srcWidth * sizeof(uint32_t),
|
||||
trg, trgWidth, trgHeight, trgWidth * sizeof(uint32_t),
|
||||
i, iLast, [](uint32_t pix) { return pix; });
|
||||
});
|
||||
tg.wait();
|
||||
}
|
||||
|
||||
|
||||
//Perf: AMP vs CPU: merely ~10% shorter runtime (scaling 1280x800 -> 1920x1080)
|
||||
//#include <amp.h>
|
||||
void bilinearScaleAmp(const uint32_t* src, int srcWidth, int srcHeight, //throw concurrency::runtime_exception
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight)
|
||||
{
|
||||
//C++ AMP reference: https://msdn.microsoft.com/en-us/library/hh289390.aspx
|
||||
//introduction to C++ AMP: https://msdn.microsoft.com/en-us/magazine/hh882446.aspx
|
||||
using namespace concurrency;
|
||||
//TODO: pitch
|
||||
|
||||
if (srcHeight <= 0 || srcWidth <= 0) return;
|
||||
|
||||
const float scaleX = static_cast<float>(trgWidth ) / srcWidth;
|
||||
const float scaleY = static_cast<float>(trgHeight) / srcHeight;
|
||||
|
||||
array_view<const uint32_t, 2> srcView(srcHeight, srcWidth, src);
|
||||
array_view< uint32_t, 2> trgView(trgHeight, trgWidth, trg);
|
||||
trgView.discard_data();
|
||||
|
||||
parallel_for_each(trgView.extent, [=](index<2> idx) restrict(amp) //throw ?
|
||||
{
|
||||
const int y = idx[0];
|
||||
const int x = idx[1];
|
||||
//Perf notes:
|
||||
// -> float-based calculation is (almost) 2x as fas as double!
|
||||
// -> no noticeable improvement via tiling: https://msdn.microsoft.com/en-us/magazine/hh882447.aspx
|
||||
// -> no noticeable improvement with restrict(amp,cpu)
|
||||
// -> iterating over y-axis only is significantly slower!
|
||||
// -> pre-calculating x,y-dependent variables in a buffer + array_view<> is ~ 20 % slower!
|
||||
const int y1 = srcHeight * y / trgHeight;
|
||||
int y2 = y1 + 1;
|
||||
if (y2 == srcHeight) --y2;
|
||||
|
||||
const float yy1 = y / scaleY - y1;
|
||||
const float y2y = 1 - yy1;
|
||||
//-------------------------------------
|
||||
const int x1 = srcWidth * x / trgWidth;
|
||||
int x2 = x1 + 1;
|
||||
if (x2 == srcWidth) --x2;
|
||||
|
||||
const float xx1 = x / scaleX - x1;
|
||||
const float x2x = 1 - xx1;
|
||||
//-------------------------------------
|
||||
const float x2xy2y = x2x * y2y;
|
||||
const float xx1y2y = xx1 * y2y;
|
||||
const float x2xyy1 = x2x * yy1;
|
||||
const float xx1yy1 = xx1 * yy1;
|
||||
|
||||
auto interpolate = [=](int offset)
|
||||
{
|
||||
/*
|
||||
https://en.wikipedia.org/wiki/Bilinear_interpolation
|
||||
(c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
|
||||
(c12(x2 - x) + c22(x - x1)) * (y - y1)
|
||||
*/
|
||||
const auto c11 = (srcView(y1, x1) >> (8 * offset)) & 0xff;
|
||||
const auto c21 = (srcView(y1, x2) >> (8 * offset)) & 0xff;
|
||||
const auto c12 = (srcView(y2, x1) >> (8 * offset)) & 0xff;
|
||||
const auto c22 = (srcView(y2, x2) >> (8 * offset)) & 0xff;
|
||||
|
||||
return c11 * x2xy2y + c21 * xx1y2y +
|
||||
c12 * x2xyy1 + c22 * xx1yy1;
|
||||
};
|
||||
|
||||
const float bi = interpolate(0);
|
||||
const float gi = interpolate(1);
|
||||
const float ri = interpolate(2);
|
||||
const float ai = interpolate(3);
|
||||
|
||||
const auto b = static_cast<uint32_t>(bi + 0.5f);
|
||||
const auto g = static_cast<uint32_t>(gi + 0.5f);
|
||||
const auto r = static_cast<uint32_t>(ri + 0.5f);
|
||||
const auto a = static_cast<uint32_t>(ai + 0.5f);
|
||||
|
||||
trgView(y, x) = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
});
|
||||
trgView.synchronize(); //throw ?
|
||||
}
|
||||
#endif
|
||||
+13
-11
@@ -1,13 +1,14 @@
|
||||
// ****************************************************************************
|
||||
// * This file is part of the HqMAME project. It is distributed under *
|
||||
// * This file is part of the xBRZ project. It is distributed under *
|
||||
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
|
||||
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
|
||||
// * *
|
||||
// * Additionally and as a special exception, the author gives permission *
|
||||
// * to link the code of this program with the MAME library (or with modified *
|
||||
// * versions of MAME that use the same license as MAME), and distribute *
|
||||
// * linked combinations including the two. You must obey the GNU General *
|
||||
// * Public License in all respects for all of the code used other than MAME. *
|
||||
// * to link the code of this program with the following libraries *
|
||||
// * (or with modified versions that use the same licenses), and distribute *
|
||||
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
|
||||
// * You must obey the GNU General Public License in all respects for all of *
|
||||
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
|
||||
// * If you modify this file, you may extend this exception to your version *
|
||||
// * of the file, but you are not obligated to do so. If you do not wish to *
|
||||
// * do so, delete this exception statement from your version. *
|
||||
@@ -21,10 +22,6 @@
|
||||
#include <limits>
|
||||
#include "xbrz_config.h"
|
||||
|
||||
// Guard against msvc stupidity
|
||||
#ifdef max
|
||||
#undef max
|
||||
#endif
|
||||
|
||||
namespace xbrz
|
||||
{
|
||||
@@ -47,11 +44,13 @@ enum class ColorFormat //from high bits -> low bits, 8 bit per channel
|
||||
{
|
||||
RGB, //8 bit for each red, green, blue, upper 8 bits unused
|
||||
ARGB, //including alpha channel, BGRA byte order on little-endian machines
|
||||
ARGB_UNBUFFERED, //like ARGB, but without the one-time buffer creation overhead (ca. 100 - 300 ms) at the expense of a slightly slower scaling time
|
||||
};
|
||||
|
||||
const int SCALE_FACTOR_MAX = 6;
|
||||
|
||||
/*
|
||||
-> map source (srcWidth * srcHeight) to target (scale * width x scale * height) image, optionally processing a half-open slice of rows [yFirst, yLast) only
|
||||
-> support for source/target pitch in bytes!
|
||||
-> if your emulator changes only a few image slices during each cycle (e.g. DOSBox) then there's no need to run xBRZ on the complete image:
|
||||
Just make sure you enlarge the source image slice by 2 rows on top and 2 on bottom (this is the additional range the xBRZ algorithm is using during analysis)
|
||||
CAVEAT: If there are multiple changed slices, make sure they do not overlap after adding these additional rows in order to avoid a memory race condition
|
||||
@@ -60,12 +59,15 @@ enum class ColorFormat //from high bits -> low bits, 8 bit per channel
|
||||
THREAD-SAFETY: - parts of the same image may be scaled by multiple threads as long as the [yFirst, yLast) ranges do not overlap!
|
||||
- there is a minor inefficiency for the first row of a slice, so avoid processing single rows only; suggestion: process at least 8-16 rows
|
||||
*/
|
||||
void scale(size_t factor, //valid range: 2 - 6
|
||||
void scale(size_t factor, //valid range: 2 - SCALE_FACTOR_MAX
|
||||
const uint32_t* src, uint32_t* trg, int srcWidth, int srcHeight,
|
||||
ColorFormat colFmt,
|
||||
const ScalerCfg& cfg = ScalerCfg(),
|
||||
int yFirst = 0, int yLast = std::numeric_limits<int>::max()); //slice of source image
|
||||
|
||||
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight,
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight);
|
||||
|
||||
void nearestNeighborScale(const uint32_t* src, int srcWidth, int srcHeight,
|
||||
/**/ uint32_t* trg, int trgWidth, int trgHeight);
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
// ****************************************************************************
|
||||
// * This file is part of the HqMAME project. It is distributed under *
|
||||
// * This file is part of the xBRZ project. It is distributed under *
|
||||
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
|
||||
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
|
||||
// * *
|
||||
// * Additionally and as a special exception, the author gives permission *
|
||||
// * to link the code of this program with the MAME library (or with modified *
|
||||
// * versions of MAME that use the same license as MAME), and distribute *
|
||||
// * linked combinations including the two. You must obey the GNU General *
|
||||
// * Public License in all respects for all of the code used other than MAME. *
|
||||
// * to link the code of this program with the following libraries *
|
||||
// * (or with modified versions that use the same licenses), and distribute *
|
||||
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
|
||||
// * You must obey the GNU General Public License in all respects for all of *
|
||||
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
|
||||
// * If you modify this file, you may extend this exception to your version *
|
||||
// * of the file, but you are not obligated to do so. If you do not wish to *
|
||||
// * do so, delete this exception statement from your version. *
|
||||
@@ -24,6 +25,7 @@ struct ScalerCfg
|
||||
{
|
||||
double luminanceWeight = 1;
|
||||
double equalColorTolerance = 30;
|
||||
double centerDirectionBias = 4;
|
||||
double dominantDirectionThreshold = 3.6;
|
||||
double steepDirectionThreshold = 2.2;
|
||||
double newTestAttribute = 0; //unused; test new parameters
|
||||
|
||||
+182
-71
@@ -1,13 +1,14 @@
|
||||
// ****************************************************************************
|
||||
// * This file is part of the HqMAME project. It is distributed under *
|
||||
// * This file is part of the xBRZ project. It is distributed under *
|
||||
// * GNU General Public License: https://www.gnu.org/licenses/gpl-3.0 *
|
||||
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
|
||||
// * *
|
||||
// * Additionally and as a special exception, the author gives permission *
|
||||
// * to link the code of this program with the MAME library (or with modified *
|
||||
// * versions of MAME that use the same license as MAME), and distribute *
|
||||
// * linked combinations including the two. You must obey the GNU General *
|
||||
// * Public License in all respects for all of the code used other than MAME. *
|
||||
// * to link the code of this program with the following libraries *
|
||||
// * (or with modified versions that use the same licenses), and distribute *
|
||||
// * linked combinations including the two: MAME, FreeFileSync, Snes9x, ePSXe *
|
||||
// * You must obey the GNU General Public License in all respects for all of *
|
||||
// * the code used other than MAME, FreeFileSync, Snes9x, ePSXe. *
|
||||
// * If you modify this file, you may extend this exception to your version *
|
||||
// * of the file, but you are not obligated to do so. If you do not wish to *
|
||||
// * do so, delete this exception statement from your version. *
|
||||
@@ -25,14 +26,14 @@ namespace xbrz
|
||||
{
|
||||
template <uint32_t N> inline
|
||||
unsigned char getByte(uint32_t val) { return static_cast<unsigned char>((val >> (8 * N)) & 0xff); }
|
||||
// Note that we use RGBA, not ARGB
|
||||
inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); }
|
||||
inline unsigned char getRed (uint32_t pix) { return getByte<0>(pix); }
|
||||
inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); }
|
||||
inline unsigned char getBlue (uint32_t pix) { return getByte<2>(pix); }
|
||||
|
||||
inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (b << 16) | (g << 8) | r; }
|
||||
inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return 0xFF000000 | (b << 16) | (g << 8) | r; }
|
||||
inline unsigned char getAlpha(uint32_t pix) { return getByte<3>(pix); }
|
||||
inline unsigned char getRed (uint32_t pix) { return getByte<2>(pix); }
|
||||
inline unsigned char getGreen(uint32_t pix) { return getByte<1>(pix); }
|
||||
inline unsigned char getBlue (uint32_t pix) { return getByte<0>(pix); }
|
||||
|
||||
inline uint32_t makePixel(unsigned char a, unsigned char r, unsigned char g, unsigned char b) { return (a << 24) | (r << 16) | (g << 8) | b; }
|
||||
inline uint32_t makePixel( unsigned char r, unsigned char g, unsigned char b) { return (r << 16) | (g << 8) | b; }
|
||||
|
||||
inline uint32_t rgb555to888(uint16_t pix) { return ((pix & 0x7C00) << 9) | ((pix & 0x03E0) << 6) | ((pix & 0x001F) << 3); }
|
||||
inline uint32_t rgb565to888(uint16_t pix) { return ((pix & 0xF800) << 8) | ((pix & 0x07E0) << 5) | ((pix & 0x001F) << 3); }
|
||||
@@ -55,7 +56,7 @@ Pix* byteAdvance(Pix* ptr, int bytes)
|
||||
|
||||
//fill block with the given color
|
||||
template <class Pix> inline
|
||||
void fillBlock(Pix* trg, int pitch, Pix col, int blockWidth, int blockHeight)
|
||||
void fillBlock(Pix* trg, int pitch /*[bytes]*/, Pix col, int blockWidth, int blockHeight)
|
||||
{
|
||||
//for (int y = 0; y < blockHeight; ++y, trg = byteAdvance(trg, pitch))
|
||||
// std::fill(trg, trg + blockWidth, col);
|
||||
@@ -66,16 +67,11 @@ void fillBlock(Pix* trg, int pitch, Pix col, int blockWidth, int blockHeight)
|
||||
}
|
||||
|
||||
|
||||
enum class SliceType
|
||||
{
|
||||
SOURCE,
|
||||
TARGET,
|
||||
};
|
||||
|
||||
//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
|
||||
template <class PixSrc, class PixTrg, class PixConverter>
|
||||
void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch,
|
||||
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
|
||||
SliceType st, int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
|
||||
void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
|
||||
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
|
||||
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
|
||||
{
|
||||
static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
|
||||
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
|
||||
@@ -89,65 +85,180 @@ void nearestNeighborScale(const PixSrc* src, int srcWidth, int srcHeight, int sr
|
||||
return;
|
||||
}
|
||||
|
||||
switch (st)
|
||||
yFirst = std::max(yFirst, 0);
|
||||
yLast = std::min(yLast, trgHeight);
|
||||
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
|
||||
|
||||
for (int y = yFirst; y < yLast; ++y)
|
||||
{
|
||||
case SliceType::SOURCE:
|
||||
//nearest-neighbor (going over source image - fast for upscaling, since source is read only once
|
||||
yFirst = std::max(yFirst, 0);
|
||||
yLast = std::min(yLast, srcHeight);
|
||||
if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return;
|
||||
const int ySrc = srcHeight * y / trgHeight;
|
||||
const PixSrc* const srcLine = byteAdvance(src, ySrc * srcPitch);
|
||||
PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
|
||||
|
||||
for (int y = yFirst; y < yLast; ++y)
|
||||
for (int x = 0; x < trgWidth; ++x)
|
||||
{
|
||||
const int xSrc = srcWidth * x / trgWidth;
|
||||
trgLine[x] = pixCvrt(srcLine[xSrc]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//nearest-neighbor (going over source image - fast for upscaling, since source is read only once
|
||||
template <class PixSrc, class PixTrg, class PixConverter>
|
||||
void nearestNeighborScaleOverSource(const PixSrc* src, int srcWidth, int srcHeight, int srcPitch /*[bytes]*/,
|
||||
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch /*[bytes]*/,
|
||||
int yFirst, int yLast, PixConverter pixCvrt /*convert PixSrc to PixTrg*/)
|
||||
{
|
||||
static_assert(std::is_integral<PixSrc>::value, "PixSrc* is expected to be cast-able to char*");
|
||||
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
|
||||
|
||||
static_assert(std::is_same<decltype(pixCvrt(PixSrc())), PixTrg>::value, "PixConverter returning wrong pixel format");
|
||||
|
||||
if (srcPitch < srcWidth * static_cast<int>(sizeof(PixSrc)) ||
|
||||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
|
||||
{
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
yFirst = std::max(yFirst, 0);
|
||||
yLast = std::min(yLast, srcHeight);
|
||||
if (yFirst >= yLast || trgWidth <= 0 || trgHeight <= 0) return;
|
||||
|
||||
for (int y = yFirst; y < yLast; ++y)
|
||||
{
|
||||
//mathematically: ySrc = floor(srcHeight * yTrg / trgHeight)
|
||||
// => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight
|
||||
|
||||
//keep within for loop to support MT input slices!
|
||||
const int yTrgFirst = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight)
|
||||
const int yTrgLast = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight)
|
||||
const int blockHeight = yTrgLast - yTrgFirst;
|
||||
|
||||
if (blockHeight > 0)
|
||||
{
|
||||
const PixSrc* srcLine = byteAdvance(src, y * srcPitch);
|
||||
/**/ PixTrg* trgLine = byteAdvance(trg, yTrgFirst * trgPitch);
|
||||
int xTrgFirst = 0;
|
||||
|
||||
for (int x = 0; x < srcWidth; ++x)
|
||||
{
|
||||
//mathematically: ySrc = floor(srcHeight * yTrg / trgHeight)
|
||||
// => search for integers in: [ySrc, ySrc + 1) * trgHeight / srcHeight
|
||||
|
||||
//keep within for loop to support MT input slices!
|
||||
const int yTrg_first = ( y * trgHeight + srcHeight - 1) / srcHeight; //=ceil(y * trgHeight / srcHeight)
|
||||
const int yTrg_last = ((y + 1) * trgHeight + srcHeight - 1) / srcHeight; //=ceil(((y + 1) * trgHeight) / srcHeight)
|
||||
const int blockHeight = yTrg_last - yTrg_first;
|
||||
|
||||
if (blockHeight > 0)
|
||||
const int xTrgLast = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth;
|
||||
const int blockWidth = xTrgLast - xTrgFirst;
|
||||
if (blockWidth > 0)
|
||||
{
|
||||
const PixSrc* srcLine = byteAdvance(src, y * srcPitch);
|
||||
/**/ PixTrg* trgLine = byteAdvance(trg, yTrg_first * trgPitch);
|
||||
int xTrg_first = 0;
|
||||
xTrgFirst = xTrgLast;
|
||||
|
||||
for (int x = 0; x < srcWidth; ++x)
|
||||
{
|
||||
const int xTrg_last = ((x + 1) * trgWidth + srcWidth - 1) / srcWidth;
|
||||
const int blockWidth = xTrg_last - xTrg_first;
|
||||
if (blockWidth > 0)
|
||||
{
|
||||
xTrg_first = xTrg_last;
|
||||
|
||||
const auto trgPix = pixCvrt(srcLine[x]);
|
||||
fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight);
|
||||
trgLine += blockWidth;
|
||||
}
|
||||
}
|
||||
const auto trgPix = pixCvrt(srcLine[x]);
|
||||
fillBlock(trgLine, trgPitch, trgPix, blockWidth, blockHeight);
|
||||
trgLine += blockWidth;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case SliceType::TARGET:
|
||||
//nearest-neighbor (going over target image - slow for upscaling, since source is read multiple times missing out on cache! Fast for similar image sizes!)
|
||||
yFirst = std::max(yFirst, 0);
|
||||
yLast = std::min(yLast, trgHeight);
|
||||
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
|
||||
|
||||
for (int y = yFirst; y < yLast; ++y)
|
||||
template <class PixTrg, class PixConverter>
|
||||
void bilinearScale(const uint32_t* src, int srcWidth, int srcHeight, int srcPitch,
|
||||
/**/ PixTrg* trg, int trgWidth, int trgHeight, int trgPitch,
|
||||
int yFirst, int yLast, PixConverter pixCvrt /*convert uint32_t to PixTrg*/)
|
||||
{
|
||||
static_assert(std::is_integral<PixTrg>::value, "PixTrg* is expected to be cast-able to char*");
|
||||
static_assert(std::is_same<decltype(pixCvrt(uint32_t())), PixTrg>::value, "PixConverter returning wrong pixel format");
|
||||
|
||||
if (srcPitch < srcWidth * static_cast<int>(sizeof(uint32_t)) ||
|
||||
trgPitch < trgWidth * static_cast<int>(sizeof(PixTrg)))
|
||||
{
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
yFirst = std::max(yFirst, 0);
|
||||
yLast = std::min(yLast, trgHeight);
|
||||
if (yFirst >= yLast || srcHeight <= 0 || srcWidth <= 0) return;
|
||||
|
||||
const double scaleX = static_cast<double>(trgWidth ) / srcWidth;
|
||||
const double scaleY = static_cast<double>(trgHeight) / srcHeight;
|
||||
|
||||
//perf notes:
|
||||
// -> double-based calculation is (slightly) faster than float
|
||||
// -> pre-calculation gives significant boost; std::vector<> memory allocation is negligible!
|
||||
struct CoeffsX
|
||||
{
|
||||
int x1 = 0;
|
||||
int x2 = 0;
|
||||
double xx1 = 0;
|
||||
double x2x = 0;
|
||||
};
|
||||
std::vector<CoeffsX> buf(trgWidth);
|
||||
for (int x = 0; x < trgWidth; ++x)
|
||||
{
|
||||
const int x1 = srcWidth * x / trgWidth;
|
||||
int x2 = x1 + 1;
|
||||
if (x2 == srcWidth) --x2;
|
||||
|
||||
const double xx1 = x / scaleX - x1;
|
||||
const double x2x = 1 - xx1;
|
||||
|
||||
buf[x] = { x1, x2, xx1, x2x };
|
||||
}
|
||||
|
||||
for (int y = yFirst; y < yLast; ++y)
|
||||
{
|
||||
const int y1 = srcHeight * y / trgHeight;
|
||||
int y2 = y1 + 1;
|
||||
if (y2 == srcHeight) --y2;
|
||||
|
||||
const double yy1 = y / scaleY - y1;
|
||||
const double y2y = 1 - yy1;
|
||||
|
||||
const uint32_t* const srcLine = byteAdvance(src, y1 * srcPitch);
|
||||
const uint32_t* const srcLineNext = byteAdvance(src, y2 * srcPitch);
|
||||
PixTrg* const trgLine = byteAdvance(trg, y * trgPitch);
|
||||
|
||||
for (int x = 0; x < trgWidth; ++x)
|
||||
{
|
||||
//perf: do NOT "simplify" the variable layout without measurement!
|
||||
const int x1 = buf[x].x1;
|
||||
const int x2 = buf[x].x2;
|
||||
const double xx1 = buf[x].xx1;
|
||||
const double x2x = buf[x].x2x;
|
||||
|
||||
const double x2xy2y = x2x * y2y;
|
||||
const double xx1y2y = xx1 * y2y;
|
||||
const double x2xyy1 = x2x * yy1;
|
||||
const double xx1yy1 = xx1 * yy1;
|
||||
|
||||
auto interpolate = [=](int offset)
|
||||
{
|
||||
PixTrg* trgLine = byteAdvance(trg, y * trgPitch);
|
||||
const int ySrc = srcHeight * y / trgHeight;
|
||||
const PixSrc* srcLine = byteAdvance(src, ySrc * srcPitch);
|
||||
for (int x = 0; x < trgWidth; ++x)
|
||||
{
|
||||
const int xSrc = srcWidth * x / trgWidth;
|
||||
trgLine[x] = pixCvrt(srcLine[xSrc]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
/* https://en.wikipedia.org/wiki/Bilinear_interpolation
|
||||
(c11(x2 - x) + c21(x - x1)) * (y2 - y ) +
|
||||
(c12(x2 - x) + c22(x - x1)) * (y - y1) */
|
||||
const auto c11 = (srcLine [x1] >> (8 * offset)) & 0xff;
|
||||
const auto c21 = (srcLine [x2] >> (8 * offset)) & 0xff;
|
||||
const auto c12 = (srcLineNext[x1] >> (8 * offset)) & 0xff;
|
||||
const auto c22 = (srcLineNext[x2] >> (8 * offset)) & 0xff;
|
||||
|
||||
return c11 * x2xy2y + c21 * xx1y2y +
|
||||
c12 * x2xyy1 + c22 * xx1yy1;
|
||||
};
|
||||
|
||||
const double bi = interpolate(0);
|
||||
const double gi = interpolate(1);
|
||||
const double ri = interpolate(2);
|
||||
const double ai = interpolate(3);
|
||||
|
||||
const auto b = static_cast<uint32_t>(bi + 0.5);
|
||||
const auto g = static_cast<uint32_t>(gi + 0.5);
|
||||
const auto r = static_cast<uint32_t>(ri + 0.5);
|
||||
const auto a = static_cast<uint32_t>(ai + 0.5);
|
||||
|
||||
const uint32_t trgPix = (a << 24) | (r << 16) | (g << 8) | b;
|
||||
|
||||
trgLine[x] = pixCvrt(trgPix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Submodule
+1
Submodule core/deps/xbyak added at 0d67fd1530.
-11
@@ -1,11 +0,0 @@
|
||||
name: test
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: sudo apt install nasm yasm g++-multilib tcsh
|
||||
- run: make test
|
||||
@@ -1 +0,0 @@
|
||||
/build* # cmake
|
||||
@@ -1,46 +0,0 @@
|
||||
cmake_minimum_required(VERSION 2.6...3.0.2)
|
||||
|
||||
project(xbyak CXX)
|
||||
|
||||
file(GLOB headers xbyak/*.h)
|
||||
|
||||
if (DEFINED CMAKE_VERSION AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.0.2)
|
||||
include(GNUInstallDirs)
|
||||
add_library(${PROJECT_NAME} INTERFACE)
|
||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||
|
||||
target_include_directories(
|
||||
${PROJECT_NAME} INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME}
|
||||
EXPORT ${PROJECT_NAME}-targets
|
||||
)
|
||||
|
||||
configure_file(
|
||||
cmake/config.cmake.in
|
||||
${PROJECT_NAME}Config.cmake
|
||||
@ONLY
|
||||
)
|
||||
|
||||
install(
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
install(
|
||||
EXPORT ${PROJECT_NAME}-targets
|
||||
NAMESPACE ${PROJECT_NAME}::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
|
||||
)
|
||||
elseif(NOT DEFINED CMAKE_INSTALL_INCLUDEDIR)
|
||||
set(CMAKE_INSTALL_INCLUDEDIR "include")
|
||||
endif()
|
||||
|
||||
install(
|
||||
FILES ${headers}
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/xbyak
|
||||
)
|
||||
@@ -1,47 +0,0 @@
|
||||
|
||||
Copyright (c) 2007 MITSUNARI Shigeo
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
Neither the name of the copyright owner nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-----------------------------------------------------------------------------
|
||||
ソースコード形式かバイナリ形式か、変更するかしないかを問わず、以下の条件を満た
|
||||
す場合に限り、再頒布および使用が許可されます。
|
||||
|
||||
ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、および下記免責条項
|
||||
を含めること。
|
||||
バイナリ形式で再頒布する場合、頒布物に付属のドキュメント等の資料に、上記の著作
|
||||
権表示、本条件一覧、および下記免責条項を含めること。
|
||||
書面による特別の許可なしに、本ソフトウェアから派生した製品の宣伝または販売促進
|
||||
に、著作権者の名前またはコントリビューターの名前を使用してはならない。
|
||||
本ソフトウェアは、著作権者およびコントリビューターによって「現状のまま」提供さ
|
||||
れており、明示黙示を問わず、商業的な使用可能性、および特定の目的に対する適合性
|
||||
に関する暗黙の保証も含め、またそれに限定されない、いかなる保証もありません。
|
||||
著作権者もコントリビューターも、事由のいかんを問わず、 損害発生の原因いかんを
|
||||
問わず、かつ責任の根拠が契約であるか厳格責任であるか(過失その他の)不法行為で
|
||||
あるかを問わず、仮にそのような損害が発生する可能性を知らされていたとしても、
|
||||
本ソフトウェアの使用によって発生した(代替品または代用サービスの調達、使用の
|
||||
喪失、データの喪失、利益の喪失、業務の中断も含め、またそれに限定されない)直接
|
||||
損害、間接損害、偶発的な損害、特別損害、懲罰的損害、または結果損害について、
|
||||
一切責任を負わないものとします。
|
||||
@@ -1,24 +0,0 @@
|
||||
PREFIX?=/usr/local
|
||||
INSTALL_DIR=$(PREFIX)/include/xbyak
|
||||
|
||||
all:
|
||||
$(MAKE) -C sample
|
||||
|
||||
clean:
|
||||
$(MAKE) -C sample clean
|
||||
|
||||
install:
|
||||
mkdir -p $(INSTALL_DIR)
|
||||
cp -pR xbyak/*.h $(INSTALL_DIR)
|
||||
|
||||
uninstall:
|
||||
rm -i $(INSTALL_DIR)/*.h
|
||||
rmdir $(INSTALL_DIR)
|
||||
|
||||
update:
|
||||
$(MAKE) -C gen
|
||||
|
||||
test:
|
||||
$(MAKE) -C test test
|
||||
|
||||
.PHONY: test update
|
||||
@@ -1 +0,0 @@
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/@[email protected]")
|
||||
@@ -1,26 +0,0 @@
|
||||
TARGET=../xbyak/xbyak_mnemonic.h
|
||||
BIN=sortline gen_code gen_avx512
|
||||
CFLAGS=-I../ -O2 -DXBYAK_NO_OP_NAMES -Wall -Wextra -Wno-missing-field-initializers
|
||||
all: $(TARGET)
|
||||
sortline: sortline.cpp
|
||||
$(CXX) $(CFLAGS) $< -o $@
|
||||
gen_code: gen_code.cpp ../xbyak/xbyak.h avx_type.hpp
|
||||
$(CXX) $(CFLAGS) $< -o $@
|
||||
gen_avx512: gen_avx512.cpp ../xbyak/xbyak.h avx_type.hpp
|
||||
$(CXX) $(CFLAGS) $< -o $@
|
||||
|
||||
$(TARGET): $(BIN)
|
||||
./gen_code | ./sortline > $@
|
||||
echo "#ifdef XBYAK_ENABLE_OMITTED_OPERAND" >> $@
|
||||
./gen_code omit | ./sortline >> $@
|
||||
echo "#endif" >>$@
|
||||
./gen_code fixed >> $@
|
||||
echo "#ifndef XBYAK_DISABLE_AVX512" >> $@
|
||||
./gen_avx512 | ./sortline >> $@
|
||||
echo "#ifdef XBYAK64" >> $@
|
||||
./gen_avx512 64 | ./sortline >> $@
|
||||
echo "#endif" >> $@
|
||||
echo "#endif" >> $@
|
||||
|
||||
clean:
|
||||
$(RM) $(BIN) $(TARGET)
|
||||
@@ -1,170 +0,0 @@
|
||||
#include <assert.h>
|
||||
// copy CodeGenerator::AVXtype
|
||||
enum AVXtype {
|
||||
// low 3 bit
|
||||
T_N1 = 1,
|
||||
T_N2 = 2,
|
||||
T_N4 = 3,
|
||||
T_N8 = 4,
|
||||
T_N16 = 5,
|
||||
T_N32 = 6,
|
||||
T_NX_MASK = 7,
|
||||
//
|
||||
T_N_VL = 1 << 3, // N * (1, 2, 4) for VL
|
||||
T_DUP = 1 << 4, // N = (8, 32, 64)
|
||||
T_66 = 1 << 5,
|
||||
T_F3 = 1 << 6,
|
||||
T_F2 = 1 << 7,
|
||||
T_0F = 1 << 8,
|
||||
T_0F38 = 1 << 9,
|
||||
T_0F3A = 1 << 10,
|
||||
T_L0 = 1 << 11,
|
||||
T_L1 = 1 << 12,
|
||||
T_W0 = 1 << 13,
|
||||
T_W1 = 1 << 14,
|
||||
T_EW0 = 1 << 15,
|
||||
T_EW1 = 1 << 16,
|
||||
T_YMM = 1 << 17, // support YMM, ZMM
|
||||
T_EVEX = 1 << 18,
|
||||
T_ER_X = 1 << 19, // xmm{er}
|
||||
T_ER_Y = 1 << 20, // ymm{er}
|
||||
T_ER_Z = 1 << 21, // zmm{er}
|
||||
T_SAE_X = 1 << 22, // xmm{sae}
|
||||
T_SAE_Y = 1 << 23, // ymm{sae}
|
||||
T_SAE_Z = 1 << 24, // zmm{sae}
|
||||
T_MUST_EVEX = 1 << 25, // contains T_EVEX
|
||||
T_B32 = 1 << 26, // m32bcst
|
||||
T_B64 = 1 << 27, // m64bcst
|
||||
T_M_K = 1 << 28, // mem{k}
|
||||
T_VSIB = 1 << 29,
|
||||
T_MEM_EVEX = 1 << 30, // use evex if mem
|
||||
T_XXX
|
||||
};
|
||||
|
||||
const int NONE = 256; // same as Xbyak::CodeGenerator::NONE
|
||||
|
||||
std::string type2String(int type)
|
||||
{
|
||||
std::string str;
|
||||
int low = type & T_NX_MASK;
|
||||
if (0 < low) {
|
||||
const char *tbl[8] = {
|
||||
"T_N1", "T_N2", "T_N4", "T_N8", "T_N16", "T_N32"
|
||||
};
|
||||
assert(low < int(sizeof(tbl) / sizeof(tbl[0])));
|
||||
str = tbl[low - 1];
|
||||
}
|
||||
if (type & T_N_VL) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_N_VL";
|
||||
}
|
||||
if (type & T_DUP) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_DUP";
|
||||
}
|
||||
if (type & T_66) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_66";
|
||||
}
|
||||
if (type & T_F3) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_F3";
|
||||
}
|
||||
if (type & T_F2) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_F2";
|
||||
}
|
||||
if (type & T_0F) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_0F";
|
||||
}
|
||||
if (type & T_0F38) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_0F38";
|
||||
}
|
||||
if (type & T_0F3A) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_0F3A";
|
||||
}
|
||||
if (type & T_L0) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "VEZ_L0";
|
||||
}
|
||||
if (type & T_L1) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "VEZ_L1";
|
||||
}
|
||||
if (type & T_W0) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_W0";
|
||||
}
|
||||
if (type & T_W1) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_W1";
|
||||
}
|
||||
if (type & T_EW0) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_EW0";
|
||||
}
|
||||
if (type & T_EW1) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_EW1";
|
||||
}
|
||||
if (type & T_YMM) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_YMM";
|
||||
}
|
||||
if (type & T_EVEX) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_EVEX";
|
||||
}
|
||||
if (type & T_ER_X) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_ER_X";
|
||||
}
|
||||
if (type & T_ER_Y) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_ER_Y";
|
||||
}
|
||||
if (type & T_ER_Z) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_ER_Z";
|
||||
}
|
||||
if (type & T_SAE_X) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_SAE_X";
|
||||
}
|
||||
if (type & T_SAE_Y) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_SAE_Y";
|
||||
}
|
||||
if (type & T_SAE_Z) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_SAE_Z";
|
||||
}
|
||||
if (type & T_MUST_EVEX) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_MUST_EVEX";
|
||||
}
|
||||
if (type & T_B32) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_B32";
|
||||
}
|
||||
if (type & T_B64) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_B64";
|
||||
}
|
||||
if (type & T_M_K) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_M_K";
|
||||
}
|
||||
if (type & T_VSIB) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_VSIB";
|
||||
}
|
||||
if (type & T_MEM_EVEX) {
|
||||
if (!str.empty()) str += " | ";
|
||||
str += "T_MEM_EVEX";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int main()
|
||||
{
|
||||
puts("enum {");
|
||||
for (int i = 0; i < 256; i++) {
|
||||
printf(" B");
|
||||
for (int j = 0; j < 8; j++) {
|
||||
putchar(i & (1 << (7 - j)) ? '1' : '0');
|
||||
}
|
||||
printf("= %d", i);
|
||||
if (i < 255) putchar(',');
|
||||
putchar('\n');
|
||||
}
|
||||
puts("};");
|
||||
return 0;
|
||||
}
|
||||
@@ -1,750 +0,0 @@
|
||||
#define XBYAK_DONT_READ_LIST
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "../xbyak/xbyak.h"
|
||||
#define NUM_OF_ARRAY(x) (sizeof(x) / sizeof(x[0]))
|
||||
|
||||
using namespace Xbyak;
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable : 4996) // scanf
|
||||
#define snprintf _snprintf_s
|
||||
#endif
|
||||
|
||||
#include "avx_type.hpp"
|
||||
|
||||
void putOpmask(bool only64bit)
|
||||
{
|
||||
if (only64bit) {
|
||||
puts("void kmovq(const Opmask& k, const Reg64& r) { opVex(k, 0, r, T_L0 | T_0F | T_F2 | T_W1, 0x92); }");
|
||||
puts("void kmovq(const Reg64& r, const Opmask& k) { opVex(r, 0, k, T_L0 | T_0F | T_F2 | T_W1, 0x93); }");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
} tbl[] = {
|
||||
{ "kadd", 0x4A },
|
||||
{ "kand", 0x41 },
|
||||
{ "kandn", 0x42 },
|
||||
{ "kor", 0x45 },
|
||||
{ "kxnor", 0x46 },
|
||||
{ "kxor", 0x47 },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
printf("void %sw(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_W0, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sq(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_W1, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sb(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_66 | T_W0, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sd(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_66 | T_W1, 0x%02X); }\n", p.name, p.code);
|
||||
}
|
||||
printf("void kunpckbw(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_66 | T_W0, 0x4B); }\n");
|
||||
printf("void kunpckwd(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_W0, 0x4B); }\n");
|
||||
printf("void kunpckdq(const Opmask& r1, const Opmask& r2, const Opmask& r3) { opVex(r1, &r2, r3, T_L1 | T_0F | T_W1, 0x4B); }\n");
|
||||
}
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
} tbl[] = {
|
||||
{ "knot", 0x44 },
|
||||
{ "kortest", 0x98 },
|
||||
{ "ktest", 0x99 },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
printf("void %sw(const Opmask& r1, const Opmask& r2) { opVex(r1, 0, r2, T_0F | T_W0, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sq(const Opmask& r1, const Opmask& r2) { opVex(r1, 0, r2, T_0F | T_W1, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sb(const Opmask& r1, const Opmask& r2) { opVex(r1, 0, r2, T_0F | T_66 | T_W0, 0x%02X); }\n", p.name, p.code);
|
||||
printf("void %sd(const Opmask& r1, const Opmask& r2) { opVex(r1, 0, r2, T_0F | T_66 | T_W1, 0x%02X); }\n", p.name, p.code);
|
||||
}
|
||||
}
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
} tbl[] = {
|
||||
{ "kshiftl", 0x32 },
|
||||
{ "kshiftr", 0x30 },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
printf("void %sw(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W1, 0x%02X, imm); }\n", p.name, p.code);
|
||||
printf("void %sq(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W1, 0x%02X, imm); }\n", p.name, p.code + 1);
|
||||
printf("void %sb(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W0, 0x%02X, imm); }\n", p.name, p.code);
|
||||
printf("void %sd(const Opmask& r1, const Opmask& r2, uint8_t imm) { opVex(r1, 0, r2, T_66 | T_0F3A | T_W0, 0x%02X, imm); }\n", p.name, p.code + 1);
|
||||
}
|
||||
}
|
||||
puts("void kmovw(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_W0, 0x90); }");
|
||||
puts("void kmovq(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_W1, 0x90); }");
|
||||
puts("void kmovb(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_66 | T_W0, 0x90); }");
|
||||
puts("void kmovd(const Opmask& k, const Operand& op) { if (!op.isMEM() && !op.isOPMASK()) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(k, 0, op, T_L0 | T_0F | T_66 | T_W1, 0x90); }");
|
||||
|
||||
puts("void kmovw(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_W0, 0x91); }");
|
||||
puts("void kmovq(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_W1, 0x91); }");
|
||||
puts("void kmovb(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_66 | T_W0, 0x91); }");
|
||||
puts("void kmovd(const Address& addr, const Opmask& k) { opVex(k, 0, addr, T_L0 | T_0F | T_66 | T_W1, 0x91); }");
|
||||
|
||||
puts("void kmovw(const Opmask& k, const Reg32& r) { opVex(k, 0, r, T_L0 | T_0F | T_W0, 0x92); }");
|
||||
puts("void kmovw(const Reg32& r, const Opmask& k) { opVex(r, 0, k, T_L0 | T_0F | T_W0, 0x93); }");
|
||||
puts("void kmovb(const Opmask& k, const Reg32& r) { opVex(k, 0, r, T_L0 | T_0F | T_66 | T_W0, 0x92); }");
|
||||
puts("void kmovb(const Reg32& r, const Opmask& k) { opVex(r, 0, k, T_L0 | T_0F | T_66 | T_W0, 0x93); }");
|
||||
puts("void kmovd(const Opmask& k, const Reg32& r) { opVex(k, 0, r, T_L0 | T_0F | T_F2 | T_W0, 0x92); }");
|
||||
puts("void kmovd(const Reg32& r, const Opmask& k) { opVex(r, 0, k, T_L0 | T_0F | T_F2 | T_W0, 0x93); }");
|
||||
}
|
||||
|
||||
// vcmppd(k, x, op)
|
||||
void putVcmp()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
bool hasIMM;
|
||||
} tbl[] = {
|
||||
{ 0xC2, "vcmppd", T_0F | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_YMM | T_66 | T_B64, true },
|
||||
{ 0xC2, "vcmpps", T_0F | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_YMM | T_B32, true },
|
||||
{ 0xC2, "vcmpsd", T_0F | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_F2 | T_N8, true },
|
||||
{ 0xC2, "vcmpss", T_0F | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_F3 | T_N4, true },
|
||||
|
||||
{ 0x74, "vpcmpeqb", T_66 | T_0F | T_MUST_EVEX | T_YMM, false },
|
||||
{ 0x75, "vpcmpeqw", T_66 | T_0F | T_MUST_EVEX | T_YMM, false },
|
||||
{ 0x76, "vpcmpeqd", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_B32, false },
|
||||
{ 0x29, "vpcmpeqq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x64, "vpcmpgtb", T_66 | T_0F | T_MUST_EVEX | T_YMM, false },
|
||||
{ 0x65, "vpcmpgtw", T_66 | T_0F | T_MUST_EVEX | T_YMM, false },
|
||||
{ 0x66, "vpcmpgtd", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, false },
|
||||
{ 0x37, "vpcmpgtq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x3F, "vpcmpb", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0, true },
|
||||
{ 0x3E, "vpcmpub", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0, true },
|
||||
|
||||
{ 0x3F, "vpcmpw", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1, true },
|
||||
{ 0x3E, "vpcmpuw", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1, true },
|
||||
{ 0x1F, "vpcmpd", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, true },
|
||||
{ 0x1E, "vpcmpud", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, true },
|
||||
{ 0x1F, "vpcmpq", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, true },
|
||||
{ 0x1E, "vpcmpuq", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, true },
|
||||
|
||||
{ 0x26, "vptestmb", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, false },
|
||||
{ 0x26, "vptestmw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
{ 0x27, "vptestmd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, false },
|
||||
{ 0x27, "vptestmq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x26, "vptestnmb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, false },
|
||||
{ 0x26, "vptestnmw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
{ 0x27, "vptestnmd", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, false },
|
||||
{ 0x27, "vptestnmq", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Opmask& k, const Xmm& x, const Operand& op%s) { opAVX_K_X_XM(k, x, op, %s, 0x%02X%s); }\n"
|
||||
, p->name, p->hasIMM ? ", uint8_t imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : "");
|
||||
}
|
||||
}
|
||||
|
||||
// XM_X
|
||||
void putX_XM()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
} tbl[] = {
|
||||
{ 0x6F, "vmovdqa32", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x6F, "vmovdqa64", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x6F, "vmovdqu8", T_F2 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x6F, "vmovdqu16", T_F2 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x6F, "vmovdqu32", T_F3 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x6F, "vmovdqu64", T_F3 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z },
|
||||
{ 0x7B, "vcvtpd2qq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_ER_Z },
|
||||
// putCvt
|
||||
{ 0x79, "vcvtpd2uqq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_ER_Z },
|
||||
{ 0x79, "vcvtps2udq", T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_ER_Z },
|
||||
{ 0xE6, "vcvtqq2pd", T_F3 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_ER_Z },
|
||||
{ 0x7A, "vcvttpd2qq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z },
|
||||
{ 0x78, "vcvttpd2uqq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z },
|
||||
{ 0x78, "vcvttps2udq", T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z },
|
||||
{ 0x7A, "vcvtudq2ps", T_F2 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_ER_Z },
|
||||
{ 0x7A, "vcvtuqq2pd", T_F3 | T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_ER_Z },
|
||||
|
||||
{ 0x88, "vexpandpd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_N8 },
|
||||
{ 0x88, "vexpandps", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 },
|
||||
|
||||
{ 0x89, "vpexpandd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 },
|
||||
{ 0x89, "vpexpandq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_N8 },
|
||||
{ 0x42, "vgetexppd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z },
|
||||
{ 0x42, "vgetexpps", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Xmm& x, const Operand& op) { opAVX_X_XM_IMM(x, op, %s, 0x%02X); }\n", p->name, type.c_str(), p->code);
|
||||
}
|
||||
puts("void vpabsq(const Xmm& x, const Operand& op) { opAVX_X_XM_IMM(x, op, T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_B64 | T_YMM, 0x1F); }");
|
||||
|
||||
puts("void vexp2pd(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z, 0xC8); }");
|
||||
puts("void vexp2ps(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z, 0xC8); }");
|
||||
puts("void vrcp28pd(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z, 0xCA); }");
|
||||
puts("void vrcp28ps(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z, 0xCA); }");
|
||||
|
||||
puts("void vrsqrt28pd(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z, 0xCC); }");
|
||||
puts("void vrsqrt28ps(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z, 0xCC); }");
|
||||
}
|
||||
|
||||
void putM_X()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
} tbl[] = {
|
||||
{ 0x7F, "vmovdqa32", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
{ 0x7F, "vmovdqa64", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
{ 0x7F, "vmovdqu8", T_F2 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
{ 0x7F, "vmovdqu16", T_F2 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
{ 0x7F, "vmovdqu32", T_F3 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
{ 0x7F, "vmovdqu64", T_F3 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_ER_X | T_ER_Y | T_ER_Z | T_M_K },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Address& addr, const Xmm& x) { opAVX_X_XM_IMM(x, addr, %s, 0x%02X); }\n", p->name, type.c_str(), p->code);
|
||||
}
|
||||
}
|
||||
|
||||
void putXM_X()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
} tbl[] = {
|
||||
{ 0x8A, "vcompresspd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_N8 },
|
||||
{ 0x8A, "vcompressps", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 },
|
||||
|
||||
{ 0x8B, "vpcompressd", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 },
|
||||
{ 0x8B, "vpcompressq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_N8 },
|
||||
|
||||
{ 0x63, "vcompressb", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N1 },
|
||||
{ 0x63, "vcompressw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_N2 },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Operand& op, const Xmm& x) { opAVX_X_XM_IMM(x, op, %s, 0x%02X); }\n", p->name, type.c_str(), p->code);
|
||||
}
|
||||
}
|
||||
|
||||
void putX_X_XM_IMM()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
bool hasIMM;
|
||||
} tbl[] = {
|
||||
{ 0x03, "valignd", T_MUST_EVEX | T_66 | T_0F3A | T_EW0 | T_YMM, true },
|
||||
{ 0x03, "valignq", T_MUST_EVEX | T_66 | T_0F3A | T_EW1 | T_YMM, true },
|
||||
{ 0xDB, "vpandd", T_MUST_EVEX | T_YMM | T_66 | T_0F | T_EW0 | T_B32, false },
|
||||
{ 0xDB, "vpandq", T_MUST_EVEX | T_YMM | T_66 | T_0F | T_EW1 | T_B64, false },
|
||||
{ 0xDF, "vpandnd", T_MUST_EVEX | T_YMM | T_66 | T_0F | T_EW0 | T_B32, false },
|
||||
{ 0xDF, "vpandnq", T_MUST_EVEX | T_YMM | T_66 | T_0F | T_EW1 | T_B64, false },
|
||||
{ 0x3D, "vpmaxsq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
{ 0x3F, "vpmaxuq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
{ 0x39, "vpminsq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
{ 0x3B, "vpminuq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
{ 0xE2, "vpsraq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_N16, false },
|
||||
{ 0x46, "vpsravq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
{ 0x11, "vpsravw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
{ 0x12, "vpsllvw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
{ 0x10, "vpsrlvw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
{ 0xEB, "vpord", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, false },
|
||||
{ 0xEB, "vporq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0xEF, "vpxord", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, false },
|
||||
{ 0xEF, "vpxorq", T_66 | T_0F | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x40, "vpmullq", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x8D, "vpermb", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, false },
|
||||
{ 0x8D, "vpermw", T_66 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, false },
|
||||
|
||||
{ 0x65, "vblendmpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0x65, "vblendmps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x66, "vpblendmb", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, false },
|
||||
{ 0x66, "vpblendmw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1, false },
|
||||
{ 0x64, "vpblendmd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x64, "vpblendmq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x7D, "vpermt2b", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, false },
|
||||
{ 0x7D, "vpermt2w", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1, false },
|
||||
{ 0x7E, "vpermt2d", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x7E, "vpermt2q", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0x7F, "vpermt2ps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x7F, "vpermt2pd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x75, "vpermi2b", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, false },
|
||||
{ 0x75, "vpermi2w", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1, false },
|
||||
{ 0x76, "vpermi2d", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x76, "vpermi2q", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0x77, "vpermi2ps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x77, "vpermi2pd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x25, "vpternlogd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, true },
|
||||
{ 0x25, "vpternlogq", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, true },
|
||||
|
||||
{ 0x43, "vgetexpsd", T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_SAE_X | T_N8, false },
|
||||
{ 0x43, "vgetexpss", T_66 | T_0F38 | T_MUST_EVEX | T_EW0 | T_SAE_X | T_N4, false },
|
||||
{ 0x27, "vgetmantsd", T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_SAE_X | T_N8, true },
|
||||
{ 0x27, "vgetmantss", T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_SAE_X | T_N4, true },
|
||||
|
||||
{ 0x54, "vfixupimmpd", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1 | T_B64 | T_SAE_Z, true },
|
||||
{ 0x54, "vfixupimmps", T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0 | T_B32 | T_SAE_Z, true },
|
||||
{ 0x55, "vfixupimmsd", T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_N8, true },
|
||||
{ 0x55, "vfixupimmss", T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_N4, true },
|
||||
|
||||
{ 0x4D, "vrcp14sd", T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_N8, false },
|
||||
{ 0x4D, "vrcp14ss", T_66 | T_0F38 | T_MUST_EVEX | T_EW0 | T_N4, false },
|
||||
|
||||
{ 0x4F, "vrsqrt14sd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8, false },
|
||||
{ 0x4F, "vrsqrt14ss", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, false },
|
||||
|
||||
{ 0x0B, "vrndscalesd", T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_N8, true },
|
||||
{ 0x0A, "vrndscaless", T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_N4, true },
|
||||
|
||||
{ 0x2C, "vscalefpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_ER_Z, false },
|
||||
{ 0x2C, "vscalefps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_ER_Z, false },
|
||||
{ 0x2D, "vscalefsd", T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_ER_X | T_N8, false },
|
||||
{ 0x2D, "vscalefss", T_66 | T_0F38 | T_MUST_EVEX | T_EW0 | T_ER_X | T_N4, false },
|
||||
|
||||
{ 0x42, "vdbpsadbw", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0, true },
|
||||
{ 0x83, "vpmultishiftqb", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x15, "vprolvd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x15, "vprolvq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x14, "vprorvd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x14, "vprorvq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0xCB, "vrcp28sd", T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_N8 | T_SAE_X, false },
|
||||
{ 0xCB, "vrcp28ss", T_66 | T_0F38 | T_MUST_EVEX | T_EW0 | T_N4 | T_SAE_X, false },
|
||||
|
||||
{ 0xCD, "vrsqrt28sd", T_66 | T_0F38 | T_MUST_EVEX | T_EW1 | T_N8 | T_SAE_X, false },
|
||||
{ 0xCD, "vrsqrt28ss", T_66 | T_0F38 | T_MUST_EVEX | T_EW0 | T_N4 | T_SAE_X, false },
|
||||
|
||||
{ 0x50, "vrangepd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_SAE_Z, true },
|
||||
{ 0x50, "vrangeps", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_SAE_Z, true },
|
||||
{ 0x51, "vrangesd", T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_SAE_X | T_N8, true },
|
||||
{ 0x51, "vrangess", T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_SAE_X | T_N4, true },
|
||||
|
||||
{ 0x57, "vreducesd", T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_SAE_X | T_N8, true },
|
||||
{ 0x57, "vreducess", T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_SAE_X | T_N4, true },
|
||||
|
||||
{ 0xB4, "vpmadd52luq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0xB5, "vpmadd52huq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x70, "vpshldw", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z, true },
|
||||
{ 0x71, "vpshldd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, true },
|
||||
{ 0x71, "vpshldq", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_B64, true },
|
||||
|
||||
{ 0x70, "vpshldvw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z, false },
|
||||
{ 0x71, "vpshldvd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
|
||||
{ 0x71, "vpshldvq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_B64, false },
|
||||
|
||||
{ 0x72, "vpshrdw", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z, true },
|
||||
{ 0x73, "vpshrdd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, true },
|
||||
{ 0x73, "vpshrdq", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_B64, true },
|
||||
|
||||
{ 0x72, "vpshrdvw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z, false },
|
||||
{ 0x73, "vpshrdvd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
|
||||
{ 0x73, "vpshrdvq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_B64, false },
|
||||
|
||||
{ 0x72, "vcvtne2ps2bf16", T_F2 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
|
||||
{ 0x52, "vdpbf16ps", T_F3 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Xmm& x1, const Xmm& x2, const Operand& op%s) { opAVX_X_X_XM(x1, x2, op, %s, 0x%02X%s); }\n"
|
||||
, p->name, p->hasIMM ? ", uint8_t imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : "");
|
||||
}
|
||||
}
|
||||
|
||||
void putShift()
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
int idx;
|
||||
int type;
|
||||
} tbl[] = {
|
||||
{ "vpsraq", 0x72, 4, T_0F | T_66 | T_YMM | T_MUST_EVEX |T_EW1 | T_B64 },
|
||||
{ "vprold", 0x72, 1, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 },
|
||||
{ "vprolq", 0x72, 1, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 },
|
||||
{ "vprord", 0x72, 0, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 },
|
||||
{ "vprorq", 0x72, 0, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type);
|
||||
printf("void %s(const Xmm& x, const Operand& op, uint8_t imm) { opAVX_X_X_XM(Xmm(x.getKind(), %d), x, op, %s, 0x%02X, imm); }\n", p.name, p.idx, type.c_str(), p.code);
|
||||
}
|
||||
}
|
||||
|
||||
void putExtractInsert()
|
||||
{
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
int type;
|
||||
bool isZMM;
|
||||
} tbl[] = {
|
||||
{ "vextractf32x4", 0x19, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N16, false },
|
||||
{ "vextractf64x2", 0x19, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N16, false },
|
||||
{ "vextractf32x8", 0x1B, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N32, true },
|
||||
{ "vextractf64x4", 0x1B, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N32, true },
|
||||
|
||||
{ "vextracti32x4", 0x39, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N16, false },
|
||||
{ "vextracti64x2", 0x39, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N16, false },
|
||||
{ "vextracti32x8", 0x3B, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N32, true },
|
||||
{ "vextracti64x4", 0x3B, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N32, true },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type);
|
||||
const char *kind = p.isZMM ? "Operand::MEM | Operand::YMM" : "Operand::MEM | Operand::XMM";
|
||||
printf("void %s(const Operand& op, const %s& r, uint8_t imm) { if (!op.is(%s)) XBYAK_THROW(ERR_BAD_COMBINATION) opVex(r, 0, op, %s, 0x%2X, imm); }\n", p.name, p.isZMM ? "Zmm" : "Ymm", kind, type.c_str(), p.code);
|
||||
}
|
||||
}
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
uint8_t code;
|
||||
int type;
|
||||
bool isZMM;
|
||||
} tbl[] = {
|
||||
{ "vinsertf32x4", 0x18, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N16, false },
|
||||
{ "vinsertf64x2", 0x18, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N16, false },
|
||||
{ "vinsertf32x8", 0x1A, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N32, true },
|
||||
{ "vinsertf64x4", 0x1A, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N32, true },
|
||||
|
||||
{ "vinserti32x4", 0x38, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N16, false },
|
||||
{ "vinserti64x2", 0x38, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N16, false },
|
||||
{ "vinserti32x8", 0x3A, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_YMM | T_N32, true },
|
||||
{ "vinserti64x4", 0x3A, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_YMM | T_N32, true },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type);
|
||||
const char *x = p.isZMM ? "Zmm" : "Ymm";
|
||||
const char *cond = p.isZMM ? "op.is(Operand::MEM | Operand::YMM)" : "(r1.getKind() == r2.getKind() && op.is(Operand::MEM | Operand::XMM))";
|
||||
printf("void %s(const %s& r1, const %s& r2, const Operand& op, uint8_t imm) {"
|
||||
"if (!%s) XBYAK_THROW(ERR_BAD_COMBINATION) "
|
||||
"opVex(r1, &r2, op, %s, 0x%2X, imm); }\n", p.name, x, x, cond, type.c_str(), p.code);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void putBroadcast(bool only64bit)
|
||||
{
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
int reg;
|
||||
} tbl[] = {
|
||||
{ 0x7A, "vpbroadcastb", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, 8 },
|
||||
{ 0x7B, "vpbroadcastw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, 16 },
|
||||
{ 0x7C, "vpbroadcastd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, 32 },
|
||||
{ 0x7C, "vpbroadcastq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1, 64},
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type);
|
||||
if ((only64bit && p.reg == 64) || (!only64bit && p.reg != 64)) {
|
||||
printf("void %s(const Xmm& x, const Reg%d& r) { opVex(x, 0, r, %s, 0x%02X); }\n", p.name, p.reg, type.c_str(), p.code);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (only64bit) return;
|
||||
puts("void vbroadcastf32x2(const Ymm& y, const Operand& op) { opAVX_X_XM_IMM(y, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N8, 0x19); }");
|
||||
puts("void vbroadcastf32x4(const Ymm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N16, 0x1A); }");
|
||||
puts("void vbroadcastf64x2(const Ymm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N16, 0x1A); }");
|
||||
puts("void vbroadcastf64x4(const Zmm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N32, 0x1B); }");
|
||||
puts("void vbroadcastf32x8(const Zmm& y, const Address& addr) { opAVX_X_XM_IMM(y, addr, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N32, 0x1B); }");
|
||||
|
||||
puts("void vbroadcasti32x2(const Xmm& x, const Operand& op) { opAVX_X_XM_IMM(x, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N8, 0x59); }");
|
||||
puts("void vbroadcasti32x4(const Ymm& y, const Operand& op) { opAVX_X_XM_IMM(y, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N16, 0x5A); }");
|
||||
puts("void vbroadcasti64x2(const Ymm& y, const Operand& op) { opAVX_X_XM_IMM(y, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N16, 0x5A); }");
|
||||
puts("void vbroadcasti32x8(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N32, 0x5B); }");
|
||||
puts("void vbroadcasti64x4(const Zmm& z, const Operand& op) { opAVX_X_XM_IMM(z, op, T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N32, 0x5B); }");
|
||||
}
|
||||
|
||||
void putCvt()
|
||||
{
|
||||
puts("void vcvtpd2udq(const Xmm& x, const Operand& op) { opCvt2(x, op, T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_ER_Z, 0x79); }");
|
||||
puts("void vcvtps2qq(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_N8 | T_N_VL | T_ER_Y, 0x7B); }");
|
||||
puts("void vcvtps2uqq(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_N8 | T_N_VL | T_ER_Y, 0x79); }");
|
||||
puts("void vcvtqq2ps(const Xmm& x, const Operand& op) { opCvt2(x, op, T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_ER_Z, 0x5B); }");
|
||||
puts("void vcvttpd2udq(const Xmm& x, const Operand& op) { opCvt2(x, op, T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_SAE_Z, 0x78); }");
|
||||
puts("void vcvttps2qq(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_N8 | T_N_VL | T_SAE_Y, 0x7A); }");
|
||||
puts("void vcvttps2uqq(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_66 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_N8 | T_N_VL | T_SAE_Y, 0x78); }");
|
||||
puts("void vcvtudq2pd(const Xmm& x, const Operand& op) { checkCvt1(x, op); opVex(x, 0, op, T_F3 | T_0F | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_N8 | T_N_VL, 0x7A); }");
|
||||
|
||||
puts("void vcvtsd2usi(const Reg32e& r, const Operand& op) { int type = (T_F2 | T_0F | T_MUST_EVEX | T_N8 | T_ER_X) | (r.isREG(64) ? T_EW1 : T_EW0); opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, type, 0x79); }");
|
||||
puts("void vcvtss2usi(const Reg32e& r, const Operand& op) { int type = (T_F3 | T_0F | T_MUST_EVEX | T_N4 | T_ER_X) | (r.isREG(64) ? T_EW1 : T_EW0); opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, type, 0x79); }");
|
||||
puts("void vcvttsd2usi(const Reg32e& r, const Operand& op) { int type = (T_F2 | T_0F | T_MUST_EVEX | T_N8 | T_SAE_X) | (r.isREG(64) ? T_EW1 : T_EW0); opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, type, 0x78); }");
|
||||
puts("void vcvttss2usi(const Reg32e& r, const Operand& op) { int type = (T_F3 | T_0F | T_MUST_EVEX | T_N4 | T_SAE_X) | (r.isREG(64) ? T_EW1 : T_EW0); opAVX_X_X_XM(Xmm(r.getIdx()), xm0, op, type, 0x78); }");
|
||||
puts("void vcvtuqq2ps(const Xmm& x, const Operand& op) { opCvt2(x, op, T_F2 | T_0F | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_ER_Z, 0x7A); }");
|
||||
puts("void vcvtusi2sd(const Xmm& x1, const Xmm& x2, const Operand& op) { opCvt3(x1, x2, op, T_F2 | T_0F | T_MUST_EVEX, T_W1 | T_EW1 | T_ER_X | T_N8, T_W0 | T_EW0 | T_N4, 0x7B); }");
|
||||
puts("void vcvtusi2ss(const Xmm& x1, const Xmm& x2, const Operand& op) { opCvt3(x1, x2, op, T_F3 | T_0F | T_MUST_EVEX | T_ER_X, T_W1 | T_EW1 | T_N8, T_W0 | T_EW0 | T_N4, 0x7B); }");
|
||||
}
|
||||
|
||||
enum { // same as xbyak.h
|
||||
xx_yy_zz = 0,
|
||||
xx_yx_zy = 1,
|
||||
xx_xy_yz = 2,
|
||||
};
|
||||
void putGather()
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
int type;
|
||||
uint8_t code;
|
||||
int mode;
|
||||
} tbl[] = {
|
||||
{ "vpgatherdd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, 0x90, xx_yy_zz },
|
||||
{ "vpgatherdq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8, 0x90, xx_yx_zy },
|
||||
{ "vpgatherqd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, 0x91, xx_xy_yz },
|
||||
{ "vpgatherqq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8, 0x91, xx_yy_zz },
|
||||
{ "vgatherdps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, 0x92, xx_yy_zz },
|
||||
{ "vgatherdpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8, 0x92, xx_yx_zy },
|
||||
{ "vgatherqps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4, 0x93, xx_xy_yz },
|
||||
{ "vgatherqpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8, 0x93, xx_yy_zz },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type | T_VSIB);
|
||||
printf("void %s(const Xmm& x, const Address& addr) { opGather2(x, addr, %s, 0x%02X, %d); }\n", p.name, type.c_str(), p.code, p.mode);
|
||||
}
|
||||
}
|
||||
void putScatter()
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
int type;
|
||||
uint8_t code;
|
||||
int mode; // reverse of gather
|
||||
} tbl[] = {
|
||||
{ "vpscatterdd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4 | T_M_K, 0xA0, xx_yy_zz },
|
||||
{ "vpscatterdq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8 | T_M_K, 0xA0, xx_yx_zy },
|
||||
{ "vpscatterqd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4 | T_M_K, 0xA1, xx_xy_yz },
|
||||
{ "vpscatterqq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8 | T_M_K, 0xA1, xx_yy_zz },
|
||||
|
||||
{ "vscatterdps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4 | T_M_K, 0xA2, xx_yy_zz },
|
||||
{ "vscatterdpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8 | T_M_K, 0xA2, xx_yx_zy },
|
||||
{ "vscatterqps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_N4 | T_M_K, 0xA3, xx_xy_yz },
|
||||
{ "vscatterqpd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_N8 | T_M_K, 0xA3, xx_yy_zz },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type | T_VSIB);
|
||||
printf("void %s(const Address& addr, const Xmm& x) { opGather2(x, addr, %s, 0x%02X, %d); }\n", p.name, type.c_str(), p.code, p.mode);
|
||||
}
|
||||
}
|
||||
|
||||
void putShuff()
|
||||
{
|
||||
puts("void vshuff32x4(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, 0x23, imm); }");
|
||||
puts("void vshuff64x2(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, 0x23, imm); }");
|
||||
puts("void vshufi32x4(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, 0x43, imm); }");
|
||||
puts("void vshufi64x2(const Ymm& y1, const Ymm& y2, const Operand& op, uint8_t imm) { opAVX_X_X_XM(y1, y2, op, T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, 0x43, imm); }");
|
||||
}
|
||||
|
||||
void putMov()
|
||||
{
|
||||
puts("void vpmovm2b(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, 0x28); }");
|
||||
puts("void vpmovm2w(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, 0x28); }");
|
||||
puts("void vpmovm2d(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, 0x38); }");
|
||||
puts("void vpmovm2q(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, 0x38); }");
|
||||
|
||||
puts("void vpmovb2m(const Opmask& k, const Xmm& x) { opVex(k, 0, x, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, 0x29); }");
|
||||
puts("void vpmovw2m(const Opmask& k, const Xmm& x) { opVex(k, 0, x, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, 0x29); }");
|
||||
puts("void vpmovd2m(const Opmask& k, const Xmm& x) { opVex(k, 0, x, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0, 0x39); }");
|
||||
puts("void vpmovq2m(const Opmask& k, const Xmm& x) { opVex(k, 0, x, T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW1, 0x39); }");
|
||||
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
int mode;
|
||||
} tbl[] = {
|
||||
{ 0x32, "vpmovqb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N2 | T_N_VL, false },
|
||||
{ 0x22, "vpmovsqb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N2 | T_N_VL, false },
|
||||
{ 0x12, "vpmovusqb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N2 | T_N_VL, false },
|
||||
|
||||
{ 0x34, "vpmovqw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
{ 0x24, "vpmovsqw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
{ 0x14, "vpmovusqw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
|
||||
{ 0x35, "vpmovqd", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x25, "vpmovsqd", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x15, "vpmovusqd", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
|
||||
{ 0x31, "vpmovdb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
{ 0x21, "vpmovsdb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
{ 0x11, "vpmovusdb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N4 | T_N_VL, false },
|
||||
|
||||
{ 0x33, "vpmovdw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x23, "vpmovsdw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x13, "vpmovusdw", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
|
||||
{ 0x30, "vpmovwb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x20, "vpmovswb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
{ 0x10, "vpmovuswb", T_F3 | T_0F38 | T_MUST_EVEX | T_YMM | T_EW0 | T_N8 | T_N_VL, true },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type);
|
||||
printf("void %s(const Operand& op, const Xmm& x) { opVmov(op, x, %s, 0x%02X, %s); }\n", p.name, type.c_str(), p.code, p.mode ? "true" : "false");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void putX_XM_IMM()
|
||||
{
|
||||
const struct Tbl {
|
||||
uint8_t code;
|
||||
const char *name;
|
||||
int type;
|
||||
bool hasIMM;
|
||||
} tbl[] = {
|
||||
{ 0x26, "vgetmantpd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_SAE_Z, true },
|
||||
{ 0x26, "vgetmantps", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_SAE_Z, true },
|
||||
{ 0x4C, "vrcp14pd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0x4C, "vrcp14ps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
|
||||
{ 0x4E, "vrsqrt14pd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
{ 0x4E, "vrsqrt14ps", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
|
||||
{ 0x09, "vrndscalepd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, true },
|
||||
{ 0x08, "vrndscaleps", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, true },
|
||||
|
||||
{ 0xC4, "vpconflictd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0xC4, "vpconflictq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x44, "vplzcntd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_B32, false },
|
||||
{ 0x44, "vplzcntq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_B64, false },
|
||||
|
||||
{ 0x56, "vreducepd", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW1 | T_B64 | T_SAE_Z, true },
|
||||
{ 0x56, "vreduceps", T_66 | T_0F3A | T_YMM | T_MUST_EVEX | T_EW0 | T_B32 | T_SAE_Z, true },
|
||||
|
||||
{ 0x54, "vpopcntb", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z, false },
|
||||
{ 0x54, "vpopcntw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z, false },
|
||||
{ 0x55, "vpopcntd", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_B32, false },
|
||||
{ 0x55, "vpopcntq", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_B64, false },
|
||||
|
||||
{ 0x62, "vpexpandb", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0 | T_SAE_Z | T_N1, false },
|
||||
{ 0x62, "vpexpandw", T_66 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1 | T_SAE_Z | T_N2, false },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl *p = &tbl[i];
|
||||
std::string type = type2String(p->type);
|
||||
printf("void %s(const Xmm& x, const Operand& op%s) { opAVX_X_XM_IMM(x, op, %s, 0x%02X%s); }\n"
|
||||
, p->name, p->hasIMM ? ", uint8_t imm" : "", type.c_str(), p->code, p->hasIMM ? ", imm" : "");
|
||||
}
|
||||
}
|
||||
|
||||
void putMisc()
|
||||
{
|
||||
puts("void vpbroadcastmb2q(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW1, 0x2A); }");
|
||||
puts("void vpbroadcastmw2d(const Xmm& x, const Opmask& k) { opVex(x, 0, k, T_F3 | T_0F38 | T_YMM | T_MUST_EVEX | T_EW0, 0x3A); }");
|
||||
{
|
||||
const struct Tbl {
|
||||
const char *name;
|
||||
int zm;
|
||||
int type;
|
||||
uint8_t code;
|
||||
bool isZmm;
|
||||
} tbl[] = {
|
||||
{ "vgatherpf0dps", 1, T_EW0 | T_N4, 0xC6, true },
|
||||
{ "vgatherpf0qps", 1, T_EW0 | T_N4, 0xC7, true },
|
||||
{ "vgatherpf0dpd", 1, T_EW1 | T_N8, 0xC6, false },
|
||||
{ "vgatherpf0qpd", 1, T_EW1 | T_N8, 0xC7, true },
|
||||
|
||||
{ "vgatherpf1dps", 2, T_EW0 | T_N4, 0xC6, true },
|
||||
{ "vgatherpf1qps", 2, T_EW0 | T_N4, 0xC7, true },
|
||||
{ "vgatherpf1dpd", 2, T_EW1 | T_N8, 0xC6, false },
|
||||
{ "vgatherpf1qpd", 2, T_EW1 | T_N8, 0xC7, true },
|
||||
|
||||
{ "vscatterpf0dps", 5, T_EW0 | T_N4, 0xC6, true },
|
||||
{ "vscatterpf0qps", 5, T_EW0 | T_N4, 0xC7, true },
|
||||
{ "vscatterpf0dpd", 5, T_EW1 | T_N8, 0xC6, false },
|
||||
{ "vscatterpf0qpd", 5, T_EW1 | T_N8, 0xC7, true },
|
||||
|
||||
{ "vscatterpf1dps", 6, T_EW0 | T_N4, 0xC6, true },
|
||||
{ "vscatterpf1qps", 6, T_EW0 | T_N4, 0xC7, true },
|
||||
{ "vscatterpf1dpd", 6, T_EW1 | T_N8, 0xC6, false },
|
||||
{ "vscatterpf1qpd", 6, T_EW1 | T_N8, 0xC7, true },
|
||||
};
|
||||
for (size_t i = 0; i < NUM_OF_ARRAY(tbl); i++) {
|
||||
const Tbl& p = tbl[i];
|
||||
std::string type = type2String(p.type | T_66 | T_0F38 | T_MUST_EVEX | T_M_K | T_VSIB);
|
||||
printf("void %s(const Address& addr) { opGatherFetch(addr, zm%d, %s, 0x%2X, Operand::%s); }\n"
|
||||
, p.name, p.zm, type.c_str(), p.code, p.isZmm ? "ZMM" : "YMM");
|
||||
}
|
||||
}
|
||||
|
||||
puts("void vfpclasspd(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isBit(128|256|512)) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k.changeBit(op.getBit()), 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW1 | T_B64, 0x66, imm); }");
|
||||
puts("void vfpclassps(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isBit(128|256|512)) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k.changeBit(op.getBit()), 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_YMM | T_EW0 | T_B32, 0x66, imm); }");
|
||||
puts("void vfpclasssd(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isXMEM()) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k, 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_EW1 | T_N8, 0x67, imm); }");
|
||||
puts("void vfpclassss(const Opmask& k, const Operand& op, uint8_t imm) { if (!op.isXMEM()) XBYAK_THROW(ERR_BAD_MEM_SIZE) opVex(k, 0, op, T_66 | T_0F3A | T_MUST_EVEX | T_EW0 | T_N4, 0x67, imm); }");
|
||||
|
||||
puts("void vpshufbitqmb(const Opmask& k, const Xmm& x, const Operand& op) { opVex(k, &x, op, T_66 | T_0F38 | T_EW0 | T_YMM | T_MUST_EVEX, 0x8F); }");
|
||||
puts("void vcvtneps2bf16(const Xmm& x, const Operand& op) { opCvt2(x, op, T_F3 | T_0F38 | T_EW0 | T_YMM | T_SAE_Z | T_MUST_EVEX | T_B32, 0x72); }");
|
||||
|
||||
puts("void vp2intersectd(const Opmask& k, const Xmm& x, const Operand& op) { if (k.getOpmaskIdx() != 0) XBYAK_THROW(ERR_OPMASK_IS_ALREADY_SET) opAVX_K_X_XM(k, x, op, T_F2 | T_0F38 | T_YMM | T_EVEX | T_EW0 | T_B32, 0x68); }");
|
||||
puts("void vp2intersectq(const Opmask& k, const Xmm& x, const Operand& op) { if (k.getOpmaskIdx() != 0) XBYAK_THROW(ERR_OPMASK_IS_ALREADY_SET) opAVX_K_X_XM(k, x, op, T_F2 | T_0F38 | T_YMM | T_EVEX | T_EW1 | T_B64, 0x68); }");
|
||||
}
|
||||
|
||||
void putV4FMA()
|
||||
{
|
||||
puts("void v4fmaddps(const Zmm& z1, const Zmm& z2, const Address& addr) { opAVX_X_X_XM(z1, z2, addr, T_0F38 | T_F2 | T_EW0 | T_YMM | T_MUST_EVEX | T_N16, 0x9A); }");
|
||||
puts("void v4fnmaddps(const Zmm& z1, const Zmm& z2, const Address& addr) { opAVX_X_X_XM(z1, z2, addr, T_0F38 | T_F2 | T_EW0 | T_YMM | T_MUST_EVEX | T_N16, 0xAA); }");
|
||||
puts("void v4fmaddss(const Xmm& x1, const Xmm& x2, const Address& addr) { opAVX_X_X_XM(x1, x2, addr, T_0F38 | T_F2 | T_EW0 | T_MUST_EVEX | T_N16, 0x9B); }");
|
||||
puts("void v4fnmaddss(const Xmm& x1, const Xmm& x2, const Address& addr) { opAVX_X_X_XM(x1, x2, addr, T_0F38 | T_F2 | T_EW0 | T_MUST_EVEX | T_N16, 0xAB); }");
|
||||
puts("void vp4dpwssd(const Zmm& z1, const Zmm& z2, const Address& addr) { opAVX_X_X_XM(z1, z2, addr, T_0F38 | T_F2 | T_EW0 | T_YMM | T_MUST_EVEX | T_N16, 0x52); }");
|
||||
puts("void vp4dpwssds(const Zmm& z1, const Zmm& z2, const Address& addr) { opAVX_X_X_XM(z1, z2, addr, T_0F38 | T_F2 | T_EW0 | T_YMM | T_MUST_EVEX | T_N16, 0x53); }");
|
||||
}
|
||||
|
||||
int main(int argc, char *[])
|
||||
{
|
||||
bool only64bit = argc == 2;
|
||||
putOpmask(only64bit);
|
||||
putBroadcast(only64bit);
|
||||
if (only64bit) {
|
||||
return 0;
|
||||
}
|
||||
putVcmp();
|
||||
putX_XM();
|
||||
putM_X();
|
||||
putXM_X();
|
||||
putX_X_XM_IMM();
|
||||
putShift();
|
||||
putExtractInsert();
|
||||
putCvt();
|
||||
putGather();
|
||||
putShuff();
|
||||
putMov();
|
||||
putX_XM_IMM();
|
||||
putMisc();
|
||||
putScatter();
|
||||
putV4FMA();
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 725 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user