mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-03-28 15:57:03 -05:00
Compare commits
597 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92b5fd84c2 | ||
|
|
3e0bb6d8be | ||
|
|
cf34c4bd95 | ||
|
|
bab1d2e27e | ||
|
|
d1b6a21e86 | ||
|
|
3049590b68 | ||
|
|
8a289d2e4f | ||
|
|
7a81fa7ac5 | ||
|
|
fbf59b6f0b | ||
|
|
0ab200f77f | ||
|
|
a91e40d731 | ||
|
|
e553fbc86c | ||
|
|
b4a810c374 | ||
|
|
0e97914d94 | ||
|
|
a5c250c811 | ||
|
|
08c2f3fc15 | ||
|
|
63f66662ce | ||
|
|
89eee104ab | ||
|
|
92b1234ddb | ||
|
|
0b0bf90e0b | ||
|
|
974c4ba040 | ||
|
|
4e2c1d20b4 | ||
|
|
e5b83d0ddf | ||
|
|
bdaf1e4151 | ||
|
|
751eff0edf | ||
|
|
666dc7dccb | ||
|
|
a172e89620 | ||
|
|
ecb9537c4e | ||
|
|
bba4cf9578 | ||
|
|
bfdb9b4019 | ||
|
|
bad37d0940 | ||
|
|
62f5640678 | ||
|
|
71c1bcde0d | ||
|
|
e9b492a287 | ||
|
|
9b9b1aa6cc | ||
|
|
2cb673fd81 | ||
|
|
d5eb6b5bbc | ||
|
|
1e48277566 | ||
|
|
ec748f4a64 | ||
|
|
22f1713739 | ||
|
|
f0e135530a | ||
|
|
125d6a3e2a | ||
|
|
d078f9d847 | ||
|
|
6b6a6ae5f0 | ||
|
|
1d6676f059 | ||
|
|
1e91505e6e | ||
|
|
9e2f228d9a | ||
|
|
7a1d163450 | ||
|
|
22e717d778 | ||
|
|
2546c042dc | ||
|
|
b54f46de30 | ||
|
|
94bc279bd8 | ||
|
|
2f7c2e79d2 | ||
|
|
2ed5381f5a | ||
|
|
964d98dd7b | ||
|
|
0571dae9b7 | ||
|
|
563bf78f03 | ||
|
|
cf480d95db | ||
|
|
44331506b2 | ||
|
|
f6953fd829 | ||
|
|
663b99ed64 | ||
|
|
0ef3be1851 | ||
|
|
0453a23b12 | ||
|
|
7a14e3dac4 | ||
|
|
275774a10a | ||
|
|
a6277533e8 | ||
|
|
1426424899 | ||
|
|
85fa1b2122 | ||
|
|
c28522b7ef | ||
|
|
d90a04990b | ||
|
|
027ff793ed | ||
|
|
c69d3bc7f4 | ||
|
|
ca17054a1e | ||
|
|
6a9e07729f | ||
|
|
ceeda6de3b | ||
|
|
17ab059b12 | ||
|
|
dedd99f30c | ||
|
|
add94c5926 | ||
|
|
a239edc759 | ||
|
|
ff569417fc | ||
|
|
240bb299f0 | ||
|
|
e2dd12416c | ||
|
|
2a726c7136 | ||
|
|
62aea46c61 | ||
|
|
09bffb6745 | ||
|
|
3c91cb09e3 | ||
|
|
881a379fb4 | ||
|
|
90a67af887 | ||
|
|
d727100304 | ||
|
|
543fcf5447 | ||
|
|
e9b140b75c | ||
|
|
92a9843ef7 | ||
|
|
12528d6e6e | ||
|
|
8fae55487a | ||
|
|
973af4650c | ||
|
|
5f192d5dc7 | ||
|
|
ec39546fed | ||
|
|
ea0cafa229 | ||
|
|
978fa17932 | ||
|
|
6602e800ac | ||
|
|
fdf9209605 | ||
|
|
8a3739ee1c | ||
|
|
89f360d1a7 | ||
|
|
d7ddf991a9 | ||
|
|
19c02be673 | ||
|
|
adbcc48de7 | ||
|
|
a5eb031401 | ||
|
|
10351c5bdc | ||
|
|
5bc60d4b63 | ||
|
|
32a659a477 | ||
|
|
40c4dbc20e | ||
|
|
f2b4e49ff3 | ||
|
|
39dd67af78 | ||
|
|
337ec6bca6 | ||
|
|
2994e69c08 | ||
|
|
49987b8793 | ||
|
|
0f5e125992 | ||
|
|
57857559f5 | ||
|
|
4eba620bee | ||
|
|
964a2e990e | ||
|
|
761bc941a8 | ||
|
|
aa5a3ed080 | ||
|
|
6fbbf899b0 | ||
|
|
1df0eea6c6 | ||
|
|
ef99e9d6f8 | ||
|
|
a685d2e97d | ||
|
|
df04acc1b9 | ||
|
|
f510faa1da | ||
|
|
3ad2c74519 | ||
|
|
0e58204501 | ||
|
|
f847807df5 | ||
|
|
81982aa821 | ||
|
|
08fc393451 | ||
|
|
a7033b68f7 | ||
|
|
3794aa425d | ||
|
|
a1ea8dfd84 | ||
|
|
79e1df1af2 | ||
|
|
fd61e757f0 | ||
|
|
7ec245925a | ||
|
|
f913cd742f | ||
|
|
cc7a0db35c | ||
|
|
6f11873d7e | ||
|
|
4b1b52caf0 | ||
|
|
e9ebfe36b0 | ||
|
|
43149498cf | ||
|
|
43070a1f5b | ||
|
|
f135bd86ac | ||
|
|
0bd8c5d115 | ||
|
|
9de10df90d | ||
|
|
46ed6e2487 | ||
|
|
d81d409051 | ||
|
|
5f75c8684f | ||
|
|
5d08499d20 | ||
|
|
4115184952 | ||
|
|
e6a14977b9 | ||
|
|
a449f7a5e3 | ||
|
|
51302cfd88 | ||
|
|
696612385a | ||
|
|
edf047dde8 | ||
|
|
166cd6c426 | ||
|
|
51010096bb | ||
|
|
4e5a7ba483 | ||
|
|
92803c1536 | ||
|
|
cc593fb6c4 | ||
|
|
aeabc0c436 | ||
|
|
32ad8ddb53 | ||
|
|
f084bc4147 | ||
|
|
cb1dcc2c9f | ||
|
|
8030de7af2 | ||
|
|
dd5ddbcc0f | ||
|
|
f587710d1c | ||
|
|
99142525b6 | ||
|
|
2d9ef1142d | ||
|
|
547169ea78 | ||
|
|
8d08ab20ec | ||
|
|
966a780432 | ||
|
|
99abc4e78a | ||
|
|
ce1d581c3f | ||
|
|
b31ae6e690 | ||
|
|
e984fde966 | ||
|
|
5d0b474a7e | ||
|
|
d09f2c4f26 | ||
|
|
dd20a16d3a | ||
|
|
7d22b71e86 | ||
|
|
567ccbfc3a | ||
|
|
ca40775678 | ||
|
|
4916e5542a | ||
|
|
ac8ec2b622 | ||
|
|
9b9f7e2a1d | ||
|
|
28ea91e6c3 | ||
|
|
0d58307e82 | ||
|
|
c8ca84ede9 | ||
|
|
ed2939c39e | ||
|
|
d36bd253e8 | ||
|
|
4615dce0a9 | ||
|
|
7ce8aa3638 | ||
|
|
9236b92dc1 | ||
|
|
05ffcab911 | ||
|
|
61b9c0970b | ||
|
|
3b3701135f | ||
|
|
f5987fde5a | ||
|
|
e56b34f174 | ||
|
|
0fb43ccc2b | ||
|
|
48db4df028 | ||
|
|
86a0693081 | ||
|
|
6295c1d0c3 | ||
|
|
35d29c8e30 | ||
|
|
ca78c4c2fc | ||
|
|
f276409cde | ||
|
|
682f7bee72 | ||
|
|
43bec6a636 | ||
|
|
6eb9c750a7 | ||
|
|
31c93c8c5c | ||
|
|
5aa1046541 | ||
|
|
0f4504476a | ||
|
|
3897245a7e | ||
|
|
373db3de95 | ||
|
|
a1437658af | ||
|
|
f4ec69021d | ||
|
|
6012f20fb3 | ||
|
|
95da957f73 | ||
|
|
642722bdb1 | ||
|
|
cbc31f3c18 | ||
|
|
f2309ba079 | ||
|
|
246ed15d6d | ||
|
|
88756c83c7 | ||
|
|
cf320266df | ||
|
|
47e7e80afe | ||
|
|
0d880babfb | ||
|
|
28ba34f1bf | ||
|
|
e786cb8180 | ||
|
|
458584d778 | ||
|
|
2c711ea206 | ||
|
|
7b25be51a5 | ||
|
|
45b05a4846 | ||
|
|
6972736abf | ||
|
|
3798654f92 | ||
|
|
fdf01dfb50 | ||
|
|
876f091244 | ||
|
|
2988561f01 | ||
|
|
fbfc319ac1 | ||
|
|
9b1417f32d | ||
|
|
c727762940 | ||
|
|
e3565d5bcb | ||
|
|
a3f550c585 | ||
|
|
c610d804b1 | ||
|
|
3d592dbc79 | ||
|
|
0186f2f456 | ||
|
|
d817a813b0 | ||
|
|
1d219ba511 | ||
|
|
285afb6d4b | ||
|
|
ca3708df71 | ||
|
|
c2aafb14c2 | ||
|
|
d4d1acb555 | ||
|
|
d1a59f8c1b | ||
|
|
2fd17f97b6 | ||
|
|
45a3bdffe0 | ||
|
|
f050c69ccd | ||
|
|
c31a2551f1 | ||
|
|
90e93492a7 | ||
|
|
54266bf63b | ||
|
|
ba12f7aec9 | ||
|
|
deafb6fe08 | ||
|
|
091be1440a | ||
|
|
030aee17f5 | ||
|
|
bbbf836374 | ||
|
|
f1b91ef360 | ||
|
|
f6c59b456f | ||
|
|
8f3f941600 | ||
|
|
e561f49e80 | ||
|
|
2ff884fd11 | ||
|
|
296af748ee | ||
|
|
0cb10fcc34 | ||
|
|
4a67ea0b29 | ||
|
|
8e94acc98f | ||
|
|
39cda3764b | ||
|
|
97f5175c84 | ||
|
|
78f8e5055e | ||
|
|
735d896260 | ||
|
|
cb7a6596ba | ||
|
|
fdaa56fd86 | ||
|
|
667b940feb | ||
|
|
bb3de7d510 | ||
|
|
7bdde15796 | ||
|
|
c412ba66d8 | ||
|
|
dd62bee264 | ||
|
|
f886eac7b5 | ||
|
|
623079ca40 | ||
|
|
ce9bd796d6 | ||
|
|
d5f323a2cd | ||
|
|
40592a93ac | ||
|
|
dc1a5a860c | ||
|
|
f7b431902d | ||
|
|
686d47a59e | ||
|
|
eaa4688182 | ||
|
|
72645aa800 | ||
|
|
7044fc8004 | ||
|
|
9e8c780d66 | ||
|
|
e1795d687f | ||
|
|
607f7cba8d | ||
|
|
2572e23928 | ||
|
|
60921031bd | ||
|
|
77550d902c | ||
|
|
41935781fb | ||
|
|
47362559ef | ||
|
|
032ef0722d | ||
|
|
6e32f03a6b | ||
|
|
5731dcf135 | ||
|
|
66e0f54d0e | ||
|
|
cadc9cecf1 | ||
|
|
491e2dfe56 | ||
|
|
29c7b342eb | ||
|
|
ea601a7d03 | ||
|
|
dac45659c0 | ||
|
|
4f72c60eb0 | ||
|
|
7434fdec6f | ||
|
|
c4f3ea901a | ||
|
|
58ad9f2ca8 | ||
|
|
3fa06cc7c0 | ||
|
|
d551e39a1c | ||
|
|
afede0ff9c | ||
|
|
d51b065723 | ||
|
|
752a2d2e8d | ||
|
|
b57eaca365 | ||
|
|
859574c014 | ||
|
|
0505b123a0 | ||
|
|
00b3d7809c | ||
|
|
ec69849749 | ||
|
|
8bf7aa9ceb | ||
|
|
ca0b6f2c6d | ||
|
|
5182a61fcc | ||
|
|
2f7b949bd1 | ||
|
|
4100e48fe2 | ||
|
|
132b211796 | ||
|
|
0d4d8efe4e | ||
|
|
393bea6d4b | ||
|
|
b2edb0441a | ||
|
|
9d02379583 | ||
|
|
3c365d65a4 | ||
|
|
2049852a80 | ||
|
|
be6a7490fd | ||
|
|
222d0b74d0 | ||
|
|
cdde0dedc8 | ||
|
|
6b14facd29 | ||
|
|
a7d6a4968e | ||
|
|
2173707925 | ||
|
|
75c03d56d2 | ||
|
|
85ec807417 | ||
|
|
e7df0d201f | ||
|
|
026713750d | ||
|
|
d19d62b1fc | ||
|
|
1f9d0181c9 | ||
|
|
4d91e7f347 | ||
|
|
9bfdfa149e | ||
|
|
214e542da4 | ||
|
|
daf74347a3 | ||
|
|
61fd327aa1 | ||
|
|
91dcfefc5c | ||
|
|
8059f22a32 | ||
|
|
a271658154 | ||
|
|
e6854d6a6a | ||
|
|
1ede41c778 | ||
|
|
ed905aa0ff | ||
|
|
e28b72e356 | ||
|
|
daf007fae7 | ||
|
|
684c339309 | ||
|
|
3a44b840be | ||
|
|
5db041adb7 | ||
|
|
ce704a7d92 | ||
|
|
0a9dca5be7 | ||
|
|
5ccb7a7b9a | ||
|
|
4bd24a4ffe | ||
|
|
56e7c15064 | ||
|
|
a2ffac9424 | ||
|
|
ca35c90cbb | ||
|
|
74d59705ad | ||
|
|
adc51d3773 | ||
|
|
218946d5de | ||
|
|
6b32fcef0f | ||
|
|
016d47b9d7 | ||
|
|
1bf1a56b01 | ||
|
|
0413302470 | ||
|
|
01c934f53a | ||
|
|
c1aac6c85e | ||
|
|
ed292a1e7a | ||
|
|
61b164a183 | ||
|
|
d196169bea | ||
|
|
a2284a5143 | ||
|
|
7486468537 | ||
|
|
e475e763db | ||
|
|
dfdd06b24c | ||
|
|
510ed25509 | ||
|
|
9f2f01c17d | ||
|
|
12ba05385b | ||
|
|
f113a2befe | ||
|
|
cc8a1ccc8b | ||
|
|
2f88994c37 | ||
|
|
e016c8a702 | ||
|
|
982d367b11 | ||
|
|
d70f7422b7 | ||
|
|
56b2e09b01 | ||
|
|
ee3d6ec24b | ||
|
|
03beca1099 | ||
|
|
588f8af966 | ||
|
|
eee5e9cd7f | ||
|
|
27a78a00fd | ||
|
|
0aae605ac4 | ||
|
|
5cfcca0bc4 | ||
|
|
daf4e5cad6 | ||
|
|
27b5d13733 | ||
|
|
5d405b4d10 | ||
|
|
4519e24297 | ||
|
|
2fd81c7ffd | ||
|
|
106c35344b | ||
|
|
12f64e5fde | ||
|
|
bcbcb1f23c | ||
|
|
4b20e35fd2 | ||
|
|
f332963c75 | ||
|
|
a5f6756659 | ||
|
|
ebe0276141 | ||
|
|
9d47ba9031 | ||
|
|
09dd25dc0a | ||
|
|
dfc249135f | ||
|
|
c3d755a3e2 | ||
|
|
1195d2f2e4 | ||
|
|
b05f478207 | ||
|
|
1c28d4f610 | ||
|
|
77baf6f522 | ||
|
|
f583df6c7d | ||
|
|
4093fadcae | ||
|
|
16adacb722 | ||
|
|
5e5714baeb | ||
|
|
f60986de8e | ||
|
|
a6f4d0cdec | ||
|
|
64d147bf96 | ||
|
|
fe3facfc95 | ||
|
|
3555fc01c5 | ||
|
|
ab02cb0a19 | ||
|
|
24815c0370 | ||
|
|
a03e8dd879 | ||
|
|
2ea0bbe5ca | ||
|
|
21e1d01394 | ||
|
|
ab842cbd73 | ||
|
|
e864c1aaac | ||
|
|
a80f9e9ca7 | ||
|
|
959a404e1c | ||
|
|
7546655695 | ||
|
|
be0e50f983 | ||
|
|
baaf84298c | ||
|
|
dfeb9dbc84 | ||
|
|
4fd4b3dfad | ||
|
|
7709f4e307 | ||
|
|
26eec66f89 | ||
|
|
e3c3233b3a | ||
|
|
f0a44e54d0 | ||
|
|
7a6ee756b8 | ||
|
|
944b3a5b6c | ||
|
|
4a9bac3cd5 | ||
|
|
2e630a48f7 | ||
|
|
baf1e88689 | ||
|
|
80953a2286 | ||
|
|
6393bfda37 | ||
|
|
d0bdfcd20b | ||
|
|
e7b615c7e7 | ||
|
|
2fd370f4ae | ||
|
|
6b86ef3015 | ||
|
|
10f6aa3e4e | ||
|
|
d5365fbf0c | ||
|
|
66d4034a4e | ||
|
|
f4d1049be4 | ||
|
|
c2ff6f4e6b | ||
|
|
79834b9566 | ||
|
|
5459be46a4 | ||
|
|
d584edf546 | ||
|
|
0cba735eb3 | ||
|
|
62978e5d34 | ||
|
|
3ea32212d7 | ||
|
|
ba6373daa4 | ||
|
|
18b717594f | ||
|
|
76e304c34e | ||
|
|
2e74a78f46 | ||
|
|
6a146e239a | ||
|
|
cecb8b8d31 | ||
|
|
339541a56f | ||
|
|
6709087760 | ||
|
|
069544eb93 | ||
|
|
17f769c40d | ||
|
|
ba20790ed2 | ||
|
|
cb3bace15e | ||
|
|
7c6f4d7bff | ||
|
|
f0a56b4201 | ||
|
|
d2a26017d7 | ||
|
|
61048757e6 | ||
|
|
25b4745997 | ||
|
|
7fec97561b | ||
|
|
1957d6f432 | ||
|
|
823881f7f1 | ||
|
|
11f75f72ee | ||
|
|
5747b72a41 | ||
|
|
44510b5b64 | ||
|
|
6a1a991c08 | ||
|
|
33637e92b5 | ||
|
|
4c06fd2fb8 | ||
|
|
f5c529b2b3 | ||
|
|
b7349e42c7 | ||
|
|
a4d6932ed8 | ||
|
|
d23d382038 | ||
|
|
bde476dfb7 | ||
|
|
6ae86ce906 | ||
|
|
b2121b25c1 | ||
|
|
a08afcf11a | ||
|
|
919110b024 | ||
|
|
9c25a1609e | ||
|
|
d86bf44e39 | ||
|
|
e61ee528ff | ||
|
|
82b56613e9 | ||
|
|
78723887e1 | ||
|
|
920b403ee3 | ||
|
|
390c1469b1 | ||
|
|
b605c463a1 | ||
|
|
00491c8d90 | ||
|
|
cc3a9f1e81 | ||
|
|
60e7362f4e | ||
|
|
f2bab005d0 | ||
|
|
7068a883ed | ||
|
|
3783ec6a23 | ||
|
|
0aaa02b347 | ||
|
|
1af3bf5da7 | ||
|
|
d5a57564fe | ||
|
|
2d92858193 | ||
|
|
f666751f5f | ||
|
|
58603ed12a | ||
|
|
d005b5d2d9 | ||
|
|
a13b5bf8c0 | ||
|
|
9d351317b8 | ||
|
|
f29d784e13 | ||
|
|
2e582e3a45 | ||
|
|
566147dfae | ||
|
|
3e5967c5a7 | ||
|
|
52fda5aeb7 | ||
|
|
a657a23aaa | ||
|
|
b32055290a | ||
|
|
cc97c0e525 | ||
|
|
ffb324f685 | ||
|
|
ad53a0bf4c | ||
|
|
2cc07f0e73 | ||
|
|
b3d3794e1d | ||
|
|
82a3017629 | ||
|
|
d511080814 | ||
|
|
db1373d572 | ||
|
|
ea7483f9a7 | ||
|
|
7441720a88 | ||
|
|
0d0dd7d57c | ||
|
|
e5c7e52d72 | ||
|
|
72f4331703 | ||
|
|
5f02320e8e | ||
|
|
3449525ead | ||
|
|
1c17ec5599 | ||
|
|
6611c865f7 | ||
|
|
016c93e795 | ||
|
|
f518bdadbd | ||
|
|
b408baf254 | ||
|
|
58441634d6 | ||
|
|
d5c8021b41 | ||
|
|
929b5176ce | ||
|
|
179a65ed8b | ||
|
|
dd9a2e1818 | ||
|
|
0b5656dcc4 | ||
|
|
037d77f28e | ||
|
|
301e95b708 | ||
|
|
874bac7de2 | ||
|
|
4668f429fb | ||
|
|
d43f25ec70 | ||
|
|
bdee628360 | ||
|
|
a35530f63b | ||
|
|
21d6c1326c | ||
|
|
bfafc692db | ||
|
|
3a068b9719 | ||
|
|
9530100455 | ||
|
|
cab329556c | ||
|
|
1c5d6cbe94 | ||
|
|
576bc80716 | ||
|
|
8042aa39bf | ||
|
|
409b3ccd6c | ||
|
|
c89d19cd27 | ||
|
|
f9ab16049b | ||
|
|
09300c209f | ||
|
|
d199e0fa0f | ||
|
|
89cab26888 | ||
|
|
31b900a71c | ||
|
|
76a58cd843 | ||
|
|
dbdc900e94 | ||
|
|
e3ec4ebd21 | ||
|
|
2ab529a3f5 | ||
|
|
a82fdcca4b | ||
|
|
0627b36bf6 | ||
|
|
e8a5b5ab9c |
5
.github/codecov.yml
vendored
Normal file
5
.github/codecov.yml
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
comment: false
|
||||
ignore:
|
||||
- "lib/third_party" # Third party libraries should be ignored
|
||||
- "lib/external" # Our own libraries should be checked in their own repositories
|
||||
- "tests" # https://about.codecov.io/blog/should-i-include-test-files-in-code-coverage-calculations/
|
||||
6
.github/workflows/analysis.yml
vendored
6
.github/workflows/analysis.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -26,14 +26,14 @@ jobs:
|
||||
languages: 'cpp'
|
||||
|
||||
- name: 📜 Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ runner.os }}-analysis-build-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-analysis-build
|
||||
max-size: 50M
|
||||
|
||||
- name: 📜 Restore CMakeCache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
|
||||
210
.github/workflows/build.yml
vendored
210
.github/workflows/build.yml
vendored
@@ -2,7 +2,10 @@ name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["*"]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
- 'tests/**'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -22,46 +25,30 @@ jobs:
|
||||
CCACHE_DIR: "${{ github.workspace }}/.ccache"
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: 📜 Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
id: cache-ccache
|
||||
with:
|
||||
key: ${{ runner.os }}-ccache-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-ccache
|
||||
max-size: 1G
|
||||
|
||||
- name: 📜 Restore CMakeCache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
key: ${{ runner.os }}-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: 🟦 Install msys2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: mingw64
|
||||
pacboy: >-
|
||||
gcc:p
|
||||
lld:p
|
||||
cmake:p
|
||||
ccache:p
|
||||
glfw:p
|
||||
file:p
|
||||
mbedtls:p
|
||||
freetype:p
|
||||
dlfcn:p
|
||||
libbacktrace:p
|
||||
ninja:p
|
||||
curl-winssl:p
|
||||
capstone:p
|
||||
|
||||
- name: ⬇️ Install dependencies
|
||||
run: |
|
||||
set -x
|
||||
dist/get_deps_msys2.sh
|
||||
|
||||
- name: ⬇️ Install .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '8.0.100'
|
||||
|
||||
@@ -70,24 +57,29 @@ jobs:
|
||||
echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV
|
||||
|
||||
# Windows cmake build
|
||||
- name: 🛠️ Build
|
||||
- name: 🛠️ Configure CMake
|
||||
run: |
|
||||
set -x
|
||||
mkdir -p build
|
||||
cd build
|
||||
|
||||
cmake -G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
|
||||
-DCMAKE_INSTALL_PREFIX="$PWD/install" \
|
||||
-DIMHEX_GENERATE_PACKAGE=ON \
|
||||
-DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON \
|
||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
|
||||
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
|
||||
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
|
||||
-DUSE_SYSTEM_CAPSTONE=ON \
|
||||
cmake -G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
|
||||
-DCMAKE_INSTALL_PREFIX="$PWD/install" \
|
||||
-DIMHEX_GENERATE_PACKAGE=ON \
|
||||
-DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON \
|
||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
|
||||
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
|
||||
-DUSE_SYSTEM_CAPSTONE=ON \
|
||||
-DIMHEX_GENERATE_PDBS=ON \
|
||||
-DIMHEX_REPLACE_DWARF_WITH_PDB=ON \
|
||||
-DDOTNET_EXECUTABLE="C:/Program Files/dotnet/dotnet.exe" \
|
||||
..
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: |
|
||||
cd build
|
||||
ninja install
|
||||
cpack
|
||||
mv ImHex-*.msi ../imhex-${{env.IMHEX_VERSION}}-Windows-x86_64.msi
|
||||
@@ -144,7 +136,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -153,19 +145,12 @@ jobs:
|
||||
echo "IMHEX_VERSION=`cat VERSION`" >> $GITHUB_ENV
|
||||
|
||||
- name: 📜 Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ runner.os }}${{ matrix.suffix }}-ccache-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}${{ matrix.suffix }}-ccache
|
||||
max-size: 1G
|
||||
|
||||
- name: 📜 Restore CMakeCache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
key: ${{ runner.os }}-${{ matrix.suffix }}-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: ⬇️ Install dependencies
|
||||
run: |
|
||||
set -x
|
||||
@@ -179,13 +164,13 @@ jobs:
|
||||
brew install glfw
|
||||
|
||||
- name: ⬇️ Install .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '8.0.100'
|
||||
|
||||
- name: 🧰 Checkout glfw
|
||||
if: ${{matrix.custom_glfw}}
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: glfw/glfw
|
||||
path: glfw
|
||||
@@ -212,7 +197,7 @@ jobs:
|
||||
ninja install
|
||||
|
||||
# MacOS cmake build
|
||||
- name: 🛠️ Build
|
||||
- name: 🛠️ Configure CMake
|
||||
run: |
|
||||
set -x
|
||||
mkdir -p build
|
||||
@@ -229,20 +214,45 @@ jobs:
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_INSTALL_PREFIX="./install" \
|
||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
|
||||
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
|
||||
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
|
||||
-DCPACK_PACKAGE_FILE_NAME="imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64" \
|
||||
..
|
||||
ninja package
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: cd build && ninja install
|
||||
|
||||
- name: ✒️ Fix Signature
|
||||
run: |
|
||||
set -x
|
||||
cd build/install
|
||||
mv imhex.app ImHex.app
|
||||
codesign --remove-signature ImHex.app
|
||||
codesign --force --deep --sign - ImHex.app
|
||||
|
||||
- name: 📁 Fix permissions
|
||||
run: |
|
||||
set -x
|
||||
cd build/install
|
||||
chmod -R 755 ImHex.app/
|
||||
|
||||
- name: 📦 Create DMG
|
||||
run: |
|
||||
set -x
|
||||
mkdir bundle
|
||||
mv build/install/ImHex.app bundle
|
||||
cd bundle
|
||||
ln -s /Applications Applications
|
||||
cd ..
|
||||
hdiutil create -volname "ImHex" -srcfolder bundle -ov -format UDZO imhex-${{env.IMHEX_VERSION}}-macOS${{matrix.suffix}}-x86_64.dmg
|
||||
|
||||
- name: ⬆️ Upload DMG
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
if-no-files-found: error
|
||||
name: macOS DMG${{matrix.suffix}} x86_64
|
||||
path: build/*.dmg
|
||||
path: ./*.dmg
|
||||
|
||||
macos-arm64-build:
|
||||
runs-on: ubuntu-22.04
|
||||
@@ -251,18 +261,18 @@ jobs:
|
||||
IMHEX_VERSION: ${{ steps.build.outputs.IMHEX_VERSION }}
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: 📁 Restore docker /cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: cache
|
||||
key: build-macos-arm64-cache
|
||||
|
||||
- name: 🐳 Inject /cache into docker
|
||||
uses: reproducible-containers/buildkit-cache-dance@v2.1.2 # Doesn't work with a MacOS runner ?
|
||||
uses: reproducible-containers/buildkit-cache-dance@v2
|
||||
with:
|
||||
cache-source: cache
|
||||
cache-target: /cache
|
||||
@@ -302,15 +312,15 @@ jobs:
|
||||
path: out
|
||||
|
||||
- name: 🗑️ Delete artifact
|
||||
uses: geekyeggo/delete-artifact@v4
|
||||
uses: geekyeggo/delete-artifact@v5
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: macos_arm64_intermediate
|
||||
|
||||
- name: ✒️ Fix Signature
|
||||
run: |
|
||||
set -x
|
||||
cd out
|
||||
mv imhex.app ImHex.app
|
||||
codesign --remove-signature ImHex.app
|
||||
codesign --force --deep --sign - ImHex.app
|
||||
|
||||
@@ -343,10 +353,8 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: Ubuntu
|
||||
release_num: 22.04
|
||||
- name: Ubuntu
|
||||
release_num: 23.04
|
||||
- release_num: 22.04
|
||||
- release_num: 23.04
|
||||
|
||||
name: 🐧 Ubuntu ${{ matrix.release_num }}
|
||||
runs-on: ubuntu-latest
|
||||
@@ -360,56 +368,55 @@ jobs:
|
||||
run: apt update && apt install -y git curl
|
||||
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: 📜 Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: Ubuntu-${{matrix.release_num}}-ccache-${{ github.run_id }}
|
||||
restore-keys: Ubuntu-${{matrix.release_num}}-ccache
|
||||
max-size: 1G
|
||||
|
||||
- name: 📜 Restore CMakeCache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
key: Ubuntu-${{matrix.release_num}}-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: ⬇️ Install dependencies
|
||||
run: |
|
||||
apt update
|
||||
bash dist/get_deps_debian.sh
|
||||
|
||||
apt install software-properties-common -y
|
||||
add-apt-repository ppa:ubuntu-toolchain-r/test -y
|
||||
apt update
|
||||
apt install -y gcc-13 g++-13
|
||||
|
||||
- name: ⬇️ Install .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '8.0.100'
|
||||
|
||||
# Ubuntu cmake build
|
||||
- name: 🛠️ Build
|
||||
- name: 🛠️ Configure CMake
|
||||
shell: bash
|
||||
run: |
|
||||
set -x
|
||||
git config --global --add safe.directory '*'
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=gcc-12 CXX=g++-12 cmake -G "Ninja" \
|
||||
CC=gcc-13 CXX=g++-13 cmake -G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
|
||||
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
|
||||
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
|
||||
-DIMHEX_ENABLE_LTO=ON \
|
||||
-DIMHEX_USE_GTK_FILE_PICKER=ON \
|
||||
-DDOTNET_EXECUTABLE="dotnet" \
|
||||
..
|
||||
DESTDIR=DebDir ninja install
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: cd build && DESTDIR=DebDir ninja install
|
||||
|
||||
- name: 📜 Set version variable
|
||||
run: |
|
||||
@@ -434,19 +441,19 @@ jobs:
|
||||
name: ⬇️ AppImage
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: 📁 Restore docker /cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: cache
|
||||
key: appimage-ccache-${{ github.run_id }}
|
||||
restore-keys: appimage-cache
|
||||
|
||||
- name: 🐳 Inject /cache into docker
|
||||
uses: reproducible-containers/buildkit-cache-dance@v2.1.2
|
||||
uses: reproducible-containers/buildkit-cache-dance@v2
|
||||
with:
|
||||
cache-source: cache
|
||||
cache-target: /cache
|
||||
@@ -489,7 +496,7 @@ jobs:
|
||||
pacman -Syu git ccache --noconfirm
|
||||
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
@@ -498,26 +505,19 @@ jobs:
|
||||
dist/get_deps_archlinux.sh --noconfirm
|
||||
|
||||
- name: ⬇️ Install .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '8.0.100'
|
||||
|
||||
- name: 📜 Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: archlinux-ccache-${{ github.run_id }}
|
||||
restore-keys: archlinux-ccache
|
||||
max-size: 1G
|
||||
|
||||
- name: 📜 Restore CMakeCache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
key: archlinux-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
# ArchLinux cmake build
|
||||
- name: 🛠️ Build
|
||||
- name: 🛠️ Configure CMake
|
||||
run: |
|
||||
set -x
|
||||
mkdir -p build
|
||||
@@ -532,13 +532,14 @@ jobs:
|
||||
-DUSE_SYSTEM_NLOHMANN_JSON=ON \
|
||||
-DUSE_SYSTEM_CAPSTONE=OFF \
|
||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||
-DIMHEX_COMMIT_HASH_SHORT="${GITHUB_SHA::7}" \
|
||||
-DIMHEX_COMMIT_HASH_LONG="${GITHUB_SHA}" \
|
||||
-DIMHEX_COMMIT_BRANCH="${GITHUB_REF##*/}" \
|
||||
-DIMHEX_ENABLE_LTO=ON \
|
||||
-DIMHEX_USE_GTK_FILE_PICKER=ON \
|
||||
..
|
||||
DESTDIR=installDir ninja install
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: cd build && DESTDIR=installDir ninja install
|
||||
|
||||
- name: 📜 Set version variable
|
||||
run: |
|
||||
@@ -565,6 +566,7 @@ jobs:
|
||||
|
||||
# Replace the old file
|
||||
rm imhex-${{env.IMHEX_VERSION}}-ArchLinux-x86_64.pkg.tar.zst
|
||||
rm *imhex-bin-debug* # rm debug package which is created for some reason
|
||||
mv *.pkg.tar.zst imhex-${{env.IMHEX_VERSION}}-ArchLinux-x86_64.pkg.tar.zst
|
||||
|
||||
- name: ⬆️ Upload imhex-archlinux.pkg.tar.zst
|
||||
@@ -610,13 +612,13 @@ jobs:
|
||||
run: dnf install --disablerepo="*" --enablerepo="fedora" git-core -y
|
||||
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ImHex
|
||||
submodules: recursive
|
||||
|
||||
- name: 📜 Setup DNF Cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: /var/cache/dnf
|
||||
key: ${{ matrix.mock_release }}-dnf-${{ github.run_id }}
|
||||
@@ -632,12 +634,12 @@ jobs:
|
||||
ccache
|
||||
|
||||
- name: ⬇️ Install .NET
|
||||
uses: actions/setup-dotnet@v3
|
||||
uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: '8.0.100'
|
||||
|
||||
- name: 📜 Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2.5
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ matrix.mock_release }}-rpm-${{ github.run_id }}
|
||||
restore-keys: ${{ matrix.mock_release }}-rpm
|
||||
@@ -653,11 +655,11 @@ jobs:
|
||||
- name: ✒️ Modify spec file
|
||||
run: |
|
||||
sed -i \
|
||||
-e 's/Version: [0-9]*\.[0-9]*\.[0-9]*$/Version: ${{env.IMHEX_VERSION}}/g' \
|
||||
-e 's/IMHEX_OFFLINE_BUILD=ON/IMHEX_OFFLINE_BUILD=OFF/g' \
|
||||
-e '/IMHEX_OFFLINE_BUILD=OFF/a -D IMHEX_PATTERNS_PULL_MASTER=ON \\' \
|
||||
-e '/BuildRequires: cmake/a BuildRequires: git-core' \
|
||||
-e '/%files/a %{_datadir}/%{name}/' \
|
||||
-e 's/Version: VERSION$/Version: ${{env.IMHEX_VERSION}}/g' \
|
||||
-e 's/IMHEX_OFFLINE_BUILD=ON/IMHEX_OFFLINE_BUILD=OFF/g' \
|
||||
-e '/IMHEX_OFFLINE_BUILD=OFF/a -D IMHEX_PATTERNS_PULL_MASTER=ON \\' \
|
||||
-e '/BuildRequires: cmake/a BuildRequires: git-core' \
|
||||
-e '/%files/a %{_datadir}/%{name}/' \
|
||||
$GITHUB_WORKSPACE/ImHex/dist/rpm/imhex.spec
|
||||
|
||||
- name: 📜 Fix ccache on EL9
|
||||
@@ -677,14 +679,6 @@ jobs:
|
||||
config_opts['plugin_conf']['ccache_opts']['dir'] = "$GITHUB_WORKSPACE/.ccache"
|
||||
EOT
|
||||
|
||||
- name: 📜 Setup Mock Cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /var/cache/mock
|
||||
key: ${{ matrix.mock_release }}-mock-${{ github.run_id }}
|
||||
restore-keys: |
|
||||
${{ matrix.mock_release }}-mock
|
||||
|
||||
# Fedora cmake build (in imhex.spec)
|
||||
- name: 📦 Build RPM
|
||||
run: |
|
||||
|
||||
15
.github/workflows/build_web.yml
vendored
15
.github/workflows/build_web.yml
vendored
@@ -2,7 +2,10 @@ name: Build for the web
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["*"]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
- 'tests/**'
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -21,25 +24,25 @@ jobs:
|
||||
name: 🌍 WebAssembly
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: 📁 Restore docker /cache
|
||||
uses: actions/cache@v3
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: cache
|
||||
key: web-cmakecache-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
key: web-cache-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: 🐳 Inject /cache into docker
|
||||
uses: reproducible-containers/buildkit-cache-dance@v2.1.2
|
||||
uses: reproducible-containers/buildkit-cache-dance@v2
|
||||
with:
|
||||
cache-source: cache
|
||||
cache-target: /cache
|
||||
|
||||
- name: 🛠️ Build using docker
|
||||
run: |
|
||||
docker buildx build . -f dist/web/Dockerfile --progress=plain --build-arg 'JOBS=4' --output out
|
||||
docker buildx build . -f dist/web/Dockerfile --progress=plain --build-arg 'JOBS=4' --output out --target raw
|
||||
|
||||
- name: 🔨 Fix permissions
|
||||
run: |
|
||||
|
||||
18
.github/workflows/release.yml
vendored
18
.github/workflows/release.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ImHex
|
||||
|
||||
@@ -52,13 +52,23 @@ jobs:
|
||||
repo: ImHex-Patterns
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
- name: 🎫 Create imhex-download-sdk release
|
||||
uses: ncipollo/release-action@v1
|
||||
env:
|
||||
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
|
||||
if: "${{ env.RELEASE_TOKEN != '' }}"
|
||||
with:
|
||||
tag: v${{ env.IMHEX_VERSION }}
|
||||
repo: imhex-download-sdk
|
||||
token: ${{ secrets.RELEASE_TOKEN }}
|
||||
|
||||
release-upload-artifacts:
|
||||
runs-on: ubuntu-latest
|
||||
name: Release Upload Artifacts
|
||||
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: ImHex
|
||||
submodules: recursive
|
||||
@@ -80,7 +90,7 @@ jobs:
|
||||
run: tar --exclude-vcs -czvf Full.Sources.tar.gz ImHex
|
||||
|
||||
- name: ⬇️ Download artifacts from latest workflow
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
with:
|
||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||
workflow: build.yml
|
||||
@@ -109,7 +119,7 @@ jobs:
|
||||
mv "Windows Portable NoGPU x86_64.zip" imhex-${{ env.IMHEX_VERSION }}-Windows-Portable-NoGPU-x86_64.zip
|
||||
|
||||
- name: ⬆️ Upload everything to release
|
||||
uses: softprops/action-gh-release@v1
|
||||
uses: softprops/action-gh-release@4634c16e79c963813287e889244c50009e7f0981
|
||||
with:
|
||||
files: '*'
|
||||
|
||||
|
||||
68
.github/workflows/tests.yml
vendored
68
.github/workflows/tests.yml
vendored
@@ -2,9 +2,14 @@ name: "Unit Tests"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
- 'tests/**'
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches:
|
||||
- 'master'
|
||||
- 'releases/**'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
@@ -18,46 +23,71 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: 📜 Setup ccache
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
uses: hendrikmuhs/ccache-action@v1
|
||||
with:
|
||||
key: ${{ runner.os }}-tests-build-${{ github.run_id }}
|
||||
restore-keys: ${{ runner.os }}-tests-build
|
||||
max-size: 50M
|
||||
|
||||
|
||||
- name: 📜 Restore CMakeCache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
build/CMakeCache.txt
|
||||
key: ${{ runner.os }}-tests-build-${{ hashFiles('**/CMakeLists.txt') }}
|
||||
|
||||
- name: ⬇️ Install dependencies
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo bash dist/get_deps_debian.sh
|
||||
sudo apt install gcovr -y
|
||||
|
||||
- name: 🛠️ Build
|
||||
run: |
|
||||
set -x
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=gcc-12 CXX=g++-12 cmake \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_C_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all" \
|
||||
-DCMAKE_CXX_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all" \
|
||||
-DIMHEX_OFFLINE_BUILD=ON \
|
||||
CC=gcc-12 CXX=g++-12 cmake \
|
||||
-DCMAKE_BUILD_TYPE=Debug \
|
||||
-DIMHEX_ENABLE_UNIT_TESTS=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_C_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all --coverage" \
|
||||
-DCMAKE_CXX_FLAGS="-fuse-ld=lld -fsanitize=address,leak,undefined -fno-sanitize-recover=all --coverage" \
|
||||
-DIMHEX_OFFLINE_BUILD=ON \
|
||||
..
|
||||
make -j4 unit_tests
|
||||
|
||||
- name: 🧪 Perform plcli Integration Tests
|
||||
run: |
|
||||
cd lib/external/pattern_language
|
||||
python tests/integration/integration.py ../../../build/imhex --pl
|
||||
|
||||
- name: 🧪 Perform Unit Tests
|
||||
run: |
|
||||
cd build
|
||||
ctest --output-on-failure
|
||||
|
||||
# Generate report from all gcov .gcda files
|
||||
- name: 🧪 Generate coverage report
|
||||
run: |
|
||||
gcovr --gcov-executable /usr/bin/gcov-12 -r . build --xml coverage_report.xml --verbose
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
fail_ci_if_error: true
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
file: coverage_report.xml
|
||||
|
||||
langs:
|
||||
name: 🧪 Langs
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- name: 🧰 Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Check langs
|
||||
run:
|
||||
python3 tests/check_langs.py
|
||||
|
||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
.vscode/
|
||||
.idea/
|
||||
.kdev4/
|
||||
|
||||
cmake-build-*/
|
||||
build*/
|
||||
@@ -7,7 +8,8 @@ local/
|
||||
venv/
|
||||
|
||||
*.mgc
|
||||
*.kdev4
|
||||
imgui.ini
|
||||
.DS_Store
|
||||
./CMakeUserPresets.json
|
||||
Brewfile.lock.json
|
||||
CMakeUserPresets.json
|
||||
Brewfile.lock.json
|
||||
|
||||
12
.gitmodules
vendored
12
.gitmodules
vendored
@@ -8,7 +8,7 @@
|
||||
ignore = dirty
|
||||
[submodule "lib/third_party/xdgpp"]
|
||||
path = lib/third_party/xdgpp
|
||||
url = https://git.sr.ht/~danyspin97/xdgpp
|
||||
url = https://github.com/WerWolv/xdgpp
|
||||
ignore = dirty
|
||||
[submodule "lib/third_party/fmt"]
|
||||
path = lib/third_party/fmt
|
||||
@@ -22,6 +22,14 @@
|
||||
path = lib/third_party/jthread/jthread
|
||||
url = https://github.com/josuttis/jthread
|
||||
ignore = dirty
|
||||
[submodule "lib/third_party/edlib"]
|
||||
path = lib/third_party/edlib
|
||||
url = https://github.com/Martinsos/edlib
|
||||
ignore = dirty
|
||||
[submodule "lib/third_party/lunasvg"]
|
||||
path = lib/third_party/lunasvg
|
||||
url = https://github.com/sammycage/lunasvg
|
||||
ignore = dirty
|
||||
|
||||
[submodule "lib/external/libromfs"]
|
||||
path = lib/external/libromfs
|
||||
@@ -35,4 +43,4 @@
|
||||
|
||||
[submodule "lib/third_party/HashLibPlus"]
|
||||
path = lib/third_party/HashLibPlus
|
||||
url = https://github.com/WerWolv/HashLibPlus
|
||||
url = https://github.com/WerWolv/HashLibPlus
|
||||
@@ -1,38 +1,54 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
|
||||
# Options
|
||||
option(IMHEX_PLUGINS_IN_SHARE "Put the plugins in share/imhex/plugins instead of lib[..]/imhex/plugins" OFF)
|
||||
option(IMHEX_STRIP_RELEASE "Strip the release builds" ON)
|
||||
option(IMHEX_OFFLINE_BUILD "Enable offline build" OFF)
|
||||
option(IMHEX_IGNORE_BAD_CLONE "Disable the bad clone prevention checks" OFF)
|
||||
option(IMHEX_PATTERNS_PULL_MASTER "Download latest files from master branch of the ImHex-Patterns repo" OFF)
|
||||
option(IMHEX_IGNORE_BAD_COMPILER "Allow compiling with an unsupported compiler" OFF)
|
||||
option(IMHEX_USE_GTK_FILE_PICKER "Use GTK file picker instead of xdg-desktop-portals" OFF)
|
||||
option(IMHEX_DISABLE_STACKTRACE "Disables support for printing stack traces" OFF)
|
||||
option(IMHEX_BUNDLE_DOTNET "Bundle .NET runtime" ON)
|
||||
option(IMHEX_ENABLE_LTO "Enables Link Time Optimizations if possible" OFF)
|
||||
option(IMHEX_USE_DEFAULT_BUILD_SETTINGS "Use default build settings" OFF)
|
||||
option(IMHEX_STRICT_WARNINGS "Enable most available warnings and treat them as errors" ON)
|
||||
option(IMHEX_STATIC_LINK_PLUGINS "Statically link all plugins into the main executable" OFF)
|
||||
option(IMHEX_GENERATE_PACKAGE "Specify if a native package should be built. Only usable on Windows and MacOS" OFF)
|
||||
option(IMHEX_ENABLE_UNITY_BUILD "Enables building ImHex as a unity build." OFF)
|
||||
option(IMHEX_PLUGINS_IN_SHARE "Put the plugins in share/imhex/plugins instead of lib[..]/imhex/plugins (Linux only)" OFF)
|
||||
option(IMHEX_STRIP_RELEASE "Strip the release builds" ON )
|
||||
option(IMHEX_OFFLINE_BUILD "Enable offline build" OFF)
|
||||
option(IMHEX_IGNORE_BAD_CLONE "Disable the bad clone prevention checks" OFF)
|
||||
option(IMHEX_PATTERNS_PULL_MASTER "Download latest files from master branch of the ImHex-Patterns repo" OFF)
|
||||
option(IMHEX_IGNORE_BAD_COMPILER "Allow compiling with an unsupported compiler" OFF)
|
||||
option(IMHEX_USE_GTK_FILE_PICKER "Use GTK file picker instead of xdg-desktop-portals (Linux only)" OFF)
|
||||
option(IMHEX_DISABLE_STACKTRACE "Disables support for printing stack traces" OFF)
|
||||
option(IMHEX_BUNDLE_DOTNET "Bundle .NET runtime" ON )
|
||||
option(IMHEX_ENABLE_LTO "Enables Link Time Optimizations if possible" OFF)
|
||||
option(IMHEX_USE_DEFAULT_BUILD_SETTINGS "Use default build settings" OFF)
|
||||
option(IMHEX_STRICT_WARNINGS "Enable most available warnings and treat them as errors" ON )
|
||||
option(IMHEX_STATIC_LINK_PLUGINS "Statically link all plugins into the main executable" OFF)
|
||||
option(IMHEX_GENERATE_PACKAGE "Specify if a native package should be built. (Windows and MacOS only)" OFF)
|
||||
option(IMHEX_ENABLE_UNITY_BUILD "Enables building ImHex as a unity build." OFF)
|
||||
option(IMHEX_GENERATE_PDBS "Enable generating PDB files in non-debug builds (Windows only)" OFF)
|
||||
option(IMHEX_REPLACE_DWARF_WITH_PDB "Remove DWARF information from binaries when generating PDBS (Windows only)" OFF)
|
||||
option(IMHEX_ENABLE_STD_ASSERTS "Enable debug asserts in the C++ std library. (Breaks Plugin ABI!)" OFF)
|
||||
option(IMHEX_ENABLE_UNIT_TESTS "Enable building unit tests" OFF)
|
||||
option(IMHEX_ENABLE_PRECOMPILED_HEADERS "Enable precompiled headers" OFF)
|
||||
option(IMHEX_COMPRESS_DEBUG_INFO "Compress debug information" ON )
|
||||
|
||||
set(IMHEX_BASE_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules")
|
||||
|
||||
# Optional IDE support
|
||||
include("${IMHEX_BASE_FOLDER}/cmake/ide_helpers.cmake")
|
||||
|
||||
# Basic compiler and cmake configurations
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_INCLUDE_DIRECTORIES_BEFORE ON)
|
||||
set(IMHEX_BASE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
set(CMAKE_MODULE_PATH "${IMHEX_BASE_FOLDER}/cmake/modules")
|
||||
include("${IMHEX_BASE_FOLDER}/cmake/build_helpers.cmake")
|
||||
|
||||
# Setup project
|
||||
loadVersion(IMHEX_VERSION)
|
||||
setVariableInParent(IMHEX_VERSION ${IMHEX_VERSION})
|
||||
configureCMake()
|
||||
|
||||
project(imhex
|
||||
LANGUAGES C CXX VERSION ${IMHEX_VERSION}
|
||||
DESCRIPTION "The ImHex Hex Editor"
|
||||
HOMEPAGE_URL "https://imhex.werwolv.net"
|
||||
LANGUAGES C CXX
|
||||
VERSION ${IMHEX_VERSION}
|
||||
DESCRIPTION "The ImHex Hex Editor"
|
||||
HOMEPAGE_URL "https://imhex.werwolv.net"
|
||||
)
|
||||
configureProject()
|
||||
|
||||
# Add ImHex sources
|
||||
add_custom_target(imhex_all ALL)
|
||||
|
||||
# Make sure project is configured correctly
|
||||
setDefaultBuiltTypeIfUnset()
|
||||
@@ -50,17 +66,23 @@ configurePackingResources()
|
||||
setUninstallTarget()
|
||||
addBundledLibraries()
|
||||
|
||||
# Add ImHex sources
|
||||
add_custom_target(imhex_all ALL)
|
||||
|
||||
add_subdirectory(lib/libimhex)
|
||||
add_subdirectory(main)
|
||||
addPluginDirectories()
|
||||
add_subdirectory(lib/trace)
|
||||
|
||||
# Add unit tests
|
||||
enable_testing()
|
||||
add_subdirectory(tests EXCLUDE_FROM_ALL)
|
||||
if (IMHEX_ENABLE_UNIT_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(tests EXCLUDE_FROM_ALL)
|
||||
endif ()
|
||||
|
||||
# Configure more resources that will be added to the install package
|
||||
createPackage()
|
||||
generatePDBs()
|
||||
generateSDKDirectory()
|
||||
generateSDKDirectory()
|
||||
|
||||
# Handle package generation
|
||||
createPackage()
|
||||
|
||||
# Accomodate IDEs with FOLDER support
|
||||
tweakTargetsForIDESupport()
|
||||
|
||||
@@ -28,6 +28,23 @@
|
||||
"displayName": "x86_64 Build",
|
||||
"description": "x86_64 build",
|
||||
"inherits": [ "base" ]
|
||||
},
|
||||
{
|
||||
"name": "xcode",
|
||||
"inherits": [ "base" ],
|
||||
|
||||
"displayName": "Xcode",
|
||||
"description": "Xcode with external compiler override",
|
||||
"generator": "Xcode",
|
||||
|
||||
"cacheVariables": {
|
||||
"CMAKE_C_COMPILER": "clang",
|
||||
|
||||
"CMAKE_CXX_COMPILER": "clang++",
|
||||
"CMAKE_CXX_FLAGS": "-fexperimental-library -Wno-shorten-64-to-32 -Wno-deprecated-declarations",
|
||||
|
||||
"IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER": "ON"
|
||||
}
|
||||
}
|
||||
],
|
||||
"buildPresets": [
|
||||
|
||||
93
README.md
93
README.md
@@ -1,8 +1,7 @@
|
||||
<a href="https://imhex.werwolv.net">
|
||||
<h1 align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="./resources/projects/logo_text_light.svg">
|
||||
<img height="100px" src="./resources/projects/logo_text_dark.svg">
|
||||
<img height="300px" src="./resources/dist/common/logo/ImHexLogoSVGBG.svg">
|
||||
</picture>
|
||||
</h1>
|
||||
</a>
|
||||
@@ -10,33 +9,19 @@
|
||||
<p align="center">A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.</p>
|
||||
|
||||
<p align="center">
|
||||
<a title="'Build' workflow Status" href="https://github.com/WerWolv/ImHex/actions?query=workflow%3ABuild">
|
||||
<img alt="'Build' workflow Status" src="https://img.shields.io/github/actions/workflow/status/WerWolv/ImHex/build.yml?longCache=true&style=for-the-badge&label=Build&logoColor=fff&logo=GitHub%20Actions&branch=master">
|
||||
</a>
|
||||
<a title="Discord Server" href="https://discord.gg/X63jZ36xBY">
|
||||
<img alt="Discord Server" src="https://img.shields.io/discord/789833418631675954?label=Discord&logo=Discord&logoColor=fff&style=for-the-badge">
|
||||
</a>
|
||||
<a title="Total Downloads" href="https://github.com/WerWolv/ImHex/releases/latest">
|
||||
<img alt="Total Downloads" src="https://img.shields.io/github/downloads/WerWolv/ImHex/total?longCache=true&style=for-the-badge&label=Downloads&logoColor=fff&logo=GitHub">
|
||||
</a>
|
||||
<a title="Code Quality" href="https://www.codefactor.io/repository/github/werwolv/imhex">
|
||||
<img alt="Code Quality" src="https://img.shields.io/codefactor/grade/github/WerWolv/ImHex?longCache=true&style=for-the-badge&label=Code%20Quality&logoColor=fff&logo=CodeFactor&branch=master">
|
||||
</a>
|
||||
<a title="Translation" href="https://weblate.werwolv.net/projects/imhex/">
|
||||
<img alt="Translation" src="https://img.shields.io/weblate/progress/imhex?logo=weblate&logoColor=%23FFFFFF&server=https%3A%2F%2Fweblate.werwolv.net&style=for-the-badge">
|
||||
</a>
|
||||
<a title="Documentation" href="https://imhex.werwolv.net/docs">
|
||||
<img alt="Documentation" src="https://img.shields.io/badge/Docs-Available-brightgreen?logo=gitbook&logoColor=%23FFFFFF&style=for-the-badge">
|
||||
</a>
|
||||
<a title="Plugins" href="https://github.com/WerWolv/ImHex/blob/master/PLUGINS.md">
|
||||
<img alt="Plugins" src="https://img.shields.io/badge/Plugins-Supported-brightgreen?logo=stackedit&logoColor=%23FFFFFF&style=for-the-badge">
|
||||
</a>
|
||||
<a title="'Build' workflow Status" href="https://github.com/WerWolv/ImHex/actions?query=workflow%3ABuild"><img alt="'Build' workflow Status" src="https://img.shields.io/github/actions/workflow/status/WerWolv/ImHex/build.yml?longCache=true&style=for-the-badge&label=Build&logoColor=fff&logo=GitHub%20Actions&branch=master"></a>
|
||||
<a title="Discord Server" href="https://discord.gg/X63jZ36xBY"><img alt="Discord Server" src="https://img.shields.io/discord/789833418631675954?label=Discord&logo=Discord&logoColor=fff&style=for-the-badge"></a>
|
||||
<a title="Total Downloads" href="https://github.com/WerWolv/ImHex/releases/latest"><img alt="Total Downloads" src="https://img.shields.io/github/downloads/WerWolv/ImHex/total?longCache=true&style=for-the-badge&label=Downloads&logoColor=fff&logo=GitHub"></a>
|
||||
<a title="Code Quality" href="https://www.codefactor.io/repository/github/werwolv/imhex"><img alt="Code Quality" src="https://img.shields.io/codefactor/grade/github/WerWolv/ImHex?longCache=true&style=for-the-badge&label=Code%20Quality&logoColor=fff&logo=CodeFactor&branch=master"></a>
|
||||
<a title="Translation" href="https://weblate.werwolv.net/projects/imhex/"><img alt="Translation" src="https://img.shields.io/weblate/progress/imhex?logo=weblate&logoColor=%23FFFFFF&server=https%3A%2F%2Fweblate.werwolv.net&style=for-the-badge"></a>
|
||||
<a title="Plugins" href="https://github.com/WerWolv/ImHex/blob/master/PLUGINS.md"><img alt="Plugins" src="https://img.shields.io/badge/Plugins-Supported-brightgreen?logo=stackedit&logoColor=%23FFFFFF&style=for-the-badge"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a title="Use the Web version of ImHex right in your browser!" href="https://web.imhex.werwolv.net">
|
||||
<img alt="Use the Web version of ImHex right in your browser!" src="resources/dist/common/try_online_banner.png">
|
||||
</a>
|
||||
<a title="Download the latest version of ImHex" href="https://imhex.download"><img alt="Download the latest version of ImHex!" src="resources/dist/common/get_release_banner.png"></a>
|
||||
<a title="Download the latest nightly pre-release version of ImHex" href="https://imhex.download/#nightly"><img alt="Download the latest nightly pre-release version of ImHex" src="resources/dist/common/get_nightly_banner.png"></a>
|
||||
<a title="Use the Web version of ImHex right in your browser!" href="https://web.imhex.werwolv.net"><img alt="Use the Web version of ImHex right in your browser!" src="resources/dist/common/try_online_banner.png"></a>
|
||||
<a title="Read the documentation of ImHex!" href="https://docs.werwolv.net"><img alt="Read the documentation of ImHex!" src="resources/dist/common/read_docs_banner.png"></a>
|
||||
</p>
|
||||
|
||||
## Supporting
|
||||
@@ -44,22 +29,21 @@
|
||||
If you like my work, please consider supporting me on GitHub Sponsors, Patreon or PayPal. Thanks a lot!
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/sponsors/WerWolv"><img src="https://werwolv.net/assets/github_banner.png" alt="GitHub donate button" /> </a>
|
||||
<a href="https://www.patreon.com/werwolv"><img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Patreon donate button" /> </a>
|
||||
<a href="https://werwolv.net/donate"><img src="https://werwolv.net/assets/paypal_banner.png" alt="PayPal donate button" /> </a>
|
||||
<a href="https://github.com/sponsors/WerWolv"><img src="https://werwolv.net/assets/github_banner.png" alt="GitHub donate button" /></a>
|
||||
<a href="https://www.patreon.com/werwolv"><img src="https://c5.patreon.com/external/logo/become_a_patron_button.png" alt="Patreon donate button" /></a>
|
||||
<a href="https://werwolv.net/donate"><img src="https://werwolv.net/assets/paypal_banner.png" alt="PayPal donate button" /></a>
|
||||
</p>
|
||||
|
||||
## Screenshots
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
<details>
|
||||
<summary><strong>More Screenshots</strong></summary>
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
</details>
|
||||
|
||||
@@ -124,7 +108,7 @@ If you like my work, please consider supporting me on GitHub Sponsors, Patreon o
|
||||
<details>
|
||||
<summary><strong>Data Inspector</strong></summary>
|
||||
|
||||
- Interpreting data as many different types with endianess, decimal, hexadecimal and octal support and bit inversion
|
||||
- Interpreting data as many different types with endianness, decimal, hexadecimal and octal support and bit inversion
|
||||
- Unsigned and signed integers (8, 16, 24, 32, 48, 64 bit)
|
||||
- Floats (16, 32, 64 bit)
|
||||
- Signed and Unsigned LEB128
|
||||
@@ -173,7 +157,7 @@ If you like my work, please consider supporting me on GitHub Sponsors, Patreon o
|
||||
- Numeric Value search
|
||||
- Search for signed/unsigned integers and floats
|
||||
- Search for ranges of values
|
||||
- Option to specify size and endianess
|
||||
- Option to specify size and endianness
|
||||
- Option to ignore unaligned values
|
||||
</details>
|
||||
<details>
|
||||
@@ -365,31 +349,44 @@ See [Contributing](/CONTRIBUTING.md)
|
||||
|
||||
## Plugin development
|
||||
|
||||
To develop plugins for ImHex, use one of the following two templates projects to get started. You then have access to the entirety of libimhex as well as the ImHex API and the Content Registry to interact with ImHex or to add new content.
|
||||
- [C++ Plugin Template](https://github.com/WerWolv/ImHex-Cpp-Plugin-Template)
|
||||
- [Rust Plugin Template](https://github.com/WerWolv/ImHex-Rust-Plugin-Template)
|
||||
To develop plugins for ImHex, use the following template project to get started. You then have access to the entirety of libimhex as well as the ImHex API and the Content Registry to interact with ImHex or to add new content.
|
||||
- [ImHex Plugin Template](https://github.com/WerWolv/ImHex-Plugin-Template)
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
### Contributors
|
||||
|
||||
- [iTrooz](https://github.com/iTrooz) for getting ImHex onto the Web as well as hundreds of contributions in every part of the project
|
||||
- [jumanji144](https://github.com/jumanji144) for huge contributions to the Pattern Language and ImHex's infrastructure
|
||||
- [Mary](https://github.com/marysaka) for her immense help porting ImHex to MacOS and help during development
|
||||
- [Roblabla](https://github.com/Roblabla) for adding MSI Installer support to ImHex
|
||||
- [jam1garner](https://github.com/jam1garner) and [raytwo](https://github.com/raytwo) for their help with adding Rust support to plugins
|
||||
- [Mailaender](https://github.com/Mailaender) for getting ImHex onto Flathub
|
||||
- [iTrooz](https://github.com/iTrooz) for many improvements and new features to Imhex
|
||||
- Everybody else who has reported issues on Discord or GitHub that I had great conversations with :)
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Thanks a lot to ocornut for their amazing [Dear ImGui](https://github.com/ocornut/imgui) which is used for building the entire interface
|
||||
- Thanks to ocornut as well for their hex editor view used as base for this project.
|
||||
- Thanks to BalazsJako for their incredible [ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit) used for the pattern language syntax highlighting
|
||||
- Thanks to nlohmann for their [json](https://github.com/nlohmann/json) library used for project files
|
||||
- Thanks to aquynh for [capstone](https://github.com/aquynh/capstone) which is the base of the disassembly window
|
||||
- Thanks to epezent for [ImPlot](https://github.com/epezent/implot) used to plot data in various places
|
||||
- Thanks to Nelarius for [ImNodes](https://github.com/Nelarius/imnodes) used as base for the data processor
|
||||
- Thanks to BalazsJako for [ImGuiColorTextEdit](https://github.com/BalazsJako/ImGuiColorTextEdit) used for the pattern language syntax highlighting
|
||||
- Thanks to nlohmann for their [json](https://github.com/nlohmann/json) library used for configuration files
|
||||
- Thanks to vitaut for their [libfmt](https://github.com/fmtlib/fmt) library which makes formatting and logging so much better
|
||||
- Thanks to btzy for [nativefiledialog-extended](https://github.com/btzy/nativefiledialog-extended) and their great support, used for handling file dialogs on all platforms
|
||||
- Thanks to danyspin97 for [xdgpp](https://sr.ht/~danyspin97/xdgpp) used to handle folder paths on Linux
|
||||
- Thanks to aquynh for [capstone](https://github.com/aquynh/capstone) which is the base of the disassembly window
|
||||
- Thanks to rxi for [microtar](https://github.com/rxi/microtar) used for extracting downloaded store assets
|
||||
- Thanks to btzy for [nativefiledialog-extended](https://github.com/btzy/nativefiledialog-extended)
|
||||
- Thanks to danyspin97 for [xdgpp](https://sr.ht/~danyspin97/xdgpp)
|
||||
- Thanks to VirusTotal for [Yara](https://github.com/VirusTotal/yara) used by the Yara plugin
|
||||
- Thanks to Martinsos for [edlib](https://github.com/Martinsos/edlib) used for sequence searching in the diffing view
|
||||
- Thanks to ron4fun for [HashLibPlus](https://github.com/ron4fun/HashLibPlus) which implements every hashing algorithm under the sun
|
||||
- Thanks to mackron for [miniaudio](https://github.com/mackron/miniaudio) used to play audio files
|
||||
- Thanks to all other groups and organizations whose libraries are used in ImHex
|
||||
|
||||
### License
|
||||
|
||||
The biggest part of ImHex is under the GPLv2-only license.
|
||||
Notable exceptions to this are the following parts which are under the LGPLv2.1 license:
|
||||
- **/lib/libimhex**: The library that allows Plugins to interact with ImHex.
|
||||
- **/plugins/ui**: The UI plugin library that contains some common UI elements that can be used by other plugins
|
||||
|
||||
The reason for this is to allow for proprietary plugins to be developed for ImHex.
|
||||
|
||||
@@ -22,7 +22,7 @@ macro(addDefines)
|
||||
add_compile_definitions(NDEBUG)
|
||||
elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING}-Debug)
|
||||
add_compile_definitions(DEBUG _GLIBCXX_DEBUG _GLIBCXX_VERBOSE)
|
||||
add_compile_definitions(DEBUG)
|
||||
elseif (CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo")
|
||||
set(IMHEX_VERSION_STRING ${IMHEX_VERSION_STRING})
|
||||
add_compile_definitions(NDEBUG)
|
||||
@@ -31,6 +31,10 @@ macro(addDefines)
|
||||
add_compile_definitions(NDEBUG)
|
||||
endif ()
|
||||
|
||||
if (IMHEX_ENABLE_STD_ASSERTS)
|
||||
add_compile_definitions(_GLIBCXX_DEBUG _GLIBCXX_VERBOSE)
|
||||
endif()
|
||||
|
||||
if (IMHEX_STATIC_LINK_PLUGINS)
|
||||
add_compile_definitions(IMHEX_STATIC_LINK_PLUGINS)
|
||||
endif ()
|
||||
@@ -42,6 +46,9 @@ function(addDefineToSource SOURCE DEFINE)
|
||||
APPEND
|
||||
PROPERTY COMPILE_DEFINITIONS "${DEFINE}"
|
||||
)
|
||||
|
||||
# Disable precompiled headers for this file
|
||||
set_source_files_properties(${SOURCE} PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
|
||||
endfunction()
|
||||
|
||||
# Detect current OS / System
|
||||
@@ -51,6 +58,7 @@ macro(detectOS)
|
||||
set(CMAKE_INSTALL_BINDIR ".")
|
||||
set(CMAKE_INSTALL_LIBDIR ".")
|
||||
set(PLUGINS_INSTALL_LOCATION "plugins")
|
||||
add_compile_definitions(WIN32_LEAN_AND_MEAN)
|
||||
elseif (APPLE)
|
||||
add_compile_definitions(OS_MACOS)
|
||||
set(CMAKE_INSTALL_BINDIR ".")
|
||||
@@ -62,6 +70,9 @@ macro(detectOS)
|
||||
add_compile_definitions(OS_WEB)
|
||||
elseif (UNIX AND NOT APPLE)
|
||||
add_compile_definitions(OS_LINUX)
|
||||
if (BSD AND BSD STREQUAL "FreeBSD")
|
||||
add_compile_definitions(OS_FREEBSD)
|
||||
endif()
|
||||
include(GNUInstallDirs)
|
||||
|
||||
if(IMHEX_PLUGINS_IN_SHARE)
|
||||
@@ -81,6 +92,8 @@ macro(detectOS)
|
||||
endmacro()
|
||||
|
||||
macro(configurePackingResources)
|
||||
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
|
||||
if (WIN32)
|
||||
if (NOT (CMAKE_BUILD_TYPE STREQUAL "Debug"))
|
||||
set(APPLICATION_TYPE WIN32)
|
||||
@@ -116,7 +129,8 @@ macro(configurePackingResources)
|
||||
set(MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resources/dist/macos/Info.plist.in")
|
||||
set(MACOSX_BUNDLE_BUNDLE_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
|
||||
set(MACOSX_BUNDLE_GUI_IDENTIFIER "net.WerWolv.ImHex")
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}-${IMHEX_COMMIT_HASH_SHORT}")
|
||||
string(SUBSTRING "${IMHEX_COMMIT_HASH_LONG}" 0 7 COMMIT_HASH_SHORT)
|
||||
set(MACOSX_BUNDLE_LONG_VERSION_STRING "${PROJECT_VERSION}-${COMMIT_HASH_SHORT}")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}")
|
||||
|
||||
string(TIMESTAMP CURR_YEAR "%Y")
|
||||
@@ -133,21 +147,17 @@ macro(configurePackingResources)
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
macro(createPackage)
|
||||
set(LIBRARY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
|
||||
|
||||
macro(addPluginDirectories)
|
||||
file(MAKE_DIRECTORY "plugins")
|
||||
foreach (plugin IN LISTS PLUGINS)
|
||||
add_subdirectory("plugins/${plugin}")
|
||||
if (TARGET ${plugin})
|
||||
set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
|
||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
|
||||
set_target_properties(${plugin} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins")
|
||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins")
|
||||
|
||||
if (APPLE)
|
||||
if (IMHEX_GENERATE_PACKAGE)
|
||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PLUGINS_INSTALL_LOCATION})
|
||||
else ()
|
||||
set_target_properties(${plugin} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins)
|
||||
endif ()
|
||||
else ()
|
||||
if (WIN32)
|
||||
@@ -166,9 +176,9 @@ macro(createPackage)
|
||||
add_dependencies(imhex_all ${plugin})
|
||||
endif ()
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
macro(createPackage)
|
||||
if (WIN32)
|
||||
# Install binaries directly in the prefix, usually C:\Program Files\ImHex.
|
||||
set(CMAKE_INSTALL_BINDIR ".")
|
||||
@@ -205,7 +215,6 @@ macro(createPackage)
|
||||
endforeach()
|
||||
]])
|
||||
|
||||
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
|
||||
downloadImHexPatternsFiles("./")
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
|
||||
@@ -216,7 +225,6 @@ macro(createPackage)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/LICENSE DESTINATION ${CMAKE_INSTALL_PREFIX}/share/licenses/imhex)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/dist/imhex.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications)
|
||||
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/resources/icon.png DESTINATION ${CMAKE_INSTALL_PREFIX}/share/pixmaps RENAME imhex.png)
|
||||
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
|
||||
downloadImHexPatternsFiles("./share/imhex")
|
||||
|
||||
# install AppStream file
|
||||
@@ -237,14 +245,14 @@ macro(createPackage)
|
||||
set_property(TARGET main PROPERTY MACOSX_BUNDLE_INFO_PLIST ${MACOSX_BUNDLE_INFO_PLIST})
|
||||
|
||||
# Fix rpath
|
||||
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $<TARGET_FILE:main>)
|
||||
install(CODE "execute_process(COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath \"@executable_path/../Frameworks/\" $<TARGET_FILE:main>)")
|
||||
|
||||
add_custom_target(build-time-make-plugins-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/MacOS/plugins")
|
||||
add_custom_target(build-time-make-resources-directory ALL COMMAND ${CMAKE_COMMAND} -E make_directory "${IMHEX_BUNDLE_PATH}/Contents/Resources")
|
||||
|
||||
downloadImHexPatternsFiles("${IMHEX_BUNDLE_PATH}/Contents/MacOS")
|
||||
downloadImHexPatternsFiles("${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/MacOS")
|
||||
|
||||
install(FILES ${IMHEX_ICON} DESTINATION "${IMHEX_BUNDLE_PATH}/Contents/Resources")
|
||||
install(FILES ${IMHEX_ICON} DESTINATION "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources")
|
||||
install(TARGETS main BUNDLE DESTINATION ".")
|
||||
|
||||
# Update library references to make the bundle portable
|
||||
@@ -254,13 +262,19 @@ macro(createPackage)
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
|
||||
set(CPACK_BUNDLE_ICON "${CMAKE_SOURCE_DIR}/resources/dist/macos/AppIcon.icns")
|
||||
set(CPACK_BUNDLE_PLIST "${CMAKE_BINARY_DIR}/${BUNDLE_NAME}/Contents/Info.plist")
|
||||
set(CPACK_BUNDLE_PLIST "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Info.plist")
|
||||
|
||||
# Sign the bundle
|
||||
find_program(CODESIGN_PATH codesign)
|
||||
if (CODESIGN_PATH)
|
||||
add_custom_command(TARGET imhex_all POST_BUILD COMMAND ${CODESIGN_PATH} --force --deep --sign - ${CMAKE_BINARY_DIR}/${BUNDLE_NAME})
|
||||
if (IMHEX_RESIGN_BUNDLE)
|
||||
find_program(CODESIGN_PATH codesign)
|
||||
if (CODESIGN_PATH)
|
||||
install(CODE "message(STATUS \"Signing bundle '${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}'...\")")
|
||||
install(CODE "execute_process(COMMAND ${CODESIGN_PATH} --force --deep --sign - ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME} COMMAND_ERROR_IS_FATAL ANY)")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(CODE [[ message(STATUS "MacOS Bundle finalized. DO NOT TOUCH IT ANYMORE! ANY MODIFICATIONS WILL BREAK IT FROM NOW ON!") ]])
|
||||
else()
|
||||
downloadImHexPatternsFiles("${IMHEX_MAIN_OUTPUT_DIRECTORY}")
|
||||
endif()
|
||||
else()
|
||||
install(TARGETS main RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
@@ -295,6 +309,8 @@ macro(configureCMake)
|
||||
# Enable C and C++ languages
|
||||
enable_language(C CXX)
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "Enable position independent code for all targets" FORCE)
|
||||
|
||||
# Configure use of recommended build tools
|
||||
if (IMHEX_USE_DEFAULT_BUILD_SETTINGS)
|
||||
message(STATUS "Configuring CMake to use recommended build tools...")
|
||||
@@ -326,8 +342,11 @@ macro(configureCMake)
|
||||
|
||||
if (LD_LLD_PATH)
|
||||
set(CMAKE_LINKER ${LD_LLD_PATH})
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=lld")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld")
|
||||
|
||||
if (NOT XCODE)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fuse-ld=lld")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fuse-ld=lld")
|
||||
endif()
|
||||
else ()
|
||||
message(WARNING "lld not found, using default linker!")
|
||||
endif ()
|
||||
@@ -356,8 +375,21 @@ macro(configureCMake)
|
||||
# display a warning about options being set using set() instead of option().
|
||||
# Explicitly set the policy to NEW to suppress the warning.
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
|
||||
|
||||
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW)
|
||||
|
||||
set(CMAKE_WARN_DEPRECATED OFF CACHE BOOL "Disable deprecated warnings" FORCE)
|
||||
endmacro()
|
||||
|
||||
function(configureProject)
|
||||
if (XCODE)
|
||||
# Support Xcode's multi configuration paradigm by placing built artifacts into separate directories
|
||||
set(IMHEX_MAIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/Configs/$<CONFIG>" PARENT_SCOPE)
|
||||
else()
|
||||
set(IMHEX_MAIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
macro(setDefaultBuiltTypeIfUnset)
|
||||
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Using RelWithDebInfo build type as it was left unset" FORCE)
|
||||
@@ -460,22 +492,70 @@ function(downloadImHexPatternsFiles dest)
|
||||
message(STATUS "Finished downloading ImHex-Patterns")
|
||||
|
||||
else ()
|
||||
set(imhex_patterns_SOURCE_DIR "")
|
||||
|
||||
# Maybe patterns are cloned to a subdirectory
|
||||
set(imhex_patterns_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ImHex-Patterns")
|
||||
if (NOT EXISTS ${imhex_patterns_SOURCE_DIR})
|
||||
set(imhex_patterns_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ImHex-Patterns")
|
||||
endif()
|
||||
|
||||
# Or a sibling directory
|
||||
if (NOT EXISTS ${imhex_patterns_SOURCE_DIR})
|
||||
set(imhex_patterns_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../ImHex-Patterns")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (EXISTS ${imhex_patterns_SOURCE_DIR})
|
||||
if (NOT EXISTS ${imhex_patterns_SOURCE_DIR})
|
||||
message(WARNING "Failed to locate ImHex-Patterns repository, some resources will be missing during install!")
|
||||
elseif(XCODE)
|
||||
# The Xcode build has multiple configurations, which each need a copy of these files
|
||||
file(GLOB_RECURSE sourceFilePaths LIST_DIRECTORIES NO CONFIGURE_DEPENDS RELATIVE "${imhex_patterns_SOURCE_DIR}"
|
||||
"${imhex_patterns_SOURCE_DIR}/constants/*"
|
||||
"${imhex_patterns_SOURCE_DIR}/encodings/*"
|
||||
"${imhex_patterns_SOURCE_DIR}/includes/*"
|
||||
"${imhex_patterns_SOURCE_DIR}/patterns/*"
|
||||
"${imhex_patterns_SOURCE_DIR}/magic/*"
|
||||
"${imhex_patterns_SOURCE_DIR}/nodes/*"
|
||||
)
|
||||
list(FILTER sourceFilePaths EXCLUDE REGEX "_schema.json$")
|
||||
|
||||
foreach(relativePath IN LISTS sourceFilePaths)
|
||||
file(GENERATE OUTPUT "${dest}/${relativePath}" INPUT "${imhex_patterns_SOURCE_DIR}/${relativePath}")
|
||||
endforeach()
|
||||
else()
|
||||
set(PATTERNS_FOLDERS_TO_INSTALL constants encodings includes patterns magic nodes)
|
||||
foreach (FOLDER ${PATTERNS_FOLDERS_TO_INSTALL})
|
||||
install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION ${dest} PATTERN "**/_schema.json" EXCLUDE)
|
||||
install(DIRECTORY "${imhex_patterns_SOURCE_DIR}/${FOLDER}" DESTINATION "${dest}" PATTERN "**/_schema.json" EXCLUDE)
|
||||
endforeach ()
|
||||
endif ()
|
||||
|
||||
endfunction()
|
||||
|
||||
macro(setupCompilerFlags target)
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
# Compress debug info. See https://github.com/WerWolv/ImHex/issues/1714 for relevant problem
|
||||
macro(setupDebugCompressionFlag)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckLinkerFlag)
|
||||
|
||||
check_cxx_compiler_flag(-gz=zstd ZSTD_AVAILABLE_COMPILER)
|
||||
check_linker_flag(CXX -gz=zstd ZSTD_AVAILABLE_LINKER)
|
||||
check_cxx_compiler_flag(-gz COMPRESS_AVAILABLE_COMPILER)
|
||||
check_linker_flag(CXX -gz COMPRESS_AVAILABLE_LINKER)
|
||||
|
||||
if (NOT DEBUG_COMPRESSION_FLAG) # Cache variable
|
||||
if (ZSTD_AVAILABLE_COMPILER AND ZSTD_AVAILABLE_LINKER)
|
||||
set(DEBUG_COMPRESSION_FLAG "-gz=zstd" CACHE STRING "Cache to use for debug info compression")
|
||||
elseif (COMPRESS_AVAILABLE_COMPILER AND COMPRESS_AVAILABLE_LINKER)
|
||||
set(DEBUG_COMPRESSION_FLAG "-gz" CACHE STRING "Cache to use for debug info compression")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(IMHEX_COMMON_FLAGS "${IMHEX_COMMON_FLAGS} ${DEBUG_COMPRESSION_FLAG}")
|
||||
endmacro()
|
||||
|
||||
macro(setupCompilerFlags target)
|
||||
# IMHEX_COMMON_FLAGS: flags common for C, C++, Objective C, etc.. compilers
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
# Define strict compilation flags
|
||||
if (IMHEX_STRICT_WARNINGS)
|
||||
set(IMHEX_COMMON_FLAGS "${IMHEX_COMMON_FLAGS} -Wall -Wextra -Wpedantic -Werror")
|
||||
@@ -488,8 +568,10 @@ macro(setupCompilerFlags target)
|
||||
set(IMHEX_CXX_FLAGS "-fexceptions -frtti")
|
||||
|
||||
# Disable some warnings
|
||||
set(IMHEX_C_CXX_FLAGS "-Wno-unknown-warning-option -Wno-array-bounds -Wno-deprecated-declarations")
|
||||
set(IMHEX_C_CXX_FLAGS "-Wno-unknown-warning-option -Wno-array-bounds -Wno-deprecated-declarations -Wno-unknown-pragmas")
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
if (IMHEX_ENABLE_UNITY_BUILD AND WIN32)
|
||||
set(IMHEX_COMMON_FLAGS "${IMHEX_COMMON_FLAGS} -Wa,-mbig-obj")
|
||||
endif ()
|
||||
@@ -505,11 +587,24 @@ macro(setupCompilerFlags target)
|
||||
set(IMHEX_C_CXX_FLAGS "${IMHEX_C_CXX_FLAGS} -pthread -Wno-dollar-in-identifier-extension -Wno-pthreads-mem-growth")
|
||||
endif ()
|
||||
|
||||
if (IMHEX_COMPRESS_DEBUG_INFO)
|
||||
setupDebugCompressionFlag()
|
||||
endif()
|
||||
|
||||
# Set actual CMake flags
|
||||
set_target_properties(${target} PROPERTIES COMPILE_FLAGS "${IMHEX_COMMON_FLAGS} ${IMHEX_C_CXX_FLAGS}")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${IMHEX_COMMON_FLAGS} ${IMHEX_C_CXX_FLAGS}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${IMHEX_COMMON_FLAGS} ${IMHEX_C_CXX_FLAGS} ${IMHEX_CXX_FLAGS}")
|
||||
set(CMAKE_OBJC_FLAGS "${CMAKE_OBJC_FLAGS} ${IMHEX_COMMON_FLAGS}")
|
||||
|
||||
# Only generate minimal debug information for stacktraces in RelWithDebInfo builds
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -g1")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -g1")
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# Add flags for debug info in inline functions
|
||||
set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -gstatement-frontiers -ginline-points")
|
||||
set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -gstatement-frontiers -ginline-points")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# uninstall target
|
||||
@@ -526,8 +621,6 @@ macro(setUninstallTarget)
|
||||
endmacro()
|
||||
|
||||
macro(addBundledLibraries)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
set(EXTERNAL_LIBS_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/lib/external")
|
||||
set(THIRD_PARTY_LIBS_FOLDER "${CMAKE_CURRENT_SOURCE_DIR}/lib/third_party")
|
||||
|
||||
@@ -535,28 +628,17 @@ macro(addBundledLibraries)
|
||||
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/imgui)
|
||||
|
||||
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/microtar EXCLUDE_FROM_ALL)
|
||||
set_target_properties(microtar PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
add_subdirectory(${EXTERNAL_LIBS_FOLDER}/libwolv EXCLUDE_FROM_ALL)
|
||||
set_property(TARGET libwolv-types PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET libwolv-utils PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET libwolv-io PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET libwolv-hash PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET libwolv-containers PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET libwolv-net PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
set_property(TARGET libwolv-math_eval PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
set(XDGPP_INCLUDE_DIRS "${THIRD_PARTY_LIBS_FOLDER}/xdgpp")
|
||||
set(FPHSA_NAME_MISMATCHED ON CACHE BOOL "")
|
||||
|
||||
find_package(PkgConfig REQUIRED)
|
||||
|
||||
if(NOT USE_SYSTEM_FMT)
|
||||
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/fmt EXCLUDE_FROM_ALL)
|
||||
set_target_properties(fmt PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
set(FMT_LIBRARIES fmt::fmt-header-only)
|
||||
else()
|
||||
find_package(fmt 8.0.0 REQUIRED)
|
||||
find_package(fmt REQUIRED)
|
||||
set(FMT_LIBRARIES fmt::fmt)
|
||||
endif()
|
||||
|
||||
@@ -568,13 +650,11 @@ macro(addBundledLibraries)
|
||||
|
||||
if (NOT EMSCRIPTEN)
|
||||
# curl
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBCURL REQUIRED IMPORTED_TARGET libcurl>=7.60.0)
|
||||
find_package(CURL REQUIRED)
|
||||
|
||||
# nfd
|
||||
if (NOT USE_SYSTEM_NFD)
|
||||
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/nativefiledialog EXCLUDE_FROM_ALL)
|
||||
set_target_properties(nfd PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
set(NFD_LIBRARIES nfd)
|
||||
else()
|
||||
find_package(nfd)
|
||||
@@ -590,9 +670,16 @@ macro(addBundledLibraries)
|
||||
set(NLOHMANN_JSON_LIBRARIES nlohmann_json::nlohmann_json)
|
||||
endif()
|
||||
|
||||
if (NOT USE_SYSTEM_LUNASVG)
|
||||
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/lunasvg EXCLUDE_FROM_ALL)
|
||||
set(LUNASVG_LIBRARIES lunasvg)
|
||||
else()
|
||||
find_package(LunaSVG REQUIRED)
|
||||
set(LUNASVG_LIBRARIES lunasvg)
|
||||
endif()
|
||||
|
||||
if (NOT USE_SYSTEM_LLVM)
|
||||
add_subdirectory(${THIRD_PARTY_LIBS_FOLDER}/llvm-demangle EXCLUDE_FROM_ALL)
|
||||
set_target_properties(LLVMDemangle PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
else()
|
||||
find_package(LLVM REQUIRED Demangle)
|
||||
endif()
|
||||
@@ -609,42 +696,40 @@ macro(addBundledLibraries)
|
||||
set(JTHREAD_LIBRARIES jthread)
|
||||
endif()
|
||||
|
||||
set(LIBPL_BUILD_CLI_AS_EXECUTABLE OFF)
|
||||
add_subdirectory(${EXTERNAL_LIBS_FOLDER}/pattern_language EXCLUDE_FROM_ALL)
|
||||
set_target_properties(libpl PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
set(LIBPL_BUILD_CLI_AS_EXECUTABLE OFF CACHE BOOL "" FORCE)
|
||||
set(LIBPL_ENABLE_PRECOMPILED_HEADERS ${IMHEX_ENABLE_PRECOMPILED_HEADERS} CACHE BOOL "" FORCE)
|
||||
|
||||
find_package(mbedTLS 3.4.0 REQUIRED)
|
||||
|
||||
pkg_search_module(MAGIC libmagic>=5.39)
|
||||
if(NOT MAGIC_FOUND)
|
||||
find_library(MAGIC 5.39 magic REQUIRED)
|
||||
if (WIN32)
|
||||
set(LIBPL_SHARED_LIBRARY ON CACHE BOOL "" FORCE)
|
||||
else()
|
||||
set(MAGIC_INCLUDE_DIRS ${MAGIC_INCLUDEDIR})
|
||||
set(LIBPL_SHARED_LIBRARY OFF CACHE BOOL "" FORCE)
|
||||
endif()
|
||||
|
||||
if (NOT IMHEX_DISABLE_STACKTRACE)
|
||||
if (WIN32)
|
||||
message(STATUS "StackWalk enabled!")
|
||||
set(LIBBACKTRACE_LIBRARIES DbgHelp.lib)
|
||||
else ()
|
||||
find_package(Backtrace)
|
||||
if (${Backtrace_FOUND})
|
||||
message(STATUS "Backtrace enabled! Header: ${Backtrace_HEADER}")
|
||||
add_subdirectory(${EXTERNAL_LIBS_FOLDER}/pattern_language EXCLUDE_FROM_ALL)
|
||||
|
||||
if (Backtrace_HEADER STREQUAL "execinfo.h")
|
||||
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
|
||||
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
|
||||
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
|
||||
add_compile_definitions(HEX_HAS_EXECINFO)
|
||||
elseif (Backtrace_HEADER STREQUAL "backtrace.h")
|
||||
set(LIBBACKTRACE_LIBRARIES ${Backtrace_LIBRARY})
|
||||
set(LIBBACKTRACE_INCLUDE_DIRS ${Backtrace_INCLUDE_DIR})
|
||||
add_compile_definitions(BACKTRACE_HEADER=\"${Backtrace_HEADER}\")
|
||||
add_compile_definitions(HEX_HAS_BACKTRACE)
|
||||
endif ()
|
||||
endif()
|
||||
endif ()
|
||||
endif ()
|
||||
if (LIBPL_SHARED_LIBRARY)
|
||||
install(
|
||||
TARGETS
|
||||
libpl
|
||||
DESTINATION
|
||||
"${CMAKE_INSTALL_LIBDIR}"
|
||||
PERMISSIONS
|
||||
OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE
|
||||
)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
set_target_properties(
|
||||
libpl
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
)
|
||||
endif()
|
||||
enableUnityBuild(libpl)
|
||||
|
||||
find_package(mbedTLS 3.4.0 REQUIRED)
|
||||
find_package(Magic 5.39 REQUIRED)
|
||||
endmacro()
|
||||
|
||||
function(enableUnityBuild TARGET)
|
||||
@@ -654,6 +739,10 @@ function(enableUnityBuild TARGET)
|
||||
endfunction()
|
||||
|
||||
function(generatePDBs)
|
||||
if (NOT IMHEX_GENERATE_PDBS)
|
||||
return()
|
||||
endif ()
|
||||
|
||||
if (NOT WIN32 OR CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
return()
|
||||
endif ()
|
||||
@@ -667,7 +756,6 @@ function(generatePDBs)
|
||||
FetchContent_Populate(cv2pdb)
|
||||
|
||||
set(PDBS_TO_GENERATE main main-forwarder libimhex ${PLUGINS})
|
||||
add_custom_target(pdbs)
|
||||
foreach (PDB ${PDBS_TO_GENERATE})
|
||||
if (PDB STREQUAL "main")
|
||||
set(GENERATED_PDB imhex)
|
||||
@@ -679,19 +767,26 @@ function(generatePDBs)
|
||||
set(GENERATED_PDB plugins/${PDB})
|
||||
endif ()
|
||||
|
||||
if (IMHEX_REPLACE_DWARF_WITH_PDB)
|
||||
set(PDB_OUTPUT_PATH ${CMAKE_BINARY_DIR}/${GENERATED_PDB})
|
||||
else ()
|
||||
set(PDB_OUTPUT_PATH)
|
||||
endif()
|
||||
|
||||
add_custom_target(${PDB}_pdb DEPENDS ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb)
|
||||
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb
|
||||
WORKING_DIRECTORY ${cv2pdb_SOURCE_DIR}
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
|
||||
COMMAND
|
||||
(
|
||||
${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb &&
|
||||
${cv2pdb_SOURCE_DIR}/cv2pdb64.exe
|
||||
$<TARGET_FILE:${PDB}>
|
||||
${cv2pdb_SOURCE_DIR}/cv2pdb64.exe $<TARGET_FILE:${PDB}> ${PDB_OUTPUT_PATH} &&
|
||||
${CMAKE_COMMAND} -E remove -f ${CMAKE_BINARY_DIR}/${GENERATED_PDB}
|
||||
) || (exit 0)
|
||||
DEPENDS $<TARGET_FILE:${PDB}>
|
||||
COMMAND_EXPAND_LISTS)
|
||||
|
||||
target_sources(imhex_all PRIVATE ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${GENERATED_PDB}.pdb DESTINATION ".")
|
||||
|
||||
add_dependencies(imhex_all ${PDB}_pdb)
|
||||
endforeach ()
|
||||
|
||||
endfunction()
|
||||
@@ -700,14 +795,16 @@ function(generateSDKDirectory)
|
||||
if (WIN32)
|
||||
set(SDK_PATH "./sdk")
|
||||
elseif (APPLE)
|
||||
set(SDK_PATH "${BUNDLE_NAME}/Contents/Resources/sdk")
|
||||
set(SDK_PATH "${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME}/Contents/Resources/sdk")
|
||||
else()
|
||||
set(SDK_PATH "share/imhex/sdk")
|
||||
endif()
|
||||
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/libimhex DESTINATION "${SDK_PATH}/lib")
|
||||
set(SDK_BUILD_PATH "${CMAKE_BINARY_DIR}/sdk")
|
||||
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/libimhex DESTINATION "${SDK_PATH}/lib" PATTERN "**/source/*" EXCLUDE)
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/external DESTINATION "${SDK_PATH}/lib")
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/imgui DESTINATION "${SDK_PATH}/lib/third_party")
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/imgui DESTINATION "${SDK_PATH}/lib/third_party" PATTERN "**/source/*" EXCLUDE)
|
||||
if (NOT USE_SYSTEM_FMT)
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/fmt DESTINATION "${SDK_PATH}/lib/third_party")
|
||||
endif()
|
||||
@@ -715,15 +812,28 @@ function(generateSDKDirectory)
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/lib/third_party/nlohmann_json DESTINATION "${SDK_PATH}/lib/third_party")
|
||||
endif()
|
||||
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/cmake/modules/ImHexPlugin.cmake DESTINATION "${SDK_PATH}/cmake/modules")
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/modules DESTINATION "${SDK_PATH}/cmake")
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/cmake/build_helpers.cmake DESTINATION "${SDK_PATH}/cmake")
|
||||
install(DIRECTORY ${CMAKE_SOURCE_DIR}/cmake/sdk/ DESTINATION "${SDK_PATH}")
|
||||
install(TARGETS libimhex ARCHIVE DESTINATION "${SDK_PATH}/lib")
|
||||
install(TARGETS libimhex RUNTIME DESTINATION "${SDK_PATH}/lib")
|
||||
install(TARGETS libimhex LIBRARY DESTINATION "${SDK_PATH}/lib")
|
||||
endfunction()
|
||||
|
||||
function(addIncludesFromLibrary target library)
|
||||
get_target_property(library_include_dirs ${library} INTERFACE_INCLUDE_DIRECTORIES)
|
||||
target_include_directories(${target} PRIVATE ${library_include_dirs})
|
||||
endfunction()
|
||||
|
||||
function(precompileHeaders target includeFolder)
|
||||
if (NOT IMHEX_ENABLE_PRECOMPILED_HEADERS)
|
||||
return()
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE TARGET_INCLUDES "${includeFolder}/**/*.hpp")
|
||||
set(SYSTEM_INCLUDES "<algorithm>;<array>;<atomic>;<chrono>;<cmath>;<cstddef>;<cstdint>;<cstdio>;<cstdlib>;<cstring>;<exception>;<filesystem>;<functional>;<iterator>;<limits>;<list>;<map>;<memory>;<optional>;<ranges>;<set>;<stdexcept>;<string>;<string_view>;<thread>;<tuple>;<type_traits>;<unordered_map>;<unordered_set>;<utility>;<variant>;<vector>")
|
||||
set(INCLUDES "${SYSTEM_INCLUDES};${TARGET_INCLUDES}")
|
||||
string(REPLACE ">" "$<ANGLE-R>" INCLUDES "${INCLUDES}")
|
||||
target_precompile_headers(${target}
|
||||
PUBLIC
|
||||
"$<$<COMPILE_LANGUAGE:CXX>:${INCLUDES}>"
|
||||
)
|
||||
endfunction()
|
||||
149
cmake/ide_helpers.cmake
Normal file
149
cmake/ide_helpers.cmake
Normal file
@@ -0,0 +1,149 @@
|
||||
|
||||
option(IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER "Enable choice of compiler for Xcode builds, despite CMake's best efforts" OFF)
|
||||
option(IMHEX_IDE_HELPERS_INTRUSIVE_IDE_TWEAKS "Enable intrusive CMake tweaks to better support IDEs with folder support" OFF)
|
||||
|
||||
# The CMake infrastructure silently ignores the CMAKE_<>_COMPILER settings when
|
||||
# using the `Xcode` generator.
|
||||
#
|
||||
# A particularly nasty (and potentially only) way of getting around this is to
|
||||
# temporarily lie about the generator being used, while CMake determines and
|
||||
# locks in the compiler to use.
|
||||
#
|
||||
# Needless to say, this is hacky and fragile. Use at your own risk!
|
||||
if (IMHEX_IDE_HELPERS_OVERRIDE_XCODE_COMPILER AND CMAKE_GENERATOR STREQUAL "Xcode")
|
||||
set(CMAKE_GENERATOR "Unknown")
|
||||
enable_language(C CXX)
|
||||
|
||||
set(CMAKE_GENERATOR "Xcode")
|
||||
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CC "${CMAKE_C_COMPILER}")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_CXX "${CMAKE_CXX_COMPILER}")
|
||||
|
||||
if (CLANG)
|
||||
set(CMAKE_XCODE_ATTRIBUTE_LD "${CMAKE_C_COMPILER}")
|
||||
set(CMAKE_XCODE_ATTRIBUTE_LDPLUSPLUS "${CMAKE_CXX_COMPILER}")
|
||||
endif()
|
||||
|
||||
# By default Xcode passes a `-index-store-path=<...>` parameter to the compiler
|
||||
# during builds to build code completion indexes. This is not supported by
|
||||
# anything other than AppleClang
|
||||
set(CMAKE_XCODE_ATTRIBUTE_COMPILER_INDEX_STORE_ENABLE "NO")
|
||||
endif()
|
||||
|
||||
# Generate a launch/build scheme for all targets
|
||||
set(CMAKE_XCODE_GENERATE_SCHEME YES)
|
||||
|
||||
# Utility function that helps avoid messing with non-standard targets
|
||||
macro(returnIfTargetIsNonTweakable target)
|
||||
get_target_property(targetIsAliased ${target} ALIASED_TARGET)
|
||||
get_target_property(targetIsImported ${target} IMPORTED)
|
||||
|
||||
if (targetIsAliased OR targetIsImported)
|
||||
return()
|
||||
endif()
|
||||
|
||||
get_target_property(targetType ${target} TYPE)
|
||||
if (targetType MATCHES "INTERFACE_LIBRARY|UNKNOWN_LIBRARY")
|
||||
return()
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
# Targets usually don't specify their private headers, nor group their source files
|
||||
# which results in very spotty coverage by IDEs with folders support
|
||||
#
|
||||
# Unfortunately, CMake does not have a `target_source_group` like construct yet, therefore
|
||||
# we have to play by the limitations of `source_group`.
|
||||
#
|
||||
# A particularly problematic part is that the function must be called within the directoryies
|
||||
# scope for the grouping to take effect.
|
||||
#
|
||||
# See: https://discourse.cmake.org/t/topic/7388
|
||||
function(tweakTargetForIDESupport target)
|
||||
returnIfTargetIsNonTweakable(${target})
|
||||
|
||||
# Don't assume directory structure of third parties
|
||||
get_target_property(targetSourceDir ${target} SOURCE_DIR)
|
||||
if (targetSourceDir MATCHES "third_party")
|
||||
return()
|
||||
endif()
|
||||
|
||||
# Add headers to target
|
||||
get_target_property(targetSourceDir ${target} SOURCE_DIR)
|
||||
if (targetSourceDir)
|
||||
file(GLOB_RECURSE targetPrivateHeaders CONFIGURE_DEPENDS "${targetSourceDir}/include/*.hpp")
|
||||
|
||||
target_sources(${target} PRIVATE "${targetPrivateHeaders}")
|
||||
endif()
|
||||
|
||||
# Organize target sources into directory tree
|
||||
get_target_property(sources ${target} SOURCES)
|
||||
foreach(file IN LISTS sources)
|
||||
get_filename_component(path "${file}" ABSOLUTE)
|
||||
|
||||
if (NOT path MATCHES "^${targetSourceDir}")
|
||||
continue()
|
||||
endif()
|
||||
|
||||
source_group(TREE "${targetSourceDir}" PREFIX "Source Tree" FILES "${file}")
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
if (IMHEX_IDE_HELPERS_INTRUSIVE_IDE_TWEAKS)
|
||||
# See tweakTargetForIDESupport for rationale
|
||||
|
||||
function(add_library target)
|
||||
_add_library(${target} ${ARGN})
|
||||
|
||||
tweakTargetForIDESupport(${target})
|
||||
endfunction()
|
||||
|
||||
function(add_executable target)
|
||||
_add_executable(${target} ${ARGN})
|
||||
|
||||
tweakTargetForIDESupport(${target})
|
||||
endfunction()
|
||||
endif()
|
||||
|
||||
# Adjust target's FOLDER property, which is an IDE only preference
|
||||
function(_tweakTarget target path)
|
||||
get_target_property(targetType ${target} TYPE)
|
||||
|
||||
if (TARGET generator-${target})
|
||||
set_target_properties(generator-${target} PROPERTIES FOLDER "romfs/${target}")
|
||||
endif()
|
||||
if (TARGET romfs_file_packer-${target})
|
||||
set_target_properties(romfs_file_packer-${target} PROPERTIES FOLDER "romfs/${target}")
|
||||
endif()
|
||||
if (TARGET libromfs-${target})
|
||||
set_target_properties(libromfs-${target} PROPERTIES FOLDER "romfs/${target}")
|
||||
endif()
|
||||
|
||||
if (${targetType} MATCHES "EXECUTABLE|LIBRARY")
|
||||
set_target_properties(${target} PROPERTIES FOLDER "${path}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
macro(_tweakTargetsRecursive dir)
|
||||
get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES)
|
||||
foreach(subdir IN LISTS subdirectories)
|
||||
_tweakTargetsRecursive("${subdir}")
|
||||
endforeach()
|
||||
|
||||
if(${dir} STREQUAL ${CMAKE_SOURCE_DIR})
|
||||
return()
|
||||
endif()
|
||||
|
||||
get_property(targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS)
|
||||
file(RELATIVE_PATH rdir ${CMAKE_SOURCE_DIR} "${dir}/..")
|
||||
|
||||
foreach(target ${targets})
|
||||
_tweakTarget(${target} "${rdir}")
|
||||
endforeach()
|
||||
endmacro()
|
||||
|
||||
# Tweak all targets this CMake build is aware about
|
||||
function(tweakTargetsForIDESupport)
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
_tweakTargetsRecursive("${CMAKE_SOURCE_DIR}")
|
||||
endfunction()
|
||||
8
cmake/modules/FindCapstone.cmake
Normal file
8
cmake/modules/FindCapstone.cmake
Normal file
@@ -0,0 +1,8 @@
|
||||
find_path(CAPSTONE_INCLUDE_DIR capstone.h PATH_SUFFIXES capstone)
|
||||
find_library(CAPSTONE_LIBRARY NAMES capstone)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
find_package_handle_standard_args(Capstone DEFAULT_MSG CAPSTONE_LIBRARY CAPSTONE_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(CAPSTONE_INCLUDE_DIR CAPSTONE_LIBRARY)
|
||||
@@ -11,6 +11,14 @@ if (UNIX)
|
||||
set(CORECLR_SUBARCH "arm64")
|
||||
endif()
|
||||
endif()
|
||||
if (APPLE)
|
||||
if (CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
set(CORECLR_ARCH "osx-arm64")
|
||||
set(CORECLR_SUBARCH "arm64")
|
||||
else()
|
||||
set(CORECLR_ARCH "osx-x64")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (NOT DOTNET_EXECUTABLE)
|
||||
set(DOTNET_EXECUTABLE dotnet)
|
||||
@@ -49,13 +57,16 @@ endif()
|
||||
set(CoreClrEmbed_ROOT_PATH "${CORECLR_RUNTIME_ROOT_PATH}")
|
||||
|
||||
|
||||
file(GLOB _CORECLR_HOST_ARCH_PATH "${CORECLR_RUNTIME_ROOT_PATH}/packs/Microsoft.NETCore.App.Host.*-${CORECLR_SUBARCH}")
|
||||
if (_CORECLR_HOST_ARCH_PATH)
|
||||
get_filename_component(_CORECLR_HOST_ARCH_FILENAME ${_CORECLR_HOST_ARCH_PATH} NAME)
|
||||
string(REPLACE "Microsoft.NETCore.App.Host." "" _CORECLR_COMPUTED_ARCH "${_CORECLR_HOST_ARCH_FILENAME}")
|
||||
if (_CORECLR_COMPUTED_ARCH)
|
||||
set(CORECLR_ARCH "${_CORECLR_COMPUTED_ARCH}")
|
||||
endif()
|
||||
file(GLOB _CORECLR_HOST_ARCH_PATH_LIST "${CORECLR_RUNTIME_ROOT_PATH}/packs/Microsoft.NETCore.App.Host.*-${CORECLR_SUBARCH}")
|
||||
if (_CORECLR_HOST_ARCH_PATH_LIST)
|
||||
foreach(_CORECLR_HOST_ARCH_PATH ${_CORECLR_HOST_ARCH_PATH_LIST})
|
||||
get_filename_component(_CORECLR_HOST_ARCH_FILENAME ${_CORECLR_HOST_ARCH_PATH} NAME)
|
||||
string(REPLACE "Microsoft.NETCore.App.Host." "" _CORECLR_COMPUTED_ARCH "${_CORECLR_HOST_ARCH_FILENAME}")
|
||||
if (_CORECLR_COMPUTED_ARCH)
|
||||
set(CORECLR_ARCH "${_CORECLR_COMPUTED_ARCH}")
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
set(CORECLR_HOST_BASE_PATH "${CORECLR_RUNTIME_ROOT_PATH}/packs/Microsoft.NETCore.App.Host.${CORECLR_ARCH}/${CORECLR_RUNTIME_VERSION_FULL}")
|
||||
|
||||
139
cmake/modules/FindGLFW.cmake
Normal file
139
cmake/modules/FindGLFW.cmake
Normal file
@@ -0,0 +1,139 @@
|
||||
#.rst:
|
||||
# Find GLFW
|
||||
# ---------
|
||||
#
|
||||
# Finds the GLFW library using its cmake config if that exists, otherwise
|
||||
# falls back to finding it manually. This module defines:
|
||||
#
|
||||
# GLFW_FOUND - True if GLFW library is found
|
||||
# GLFW::GLFW - GLFW imported target
|
||||
#
|
||||
# Additionally, in case the config was not found, these variables are defined
|
||||
# for internal usage:
|
||||
#
|
||||
# GLFW_LIBRARY - GLFW library
|
||||
# GLFW_DLL_DEBUG - GLFW debug DLL on Windows, if found
|
||||
# GLFW_DLL_RELEASE - GLFW release DLL on Windows, if found
|
||||
# GLFW_INCLUDE_DIR - Root include dir
|
||||
#
|
||||
|
||||
#
|
||||
# This file is part of Magnum.
|
||||
#
|
||||
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019,
|
||||
# 2020, 2021, 2022 Vladimír Vondruš <mosra@centrum.cz>
|
||||
# Copyright © 2016 Jonathan Hale <squareys@googlemail.com>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a
|
||||
# copy of this software and associated documentation files (the "Software"),
|
||||
# to deal in the Software without restriction, including without limitation
|
||||
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
# and/or sell copies of the Software, and to permit persons to whom the
|
||||
# Software is furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included
|
||||
# in all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
# DEALINGS IN THE SOFTWARE.
|
||||
#
|
||||
|
||||
# GLFW installs cmake package config files which handles dependencies in case
|
||||
# GLFW is built statically. Try to find first, quietly, so it doesn't print
|
||||
# loud messages when it's not found, since that's okay. If the glfw target
|
||||
# already exists, it means we're using it through a CMake subproject -- don't
|
||||
# attempt to find the package in that case.
|
||||
if(NOT TARGET glfw)
|
||||
find_package(glfw3 CONFIG QUIET)
|
||||
endif()
|
||||
|
||||
# If either a glfw config file was found or we have a subproject, point
|
||||
# GLFW::GLFW to that and exit -- nothing else to do here.
|
||||
if(TARGET glfw)
|
||||
if(NOT TARGET GLFW::GLFW)
|
||||
# Aliases of (global) targets are only supported in CMake 3.11, so we
|
||||
# work around it by this. This is easier than fetching all possible
|
||||
# properties (which are impossible to track of) and then attempting to
|
||||
# rebuild them into a new target.
|
||||
add_library(GLFW::GLFW INTERFACE IMPORTED)
|
||||
set_target_properties(GLFW::GLFW PROPERTIES INTERFACE_LINK_LIBRARIES glfw)
|
||||
endif()
|
||||
|
||||
# Just to make FPHSA print some meaningful location, nothing else
|
||||
get_target_property(_GLFW_INTERFACE_INCLUDE_DIRECTORIES glfw INTERFACE_INCLUDE_DIRECTORIES)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args("GLFW" DEFAULT_MSG
|
||||
_GLFW_INTERFACE_INCLUDE_DIRECTORIES)
|
||||
|
||||
if(CORRADE_TARGET_WINDOWS)
|
||||
# .dll is in LOCATION, .lib is in IMPLIB. Yay, useful!
|
||||
get_target_property(GLFW_DLL_DEBUG glfw IMPORTED_LOCATION_DEBUG)
|
||||
get_target_property(GLFW_DLL_RELEASE glfw IMPORTED_LOCATION_RELEASE)
|
||||
endif()
|
||||
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(CORRADE_TARGET_WINDOWS)
|
||||
if(MSVC)
|
||||
if(MSVC_VERSION VERSION_LESS 1910)
|
||||
set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2015)
|
||||
elseif(MSVC_VERSION VERSION_LESS 1920)
|
||||
set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2017)
|
||||
elseif(MSVC_VERSION VERSION_LESS 1930)
|
||||
set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2019)
|
||||
elseif(MSVC_VERSION VERSION_LESS 1940)
|
||||
set(_GLFW_LIBRARY_PATH_SUFFIX lib-vc2022)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported MSVC version")
|
||||
endif()
|
||||
elseif(MINGW)
|
||||
set(_GLFW_LIBRARY_PATH_SUFFIX lib-mingw-w64)
|
||||
else()
|
||||
message(FATAL_ERROR "Unsupported compiler")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# In case no config file was found, try manually finding the library. Prefer
|
||||
# the glfw3dll as it's a dynamic library.
|
||||
find_library(GLFW_LIBRARY
|
||||
NAMES glfw glfw3dll glfw3
|
||||
PATH_SUFFIXES ${_GLFW_LIBRARY_PATH_SUFFIX})
|
||||
|
||||
if(CORRADE_TARGET_WINDOWS AND GLFW_LIBRARY MATCHES "glfw3dll.(lib|a)$")
|
||||
# TODO: debug?
|
||||
find_file(GLFW_DLL_RELEASE
|
||||
NAMES glfw3.dll
|
||||
PATH_SUFFIXES ${_GLFW_LIBRARY_PATH_SUFFIX})
|
||||
endif()
|
||||
|
||||
# Include dir
|
||||
find_path(GLFW_INCLUDE_DIR
|
||||
NAMES GLFW/glfw3.h)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args("GLFW" DEFAULT_MSG
|
||||
GLFW_LIBRARY
|
||||
GLFW_INCLUDE_DIR)
|
||||
|
||||
if(NOT TARGET GLFW::GLFW)
|
||||
add_library(GLFW::GLFW UNKNOWN IMPORTED)
|
||||
|
||||
# Work around BUGGY framework support on macOS
|
||||
# https://cmake.org/Bug/view.php?id=14105
|
||||
if(CORRADE_TARGET_APPLE AND GLFW_LIBRARY MATCHES "\\.framework$")
|
||||
set_property(TARGET GLFW::GLFW PROPERTY IMPORTED_LOCATION ${GLFW_LIBRARY}/GLFW)
|
||||
else()
|
||||
set_property(TARGET GLFW::GLFW PROPERTY IMPORTED_LOCATION ${GLFW_LIBRARY})
|
||||
endif()
|
||||
|
||||
set_property(TARGET GLFW::GLFW PROPERTY
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${GLFW_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
mark_as_advanced(GLFW_LIBRARY GLFW_INCLUDE_DIR)
|
||||
18
cmake/modules/FindMagic.cmake
Normal file
18
cmake/modules/FindMagic.cmake
Normal file
@@ -0,0 +1,18 @@
|
||||
find_path(LIBMAGIC_INCLUDE_DIR magic.h)
|
||||
|
||||
find_library(LIBMAGIC_LIBRARY NAMES magic)
|
||||
|
||||
if (LIBMAGIC_INCLUDE_DIR AND LIBMAGIC_LIBRARY)
|
||||
set(LIBMAGIC_FOUND TRUE)
|
||||
endif (LIBMAGIC_INCLUDE_DIR AND LIBMAGIC_LIBRARY)
|
||||
|
||||
find_package_handle_standard_args("libmagic" DEFAULT_MSG
|
||||
LIBMAGIC_LIBRARY
|
||||
LIBMAGIC_INCLUDE_DIR
|
||||
)
|
||||
|
||||
mark_as_advanced(
|
||||
LIBMAGIC_INCLUDE_DIR
|
||||
LIBMAGIC_LIBRARY
|
||||
LIBMAGIC_FOUND
|
||||
)
|
||||
4
cmake/modules/FindYara.cmake
Normal file
4
cmake/modules/FindYara.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
find_library(YARA_LIBRARIES NAMES yara)
|
||||
find_file(yara.h YARA_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(YARA_LIBRARIES YARA_INCLUDE_DIRS)
|
||||
41
cmake/modules/FindZSTD.cmake
Normal file
41
cmake/modules/FindZSTD.cmake
Normal file
@@ -0,0 +1,41 @@
|
||||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
#
|
||||
# - Try to find Facebook zstd library
|
||||
# This will define
|
||||
# ZSTD_FOUND
|
||||
# ZSTD_INCLUDE_DIR
|
||||
# ZSTD_LIBRARY
|
||||
#
|
||||
|
||||
find_path(ZSTD_INCLUDE_DIR NAMES zstd.h)
|
||||
|
||||
find_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd_staticd)
|
||||
find_library(ZSTD_LIBRARY_RELEASE NAMES zstd zstd_static)
|
||||
|
||||
include(SelectLibraryConfigurations)
|
||||
SELECT_LIBRARY_CONFIGURATIONS(ZSTD)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
|
||||
ZSTD DEFAULT_MSG
|
||||
ZSTD_LIBRARY ZSTD_INCLUDE_DIR
|
||||
)
|
||||
|
||||
if (ZSTD_FOUND)
|
||||
message(STATUS "Found Zstd: ${ZSTD_LIBRARY}")
|
||||
endif()
|
||||
|
||||
mark_as_advanced(ZSTD_INCLUDE_DIR ZSTD_LIBRARY)
|
||||
@@ -36,8 +36,12 @@ macro(add_imhex_plugin)
|
||||
|
||||
# Add include directories and link libraries
|
||||
target_include_directories(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_INCLUDES})
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${IMHEX_PLUGIN_LIBRARIES} ${FMT_LIBRARIES} imgui_all_includes libwolv)
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_LIBRARIES})
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE libimhex ${FMT_LIBRARIES} imgui_all_includes libwolv tracing)
|
||||
addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl)
|
||||
addIncludesFromLibrary(${IMHEX_PLUGIN_NAME} libpl-gen)
|
||||
|
||||
precompileHeaders(${IMHEX_PLUGIN_NAME} "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
||||
# Add IMHEX_PROJECT_NAME and IMHEX_VERSION define
|
||||
target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}")
|
||||
@@ -45,24 +49,27 @@ macro(add_imhex_plugin)
|
||||
target_compile_definitions(${IMHEX_PLUGIN_NAME} PRIVATE IMHEX_PLUGIN_NAME=${IMHEX_PLUGIN_NAME})
|
||||
|
||||
# Enable required compiler flags
|
||||
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
enableUnityBuild(${IMHEX_PLUGIN_NAME})
|
||||
setupCompilerFlags(${IMHEX_PLUGIN_NAME})
|
||||
|
||||
# Configure build properties
|
||||
set_target_properties(${IMHEX_PLUGIN_NAME}
|
||||
PROPERTIES
|
||||
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins
|
||||
RUNTIME_OUTPUT_DIRECTORY "${IMHEX_MAIN_OUTPUT_DIRECTORY}/plugins"
|
||||
CXX_STANDARD 23
|
||||
PREFIX ""
|
||||
SUFFIX ${IMHEX_PLUGIN_SUFFIX}
|
||||
)
|
||||
|
||||
# Set rpath of plugin libraries to the plugins folder
|
||||
if (APPLE)
|
||||
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES BUILD_RPATH "@executable_path/../Frameworks;@executable_path/plugins")
|
||||
endif()
|
||||
|
||||
# Setup a romfs for the plugin
|
||||
list(APPEND LIBROMFS_RESOURCE_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/romfs)
|
||||
set(LIBROMFS_PROJECT_NAME ${IMHEX_PLUGIN_NAME})
|
||||
add_subdirectory(${IMHEX_BASE_FOLDER}/lib/external/libromfs ${CMAKE_CURRENT_BINARY_DIR}/libromfs)
|
||||
set_target_properties(${LIBROMFS_LIBRARY} PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PRIVATE ${LIBROMFS_LIBRARY})
|
||||
|
||||
foreach(feature ${IMHEX_PLUGIN_FEATURES})
|
||||
@@ -77,11 +84,19 @@ macro(add_imhex_plugin)
|
||||
|
||||
if (IMHEX_EXTERNAL_PLUGIN_BUILD)
|
||||
install(TARGETS ${IMHEX_PLUGIN_NAME} DESTINATION ".")
|
||||
endif()
|
||||
|
||||
# Fix rpath
|
||||
if (APPLE)
|
||||
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks")
|
||||
endif()
|
||||
# Fix rpath
|
||||
if (APPLE)
|
||||
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES INSTALL_RPATH "@executable_path/../Frameworks;@executable_path/plugins")
|
||||
elseif (UNIX)
|
||||
set_target_properties(${IMHEX_PLUGIN_NAME} PROPERTIES INSTALL_RPATH_USE_ORIGIN ON INSTALL_RPATH "$ORIGIN/")
|
||||
endif()
|
||||
|
||||
if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt AND IMHEX_ENABLE_UNIT_TESTS)
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/tests)
|
||||
target_link_libraries(${IMHEX_PLUGIN_NAME} PUBLIC ${IMHEX_PLUGIN_NAME}_tests)
|
||||
target_compile_definitions(${IMHEX_PLUGIN_NAME}_tests PRIVATE IMHEX_PROJECT_NAME="${IMHEX_PLUGIN_NAME}-tests")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
|
||||
@@ -14,12 +14,11 @@ if(CMAKE_GENERATOR)
|
||||
# Being called as include(PostprocessBundle), so define a helper function.
|
||||
set(_POSTPROCESS_BUNDLE_MODULE_LOCATION "${CMAKE_CURRENT_LIST_FILE}")
|
||||
function(postprocess_bundle out_target in_target)
|
||||
add_custom_command(TARGET ${out_target} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -DBUNDLE_PATH="$<TARGET_FILE_DIR:${in_target}>/../.."
|
||||
-DCODE_SIGN_CERTIFICATE_ID="${CODE_SIGN_CERTIFICATE_ID}"
|
||||
-DEXTRA_BUNDLE_LIBRARY_PATHS="${EXTRA_BUNDLE_LIBRARY_PATHS}"
|
||||
-P "${_POSTPROCESS_BUNDLE_MODULE_LOCATION}"
|
||||
)
|
||||
|
||||
install(CODE "set(BUNDLE_PATH ${CMAKE_INSTALL_PREFIX}/${BUNDLE_NAME})")
|
||||
install(CODE "set(CODE_SIGN_CERTIFICATE_ID ${CODE_SIGN_CERTIFICATE_ID})")
|
||||
install(CODE "set(EXTRA_BUNDLE_LIBRARY_PATHS ${EXTRA_BUNDLE_LIBRARY_PATHS})")
|
||||
install(SCRIPT ${_POSTPROCESS_BUNDLE_MODULE_LOCATION})
|
||||
endfunction()
|
||||
return()
|
||||
endif()
|
||||
@@ -36,14 +35,13 @@ message(STATUS "Fixing up application bundle: ${BUNDLE_PATH}")
|
||||
|
||||
# Make sure to fix up any included ImHex plugin.
|
||||
file(GLOB_RECURSE plugins "${BUNDLE_PATH}/Contents/MacOS/plugins/*.hexplug")
|
||||
file(GLOB_RECURSE plugin_libs "${BUNDLE_PATH}/Contents/MacOS/plugins/*.hexpluglib")
|
||||
|
||||
|
||||
# BundleUtilities doesn't support DYLD_FALLBACK_LIBRARY_PATH behavior, which
|
||||
# makes it sometimes break on libraries that do weird things with @rpath. Specify
|
||||
# equivalent search directories until https://gitlab.kitware.com/cmake/cmake/issues/16625
|
||||
# is fixed and in our minimum CMake version.
|
||||
set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib" ${EXTRA_BUNDLE_LIBRARY_PATHS} "${BUNDLE_PATH}/Contents/MacOS/plugins")
|
||||
set(extra_dirs "/usr/local/lib" "/lib" "/usr/lib" ${EXTRA_BUNDLE_LIBRARY_PATHS} "${BUNDLE_PATH}/Contents/MacOS/plugins" "${BUNDLE_PATH}/Contents/Frameworks")
|
||||
message(STATUS "Fixing up application bundle: ${extra_dirs}")
|
||||
|
||||
# BundleUtilities is overly verbose, so disable most of its messages
|
||||
@@ -55,7 +53,8 @@ message(STATUS "Fixing up application bundle: ${extra_dirs}")
|
||||
|
||||
include(BundleUtilities)
|
||||
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
||||
fixup_bundle("${BUNDLE_PATH}" "${plugins};${plugin_libs}" "${extra_dirs}")
|
||||
|
||||
fixup_bundle("${BUNDLE_PATH}" "${plugins}" "${extra_dirs}")
|
||||
|
||||
if (CODE_SIGN_CERTIFICATE_ID)
|
||||
# Hack around Apple Silicon signing bugs by copying the real app, signing it and moving it back.
|
||||
@@ -67,4 +66,13 @@ if (CODE_SIGN_CERTIFICATE_ID)
|
||||
endif()
|
||||
|
||||
# Add a necessary rpath to the imhex binary
|
||||
get_bundle_main_executable("${BUNDLE_PATH}" IMHEX_EXECUTABLE)
|
||||
get_bundle_main_executable("${BUNDLE_PATH}" IMHEX_EXECUTABLE)
|
||||
|
||||
file(GLOB_RECURSE plugin_libs "${BUNDLE_PATH}/Contents/MacOS/*.hexpluglib")
|
||||
foreach(plugin_lib ${plugin_libs})
|
||||
get_filename_component(plugin_lib_name ${plugin_lib} NAME)
|
||||
set(plugin_lib_dest "${BUNDLE_PATH}/Contents/MacOS/plugins/${plugin_lib_name}")
|
||||
|
||||
configure_file(${plugin_lib} "${plugin_lib_dest}" COPYONLY)
|
||||
message(STATUS "Copying plugin library: ${plugin_lib} to ${plugin_lib_dest}")
|
||||
endforeach ()
|
||||
@@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.20)
|
||||
project(ImHexSDK)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON CACHE BOOL "" FORCE)
|
||||
include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/build_helpers.cmake")
|
||||
|
||||
set(IMHEX_BASE_FOLDER ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)
|
||||
@@ -22,27 +23,36 @@ add_subdirectory(lib/third_party/imgui EXCLUDE_FROM_ALL)
|
||||
set(FMT_INSTALL OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory_if_exists(lib/third_party/fmt)
|
||||
set(FMT_LIBRARIES fmt::fmt-header-only PARENT_SCOPE)
|
||||
set(FMT_LIBRARIES fmt::fmt-header-only)
|
||||
|
||||
add_subdirectory_if_exists(lib/third_party/nlohmann_json)
|
||||
set(NLOHMANN_JSON_LIBRARIES nlohmann_json PARENT_SCOPE)
|
||||
set(NLOHMANN_JSON_LIBRARIES nlohmann_json)
|
||||
|
||||
add_subdirectory(lib/external/libwolv EXCLUDE_FROM_ALL)
|
||||
|
||||
set(LIBPL_ENABLE_CLI OFF CACHE BOOL "" FORCE)
|
||||
add_subdirectory(lib/external/pattern_language EXCLUDE_FROM_ALL)
|
||||
|
||||
find_package(CURL REQUIRED)
|
||||
find_package(mbedTLS 3.4.0 REQUIRED)
|
||||
set(CURL_LIBRARIES ${CURL_LIBRARIES} PARENT_SCOPE)
|
||||
set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARIES} PARENT_SCOPE)
|
||||
|
||||
add_subdirectory(lib/libimhex)
|
||||
|
||||
if (WIN32)
|
||||
set_target_properties(libimhex PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex.dll"
|
||||
IMPORTED_IMPLIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/liblibimhex.dll.a"
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../libimhex.dll"
|
||||
IMPORTED_IMPLIB "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex.dll.a"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include")
|
||||
elseif (APPLE)
|
||||
file(GLOB LIBIMHEX_DYLIB "${CMAKE_CURRENT_SOURCE_DIR}/../../Frameworks/libimhex.*.dylib")
|
||||
set_target_properties(libimhex PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex.dylib"
|
||||
IMPORTED_LOCATION "${LIBIMHEX_DYLIB}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include")
|
||||
else()
|
||||
set_target_properties(libimhex PROPERTIES
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex.so"
|
||||
IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/../libimhex.so"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_SOURCE_DIR}/lib/libimhex/include")
|
||||
endif()
|
||||
|
||||
0
dist/Dockerfile → dist/Arch/Dockerfile
vendored
0
dist/Dockerfile → dist/Arch/Dockerfile
vendored
1
dist/appimage/Dockerfile
vendored
1
dist/appimage/Dockerfile
vendored
@@ -53,7 +53,6 @@ CC=gcc-12 CXX=g++-12 cmake -G "Ninja" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DIMHEX_PATTERNS_PULL_MASTER=ON \
|
||||
-DIMHEX_COMMIT_HASH_SHORT="${GIT_COMMIT_HASH::7}" \
|
||||
-DIMHEX_COMMIT_HASH_LONG="${GIT_COMMIT_HASH}" \
|
||||
-DIMHEX_COMMIT_BRANCH="${GIT_BRANCH}" \
|
||||
-DIMHEX_ENABLE_LTO=${LTO} \
|
||||
|
||||
13
dist/compiling/docker.md
vendored
13
dist/compiling/docker.md
vendored
@@ -18,11 +18,11 @@ docker buildx build . -f <DOCKERFILE_PATH> --progress plain --build-arg 'JOBS=4'
|
||||
|
||||
where `<DOCKERFILE_PATH>` should be replaced by the wanted Dockerfile base d on the build you want to do:
|
||||
|
||||
| Wanted build | Dockerfile path |
|
||||
|--------------|-----------------------------|
|
||||
| MacOS M1 | dist/macOS/arm64.Dockerfile |
|
||||
| AppImage | dist/appimage/Dockerfile |
|
||||
| Web version | dist/web/Dockerfile |
|
||||
| Wanted build | Dockerfile path | Target |
|
||||
|--------------|-----------------------------|--------|
|
||||
| MacOS M1 | dist/macOS/arm64.Dockerfile | - |
|
||||
| AppImage | dist/appimage/Dockerfile | - |
|
||||
| Web version | dist/web/Dockerfile | raw |
|
||||
|
||||
We'll explain this command in the next section
|
||||
|
||||
@@ -43,6 +43,7 @@ In the command saw earlier:
|
||||
- `.` is the base folder that the Dockerfile will be allowed to see
|
||||
- `-f <path>` is to specify the Dockerfile path
|
||||
- `--progress plain` is to allow you to see the output of instructions
|
||||
- `--build-arg <key>=<value>` is to allow to to specify arguments to the build (like -DKEY=VALUE in CMake)
|
||||
- `--build-arg <key>=<value>` is to allow to specify arguments to the build (like -DKEY=VALUE in CMake)
|
||||
- `--build-context key=<folder>` is to specify folders other than the base folder that the Dockerfile is allowed to see
|
||||
- `--output <path>` is the path to write the output package to. If not specified, Docker will create an image as the output (probably not what you want)
|
||||
- `--target <target>` specifies which docker target to build
|
||||
|
||||
13
dist/compiling/linux.md
vendored
13
dist/compiling/linux.md
vendored
@@ -9,17 +9,12 @@ On Linux, ImHex is built through regular GCC (or optionally Clang).
|
||||
cd ImHex
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=gcc-12 CXX=g++-12 cmake \
|
||||
CC=gcc-12 CXX=g++-12 \
|
||||
cmake -G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_C_FLAGS="-fuse-ld=lld" \
|
||||
-DCMAKE_CXX_FLAGS="-fuse-ld=lld" \
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_INSTALL_PREFIX="/usr" \
|
||||
..
|
||||
make -j 4 install
|
||||
ninja install
|
||||
```
|
||||
|
||||
All paths follow the XDG Base Directories standard, and can thus be modified
|
||||
|
||||
21
dist/compiling/macos.md
vendored
21
dist/compiling/macos.md
vendored
@@ -9,23 +9,14 @@ On macOS, ImHex is built through regular GCC and LLVM clang.
|
||||
cd ImHex
|
||||
mkdir -p build
|
||||
cd build
|
||||
CC=$(brew --prefix gcc@12)/bin/gcc-12 \
|
||||
CXX=$(brew --prefix gcc@12)/bin/g++-12 \
|
||||
CC=$(brew --prefix llvm)/bin/clang \
|
||||
CXX=$(brew --prefix llvm)/bin/clang++ \
|
||||
OBJC=$(brew --prefix llvm)/bin/clang \
|
||||
OBJCXX=$(brew --prefix llvm)/bin/clang++ \
|
||||
PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig":"$(brew --prefix)/lib/pkgconfig" \
|
||||
MACOSX_DEPLOYMENT_TARGET="10.15" \
|
||||
cmake \
|
||||
cmake -G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="./install" \
|
||||
-DIMHEX_GENERATE_PACKAGE=ON \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
..
|
||||
make -j4 package
|
||||
```
|
||||
|
||||
If the build fails while trying to find the macOS libraries, make sure you have
|
||||
Xcode installed with `xcode-select --install`. Homebrew will also help get the
|
||||
most recent SDK installed and configured with `brew doctor`.
|
||||
ninja install
|
||||
```
|
||||
2
dist/compiling/windows.md
vendored
2
dist/compiling/windows.md
vendored
@@ -14,7 +14,7 @@ mkdir build
|
||||
cd build
|
||||
cmake -G "Ninja" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="$PWD/install" \
|
||||
-DCMAKE_INSTALL_PREFIX="./install" \
|
||||
-DIMHEX_USE_DEFAULT_BUILD_SETTINGS=ON \
|
||||
..
|
||||
ninja install
|
||||
|
||||
35
dist/get_deps_msys2.sh
vendored
35
dist/get_deps_msys2.sh
vendored
@@ -1,19 +1,20 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
pacman -S --needed --noconfirm \
|
||||
mingw-w64-x86_64-gcc \
|
||||
mingw-w64-x86_64-lld \
|
||||
mingw-w64-x86_64-cmake \
|
||||
mingw-w64-x86_64-ccache \
|
||||
mingw-w64-x86_64-glfw \
|
||||
mingw-w64-x86_64-file \
|
||||
mingw-w64-x86_64-curl-winssl \
|
||||
mingw-w64-x86_64-mbedtls \
|
||||
mingw-w64-x86_64-freetype \
|
||||
mingw-w64-x86_64-dlfcn \
|
||||
mingw-w64-x86_64-ninja \
|
||||
mingw-w64-x86_64-capstone \
|
||||
mingw-w64-x86_64-zlib \
|
||||
mingw-w64-x86_64-bzip2 \
|
||||
mingw-w64-x86_64-xz \
|
||||
mingw-w64-x86_64-zstd
|
||||
pacman -S --needed --noconfirm pactoys
|
||||
pacboy -S --needed --noconfirm \
|
||||
gcc:p \
|
||||
lld:p \
|
||||
cmake:p \
|
||||
ccache:p \
|
||||
glfw:p \
|
||||
file:p \
|
||||
curl-winssl:p \
|
||||
mbedtls:p \
|
||||
freetype:p \
|
||||
dlfcn:p \
|
||||
ninja:p \
|
||||
capstone:p \
|
||||
zlib:p \
|
||||
bzip2:p \
|
||||
xz:p \
|
||||
zstd:p
|
||||
|
||||
19
dist/get_deps_tumbleweed.sh
vendored
Executable file
19
dist/get_deps_tumbleweed.sh
vendored
Executable file
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
zypper install \
|
||||
cmake \
|
||||
ninja \
|
||||
gcc12 \
|
||||
gcc12-c++ \
|
||||
freetype2-devel \
|
||||
libcurl-devel \
|
||||
dbus-1-devel \
|
||||
file-devel \
|
||||
Mesa-libGL-devel \
|
||||
libglfw-devel \
|
||||
mbedtls-devel \
|
||||
gtk3-devel \
|
||||
libzstd-devel \
|
||||
zlib-devel \
|
||||
bzip3-devel \
|
||||
xz-devel
|
||||
238
dist/langtool.py
vendored
Normal file → Executable file
238
dist/langtool.py
vendored
Normal file → Executable file
@@ -1,104 +1,208 @@
|
||||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
import sys
|
||||
import argparse
|
||||
import json
|
||||
|
||||
# This fixes a CJK full-width character input issue
|
||||
# which makes left halves of deleted characters displayed on screen
|
||||
# pylint: disable=unused-import
|
||||
import re
|
||||
import readline
|
||||
|
||||
DEFAULT_LANG = "en_US"
|
||||
DEFAULT_LANG_PATH = "plugins/*/romfs/lang/"
|
||||
INVALID_TRANSLATION = ""
|
||||
|
||||
|
||||
def handle_missing_key(command, lang_data, key, value):
|
||||
if command == "check":
|
||||
print(f"Error: Translation {lang_data['code']} is missing translation for key '{key}'")
|
||||
exit(2)
|
||||
elif command == "translate" or command == "create":
|
||||
print(f"Key \033[1m'{key}': '{value}'\033[0m is missing in translation '{lang_data['code']}'")
|
||||
new_value = input("Enter translation: ")
|
||||
lang_data["translations"][key] = new_value
|
||||
elif command == "update":
|
||||
lang_data["translations"][key] = INVALID_TRANSLATION
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 3:
|
||||
print(f"Usage: {Path(sys.argv[0]).name} <check|translate|update|create> <lang folder path> <language>")
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="langtool",
|
||||
description="ImHex translate tool",
|
||||
)
|
||||
parser.add_argument(
|
||||
"command",
|
||||
choices=[
|
||||
"check",
|
||||
"translate",
|
||||
"update",
|
||||
"create",
|
||||
"retranslate",
|
||||
"untranslate",
|
||||
"fmtzh",
|
||||
],
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c", "--langdir", default=DEFAULT_LANG_PATH, help="Language folder glob"
|
||||
)
|
||||
parser.add_argument("-l", "--lang", default="", help="Language to translate")
|
||||
parser.add_argument(
|
||||
"-r", "--reflang", default="", help="Language for reference when translating"
|
||||
)
|
||||
parser.add_argument(
|
||||
"-k", "--keys", help="Keys to re-translate (only in re/untranslate mode)"
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
command = args.command
|
||||
lang = args.lang
|
||||
|
||||
print(f"Running in {command} mode")
|
||||
lang_files_glob = f"{lang}.json" if lang != "" else "*.json"
|
||||
|
||||
lang_folders = set(Path(".").glob(args.langdir))
|
||||
if len(lang_folders) == 0:
|
||||
print(f"Error: {args.langdir} matches nothing")
|
||||
return 1
|
||||
|
||||
command = sys.argv[1]
|
||||
if command not in ["check", "translate", "update", "create"]:
|
||||
print(f"Unknown command: {command}")
|
||||
return 1
|
||||
for lang_folder in lang_folders:
|
||||
if not lang_folder.is_dir():
|
||||
print(f"Error: {lang_folder} is not a folder")
|
||||
return 1
|
||||
|
||||
print(f"Using langtool in {command} mode")
|
||||
default_lang_data = {}
|
||||
default_lang_path = lang_folder / Path(DEFAULT_LANG + ".json")
|
||||
if not default_lang_path.exists():
|
||||
print(
|
||||
f"Error: Default language file {default_lang_path} does not exist in {lang_folder}"
|
||||
)
|
||||
return 1
|
||||
with default_lang_path.open("r", encoding="utf-8") as file:
|
||||
default_lang_data = json.load(file)
|
||||
|
||||
lang_folder_path = Path(sys.argv[2])
|
||||
if not lang_folder_path.exists():
|
||||
print(f"Error: {lang_folder_path} does not exist")
|
||||
return 1
|
||||
|
||||
if not lang_folder_path.is_dir():
|
||||
print(f"Error: {lang_folder_path} is not a folder")
|
||||
return 1
|
||||
|
||||
lang = sys.argv[3] if len(sys.argv) > 3 else ""
|
||||
|
||||
print(f"Processing language files in {lang_folder_path}...")
|
||||
|
||||
default_lang_file_path = lang_folder_path / Path(DEFAULT_LANG + ".json")
|
||||
if not default_lang_file_path.exists():
|
||||
print(f"Error: Default language file {default_lang_file_path} does not exist")
|
||||
return 1
|
||||
|
||||
print(f"Using file '{default_lang_file_path.name}' as template language file")
|
||||
|
||||
with default_lang_file_path.open("r", encoding="utf-8") as default_lang_file:
|
||||
default_lang_data = json.load(default_lang_file)
|
||||
reference_lang_data = None
|
||||
reference_lang_path = lang_folder / Path(args.reflang + ".json")
|
||||
if reference_lang_path.exists():
|
||||
with reference_lang_path.open("r", encoding="utf-8") as file:
|
||||
reference_lang_data = json.load(file)
|
||||
|
||||
if command == "create" and lang != "":
|
||||
lang_file_path = lang_folder_path / Path(lang + ".json")
|
||||
lang_file_path = lang_folder / Path(lang + ".json")
|
||||
if lang_file_path.exists():
|
||||
print(f"Error: Language file {lang_file_path} already exists")
|
||||
return 1
|
||||
continue
|
||||
|
||||
print(f"Creating new language file '{lang_file_path.name}'")
|
||||
exist_lang_data = None
|
||||
for lang_folder1 in lang_folders:
|
||||
lang_file_path1 = lang_folder1 / Path(lang + ".json")
|
||||
if lang_file_path1.exists():
|
||||
with lang_file_path1.open("r", encoding="utf-8") as file:
|
||||
exist_lang_data = json.load(file)
|
||||
break
|
||||
|
||||
print(f"Creating new language file '{lang_file_path}'")
|
||||
|
||||
with lang_file_path.open("w", encoding="utf-8") as new_lang_file:
|
||||
new_lang_data = {
|
||||
"code": lang,
|
||||
"language": input("Enter language: "),
|
||||
"country": input("Enter country: "),
|
||||
"translations": {}
|
||||
"language": (
|
||||
exist_lang_data["language"]
|
||||
if exist_lang_data
|
||||
else input("Enter language name: ")
|
||||
),
|
||||
"country": (
|
||||
exist_lang_data["country"]
|
||||
if exist_lang_data
|
||||
else input("Enter country name: ")
|
||||
),
|
||||
"translations": {},
|
||||
}
|
||||
json.dump(new_lang_data, new_lang_file, indent=4, ensure_ascii=False)
|
||||
|
||||
for additional_lang_file_path in lang_folder_path.glob("*.json"):
|
||||
if not lang == "" and not additional_lang_file_path.stem == lang:
|
||||
lang_files = set(lang_folder.glob(lang_files_glob))
|
||||
if len(lang_files) == 0:
|
||||
print(f"Warn: Language file for '{lang}' does not exist in '{lang_folder}'")
|
||||
for lang_file_path in lang_files:
|
||||
if (
|
||||
lang_file_path.stem == f"{DEFAULT_LANG}.json"
|
||||
or lang_file_path.stem == f"{args.reflang}.json"
|
||||
):
|
||||
continue
|
||||
|
||||
if additional_lang_file_path.name.startswith(DEFAULT_LANG):
|
||||
continue
|
||||
print(f"\nProcessing '{lang_file_path}'")
|
||||
if not (command == "update" or command == "create"):
|
||||
print("\n----------------------------\n")
|
||||
|
||||
print(f"\nProcessing file '{additional_lang_file_path.name}'\n----------------------------\n")
|
||||
|
||||
with additional_lang_file_path.open("r+", encoding="utf-8") as additional_lang_file:
|
||||
additional_lang_data = json.load(additional_lang_file)
|
||||
with lang_file_path.open("r+", encoding="utf-8") as target_lang_file:
|
||||
lang_data = json.load(target_lang_file)
|
||||
|
||||
for key, value in default_lang_data["translations"].items():
|
||||
if key not in additional_lang_data["translations"] or additional_lang_data["translations"][key] == INVALID_TRANSLATION:
|
||||
handle_missing_key(command, additional_lang_data, key, value)
|
||||
has_translation = (
|
||||
key in lang_data["translations"]
|
||||
and lang_data["translations"][key] != INVALID_TRANSLATION
|
||||
)
|
||||
if not has_translation and not (
|
||||
(command == "retranslate" or command == "untranslate")
|
||||
and re.compile(args.keys).fullmatch(key)
|
||||
):
|
||||
continue
|
||||
if command == "check":
|
||||
print(
|
||||
f"Error: Translation {lang_data['code']} is missing translation for key '{key}'"
|
||||
)
|
||||
exit(2)
|
||||
elif (
|
||||
command == "translate"
|
||||
or command == "retranslate"
|
||||
or command == "untranslate"
|
||||
):
|
||||
if command == "untranslate" and not has_translation:
|
||||
continue
|
||||
reference_tranlsation = (
|
||||
" '%s'" % reference_lang_data["translations"][key]
|
||||
if reference_lang_data
|
||||
else ""
|
||||
)
|
||||
print(
|
||||
f"\033[1m'{key}' '{value}'{reference_tranlsation}\033[0m => {lang_data['language']}",
|
||||
end="",
|
||||
)
|
||||
if has_translation:
|
||||
translation = lang_data["translations"][key]
|
||||
print(f" <= \033[1m'{translation}'\033[0m")
|
||||
print() # for a new line
|
||||
if command == "untranslate":
|
||||
lang_data["translations"][key] = INVALID_TRANSLATION
|
||||
continue
|
||||
try:
|
||||
new_value = input("=> ")
|
||||
lang_data["translations"][key] = new_value
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
elif command == "update" or command == "create":
|
||||
lang_data["translations"][key] = INVALID_TRANSLATION
|
||||
elif command == "fmtzh":
|
||||
if has_translation:
|
||||
lang_data["translations"][key] = fmtzh(
|
||||
lang_data["translations"][key]
|
||||
)
|
||||
|
||||
keys_to_remove = []
|
||||
for key, value in additional_lang_data["translations"].items():
|
||||
for key, value in lang_data["translations"].items():
|
||||
if key not in default_lang_data["translations"]:
|
||||
keys_to_remove.append(key)
|
||||
|
||||
for key in keys_to_remove:
|
||||
additional_lang_data["translations"].pop(key)
|
||||
print(f"Removed unused key '{key}' from translation '{additional_lang_data['code']}'")
|
||||
lang_data["translations"].pop(key)
|
||||
print(
|
||||
f"Removed unused key '{key}' from translation '{lang_data['code']}'"
|
||||
)
|
||||
|
||||
additional_lang_file.seek(0)
|
||||
additional_lang_file.truncate()
|
||||
json.dump(additional_lang_data, additional_lang_file, indent=4, sort_keys=True, ensure_ascii=False)
|
||||
target_lang_file.seek(0)
|
||||
target_lang_file.truncate()
|
||||
json.dump(
|
||||
lang_data,
|
||||
target_lang_file,
|
||||
indent=4,
|
||||
sort_keys=True,
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
def fmtzh(text: str) -> str:
|
||||
text = re.sub(r"(\.{3}|\.{6})", "……", text)
|
||||
text = text.replace("!", "!")
|
||||
text = re.sub(r"([^\.\na-zA-Z\d])\.$", "\1。", text, flags=re.M)
|
||||
text = text.replace("?", "?")
|
||||
return text
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
||||
|
||||
10
dist/macOS/arm64.Dockerfile
vendored
10
dist/macOS/arm64.Dockerfile
vendored
@@ -35,9 +35,9 @@ EOF
|
||||
|
||||
## Download libmagic
|
||||
### Clone libmagic
|
||||
RUN git clone https://github.com/file/file /mnt/file
|
||||
RUN git clone --depth 1 --branch FILE5_45 https://github.com/file/file /mnt/file
|
||||
### Download libmagic dependencies
|
||||
RUN --mount=type=cache,target=/var/lib/apt/lists/ apt install -y libtool autoconf
|
||||
RUN --mount=type=cache,target=/var/lib/apt/lists/ apt update && apt install -y libtool autoconf
|
||||
|
||||
# -- DOWNLOADING + BUILDING STUFF
|
||||
|
||||
@@ -126,7 +126,7 @@ if [ "$CUSTOM_GLFW" ]; then
|
||||
-DCMAKE_OBJC_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_INSTALL_PREFIX=/vcpkg/installed/arm-osx-mytriplet \
|
||||
-DVCPKG_TARGET_TRIPLET=arm-osx-mytriplet -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/osxcross/target/toolchain.cmake -DCMAKE_OSX_SYSROOT=/osxcross/target/SDK/MacOSX14.0.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \
|
||||
-DVCPKG_TARGET_TRIPLET=arm-osx-mytriplet -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/osxcross/target/toolchain.cmake -DCMAKE_OSX_SYSROOT=/osxcross/target/SDK/MacOSX14.0.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=12.1 \
|
||||
..
|
||||
ninja -j $JOBS install
|
||||
|
||||
@@ -148,7 +148,7 @@ RUN --mount=type=cache,target=/cache --mount=type=cache,target=/mnt/ImHex/build/
|
||||
`# ccache flags` \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_OBJC_COMPILER_LAUNCHER=ccache -DCMAKE_OBJCXX_COMPILER_LAUNCHER=ccache \
|
||||
`# MacOS cross-compiling flags` \
|
||||
-DVCPKG_TARGET_TRIPLET=arm-osx-mytriplet -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/osxcross/target/toolchain.cmake -DCMAKE_OSX_SYSROOT=/osxcross/target/SDK/MacOSX14.0.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0 \
|
||||
-DVCPKG_TARGET_TRIPLET=arm-osx-mytriplet -DCMAKE_TOOLCHAIN_FILE=/vcpkg/scripts/buildsystems/vcpkg.cmake -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=/osxcross/target/toolchain.cmake -DCMAKE_OSX_SYSROOT=/osxcross/target/SDK/MacOSX14.0.sdk -DCMAKE_OSX_DEPLOYMENT_TARGET=12.1 \
|
||||
`# Override compilers for code generators` \
|
||||
-DNATIVE_CMAKE_C_COMPILER=/usr/bin/clang -DNATIVE_CMAKE_CXX_COMPILER=/usr/bin/clang++ \
|
||||
`# Normal ImHex flags` \
|
||||
@@ -170,4 +170,4 @@ EOF
|
||||
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /mnt/ImHex/build/install/imhex.app ImHex.app
|
||||
COPY --from=build /mnt/ImHex/build/install/imhex.app imhex.app
|
||||
|
||||
9
dist/rpm/imhex.spec
vendored
9
dist/rpm/imhex.spec
vendored
@@ -1,5 +1,5 @@
|
||||
Name: imhex
|
||||
Version: 1.26.2
|
||||
Version: VERSION
|
||||
Release: 0%{?dist}
|
||||
Summary: A hex editor for reverse engineers and programmers
|
||||
|
||||
@@ -26,7 +26,7 @@ BuildRequires: llvm-devel
|
||||
BuildRequires: mbedtls-devel
|
||||
BuildRequires: yara-devel
|
||||
BuildRequires: nativefiledialog-extended-devel
|
||||
BuildRequires: dotnet-sdk-7.0
|
||||
BuildRequires: dotnet-sdk-8.0
|
||||
BuildRequires: libzstd-devel
|
||||
BuildRequires: zlib-devel
|
||||
BuildRequires: bzip2-devel
|
||||
@@ -40,7 +40,6 @@ Provides: bundled(capstone) = 5.0-rc2
|
||||
Provides: bundled(imgui)
|
||||
Provides: bundled(libromfs)
|
||||
Provides: bundled(microtar)
|
||||
Provides: bundled(libpl)
|
||||
Provides: bundled(xdgpp)
|
||||
|
||||
# ftbfs on these arches. armv7hl might compile when capstone 5.x
|
||||
@@ -96,10 +95,6 @@ CXXFLAGS+=" -std=gnu++2b"
|
||||
%set_build_flags
|
||||
CXXFLAGS+=" -std=gnu++2b"
|
||||
%endif
|
||||
# build binaries required for tests
|
||||
%cmake_build --target unit_tests
|
||||
%ctest --exclude-regex '(Helpers/StoreAPI|Helpers/TipsAPI|Helpers/ContentAPI)'
|
||||
# Helpers/*API exclude tests that require network access
|
||||
|
||||
|
||||
%install
|
||||
|
||||
15
dist/web/Dockerfile
vendored
15
dist/web/Dockerfile
vendored
@@ -1,4 +1,4 @@
|
||||
FROM emscripten/emsdk:latest as build
|
||||
FROM emscripten/emsdk:3.1.51 as build
|
||||
|
||||
# Used to invalidate layer cache but not mount cache
|
||||
# See https://github.com/moby/moby/issues/41715#issuecomment-733976493
|
||||
@@ -59,6 +59,7 @@ cmake /imhex
|
||||
-DIMHEX_OFFLINE_BUILD=ON \
|
||||
-DIMHEX_STATIC_LINK_PLUGINS=ON \
|
||||
-DIMHEX_EXCLUDE_PLUGINS="script_loader" \
|
||||
-DIMHEX_COMPRESS_DEBUG_INFO=OFF \
|
||||
-DNATIVE_CMAKE_C_COMPILER=gcc \
|
||||
-DNATIVE_CMAKE_CXX_COMPILER=g++ \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
@@ -73,10 +74,15 @@ cp /imhex/dist/web/source/* /build
|
||||
ccache -s
|
||||
EOF
|
||||
|
||||
FROM scratch
|
||||
# Create a file dedicated to store wasm size, because I know no way to get the wasm content length if the web server uses compression
|
||||
# See https://stackoverflow.com/questions/41701849/cannot-modify-accept-encoding-with-fetch https://github.com/AnthumChris/fetch-progress-indicators/issues/13
|
||||
RUN du -b /build/imhex.wasm | cut -f1 > imhex.wasm.size
|
||||
|
||||
FROM scratch as raw
|
||||
COPY --from=build [ \
|
||||
# ImHex \
|
||||
"/build/imhex.wasm", \
|
||||
"/build/imhex.wasm.size", \
|
||||
"/build/imhex.js", \
|
||||
"/build/imhex.worker.js", \
|
||||
\
|
||||
@@ -93,4 +99,7 @@ COPY --from=build [ \
|
||||
\
|
||||
# Destination \
|
||||
"./" \
|
||||
]
|
||||
]
|
||||
|
||||
FROM nginx
|
||||
COPY --from=raw . /usr/share/nginx/html
|
||||
|
||||
10
dist/web/compose.yml
vendored
Normal file
10
dist/web/compose.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# docker compose -f dist/web/compose.yml up --build
|
||||
version: '3'
|
||||
services:
|
||||
imhex_web:
|
||||
image: imhex_web:latest
|
||||
build:
|
||||
context: ../../ # ImHex folder
|
||||
dockerfile: ./dist/web/Dockerfile
|
||||
ports:
|
||||
- 8080:80
|
||||
4
dist/web/plugin-bundle.cpp.in
vendored
4
dist/web/plugin-bundle.cpp.in
vendored
@@ -2,10 +2,12 @@
|
||||
|
||||
extern "C" void forceLinkPlugin_@IMHEX_PLUGIN_NAME@();
|
||||
|
||||
namespace {
|
||||
struct StaticLoad {
|
||||
StaticLoad() {
|
||||
forceLinkPlugin_@IMHEX_PLUGIN_NAME@();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static StaticLoad staticLoad;
|
||||
static StaticLoad staticLoad;
|
||||
|
||||
61
dist/web/source/index.html
vendored
61
dist/web/source/index.html
vendored
@@ -61,37 +61,44 @@
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="loading" class="centered">
|
||||
<img src="https://raw.githubusercontent.com/WerWolv/ImHex/master/plugins/builtin/romfs/assets/dark/banner.png" id="logo" alt="ImHex Logo">
|
||||
<h1>A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.</h1>
|
||||
<h2>Available both natively and on the web</h2>
|
||||
<h5>ImHex runs directly in your web browser with the help of Emscripten and WebAssembly.</h5>
|
||||
<div id="loading" class="centered">
|
||||
<img src="https://raw.githubusercontent.com/WerWolv/ImHex/master/plugins/builtin/romfs/assets/dark/banner.png" id="logo" alt="ImHex Logo">
|
||||
<h1>A Hex Editor for Reverse Engineers, Programmers and people who value their retinas when working at 3 AM.</h1>
|
||||
<h2>Available both natively and on the web</h2>
|
||||
<h5>ImHex runs directly in your web browser with the help of Emscripten and WebAssembly.</h5>
|
||||
|
||||
<div style="height: 50%">
|
||||
<div style="height: 30%"> </div>
|
||||
<h2 id="not_working">
|
||||
Not loading in your Browser? <a href="https://imhex.werwolv.net">Try the native version</a>
|
||||
</h2>
|
||||
<div style="height: 50%"></div>
|
||||
</div>
|
||||
|
||||
<div class="loading_ripple">
|
||||
<div class="lds-ripple"><div></div><div></div></div>
|
||||
</div>
|
||||
|
||||
<div style="height: 10%">
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<a href="https://imhex.werwolv.net">Homepage</a>
|
||||
<p>Made with ♥️ by the ImHex Team</p>
|
||||
<a href="https://github.com/WerWolv/ImHex">GitHub</a>
|
||||
<div style="height: 50%">
|
||||
<div style="height: 30%"> </div>
|
||||
<h2 id="not_working">
|
||||
Not loading in your Browser? <a href="https://imhex.werwolv.net">Try the native version</a>
|
||||
</h2>
|
||||
<div class="progress-bar-container">
|
||||
<div class="progress progress-moved">
|
||||
<div class="progress-bar" id="progress-bar-content">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
|
||||
|
||||
<script src="wasm-config.js"></script>
|
||||
<script async src="imhex.js"></script>
|
||||
|
||||
<div class="loading_ripple">
|
||||
<div class="lds-ripple"><div></div><div></div></div>
|
||||
</div>
|
||||
|
||||
<div style="height: 10%">
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<a href="https://imhex.werwolv.net">Homepage</a>
|
||||
<p>Made with ♥️ by the ImHex Team</p>
|
||||
<a href="https://github.com/WerWolv/ImHex">GitHub</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<canvas class="emscripten" id="canvas" oncontextmenu="event.preventDefault()"></canvas>
|
||||
|
||||
<script src="wasm-config.js"></script>
|
||||
<script async src="imhex.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
56
dist/web/source/sitemap.xml
vendored
56
dist/web/source/sitemap.xml
vendored
@@ -5,10 +5,58 @@
|
||||
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
|
||||
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
|
||||
|
||||
<url>
|
||||
<loc>https://web.imhex.werwolv.net/</loc>
|
||||
<lastmod>2023-12-07T22:53:06+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://web.imhex.werwolv.net/</loc>
|
||||
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
|
||||
<priority>1.00</priority>
|
||||
</url>
|
||||
<url>
|
||||
<title>English</title>
|
||||
<loc>https://web.imhex.werwolv.net?lang=en-US</loc>
|
||||
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
|
||||
<priority>1.00</priority>
|
||||
</url>
|
||||
<url>
|
||||
<title>Deutsch</title>
|
||||
<loc>https://web.imhex.werwolv.net?lang=de-DE</loc>
|
||||
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
|
||||
<priority>1.00</priority>
|
||||
</url>
|
||||
<url>
|
||||
<title>Português</title>
|
||||
<loc>https://web.imhex.werwolv.net?lang=pt-BR</loc>
|
||||
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<title>中国</title>
|
||||
<loc>https://web.imhex.werwolv.net?lang=zh-CN</loc>
|
||||
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<title>國語</title>
|
||||
<loc>https://web.imhex.werwolv.net?lang=zh-TW</loc>
|
||||
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<title>日本語</title>
|
||||
<loc>https://web.imhex.werwolv.net?lang=ja-JP</loc>
|
||||
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<title>한국어</title>
|
||||
<loc>https://web.imhex.werwolv.net?lang=ko-KR</loc>
|
||||
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<title>Español</title>
|
||||
<loc>https://web.imhex.werwolv.net?lang=es-ES</loc>
|
||||
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
|
||||
</url>
|
||||
<url>
|
||||
<title>Italiano</title>
|
||||
<loc>https://web.imhex.werwolv.net?lang=it-IT</loc>
|
||||
<lastmod>2024-01-02T11:44:00+00:00</lastmod>
|
||||
</url>
|
||||
|
||||
|
||||
</urlset>
|
||||
42
dist/web/source/style.css
vendored
42
dist/web/source/style.css
vendored
@@ -17,6 +17,7 @@ body {
|
||||
margin-right: auto;
|
||||
display: none;
|
||||
border: 0 none;
|
||||
image-rendering: smooth;
|
||||
}
|
||||
|
||||
h1, h2, h5 {
|
||||
@@ -132,4 +133,45 @@ a:hover {
|
||||
height: 72px;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
:root {
|
||||
--progress: 25%;
|
||||
}
|
||||
|
||||
.progress-bar-container {
|
||||
margin: 100px auto;
|
||||
width: 600px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.progress {
|
||||
padding: 6px;
|
||||
border-radius: 30px;
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.25),
|
||||
0 1px rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
color: rgba(240, 240, 240, 0.9);
|
||||
height: 18px;
|
||||
border-radius: 30px;
|
||||
font-size: 13px;
|
||||
font-family: monospace;
|
||||
font-weight: bold;
|
||||
text-wrap: avoid;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
background-image: linear-gradient(
|
||||
to bottom,
|
||||
rgba(255, 255, 255, 0.2),
|
||||
rgba(255, 255, 255, 0.0)
|
||||
);
|
||||
}
|
||||
|
||||
.progress-moved .progress-bar {
|
||||
width: var(--progress);
|
||||
background-color: #3864cb;
|
||||
}
|
||||
81
dist/web/source/wasm-config.js
vendored
81
dist/web/source/wasm-config.js
vendored
@@ -1,3 +1,67 @@
|
||||
let wasmSize = null;
|
||||
// See comment in dist/web/Dockerfile about imhex.wasm.size
|
||||
fetch("imhex.wasm.size").then(async (resp) => {
|
||||
wasmSize = parseInt((await resp.text()).trim());
|
||||
console.log(`Real WASM binary size is ${wasmSize} bytes`);
|
||||
});
|
||||
|
||||
// Monkeypatch WebAssembly to have a progress bar
|
||||
// inspired from: https://github.com/WordPress/wordpress-playground/pull/46 (but had to be modified)
|
||||
function monkeyPatch(progressFun) {
|
||||
const _instantiateStreaming = WebAssembly.instantiateStreaming;
|
||||
WebAssembly.instantiateStreaming = (response, ...args) => {
|
||||
// Do not collect wasm content length here see above
|
||||
const file = response.url.substring(
|
||||
new URL(response.url).origin.length + 1
|
||||
);
|
||||
const reportingResponse = new Response(
|
||||
new ReadableStream(
|
||||
{
|
||||
async start(controller) {
|
||||
const reader = response.clone().body.getReader();
|
||||
let loaded = 0;
|
||||
for (; ;) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) {
|
||||
if(wasmSize) progressFun(file, wasmSize);
|
||||
break;
|
||||
}
|
||||
loaded += value.byteLength;
|
||||
progressFun(file, loaded);
|
||||
controller.enqueue(value);
|
||||
}
|
||||
controller.close();
|
||||
}
|
||||
},
|
||||
{
|
||||
status: response.status,
|
||||
statusText: response.statusText
|
||||
}
|
||||
)
|
||||
);
|
||||
for (const pair of response.headers.entries()) {
|
||||
reportingResponse.headers.set(pair[0], pair[1]);
|
||||
}
|
||||
|
||||
return _instantiateStreaming(reportingResponse, ...args);
|
||||
}
|
||||
}
|
||||
monkeyPatch((file, done) => {
|
||||
if (!wasmSize) return;
|
||||
if (done > wasmSize) {
|
||||
console.warn(`Downloaded binary size ${done} is larger than expected WASM size ${wasmSize}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const percent = ((done / wasmSize) * 100).toFixed(0);
|
||||
const mibNow = (done / 1024**2).toFixed(1);
|
||||
const mibTotal = (wasmSize / 1024**2).toFixed(1);
|
||||
|
||||
let root = document.querySelector(':root');
|
||||
root.style.setProperty("--progress", `${percent}%`)
|
||||
document.getElementById("progress-bar-content").innerHTML = `${percent}% [${mibNow} MiB / ${mibTotal} MiB]`;
|
||||
});
|
||||
|
||||
function glfwSetCursorCustom(wnd, shape) {
|
||||
let body = document.getElementsByTagName("body")[0]
|
||||
switch (shape) {
|
||||
@@ -57,14 +121,25 @@ var Module = {
|
||||
})(),
|
||||
setStatus: function(text) { },
|
||||
totalDependencies: 0,
|
||||
monitorRunDependencies: function(left) { },
|
||||
monitorRunDependencies: function(left) {
|
||||
},
|
||||
instantiateWasm: function(imports, successCallback) {
|
||||
imports.env.glfwSetCursor = glfwSetCursorCustom
|
||||
imports.env.glfwCreateStandardCursor = glfwCreateStandardCursorCustom
|
||||
instantiateAsync(wasmBinary, wasmBinaryFile, imports, (result) => successCallback(result.instance, result.module));
|
||||
}
|
||||
instantiateAsync(wasmBinary, wasmBinaryFile, imports, (result) => {
|
||||
successCallback(result.instance, result.module)
|
||||
});
|
||||
},
|
||||
arguments: []
|
||||
};
|
||||
|
||||
// Handle passing arguments to the wasm module
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
if (urlParams.has("lang")) {
|
||||
Module["arguments"].push("--language");
|
||||
Module["arguments"].push(urlParams.get("lang"));
|
||||
}
|
||||
|
||||
window.addEventListener('resize', js_resizeCanvas, false);
|
||||
function js_resizeCanvas() {
|
||||
|
||||
2
lib/external/libromfs
vendored
2
lib/external/libromfs
vendored
Submodule lib/external/libromfs updated: 0a72f7bb33...03adcfdde0
2
lib/external/libwolv
vendored
2
lib/external/libwolv
vendored
Submodule lib/external/libwolv updated: 7efd66f817...101da16892
2
lib/external/pattern_language
vendored
2
lib/external/pattern_language
vendored
Submodule lib/external/pattern_language updated: 78cdc3e7fd...4b1ecbaf8d
@@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.16)
|
||||
project(libimhex)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX "")
|
||||
|
||||
set(LIBIMHEX_SOURCES
|
||||
source/api/imhex_api.cpp
|
||||
@@ -38,6 +37,8 @@ set(LIBIMHEX_SOURCES
|
||||
source/helpers/tar.cpp
|
||||
source/helpers/debugging.cpp
|
||||
|
||||
source/test/tests.cpp
|
||||
|
||||
source/providers/provider.cpp
|
||||
source/providers/memory_provider.cpp
|
||||
source/providers/undo/stack.cpp
|
||||
@@ -77,43 +78,9 @@ else()
|
||||
target_compile_definitions(libimhex PRIVATE IMHEX_PROJECT_NAME="${PROJECT_NAME}")
|
||||
endif()
|
||||
|
||||
set_target_properties(libimhex PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
enableUnityBuild(libimhex)
|
||||
setupCompilerFlags(libimhex)
|
||||
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(libimhex)
|
||||
|
||||
target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} include ${XDGPP_INCLUDE_DIRS} ${MBEDTLS_INCLUDE_DIR} ${MAGIC_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS} ${CURL_INCLUDE_DIRS} ${LIBBACKTRACE_INCLUDE_DIRS})
|
||||
target_link_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARY_DIR} ${MAGIC_LIBRARY_DIRS})
|
||||
|
||||
if (NOT EMSCRIPTEN)
|
||||
# curl is only used in non-emscripten builds
|
||||
target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${CURL_INCLUDE_DIRS})
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${LIBCURL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
|
||||
if (WIN32)
|
||||
set_target_properties(libimhex PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
||||
target_link_options(libimhex PRIVATE -Wl,--export-all-symbols)
|
||||
elseif (APPLE)
|
||||
find_library(FOUNDATION NAMES Foundation)
|
||||
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
|
||||
endif ()
|
||||
|
||||
target_link_libraries(libimhex PRIVATE microtar libpl plcli libpl-gen libwolv ${NFD_LIBRARIES} magic dl ${NLOHMANN_JSON_LIBRARIES} ${MBEDTLS_LIBRARIES} ${LIBBACKTRACE_LIBRARIES} ${JTHREAD_LIBRARIES})
|
||||
target_link_libraries(libimhex PUBLIC ${IMGUI_LIBRARIES})
|
||||
endif()
|
||||
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${FMT_LIBRARIES})
|
||||
|
||||
set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
|
||||
|
||||
if (DEFINED IMHEX_COMMIT_HASH_LONG AND DEFINED IMHEX_COMMIT_HASH_SHORT AND DEFINED IMHEX_COMMIT_BRANCH)
|
||||
if (DEFINED IMHEX_COMMIT_HASH_LONG AND DEFINED IMHEX_COMMIT_BRANCH)
|
||||
set(GIT_COMMIT_HASH_LONG "${IMHEX_COMMIT_HASH_LONG}")
|
||||
set(GIT_COMMIT_HASH_SHORT "${IMHEX_COMMIT_HASH_SHORT}")
|
||||
set(GIT_BRANCH "${IMHEX_COMMIT_BRANCH}")
|
||||
else()
|
||||
# Get the current working branch
|
||||
@@ -126,16 +93,6 @@ else()
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
# Get the latest abbreviated commit hash of the working branch
|
||||
execute_process(
|
||||
COMMAND git log -1 --format=%h --abbrev=7
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
OUTPUT_VARIABLE GIT_COMMIT_HASH_SHORT
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
RESULT_VARIABLE RESULT_HASH_SHORT
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
execute_process(
|
||||
COMMAND git log -1 --format=%H
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
@@ -146,12 +103,51 @@ else()
|
||||
)
|
||||
endif ()
|
||||
|
||||
if ((NOT GIT_COMMIT_HASH_SHORT STREQUAL "") AND (NOT GIT_COMMIT_HASH_LONG STREQUAL "") AND (NOT GIT_BRANCH STREQUAL ""))
|
||||
addDefineToSource(source/api/imhex_api.cpp "GIT_COMMIT_HASH_SHORT=\"${GIT_COMMIT_HASH_SHORT}\"")
|
||||
if (GIT_COMMIT_HASH_LONG STREQUAL "" OR GIT_BRANCH STREQUAL "")
|
||||
message(WARNING "Failed to to determine commit hash/branch")
|
||||
else()
|
||||
addDefineToSource(source/api/imhex_api.cpp "GIT_COMMIT_HASH_LONG=\"${GIT_COMMIT_HASH_LONG}\"")
|
||||
addDefineToSource(source/api/imhex_api.cpp "GIT_BRANCH=\"${GIT_BRANCH}\"")
|
||||
endif ()
|
||||
|
||||
addDefineToSource(source/api/imhex_api.cpp "IMHEX_VERSION=\"${IMHEX_VERSION_STRING}\"")
|
||||
|
||||
add_dependencies(imhex_all libimhex)
|
||||
enableUnityBuild(libimhex)
|
||||
setupCompilerFlags(libimhex)
|
||||
|
||||
include(GenerateExportHeader)
|
||||
generate_export_header(libimhex)
|
||||
|
||||
target_include_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} include ${XDGPP_INCLUDE_DIRS} ${MBEDTLS_INCLUDE_DIR} ${MAGIC_INCLUDE_DIRS} ${LLVM_INCLUDE_DIRS} ${FMT_INCLUDE_DIRS} ${LIBBACKTRACE_INCLUDE_DIRS})
|
||||
target_link_directories(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${MBEDTLS_LIBRARY_DIR} ${MAGIC_LIBRARY_DIRS})
|
||||
|
||||
if (NOT EMSCRIPTEN)
|
||||
# curl is only used in non-emscripten builds
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${CURL_LIBRARIES})
|
||||
endif()
|
||||
|
||||
|
||||
if (NOT IMHEX_EXTERNAL_PLUGIN_BUILD)
|
||||
if (WIN32)
|
||||
set_target_properties(libimhex PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS TRUE)
|
||||
target_link_options(libimhex PRIVATE -Wl,--export-all-symbols)
|
||||
elseif (APPLE)
|
||||
find_library(FOUNDATION NAMES Foundation)
|
||||
target_link_libraries(libimhex PUBLIC ${FOUNDATION})
|
||||
endif ()
|
||||
|
||||
target_link_libraries(libimhex PRIVATE microtar libwolv ${NFD_LIBRARIES} magic dl ${JTHREAD_LIBRARIES})
|
||||
target_link_libraries(libimhex PUBLIC libpl ${IMGUI_LIBRARIES})
|
||||
|
||||
precompileHeaders(libimhex "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
endif()
|
||||
|
||||
target_link_libraries(libimhex ${LIBIMHEX_LIBRARY_TYPE} ${NLOHMANN_JSON_LIBRARIES} imgui_all_includes ${MBEDTLS_LIBRARIES} ${FMT_LIBRARIES} ${LUNASVG_LIBRARIES} tracing)
|
||||
|
||||
set_property(TARGET libimhex PROPERTY INTERPROCEDURAL_OPTIMIZATION FALSE)
|
||||
|
||||
add_dependencies(imhex_all libimhex)
|
||||
|
||||
install(FILES "$<TARGET_FILE:libimhex>" DESTINATION "${CMAKE_INSTALL_LIBDIR}" PERMISSIONS ${LIBRARY_PERMISSIONS})
|
||||
set_target_properties(libimhex PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
set_target_properties(libimhex PROPERTIES PREFIX "")
|
||||
502
lib/libimhex/LICENSE
Normal file
502
lib/libimhex/LICENSE
Normal file
@@ -0,0 +1,502 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
@@ -151,7 +151,7 @@ namespace hex {
|
||||
if (m_icon.isValid())
|
||||
return m_icon;
|
||||
|
||||
m_icon = ImGuiExt::Texture(m_iconData.data(), m_iconData.size(), ImGuiExt::Texture::Filter::Linear);
|
||||
m_icon = ImGuiExt::Texture::fromImage(m_iconData.data(), m_iconData.size(), ImGuiExt::Texture::Filter::Linear);
|
||||
|
||||
return m_icon;
|
||||
}
|
||||
@@ -331,18 +331,7 @@ namespace hex {
|
||||
static Achievement& addAchievement(auto && ... args) {
|
||||
auto newAchievement = std::make_unique<T>(std::forward<decltype(args)>(args)...);
|
||||
|
||||
const auto &category = newAchievement->getUnlocalizedCategory();
|
||||
const auto &name = newAchievement->getUnlocalizedName();
|
||||
|
||||
auto [categoryIter, categoryInserted] = getAchievements().insert({ category, std::unordered_map<std::string, std::unique_ptr<Achievement>>{} });
|
||||
auto &[categoryKey, achievements] = *categoryIter;
|
||||
|
||||
auto [achievementIter, achievementInserted] = achievements.insert({ name, std::move(newAchievement) });
|
||||
auto &[achievementKey, achievement] = *achievementIter;
|
||||
|
||||
achievementAdded();
|
||||
|
||||
return *achievement;
|
||||
return addAchievementImpl(std::move(newAchievement));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -371,7 +360,7 @@ namespace hex {
|
||||
* @brief Returns all registered achievements
|
||||
* @return All achievements
|
||||
*/
|
||||
static std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>>& getAchievements();
|
||||
static const std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>>& getAchievements();
|
||||
|
||||
/**
|
||||
* @brief Returns all achievement start nodes
|
||||
@@ -379,14 +368,14 @@ namespace hex {
|
||||
* @param rebuild Whether to rebuild the list of start nodes
|
||||
* @return All achievement start nodes
|
||||
*/
|
||||
static std::unordered_map<std::string, std::vector<AchievementNode*>>& getAchievementStartNodes(bool rebuild = true);
|
||||
static const std::unordered_map<std::string, std::vector<AchievementNode*>>& getAchievementStartNodes(bool rebuild = true);
|
||||
|
||||
/**
|
||||
* @brief Returns all achievement nodes
|
||||
* @param rebuild Whether to rebuild the list of nodes
|
||||
* @return All achievement nodes
|
||||
*/
|
||||
static std::unordered_map<std::string, std::list<AchievementNode>>& getAchievementNodes(bool rebuild = true);
|
||||
static const std::unordered_map<std::string, std::list<AchievementNode>>& getAchievementNodes(bool rebuild = true);
|
||||
|
||||
/**
|
||||
* @brief Loads the progress of all achievements from the achievements save file
|
||||
@@ -398,11 +387,6 @@ namespace hex {
|
||||
*/
|
||||
static void storeProgress();
|
||||
|
||||
/**
|
||||
* @brief Removes all registered achievements from the tree
|
||||
*/
|
||||
static void clear();
|
||||
|
||||
/**
|
||||
* @brief Removes all temporary achievements from the tree
|
||||
*/
|
||||
@@ -416,6 +400,8 @@ namespace hex {
|
||||
|
||||
private:
|
||||
static void achievementAdded();
|
||||
|
||||
static Achievement& addAchievementImpl(std::unique_ptr<Achievement> &&newAchievement);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -18,11 +19,13 @@
|
||||
using ImGuiDataType = int;
|
||||
using ImGuiInputTextFlags = int;
|
||||
struct ImColor;
|
||||
enum ImGuiCustomCol : int;
|
||||
|
||||
namespace hex {
|
||||
|
||||
class View;
|
||||
class Shortcut;
|
||||
class Task;
|
||||
|
||||
namespace dp {
|
||||
class Node;
|
||||
@@ -96,7 +99,7 @@ namespace hex {
|
||||
bool m_requiresRestart = false;
|
||||
std::function<bool()> m_enabledCallback;
|
||||
std::function<void(Widget&)> m_changedCallback;
|
||||
std::optional<std::string> m_tooltip;
|
||||
std::optional<UnlocalizedString> m_tooltip;
|
||||
};
|
||||
|
||||
[[nodiscard]]
|
||||
@@ -110,7 +113,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
const std::optional<std::string>& getTooltip() const {
|
||||
const std::optional<UnlocalizedString>& getTooltip() const {
|
||||
return m_interface.m_tooltip;
|
||||
}
|
||||
|
||||
@@ -139,7 +142,7 @@ namespace hex {
|
||||
|
||||
[[nodiscard]] bool isChecked() const { return m_value; }
|
||||
|
||||
private:
|
||||
protected:
|
||||
bool m_value;
|
||||
};
|
||||
|
||||
@@ -153,7 +156,7 @@ namespace hex {
|
||||
|
||||
[[nodiscard]] i32 getValue() const { return m_value; }
|
||||
|
||||
private:
|
||||
protected:
|
||||
int m_value;
|
||||
i32 m_min, m_max;
|
||||
};
|
||||
@@ -168,11 +171,26 @@ namespace hex {
|
||||
|
||||
[[nodiscard]] float getValue() const { return m_value; }
|
||||
|
||||
private:
|
||||
protected:
|
||||
float m_value;
|
||||
float m_min, m_max;
|
||||
};
|
||||
|
||||
class SliderDataSize : public Widget {
|
||||
public:
|
||||
SliderDataSize(u64 defaultValue, u64 min, u64 max) : m_value(defaultValue), m_min(min), m_max(max) { }
|
||||
bool draw(const std::string &name) override;
|
||||
|
||||
void load(const nlohmann::json &data) override;
|
||||
nlohmann::json store() override;
|
||||
|
||||
[[nodiscard]] i32 getValue() const { return m_value; }
|
||||
|
||||
protected:
|
||||
u64 m_value;
|
||||
u64 m_min, m_max;
|
||||
};
|
||||
|
||||
class ColorPicker : public Widget {
|
||||
public:
|
||||
explicit ColorPicker(ImColor defaultColor);
|
||||
@@ -184,7 +202,7 @@ namespace hex {
|
||||
|
||||
[[nodiscard]] ImColor getColor() const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::array<float, 4> m_value{};
|
||||
};
|
||||
|
||||
@@ -200,7 +218,7 @@ namespace hex {
|
||||
[[nodiscard]]
|
||||
const nlohmann::json& getValue() const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::vector<std::string> m_items;
|
||||
std::vector<nlohmann::json> m_settingsValues;
|
||||
nlohmann::json m_defaultItem;
|
||||
@@ -220,7 +238,7 @@ namespace hex {
|
||||
[[nodiscard]]
|
||||
const std::string& getValue() const { return m_value; }
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::string m_value;
|
||||
};
|
||||
|
||||
@@ -231,12 +249,12 @@ namespace hex {
|
||||
void load(const nlohmann::json &data) override;
|
||||
nlohmann::json store() override;
|
||||
|
||||
[[nodiscard]] std::fs::path getPath() const {
|
||||
return m_value;
|
||||
[[nodiscard]] const std::fs::path& getPath() const {
|
||||
return m_path;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_value;
|
||||
protected:
|
||||
std::fs::path m_path;
|
||||
};
|
||||
|
||||
class Label : public Widget {
|
||||
@@ -271,11 +289,15 @@ namespace hex {
|
||||
void store();
|
||||
void clear();
|
||||
|
||||
std::vector<Category> &getSettings();
|
||||
const std::vector<Category>& getSettings();
|
||||
nlohmann::json& getSetting(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue);
|
||||
nlohmann::json &getSettingsData();
|
||||
const nlohmann::json& getSettingsData();
|
||||
|
||||
Widgets::Widget* add(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedSubCategory, const UnlocalizedString &unlocalizedName, std::unique_ptr<Widgets::Widget> &&widget);
|
||||
|
||||
void printSettingReadError(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json::exception &e);
|
||||
|
||||
void runOnChangeHandlers(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &value);
|
||||
}
|
||||
|
||||
template<std::derived_from<Widgets::Widget> T>
|
||||
@@ -290,8 +312,57 @@ namespace hex {
|
||||
|
||||
void setCategoryDescription(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedDescription);
|
||||
|
||||
[[nodiscard]] nlohmann::json read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &defaultValue);
|
||||
void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const nlohmann::json &value);
|
||||
class SettingsValue {
|
||||
public:
|
||||
SettingsValue(nlohmann::json value) : m_value(std::move(value)) {}
|
||||
|
||||
template<typename T>
|
||||
T get(std::common_type_t<T> defaultValue) const {
|
||||
try {
|
||||
auto result = m_value;
|
||||
if (result.is_number() && std::same_as<T, bool>)
|
||||
result = m_value.get<int>() != 0;
|
||||
if (m_value.is_null())
|
||||
result = defaultValue;
|
||||
|
||||
return result.get<T>();
|
||||
} catch (const nlohmann::json::exception &e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
private:
|
||||
nlohmann::json m_value;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] T read(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const std::common_type_t<T> &defaultValue) {
|
||||
auto setting = impl::getSetting(unlocalizedCategory, unlocalizedName, defaultValue);
|
||||
|
||||
try {
|
||||
if (setting.is_number() && std::same_as<T, bool>)
|
||||
setting = setting.template get<int>() != 0;
|
||||
if (setting.is_null())
|
||||
setting = defaultValue;
|
||||
|
||||
return setting.template get<T>();
|
||||
} catch (const nlohmann::json::exception &e) {
|
||||
impl::printSettingReadError(unlocalizedCategory, unlocalizedName, e);
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const std::common_type_t<T> &value) {
|
||||
impl::getSetting(unlocalizedCategory, unlocalizedName, value) = value;
|
||||
impl::runOnChangeHandlers(unlocalizedCategory, unlocalizedName, value);
|
||||
|
||||
impl::store();
|
||||
}
|
||||
|
||||
using OnChangeCallback = std::function<void(const SettingsValue &)>;
|
||||
u64 onChange(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName, const OnChangeCallback &callback);
|
||||
|
||||
}
|
||||
|
||||
/* Command Palette Command Registry. Allows adding of new commands to the command palette */
|
||||
@@ -328,8 +399,8 @@ namespace hex {
|
||||
DisplayCallback displayCallback;
|
||||
};
|
||||
|
||||
std::vector<Entry> &getEntries();
|
||||
std::vector<Handler> &getHandlers();
|
||||
const std::vector<Entry>& getEntries();
|
||||
const std::vector<Handler>& getHandlers();
|
||||
|
||||
}
|
||||
|
||||
@@ -384,10 +455,10 @@ namespace hex {
|
||||
VisualizerFunctionCallback callback;
|
||||
};
|
||||
|
||||
std::map<std::string, Visualizer> &getVisualizers();
|
||||
std::map<std::string, Visualizer> &getInlineVisualizers();
|
||||
std::map<std::string, pl::api::PragmaHandler> &getPragmas();
|
||||
std::vector<FunctionDefinition> &getFunctions();
|
||||
const std::map<std::string, Visualizer>& getVisualizers();
|
||||
const std::map<std::string, Visualizer>& getInlineVisualizers();
|
||||
const std::map<std::string, pl::api::PragmaHandler>& getPragmas();
|
||||
const std::vector<FunctionDefinition>& getFunctions();
|
||||
|
||||
}
|
||||
|
||||
@@ -424,7 +495,12 @@ namespace hex {
|
||||
* @param parameterCount The amount of parameters the function takes
|
||||
* @param func The function callback
|
||||
*/
|
||||
void addFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func);
|
||||
void addFunction(
|
||||
const pl::api::Namespace &ns,
|
||||
const std::string &name,
|
||||
pl::api::FunctionParameterCount parameterCount,
|
||||
const pl::api::FunctionCallback &func
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Adds a new dangerous function to the pattern language
|
||||
@@ -434,7 +510,12 @@ namespace hex {
|
||||
* @param parameterCount The amount of parameters the function takes
|
||||
* @param func The function callback
|
||||
*/
|
||||
void addDangerousFunction(const pl::api::Namespace &ns, const std::string &name, pl::api::FunctionParameterCount parameterCount, const pl::api::FunctionCallback &func);
|
||||
void addDangerousFunction(
|
||||
const pl::api::Namespace &ns,
|
||||
const std::string &name,
|
||||
pl::api::FunctionParameterCount parameterCount,
|
||||
const pl::api::FunctionCallback &func
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Adds a new visualizer to the pattern language
|
||||
@@ -443,7 +524,11 @@ namespace hex {
|
||||
* @param function The function callback
|
||||
* @param parameterCount The amount of parameters the function takes
|
||||
*/
|
||||
void addVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount);
|
||||
void addVisualizer(
|
||||
const std::string &name,
|
||||
const impl::VisualizerFunctionCallback &function,
|
||||
pl::api::FunctionParameterCount parameterCount
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Adds a new inline visualizer to the pattern language
|
||||
@@ -452,7 +537,11 @@ namespace hex {
|
||||
* @param function The function callback
|
||||
* @param parameterCount The amount of parameters the function takes
|
||||
*/
|
||||
void addInlineVisualizer(const std::string &name, const impl::VisualizerFunctionCallback &function, pl::api::FunctionParameterCount parameterCount);
|
||||
void addInlineVisualizer(
|
||||
const std::string &name,
|
||||
const impl::VisualizerFunctionCallback &function,
|
||||
pl::api::FunctionParameterCount parameterCount
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@@ -462,7 +551,7 @@ namespace hex {
|
||||
namespace impl {
|
||||
|
||||
void add(std::unique_ptr<View> &&view);
|
||||
std::map<std::string, std::unique_ptr<View>> &getEntries();
|
||||
const std::map<std::string, std::unique_ptr<View>>& getEntries();
|
||||
|
||||
}
|
||||
|
||||
@@ -493,12 +582,11 @@ namespace hex {
|
||||
using Callback = std::function<void()>;
|
||||
|
||||
struct Entry {
|
||||
std::string name;
|
||||
UnlocalizedString unlocalizedName;
|
||||
Callback function;
|
||||
bool detached;
|
||||
};
|
||||
|
||||
std::vector<Entry> &getEntries();
|
||||
const std::vector<Entry>& getEntries();
|
||||
|
||||
}
|
||||
|
||||
@@ -534,7 +622,7 @@ namespace hex {
|
||||
std::optional<EditingFunction> editingFunction;
|
||||
};
|
||||
|
||||
std::vector<Entry> &getEntries();
|
||||
const std::vector<Entry>& getEntries();
|
||||
|
||||
}
|
||||
|
||||
@@ -545,7 +633,12 @@ namespace hex {
|
||||
* @param displayGeneratorFunction The function that will be called to generate the display function
|
||||
* @param editingFunction The function that will be called to edit the data
|
||||
*/
|
||||
void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction = std::nullopt);
|
||||
void add(
|
||||
const UnlocalizedString &unlocalizedName,
|
||||
size_t requiredSize,
|
||||
impl::GeneratorFunction displayGeneratorFunction,
|
||||
std::optional<impl::EditingFunction> editingFunction = std::nullopt
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Adds a new entry to the data inspector
|
||||
@@ -555,7 +648,14 @@ namespace hex {
|
||||
* @param displayGeneratorFunction The function that will be called to generate the display function
|
||||
* @param editingFunction The function that will be called to edit the data
|
||||
*/
|
||||
void add(const UnlocalizedString &unlocalizedName, size_t requiredSize, size_t maxSize, impl::GeneratorFunction displayGeneratorFunction, std::optional<impl::EditingFunction> editingFunction = std::nullopt);
|
||||
void add(
|
||||
const UnlocalizedString &unlocalizedName,
|
||||
size_t requiredSize,
|
||||
size_t maxSize,
|
||||
impl::GeneratorFunction displayGeneratorFunction,
|
||||
std::optional<impl::EditingFunction> editingFunction = std::nullopt
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/* Data Processor Node Registry. Allows adding new processor nodes to be used in the data processor */
|
||||
@@ -573,7 +673,7 @@ namespace hex {
|
||||
|
||||
void add(const Entry &entry);
|
||||
|
||||
std::vector<Entry> &getEntries();
|
||||
const std::vector<Entry>& getEntries();
|
||||
}
|
||||
|
||||
|
||||
@@ -616,8 +716,8 @@ namespace hex {
|
||||
|
||||
namespace impl {
|
||||
|
||||
std::map<std::string, std::string> &getLanguages();
|
||||
std::map<std::string, std::vector<LocalizationManager::LanguageDefinition>> &getLanguageDefinitions();
|
||||
const std::map<std::string, std::string>& getLanguages();
|
||||
const std::map<std::string, std::vector<LocalizationManager::LanguageDefinition>>& getLanguageDefinitions();
|
||||
|
||||
}
|
||||
|
||||
@@ -626,11 +726,19 @@ namespace hex {
|
||||
/* Interface Registry. Allows adding new items to various interfaces */
|
||||
namespace Interface {
|
||||
|
||||
struct Icon {
|
||||
Icon(const char *glyph, ImGuiCustomCol color = ImGuiCustomCol(0x00)) : glyph(glyph), color(color) {}
|
||||
|
||||
std::string glyph;
|
||||
ImGuiCustomCol color;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
using DrawCallback = std::function<void()>;
|
||||
using MenuCallback = std::function<void()>;
|
||||
using EnabledCallback = std::function<bool()>;
|
||||
using SelectedCallback = std::function<bool()>;
|
||||
using ClickCallback = std::function<void()>;
|
||||
|
||||
struct MainMenuItem {
|
||||
@@ -639,10 +747,13 @@ namespace hex {
|
||||
|
||||
struct MenuItem {
|
||||
std::vector<UnlocalizedString> unlocalizedNames;
|
||||
Icon icon;
|
||||
std::unique_ptr<Shortcut> shortcut;
|
||||
View *view;
|
||||
MenuCallback callback;
|
||||
EnabledCallback enabledCallback;
|
||||
SelectedCallback selectedCallback;
|
||||
i32 toolbarIndex;
|
||||
};
|
||||
|
||||
struct SidebarItem {
|
||||
@@ -660,14 +771,16 @@ namespace hex {
|
||||
constexpr static auto SeparatorValue = "$SEPARATOR$";
|
||||
constexpr static auto SubMenuValue = "$SUBMENU$";
|
||||
|
||||
std::multimap<u32, MainMenuItem> &getMainMenuItems();
|
||||
std::multimap<u32, MenuItem> &getMenuItems();
|
||||
const std::multimap<u32, MainMenuItem>& getMainMenuItems();
|
||||
|
||||
std::vector<DrawCallback> &getWelcomeScreenEntries();
|
||||
std::vector<DrawCallback> &getFooterItems();
|
||||
std::vector<DrawCallback> &getToolbarItems();
|
||||
std::vector<SidebarItem> &getSidebarItems();
|
||||
std::vector<TitleBarButton> &getTitleBarButtons();
|
||||
const std::multimap<u32, MenuItem>& getMenuItems();
|
||||
std::multimap<u32, MenuItem>& getMenuItemsMutable();
|
||||
|
||||
const std::vector<DrawCallback>& getWelcomeScreenEntries();
|
||||
const std::vector<DrawCallback>& getFooterItems();
|
||||
const std::vector<DrawCallback>& getToolbarItems();
|
||||
const std::vector<SidebarItem>& getSidebarItems();
|
||||
const std::vector<TitleBarButton>& getTitlebarButtons();
|
||||
|
||||
}
|
||||
|
||||
@@ -681,13 +794,63 @@ namespace hex {
|
||||
/**
|
||||
* @brief Adds a new main menu entry
|
||||
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
|
||||
* @param icon The icon to use for the entry
|
||||
* @param priority The priority of the entry. Lower values are displayed first
|
||||
* @param shortcut The shortcut to use for the entry
|
||||
* @param function The function to call when the entry is clicked
|
||||
* @param enabledCallback The function to call to determine if the entry is enabled
|
||||
* @param view The view to use for the entry. If nullptr, the shortcut will work globally
|
||||
*/
|
||||
void addMenuItem(const std::vector<UnlocalizedString> &unlocalizedMainMenuNames, u32 priority, const Shortcut &shortcut, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; }, View *view = nullptr);
|
||||
void addMenuItem(
|
||||
const std::vector<UnlocalizedString> &unlocalizedMainMenuNames,
|
||||
const Icon &icon,
|
||||
u32 priority,
|
||||
const Shortcut &shortcut,
|
||||
const impl::MenuCallback &function,
|
||||
const impl::EnabledCallback& enabledCallback, View *view
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Adds a new main menu entry
|
||||
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
|
||||
* @param icon The icon to use for the entry
|
||||
* @param priority The priority of the entry. Lower values are displayed first
|
||||
* @param shortcut The shortcut to use for the entry
|
||||
* @param function The function to call when the entry is clicked
|
||||
* @param enabledCallback The function to call to determine if the entry is enabled
|
||||
* @param selectedCallback The function to call to determine if the entry is selected
|
||||
* @param view The view to use for the entry. If nullptr, the shortcut will work globally
|
||||
*/
|
||||
void addMenuItem(
|
||||
const std::vector<UnlocalizedString> &unlocalizedMainMenuNames,
|
||||
const Icon &icon,
|
||||
u32 priority,
|
||||
const Shortcut &shortcut,
|
||||
const impl::MenuCallback &function,
|
||||
const impl::EnabledCallback& enabledCallback = []{ return true; },
|
||||
const impl::SelectedCallback &selectedCallback = []{ return false; },
|
||||
View *view = nullptr
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Adds a new main menu entry
|
||||
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
|
||||
* @param priority The priority of the entry. Lower values are displayed first
|
||||
* @param shortcut The shortcut to use for the entry
|
||||
* @param function The function to call when the entry is clicked
|
||||
* @param enabledCallback The function to call to determine if the entry is enabled
|
||||
* @param selectedCallback The function to call to determine if the entry is selected
|
||||
* @param view The view to use for the entry. If nullptr, the shortcut will work globally
|
||||
*/
|
||||
void addMenuItem(
|
||||
const std::vector<UnlocalizedString> &unlocalizedMainMenuNames,
|
||||
u32 priority,
|
||||
const Shortcut &shortcut,
|
||||
const impl::MenuCallback &function,
|
||||
const impl::EnabledCallback& enabledCallback = []{ return true; },
|
||||
const impl::SelectedCallback &selectedCallback = []{ return false; },
|
||||
View *view = nullptr
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Adds a new main menu sub-menu entry
|
||||
@@ -696,7 +859,29 @@ namespace hex {
|
||||
* @param function The function to call when the entry is clicked
|
||||
* @param enabledCallback The function to call to determine if the entry is enabled
|
||||
*/
|
||||
void addMenuItemSubMenu(std::vector<UnlocalizedString> unlocalizedMainMenuNames, u32 priority, const impl::MenuCallback &function, const impl::EnabledCallback& enabledCallback = []{ return true; });
|
||||
void addMenuItemSubMenu(
|
||||
std::vector<UnlocalizedString> unlocalizedMainMenuNames,
|
||||
u32 priority,
|
||||
const impl::MenuCallback &function,
|
||||
const impl::EnabledCallback& enabledCallback = []{ return true; }
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Adds a new main menu sub-menu entry
|
||||
* @param unlocalizedMainMenuNames The unlocalized names of the main menu entries
|
||||
* @param icon The icon to use for the entry
|
||||
* @param priority The priority of the entry. Lower values are displayed first
|
||||
* @param function The function to call when the entry is clicked
|
||||
* @param enabledCallback The function to call to determine if the entry is enabled
|
||||
*/
|
||||
void addMenuItemSubMenu(
|
||||
std::vector<UnlocalizedString> unlocalizedMainMenuNames,
|
||||
const char *icon,
|
||||
u32 priority,
|
||||
const impl::MenuCallback &function,
|
||||
const impl::EnabledCallback& enabledCallback = []{ return true; }
|
||||
);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Adds a new main menu separator
|
||||
@@ -724,13 +909,24 @@ namespace hex {
|
||||
*/
|
||||
void addToolbarItem(const impl::DrawCallback &function);
|
||||
|
||||
/**
|
||||
* @brief Adds a menu item to the toolbar
|
||||
* @param unlocalizedName Unlocalized name of the menu item
|
||||
* @param color Color of the toolbar icon
|
||||
*/
|
||||
void addMenuItemToToolbar(const UnlocalizedString &unlocalizedName, ImGuiCustomCol color);
|
||||
|
||||
/**
|
||||
* @brief Adds a new sidebar item
|
||||
* @param icon The icon to use for the item
|
||||
* @param function The function to call to draw the item
|
||||
* @param enabledCallback The function
|
||||
*/
|
||||
void addSidebarItem(const std::string &icon, const impl::DrawCallback &function, const impl::EnabledCallback &enabledCallback = []{ return true; });
|
||||
void addSidebarItem(
|
||||
const std::string &icon,
|
||||
const impl::DrawCallback &function,
|
||||
const impl::EnabledCallback &enabledCallback = []{ return true; }
|
||||
);
|
||||
|
||||
/**
|
||||
* @brief Adds a new title bar button
|
||||
@@ -738,7 +934,11 @@ namespace hex {
|
||||
* @param unlocalizedTooltip The unlocalized tooltip to use for the button
|
||||
* @param function The function to call when the button is clicked
|
||||
*/
|
||||
void addTitleBarButton(const std::string &icon, const UnlocalizedString &unlocalizedTooltip, const impl::ClickCallback &function);
|
||||
void addTitleBarButton(
|
||||
const std::string &icon,
|
||||
const UnlocalizedString &unlocalizedTooltip,
|
||||
const impl::ClickCallback &function
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
@@ -749,10 +949,10 @@ namespace hex {
|
||||
|
||||
void addProviderName(const UnlocalizedString &unlocalizedName);
|
||||
|
||||
using ProviderCreationFunction = prv::Provider*(*)();
|
||||
using ProviderCreationFunction = std::function<std::unique_ptr<prv::Provider>()>;
|
||||
void add(const std::string &typeName, ProviderCreationFunction creationFunction);
|
||||
|
||||
std::vector<std::string> &getEntries();
|
||||
const std::vector<std::string>& getEntries();
|
||||
|
||||
}
|
||||
|
||||
@@ -765,8 +965,8 @@ namespace hex {
|
||||
void add(bool addToList = true) {
|
||||
auto typeName = T().getTypeName();
|
||||
|
||||
impl::add(typeName, [] -> prv::Provider* {
|
||||
return new T();
|
||||
impl::add(typeName, [] -> std::unique_ptr<prv::Provider> {
|
||||
return std::make_unique<T>();
|
||||
});
|
||||
|
||||
if (addToList)
|
||||
@@ -786,7 +986,7 @@ namespace hex {
|
||||
Callback callback;
|
||||
};
|
||||
|
||||
std::vector<Entry> &getEntries();
|
||||
const std::vector<Entry>& getEntries();
|
||||
|
||||
}
|
||||
|
||||
@@ -811,7 +1011,7 @@ namespace hex {
|
||||
Callback callback;
|
||||
};
|
||||
|
||||
std::vector<Entry> &getEntries();
|
||||
const std::vector<Entry>& getEntries();
|
||||
|
||||
}
|
||||
|
||||
@@ -856,11 +1056,19 @@ namespace hex {
|
||||
u16 m_maxCharsPerCell;
|
||||
};
|
||||
|
||||
struct MiniMapVisualizer {
|
||||
using Callback = std::function<ImColor(const std::vector<u8>&)>;
|
||||
|
||||
UnlocalizedString unlocalizedName;
|
||||
Callback callback;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
void addDataVisualizer(std::shared_ptr<DataVisualizer> &&visualizer);
|
||||
|
||||
std::vector<std::shared_ptr<DataVisualizer>> &getVisualizers();
|
||||
const std::vector<std::shared_ptr<DataVisualizer>>& getVisualizers();
|
||||
const std::vector<std::shared_ptr<MiniMapVisualizer>>& getMiniMapVisualizers();
|
||||
|
||||
}
|
||||
|
||||
@@ -881,6 +1089,63 @@ namespace hex {
|
||||
*/
|
||||
std::shared_ptr<DataVisualizer> getVisualizerByName(const UnlocalizedString &unlocalizedName);
|
||||
|
||||
/**
|
||||
* @brief Adds a new minimap visualizer
|
||||
* @param unlocalizedName Unlocalized name of the minimap visualizer
|
||||
* @param callback The callback that will be called to get the color of a line
|
||||
*/
|
||||
void addMiniMapVisualizer(UnlocalizedString unlocalizedName, MiniMapVisualizer::Callback callback);
|
||||
|
||||
}
|
||||
|
||||
/* Diffing Registry. Allows adding new diffing algorithms */
|
||||
namespace Diffing {
|
||||
|
||||
enum class DifferenceType : u8 {
|
||||
Match = 0,
|
||||
Insertion = 1,
|
||||
Deletion = 2,
|
||||
Mismatch = 3
|
||||
};
|
||||
|
||||
using DiffTree = wolv::container::IntervalTree<DifferenceType>;
|
||||
|
||||
class Algorithm {
|
||||
public:
|
||||
explicit Algorithm(UnlocalizedString unlocalizedName, UnlocalizedString unlocalizedDescription)
|
||||
: m_unlocalizedName(std::move(unlocalizedName)),
|
||||
m_unlocalizedDescription(std::move(unlocalizedDescription)) { }
|
||||
|
||||
virtual ~Algorithm() = default;
|
||||
|
||||
virtual std::vector<DiffTree> analyze(prv::Provider *providerA, prv::Provider *providerB) const = 0;
|
||||
virtual void drawSettings() { }
|
||||
|
||||
const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; }
|
||||
const UnlocalizedString& getUnlocalizedDescription() const { return m_unlocalizedDescription; }
|
||||
|
||||
private:
|
||||
UnlocalizedString m_unlocalizedName, m_unlocalizedDescription;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
const std::vector<std::unique_ptr<Algorithm>>& getAlgorithms();
|
||||
|
||||
void addAlgorithm(std::unique_ptr<Algorithm> &&hash);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a new hash
|
||||
* @tparam T The hash type that extends hex::Hash
|
||||
* @param args The arguments to pass to the constructor of the hash
|
||||
*/
|
||||
template<typename T, typename ... Args>
|
||||
void addAlgorithm(Args && ... args) {
|
||||
impl::addAlgorithm(std::make_unique<T>(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Hash Registry. Allows adding new hashes to the Hash view */
|
||||
@@ -902,7 +1167,7 @@ namespace hex {
|
||||
|
||||
[[nodiscard]] Hash *getType() { return m_type; }
|
||||
[[nodiscard]] const Hash *getType() const { return m_type; }
|
||||
[[nodiscard]] const std::string &getName() const { return m_name; }
|
||||
[[nodiscard]] const std::string& getName() const { return m_name; }
|
||||
|
||||
const std::vector<u8>& get(const Region& region, prv::Provider *provider) {
|
||||
if (m_cache.empty()) {
|
||||
@@ -930,7 +1195,7 @@ namespace hex {
|
||||
[[nodiscard]] virtual nlohmann::json store() const = 0;
|
||||
virtual void load(const nlohmann::json &json) = 0;
|
||||
|
||||
[[nodiscard]] const UnlocalizedString &getUnlocalizedName() const {
|
||||
[[nodiscard]] const UnlocalizedString& getUnlocalizedName() const {
|
||||
return m_unlocalizedName;
|
||||
}
|
||||
|
||||
@@ -945,9 +1210,10 @@ namespace hex {
|
||||
|
||||
namespace impl {
|
||||
|
||||
std::vector<std::unique_ptr<Hash>> &getHashes();
|
||||
const std::vector<std::unique_ptr<Hash>>& getHashes();
|
||||
|
||||
void add(std::unique_ptr<Hash> &&hash);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -981,7 +1247,7 @@ namespace hex {
|
||||
namespace impl {
|
||||
using NetworkCallback = std::function<nlohmann::json(const nlohmann::json &)>;
|
||||
|
||||
std::map<std::string, NetworkCallback> &getNetworkEndpoints();
|
||||
const std::map<std::string, NetworkCallback>& getNetworkEndpoints();
|
||||
}
|
||||
|
||||
void registerNetworkEndpoint(const std::string &endpoint, const impl::NetworkCallback &callback);
|
||||
@@ -998,15 +1264,21 @@ namespace hex {
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
std::map<std::string, Experiment> &getExperiments();
|
||||
const std::map<std::string, Experiment>& getExperiments();
|
||||
}
|
||||
|
||||
void addExperiment(const std::string &experimentName, const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription = "");
|
||||
void addExperiment(
|
||||
const std::string &experimentName,
|
||||
const UnlocalizedString &unlocalizedName,
|
||||
const UnlocalizedString &unlocalizedDescription = ""
|
||||
);
|
||||
|
||||
void enableExperiement(const std::string &experimentName, bool enabled);
|
||||
|
||||
[[nodiscard]] bool isExperimentEnabled(const std::string &experimentName);
|
||||
}
|
||||
|
||||
/* Reports Registry. Allows adding new sections to exported reports */
|
||||
namespace Reports {
|
||||
|
||||
namespace impl {
|
||||
@@ -1017,13 +1289,80 @@ namespace hex {
|
||||
Callback callback;
|
||||
};
|
||||
|
||||
std::vector<ReportGenerator>& getGenerators();
|
||||
const std::vector<ReportGenerator>& getGenerators();
|
||||
|
||||
}
|
||||
|
||||
void addReportProvider(impl::Callback callback);
|
||||
|
||||
}
|
||||
|
||||
namespace DataInformation {
|
||||
|
||||
class InformationSection {
|
||||
public:
|
||||
InformationSection(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription = "", bool hasSettings = false)
|
||||
: m_unlocalizedName(unlocalizedName), m_unlocalizedDescription(unlocalizedDescription),
|
||||
m_hasSettings(hasSettings) { }
|
||||
virtual ~InformationSection() = default;
|
||||
|
||||
[[nodiscard]] const UnlocalizedString& getUnlocalizedName() const { return m_unlocalizedName; }
|
||||
[[nodiscard]] const UnlocalizedString& getUnlocalizedDescription() const { return m_unlocalizedDescription; }
|
||||
|
||||
virtual void process(Task &task, prv::Provider *provider, Region region) = 0;
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual void drawSettings() { }
|
||||
virtual void drawContent() = 0;
|
||||
|
||||
[[nodiscard]] bool isValid() const { return m_valid; }
|
||||
void markValid(bool valid = true) { m_valid = valid; }
|
||||
|
||||
[[nodiscard]] bool isEnabled() const { return m_enabled; }
|
||||
void setEnabled(bool enabled) { m_enabled = enabled; }
|
||||
|
||||
[[nodiscard]] bool isAnalyzing() const { return m_analyzing; }
|
||||
void setAnalyzing(bool analyzing) { m_analyzing = analyzing; }
|
||||
|
||||
virtual void load(const nlohmann::json &data) {
|
||||
m_enabled = data.value<bool>("enabled", true);
|
||||
}
|
||||
[[nodiscard]] virtual nlohmann::json store() {
|
||||
nlohmann::json data;
|
||||
data["enabled"] = m_enabled.load();
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool hasSettings() const { return m_hasSettings; }
|
||||
|
||||
private:
|
||||
UnlocalizedString m_unlocalizedName, m_unlocalizedDescription;
|
||||
bool m_hasSettings;
|
||||
|
||||
std::atomic<bool> m_analyzing = false;
|
||||
std::atomic<bool> m_valid = false;
|
||||
std::atomic<bool> m_enabled = true;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
using CreateCallback = std::function<std::unique_ptr<InformationSection>()>;
|
||||
|
||||
const std::vector<CreateCallback>& getInformationSectionConstructors();
|
||||
void addInformationSectionCreator(const CreateCallback &callback);
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void addInformationSection(auto && ...args) {
|
||||
impl::addInformationSectionCreator([args...] {
|
||||
return std::make_unique<T>(std::forward<decltype(args)>(args)...);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <string_view>
|
||||
#include <functional>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <wolv/types/type_name.hpp>
|
||||
@@ -23,7 +24,7 @@
|
||||
static void subscribe(void *token, Event::Callback function) { EventManager::subscribe<event_name>(token, function); } \
|
||||
static void unsubscribe(const EventManager::EventList::iterator &token) noexcept { EventManager::unsubscribe(token); } \
|
||||
static void unsubscribe(void *token) noexcept { EventManager::unsubscribe<event_name>(token); } \
|
||||
static void post(auto &&...args) noexcept { EventManager::post<event_name>(std::forward<decltype(args)>(args)...); } \
|
||||
static void post(auto &&...args) { EventManager::post<event_name>(std::forward<decltype(args)>(args)...); } \
|
||||
};
|
||||
|
||||
#define EVENT_DEF(event_name, ...) EVENT_DEF_IMPL(event_name, #event_name, true, __VA_ARGS__)
|
||||
@@ -37,6 +38,7 @@ namespace hex {
|
||||
class View;
|
||||
}
|
||||
|
||||
namespace pl::ptrn { class Pattern; }
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -46,7 +48,7 @@ namespace hex {
|
||||
public:
|
||||
explicit constexpr EventId(const char *eventName) {
|
||||
m_hash = 0x811C'9DC5;
|
||||
for (auto c : std::string_view(eventName)) {
|
||||
for (const char c : std::string_view(eventName)) {
|
||||
m_hash = (m_hash >> 5) | (m_hash << 27);
|
||||
m_hash ^= c;
|
||||
}
|
||||
@@ -62,6 +64,7 @@ namespace hex {
|
||||
|
||||
struct EventBase {
|
||||
EventBase() noexcept = default;
|
||||
virtual ~EventBase() = default;
|
||||
};
|
||||
|
||||
template<typename... Params>
|
||||
@@ -70,8 +73,13 @@ namespace hex {
|
||||
|
||||
explicit Event(Callback func) noexcept : m_func(std::move(func)) { }
|
||||
|
||||
void operator()(Params... params) const noexcept {
|
||||
m_func(params...);
|
||||
void operator()(std::string_view eventName, Params... params) const {
|
||||
try {
|
||||
m_func(params...);
|
||||
} catch (const std::exception &e) {
|
||||
log::error("An exception occurred while handling event {}: {}", eventName, e.what());
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -118,7 +126,7 @@ namespace hex {
|
||||
|
||||
if (getTokenStore().contains(token)) {
|
||||
auto&& [begin, end] = getTokenStore().equal_range(token);
|
||||
auto eventRegistered = std::any_of(begin, end, [&](auto &item) {
|
||||
const auto eventRegistered = std::any_of(begin, end, [&](auto &item) {
|
||||
return item.second->first == E::Id;
|
||||
});
|
||||
if (eventRegistered) {
|
||||
@@ -167,16 +175,12 @@ namespace hex {
|
||||
* @param args Arguments to pass to the event
|
||||
*/
|
||||
template<impl::EventType E>
|
||||
static void post(auto &&...args) noexcept {
|
||||
static void post(auto && ...args) {
|
||||
std::scoped_lock lock(getEventMutex());
|
||||
|
||||
for (const auto &[id, event] : getEvents()) {
|
||||
if (id == E::Id) {
|
||||
try {
|
||||
(*static_cast<E *const>(event.get()))(std::forward<decltype(args)>(args)...);
|
||||
} catch (const std::exception &e) {
|
||||
log::error("Event '{}' threw {}: {}", wolv::type::getTypeName<decltype(e)>(), wolv::type::getTypeName<E>(), e.what());
|
||||
}
|
||||
(*static_cast<E *const>(event.get()))(wolv::type::getTypeName<E>(), std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,14 +214,14 @@ namespace hex {
|
||||
EVENT_DEF(EventImHexStartupFinished);
|
||||
|
||||
EVENT_DEF(EventFileLoaded, std::fs::path);
|
||||
EVENT_DEF(EventDataChanged);
|
||||
EVENT_DEF(EventDataChanged, prv::Provider *);
|
||||
EVENT_DEF(EventHighlightingChanged);
|
||||
EVENT_DEF(EventWindowClosing, GLFWwindow *);
|
||||
EVENT_DEF(EventRegionSelected, ImHexApi::HexEditor::ProviderRegion);
|
||||
EVENT_DEF(EventSettingsChanged);
|
||||
EVENT_DEF(EventAbnormalTermination, int);
|
||||
EVENT_DEF(EventThemeChanged);
|
||||
EVENT_DEF(EventOSThemeChanged);
|
||||
EVENT_DEF(EventWindowFocused, bool);
|
||||
|
||||
/**
|
||||
* @brief Called when the provider is created.
|
||||
@@ -241,6 +245,7 @@ namespace hex {
|
||||
EVENT_DEF(EventProviderDeleted, prv::Provider *);
|
||||
EVENT_DEF(EventProviderSaved, prv::Provider *);
|
||||
EVENT_DEF(EventWindowInitialized);
|
||||
EVENT_DEF(EventWindowDeinitializing, GLFWwindow *);
|
||||
EVENT_DEF(EventBookmarkCreated, ImHexApi::Bookmarks::Entry&);
|
||||
EVENT_DEF(EventPatchCreated, u64, u8, u8);
|
||||
EVENT_DEF(EventPatternEvaluating);
|
||||
@@ -252,6 +257,10 @@ namespace hex {
|
||||
EVENT_DEF(EventAchievementUnlocked, const Achievement&);
|
||||
EVENT_DEF(EventSearchBoxClicked, u32);
|
||||
EVENT_DEF(EventViewOpened, View*);
|
||||
EVENT_DEF(EventFirstLaunch);
|
||||
|
||||
EVENT_DEF(EventFileDragged, bool);
|
||||
EVENT_DEF(EventFileDropped, std::fs::path);
|
||||
|
||||
EVENT_DEF(EventProviderDataModified, prv::Provider *, u64, u64, const u8*);
|
||||
EVENT_DEF(EventProviderDataInserted, prv::Provider *, u64, u64);
|
||||
@@ -269,7 +278,9 @@ namespace hex {
|
||||
EVENT_DEF(RequestAddInitTask, std::string, bool, std::function<bool()>);
|
||||
EVENT_DEF(RequestAddExitTask, std::string, std::function<bool()>);
|
||||
EVENT_DEF(RequestOpenWindow, std::string);
|
||||
EVENT_DEF(RequestSelectionChange, Region);
|
||||
EVENT_DEF(RequestHexEditorSelectionChange, Region);
|
||||
EVENT_DEF(RequestPatternEditorSelectionChange, u32, u32);
|
||||
EVENT_DEF(RequestJumpToPattern, const pl::ptrn::Pattern*);
|
||||
EVENT_DEF(RequestAddBookmark, Region, std::string, std::string, color_t, u64*);
|
||||
EVENT_DEF(RequestRemoveBookmark, u64);
|
||||
EVENT_DEF(RequestSetPatternLanguageCode, std::string);
|
||||
@@ -282,6 +293,7 @@ namespace hex {
|
||||
EVENT_DEF(RequestOpenFile, std::fs::path);
|
||||
EVENT_DEF(RequestChangeTheme, std::string);
|
||||
EVENT_DEF(RequestOpenPopup, std::string);
|
||||
EVENT_DEF(RequestAddVirtualFile, std::fs::path, std::vector<u8>, Region);
|
||||
|
||||
/**
|
||||
* @brief Creates a provider from it's unlocalized name, and add it to the provider list
|
||||
@@ -299,4 +311,9 @@ namespace hex {
|
||||
* The 'from' provider should not have any per provider data after this, and should be immediately deleted
|
||||
*/
|
||||
EVENT_DEF(MovePerProviderData, prv::Provider *, prv::Provider *);
|
||||
|
||||
/**
|
||||
* Called when ImHex managed to catch an error in a general try/catch to prevent/recover from a crash
|
||||
*/
|
||||
EVENT_DEF(EventCrashRecovered, const std::exception &);
|
||||
}
|
||||
@@ -3,11 +3,13 @@
|
||||
#include <hex.hpp>
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
#include <wolv/io/fs.hpp>
|
||||
|
||||
@@ -15,9 +17,14 @@ using ImGuiID = unsigned int;
|
||||
struct ImVec2;
|
||||
struct ImFontAtlas;
|
||||
struct ImFont;
|
||||
struct GLFWwindow;
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace impl {
|
||||
class AutoResetBase;
|
||||
}
|
||||
|
||||
namespace prv {
|
||||
class Provider;
|
||||
}
|
||||
@@ -34,8 +41,8 @@ namespace hex {
|
||||
Highlighting() = default;
|
||||
Highlighting(Region region, color_t color);
|
||||
|
||||
[[nodiscard]] const Region &getRegion() const { return m_region; }
|
||||
[[nodiscard]] const color_t &getColor() const { return m_color; }
|
||||
[[nodiscard]] const Region& getRegion() const { return m_region; }
|
||||
[[nodiscard]] const color_t& getColor() const { return m_color; }
|
||||
|
||||
private:
|
||||
Region m_region = {};
|
||||
@@ -47,9 +54,9 @@ namespace hex {
|
||||
Tooltip() = default;
|
||||
Tooltip(Region region, std::string value, color_t color);
|
||||
|
||||
[[nodiscard]] const Region &getRegion() const { return m_region; }
|
||||
[[nodiscard]] const color_t &getColor() const { return m_color; }
|
||||
[[nodiscard]] const std::string &getValue() const { return m_value; }
|
||||
[[nodiscard]] const Region& getRegion() const { return m_region; }
|
||||
[[nodiscard]] const color_t& getColor() const { return m_color; }
|
||||
[[nodiscard]] const std::string& getValue() const { return m_value; }
|
||||
|
||||
private:
|
||||
Region m_region = {};
|
||||
@@ -68,15 +75,18 @@ namespace hex {
|
||||
namespace impl {
|
||||
|
||||
using HighlightingFunction = std::function<std::optional<color_t>(u64, const u8*, size_t, bool)>;
|
||||
using HoveringFunction = std::function<bool(const prv::Provider *, u64, const u8*, size_t)>;
|
||||
|
||||
std::map<u32, Highlighting> &getBackgroundHighlights();
|
||||
std::map<u32, HighlightingFunction> &getBackgroundHighlightingFunctions();
|
||||
std::map<u32, Highlighting> &getForegroundHighlights();
|
||||
std::map<u32, HighlightingFunction> &getForegroundHighlightingFunctions();
|
||||
std::map<u32, Tooltip> &getTooltips();
|
||||
std::map<u32, TooltipFunction> &getTooltipFunctions();
|
||||
const std::map<u32, Highlighting>& getBackgroundHighlights();
|
||||
const std::map<u32, HighlightingFunction>& getBackgroundHighlightingFunctions();
|
||||
const std::map<u32, Highlighting>& getForegroundHighlights();
|
||||
const std::map<u32, HighlightingFunction>& getForegroundHighlightingFunctions();
|
||||
const std::map<u32, HoveringFunction>& getHoveringFunctions();
|
||||
const std::map<u32, Tooltip>& getTooltips();
|
||||
const std::map<u32, TooltipFunction>& getTooltipFunctions();
|
||||
|
||||
void setCurrentSelection(const std::optional<ProviderRegion> ®ion);
|
||||
void setHoveredRegion(const prv::Provider *provider, const Region ®ion);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,6 +175,19 @@ namespace hex {
|
||||
*/
|
||||
void removeForegroundHighlightingProvider(u32 id);
|
||||
|
||||
/**
|
||||
* @brief Adds a hovering provider to the Hex Editor using a callback function
|
||||
* @param function Function that draws the highlighting based on the hovered region
|
||||
* @return Unique ID used to remove the highlighting again later
|
||||
*/
|
||||
u32 addHoverHighlightProvider(const impl::HoveringFunction &function);
|
||||
|
||||
/**
|
||||
* @brief Removes a hovering color highlighting from the Hex Editor
|
||||
* @param id The ID of the highlighting to remove
|
||||
*/
|
||||
void removeHoverHighlightProvider(u32 id);
|
||||
|
||||
/**
|
||||
* @brief Checks if there's a valid selection in the Hex Editor right now
|
||||
*/
|
||||
@@ -202,6 +225,20 @@ namespace hex {
|
||||
*/
|
||||
void setSelection(u64 address, size_t size, prv::Provider *provider = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Adds a virtual file to the list in the Hex Editor
|
||||
* @param path The path of the file
|
||||
* @param data The data of the file
|
||||
* @param region The location of the file in the Hex Editor if available
|
||||
*/
|
||||
void addVirtualFile(const std::fs::path &path, std::vector<u8> data, Region region = Region::Invalid());
|
||||
|
||||
/**
|
||||
* @brief Gets the currently hovered cell region in the Hex Editor
|
||||
* @return
|
||||
*/
|
||||
const std::optional<Region>& getHoveredRegion(const prv::Provider *provider);
|
||||
|
||||
}
|
||||
|
||||
/* Functions to interact with Bookmarks */
|
||||
@@ -256,7 +293,7 @@ namespace hex {
|
||||
namespace impl {
|
||||
|
||||
void resetClosingProvider();
|
||||
const std::vector<prv::Provider*>& getClosingProviders();
|
||||
const std::set<prv::Provider*>& getClosingProviders();
|
||||
|
||||
}
|
||||
|
||||
@@ -270,13 +307,19 @@ namespace hex {
|
||||
* @brief Gets a list of all currently loaded data providers
|
||||
* @return The currently loaded data providers
|
||||
*/
|
||||
const std::vector<prv::Provider *> &getProviders();
|
||||
std::vector<prv::Provider*> getProviders();
|
||||
|
||||
/**
|
||||
* @brief Sets the currently selected data provider
|
||||
* @param index Index of the provider to select
|
||||
*/
|
||||
void setCurrentProvider(u32 index);
|
||||
void setCurrentProvider(i64 index);
|
||||
|
||||
/**
|
||||
* @brief Sets the currently selected data provider
|
||||
* @param provider The provider to select
|
||||
*/
|
||||
void setCurrentProvider(NonNull<prv::Provider*> provider);
|
||||
|
||||
/**
|
||||
* @brief Gets the index of the currently selected data provider
|
||||
@@ -314,7 +357,7 @@ namespace hex {
|
||||
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
|
||||
* @param select Whether to select the provider after adding it
|
||||
*/
|
||||
void add(prv::Provider *provider, bool skipLoadInterface = false, bool select = true);
|
||||
void add(std::unique_ptr<prv::Provider> &&provider, bool skipLoadInterface = false, bool select = true);
|
||||
|
||||
/**
|
||||
* @brief Creates a new provider and adds it to the list of providers
|
||||
@@ -323,7 +366,7 @@ namespace hex {
|
||||
*/
|
||||
template<std::derived_from<prv::Provider> T>
|
||||
void add(auto &&...args) {
|
||||
add(new T(std::forward<decltype(args)>(args)...));
|
||||
add(std::make_unique<T>(std::forward<decltype(args)>(args)...));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,42 +382,29 @@ namespace hex {
|
||||
* @param skipLoadInterface Whether to skip the provider's loading interface (see property documentation)
|
||||
* @param select Whether to select the provider after adding it
|
||||
*/
|
||||
prv::Provider* createProvider(const UnlocalizedString &unlocalizedName, bool skipLoadInterface = false, bool select = true);
|
||||
prv::Provider* createProvider(
|
||||
const UnlocalizedString &unlocalizedName,
|
||||
bool skipLoadInterface = false,
|
||||
bool select = true
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/* Functions to interact with various ImHex system settings */
|
||||
namespace System {
|
||||
|
||||
bool isMainInstance();
|
||||
|
||||
namespace impl {
|
||||
void setMainInstanceStatus(bool status);
|
||||
|
||||
void setMainWindowPosition(i32 x, i32 y);
|
||||
void setMainWindowSize(u32 width, u32 height);
|
||||
void setMainDockSpaceId(ImGuiID id);
|
||||
|
||||
void setGlobalScale(float scale);
|
||||
void setNativeScale(float scale);
|
||||
|
||||
void setBorderlessWindowMode(bool enabled);
|
||||
|
||||
void setGPUVendor(const std::string &vendor);
|
||||
|
||||
void setPortableVersion(bool enabled);
|
||||
|
||||
void addInitArgument(const std::string &key, const std::string &value = { });
|
||||
|
||||
void setLastFrameTime(double time);
|
||||
}
|
||||
|
||||
struct ProgramArguments {
|
||||
int argc;
|
||||
char **argv;
|
||||
char **envp;
|
||||
};
|
||||
|
||||
struct InitialWindowProperties {
|
||||
i32 x, y;
|
||||
u32 width, height;
|
||||
bool maximized;
|
||||
};
|
||||
|
||||
enum class TaskProgressState {
|
||||
Reset,
|
||||
Progress,
|
||||
@@ -387,6 +417,35 @@ namespace hex {
|
||||
Error
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
void setMainInstanceStatus(bool status);
|
||||
|
||||
void setMainWindowPosition(i32 x, i32 y);
|
||||
void setMainWindowSize(u32 width, u32 height);
|
||||
void setMainDockSpaceId(ImGuiID id);
|
||||
void setMainWindowHandle(GLFWwindow *window);
|
||||
|
||||
void setGlobalScale(float scale);
|
||||
void setNativeScale(float scale);
|
||||
|
||||
void setBorderlessWindowMode(bool enabled);
|
||||
void setMultiWindowMode(bool enabled);
|
||||
void setInitialWindowProperties(InitialWindowProperties properties);
|
||||
|
||||
void setGPUVendor(const std::string &vendor);
|
||||
|
||||
void addInitArgument(const std::string &key, const std::string &value = { });
|
||||
|
||||
void setLastFrameTime(double time);
|
||||
|
||||
bool isWindowResizable();
|
||||
|
||||
void addAutoResetObject(hex::impl::AutoResetBase *object);
|
||||
void cleanup();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Closes ImHex
|
||||
* @param noQuestions Whether to skip asking the user for confirmation
|
||||
@@ -451,6 +510,11 @@ namespace hex {
|
||||
*/
|
||||
ImGuiID getMainDockSpaceId();
|
||||
|
||||
/**
|
||||
* @brief Gets the main window's GLFW window handle
|
||||
* @return GLFW window handle
|
||||
*/
|
||||
GLFWwindow* getMainWindowHandle();
|
||||
|
||||
/**
|
||||
* @brief Checks if borderless window mode is enabled currently
|
||||
@@ -458,11 +522,24 @@ namespace hex {
|
||||
*/
|
||||
bool isBorderlessWindowModeEnabled();
|
||||
|
||||
/**
|
||||
* @brief Checks if multi-window mode is enabled currently
|
||||
* @return Whether multi-window mode is enabled
|
||||
*/
|
||||
bool isMutliWindowModeEnabled();
|
||||
|
||||
/**
|
||||
* @brief Gets the init arguments passed to ImHex from the splash screen
|
||||
* @return Init arguments
|
||||
*/
|
||||
std::map<std::string, std::string> &getInitArguments();
|
||||
const std::map<std::string, std::string>& getInitArguments();
|
||||
|
||||
/**
|
||||
* @brief Gets a init arguments passed to ImHex from the splash screen
|
||||
* @param key The key of the init argument
|
||||
* @return Init argument
|
||||
*/
|
||||
std::string getInitArgument(const std::string &key);
|
||||
|
||||
/**
|
||||
* @brief Sets if ImHex should follow the system theme
|
||||
@@ -481,7 +558,7 @@ namespace hex {
|
||||
* @brief Gets the currently set additional folder paths
|
||||
* @return The currently set additional folder paths
|
||||
*/
|
||||
std::vector<std::filesystem::path> &getAdditionalFolderPaths();
|
||||
const std::vector<std::filesystem::path>& getAdditionalFolderPaths();
|
||||
|
||||
/**
|
||||
* @brief Sets the additional folder paths
|
||||
@@ -494,7 +571,7 @@ namespace hex {
|
||||
* @brief Gets the current GPU vendor
|
||||
* @return The current GPU vendor
|
||||
*/
|
||||
const std::string &getGPUVendor();
|
||||
const std::string& getGPUVendor();
|
||||
|
||||
/**
|
||||
* @brief Checks if ImHex is running in portable mode
|
||||
@@ -557,9 +634,44 @@ namespace hex {
|
||||
*/
|
||||
bool updateImHex(UpdateType updateType);
|
||||
|
||||
/**
|
||||
* @brief Add a new startup task that will be run while ImHex's splash screen is shown
|
||||
* @param name Name to be shown in the UI
|
||||
* @param async Whether to run the task asynchronously
|
||||
* @param function The function to run
|
||||
*/
|
||||
void addStartupTask(const std::string &name, bool async, const std::function<bool()> &function);
|
||||
|
||||
/**
|
||||
* @brief Gets the time the previous frame took
|
||||
* @return Previous frame time
|
||||
*/
|
||||
double getLastFrameTime();
|
||||
|
||||
/**
|
||||
* @brief Sets the window resizable
|
||||
* @param resizable Whether the window should be resizable
|
||||
*/
|
||||
void setWindowResizable(bool resizable);
|
||||
|
||||
/**
|
||||
* @brief Checks if this window is the main instance of ImHex
|
||||
* @return True if this is the main instance, false if another instance is already running
|
||||
*/
|
||||
bool isMainInstance();
|
||||
|
||||
/**
|
||||
* @brief Gets the initial window properties
|
||||
* @return Initial window properties
|
||||
*/
|
||||
std::optional<InitialWindowProperties> getInitialWindowProperties();
|
||||
|
||||
/**
|
||||
* @brief Gets the module handle of libimhex
|
||||
* @return Module handle
|
||||
*/
|
||||
void* getLibImHexModuleHandle();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -569,11 +681,12 @@ namespace hex {
|
||||
namespace Messaging {
|
||||
|
||||
namespace impl {
|
||||
|
||||
using MessagingHandler = std::function<void(const std::vector<u8> &)>;
|
||||
|
||||
std::map<std::string, MessagingHandler> &getHandlers();
|
||||
|
||||
const std::map<std::string, MessagingHandler>& getHandlers();
|
||||
void runHandler(const std::string &eventName, const std::vector<u8> &args);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -593,11 +706,12 @@ namespace hex {
|
||||
std::vector<GlyphRange> glyphRanges;
|
||||
Offset offset;
|
||||
u32 flags;
|
||||
std::optional<u32> defaultSize;
|
||||
};
|
||||
|
||||
namespace impl {
|
||||
|
||||
std::vector<Font>& getFonts();
|
||||
const std::vector<Font>& getFonts();
|
||||
|
||||
void setCustomFontPath(const std::fs::path &path);
|
||||
void setFontSize(float size);
|
||||
@@ -611,8 +725,8 @@ namespace hex {
|
||||
GlyphRange range(const char *glyphBegin, const char *glyphEnd);
|
||||
GlyphRange range(u32 codepointBegin, u32 codepointEnd);
|
||||
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0);
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0);
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0, std::optional<u32> defaultSize = std::nullopt);
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges = {}, Offset offset = {}, u32 flags = 0, std::optional<u32> defaultSize = std::nullopt);
|
||||
|
||||
constexpr static float DefaultFontSize = 13.0;
|
||||
|
||||
@@ -623,7 +737,7 @@ namespace hex {
|
||||
* @brief Gets the current custom font path
|
||||
* @return The current custom font path
|
||||
*/
|
||||
std::filesystem::path &getCustomFontPath();
|
||||
const std::filesystem::path& getCustomFontPath();
|
||||
|
||||
/**
|
||||
* @brief Gets the current font size
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
struct ImGuiTextBuffer;
|
||||
|
||||
namespace hex {
|
||||
|
||||
class LayoutManager {
|
||||
@@ -13,6 +15,9 @@ namespace hex {
|
||||
std::fs::path path;
|
||||
};
|
||||
|
||||
using LoadCallback = std::function<void(std::string_view)>;
|
||||
using StoreCallback = std::function<void(ImGuiTextBuffer *)>;
|
||||
|
||||
/**
|
||||
* @brief Save the current layout
|
||||
* @param name Name of the layout
|
||||
@@ -41,7 +46,13 @@ namespace hex {
|
||||
* @brief Get a list of all layouts
|
||||
* @return List of all added layouts
|
||||
*/
|
||||
static std::vector<Layout> getLayouts();
|
||||
static const std::vector<Layout> &getLayouts();
|
||||
|
||||
/**
|
||||
* @brief Removes the layout with the given name
|
||||
* @param name Name of the layout
|
||||
*/
|
||||
static void removeLayout(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Handles loading of layouts if needed
|
||||
@@ -76,6 +87,12 @@ namespace hex {
|
||||
*/
|
||||
static void closeAllViews();
|
||||
|
||||
static void registerLoadCallback(const LoadCallback &callback);
|
||||
static void registerStoreCallback(const StoreCallback &callback);
|
||||
|
||||
static void onStore(ImGuiTextBuffer *buffer);
|
||||
static void onLoad(std::string_view line);
|
||||
|
||||
private:
|
||||
LayoutManager() = default;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
@@ -26,6 +27,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void loadLanguage(const std::string &language);
|
||||
std::string getLocalizedString(const std::string &unlocalizedString, const std::string &language = "");
|
||||
|
||||
[[nodiscard]] const std::map<std::string, std::string> &getSupportedLanguages();
|
||||
[[nodiscard]] const std::string &getFallbackLanguage();
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <span>
|
||||
#include <string>
|
||||
|
||||
#include <wolv/io/fs.hpp>
|
||||
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
|
||||
struct ImGuiContext;
|
||||
|
||||
namespace hex {
|
||||
|
||||
struct SubCommand {
|
||||
std::string commandKey;
|
||||
std::string commandDesc;
|
||||
std::string commandLong;
|
||||
std::string commandShort;
|
||||
|
||||
std::string commandDescription;
|
||||
std::function<void(const std::vector<std::string>&)> callback;
|
||||
};
|
||||
|
||||
@@ -26,22 +31,23 @@ namespace hex {
|
||||
using InitializePluginFunc = void (*)();
|
||||
using InitializeLibraryFunc = void (*)();
|
||||
using GetPluginNameFunc = const char *(*)();
|
||||
using GetLibraryNameFunc = const char *(*)();
|
||||
using GetPluginAuthorFunc = const char *(*)();
|
||||
using GetPluginDescriptionFunc = const char *(*)();
|
||||
using GetCompatibleVersionFunc = const char *(*)();
|
||||
using SetImGuiContextFunc = void (*)(ImGuiContext *);
|
||||
using IsBuiltinPluginFunc = bool (*)();
|
||||
using GetSubCommandsFunc = void* (*)();
|
||||
using GetFeaturesFunc = void* (*)();
|
||||
|
||||
InitializePluginFunc initializePluginFunction = nullptr;
|
||||
InitializeLibraryFunc initializeLibraryFunction = nullptr;
|
||||
GetPluginNameFunc getPluginNameFunction = nullptr;
|
||||
GetLibraryNameFunc getLibraryNameFunction = nullptr;
|
||||
GetPluginAuthorFunc getPluginAuthorFunction = nullptr;
|
||||
GetPluginDescriptionFunc getPluginDescriptionFunction = nullptr;
|
||||
GetCompatibleVersionFunc getCompatibleVersionFunction = nullptr;
|
||||
SetImGuiContextFunc setImGuiContextFunction = nullptr;
|
||||
IsBuiltinPluginFunc isBuiltinPluginFunction = nullptr;
|
||||
SetImGuiContextFunc setImGuiContextLibraryFunction = nullptr;
|
||||
GetSubCommandsFunc getSubCommandsFunction = nullptr;
|
||||
GetFeaturesFunc getFeaturesFunction = nullptr;
|
||||
};
|
||||
@@ -49,7 +55,7 @@ namespace hex {
|
||||
class Plugin {
|
||||
public:
|
||||
explicit Plugin(const std::fs::path &path);
|
||||
explicit Plugin(const PluginFunctions &functions);
|
||||
explicit Plugin(const std::string &name, const PluginFunctions &functions);
|
||||
|
||||
Plugin(const Plugin &) = delete;
|
||||
Plugin(Plugin &&other) noexcept;
|
||||
@@ -64,7 +70,6 @@ namespace hex {
|
||||
[[nodiscard]] std::string getPluginDescription() const;
|
||||
[[nodiscard]] std::string getCompatibleVersion() const;
|
||||
void setImGuiContext(ImGuiContext *ctx) const;
|
||||
[[nodiscard]] bool isBuiltinPlugin() const;
|
||||
|
||||
[[nodiscard]] const std::fs::path &getPath() const;
|
||||
|
||||
@@ -76,11 +81,14 @@ namespace hex {
|
||||
|
||||
[[nodiscard]] bool isLibraryPlugin() const;
|
||||
|
||||
[[nodiscard]] bool wasAddedManually() const;
|
||||
|
||||
private:
|
||||
uintptr_t m_handle = 0;
|
||||
std::fs::path m_path;
|
||||
|
||||
mutable bool m_initialized = false;
|
||||
bool m_addedManually = false;
|
||||
|
||||
PluginFunctions m_functions = {};
|
||||
|
||||
@@ -96,14 +104,31 @@ namespace hex {
|
||||
public:
|
||||
PluginManager() = delete;
|
||||
|
||||
static bool load();
|
||||
static bool load(const std::fs::path &pluginFolder);
|
||||
|
||||
static bool loadLibraries();
|
||||
static bool loadLibraries(const std::fs::path &libraryFolder);
|
||||
|
||||
static void unload();
|
||||
static void reload();
|
||||
static void initializeNewPlugins();
|
||||
static void addLoadPath(const std::fs::path &path);
|
||||
|
||||
static void addPlugin(PluginFunctions functions);
|
||||
static void addPlugin(const std::string &name, PluginFunctions functions);
|
||||
|
||||
static std::vector<Plugin> &getPlugins();
|
||||
static std::vector<std::fs::path> &getPluginPaths();
|
||||
static Plugin* getPlugin(const std::string &name);
|
||||
static const std::list<Plugin>& getPlugins();
|
||||
static const std::vector<std::fs::path>& getPluginPaths();
|
||||
static const std::vector<std::fs::path>& getPluginLoadPaths();
|
||||
|
||||
static bool isPluginLoaded(const std::fs::path &path);
|
||||
|
||||
private:
|
||||
static std::list<Plugin>& getPluginsMutable();
|
||||
|
||||
static AutoReset<std::vector<std::fs::path>> s_pluginPaths, s_pluginLoadPaths;
|
||||
static AutoReset<std::vector<uintptr_t>> s_loadedLibraries;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -93,30 +93,26 @@ namespace hex {
|
||||
*
|
||||
* @param handler The handler to register
|
||||
*/
|
||||
static void registerHandler(const Handler &handler) {
|
||||
getHandlers().push_back(handler);
|
||||
}
|
||||
static void registerHandler(const Handler &handler);
|
||||
|
||||
/**
|
||||
* @brief Register a handler for storing and loading per-provider data from a project file
|
||||
*
|
||||
* @param handler The handler to register
|
||||
*/
|
||||
static void registerPerProviderHandler(const ProviderHandler &handler) {
|
||||
getProviderHandlers().push_back(handler);
|
||||
}
|
||||
static void registerPerProviderHandler(const ProviderHandler &handler);
|
||||
|
||||
/**
|
||||
* @brief Get the list of registered handlers
|
||||
* @return List of registered handlers
|
||||
*/
|
||||
static std::vector<Handler>& getHandlers();
|
||||
static const std::vector<Handler>& getHandlers();
|
||||
|
||||
/**
|
||||
* @brief Get the list of registered per-provider handlers
|
||||
* @return List of registered per-provider handlers
|
||||
*/
|
||||
static std::vector<ProviderHandler>& getProviderHandlers();
|
||||
static const std::vector<ProviderHandler>& getProviderHandlers();
|
||||
|
||||
private:
|
||||
ProjectFile() = default;
|
||||
|
||||
@@ -425,7 +425,7 @@ namespace hex {
|
||||
* @param focused Whether the current view is focused
|
||||
* @param keyCode The key code of the key that was pressed
|
||||
*/
|
||||
static void process(const std::unique_ptr<View> ¤tView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode);
|
||||
static void process(const View *currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode);
|
||||
|
||||
/**
|
||||
* @brief Process a key event. This should be called from the main loop.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <memory>
|
||||
#include <list>
|
||||
#include <condition_variable>
|
||||
#include <source_location>
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -31,7 +32,9 @@ namespace hex {
|
||||
* @brief Updates the current process value of the task
|
||||
* @param value Current value
|
||||
*/
|
||||
void update(u64 value = 0);
|
||||
void update(u64 value);
|
||||
void update() const;
|
||||
void increment();
|
||||
|
||||
/**
|
||||
* @brief Sets the maximum value of the task
|
||||
@@ -148,18 +151,42 @@ namespace hex {
|
||||
*/
|
||||
static void doLater(const std::function<void()> &function);
|
||||
|
||||
/**
|
||||
* @brief Creates a new synchronous task that will execute the given function at the start of the next frame
|
||||
* @param function Function to be executed
|
||||
* @param location Source location of the function call. This is used to make sure repeated calls to the function at the same location are only executed once
|
||||
*/
|
||||
static void doLaterOnce(const std::function<void()> &function, std::source_location location = std::source_location::current());
|
||||
|
||||
/**
|
||||
* @brief Creates a callback that will be executed when all tasks are finished
|
||||
* @param function Function to be executed
|
||||
*/
|
||||
static void runWhenTasksFinished(const std::function<void()> &function);
|
||||
|
||||
/**
|
||||
* @brief Sets the name of the current thread
|
||||
* @param name Name of the thread
|
||||
*/
|
||||
static void setCurrentThreadName(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Gets the name of the current thread
|
||||
* @return Name of the thread
|
||||
*/
|
||||
static std::string getCurrentThreadName();
|
||||
|
||||
/**
|
||||
* @brief Cleans up finished tasks
|
||||
*/
|
||||
static void collectGarbage();
|
||||
|
||||
static Task& getCurrentTask();
|
||||
|
||||
static size_t getRunningTaskCount();
|
||||
static size_t getRunningBackgroundTaskCount();
|
||||
|
||||
static std::list<std::shared_ptr<Task>> &getRunningTasks();
|
||||
static const std::list<std::shared_ptr<Task>>& getRunningTasks();
|
||||
static void runDeferredCalls();
|
||||
|
||||
private:
|
||||
|
||||
@@ -78,8 +78,8 @@ namespace hex {
|
||||
StyleMap styleMap;
|
||||
};
|
||||
|
||||
static std::map<std::string, ThemeHandler>& getThemeHandlers();
|
||||
static std::map<std::string, StyleHandler>& getStyleHandlers();
|
||||
static const std::map<std::string, ThemeHandler>& getThemeHandlers();
|
||||
static const std::map<std::string, StyleHandler>& getStyleHandlers();
|
||||
|
||||
private:
|
||||
ThemeManager() = default;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <wolv/io/fs.hpp>
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
@@ -12,26 +13,30 @@ namespace hex {
|
||||
struct Workspace {
|
||||
std::string layout;
|
||||
std::fs::path path;
|
||||
bool builtin;
|
||||
};
|
||||
|
||||
static void createWorkspace(const std::string &name, const std::string &layout = "");
|
||||
static void switchWorkspace(const std::string &name);
|
||||
|
||||
static void importFromFile(const std::fs::path &path);
|
||||
static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {});
|
||||
static bool exportToFile(std::fs::path path = {}, std::string workspaceName = {}, bool builtin = false);
|
||||
|
||||
static const auto& getWorkspaces() { return s_workspaces; }
|
||||
static void removeWorkspace(const std::string &name);
|
||||
|
||||
static const auto& getWorkspaces() { return *s_workspaces; }
|
||||
static const auto& getCurrentWorkspace() { return s_currentWorkspace; }
|
||||
|
||||
static void reset();
|
||||
static void reload();
|
||||
|
||||
static void process();
|
||||
|
||||
private:
|
||||
WorkspaceManager() = default;
|
||||
|
||||
static std::map<std::string, Workspace> s_workspaces;
|
||||
static decltype(s_workspaces)::iterator s_currentWorkspace, s_previousWorkspace;
|
||||
static AutoReset<std::map<std::string, Workspace>> s_workspaces;
|
||||
static decltype(s_workspaces)::Type::iterator s_currentWorkspace, s_previousWorkspace, s_workspaceToRemove;
|
||||
};
|
||||
|
||||
}
|
||||
89
lib/libimhex/include/hex/helpers/auto_reset.hpp
Normal file
89
lib/libimhex/include/hex/helpers/auto_reset.hpp
Normal file
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/api/event_manager.hpp>
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace impl {
|
||||
|
||||
class AutoResetBase {
|
||||
public:
|
||||
virtual ~AutoResetBase() = default;
|
||||
virtual void reset() = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class AutoReset : public impl::AutoResetBase {
|
||||
public:
|
||||
using Type = T;
|
||||
|
||||
AutoReset() {
|
||||
ImHexApi::System::impl::addAutoResetObject(this);
|
||||
}
|
||||
|
||||
T* operator->() {
|
||||
return &m_value;
|
||||
}
|
||||
|
||||
const T* operator->() const {
|
||||
return &m_value;
|
||||
}
|
||||
|
||||
T& operator*() {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
const T& operator*() const {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
operator T&() {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
operator const T&() const {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
T& operator=(const T &value) {
|
||||
m_value = value;
|
||||
m_valid = true;
|
||||
return m_value;
|
||||
}
|
||||
|
||||
T& operator=(T &&value) noexcept {
|
||||
m_value = std::move(value);
|
||||
m_valid = true;
|
||||
return m_value;
|
||||
}
|
||||
|
||||
bool isValid() const {
|
||||
return m_valid;
|
||||
}
|
||||
|
||||
private:
|
||||
friend void ImHexApi::System::impl::cleanup();
|
||||
|
||||
void reset() override {
|
||||
if constexpr (requires { m_value.reset(); }) {
|
||||
m_value.reset();
|
||||
} else if constexpr (requires { m_value.clear(); }) {
|
||||
m_value.clear();
|
||||
} else if constexpr (requires(T t) { t = nullptr; }) {
|
||||
m_value = nullptr;
|
||||
} else {
|
||||
m_value = { };
|
||||
}
|
||||
|
||||
m_valid = false;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_valid = true;
|
||||
T m_value;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -28,8 +28,9 @@ namespace hex {
|
||||
EncodingFile& operator=(EncodingFile &&other) noexcept;
|
||||
|
||||
[[nodiscard]] std::pair<std::string_view, size_t> getEncodingFor(std::span<u8> buffer) const;
|
||||
[[nodiscard]] size_t getEncodingLengthFor(std::span<u8> buffer) const;
|
||||
[[nodiscard]] size_t getLongestSequence() const { return m_longestSequence; }
|
||||
[[nodiscard]] u64 getEncodingLengthFor(std::span<u8> buffer) const;
|
||||
[[nodiscard]] u64 getShortestSequence() const { return m_shortestSequence; }
|
||||
[[nodiscard]] u64 getLongestSequence() const { return m_longestSequence; }
|
||||
|
||||
[[nodiscard]] bool valid() const { return m_valid; }
|
||||
|
||||
@@ -45,7 +46,9 @@ namespace hex {
|
||||
std::string m_name;
|
||||
std::string m_tableContent;
|
||||
std::unique_ptr<std::map<size_t, std::map<std::vector<u8>, std::string>>> m_mapping;
|
||||
size_t m_longestSequence = 0;
|
||||
|
||||
u64 m_shortestSequence = std::numeric_limits<u64>::max();
|
||||
u64 m_longestSequence = std::numeric_limits<u64>::min();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ namespace hex::fs {
|
||||
Magic,
|
||||
Plugins,
|
||||
Yara,
|
||||
YaraAdvancedAnalysis,
|
||||
Config,
|
||||
Backups,
|
||||
Resources,
|
||||
|
||||
@@ -1,72 +1,76 @@
|
||||
#pragma once
|
||||
|
||||
#include <future>
|
||||
#if defined(OS_WEB)
|
||||
|
||||
#include <emscripten/fetch.h>
|
||||
#include <future>
|
||||
|
||||
namespace hex {
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
#include <emscripten/fetch.h>
|
||||
|
||||
// Execute the request
|
||||
auto result = this->executeImpl<T>(response);
|
||||
namespace hex {
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
|
||||
// Write the result to the file
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
file.writeBuffer(reinterpret_cast<const u8*>(result.getData().data()), result.getData().size());
|
||||
// Execute the request
|
||||
auto result = this->executeImpl<T>(response);
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
// Write the result to the file
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
file.writeBuffer(reinterpret_cast<const u8*>(result.getData().data()), result.getData().size());
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
hex::unused(path, mimeName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
hex::unused(data, mimeName, fileName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
std::vector<u8> responseData;
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
strcpy(m_attr.requestMethod, m_method.c_str());
|
||||
m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
|
||||
|
||||
if (!m_body.empty()) {
|
||||
m_attr.requestData = m_body.c_str();
|
||||
m_attr.requestDataSize = m_body.size();
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<const char*> headers;
|
||||
for (auto it = m_headers.begin(); it != m_headers.end(); it++) {
|
||||
headers.push_back(it->first.c_str());
|
||||
headers.push_back(it->second.c_str());
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
hex::unused(path, mimeName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
headers.push_back(nullptr);
|
||||
m_attr.requestHeaders = headers.data();
|
||||
|
||||
// Send request
|
||||
emscripten_fetch_t* fetch = emscripten_fetch(&m_attr, m_url.c_str());
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
hex::unused(data, mimeName, fileName);
|
||||
throw std::logic_error("Not implemented");
|
||||
}
|
||||
|
||||
data.resize(fetch->numBytes);
|
||||
std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin());
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
std::vector<u8> responseData;
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
strcpy(m_attr.requestMethod, m_method.c_str());
|
||||
m_attr.attributes = EMSCRIPTEN_FETCH_SYNCHRONOUS | EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
|
||||
|
||||
if (!m_body.empty()) {
|
||||
m_attr.requestData = m_body.c_str();
|
||||
m_attr.requestDataSize = m_body.size();
|
||||
}
|
||||
|
||||
std::vector<const char*> headers;
|
||||
for (auto it = m_headers.begin(); it != m_headers.end(); it++) {
|
||||
headers.push_back(it->first.c_str());
|
||||
headers.push_back(it->second.c_str());
|
||||
}
|
||||
headers.push_back(nullptr);
|
||||
m_attr.requestHeaders = headers.data();
|
||||
|
||||
// Send request
|
||||
emscripten_fetch_t* fetch = emscripten_fetch(&m_attr, m_url.c_str());
|
||||
|
||||
data.resize(fetch->numBytes);
|
||||
std::copy(fetch->data, fetch->data + fetch->numBytes, data.begin());
|
||||
|
||||
return Result<T>(fetch->status, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
return Result<T>(fetch->status, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -1,145 +1,149 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <future>
|
||||
#if !defined(OS_WEB)
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <string>
|
||||
#include <future>
|
||||
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <curl/curl.h>
|
||||
|
||||
#include <wolv/utils/string.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
namespace hex {
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
namespace hex {
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &file);
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::downloadFile(const std::fs::path &path) {
|
||||
return std::async(std::launch::async, [this, path] {
|
||||
std::vector<u8> response;
|
||||
|
||||
return this->executeImpl<T>(response);
|
||||
});
|
||||
}
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToFile);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &file);
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
return std::async(std::launch::async, [this, path, mimeName]{
|
||||
auto fileName = wolv::util::toUTF8String(path.filename());
|
||||
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Read);
|
||||
|
||||
curl_mime_data_cb(part, file.getSize(),
|
||||
[](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
return fread(buffer, size, nitems, handle);
|
||||
},
|
||||
[](void *arg, curl_off_t offset, int origin) -> int {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
if (fseek(handle, offset, origin) != 0)
|
||||
return CURL_SEEKFUNC_CANTSEEK;
|
||||
else
|
||||
return CURL_SEEKFUNC_OK;
|
||||
},
|
||||
[](void *arg) {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
fclose(handle);
|
||||
},
|
||||
file.getHandle());
|
||||
curl_mime_filename(part, fileName.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
return std::async(std::launch::async, [this, data = std::move(data), mimeName, fileName]{
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
curl_mime_data(part, reinterpret_cast<const char *>(data.data()), data.size());
|
||||
auto fileNameStr = wolv::util::toUTF8String(fileName.filename());
|
||||
curl_mime_filename(part, fileNameStr.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method.c_str());
|
||||
|
||||
setDefaultConfig();
|
||||
|
||||
if (!m_body.empty()) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_body.c_str());
|
||||
return this->executeImpl<T>(response);
|
||||
});
|
||||
}
|
||||
|
||||
curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Cache-Control: no-cache");
|
||||
ON_SCOPE_EXIT { curl_slist_free_all(headers); };
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(const std::fs::path &path, const std::string &mimeName) {
|
||||
return std::async(std::launch::async, [this, path, mimeName]{
|
||||
auto fileName = wolv::util::toUTF8String(path.filename());
|
||||
|
||||
for (auto &[key, value] : m_headers) {
|
||||
std::string header = hex::format("{}: {}", key, value);
|
||||
headers = curl_slist_append(headers, header.c_str());
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Read);
|
||||
|
||||
curl_mime_data_cb(part, file.getSize(),
|
||||
[](char *buffer, size_t size, size_t nitems, void *arg) -> size_t {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
return fread(buffer, size, nitems, handle);
|
||||
},
|
||||
[](void *arg, curl_off_t offset, int origin) -> int {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
if (fseek(handle, offset, origin) != 0)
|
||||
return CURL_SEEKFUNC_CANTSEEK;
|
||||
else
|
||||
return CURL_SEEKFUNC_OK;
|
||||
},
|
||||
[](void *arg) {
|
||||
auto handle = static_cast<FILE*>(arg);
|
||||
|
||||
fclose(handle);
|
||||
},
|
||||
file.getHandle());
|
||||
curl_mime_filename(part, fileName.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
{
|
||||
std::scoped_lock lock(m_transmissionMutex);
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::uploadFile(std::vector<u8> data, const std::string &mimeName, const std::fs::path &fileName) {
|
||||
return std::async(std::launch::async, [this, data = std::move(data), mimeName, fileName]{
|
||||
curl_mime *mime = curl_mime_init(m_curl);
|
||||
curl_mimepart *part = curl_mime_addpart(mime);
|
||||
|
||||
auto result = curl_easy_perform(m_curl);
|
||||
if (result != CURLE_OK){
|
||||
char *url = nullptr;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_EFFECTIVE_URL, &url);
|
||||
log::error("Http request '{0} {1}' failed with error {2}: '{3}'", m_method, url, u32(result), curl_easy_strerror(result));
|
||||
checkProxyErrors();
|
||||
curl_mime_data(part, reinterpret_cast<const char *>(data.data()), data.size());
|
||||
auto fileNameStr = wolv::util::toUTF8String(fileName.filename());
|
||||
curl_mime_filename(part, fileNameStr.c_str());
|
||||
curl_mime_name(part, mimeName.c_str());
|
||||
|
||||
return { };
|
||||
curl_easy_setopt(m_curl, CURLOPT_MIMEPOST, mime);
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::future<HttpRequest::Result<T>> HttpRequest::execute() {
|
||||
return std::async(std::launch::async, [this] {
|
||||
|
||||
std::vector<u8> responseData;
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeToVector);
|
||||
curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &responseData);
|
||||
|
||||
return this->executeImpl<T>(responseData);
|
||||
});
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
HttpRequest::Result<T> HttpRequest::executeImpl(std::vector<u8> &data) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_URL, m_url.c_str());
|
||||
curl_easy_setopt(m_curl, CURLOPT_CUSTOMREQUEST, m_method.c_str());
|
||||
|
||||
setDefaultConfig();
|
||||
|
||||
if (!m_body.empty()) {
|
||||
curl_easy_setopt(m_curl, CURLOPT_POSTFIELDS, m_body.c_str());
|
||||
}
|
||||
|
||||
curl_slist *headers = nullptr;
|
||||
headers = curl_slist_append(headers, "Cache-Control: no-cache");
|
||||
ON_SCOPE_EXIT { curl_slist_free_all(headers); };
|
||||
|
||||
for (auto &[key, value] : m_headers) {
|
||||
std::string header = hex::format("{}: {}", key, value);
|
||||
headers = curl_slist_append(headers, header.c_str());
|
||||
}
|
||||
curl_easy_setopt(m_curl, CURLOPT_HTTPHEADER, headers);
|
||||
|
||||
{
|
||||
std::scoped_lock lock(m_transmissionMutex);
|
||||
|
||||
auto result = curl_easy_perform(m_curl);
|
||||
if (result != CURLE_OK){
|
||||
char *url = nullptr;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_EFFECTIVE_URL, &url);
|
||||
log::error("Http request '{0} {1}' failed with error {2}: '{3}'", m_method, url, u32(result), curl_easy_strerror(result));
|
||||
checkProxyErrors();
|
||||
|
||||
return { };
|
||||
}
|
||||
}
|
||||
|
||||
long statusCode = 0;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
|
||||
|
||||
return Result<T>(statusCode, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
long statusCode = 0;
|
||||
curl_easy_getinfo(m_curl, CURLINFO_RESPONSE_CODE, &statusCode);
|
||||
|
||||
return Result<T>(statusCode, { data.begin(), data.end() });
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -2,12 +2,11 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/color.h>
|
||||
|
||||
#include <wolv/io/file.hpp>
|
||||
#include <wolv/utils/guards.hpp>
|
||||
|
||||
namespace hex::log {
|
||||
|
||||
@@ -19,7 +18,11 @@ namespace hex::log {
|
||||
[[maybe_unused]] void redirectToFile();
|
||||
[[maybe_unused]] void enableColorPrinting();
|
||||
|
||||
extern std::mutex g_loggerMutex;
|
||||
[[nodiscard]] bool isLoggingSuspended();
|
||||
[[nodiscard]] bool isDebugLoggingEnabled();
|
||||
|
||||
void lockLoggerMutex();
|
||||
void unlockLoggerMutex();
|
||||
|
||||
struct LogEntry {
|
||||
std::string project;
|
||||
@@ -28,65 +31,91 @@ namespace hex::log {
|
||||
};
|
||||
|
||||
std::vector<LogEntry>& getLogEntries();
|
||||
void addLogEntry(std::string_view project, std::string_view level, std::string_view message);
|
||||
|
||||
[[maybe_unused]] void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level, const char *projectName);
|
||||
|
||||
[[maybe_unused]] void print(const fmt::text_style &ts, const std::string &level, const std::string &fmt, auto && ... args) {
|
||||
std::scoped_lock lock(g_loggerMutex);
|
||||
if (isLoggingSuspended()) [[unlikely]]
|
||||
return;
|
||||
|
||||
lockLoggerMutex();
|
||||
ON_SCOPE_EXIT { unlockLoggerMutex(); };
|
||||
|
||||
auto dest = getDestination();
|
||||
printPrefix(dest, ts, level, IMHEX_PROJECT_NAME);
|
||||
try {
|
||||
printPrefix(dest, ts, level, IMHEX_PROJECT_NAME);
|
||||
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
|
||||
addLogEntry(IMHEX_PROJECT_NAME, level, std::move(message));
|
||||
} catch (const std::exception&) { }
|
||||
}
|
||||
|
||||
namespace color {
|
||||
|
||||
fmt::color debug();
|
||||
fmt::color info();
|
||||
fmt::color warn();
|
||||
fmt::color error();
|
||||
fmt::color fatal();
|
||||
|
||||
getLogEntries().push_back({ IMHEX_PROJECT_NAME, level, std::move(message) });
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void suspendLogging();
|
||||
void resumeLogging();
|
||||
void enableDebugLogging();
|
||||
|
||||
[[maybe_unused]] void debug(const std::string &fmt, auto && ... args) {
|
||||
#if defined(DEBUG)
|
||||
hex::log::impl::print(fg(fmt::color::light_green) | fmt::emphasis::bold, "[DEBUG]", fmt, args...);
|
||||
#else
|
||||
impl::getLogEntries().push_back({ IMHEX_PROJECT_NAME, "[DEBUG]", fmt::format(fmt::runtime(fmt), args...) });
|
||||
#endif
|
||||
if (impl::isDebugLoggingEnabled()) [[unlikely]] {
|
||||
hex::log::impl::print(fg(impl::color::debug()) | fmt::emphasis::bold, "[DEBUG]", fmt, args...);
|
||||
} else {
|
||||
impl::addLogEntry(IMHEX_PROJECT_NAME, "[DEBUG]", fmt::format(fmt::runtime(fmt), args...));
|
||||
}
|
||||
}
|
||||
|
||||
[[maybe_unused]] void info(const std::string &fmt, auto && ... args) {
|
||||
hex::log::impl::print(fg(fmt::color::cadet_blue) | fmt::emphasis::bold, "[INFO] ", fmt, args...);
|
||||
hex::log::impl::print(fg(impl::color::info()) | fmt::emphasis::bold, "[INFO] ", fmt, args...);
|
||||
}
|
||||
|
||||
[[maybe_unused]] void warn(const std::string &fmt, auto && ... args) {
|
||||
hex::log::impl::print(fg(fmt::color::orange) | fmt::emphasis::bold, "[WARN] ", fmt, args...);
|
||||
hex::log::impl::print(fg(impl::color::warn()) | fmt::emphasis::bold, "[WARN] ", fmt, args...);
|
||||
}
|
||||
|
||||
[[maybe_unused]] void error(const std::string &fmt, auto && ... args) {
|
||||
hex::log::impl::print(fg(fmt::color::red) | fmt::emphasis::bold, "[ERROR]", fmt, args...);
|
||||
hex::log::impl::print(fg(impl::color::error()) | fmt::emphasis::bold, "[ERROR]", fmt, args...);
|
||||
}
|
||||
|
||||
[[maybe_unused]] void fatal(const std::string &fmt, auto && ... args) {
|
||||
hex::log::impl::print(fg(fmt::color::purple) | fmt::emphasis::bold, "[FATAL]", fmt, args...);
|
||||
hex::log::impl::print(fg(impl::color::fatal()) | fmt::emphasis::bold, "[FATAL]", fmt, args...);
|
||||
}
|
||||
|
||||
|
||||
[[maybe_unused]] void print(const std::string &fmt, auto && ... args) {
|
||||
std::scoped_lock lock(impl::g_loggerMutex);
|
||||
impl::lockLoggerMutex();
|
||||
ON_SCOPE_EXIT { impl::unlockLoggerMutex(); };
|
||||
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}", message);
|
||||
fflush(dest);
|
||||
try {
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}", message);
|
||||
fflush(dest);
|
||||
} catch (const std::exception&) { }
|
||||
}
|
||||
|
||||
[[maybe_unused]] void println(const std::string &fmt, auto && ... args) {
|
||||
std::scoped_lock lock(impl::g_loggerMutex);
|
||||
impl::lockLoggerMutex();
|
||||
ON_SCOPE_EXIT { impl::unlockLoggerMutex(); };
|
||||
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
try {
|
||||
auto dest = impl::getDestination();
|
||||
auto message = fmt::format(fmt::runtime(fmt), args...);
|
||||
fmt::print(dest, "{}\n", message);
|
||||
fflush(dest);
|
||||
} catch (const std::exception&) { }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,14 @@ namespace hex::magic {
|
||||
using namespace hex::literals;
|
||||
|
||||
bool compile();
|
||||
std::string getDescription(const std::vector<u8> &data);
|
||||
std::string getDescription(prv::Provider *provider, size_t size = 100_KiB);
|
||||
std::string getMIMEType(const std::vector<u8> &data);
|
||||
std::string getMIMEType(prv::Provider *provider, size_t size = 100_KiB);
|
||||
std::string getDescription(const std::vector<u8> &data, bool firstEntryOnly = false);
|
||||
std::string getDescription(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);
|
||||
std::string getMIMEType(const std::vector<u8> &data, bool firstEntryOnly = false);
|
||||
std::string getMIMEType(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);
|
||||
std::string getExtensions(const std::vector<u8> &data, bool firstEntryOnly = false);
|
||||
std::string getExtensions(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);
|
||||
std::string getAppleCreatorType(const std::vector<u8> &data, bool firstEntryOnly = false);
|
||||
std::string getAppleCreatorType(prv::Provider *provider, u64 address = 0x00, size_t size = 100_KiB, bool firstEntryOnly = false);
|
||||
|
||||
bool isValidMIMEType(const std::string &mimeType);
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include <concepts>
|
||||
|
||||
using u8 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
@@ -61,4 +63,23 @@ namespace hex {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
concept Pointer = std::is_pointer_v<T>;
|
||||
|
||||
template<Pointer T>
|
||||
struct NonNull {
|
||||
NonNull(T ptr) : pointer(ptr) { }
|
||||
NonNull(std::nullptr_t) = delete;
|
||||
NonNull(std::integral auto) = delete;
|
||||
NonNull(bool) = delete;
|
||||
|
||||
[[nodiscard]] T get() const { return pointer; }
|
||||
[[nodiscard]] T operator->() const { return pointer; }
|
||||
[[nodiscard]] T operator*() const { return *pointer; }
|
||||
[[nodiscard]] operator T() const { return pointer; }
|
||||
|
||||
T pointer;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -9,8 +9,10 @@
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
@@ -75,10 +77,14 @@ namespace hex {
|
||||
int executeCommand(const std::string &command);
|
||||
void openWebpage(std::string url);
|
||||
|
||||
extern "C" void registerFont(const char *fontName, const char *fontPath);
|
||||
const std::map<std::fs::path, std::string>& getFonts();
|
||||
|
||||
[[nodiscard]] std::string encodeByteString(const std::vector<u8> &bytes);
|
||||
[[nodiscard]] std::vector<u8> decodeByteString(const std::string &string);
|
||||
|
||||
std::wstring utf8ToUtf16(const std::string& utf8);
|
||||
[[nodiscard]] std::wstring utf8ToUtf16(const std::string& utf8);
|
||||
[[nodiscard]] std::string utf16ToUtf8(const std::wstring& utf16);
|
||||
|
||||
[[nodiscard]] constexpr u64 extract(u8 from, u8 to, const std::unsigned_integral auto &value) {
|
||||
if (from < to) std::swap(from, to);
|
||||
@@ -146,7 +152,7 @@ namespace hex {
|
||||
using SizeType = typename SizeTypeImpl<Size>::Type;
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] constexpr T changeEndianess(const T &value, size_t size, std::endian endian) {
|
||||
[[nodiscard]] constexpr T changeEndianness(const T &value, size_t size, std::endian endian) {
|
||||
if (endian == std::endian::native)
|
||||
return value;
|
||||
|
||||
@@ -166,8 +172,8 @@ namespace hex {
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
[[nodiscard]] constexpr T changeEndianess(const T &value, std::endian endian) {
|
||||
return changeEndianess(value, sizeof(value), endian);
|
||||
[[nodiscard]] constexpr T changeEndianness(const T &value, std::endian endian) {
|
||||
return changeEndianness(value, sizeof(value), endian);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr u128 bitmask(u8 bits) {
|
||||
@@ -293,10 +299,29 @@ namespace hex {
|
||||
[[nodiscard]] std::optional<std::string> getEnvironmentVariable(const std::string &env);
|
||||
|
||||
[[nodiscard]] inline std::string limitStringLength(const std::string &string, size_t maxLength) {
|
||||
if (string.length() <= maxLength)
|
||||
// If the string is shorter than the max length, return it as is
|
||||
if (string.size() < maxLength)
|
||||
return string;
|
||||
|
||||
return string.substr(0, maxLength - 3) + "...";
|
||||
// If the string is longer than the max length, find the last space before the max length
|
||||
auto it = string.begin() + maxLength;
|
||||
while (it != string.begin() && !std::isspace(*it)) --it;
|
||||
|
||||
// If there's no space before the max length, just cut the string
|
||||
if (it == string.begin()) {
|
||||
it = string.begin() + maxLength;
|
||||
|
||||
// Try to find a UTF-8 character boundary
|
||||
while (it != string.begin() && (*it & 0x80) != 0x00) --it;
|
||||
++it;
|
||||
}
|
||||
|
||||
// If we still didn't find a valid boundary, just return the string as is
|
||||
if (it == string.begin())
|
||||
return string;
|
||||
|
||||
// Append
|
||||
return std::string(string.begin(), it) + "…";
|
||||
}
|
||||
|
||||
[[nodiscard]] std::optional<std::fs::path> getInitialFilePath();
|
||||
@@ -304,4 +329,16 @@ namespace hex {
|
||||
[[nodiscard]] std::string generateHexView(u64 offset, u64 size, prv::Provider *provider);
|
||||
[[nodiscard]] std::string generateHexView(u64 offset, const std::vector<u8> &data);
|
||||
|
||||
[[nodiscard]] std::string formatSystemError(i32 error);
|
||||
|
||||
/**
|
||||
* Gets the shared library handle for a given pointer
|
||||
* @param symbol Pointer to any function or variable in the shared library
|
||||
* @return The module handle
|
||||
* @warning Important! Calling this function on functions defined in other modules will return the handle of the current module!
|
||||
* This is because you're not actually passing a pointer to the function in the other module but rather a pointer to a thunk
|
||||
* that is defined in the current module.
|
||||
*/
|
||||
[[nodiscard]] void* getContainingModule(void* symbol);
|
||||
|
||||
}
|
||||
|
||||
@@ -9,10 +9,15 @@
|
||||
void errorMessageMacos(const char *message);
|
||||
void openWebpageMacos(const char *url);
|
||||
bool isMacosSystemDarkModeEnabled();
|
||||
bool isMacosFullScreenModeEnabled(GLFWwindow *window);
|
||||
float getBackingScaleFactor();
|
||||
|
||||
void setupMacosWindowStyle(GLFWwindow *window);
|
||||
void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode);
|
||||
|
||||
void enumerateFontsMacos();
|
||||
|
||||
void macosHandleTitlebarDoubleClickGesture(GLFWwindow *window);
|
||||
bool macosIsWindowBeingResizedByUser(GLFWwindow *window);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,31 @@
|
||||
|
||||
#include <wolv/utils/string.hpp>
|
||||
#include <wolv/utils/preproc.hpp>
|
||||
#include <wolv/utils/guards.hpp>
|
||||
|
||||
namespace {
|
||||
struct PluginFunctionHelperInstantiation {};
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
struct PluginFeatureFunctionHelper {
|
||||
static void* getFeatures();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct PluginSubCommandsFunctionHelper {
|
||||
static void* getSubCommands();
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
void* PluginFeatureFunctionHelper<T>::getFeatures() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void* PluginSubCommandsFunctionHelper<T>::getSubCommands() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if defined (IMHEX_STATIC_LINK_PLUGINS)
|
||||
#define IMHEX_PLUGIN_VISIBILITY_PREFIX static
|
||||
@@ -23,57 +48,65 @@
|
||||
* Name, Author and Description will be displayed in the in the plugin list on the Welcome screen.
|
||||
*/
|
||||
#define IMHEX_PLUGIN_SETUP(name, author, description) IMHEX_PLUGIN_SETUP_IMPL(name, author, description)
|
||||
#define IMHEX_LIBRARY_SETUP() IMHEX_LIBRARY_SETUP_IMPL()
|
||||
#define IMHEX_LIBRARY_SETUP(name) IMHEX_LIBRARY_SETUP_IMPL(name)
|
||||
|
||||
#define IMHEX_LIBRARY_SETUP_IMPL() \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializeLibrary(); \
|
||||
static auto WOLV_TOKEN_CONCAT(libraryInitializer_, IMHEX_PLUGIN_NAME) = [] { \
|
||||
initializeLibrary(); \
|
||||
hex::log::info("Library plugin '{}' initialized successfully", WOLV_STRINGIFY(IMHEX_PLUGIN_NAME)); \
|
||||
return 0; \
|
||||
}(); \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *ctx) { \
|
||||
ImGui::SetCurrentContext(ctx); \
|
||||
GImGui = ctx; \
|
||||
} \
|
||||
extern "C" [[gnu::visibility("default")]] void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \
|
||||
hex::PluginManager::addPlugin(hex::PluginFunctions { \
|
||||
nullptr, \
|
||||
initializeLibrary, \
|
||||
nullptr, \
|
||||
nullptr, \
|
||||
nullptr, \
|
||||
nullptr, \
|
||||
setImGuiContext, \
|
||||
nullptr, \
|
||||
nullptr \
|
||||
}); \
|
||||
} \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializeLibrary()
|
||||
#define IMHEX_LIBRARY_SETUP_IMPL(name) \
|
||||
namespace { static struct EXIT_HANDLER { ~EXIT_HANDLER() { hex::log::debug("Unloaded library '{}'", name); } } HANDLER; } \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME)(); \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *WOLV_TOKEN_CONCAT(getLibraryName_, IMHEX_PLUGIN_NAME)() { return name; } \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(setImGuiContext_, IMHEX_PLUGIN_NAME)(ImGuiContext *ctx) { \
|
||||
ImGui::SetCurrentContext(ctx); \
|
||||
GImGui = ctx; \
|
||||
} \
|
||||
extern "C" [[gnu::visibility("default")]] void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \
|
||||
hex::PluginManager::addPlugin(name, hex::PluginFunctions { \
|
||||
nullptr, \
|
||||
WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME), \
|
||||
nullptr, \
|
||||
WOLV_TOKEN_CONCAT(getLibraryName_, IMHEX_PLUGIN_NAME), \
|
||||
nullptr, \
|
||||
nullptr, \
|
||||
nullptr, \
|
||||
WOLV_TOKEN_CONCAT(setImGuiContext_, IMHEX_PLUGIN_NAME), \
|
||||
nullptr, \
|
||||
nullptr, \
|
||||
nullptr \
|
||||
}); \
|
||||
} \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void WOLV_TOKEN_CONCAT(initializeLibrary_, IMHEX_PLUGIN_NAME)()
|
||||
|
||||
#define IMHEX_PLUGIN_SETUP_IMPL(name, author, description) \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginName() { return name; } \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginAuthor() { return author; } \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginDescription() { return description; } \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getCompatibleVersion() { return IMHEX_VERSION; } \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *ctx) { \
|
||||
ImGui::SetCurrentContext(ctx); \
|
||||
GImGui = ctx; \
|
||||
} \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin(); \
|
||||
extern "C" [[gnu::visibility("default")]] void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \
|
||||
hex::PluginManager::addPlugin(hex::PluginFunctions { \
|
||||
initializePlugin, \
|
||||
nullptr, \
|
||||
getPluginName, \
|
||||
getPluginAuthor, \
|
||||
getPluginDescription, \
|
||||
getCompatibleVersion, \
|
||||
setImGuiContext, \
|
||||
nullptr, \
|
||||
nullptr \
|
||||
}); \
|
||||
} \
|
||||
#define IMHEX_PLUGIN_SETUP_IMPL(name, author, description) \
|
||||
namespace { static struct EXIT_HANDLER { ~EXIT_HANDLER() { hex::log::debug("Unloaded plugin '{}'", name); } } HANDLER; } \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginName() { return name; } \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginAuthor() { return author; } \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getPluginDescription() { return description; } \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX const char *getCompatibleVersion() { return IMHEX_VERSION; } \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void setImGuiContext(ImGuiContext *ctx) { \
|
||||
ImGui::SetCurrentContext(ctx); \
|
||||
GImGui = ctx; \
|
||||
} \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void* getFeatures() { \
|
||||
return PluginFeatureFunctionHelper<PluginFunctionHelperInstantiation>::getFeatures(); \
|
||||
} \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void* getSubCommands() { \
|
||||
return PluginSubCommandsFunctionHelper<PluginFunctionHelperInstantiation>::getSubCommands(); \
|
||||
} \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin(); \
|
||||
extern "C" [[gnu::visibility("default")]] void WOLV_TOKEN_CONCAT(forceLinkPlugin_, IMHEX_PLUGIN_NAME)() { \
|
||||
hex::PluginManager::addPlugin(name, hex::PluginFunctions { \
|
||||
initializePlugin, \
|
||||
nullptr, \
|
||||
getPluginName, \
|
||||
nullptr, \
|
||||
getPluginAuthor, \
|
||||
getPluginDescription, \
|
||||
getCompatibleVersion, \
|
||||
setImGuiContext, \
|
||||
nullptr, \
|
||||
getSubCommands, \
|
||||
getFeatures \
|
||||
}); \
|
||||
} \
|
||||
IMHEX_PLUGIN_VISIBILITY_PREFIX void initializePlugin()
|
||||
|
||||
/**
|
||||
@@ -86,18 +119,28 @@
|
||||
*/
|
||||
#define IMHEX_PLUGIN_SUBCOMMANDS() IMHEX_PLUGIN_SUBCOMMANDS_IMPL()
|
||||
|
||||
#define IMHEX_PLUGIN_SUBCOMMANDS_IMPL() \
|
||||
extern std::vector<hex::SubCommand> g_subCommands; \
|
||||
extern "C" [[gnu::visibility("default")]] void* getSubCommands() { \
|
||||
return &g_subCommands; \
|
||||
} \
|
||||
#define IMHEX_PLUGIN_SUBCOMMANDS_IMPL() \
|
||||
extern std::vector<hex::SubCommand> g_subCommands; \
|
||||
template<> \
|
||||
struct PluginSubCommandsFunctionHelper<PluginFunctionHelperInstantiation> { \
|
||||
static void* getSubCommands(); \
|
||||
}; \
|
||||
void* PluginSubCommandsFunctionHelper<PluginFunctionHelperInstantiation>::getSubCommands() { \
|
||||
return &g_subCommands; \
|
||||
} \
|
||||
std::vector<hex::SubCommand> g_subCommands
|
||||
|
||||
#define IMHEX_FEATURE_ENABLED(feature) WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(WOLV_TOKEN_CONCAT(IMHEX_PLUGIN_, IMHEX_PLUGIN_NAME), _FEATURE_), feature)
|
||||
#define IMHEX_PLUGIN_FEATURES() IMHEX_PLUGIN_FEATURES_IMPL()
|
||||
#define IMHEX_PLUGIN_FEATURES_IMPL() \
|
||||
extern std::vector<hex::Feature> g_features; \
|
||||
extern "C" [[gnu::visibility("default")]] void* getFeatures() { \
|
||||
return &g_features; \
|
||||
} \
|
||||
#define IMHEX_DEFINE_PLUGIN_FEATURES() IMHEX_DEFINE_PLUGIN_FEATURES_IMPL()
|
||||
#define IMHEX_DEFINE_PLUGIN_FEATURES_IMPL() \
|
||||
extern std::vector<hex::Feature> g_features; \
|
||||
template<> \
|
||||
struct PluginFeatureFunctionHelper<PluginFunctionHelperInstantiation> { \
|
||||
static void* getFeatures(); \
|
||||
}; \
|
||||
void* PluginFeatureFunctionHelper<PluginFunctionHelperInstantiation>::getFeatures() { \
|
||||
return &g_features; \
|
||||
} \
|
||||
std::vector<hex::Feature> g_features
|
||||
|
||||
#define IMHEX_PLUGIN_FEATURES g_features
|
||||
|
||||
@@ -11,9 +11,15 @@ namespace hex::prv {
|
||||
class MemoryProvider : public hex::prv::Provider {
|
||||
public:
|
||||
MemoryProvider() = default;
|
||||
explicit MemoryProvider(std::vector<u8> data) : m_data(std::move(data)) { }
|
||||
explicit MemoryProvider(std::vector<u8> data, std::string name = "") : m_data(std::move(data)), m_name(std::move(name)) { }
|
||||
~MemoryProvider() override = default;
|
||||
|
||||
MemoryProvider(const MemoryProvider&) = delete;
|
||||
MemoryProvider& operator=(const MemoryProvider&) = delete;
|
||||
|
||||
MemoryProvider(MemoryProvider &&provider) noexcept = default;
|
||||
MemoryProvider& operator=(MemoryProvider &&provider) noexcept = default;
|
||||
|
||||
[[nodiscard]] bool isAvailable() const override { return true; }
|
||||
[[nodiscard]] bool isReadable() const override { return true; }
|
||||
[[nodiscard]] bool isWritable() const override { return true; }
|
||||
@@ -29,10 +35,8 @@ namespace hex::prv {
|
||||
[[nodiscard]] u64 getActualSize() const override { return m_data.size(); }
|
||||
|
||||
void resizeRaw(u64 newSize) override;
|
||||
void insertRaw(u64 offset, u64 size) override;
|
||||
void removeRaw(u64 offset, u64 size) override;
|
||||
|
||||
[[nodiscard]] std::string getName() const override { return ""; }
|
||||
[[nodiscard]] std::string getName() const override { return m_name; }
|
||||
|
||||
[[nodiscard]] std::string getTypeName() const override { return "MemoryProvider"; }
|
||||
private:
|
||||
|
||||
@@ -37,6 +37,11 @@ namespace hex::prv {
|
||||
|
||||
Provider();
|
||||
virtual ~Provider();
|
||||
Provider(const Provider&) = delete;
|
||||
Provider& operator=(const Provider&) = delete;
|
||||
|
||||
Provider(Provider &&provider) noexcept = default;
|
||||
Provider& operator=(Provider &&provider) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief Opens this provider
|
||||
@@ -143,7 +148,7 @@ namespace hex::prv {
|
||||
* @brief Gets the type name of this provider
|
||||
* @note This is mainly used to be stored in project files and recents to be able to later on
|
||||
* recreate this exact provider type. This needs to be unique across all providers, this is usually something
|
||||
* like "hex.builtin.provider.memory" or "hex.builtin.provider.file"
|
||||
* like "hex.builtin.provider.mem_file" or "hex.builtin.provider.file"
|
||||
* @return The provider's type name
|
||||
*/
|
||||
[[nodiscard]] virtual std::string getTypeName() const = 0;
|
||||
@@ -156,13 +161,13 @@ namespace hex::prv {
|
||||
*/
|
||||
[[nodiscard]] virtual std::string getName() const = 0;
|
||||
|
||||
void resize(u64 newSize);
|
||||
bool resize(u64 newSize);
|
||||
void insert(u64 offset, u64 size);
|
||||
void remove(u64 offset, u64 size);
|
||||
|
||||
virtual void resizeRaw(u64 newSize) { hex::unused(newSize); }
|
||||
virtual void insertRaw(u64 offset, u64 size) { hex::unused(offset, size); }
|
||||
virtual void removeRaw(u64 offset, u64 size) { hex::unused(offset, size); }
|
||||
virtual void insertRaw(u64 offset, u64 size);
|
||||
virtual void removeRaw(u64 offset, u64 size);
|
||||
|
||||
virtual void save();
|
||||
virtual void saveAs(const std::fs::path &path);
|
||||
@@ -253,7 +258,7 @@ namespace hex::prv {
|
||||
*/
|
||||
bool m_skipLoadInterface = false;
|
||||
|
||||
std::string m_errorMessage;
|
||||
std::string m_errorMessage = "Unspecified error";
|
||||
|
||||
u64 m_pageSize = MaxPageSize;
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <hex/api/event_manager.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <ranges>
|
||||
#include <utility>
|
||||
|
||||
namespace hex {
|
||||
@@ -21,8 +22,6 @@ namespace hex {
|
||||
PerProvider& operator=(const PerProvider&) = delete;
|
||||
PerProvider& operator=(PerProvider &&) = delete;
|
||||
|
||||
PerProvider(T data) : m_data({ { ImHexApi::Provider::get(), std::move(data) } }) { this->onCreate(); }
|
||||
|
||||
~PerProvider() { this->onDestroy(); }
|
||||
|
||||
T* operator->() {
|
||||
@@ -33,19 +32,19 @@ namespace hex {
|
||||
return &this->get();
|
||||
}
|
||||
|
||||
T& get(prv::Provider *provider = ImHexApi::Provider::get()) {
|
||||
T& get(const prv::Provider *provider = ImHexApi::Provider::get()) {
|
||||
return m_data[provider];
|
||||
}
|
||||
|
||||
const T& get(prv::Provider *provider = ImHexApi::Provider::get()) const {
|
||||
const T& get(const prv::Provider *provider = ImHexApi::Provider::get()) const {
|
||||
return m_data.at(provider);
|
||||
}
|
||||
|
||||
void set(const T &data, prv::Provider *provider = ImHexApi::Provider::get()) {
|
||||
void set(const T &data, const prv::Provider *provider = ImHexApi::Provider::get()) {
|
||||
m_data[provider] = data;
|
||||
}
|
||||
|
||||
void set(T &&data, prv::Provider *provider = ImHexApi::Provider::get()) {
|
||||
void set(T &&data, const prv::Provider *provider = ImHexApi::Provider::get()) {
|
||||
m_data[provider] = std::move(data);
|
||||
}
|
||||
|
||||
@@ -71,14 +70,34 @@ namespace hex {
|
||||
return this->get();
|
||||
}
|
||||
|
||||
auto all() {
|
||||
return m_data | std::views::values;
|
||||
}
|
||||
|
||||
void setOnCreateCallback(std::function<void(prv::Provider *, T&)> callback) {
|
||||
m_onCreateCallback = std::move(callback);
|
||||
}
|
||||
|
||||
void setOnDestroyCallback(std::function<void(prv::Provider *, T&)> callback) {
|
||||
m_onDestroyCallback = std::move(callback);
|
||||
}
|
||||
|
||||
private:
|
||||
void onCreate() {
|
||||
EventProviderOpened::subscribe(this, [this](prv::Provider *provider) {
|
||||
m_data.emplace(provider, T());
|
||||
auto [it, inserted] = m_data.emplace(provider, T());
|
||||
auto &[key, value] = *it;
|
||||
if (m_onCreateCallback)
|
||||
m_onCreateCallback(provider, value);
|
||||
});
|
||||
|
||||
EventProviderDeleted::subscribe(this, [this](prv::Provider *provider){
|
||||
m_data.erase(provider);
|
||||
if (auto it = m_data.find(provider); it != m_data.end()) {
|
||||
if (m_onDestroyCallback)
|
||||
m_onDestroyCallback(provider, m_data.at(provider));
|
||||
|
||||
m_data.erase(it);
|
||||
}
|
||||
});
|
||||
|
||||
EventImHexClosing::subscribe(this, [this] {
|
||||
@@ -103,6 +122,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void onDestroy() {
|
||||
|
||||
EventProviderOpened::unsubscribe(this);
|
||||
EventProviderDeleted::unsubscribe(this);
|
||||
EventImHexClosing::unsubscribe(this);
|
||||
@@ -110,7 +130,8 @@ namespace hex {
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<prv::Provider *, T> m_data;
|
||||
std::map<const prv::Provider *, T> m_data;
|
||||
std::function<void(prv::Provider *, T&)> m_onCreateCallback, m_onDestroyCallback;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
#include <hex.hpp>
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
#include <hex/api/event_manager.hpp>
|
||||
|
||||
#include <hex/providers/undo_redo/operations/operation.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace hex::prv {
|
||||
@@ -27,13 +27,17 @@ namespace hex::prv::undo {
|
||||
|
||||
void groupOperations(u32 count, const UnlocalizedString &unlocalizedName);
|
||||
void apply(const Stack &otherStack);
|
||||
void reapply();
|
||||
|
||||
[[nodiscard]] bool canUndo() const;
|
||||
[[nodiscard]] bool canRedo() const;
|
||||
|
||||
template<std::derived_from<Operation> T>
|
||||
bool add(auto && ... args) {
|
||||
return this->add(std::make_unique<T>(std::forward<decltype(args)>(args)...));
|
||||
auto result = this->add(std::make_unique<T>(std::forward<decltype(args)>(args)...));
|
||||
EventDataChanged::post(m_provider);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool add(std::unique_ptr<Operation> &&operation);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/api/plugin_manager.hpp>
|
||||
|
||||
#include <wolv/utils/preproc.hpp>
|
||||
|
||||
@@ -28,16 +29,20 @@
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define INIT_PLUGIN(name) \
|
||||
if (!hex::test::initPluginImpl(name)) TEST_FAIL();
|
||||
|
||||
namespace hex::test {
|
||||
|
||||
using Function = int(*)();
|
||||
struct Test {
|
||||
std::function<int()> function;
|
||||
Function function;
|
||||
bool shouldFail;
|
||||
};
|
||||
|
||||
class Tests {
|
||||
public:
|
||||
static auto addTest(const std::string &name, const std::function<int()> &func, bool shouldFail) noexcept {
|
||||
static auto addTest(const std::string &name, Function func, bool shouldFail) noexcept {
|
||||
s_tests.insert({
|
||||
name, {func, shouldFail}
|
||||
});
|
||||
@@ -50,7 +55,7 @@ namespace hex::test {
|
||||
}
|
||||
|
||||
private:
|
||||
static inline std::map<std::string, Test> s_tests;
|
||||
static std::map<std::string, Test> s_tests;
|
||||
};
|
||||
|
||||
template<class F>
|
||||
@@ -86,4 +91,5 @@ namespace hex::test {
|
||||
return TestSequence<F>(executor.getName(), std::forward<F>(f), executor.shouldFail());
|
||||
}
|
||||
|
||||
bool initPluginImpl(std::string name);
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
enum ImGuiCustomCol {
|
||||
enum ImGuiCustomCol : int {
|
||||
ImGuiCustomCol_DescButton,
|
||||
ImGuiCustomCol_DescButtonHovered,
|
||||
ImGuiCustomCol_DescButtonActive,
|
||||
@@ -70,20 +70,27 @@ namespace ImGuiExt {
|
||||
|
||||
class Texture {
|
||||
public:
|
||||
enum class Filter {
|
||||
enum class Filter : int {
|
||||
Linear,
|
||||
Nearest
|
||||
};
|
||||
|
||||
Texture() = default;
|
||||
Texture(const ImU8 *buffer, int size, Filter filter = Filter::Nearest, int width = 0, int height = 0);
|
||||
Texture(std::span<const std::byte> bytes, Filter filter = Filter::Nearest, int width = 0, int height = 0);
|
||||
explicit Texture(const char *path, Filter filter = Filter::Nearest);
|
||||
explicit Texture(const std::fs::path &path, Filter filter = Filter::Nearest);
|
||||
Texture(unsigned int texture, int width, int height);
|
||||
Texture(const Texture&) = delete;
|
||||
Texture(Texture&& other) noexcept;
|
||||
|
||||
static Texture fromImage(const ImU8 *buffer, int size, Filter filter = Filter::Nearest);
|
||||
static Texture fromImage(std::span<const std::byte> buffer, Filter filter = Filter::Nearest);
|
||||
static Texture fromImage(const char *path, Filter filter = Filter::Nearest);
|
||||
static Texture fromImage(const std::fs::path &path, Filter filter = Filter::Nearest);
|
||||
static Texture fromGLTexture(unsigned int texture, int width, int height);
|
||||
static Texture fromBitmap(const ImU8 *buffer, int size, int width, int height, Filter filter = Filter::Nearest);
|
||||
static Texture fromBitmap(std::span<const std::byte> buffer, int width, int height, Filter filter = Filter::Nearest);
|
||||
static Texture fromSVG(const char *path, int width = 0, int height = 0, Filter filter = Filter::Nearest);
|
||||
static Texture fromSVG(const std::fs::path &path, int width = 0, int height = 0, Filter filter = Filter::Nearest);
|
||||
static Texture fromSVG(std::span<const std::byte> buffer, int width = 0, int height = 0, Filter filter = Filter::Nearest);
|
||||
|
||||
|
||||
~Texture();
|
||||
|
||||
Texture& operator=(const Texture&) = delete;
|
||||
@@ -124,7 +131,7 @@ namespace ImGuiExt {
|
||||
bool DescriptionButton(const char *label, const char *description, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
|
||||
bool DescriptionButtonProgress(const char *label, const char *description, float fraction, const ImVec2 &size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0);
|
||||
|
||||
void HelpHover(const char *text);
|
||||
void HelpHover(const char *text, const char *icon = "(?)", ImU32 iconColor = ImGui::GetColorU32(ImGuiCol_ButtonActive));
|
||||
|
||||
void UnderlinedText(const char *label, ImColor color = ImGui::GetStyleColorVec4(ImGuiCol_Text), const ImVec2 &size_arg = ImVec2(0, 0));
|
||||
|
||||
@@ -143,6 +150,8 @@ namespace ImGuiExt {
|
||||
bool InputHexadecimal(const char* label, u32 *value, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
|
||||
bool InputHexadecimal(const char* label, u64 *value, ImGuiInputTextFlags flags = ImGuiInputTextFlags_None);
|
||||
|
||||
bool SliderBytes(const char *label, u64 *value, u64 min, u64 max, ImGuiSliderFlags flags = ImGuiSliderFlags_None);
|
||||
|
||||
inline bool HasSecondPassed() {
|
||||
return static_cast<ImU32>(ImGui::GetTime() * 100) % 100 <= static_cast<ImU32>(ImGui::GetIO().DeltaTime * 100);
|
||||
}
|
||||
@@ -154,6 +163,7 @@ namespace ImGuiExt {
|
||||
|
||||
struct Styles {
|
||||
float WindowBlur = 0.0F;
|
||||
float PopupWindowAlpha = 0.0F; // Alpha used by Popup tool windows when the user is not hovering over them
|
||||
} styles;
|
||||
};
|
||||
|
||||
@@ -175,11 +185,11 @@ namespace ImGuiExt {
|
||||
|
||||
void SmallProgressBar(float fraction, float yOffset = 0.0F);
|
||||
|
||||
inline void TextFormatted(const std::string &fmt, auto &&...args) {
|
||||
inline void TextFormatted(std::string_view fmt, auto &&...args) {
|
||||
ImGui::TextUnformatted(hex::format(fmt, std::forward<decltype(args)>(args)...).c_str());
|
||||
}
|
||||
|
||||
inline void TextFormattedSelectable(const std::string &fmt, auto &&...args) {
|
||||
inline void TextFormattedSelectable(std::string_view fmt, auto &&...args) {
|
||||
auto text = hex::format(fmt, std::forward<decltype(args)>(args)...);
|
||||
|
||||
ImGui::PushID(text.c_str());
|
||||
@@ -187,8 +197,8 @@ namespace ImGuiExt {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4());
|
||||
|
||||
ImGui::PushItemWidth(-FLT_MIN);
|
||||
ImGui::InputText("##", const_cast<char *>(text.c_str()), text.size(), ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::PushItemWidth(ImGui::CalcTextSize(text.c_str()).x + ImGui::GetStyle().FramePadding.x * 2);
|
||||
ImGui::InputText("##", const_cast<char *>(text.c_str()), text.size(), ImGuiInputTextFlags_ReadOnly | ImGuiInputTextFlags_NoHorizontalScroll);
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
@@ -197,19 +207,19 @@ namespace ImGuiExt {
|
||||
ImGui::PopID();
|
||||
}
|
||||
|
||||
inline void TextFormattedColored(ImColor color, const std::string &fmt, auto &&...args) {
|
||||
inline void TextFormattedColored(ImColor color, std::string_view fmt, auto &&...args) {
|
||||
ImGui::TextColored(color, "%s", hex::format(fmt, std::forward<decltype(args)>(args)...).c_str());
|
||||
}
|
||||
|
||||
inline void TextFormattedDisabled(const std::string &fmt, auto &&...args) {
|
||||
inline void TextFormattedDisabled(std::string_view fmt, auto &&...args) {
|
||||
ImGui::TextDisabled("%s", hex::format(fmt, std::forward<decltype(args)>(args)...).c_str());
|
||||
}
|
||||
|
||||
inline void TextFormattedWrapped(const std::string &fmt, auto &&...args) {
|
||||
inline void TextFormattedWrapped(std::string_view fmt, auto &&...args) {
|
||||
ImGui::TextWrapped("%s", hex::format(fmt, std::forward<decltype(args)>(args)...).c_str());
|
||||
}
|
||||
|
||||
inline void TextFormattedWrappedSelectable(const std::string &fmt, auto &&...args) {
|
||||
inline void TextFormattedWrappedSelectable(std::string_view fmt, auto &&...args) {
|
||||
// Manually wrap text, using the letter M (generally the widest character in non-monospaced fonts) to calculate the character width to use.
|
||||
auto text = wolv::util::wrapMonospacedString(
|
||||
hex::format(fmt, std::forward<decltype(args)>(args)...),
|
||||
@@ -222,7 +232,7 @@ namespace ImGuiExt {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
|
||||
ImGui::PushStyleColor(ImGuiCol_FrameBg, ImVec4());
|
||||
|
||||
ImGui::PushItemWidth(-FLT_MIN);
|
||||
ImGui::PushItemWidth(ImGui::CalcTextSize(text.c_str()).x + ImGui::GetStyle().FramePadding.x * 2);
|
||||
ImGui::InputTextMultiline(
|
||||
"##",
|
||||
const_cast<char *>(text.c_str()),
|
||||
@@ -239,13 +249,13 @@ namespace ImGuiExt {
|
||||
}
|
||||
|
||||
void TextUnformattedCentered(const char *text);
|
||||
inline void TextFormattedCentered(const std::string &fmt, auto &&...args) {
|
||||
inline void TextFormattedCentered(std::string_view fmt, auto &&...args) {
|
||||
auto text = hex::format(fmt, std::forward<decltype(args)>(args)...);
|
||||
TextUnformattedCentered(text.c_str());
|
||||
}
|
||||
|
||||
|
||||
inline void TextFormattedCenteredHorizontal(const std::string &fmt, auto &&...args) {
|
||||
inline void TextFormattedCenteredHorizontal(std::string_view fmt, auto &&...args) {
|
||||
auto text = hex::format(fmt, std::forward<decltype(args)>(args)...);
|
||||
auto availableSpace = ImGui::GetContentRegionAvail();
|
||||
auto textSize = ImGui::CalcTextSize(text.c_str(), nullptr, false, availableSpace.x * 0.75F);
|
||||
@@ -297,6 +307,9 @@ namespace ImGuiExt {
|
||||
bool ToggleSwitch(const char *label, bool *v);
|
||||
bool ToggleSwitch(const char *label, bool v);
|
||||
|
||||
bool PopupTitleBarButton(const char* label, bool p_enabled);
|
||||
void PopupTitleBarText(const char* text);
|
||||
|
||||
template<typename T>
|
||||
constexpr ImGuiDataType getImGuiDataType() {
|
||||
if constexpr (std::same_as<T, u8>) return ImGuiDataType_U8;
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
#include <imgui_internal.h>
|
||||
#include <hex/ui/imgui_imhex_extensions.h>
|
||||
|
||||
#include <fonts/codicons_font.h>
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/api/shortcut_manager.hpp>
|
||||
#include <hex/api/event_manager.hpp>
|
||||
@@ -24,7 +22,7 @@
|
||||
namespace hex {
|
||||
|
||||
class View {
|
||||
explicit View(UnlocalizedString unlocalizedName);
|
||||
explicit View(UnlocalizedString unlocalizedName, const char *icon);
|
||||
public:
|
||||
virtual ~View() = default;
|
||||
|
||||
@@ -82,7 +80,9 @@ namespace hex {
|
||||
*/
|
||||
[[nodiscard]] virtual ImGuiWindowFlags getWindowFlags() const;
|
||||
|
||||
[[nodiscard]] virtual bool shouldStoreWindowState() const { return true; }
|
||||
|
||||
[[nodiscard]] const char *getIcon() const { return m_icon; }
|
||||
|
||||
[[nodiscard]] bool &getWindowOpenState();
|
||||
[[nodiscard]] const bool &getWindowOpenState() const;
|
||||
@@ -110,6 +110,7 @@ namespace hex {
|
||||
bool m_windowOpen = false, m_prevWindowOpen = false;
|
||||
std::map<Shortcut, ShortcutManager::ShortcutEntry> m_shortcuts;
|
||||
bool m_windowJustOpened = false;
|
||||
const char *m_icon;
|
||||
|
||||
friend class ShortcutManager;
|
||||
};
|
||||
@@ -120,7 +121,7 @@ namespace hex {
|
||||
*/
|
||||
class View::Window : public View {
|
||||
public:
|
||||
explicit Window(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName)) {}
|
||||
explicit Window(UnlocalizedString unlocalizedName, const char *icon) : View(std::move(unlocalizedName), icon) {}
|
||||
|
||||
void draw() final {
|
||||
if (this->shouldDraw()) {
|
||||
@@ -139,7 +140,7 @@ namespace hex {
|
||||
*/
|
||||
class View::Special : public View {
|
||||
public:
|
||||
explicit Special(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName)) {}
|
||||
explicit Special(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName), "") {}
|
||||
|
||||
void draw() final {
|
||||
if (this->shouldDraw()) {
|
||||
@@ -154,7 +155,7 @@ namespace hex {
|
||||
*/
|
||||
class View::Floating : public View::Window {
|
||||
public:
|
||||
explicit Floating(UnlocalizedString unlocalizedName) : Window(std::move(unlocalizedName)) {}
|
||||
explicit Floating(UnlocalizedString unlocalizedName) : Window(std::move(unlocalizedName), "") {}
|
||||
|
||||
[[nodiscard]] ImGuiWindowFlags getWindowFlags() const override { return ImGuiWindowFlags_NoDocking; }
|
||||
};
|
||||
@@ -164,7 +165,7 @@ namespace hex {
|
||||
*/
|
||||
class View::Modal : public View {
|
||||
public:
|
||||
explicit Modal(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName)) {}
|
||||
explicit Modal(UnlocalizedString unlocalizedName) : View(std::move(unlocalizedName), "") {}
|
||||
|
||||
void draw() final {
|
||||
if (this->shouldDraw()) {
|
||||
@@ -179,12 +180,13 @@ namespace hex {
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGui::GetKeyIndex(ImGuiKey_Escape)))
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_Escape))
|
||||
this->getWindowOpenState() = false;
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool hasCloseButton() const { return true; }
|
||||
[[nodiscard]] virtual bool hasCloseButton() const { return true; }
|
||||
[[nodiscard]] bool shouldStoreWindowState() const override { return false; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,52 +1,56 @@
|
||||
#include <hex/api/achievement_manager.hpp>
|
||||
#include <hex/api/event_manager.hpp>
|
||||
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>> &AchievementManager::getAchievements() {
|
||||
static std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>> achievements;
|
||||
|
||||
return achievements;
|
||||
static AutoReset<std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>>> s_achievements;
|
||||
const std::unordered_map<std::string, std::unordered_map<std::string, std::unique_ptr<Achievement>>> &AchievementManager::getAchievements() {
|
||||
return *s_achievements;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::list<AchievementManager::AchievementNode>>& AchievementManager::getAchievementNodes(bool rebuild) {
|
||||
static std::unordered_map<std::string, std::list<AchievementNode>> nodeCategoryStorage;
|
||||
static AutoReset<std::unordered_map<std::string, std::list<AchievementManager::AchievementNode>>> s_nodeCategoryStorage;
|
||||
std::unordered_map<std::string, std::list<AchievementManager::AchievementNode>>& getAchievementNodesMutable(bool rebuild) {
|
||||
if (!s_nodeCategoryStorage->empty() || !rebuild)
|
||||
return s_nodeCategoryStorage;
|
||||
|
||||
if (!nodeCategoryStorage.empty() || !rebuild)
|
||||
return nodeCategoryStorage;
|
||||
|
||||
nodeCategoryStorage.clear();
|
||||
s_nodeCategoryStorage->clear();
|
||||
|
||||
// Add all achievements to the node storage
|
||||
for (auto &[categoryName, achievements] : getAchievements()) {
|
||||
auto &nodes = nodeCategoryStorage[categoryName];
|
||||
for (auto &[categoryName, achievements] : AchievementManager::getAchievements()) {
|
||||
auto &nodes = (*s_nodeCategoryStorage)[categoryName];
|
||||
|
||||
for (auto &[achievementName, achievement] : achievements) {
|
||||
nodes.emplace_back(achievement.get());
|
||||
}
|
||||
}
|
||||
|
||||
return nodeCategoryStorage;
|
||||
return s_nodeCategoryStorage;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::vector<AchievementManager::AchievementNode*>>& AchievementManager::getAchievementStartNodes(bool rebuild) {
|
||||
static std::unordered_map<std::string, std::vector<AchievementNode*>> startNodes;
|
||||
const std::unordered_map<std::string, std::list<AchievementManager::AchievementNode>>& AchievementManager::getAchievementNodes(bool rebuild) {
|
||||
return getAchievementNodesMutable(rebuild);
|
||||
}
|
||||
|
||||
if (!startNodes.empty() || !rebuild)
|
||||
return startNodes;
|
||||
static AutoReset<std::unordered_map<std::string, std::vector<AchievementManager::AchievementNode*>>> s_startNodes;
|
||||
const std::unordered_map<std::string, std::vector<AchievementManager::AchievementNode*>>& AchievementManager::getAchievementStartNodes(bool rebuild) {
|
||||
|
||||
auto &nodeCategoryStorage = getAchievementNodes();
|
||||
if (!s_startNodes->empty() || !rebuild)
|
||||
return s_startNodes;
|
||||
|
||||
startNodes.clear();
|
||||
auto &nodeCategoryStorage = getAchievementNodesMutable(rebuild);
|
||||
|
||||
s_startNodes->clear();
|
||||
|
||||
// Add all parents and children to the nodes
|
||||
for (auto &[categoryName, achievements] : nodeCategoryStorage) {
|
||||
for (auto &achievementNode : achievements) {
|
||||
for (auto &requirement : achievementNode.achievement->getRequirements()) {
|
||||
for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) {
|
||||
auto iter = std::find_if(requirementAchievements.begin(), requirementAchievements.end(), [&requirement](auto &node) {
|
||||
auto iter = std::ranges::find_if(requirementAchievements, [&requirement](auto &node) {
|
||||
return node.achievement->getUnlocalizedName() == requirement;
|
||||
});
|
||||
|
||||
@@ -59,7 +63,7 @@ namespace hex {
|
||||
|
||||
for (auto &requirement : achievementNode.achievement->getVisibilityRequirements()) {
|
||||
for (auto &[requirementCategoryName, requirementAchievements] : nodeCategoryStorage) {
|
||||
auto iter = std::find_if(requirementAchievements.begin(), requirementAchievements.end(), [&requirement](auto &node) {
|
||||
auto iter = std::ranges::find_if(requirementAchievements, [&requirement](auto &node) {
|
||||
return node.achievement->getUnlocalizedName() == requirement;
|
||||
});
|
||||
|
||||
@@ -74,17 +78,17 @@ namespace hex {
|
||||
for (auto &[categoryName, achievements] : nodeCategoryStorage) {
|
||||
for (auto &achievementNode : achievements) {
|
||||
if (!achievementNode.hasParents()) {
|
||||
startNodes[categoryName].emplace_back(&achievementNode);
|
||||
(*s_startNodes)[categoryName].emplace_back(&achievementNode);
|
||||
}
|
||||
|
||||
for (const auto &parent : achievementNode.parents) {
|
||||
if (parent->achievement->getUnlocalizedCategory() != achievementNode.achievement->getUnlocalizedCategory())
|
||||
startNodes[categoryName].emplace_back(&achievementNode);
|
||||
(*s_startNodes)[categoryName].emplace_back(&achievementNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return startNodes;
|
||||
return s_startNodes;
|
||||
}
|
||||
|
||||
void AchievementManager::unlockAchievement(const UnlocalizedString &unlocalizedCategory, const UnlocalizedString &unlocalizedName) {
|
||||
@@ -97,15 +101,16 @@ namespace hex {
|
||||
|
||||
auto &[categoryName, achievements] = *categoryIter;
|
||||
|
||||
auto achievementIter = achievements.find(unlocalizedName);
|
||||
|
||||
const auto achievementIter = achievements.find(unlocalizedName);
|
||||
if (achievementIter == achievements.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto &nodes = getAchievementNodes()[categoryName];
|
||||
const auto &nodes = getAchievementNodes();
|
||||
if (!nodes.contains(categoryName))
|
||||
return;
|
||||
|
||||
for (const auto &node : nodes) {
|
||||
for (const auto &node : nodes.at(categoryName)) {
|
||||
auto &achievement = node.achievement;
|
||||
|
||||
if (achievement->getUnlocalizedCategory() != unlocalizedCategory) {
|
||||
@@ -134,14 +139,8 @@ namespace hex {
|
||||
}
|
||||
}
|
||||
|
||||
void AchievementManager::clear() {
|
||||
getAchievements().clear();
|
||||
getAchievementStartNodes(false).clear();
|
||||
getAchievementNodes(false).clear();
|
||||
}
|
||||
|
||||
void AchievementManager::clearTemporary() {
|
||||
auto &categories = getAchievements();
|
||||
auto &categories = *s_achievements;
|
||||
for (auto &[categoryName, achievements] : categories) {
|
||||
std::erase_if(achievements, [](auto &data) {
|
||||
auto &[achievementName, achievement] = data;
|
||||
@@ -154,8 +153,8 @@ namespace hex {
|
||||
return achievements.empty();
|
||||
});
|
||||
|
||||
getAchievementStartNodes(false).clear();
|
||||
getAchievementNodes(false).clear();
|
||||
s_startNodes->clear();
|
||||
s_nodeCategoryStorage->clear();
|
||||
}
|
||||
|
||||
std::pair<u32, u32> AchievementManager::getProgress() {
|
||||
@@ -175,10 +174,26 @@ namespace hex {
|
||||
}
|
||||
|
||||
void AchievementManager::achievementAdded() {
|
||||
getAchievementStartNodes(false).clear();
|
||||
getAchievementNodes(false).clear();
|
||||
s_startNodes->clear();
|
||||
s_nodeCategoryStorage->clear();
|
||||
}
|
||||
|
||||
Achievement &AchievementManager::addAchievementImpl(std::unique_ptr<Achievement> &&newAchievement) {
|
||||
const auto &category = newAchievement->getUnlocalizedCategory();
|
||||
const auto &name = newAchievement->getUnlocalizedName();
|
||||
|
||||
auto [categoryIter, categoryInserted] = s_achievements->insert({ category, std::unordered_map<std::string, std::unique_ptr<Achievement>>{} });
|
||||
auto &[categoryKey, achievements] = *categoryIter;
|
||||
|
||||
auto [achievementIter, achievementInserted] = achievements.insert({ name, std::move(newAchievement) });
|
||||
auto &[achievementKey, achievement] = *achievementIter;
|
||||
|
||||
achievementAdded();
|
||||
|
||||
return *achievement;
|
||||
}
|
||||
|
||||
|
||||
constexpr static auto AchievementsFile = "achievements.json";
|
||||
|
||||
void AchievementManager::loadProgress() {
|
||||
@@ -219,28 +234,26 @@ namespace hex {
|
||||
}
|
||||
|
||||
void AchievementManager::storeProgress() {
|
||||
nlohmann::json json;
|
||||
for (const auto &[categoryName, achievements] : getAchievements()) {
|
||||
json[categoryName] = nlohmann::json::object();
|
||||
|
||||
for (const auto &[achievementName, achievement] : achievements) {
|
||||
json[categoryName][achievementName] = achievement->getProgress();
|
||||
}
|
||||
}
|
||||
|
||||
if (json.empty())
|
||||
return;
|
||||
|
||||
for (const auto &directory : fs::getDefaultPaths(fs::ImHexPath::Config)) {
|
||||
auto path = directory / AchievementsFile;
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Write);
|
||||
|
||||
if (!file.isValid()) {
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
if (!file.isValid())
|
||||
continue;
|
||||
}
|
||||
|
||||
nlohmann::json json;
|
||||
|
||||
for (const auto &[categoryName, achievements] : getAchievements()) {
|
||||
json[categoryName] = nlohmann::json::object();
|
||||
|
||||
for (const auto &[achievementName, achievement] : achievements) {
|
||||
json[categoryName][achievementName] = achievement->getProgress();
|
||||
}
|
||||
}
|
||||
|
||||
auto result = json.dump(4);
|
||||
file.setSize(0);
|
||||
file.writeString(result);
|
||||
file.writeString(json.dump(4));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,23 +2,26 @@
|
||||
|
||||
#include <hex/api/event_manager.hpp>
|
||||
#include <hex/api/task_manager.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
#include <hex/providers/provider_data.hpp>
|
||||
#include <hex/providers/provider.hpp>
|
||||
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
#include <utility>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <set>
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <sys/utsname.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
namespace hex {
|
||||
@@ -36,39 +39,56 @@ namespace hex {
|
||||
|
||||
namespace impl {
|
||||
|
||||
static std::map<u32, Highlighting> s_backgroundHighlights;
|
||||
std::map<u32, Highlighting> &getBackgroundHighlights() {
|
||||
return s_backgroundHighlights;
|
||||
static AutoReset<std::map<u32, Highlighting>> s_backgroundHighlights;
|
||||
const std::map<u32, Highlighting>& getBackgroundHighlights() {
|
||||
return *s_backgroundHighlights;
|
||||
}
|
||||
|
||||
static std::map<u32, HighlightingFunction> s_backgroundHighlightingFunctions;
|
||||
std::map<u32, HighlightingFunction> &getBackgroundHighlightingFunctions() {
|
||||
return s_backgroundHighlightingFunctions;
|
||||
static AutoReset<std::map<u32, HighlightingFunction>> s_backgroundHighlightingFunctions;
|
||||
const std::map<u32, HighlightingFunction>& getBackgroundHighlightingFunctions() {
|
||||
return *s_backgroundHighlightingFunctions;
|
||||
}
|
||||
|
||||
static std::map<u32, Highlighting> s_foregroundHighlights;
|
||||
std::map<u32, Highlighting> &getForegroundHighlights() {
|
||||
return s_foregroundHighlights;
|
||||
static AutoReset<std::map<u32, Highlighting>> s_foregroundHighlights;
|
||||
const std::map<u32, Highlighting>& getForegroundHighlights() {
|
||||
return *s_foregroundHighlights;
|
||||
}
|
||||
|
||||
static std::map<u32, HighlightingFunction> s_foregroundHighlightingFunctions;
|
||||
std::map<u32, HighlightingFunction> &getForegroundHighlightingFunctions() {
|
||||
return s_foregroundHighlightingFunctions;
|
||||
static AutoReset<std::map<u32, HighlightingFunction>> s_foregroundHighlightingFunctions;
|
||||
const std::map<u32, HighlightingFunction>& getForegroundHighlightingFunctions() {
|
||||
return *s_foregroundHighlightingFunctions;
|
||||
}
|
||||
|
||||
static std::map<u32, Tooltip> s_tooltips;
|
||||
std::map<u32, Tooltip> &getTooltips() {
|
||||
return s_tooltips;
|
||||
static AutoReset<std::map<u32, Tooltip>> s_tooltips;
|
||||
const std::map<u32, Tooltip>& getTooltips() {
|
||||
return *s_tooltips;
|
||||
}
|
||||
|
||||
static std::map<u32, TooltipFunction> s_tooltipFunctions;
|
||||
std::map<u32, TooltipFunction> &getTooltipFunctions() {
|
||||
return s_tooltipFunctions;
|
||||
static AutoReset<std::map<u32, TooltipFunction>> s_tooltipFunctions;
|
||||
const std::map<u32, TooltipFunction>& getTooltipFunctions() {
|
||||
return *s_tooltipFunctions;
|
||||
}
|
||||
|
||||
static std::optional<ProviderRegion> s_currentSelection;
|
||||
static AutoReset<std::map<u32, HoveringFunction>> s_hoveringFunctions;
|
||||
const std::map<u32, HoveringFunction>& getHoveringFunctions() {
|
||||
return *s_hoveringFunctions;
|
||||
}
|
||||
|
||||
static AutoReset<std::optional<ProviderRegion>> s_currentSelection;
|
||||
void setCurrentSelection(const std::optional<ProviderRegion> ®ion) {
|
||||
s_currentSelection = region;
|
||||
if (region == Region::Invalid()) {
|
||||
clearSelection();
|
||||
} else {
|
||||
*s_currentSelection = region;
|
||||
}
|
||||
}
|
||||
|
||||
static PerProvider<std::optional<Region>> s_hoveredRegion;
|
||||
void setHoveredRegion(const prv::Provider *provider, const Region ®ion) {
|
||||
if (region == Region::Invalid())
|
||||
s_hoveredRegion.get(provider).reset();
|
||||
else
|
||||
s_hoveredRegion.get(provider) = region;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -78,19 +98,19 @@ namespace hex {
|
||||
|
||||
id++;
|
||||
|
||||
impl::getBackgroundHighlights().insert({
|
||||
id, Highlighting {region, color}
|
||||
impl::s_backgroundHighlights->insert({
|
||||
id, Highlighting { region, color }
|
||||
});
|
||||
|
||||
EventHighlightingChanged::post();
|
||||
TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void removeBackgroundHighlight(u32 id) {
|
||||
impl::getBackgroundHighlights().erase(id);
|
||||
impl::s_backgroundHighlights->erase(id);
|
||||
|
||||
EventHighlightingChanged::post();
|
||||
TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
|
||||
}
|
||||
|
||||
u32 addBackgroundHighlightingProvider(const impl::HighlightingFunction &function) {
|
||||
@@ -98,17 +118,17 @@ namespace hex {
|
||||
|
||||
id++;
|
||||
|
||||
impl::getBackgroundHighlightingFunctions().insert({ id, function });
|
||||
impl::s_backgroundHighlightingFunctions->insert({ id, function });
|
||||
|
||||
EventHighlightingChanged::post();
|
||||
TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void removeBackgroundHighlightingProvider(u32 id) {
|
||||
impl::getBackgroundHighlightingFunctions().erase(id);
|
||||
impl::s_backgroundHighlightingFunctions->erase(id);
|
||||
|
||||
EventHighlightingChanged::post();
|
||||
TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
|
||||
}
|
||||
|
||||
u32 addForegroundHighlight(const Region ®ion, color_t color) {
|
||||
@@ -116,19 +136,19 @@ namespace hex {
|
||||
|
||||
id++;
|
||||
|
||||
impl::getForegroundHighlights().insert({
|
||||
id, Highlighting {region, color}
|
||||
impl::s_foregroundHighlights->insert({
|
||||
id, Highlighting { region, color }
|
||||
});
|
||||
|
||||
EventHighlightingChanged::post();
|
||||
TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void removeForegroundHighlight(u32 id) {
|
||||
impl::getForegroundHighlights().erase(id);
|
||||
impl::s_foregroundHighlights->erase(id);
|
||||
|
||||
EventHighlightingChanged::post();
|
||||
TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
|
||||
}
|
||||
|
||||
u32 addForegroundHighlightingProvider(const impl::HighlightingFunction &function) {
|
||||
@@ -136,45 +156,60 @@ namespace hex {
|
||||
|
||||
id++;
|
||||
|
||||
impl::getForegroundHighlightingFunctions().insert({ id, function });
|
||||
impl::s_foregroundHighlightingFunctions->insert({ id, function });
|
||||
|
||||
EventHighlightingChanged::post();
|
||||
TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void removeForegroundHighlightingProvider(u32 id) {
|
||||
impl::getForegroundHighlightingFunctions().erase(id);
|
||||
impl::s_foregroundHighlightingFunctions->erase(id);
|
||||
|
||||
EventHighlightingChanged::post();
|
||||
TaskManager::doLaterOnce([]{ EventHighlightingChanged::post(); });
|
||||
}
|
||||
|
||||
u32 addHoverHighlightProvider(const impl::HoveringFunction &function) {
|
||||
static u32 id = 0;
|
||||
|
||||
id++;
|
||||
|
||||
impl::s_hoveringFunctions->insert({ id, function });
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void removeHoverHighlightProvider(u32 id) {
|
||||
impl::s_hoveringFunctions->erase(id);
|
||||
}
|
||||
|
||||
static u32 tooltipId = 0;
|
||||
u32 addTooltip(Region region, std::string value, color_t color) {
|
||||
tooltipId++;
|
||||
impl::getTooltips().insert({ tooltipId, { region, std::move(value), color } });
|
||||
impl::s_tooltips->insert({ tooltipId, { region, std::move(value), color } });
|
||||
|
||||
return tooltipId;
|
||||
}
|
||||
|
||||
void removeTooltip(u32 id) {
|
||||
impl::getTooltips().erase(id);
|
||||
impl::s_tooltips->erase(id);
|
||||
}
|
||||
|
||||
static u32 tooltipFunctionId;
|
||||
u32 addTooltipProvider(TooltipFunction function) {
|
||||
tooltipFunctionId++;
|
||||
impl::getTooltipFunctions().insert({ tooltipFunctionId, std::move(function) });
|
||||
impl::s_tooltipFunctions->insert({ tooltipFunctionId, std::move(function) });
|
||||
|
||||
return tooltipFunctionId;
|
||||
}
|
||||
|
||||
void removeTooltipProvider(u32 id) {
|
||||
impl::getTooltipFunctions().erase(id);
|
||||
impl::s_tooltipFunctions->erase(id);
|
||||
}
|
||||
|
||||
bool isSelectionValid() {
|
||||
return getSelection().has_value();
|
||||
auto selection = getSelection();
|
||||
return selection.has_value() && selection->provider != nullptr;
|
||||
}
|
||||
|
||||
std::optional<ProviderRegion> getSelection() {
|
||||
@@ -182,7 +217,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void clearSelection() {
|
||||
impl::s_currentSelection.reset();
|
||||
impl::s_currentSelection->reset();
|
||||
}
|
||||
|
||||
void setSelection(const Region ®ion, prv::Provider *provider) {
|
||||
@@ -190,13 +225,21 @@ namespace hex {
|
||||
}
|
||||
|
||||
void setSelection(const ProviderRegion ®ion) {
|
||||
RequestSelectionChange::post(region);
|
||||
RequestHexEditorSelectionChange::post(region);
|
||||
}
|
||||
|
||||
void setSelection(u64 address, size_t size, prv::Provider *provider) {
|
||||
setSelection({ { address, size }, provider == nullptr ? Provider::get() : provider });
|
||||
}
|
||||
|
||||
void addVirtualFile(const std::fs::path &path, std::vector<u8> data, Region region) {
|
||||
RequestAddVirtualFile::post(path, std::move(data), region);
|
||||
}
|
||||
|
||||
const std::optional<Region>& getHoveredRegion(const prv::Provider *provider) {
|
||||
return impl::s_hoveredRegion.get(provider);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -223,41 +266,64 @@ namespace hex {
|
||||
namespace ImHexApi::Provider {
|
||||
|
||||
static i64 s_currentProvider = -1;
|
||||
static std::vector<prv::Provider *> s_providers;
|
||||
static AutoReset<std::vector<std::unique_ptr<prv::Provider>>> s_providers;
|
||||
|
||||
namespace impl {
|
||||
|
||||
static std::vector<prv::Provider*> s_closingProviders;
|
||||
static std::set<prv::Provider*> s_closingProviders;
|
||||
void resetClosingProvider() {
|
||||
s_closingProviders.clear();
|
||||
}
|
||||
|
||||
const std::vector<prv::Provider*>& getClosingProviders() {
|
||||
const std::set<prv::Provider*>& getClosingProviders() {
|
||||
return s_closingProviders;
|
||||
}
|
||||
|
||||
static std::recursive_mutex s_providerMutex;
|
||||
}
|
||||
|
||||
prv::Provider *get() {
|
||||
if (!ImHexApi::Provider::isValid())
|
||||
return nullptr;
|
||||
|
||||
return s_providers[s_currentProvider];
|
||||
return (*s_providers)[s_currentProvider].get();
|
||||
}
|
||||
|
||||
const std::vector<prv::Provider *> &getProviders() {
|
||||
return s_providers;
|
||||
std::vector<prv::Provider*> getProviders() {
|
||||
std::vector<prv::Provider*> result;
|
||||
result.reserve(s_providers->size());
|
||||
for (const auto &provider : *s_providers)
|
||||
result.push_back(provider.get());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void setCurrentProvider(u32 index) {
|
||||
void setCurrentProvider(i64 index) {
|
||||
std::scoped_lock lock(impl::s_providerMutex);
|
||||
|
||||
if (TaskManager::getRunningTaskCount() > 0)
|
||||
return;
|
||||
|
||||
if (index < s_providers.size() && s_currentProvider != index) {
|
||||
if (std::cmp_less(index, s_providers->size()) && s_currentProvider != index) {
|
||||
auto oldProvider = get();
|
||||
s_currentProvider = index;
|
||||
EventProviderChanged::post(oldProvider, get());
|
||||
}
|
||||
|
||||
RequestUpdateWindowTitle::post();
|
||||
}
|
||||
|
||||
void setCurrentProvider(NonNull<prv::Provider*> provider) {
|
||||
std::scoped_lock lock(impl::s_providerMutex);
|
||||
|
||||
if (TaskManager::getRunningTaskCount() > 0)
|
||||
return;
|
||||
|
||||
const auto providers = getProviders();
|
||||
auto it = std::ranges::find(providers, provider.get());
|
||||
|
||||
auto index = std::distance(providers.begin(), it);
|
||||
setCurrentProvider(index);
|
||||
}
|
||||
|
||||
i64 getCurrentProviderIndex() {
|
||||
@@ -265,7 +331,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
bool isValid() {
|
||||
return !s_providers.empty() && s_currentProvider >= 0 && s_currentProvider < i64(s_providers.size());
|
||||
return !s_providers->empty() && s_currentProvider >= 0 && s_currentProvider < i64(s_providers->size());
|
||||
}
|
||||
|
||||
void markDirty() {
|
||||
@@ -273,31 +339,35 @@ namespace hex {
|
||||
}
|
||||
|
||||
void resetDirty() {
|
||||
for (auto &provider : s_providers)
|
||||
for (const auto &provider : *s_providers)
|
||||
provider->markDirty(false);
|
||||
}
|
||||
|
||||
bool isDirty() {
|
||||
return std::ranges::any_of(s_providers, [](const auto &provider) {
|
||||
return std::ranges::any_of(*s_providers, [](const auto &provider) {
|
||||
return provider->isDirty();
|
||||
});
|
||||
}
|
||||
|
||||
void add(prv::Provider *provider, bool skipLoadInterface, bool select) {
|
||||
void add(std::unique_ptr<prv::Provider> &&provider, bool skipLoadInterface, bool select) {
|
||||
std::scoped_lock lock(impl::s_providerMutex);
|
||||
|
||||
if (TaskManager::getRunningTaskCount() > 0)
|
||||
return;
|
||||
|
||||
if (skipLoadInterface)
|
||||
provider->skipLoadInterface();
|
||||
|
||||
s_providers.push_back(provider);
|
||||
EventProviderCreated::post(provider);
|
||||
EventProviderCreated::post(provider.get());
|
||||
s_providers->emplace_back(std::move(provider));
|
||||
|
||||
if (select || s_providers.size() == 1)
|
||||
setCurrentProvider(s_providers.size() - 1);
|
||||
if (select || s_providers->size() == 1)
|
||||
setCurrentProvider(s_providers->size() - 1);
|
||||
}
|
||||
|
||||
void remove(prv::Provider *provider, bool noQuestions) {
|
||||
std::scoped_lock lock(impl::s_providerMutex);
|
||||
|
||||
if (provider == nullptr)
|
||||
return;
|
||||
|
||||
@@ -305,7 +375,7 @@ namespace hex {
|
||||
return;
|
||||
|
||||
if (!noQuestions) {
|
||||
impl::s_closingProviders.push_back(provider);
|
||||
impl::s_closingProviders.insert(provider);
|
||||
|
||||
bool shouldClose = true;
|
||||
EventProviderClosing::post(provider, &shouldClose);
|
||||
@@ -313,31 +383,36 @@ namespace hex {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = std::find(s_providers.begin(), s_providers.end(), provider);
|
||||
if (it == s_providers.end())
|
||||
const auto it = std::ranges::find_if(*s_providers, [provider](const auto &p) {
|
||||
return p.get() == provider;
|
||||
});
|
||||
|
||||
if (it == s_providers->end())
|
||||
return;
|
||||
|
||||
if (!s_providers.empty()) {
|
||||
if (it == s_providers.begin()) {
|
||||
if (!s_providers->empty()) {
|
||||
if (it == s_providers->begin()) {
|
||||
// If the first provider is being closed, select the one that's the first one now
|
||||
setCurrentProvider(0);
|
||||
|
||||
if (s_providers.size() > 1)
|
||||
EventProviderChanged::post(s_providers[0], s_providers[1]);
|
||||
if (s_providers->size() > 1)
|
||||
EventProviderChanged::post(s_providers->at(0).get(), s_providers->at(1).get());
|
||||
}
|
||||
else if (std::distance(s_providers.begin(), it) == s_currentProvider) {
|
||||
else if (std::distance(s_providers->begin(), it) == s_currentProvider) {
|
||||
// If the current provider is being closed, select the one that's before it
|
||||
setCurrentProvider(s_currentProvider - 1);
|
||||
}
|
||||
else {
|
||||
// If any other provider is being closed, find the current provider in the list again and select it again
|
||||
auto currentProvider = get();
|
||||
auto currentIt = std::find(s_providers.begin(), s_providers.end(), currentProvider);
|
||||
const auto currentProvider = get();
|
||||
const auto currentIt = std::ranges::find_if(*s_providers, [currentProvider](const auto &p) {
|
||||
return p.get() == currentProvider;
|
||||
});
|
||||
|
||||
if (currentIt != s_providers.end()) {
|
||||
auto newIndex = std::distance(s_providers.begin(), currentIt);
|
||||
if (currentIt != s_providers->end()) {
|
||||
auto newIndex = std::distance(s_providers->begin(), currentIt);
|
||||
|
||||
if (s_currentProvider == newIndex)
|
||||
if (s_currentProvider == newIndex && newIndex != 0)
|
||||
newIndex -= 1;
|
||||
|
||||
setCurrentProvider(newIndex);
|
||||
@@ -348,20 +423,20 @@ namespace hex {
|
||||
}
|
||||
}
|
||||
|
||||
s_providers.erase(it);
|
||||
if (s_currentProvider >= i64(s_providers.size()))
|
||||
setCurrentProvider(0);
|
||||
|
||||
if (s_providers.empty())
|
||||
EventProviderChanged::post(provider, nullptr);
|
||||
|
||||
provider->close();
|
||||
EventProviderClosed::post(provider);
|
||||
RequestUpdateWindowTitle::post();
|
||||
|
||||
TaskManager::runWhenTasksFinished([provider] {
|
||||
TaskManager::runWhenTasksFinished([it, provider] {
|
||||
EventProviderDeleted::post(provider);
|
||||
std::erase(impl::s_closingProviders, provider);
|
||||
delete provider;
|
||||
impl::s_closingProviders.erase(provider);
|
||||
|
||||
s_providers->erase(it);
|
||||
if (s_currentProvider >= i64(s_providers->size()))
|
||||
setCurrentProvider(0);
|
||||
|
||||
if (s_providers->empty())
|
||||
EventProviderChanged::post(provider, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -376,7 +451,6 @@ namespace hex {
|
||||
|
||||
namespace ImHexApi::System {
|
||||
|
||||
|
||||
namespace impl {
|
||||
|
||||
// Default to true means we forward to ourselves by default
|
||||
@@ -388,11 +462,11 @@ namespace hex {
|
||||
static ImVec2 s_mainWindowPos;
|
||||
static ImVec2 s_mainWindowSize;
|
||||
void setMainWindowPosition(i32 x, i32 y) {
|
||||
s_mainWindowPos = ImVec2(x, y);
|
||||
s_mainWindowPos = ImVec2(float(x), float(y));
|
||||
}
|
||||
|
||||
void setMainWindowSize(u32 width, u32 height) {
|
||||
s_mainWindowSize = ImVec2(width, height);
|
||||
s_mainWindowSize = ImVec2(float(width), float(height));
|
||||
}
|
||||
|
||||
static ImGuiID s_mainDockSpaceId;
|
||||
@@ -400,6 +474,11 @@ namespace hex {
|
||||
s_mainDockSpaceId = id;
|
||||
}
|
||||
|
||||
static GLFWwindow *s_mainWindowHandle;
|
||||
void setMainWindowHandle(GLFWwindow *window) {
|
||||
s_mainWindowHandle = window;
|
||||
}
|
||||
|
||||
|
||||
static float s_globalScale = 1.0;
|
||||
void setGlobalScale(float scale) {
|
||||
@@ -417,22 +496,28 @@ namespace hex {
|
||||
s_borderlessWindowMode = enabled;
|
||||
}
|
||||
|
||||
static bool s_multiWindowMode = false;
|
||||
void setMultiWindowMode(bool enabled) {
|
||||
s_multiWindowMode = enabled;
|
||||
}
|
||||
|
||||
static std::string s_gpuVendor;
|
||||
static std::optional<InitialWindowProperties> s_initialWindowProperties;
|
||||
void setInitialWindowProperties(InitialWindowProperties properties) {
|
||||
s_initialWindowProperties = properties;
|
||||
}
|
||||
|
||||
|
||||
static AutoReset<std::string> s_gpuVendor;
|
||||
void setGPUVendor(const std::string &vendor) {
|
||||
s_gpuVendor = vendor;
|
||||
}
|
||||
|
||||
static bool s_portableVersion = false;
|
||||
void setPortableVersion(bool enabled) {
|
||||
s_portableVersion = enabled;
|
||||
}
|
||||
|
||||
static AutoReset<std::map<std::string, std::string>> s_initArguments;
|
||||
void addInitArgument(const std::string &key, const std::string &value) {
|
||||
static std::mutex initArgumentsMutex;
|
||||
std::scoped_lock lock(initArgumentsMutex);
|
||||
|
||||
getInitArguments()[key] = value;
|
||||
(*s_initArguments)[key] = value;
|
||||
}
|
||||
|
||||
static double s_lastFrameTime;
|
||||
@@ -440,6 +525,21 @@ namespace hex {
|
||||
s_lastFrameTime = time;
|
||||
}
|
||||
|
||||
static bool s_windowResizable = true;
|
||||
bool isWindowResizable() {
|
||||
return s_windowResizable;
|
||||
}
|
||||
|
||||
static std::vector<hex::impl::AutoResetBase*> s_autoResetObjects;
|
||||
void addAutoResetObject(hex::impl::AutoResetBase *object) {
|
||||
s_autoResetObjects.emplace_back(object);
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
for (const auto &object : s_autoResetObjects)
|
||||
object->reset();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -496,22 +596,44 @@ namespace hex {
|
||||
return impl::s_mainDockSpaceId;
|
||||
}
|
||||
|
||||
GLFWwindow* getMainWindowHandle() {
|
||||
return impl::s_mainWindowHandle;
|
||||
}
|
||||
|
||||
bool isBorderlessWindowModeEnabled() {
|
||||
return impl::s_borderlessWindowMode;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> &getInitArguments() {
|
||||
static std::map<std::string, std::string> initArgs;
|
||||
|
||||
return initArgs;
|
||||
bool isMutliWindowModeEnabled() {
|
||||
return impl::s_multiWindowMode;
|
||||
}
|
||||
|
||||
std::optional<InitialWindowProperties> getInitialWindowProperties() {
|
||||
return impl::s_initialWindowProperties;
|
||||
}
|
||||
|
||||
void* getLibImHexModuleHandle() {
|
||||
return hex::getContainingModule((void*)&getLibImHexModuleHandle);
|
||||
}
|
||||
|
||||
|
||||
const std::map<std::string, std::string>& getInitArguments() {
|
||||
return *impl::s_initArguments;
|
||||
}
|
||||
|
||||
std::string getInitArgument(const std::string &key) {
|
||||
if (impl::s_initArguments->contains(key))
|
||||
return impl::s_initArguments->at(key);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
|
||||
static bool s_systemThemeDetection;
|
||||
void enableSystemThemeDetection(bool enabled) {
|
||||
s_systemThemeDetection = enabled;
|
||||
|
||||
EventSettingsChanged::post();
|
||||
EventOSThemeChanged::post();
|
||||
}
|
||||
|
||||
@@ -520,13 +642,13 @@ namespace hex {
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::fs::path> &getAdditionalFolderPaths() {
|
||||
static std::vector<std::fs::path> additionalFolderPaths;
|
||||
return additionalFolderPaths;
|
||||
static AutoReset<std::vector<std::fs::path>> s_additionalFolderPaths;
|
||||
const std::vector<std::fs::path>& getAdditionalFolderPaths() {
|
||||
return *s_additionalFolderPaths;
|
||||
}
|
||||
|
||||
void setAdditionalFolderPaths(const std::vector<std::fs::path> &paths) {
|
||||
getAdditionalFolderPaths() = paths;
|
||||
s_additionalFolderPaths = paths;
|
||||
}
|
||||
|
||||
|
||||
@@ -535,14 +657,30 @@ namespace hex {
|
||||
}
|
||||
|
||||
bool isPortableVersion() {
|
||||
return impl::s_portableVersion;
|
||||
static std::optional<bool> portable;
|
||||
if (portable.has_value())
|
||||
return portable.value();
|
||||
|
||||
if (const auto executablePath = wolv::io::fs::getExecutablePath(); executablePath.has_value()) {
|
||||
const auto flagFile = executablePath->parent_path() / "PORTABLE";
|
||||
|
||||
portable = wolv::io::fs::exists(flagFile) && wolv::io::fs::isRegularFile(flagFile);
|
||||
} else {
|
||||
portable = false;
|
||||
}
|
||||
|
||||
return portable.value();
|
||||
}
|
||||
|
||||
std::string getOSName() {
|
||||
#if defined(OS_WINDOWS)
|
||||
return "Windows";
|
||||
#elif defined(OS_LINUX)
|
||||
return "Linux";
|
||||
#if defined(OS_FREEBSD)
|
||||
return "FreeBSD";
|
||||
#else
|
||||
return "Linux";
|
||||
#endif
|
||||
#elif defined(OS_MACOS)
|
||||
return "macOS";
|
||||
#elif defined(OS_WEB)
|
||||
@@ -560,7 +698,7 @@ namespace hex {
|
||||
|
||||
return hex::format("{}.{}.{}", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber);
|
||||
#elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB)
|
||||
struct utsname details;
|
||||
struct utsname details = { };
|
||||
|
||||
if (uname(&details) != 0) {
|
||||
return "Unknown";
|
||||
@@ -592,13 +730,13 @@ namespace hex {
|
||||
return "Unknown";
|
||||
}
|
||||
#elif defined(OS_LINUX) || defined(OS_MACOS) || defined(OS_WEB)
|
||||
struct utsname details;
|
||||
struct utsname details = { };
|
||||
|
||||
if (uname(&details) != 0) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
return std::string(details.machine);
|
||||
return { details.machine };
|
||||
#else
|
||||
return "Unknown";
|
||||
#endif
|
||||
@@ -618,20 +756,16 @@ namespace hex {
|
||||
}
|
||||
|
||||
std::string getCommitHash(bool longHash) {
|
||||
if (longHash) {
|
||||
#if defined GIT_COMMIT_HASH_LONG
|
||||
#if defined GIT_COMMIT_HASH_LONG
|
||||
if (longHash) {
|
||||
return GIT_COMMIT_HASH_LONG;
|
||||
#else
|
||||
return "Unknown";
|
||||
#endif
|
||||
}
|
||||
else {
|
||||
#if defined GIT_COMMIT_HASH_SHORT
|
||||
return GIT_COMMIT_HASH_SHORT;
|
||||
#else
|
||||
return "Unknown";
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
return std::string(GIT_COMMIT_HASH_LONG).substr(0, 7);
|
||||
}
|
||||
#else
|
||||
hex::unused(longHash);
|
||||
return "Unknown";
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string getCommitBranch() {
|
||||
@@ -696,6 +830,12 @@ namespace hex {
|
||||
return impl::s_lastFrameTime;
|
||||
}
|
||||
|
||||
void setWindowResizable(bool resizable) {
|
||||
glfwSetWindowAttrib(impl::s_mainWindowHandle, GLFW_RESIZABLE, int(resizable));
|
||||
impl::s_windowResizable = resizable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -703,15 +843,14 @@ namespace hex {
|
||||
|
||||
namespace impl {
|
||||
|
||||
std::map<std::string, MessagingHandler> &getHandlers() {
|
||||
static std::map<std::string, MessagingHandler> handlers;
|
||||
|
||||
return handlers;
|
||||
static AutoReset<std::map<std::string, MessagingHandler>> s_handlers;
|
||||
const std::map<std::string, MessagingHandler>& getHandlers() {
|
||||
return *s_handlers;
|
||||
}
|
||||
|
||||
void runHandler(const std::string &eventName, const std::vector<u8> &args) {
|
||||
const auto& handlers = impl::getHandlers();
|
||||
auto matchHandler = handlers.find(eventName);
|
||||
const auto& handlers = getHandlers();
|
||||
const auto matchHandler = handlers.find(eventName);
|
||||
|
||||
if (matchHandler == handlers.end()) {
|
||||
log::error("Forward event handler {} not found", eventName);
|
||||
@@ -726,7 +865,7 @@ namespace hex {
|
||||
void registerHandler(const std::string &eventName, const impl::MessagingHandler &handler) {
|
||||
log::debug("Registered new forward event handler: {}", eventName);
|
||||
|
||||
impl::getHandlers().insert({ eventName, handler });
|
||||
impl::s_handlers->insert({ eventName, handler });
|
||||
}
|
||||
|
||||
}
|
||||
@@ -735,13 +874,12 @@ namespace hex {
|
||||
|
||||
namespace impl {
|
||||
|
||||
std::vector<Font>& getFonts() {
|
||||
static std::vector<Font> fonts;
|
||||
|
||||
return fonts;
|
||||
static AutoReset<std::vector<Font>> s_fonts;
|
||||
const std::vector<Font>& getFonts() {
|
||||
return *s_fonts;
|
||||
}
|
||||
|
||||
static std::fs::path s_customFontPath;
|
||||
static AutoReset<std::fs::path> s_customFontPath;
|
||||
void setCustomFontPath(const std::fs::path &path) {
|
||||
s_customFontPath = path;
|
||||
}
|
||||
@@ -751,9 +889,9 @@ namespace hex {
|
||||
s_fontSize = size;
|
||||
}
|
||||
|
||||
static std::unique_ptr<ImFontAtlas> s_fontAtlas;
|
||||
static AutoReset<std::shared_ptr<ImFontAtlas>> s_fontAtlas;
|
||||
void setFontAtlas(ImFontAtlas* fontAtlas) {
|
||||
s_fontAtlas = std::unique_ptr<ImFontAtlas>(fontAtlas);
|
||||
s_fontAtlas = std::unique_ptr<ImFontAtlas, void(*)(ImFontAtlas*)>(fontAtlas, IM_DELETE);
|
||||
}
|
||||
|
||||
static ImFont *s_boldFont = nullptr;
|
||||
@@ -799,33 +937,35 @@ namespace hex {
|
||||
};
|
||||
}
|
||||
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags) {
|
||||
void loadFont(const std::fs::path &path, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags, std::optional<u32> defaultSize) {
|
||||
wolv::io::File fontFile(path, wolv::io::File::Mode::Read);
|
||||
if (!fontFile.isValid()) {
|
||||
log::error("Failed to load font from file '{}'", wolv::util::toUTF8String(path));
|
||||
return;
|
||||
}
|
||||
|
||||
impl::getFonts().emplace_back(Font {
|
||||
impl::s_fonts->emplace_back(Font {
|
||||
wolv::util::toUTF8String(path.filename()),
|
||||
fontFile.readVector(),
|
||||
glyphRanges,
|
||||
offset,
|
||||
flags
|
||||
flags,
|
||||
defaultSize
|
||||
});
|
||||
}
|
||||
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags) {
|
||||
impl::getFonts().emplace_back(Font {
|
||||
void loadFont(const std::string &name, const std::span<const u8> &data, const std::vector<GlyphRange> &glyphRanges, Offset offset, u32 flags, std::optional<u32> defaultSize) {
|
||||
impl::s_fonts->emplace_back(Font {
|
||||
name,
|
||||
{ data.begin(), data.end() },
|
||||
glyphRanges,
|
||||
offset,
|
||||
flags
|
||||
flags,
|
||||
defaultSize
|
||||
});
|
||||
}
|
||||
|
||||
std::fs::path &getCustomFontPath() {
|
||||
const std::fs::path& getCustomFontPath() {
|
||||
return impl::s_customFontPath;
|
||||
}
|
||||
|
||||
@@ -834,7 +974,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
ImFontAtlas* getFontAtlas() {
|
||||
return impl::s_fontAtlas.get();
|
||||
return impl::s_fontAtlas->get();
|
||||
}
|
||||
|
||||
ImFont* Bold() {
|
||||
|
||||
@@ -2,20 +2,23 @@
|
||||
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
#include <hex/ui/view.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace {
|
||||
|
||||
std::optional<std::fs::path> s_layoutPathToLoad;
|
||||
std::optional<std::string> s_layoutStringToLoad;
|
||||
std::vector<LayoutManager::Layout> s_layouts;
|
||||
AutoReset<std::optional<std::fs::path>> s_layoutPathToLoad;
|
||||
AutoReset<std::optional<std::string>> s_layoutStringToLoad;
|
||||
AutoReset<std::vector<LayoutManager::Layout>> s_layouts;
|
||||
|
||||
AutoReset<std::vector<LayoutManager::LoadCallback>> s_loadCallbacks;
|
||||
AutoReset<std::vector<LayoutManager::StoreCallback>> s_storeCallbacks;
|
||||
|
||||
bool s_layoutLocked = false;
|
||||
|
||||
@@ -33,7 +36,7 @@ namespace hex {
|
||||
void LayoutManager::save(const std::string &name) {
|
||||
auto fileName = name;
|
||||
fileName = wolv::util::replaceStrings(fileName, " ", "_");
|
||||
std::transform(fileName.begin(), fileName.end(), fileName.begin(), tolower);
|
||||
std::ranges::transform(fileName, fileName.begin(), tolower);
|
||||
fileName += ".hexlyt";
|
||||
|
||||
std::fs::path layoutPath;
|
||||
@@ -61,37 +64,50 @@ namespace hex {
|
||||
}
|
||||
|
||||
|
||||
std::vector<LayoutManager::Layout> LayoutManager::getLayouts() {
|
||||
const std::vector<LayoutManager::Layout>& LayoutManager::getLayouts() {
|
||||
return s_layouts;
|
||||
}
|
||||
|
||||
void LayoutManager::removeLayout(const std::string& name) {
|
||||
for (const auto &layout : *s_layouts) {
|
||||
if (layout.name == name) {
|
||||
if (wolv::io::fs::remove(layout.path)) {
|
||||
log::info("Removed layout '{}'", name);
|
||||
} else {
|
||||
log::error("Failed to remove layout '{}'", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LayoutManager::reload();
|
||||
}
|
||||
|
||||
|
||||
void LayoutManager::closeAllViews() {
|
||||
for (const auto &[name, view] : ContentRegistry::Views::impl::getEntries())
|
||||
view->getWindowOpenState() = false;
|
||||
}
|
||||
|
||||
void LayoutManager::process() {
|
||||
if (s_layoutPathToLoad.has_value()) {
|
||||
const auto pathString = wolv::util::toUTF8String(*s_layoutPathToLoad);
|
||||
|
||||
if (s_layoutPathToLoad->has_value()) {
|
||||
LayoutManager::closeAllViews();
|
||||
ImGui::LoadIniSettingsFromDisk(pathString.c_str());
|
||||
|
||||
s_layoutPathToLoad = std::nullopt;
|
||||
log::info("Loaded layout from {}", pathString);
|
||||
wolv::io::File file(**s_layoutPathToLoad, wolv::io::File::Mode::Read);
|
||||
s_layoutStringToLoad = file.readString();
|
||||
s_layoutPathToLoad->reset();
|
||||
}
|
||||
|
||||
if (s_layoutStringToLoad.has_value()) {
|
||||
if (s_layoutStringToLoad->has_value()) {
|
||||
LayoutManager::closeAllViews();
|
||||
ImGui::LoadIniSettingsFromMemory(s_layoutStringToLoad->c_str());
|
||||
ImGui::LoadIniSettingsFromMemory((*s_layoutStringToLoad)->c_str());
|
||||
|
||||
s_layoutStringToLoad = std::nullopt;
|
||||
log::info("Loaded layout from string");
|
||||
s_layoutStringToLoad->reset();
|
||||
log::info("Loaded new Layout");
|
||||
}
|
||||
}
|
||||
|
||||
void LayoutManager::reload() {
|
||||
s_layouts.clear();
|
||||
s_layouts->clear();
|
||||
|
||||
for (const auto &directory : hex::fs::getDefaultPaths(fs::ImHexPath::Layouts)) {
|
||||
for (const auto &entry : std::fs::directory_iterator(directory)) {
|
||||
@@ -107,7 +123,7 @@ namespace hex {
|
||||
name[i] = char(std::toupper(name[i]));
|
||||
}
|
||||
|
||||
s_layouts.push_back({
|
||||
s_layouts->push_back({
|
||||
name,
|
||||
path
|
||||
});
|
||||
@@ -116,9 +132,9 @@ namespace hex {
|
||||
}
|
||||
|
||||
void LayoutManager::reset() {
|
||||
s_layoutPathToLoad.reset();
|
||||
s_layoutStringToLoad.reset();
|
||||
s_layouts.clear();
|
||||
s_layoutPathToLoad->reset();
|
||||
s_layoutStringToLoad->reset();
|
||||
s_layouts->clear();
|
||||
}
|
||||
|
||||
bool LayoutManager::isLayoutLocked() {
|
||||
@@ -130,4 +146,24 @@ namespace hex {
|
||||
s_layoutLocked = locked;
|
||||
}
|
||||
|
||||
void LayoutManager::registerLoadCallback(const LoadCallback &callback) {
|
||||
s_loadCallbacks->push_back(callback);
|
||||
}
|
||||
|
||||
void LayoutManager::registerStoreCallback(const StoreCallback &callback) {
|
||||
s_storeCallbacks->push_back(callback);
|
||||
}
|
||||
|
||||
void LayoutManager::onLoad(std::string_view line) {
|
||||
for (const auto &callback : *s_loadCallbacks)
|
||||
callback(line);
|
||||
}
|
||||
|
||||
void LayoutManager::onStore(ImGuiTextBuffer *buffer) {
|
||||
for (const auto &callback : *s_storeCallbacks)
|
||||
callback(buffer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
@@ -7,17 +8,17 @@ namespace hex {
|
||||
|
||||
namespace {
|
||||
|
||||
std::string s_fallbackLanguage;
|
||||
std::string s_selectedLanguage;
|
||||
std::map<std::string, std::string> s_currStrings;
|
||||
AutoReset<std::string> s_fallbackLanguage;
|
||||
AutoReset<std::string> s_selectedLanguage;
|
||||
AutoReset<std::map<std::string, std::string>> s_currStrings;
|
||||
|
||||
}
|
||||
|
||||
namespace impl {
|
||||
|
||||
void resetLanguageStrings() {
|
||||
s_currStrings.clear();
|
||||
s_selectedLanguage.clear();
|
||||
s_currStrings->clear();
|
||||
s_selectedLanguage->clear();
|
||||
}
|
||||
|
||||
void setFallbackLanguage(const std::string &language) {
|
||||
@@ -41,25 +42,48 @@ namespace hex {
|
||||
}
|
||||
|
||||
void loadLanguage(const std::string &language) {
|
||||
s_currStrings.clear();
|
||||
|
||||
auto &definitions = ContentRegistry::Language::impl::getLanguageDefinitions();
|
||||
|
||||
if (!definitions.contains(language))
|
||||
return;
|
||||
|
||||
for (auto &definition : definitions[language])
|
||||
s_currStrings.insert(definition.getEntries().begin(), definition.getEntries().end());
|
||||
s_currStrings->clear();
|
||||
|
||||
for (const auto &definition : definitions.at(language))
|
||||
s_currStrings->insert(definition.getEntries().begin(), definition.getEntries().end());
|
||||
|
||||
const auto& fallbackLanguage = getFallbackLanguage();
|
||||
if (language != fallbackLanguage) {
|
||||
for (auto &definition : definitions[fallbackLanguage])
|
||||
s_currStrings.insert(definition.getEntries().begin(), definition.getEntries().end());
|
||||
if (language != fallbackLanguage && definitions.contains(fallbackLanguage)) {
|
||||
for (const auto &definition : definitions.at(fallbackLanguage))
|
||||
s_currStrings->insert(definition.getEntries().begin(), definition.getEntries().end());
|
||||
}
|
||||
|
||||
s_selectedLanguage = language;
|
||||
}
|
||||
|
||||
std::string getLocalizedString(const std::string& unlocalizedString, const std::string& language) {
|
||||
if (language.empty())
|
||||
return getLocalizedString(unlocalizedString, getSelectedLanguage());
|
||||
|
||||
auto &languageDefinitions = ContentRegistry::Language::impl::getLanguageDefinitions();
|
||||
if (!languageDefinitions.contains(language))
|
||||
return "";
|
||||
|
||||
std::string localizedString;
|
||||
for (const auto &definition : languageDefinitions.at(language)) {
|
||||
if (definition.getEntries().contains(unlocalizedString)) {
|
||||
localizedString = definition.getEntries().at(unlocalizedString);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (localizedString.empty())
|
||||
return getLocalizedString(unlocalizedString, getFallbackLanguage());
|
||||
|
||||
return localizedString;
|
||||
}
|
||||
|
||||
|
||||
const std::map<std::string, std::string> &getSupportedLanguages() {
|
||||
return ContentRegistry::Language::impl::getLanguages();
|
||||
}
|
||||
@@ -79,6 +103,7 @@ namespace hex {
|
||||
Lang::Lang(const UnlocalizedString &unlocalizedString) : m_unlocalizedString(unlocalizedString.get()) { }
|
||||
Lang::Lang(std::string_view unlocalizedString) : m_unlocalizedString(unlocalizedString) { }
|
||||
|
||||
|
||||
Lang::operator std::string() const {
|
||||
return get();
|
||||
}
|
||||
@@ -121,8 +146,8 @@ namespace hex {
|
||||
|
||||
const std::string &Lang::get() const {
|
||||
auto &lang = LocalizationManager::s_currStrings;
|
||||
if (lang.contains(m_unlocalizedString))
|
||||
return lang[m_unlocalizedString];
|
||||
if (lang->contains(m_unlocalizedString))
|
||||
return lang->at(m_unlocalizedString);
|
||||
else
|
||||
return m_unlocalizedString;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
@@ -16,40 +18,71 @@
|
||||
|
||||
namespace hex {
|
||||
|
||||
static uintptr_t loadLibrary(const std::fs::path &path) {
|
||||
#if defined(OS_WINDOWS)
|
||||
auto handle = uintptr_t(LoadLibraryW(path.c_str()));
|
||||
|
||||
if (handle == uintptr_t(INVALID_HANDLE_VALUE) || handle == 0) {
|
||||
log::error("Loading library '{}' failed: {} {}!", wolv::util::toUTF8String(path.filename()), ::GetLastError(), hex::formatSystemError(::GetLastError()));
|
||||
return 0;
|
||||
}
|
||||
|
||||
return handle;
|
||||
#else
|
||||
auto handle = uintptr_t(dlopen(wolv::util::toUTF8String(path).c_str(), RTLD_LAZY));
|
||||
|
||||
if (handle == 0) {
|
||||
log::error("Loading library '{}' failed: {}!", wolv::util::toUTF8String(path.filename()), dlerror());
|
||||
return 0;
|
||||
}
|
||||
|
||||
return handle;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void unloadLibrary(uintptr_t handle, const std::fs::path &path) {
|
||||
#if defined(OS_WINDOWS)
|
||||
if (handle != 0) {
|
||||
if (FreeLibrary(HMODULE(handle)) == FALSE) {
|
||||
log::error("Error when unloading library '{}': {}!", wolv::util::toUTF8String(path.filename()), hex::formatSystemError(::GetLastError()));
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (handle != 0) {
|
||||
if (dlclose(reinterpret_cast<void*>(handle)) != 0) {
|
||||
log::error("Error when unloading library '{}': {}!", path.filename().string(), dlerror());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Plugin::Plugin(const std::fs::path &path) : m_path(path) {
|
||||
log::info("Loading plugin '{}'", wolv::util::toUTF8String(path.filename()));
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
m_handle = uintptr_t(LoadLibraryW(path.c_str()));
|
||||
m_handle = loadLibrary(path);
|
||||
if (m_handle == 0)
|
||||
return;
|
||||
|
||||
if (m_handle == uintptr_t(INVALID_HANDLE_VALUE) || m_handle == 0) {
|
||||
log::error("Loading plugin '{}' failed: {} {}!", wolv::util::toUTF8String(path.filename()), ::GetLastError(), std::system_category().message(::GetLastError()));
|
||||
return;
|
||||
}
|
||||
#else
|
||||
m_handle = uintptr_t(dlopen(wolv::util::toUTF8String(path).c_str(), RTLD_LAZY));
|
||||
const auto fileName = path.stem().string();
|
||||
|
||||
if (m_handle == 0) {
|
||||
log::error("Loading plugin '{}' failed: {}!", wolv::util::toUTF8String(path.filename()), dlerror());
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_functions.initializePluginFunction = getPluginFunction<PluginFunctions::InitializePluginFunc>("initializePlugin");
|
||||
m_functions.initializeLibraryFunction = getPluginFunction<PluginFunctions::InitializePluginFunc>("initializeLibrary");
|
||||
m_functions.getPluginNameFunction = getPluginFunction<PluginFunctions::GetPluginNameFunc>("getPluginName");
|
||||
m_functions.getPluginAuthorFunction = getPluginFunction<PluginFunctions::GetPluginAuthorFunc>("getPluginAuthor");
|
||||
m_functions.getPluginDescriptionFunction = getPluginFunction<PluginFunctions::GetPluginDescriptionFunc>("getPluginDescription");
|
||||
m_functions.getCompatibleVersionFunction = getPluginFunction<PluginFunctions::GetCompatibleVersionFunc>("getCompatibleVersion");
|
||||
m_functions.setImGuiContextFunction = getPluginFunction<PluginFunctions::SetImGuiContextFunc>("setImGuiContext");
|
||||
m_functions.isBuiltinPluginFunction = getPluginFunction<PluginFunctions::IsBuiltinPluginFunc>("isBuiltinPlugin");
|
||||
m_functions.getSubCommandsFunction = getPluginFunction<PluginFunctions::GetSubCommandsFunc>("getSubCommands");
|
||||
m_functions.getFeaturesFunction = getPluginFunction<PluginFunctions::GetSubCommandsFunc>("getFeatures");
|
||||
m_functions.initializePluginFunction = getPluginFunction<PluginFunctions::InitializePluginFunc>("initializePlugin");
|
||||
m_functions.initializeLibraryFunction = getPluginFunction<PluginFunctions::InitializePluginFunc>(hex::format("initializeLibrary_{}", fileName));
|
||||
m_functions.getPluginNameFunction = getPluginFunction<PluginFunctions::GetPluginNameFunc>("getPluginName");
|
||||
m_functions.getLibraryNameFunction = getPluginFunction<PluginFunctions::GetLibraryNameFunc>(hex::format("getLibraryName_{}", fileName));
|
||||
m_functions.getPluginAuthorFunction = getPluginFunction<PluginFunctions::GetPluginAuthorFunc>("getPluginAuthor");
|
||||
m_functions.getPluginDescriptionFunction = getPluginFunction<PluginFunctions::GetPluginDescriptionFunc>("getPluginDescription");
|
||||
m_functions.getCompatibleVersionFunction = getPluginFunction<PluginFunctions::GetCompatibleVersionFunc>("getCompatibleVersion");
|
||||
m_functions.setImGuiContextFunction = getPluginFunction<PluginFunctions::SetImGuiContextFunc>("setImGuiContext");
|
||||
m_functions.setImGuiContextLibraryFunction = getPluginFunction<PluginFunctions::SetImGuiContextFunc>(hex::format("setImGuiContext_{}", fileName));
|
||||
m_functions.getSubCommandsFunction = getPluginFunction<PluginFunctions::GetSubCommandsFunc>("getSubCommands");
|
||||
m_functions.getFeaturesFunction = getPluginFunction<PluginFunctions::GetSubCommandsFunc>("getFeatures");
|
||||
}
|
||||
|
||||
Plugin::Plugin(const hex::PluginFunctions &functions) {
|
||||
Plugin::Plugin(const std::string &name, const hex::PluginFunctions &functions) {
|
||||
m_handle = 0;
|
||||
m_functions = functions;
|
||||
m_path = name;
|
||||
m_addedManually = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +91,7 @@ namespace hex {
|
||||
other.m_handle = 0;
|
||||
|
||||
m_path = std::move(other.m_path);
|
||||
m_addedManually = other.m_addedManually;
|
||||
|
||||
m_functions = other.m_functions;
|
||||
other.m_functions = {};
|
||||
@@ -68,6 +102,7 @@ namespace hex {
|
||||
other.m_handle = 0;
|
||||
|
||||
m_path = std::move(other.m_path);
|
||||
m_addedManually = other.m_addedManually;
|
||||
|
||||
m_functions = other.m_functions;
|
||||
other.m_functions = {};
|
||||
@@ -76,24 +111,29 @@ namespace hex {
|
||||
}
|
||||
|
||||
Plugin::~Plugin() {
|
||||
#if defined(OS_WINDOWS)
|
||||
if (m_handle != 0)
|
||||
FreeLibrary(HMODULE(m_handle));
|
||||
#else
|
||||
if (m_handle != 0)
|
||||
dlclose(reinterpret_cast<void*>(m_handle));
|
||||
#endif
|
||||
if (isLoaded()) {
|
||||
log::info("Trying to unload plugin '{}'", getPluginName());
|
||||
}
|
||||
|
||||
unloadLibrary(m_handle, m_path);
|
||||
}
|
||||
|
||||
bool Plugin::initializePlugin() const {
|
||||
const auto pluginName = wolv::util::toUTF8String(m_path.filename());
|
||||
|
||||
if (this->isLibraryPlugin()) {
|
||||
m_functions.initializeLibraryFunction();
|
||||
|
||||
log::info("Library '{}' initialized successfully", pluginName);
|
||||
|
||||
m_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const auto requestedVersion = getCompatibleVersion();
|
||||
if (requestedVersion != ImHexApi::System::getImHexVersion()) {
|
||||
const auto imhexVersion = ImHexApi::System::getImHexVersion();
|
||||
if (!imhexVersion.starts_with(requestedVersion)) {
|
||||
if (requestedVersion.empty()) {
|
||||
log::warn("Plugin '{}' did not specify a compatible version, assuming it is compatible with the current version of ImHex.", wolv::util::toUTF8String(m_path.filename()));
|
||||
} else {
|
||||
@@ -128,7 +168,7 @@ namespace hex {
|
||||
return m_functions.getPluginNameFunction();
|
||||
} else {
|
||||
if (this->isLibraryPlugin())
|
||||
return "Library Plugin";
|
||||
return m_functions.getLibraryNameFunction();
|
||||
else
|
||||
return hex::format("Unknown Plugin @ 0x{0:016X}", m_handle);
|
||||
}
|
||||
@@ -161,13 +201,6 @@ namespace hex {
|
||||
m_functions.setImGuiContextFunction(ctx);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool Plugin::isBuiltinPlugin() const {
|
||||
if (m_functions.isBuiltinPluginFunction != nullptr)
|
||||
return m_functions.isBuiltinPluginFunction();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::fs::path &Plugin::getPath() const {
|
||||
return m_path;
|
||||
}
|
||||
@@ -182,7 +215,10 @@ namespace hex {
|
||||
|
||||
std::span<SubCommand> Plugin::getSubCommands() const {
|
||||
if (m_functions.getSubCommandsFunction != nullptr) {
|
||||
auto result = m_functions.getSubCommandsFunction();
|
||||
const auto result = m_functions.getSubCommandsFunction();
|
||||
if (result == nullptr)
|
||||
return { };
|
||||
|
||||
return *static_cast<std::vector<SubCommand>*>(result);
|
||||
} else {
|
||||
return { };
|
||||
@@ -191,7 +227,10 @@ namespace hex {
|
||||
|
||||
std::span<Feature> Plugin::getFeatures() const {
|
||||
if (m_functions.getFeaturesFunction != nullptr) {
|
||||
auto result = m_functions.getFeaturesFunction();
|
||||
const auto result = m_functions.getFeaturesFunction();
|
||||
if (result == nullptr)
|
||||
return { };
|
||||
|
||||
return *static_cast<std::vector<Feature>*>(result);
|
||||
} else {
|
||||
return { };
|
||||
@@ -203,7 +242,9 @@ namespace hex {
|
||||
m_functions.initializePluginFunction == nullptr;
|
||||
}
|
||||
|
||||
|
||||
bool Plugin::wasAddedManually() const {
|
||||
return m_addedManually;
|
||||
}
|
||||
|
||||
void *Plugin::getPluginFunction(const std::string &symbol) const {
|
||||
#if defined(OS_WINDOWS)
|
||||
@@ -213,61 +254,146 @@ namespace hex {
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
AutoReset<std::vector<std::fs::path>> PluginManager::s_pluginPaths, PluginManager::s_pluginLoadPaths;
|
||||
|
||||
void PluginManager::addLoadPath(const std::fs::path& path) {
|
||||
s_pluginLoadPaths->emplace_back(path);
|
||||
}
|
||||
|
||||
|
||||
bool PluginManager::load() {
|
||||
bool success = true;
|
||||
for (const auto &loadPath : getPluginLoadPaths())
|
||||
success = PluginManager::load(loadPath) && success;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool PluginManager::load(const std::fs::path &pluginFolder) {
|
||||
if (!wolv::io::fs::exists(pluginFolder))
|
||||
return false;
|
||||
|
||||
getPluginPaths().push_back(pluginFolder);
|
||||
s_pluginPaths->push_back(pluginFolder);
|
||||
|
||||
// Load library plugins first
|
||||
for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) {
|
||||
if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexpluglib")
|
||||
getPlugins().emplace_back(pluginPath.path());
|
||||
if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexpluglib") {
|
||||
if (!isPluginLoaded(pluginPath.path())) {
|
||||
getPluginsMutable().emplace_back(pluginPath.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load regular plugins afterwards
|
||||
for (auto &pluginPath : std::fs::directory_iterator(pluginFolder)) {
|
||||
if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexplug")
|
||||
getPlugins().emplace_back(pluginPath.path());
|
||||
if (pluginPath.is_regular_file() && pluginPath.path().extension() == ".hexplug") {
|
||||
if (!isPluginLoaded(pluginPath.path())) {
|
||||
getPluginsMutable().emplace_back(pluginPath.path());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::erase_if(getPlugins(), [](const Plugin &plugin) {
|
||||
std::erase_if(getPluginsMutable(), [](const Plugin &plugin) {
|
||||
return !plugin.isValid();
|
||||
});
|
||||
|
||||
if (getPlugins().empty())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AutoReset<std::vector<uintptr_t>> PluginManager::s_loadedLibraries;
|
||||
|
||||
bool PluginManager::loadLibraries() {
|
||||
bool success = true;
|
||||
for (const auto &loadPath : fs::getDefaultPaths(fs::ImHexPath::Libraries))
|
||||
success = PluginManager::loadLibraries(loadPath) && success;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool PluginManager::loadLibraries(const std::fs::path& libraryFolder) {
|
||||
bool success = true;
|
||||
for (const auto &entry : std::fs::directory_iterator(libraryFolder)) {
|
||||
if (!(entry.path().extension() == ".dll" || entry.path().extension() == ".so" || entry.path().extension() == ".dylib"))
|
||||
continue;
|
||||
|
||||
auto handle = loadLibrary(entry);
|
||||
if (handle == 0) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
PluginManager::s_loadedLibraries->push_back(handle);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PluginManager::initializeNewPlugins() {
|
||||
for (const auto &plugin : getPlugins()) {
|
||||
if (!plugin.isLoaded())
|
||||
hex::unused(plugin.initializePlugin());
|
||||
}
|
||||
}
|
||||
|
||||
void PluginManager::unload() {
|
||||
getPlugins().clear();
|
||||
getPluginPaths().clear();
|
||||
s_pluginPaths->clear();
|
||||
|
||||
// Unload plugins in reverse order
|
||||
auto &plugins = getPluginsMutable();
|
||||
|
||||
std::list<Plugin> savedPlugins;
|
||||
while (!plugins.empty()) {
|
||||
if (plugins.back().wasAddedManually())
|
||||
savedPlugins.emplace_front(std::move(plugins.back()));
|
||||
plugins.pop_back();
|
||||
}
|
||||
|
||||
while (!s_loadedLibraries->empty()) {
|
||||
unloadLibrary(s_loadedLibraries->back(), "");
|
||||
s_loadedLibraries->pop_back();
|
||||
}
|
||||
|
||||
getPluginsMutable() = std::move(savedPlugins);
|
||||
}
|
||||
|
||||
void PluginManager::reload() {
|
||||
auto paths = getPluginPaths();
|
||||
|
||||
PluginManager::unload();
|
||||
for (const auto &path : paths)
|
||||
PluginManager::load(path);
|
||||
void PluginManager::addPlugin(const std::string &name, hex::PluginFunctions functions) {
|
||||
getPluginsMutable().emplace_back(name, functions);
|
||||
}
|
||||
|
||||
void PluginManager::addPlugin(hex::PluginFunctions functions) {
|
||||
getPlugins().emplace_back(functions);
|
||||
const std::list<Plugin>& PluginManager::getPlugins() {
|
||||
return getPluginsMutable();
|
||||
}
|
||||
|
||||
std::vector<Plugin> &PluginManager::getPlugins() {
|
||||
static std::vector<Plugin> plugins;
|
||||
|
||||
std::list<Plugin>& PluginManager::getPluginsMutable() {
|
||||
static std::list<Plugin> plugins;
|
||||
return plugins;
|
||||
}
|
||||
|
||||
std::vector<std::fs::path> &PluginManager::getPluginPaths() {
|
||||
static std::vector<std::fs::path> pluginPaths;
|
||||
Plugin* PluginManager::getPlugin(const std::string &name) {
|
||||
for (auto &plugin : getPluginsMutable()) {
|
||||
if (plugin.getPluginName() == name)
|
||||
return &plugin;
|
||||
}
|
||||
|
||||
return pluginPaths;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<std::fs::path>& PluginManager::getPluginPaths() {
|
||||
return s_pluginPaths;
|
||||
}
|
||||
|
||||
const std::vector<std::fs::path>& PluginManager::getPluginLoadPaths() {
|
||||
return s_pluginLoadPaths;
|
||||
}
|
||||
|
||||
bool PluginManager::isPluginLoaded(const std::fs::path &path) {
|
||||
return std::ranges::any_of(getPlugins(), [&path](const Plugin &plugin) {
|
||||
return plugin.getPath().filename() == path.filename();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <hex/api/project_file_manager.hpp>
|
||||
|
||||
#include <hex/providers/provider.hpp>
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
|
||||
#include <wolv/io/fs.hpp>
|
||||
|
||||
@@ -8,13 +8,13 @@ namespace hex {
|
||||
|
||||
namespace {
|
||||
|
||||
std::vector<ProjectFile::Handler> s_handlers;
|
||||
std::vector<ProjectFile::ProviderHandler> s_providerHandlers;
|
||||
AutoReset<std::vector<ProjectFile::Handler>> s_handlers;
|
||||
AutoReset<std::vector<ProjectFile::ProviderHandler>> s_providerHandlers;
|
||||
|
||||
std::fs::path s_currProjectPath;
|
||||
AutoReset<std::fs::path> s_currProjectPath;
|
||||
|
||||
std::function<bool(const std::fs::path&)> s_loadProjectFunction;
|
||||
std::function<bool(std::optional<std::fs::path>, bool)> s_storeProjectFunction;
|
||||
AutoReset<std::function<bool(const std::fs::path&)>> s_loadProjectFunction;
|
||||
AutoReset<std::function<bool(std::optional<std::fs::path>, bool)>> s_storeProjectFunction;
|
||||
|
||||
}
|
||||
|
||||
@@ -28,19 +28,19 @@ namespace hex {
|
||||
}
|
||||
|
||||
bool ProjectFile::load(const std::fs::path &filePath) {
|
||||
return s_loadProjectFunction(filePath);
|
||||
return (*s_loadProjectFunction)(filePath);
|
||||
}
|
||||
|
||||
bool ProjectFile::store(std::optional<std::fs::path> filePath, bool updateLocation) {
|
||||
return s_storeProjectFunction(std::move(filePath), updateLocation);
|
||||
return (*s_storeProjectFunction)(std::move(filePath), updateLocation);
|
||||
}
|
||||
|
||||
bool ProjectFile::hasPath() {
|
||||
return !s_currProjectPath.empty();
|
||||
return !s_currProjectPath->empty();
|
||||
}
|
||||
|
||||
void ProjectFile::clearPath() {
|
||||
s_currProjectPath.clear();
|
||||
s_currProjectPath->clear();
|
||||
}
|
||||
|
||||
std::fs::path ProjectFile::getPath() {
|
||||
@@ -51,11 +51,19 @@ namespace hex {
|
||||
s_currProjectPath = path;
|
||||
}
|
||||
|
||||
std::vector<ProjectFile::Handler> &ProjectFile::getHandlers() {
|
||||
void ProjectFile::registerHandler(const Handler &handler) {
|
||||
s_handlers->push_back(handler);
|
||||
}
|
||||
|
||||
void ProjectFile::registerPerProviderHandler(const ProviderHandler &handler) {
|
||||
s_providerHandlers->push_back(handler);
|
||||
}
|
||||
|
||||
const std::vector<ProjectFile::Handler>& ProjectFile::getHandlers() {
|
||||
return s_handlers;
|
||||
}
|
||||
|
||||
std::vector<ProjectFile::ProviderHandler> &ProjectFile::getProviderHandlers() {
|
||||
const std::vector<ProjectFile::ProviderHandler>& ProjectFile::getProviderHandlers() {
|
||||
return s_providerHandlers;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <hex/api/shortcut_manager.hpp>
|
||||
#include <imgui.h>
|
||||
#include <hex/api/content_registry.hpp>
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
|
||||
#include <hex/ui/view.hpp>
|
||||
|
||||
@@ -8,7 +9,7 @@ namespace hex {
|
||||
|
||||
namespace {
|
||||
|
||||
std::map<Shortcut, ShortcutManager::ShortcutEntry> s_globalShortcuts;
|
||||
AutoReset<std::map<Shortcut, ShortcutManager::ShortcutEntry>> s_globalShortcuts;
|
||||
std::atomic<bool> s_paused;
|
||||
std::optional<Shortcut> s_prevShortcut;
|
||||
|
||||
@@ -16,7 +17,7 @@ namespace hex {
|
||||
|
||||
|
||||
void ShortcutManager::addGlobalShortcut(const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback) {
|
||||
s_globalShortcuts.insert({ shortcut, { shortcut, unlocalizedName, callback } });
|
||||
s_globalShortcuts->insert({ shortcut, { shortcut, unlocalizedName, callback } });
|
||||
}
|
||||
|
||||
void ShortcutManager::addShortcut(View *view, const Shortcut &shortcut, const UnlocalizedString &unlocalizedName, const std::function<void()> &callback) {
|
||||
@@ -56,8 +57,8 @@ namespace hex {
|
||||
}
|
||||
}
|
||||
|
||||
void ShortcutManager::process(const std::unique_ptr<View> ¤tView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) {
|
||||
Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode);
|
||||
void ShortcutManager::process(const View *currentView, bool ctrl, bool alt, bool shift, bool super, bool focused, u32 keyCode) {
|
||||
const Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, focused, keyCode);
|
||||
if (keyCode != 0)
|
||||
s_prevShortcut = Shortcut(pressedShortcut.getKeys());
|
||||
|
||||
@@ -65,7 +66,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ShortcutManager::processGlobals(bool ctrl, bool alt, bool shift, bool super, u32 keyCode) {
|
||||
Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, false, keyCode);
|
||||
const Shortcut pressedShortcut = getShortcut(ctrl, alt, shift, super, false, keyCode);
|
||||
if (keyCode != 0)
|
||||
s_prevShortcut = Shortcut(pressedShortcut.getKeys());
|
||||
|
||||
@@ -73,7 +74,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ShortcutManager::clearShortcuts() {
|
||||
s_globalShortcuts.clear();
|
||||
s_globalShortcuts->clear();
|
||||
}
|
||||
|
||||
void ShortcutManager::resumeShortcuts() {
|
||||
@@ -90,17 +91,18 @@ namespace hex {
|
||||
}
|
||||
|
||||
std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getGlobalShortcuts() {
|
||||
std::vector<ShortcutManager::ShortcutEntry> result;
|
||||
std::vector<ShortcutEntry> result;
|
||||
|
||||
for (auto &[shortcut, entry] : s_globalShortcuts)
|
||||
for (auto &[shortcut, entry] : *s_globalShortcuts)
|
||||
result.push_back(entry);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<ShortcutManager::ShortcutEntry> ShortcutManager::getViewShortcuts(const View *view) {
|
||||
std::vector<ShortcutManager::ShortcutEntry> result;
|
||||
std::vector<ShortcutEntry> result;
|
||||
|
||||
result.reserve(view->m_shortcuts.size());
|
||||
for (auto &[shortcut, entry] : view->m_shortcuts)
|
||||
result.push_back(entry);
|
||||
|
||||
|
||||
@@ -4,59 +4,65 @@
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
#include <jthread.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
#include <processthreadsapi.h>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
struct SourceLocationWrapper {
|
||||
std::source_location location;
|
||||
|
||||
bool operator==(const SourceLocationWrapper &other) const {
|
||||
return location.file_name() == other.location.file_name() &&
|
||||
location.function_name() == other.location.function_name() &&
|
||||
location.column() == other.location.column() &&
|
||||
location.line() == other.location.line();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
struct std::hash<SourceLocationWrapper> {
|
||||
std::size_t operator()(const SourceLocationWrapper& s) const noexcept {
|
||||
auto h1 = std::hash<std::string>{}(s.location.file_name());
|
||||
auto h2 = std::hash<std::string>{}(s.location.function_name());
|
||||
auto h3 = std::hash<u32>{}(s.location.column());
|
||||
auto h4 = std::hash<u32>{}(s.location.line());
|
||||
return (h1 << 0) ^ (h2 << 1) ^ (h3 << 2) ^ (h4 << 3);
|
||||
}
|
||||
};
|
||||
|
||||
namespace hex {
|
||||
|
||||
namespace {
|
||||
|
||||
std::mutex s_deferredCallsMutex, s_tasksFinishedMutex;
|
||||
std::recursive_mutex s_deferredCallsMutex, s_tasksFinishedMutex;
|
||||
|
||||
std::list<std::shared_ptr<Task>> s_tasks, s_taskQueue;
|
||||
std::list<std::function<void()>> s_deferredCalls;
|
||||
std::unordered_map<SourceLocationWrapper, std::function<void()>> s_onceDeferredCalls;
|
||||
std::list<std::function<void()>> s_tasksFinishedCallbacks;
|
||||
|
||||
std::mutex s_queueMutex;
|
||||
std::condition_variable s_jobCondVar;
|
||||
std::vector<std::jthread> s_workers;
|
||||
|
||||
thread_local std::array<char, 256> s_currentThreadName;
|
||||
thread_local Task* s_currentTask = nullptr;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void setThreadName(const std::string &name) {
|
||||
#if defined(OS_WINDOWS)
|
||||
typedef struct tagTHREADNAME_INFO {
|
||||
DWORD dwType;
|
||||
LPCSTR szName;
|
||||
DWORD dwThreadID;
|
||||
DWORD dwFlags;
|
||||
} THREADNAME_INFO;
|
||||
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name.c_str();
|
||||
info.dwThreadID = ::GetCurrentThreadId();
|
||||
info.dwFlags = 0;
|
||||
|
||||
constexpr static DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info));
|
||||
#elif defined(OS_LINUX)
|
||||
pthread_setname_np(pthread_self(), name.c_str());
|
||||
#elif defined(OS_WEB)
|
||||
hex::unused(name);
|
||||
#elif defined(OS_MACOS)
|
||||
pthread_setname_np(name.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
Task::Task(UnlocalizedString unlocalizedName, u64 maxValue, bool background, std::function<void(Task &)> function)
|
||||
: m_unlocalizedName(std::move(unlocalizedName)), m_maxValue(maxValue), m_function(std::move(function)), m_background(background) { }
|
||||
|
||||
@@ -93,6 +99,19 @@ namespace hex {
|
||||
throw TaskInterruptor();
|
||||
}
|
||||
|
||||
void Task::update() const {
|
||||
if (m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]]
|
||||
throw TaskInterruptor();
|
||||
}
|
||||
|
||||
void Task::increment() {
|
||||
m_currValue.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
if (m_shouldInterrupt.load(std::memory_order_relaxed)) [[unlikely]]
|
||||
throw TaskInterruptor();
|
||||
}
|
||||
|
||||
|
||||
void Task::setMaxValue(u64 value) {
|
||||
m_maxValue = value;
|
||||
}
|
||||
@@ -170,7 +189,7 @@ namespace hex {
|
||||
|
||||
|
||||
bool TaskHolder::isRunning() const {
|
||||
auto task = m_task.lock();
|
||||
const auto &task = m_task.lock();
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
@@ -178,7 +197,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
bool TaskHolder::hadException() const {
|
||||
auto task = m_task.lock();
|
||||
const auto &task = m_task.lock();
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
@@ -186,7 +205,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
bool TaskHolder::shouldInterrupt() const {
|
||||
auto task = m_task.lock();
|
||||
const auto &task = m_task.lock();
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
@@ -194,7 +213,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
bool TaskHolder::wasInterrupted() const {
|
||||
auto task = m_task.lock();
|
||||
const auto &task = m_task.lock();
|
||||
if (!task)
|
||||
return false;
|
||||
|
||||
@@ -202,7 +221,7 @@ namespace hex {
|
||||
}
|
||||
|
||||
void TaskHolder::interrupt() const {
|
||||
auto task = m_task.lock();
|
||||
const auto &task = m_task.lock();
|
||||
if (!task)
|
||||
return;
|
||||
|
||||
@@ -210,9 +229,9 @@ namespace hex {
|
||||
}
|
||||
|
||||
u32 TaskHolder::getProgress() const {
|
||||
auto task = m_task.lock();
|
||||
const auto &task = m_task.lock();
|
||||
if (!task)
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
// If the max value is 0, the task has no progress
|
||||
if (task->getMaxValue() == 0)
|
||||
@@ -222,7 +241,6 @@ namespace hex {
|
||||
return u32((task->getValue() * 100) / task->getMaxValue());
|
||||
}
|
||||
|
||||
|
||||
void TaskManager::init() {
|
||||
const auto threadCount = std::thread::hardware_concurrency();
|
||||
|
||||
@@ -235,7 +253,7 @@ namespace hex {
|
||||
std::shared_ptr<Task> task;
|
||||
|
||||
// Set the thread name to "Idle Task" while waiting for a task
|
||||
setThreadName("Idle Task");
|
||||
TaskManager::setCurrentThreadName("Idle Task");
|
||||
|
||||
{
|
||||
// Wait for a task to be added to the queue
|
||||
@@ -251,11 +269,13 @@ namespace hex {
|
||||
// Grab the next task from the queue
|
||||
task = std::move(s_taskQueue.front());
|
||||
s_taskQueue.pop_front();
|
||||
|
||||
s_currentTask = task.get();
|
||||
}
|
||||
|
||||
try {
|
||||
// Set the thread name to the name of the task
|
||||
setThreadName(Lang(task->m_unlocalizedName));
|
||||
TaskManager::setCurrentThreadName(Lang(task->m_unlocalizedName));
|
||||
|
||||
// Execute the task
|
||||
task->m_function(*task);
|
||||
@@ -276,6 +296,7 @@ namespace hex {
|
||||
task->exception("Unknown Exception");
|
||||
}
|
||||
|
||||
s_currentTask = nullptr;
|
||||
task->finish();
|
||||
}
|
||||
});
|
||||
@@ -284,7 +305,7 @@ namespace hex {
|
||||
|
||||
void TaskManager::exit() {
|
||||
// Interrupt all tasks
|
||||
for (auto &task : s_tasks) {
|
||||
for (const auto &task : s_tasks) {
|
||||
task->interrupt();
|
||||
}
|
||||
|
||||
@@ -302,6 +323,7 @@ namespace hex {
|
||||
s_taskQueue.clear();
|
||||
|
||||
s_deferredCalls.clear();
|
||||
s_onceDeferredCalls.clear();
|
||||
s_tasksFinishedCallbacks.clear();
|
||||
}
|
||||
|
||||
@@ -349,14 +371,19 @@ namespace hex {
|
||||
|
||||
}
|
||||
|
||||
std::list<std::shared_ptr<Task>> &TaskManager::getRunningTasks() {
|
||||
Task& TaskManager::getCurrentTask() {
|
||||
return *s_currentTask;
|
||||
}
|
||||
|
||||
|
||||
const std::list<std::shared_ptr<Task>>& TaskManager::getRunningTasks() {
|
||||
return s_tasks;
|
||||
}
|
||||
|
||||
size_t TaskManager::getRunningTaskCount() {
|
||||
std::scoped_lock lock(s_queueMutex);
|
||||
|
||||
return std::count_if(s_tasks.begin(), s_tasks.end(), [](const auto &task){
|
||||
return std::ranges::count_if(s_tasks, [](const auto &task){
|
||||
return !task->isBackgroundTask();
|
||||
});
|
||||
}
|
||||
@@ -364,7 +391,7 @@ namespace hex {
|
||||
size_t TaskManager::getRunningBackgroundTaskCount() {
|
||||
std::scoped_lock lock(s_queueMutex);
|
||||
|
||||
return std::count_if(s_tasks.begin(), s_tasks.end(), [](const auto &task){
|
||||
return std::ranges::count_if(s_tasks, [](const auto &task){
|
||||
return task->isBackgroundTask();
|
||||
});
|
||||
}
|
||||
@@ -376,19 +403,84 @@ namespace hex {
|
||||
s_deferredCalls.push_back(function);
|
||||
}
|
||||
|
||||
void TaskManager::doLaterOnce(const std::function<void()> &function, std::source_location location) {
|
||||
std::scoped_lock lock(s_deferredCallsMutex);
|
||||
|
||||
s_onceDeferredCalls[SourceLocationWrapper{ location }] = function;
|
||||
}
|
||||
|
||||
void TaskManager::runDeferredCalls() {
|
||||
std::scoped_lock lock(s_deferredCallsMutex);
|
||||
|
||||
for (const auto &call : s_deferredCalls)
|
||||
call();
|
||||
|
||||
s_deferredCalls.clear();
|
||||
while (!s_deferredCalls.empty()) {
|
||||
auto callback = s_deferredCalls.front();
|
||||
s_deferredCalls.pop_front();
|
||||
callback();
|
||||
}
|
||||
while (!s_onceDeferredCalls.empty()) {
|
||||
auto node = s_onceDeferredCalls.extract(s_onceDeferredCalls.begin());
|
||||
node.mapped()();
|
||||
}
|
||||
}
|
||||
|
||||
void TaskManager::runWhenTasksFinished(const std::function<void()> &function) {
|
||||
std::scoped_lock lock(s_tasksFinishedMutex);
|
||||
|
||||
for (const auto &task : s_tasks) {
|
||||
task->interrupt();
|
||||
}
|
||||
|
||||
s_tasksFinishedCallbacks.push_back(function);
|
||||
}
|
||||
|
||||
void TaskManager::setCurrentThreadName(const std::string &name) {
|
||||
std::ranges::fill(s_currentThreadName, '\0');
|
||||
std::ranges::copy(name | std::views::take(255), s_currentThreadName.begin());
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
using SetThreadDescriptionFunc = HRESULT(WINAPI*)(HANDLE hThread, PCWSTR lpThreadDescription);
|
||||
|
||||
static auto setThreadDescription = reinterpret_cast<SetThreadDescriptionFunc>(
|
||||
reinterpret_cast<uintptr_t>(
|
||||
::GetProcAddress(
|
||||
::GetModuleHandle("Kernel32.dll"),
|
||||
"SetThreadDescription"
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if (setThreadDescription != nullptr) {
|
||||
const auto longName = hex::utf8ToUtf16(name);
|
||||
setThreadDescription(::GetCurrentThread(), longName.c_str());
|
||||
} else {
|
||||
struct THREADNAME_INFO {
|
||||
DWORD dwType;
|
||||
LPCSTR szName;
|
||||
DWORD dwThreadID;
|
||||
DWORD dwFlags;
|
||||
};
|
||||
|
||||
THREADNAME_INFO info = { };
|
||||
info.dwType = 0x1000;
|
||||
info.szName = name.c_str();
|
||||
info.dwThreadID = ::GetCurrentThreadId();
|
||||
info.dwFlags = 0;
|
||||
|
||||
constexpr static DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(ULONG_PTR), reinterpret_cast<ULONG_PTR*>(&info));
|
||||
}
|
||||
#elif defined(OS_LINUX)
|
||||
pthread_setname_np(pthread_self(), name.c_str());
|
||||
#elif defined(OS_WEB)
|
||||
hex::unused(name);
|
||||
#elif defined(OS_MACOS)
|
||||
pthread_setname_np(name.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string TaskManager::getCurrentThreadName() {
|
||||
return s_currentThreadName.data();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
@@ -10,29 +11,36 @@ namespace hex {
|
||||
|
||||
namespace {
|
||||
|
||||
std::map<std::string, nlohmann::json> s_themes;
|
||||
std::map<std::string, ThemeManager::ThemeHandler> s_themeHandlers;
|
||||
std::map<std::string, ThemeManager::StyleHandler> s_styleHandlers;
|
||||
std::string s_imageTheme;
|
||||
std::string s_currTheme;
|
||||
AutoReset<std::map<std::string, nlohmann::json>> s_themes;
|
||||
AutoReset<std::map<std::string, ThemeManager::ThemeHandler>> s_themeHandlers;
|
||||
AutoReset<std::map<std::string, ThemeManager::StyleHandler>> s_styleHandlers;
|
||||
AutoReset<std::string> s_imageTheme;
|
||||
AutoReset<std::string> s_currTheme;
|
||||
|
||||
std::recursive_mutex s_themeMutex;
|
||||
}
|
||||
|
||||
|
||||
void ThemeManager::addThemeHandler(const std::string &name, const ColorMap &colorMap, const std::function<ImColor(u32)> &getFunction, const std::function<void(u32, ImColor)> &setFunction) {
|
||||
s_themeHandlers[name] = { colorMap, getFunction, setFunction };
|
||||
std::unique_lock lock(s_themeMutex);
|
||||
|
||||
(*s_themeHandlers)[name] = { colorMap, getFunction, setFunction };
|
||||
}
|
||||
|
||||
void ThemeManager::addStyleHandler(const std::string &name, const StyleMap &styleMap) {
|
||||
s_styleHandlers[name] = { styleMap };
|
||||
std::unique_lock lock(s_themeMutex);
|
||||
|
||||
(*s_styleHandlers)[name] = { styleMap };
|
||||
}
|
||||
|
||||
void ThemeManager::addTheme(const std::string &content) {
|
||||
std::unique_lock lock(s_themeMutex);
|
||||
|
||||
try {
|
||||
auto theme = nlohmann::json::parse(content);
|
||||
|
||||
if (theme.contains("name") && theme.contains("colors")) {
|
||||
s_themes[theme["name"].get<std::string>()] = theme;
|
||||
(*s_themes)[theme["name"].get<std::string>()] = theme;
|
||||
} else {
|
||||
hex::log::error("Invalid theme file");
|
||||
}
|
||||
@@ -65,7 +73,7 @@ namespace hex {
|
||||
if (color == 0x00000000)
|
||||
return ImVec4(0, 0, 0, -1);
|
||||
|
||||
return ImColor(hex::changeEndianess(color, std::endian::big));
|
||||
return ImColor(hex::changeEndianness(color, std::endian::big));
|
||||
}
|
||||
|
||||
nlohmann::json ThemeManager::exportCurrentTheme(const std::string &name) {
|
||||
@@ -77,16 +85,16 @@ namespace hex {
|
||||
{ "base", s_currTheme }
|
||||
};
|
||||
|
||||
for (const auto &[type, handler] : s_themeHandlers) {
|
||||
for (const auto &[type, handler] : *s_themeHandlers) {
|
||||
theme["colors"][type] = {};
|
||||
|
||||
for (const auto &[key, value] : handler.colorMap) {
|
||||
auto color = handler.getFunction(value);
|
||||
theme["colors"][type][key] = fmt::format("#{:08X}", hex::changeEndianess(u32(color), std::endian::big));
|
||||
theme["colors"][type][key] = fmt::format("#{:08X}", hex::changeEndianness(u32(color), std::endian::big));
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &[type, handler] : s_styleHandlers) {
|
||||
for (const auto &[type, handler] : *s_styleHandlers) {
|
||||
theme["styles"][type] = {};
|
||||
|
||||
for (const auto &[key, style] : handler.styleMap) {
|
||||
@@ -105,17 +113,19 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ThemeManager::changeTheme(std::string name) {
|
||||
if (!s_themes.contains(name)) {
|
||||
if (s_themes.empty()) {
|
||||
std::unique_lock lock(s_themeMutex);
|
||||
|
||||
if (!s_themes->contains(name)) {
|
||||
if (s_themes->empty()) {
|
||||
return;
|
||||
} else {
|
||||
const std::string &defaultTheme = s_themes.begin()->first;
|
||||
const std::string &defaultTheme = s_themes->begin()->first;
|
||||
hex::log::error("Theme '{}' does not exist, using default theme '{}' instead!", name, defaultTheme);
|
||||
name = defaultTheme;
|
||||
}
|
||||
}
|
||||
|
||||
const auto &theme = s_themes[name];
|
||||
const auto &theme = (*s_themes)[name];
|
||||
|
||||
if (theme.contains("base")) {
|
||||
if (theme["base"].is_string()) {
|
||||
@@ -126,14 +136,14 @@ namespace hex {
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.contains("colors") && !s_themeHandlers.empty()) {
|
||||
if (theme.contains("colors") && !s_themeHandlers->empty()) {
|
||||
for (const auto&[type, content] : theme["colors"].items()) {
|
||||
if (!s_themeHandlers.contains(type)) {
|
||||
if (!s_themeHandlers->contains(type)) {
|
||||
log::warn("No theme handler found for '{}'", type);
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &handler = s_themeHandlers[type];
|
||||
const auto &handler = (*s_themeHandlers)[type];
|
||||
for (const auto &[key, value] : content.items()) {
|
||||
if (!handler.colorMap.contains(key)) {
|
||||
log::warn("No color found for '{}.{}'", type, key);
|
||||
@@ -146,19 +156,19 @@ namespace hex {
|
||||
continue;
|
||||
}
|
||||
|
||||
s_themeHandlers[type].setFunction(s_themeHandlers[type].colorMap.at(key), color.value());
|
||||
(*s_themeHandlers)[type].setFunction((*s_themeHandlers)[type].colorMap.at(key), color.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (theme.contains("styles") && !s_styleHandlers.empty()) {
|
||||
if (theme.contains("styles") && !s_styleHandlers->empty()) {
|
||||
for (const auto&[type, content] : theme["styles"].items()) {
|
||||
if (!s_styleHandlers.contains(type)) {
|
||||
if (!s_styleHandlers->contains(type)) {
|
||||
log::warn("No style handler found for '{}'", type);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto &handler = s_styleHandlers[type];
|
||||
auto &handler = (*s_styleHandlers)[type];
|
||||
for (const auto &[key, value] : content.items()) {
|
||||
if (!handler.styleMap.contains(key))
|
||||
continue;
|
||||
@@ -167,12 +177,12 @@ namespace hex {
|
||||
const float scale = style.needsScaling ? 1_scaled : 1.0F;
|
||||
|
||||
if (value.is_number_float()) {
|
||||
if (auto newValue = std::get_if<float*>(&style.value); newValue != nullptr)
|
||||
if (const auto newValue = std::get_if<float*>(&style.value); newValue != nullptr && *newValue != nullptr)
|
||||
**newValue = value.get<float>() * scale;
|
||||
else
|
||||
log::warn("Style variable '{}' was of type ImVec2 but a float was expected.", name);
|
||||
} else if (value.is_array() && value.size() == 2 && value[0].is_number_float() && value[1].is_number_float()) {
|
||||
if (auto newValue = std::get_if<ImVec2*>(&style.value); newValue != nullptr)
|
||||
if (const auto newValue = std::get_if<ImVec2*>(&style.value); newValue != nullptr && *newValue != nullptr)
|
||||
**newValue = ImVec2(value[0].get<float>() * scale, value[1].get<float>() * scale);
|
||||
else
|
||||
log::warn("Style variable '{}' was of type float but a ImVec2 was expected.", name);
|
||||
@@ -190,6 +200,8 @@ namespace hex {
|
||||
hex::log::error("Theme '{}' has invalid image theme!", name);
|
||||
s_imageTheme = "dark";
|
||||
}
|
||||
} else {
|
||||
s_imageTheme = "dark";
|
||||
}
|
||||
|
||||
s_currTheme = name;
|
||||
@@ -203,26 +215,28 @@ namespace hex {
|
||||
|
||||
std::vector<std::string> ThemeManager::getThemeNames() {
|
||||
std::vector<std::string> themeNames;
|
||||
for (const auto &[name, theme] : s_themes)
|
||||
for (const auto &[name, theme] : *s_themes)
|
||||
themeNames.push_back(name);
|
||||
|
||||
return themeNames;
|
||||
}
|
||||
|
||||
void ThemeManager::reset() {
|
||||
s_themes.clear();
|
||||
s_styleHandlers.clear();
|
||||
s_themeHandlers.clear();
|
||||
s_imageTheme.clear();
|
||||
s_currTheme.clear();
|
||||
std::unique_lock lock(s_themeMutex);
|
||||
|
||||
s_themes->clear();
|
||||
s_styleHandlers->clear();
|
||||
s_themeHandlers->clear();
|
||||
s_imageTheme->clear();
|
||||
s_currTheme->clear();
|
||||
}
|
||||
|
||||
|
||||
std::map<std::string, ThemeManager::ThemeHandler> &ThemeManager::getThemeHandlers() {
|
||||
const std::map<std::string, ThemeManager::ThemeHandler> &ThemeManager::getThemeHandlers() {
|
||||
return s_themeHandlers;
|
||||
}
|
||||
|
||||
std::map<std::string, ThemeManager::StyleHandler> &ThemeManager::getStyleHandlers() {
|
||||
const std::map<std::string, ThemeManager::StyleHandler> &ThemeManager::getStyleHandlers() {
|
||||
return s_styleHandlers;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <hex/api/localization_manager.hpp>
|
||||
#include <hex/api/task_manager.hpp>
|
||||
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
|
||||
#include <imgui_internal.h>
|
||||
#include <hex/helpers/utils.hpp>
|
||||
#include <wolv/utils/core.hpp>
|
||||
@@ -13,11 +15,11 @@ namespace hex {
|
||||
|
||||
namespace {
|
||||
|
||||
std::map<std::string, TutorialManager::Tutorial> s_tutorials;
|
||||
decltype(s_tutorials)::iterator s_currentTutorial = s_tutorials.end();
|
||||
AutoReset<std::map<std::string, TutorialManager::Tutorial>> s_tutorials;
|
||||
auto s_currentTutorial = s_tutorials->end();
|
||||
|
||||
std::map<ImGuiID, std::string> s_highlights;
|
||||
std::vector<std::pair<ImRect, std::string>> s_highlightDisplays;
|
||||
AutoReset<std::map<ImGuiID, std::string>> s_highlights;
|
||||
AutoReset<std::vector<std::pair<ImRect, std::string>>> s_highlightDisplays;
|
||||
|
||||
|
||||
class IDStack {
|
||||
@@ -67,19 +69,19 @@ namespace hex {
|
||||
|
||||
|
||||
TutorialManager::Tutorial& TutorialManager::createTutorial(const UnlocalizedString &unlocalizedName, const UnlocalizedString &unlocalizedDescription) {
|
||||
return s_tutorials.try_emplace(unlocalizedName, Tutorial(unlocalizedName, unlocalizedDescription)).first->second;
|
||||
return s_tutorials->try_emplace(unlocalizedName, Tutorial(unlocalizedName, unlocalizedDescription)).first->second;
|
||||
}
|
||||
|
||||
void TutorialManager::startTutorial(const UnlocalizedString &unlocalizedName) {
|
||||
s_currentTutorial = s_tutorials.find(unlocalizedName);
|
||||
if (s_currentTutorial == s_tutorials.end())
|
||||
s_currentTutorial = s_tutorials->find(unlocalizedName);
|
||||
if (s_currentTutorial == s_tutorials->end())
|
||||
return;
|
||||
|
||||
s_currentTutorial->second.start();
|
||||
}
|
||||
|
||||
void TutorialManager::drawHighlights() {
|
||||
for (const auto &[rect, unlocalizedText] : s_highlightDisplays) {
|
||||
for (const auto &[rect, unlocalizedText] : *s_highlightDisplays) {
|
||||
const auto drawList = ImGui::GetForegroundDrawList();
|
||||
|
||||
drawList->PushClipRectFullScreen();
|
||||
@@ -105,23 +107,24 @@ namespace hex {
|
||||
windowSize.y = textSize.y + margin.y * 2;
|
||||
|
||||
if (windowPos.y + windowSize.y > mainWindowPos.y + mainWindowSize.y)
|
||||
windowPos.y = rect.Min.y - windowSize.y - 10_scaled;
|
||||
windowPos.y = rect.Min.y - windowSize.y - 15_scaled;
|
||||
if (windowPos.y < mainWindowPos.y)
|
||||
windowPos.y = mainWindowPos.y + 10_scaled;
|
||||
windowPos.y = rect.Min.y + 10_scaled;
|
||||
|
||||
auto &style = ImGui::GetStyle();
|
||||
|
||||
ImVec2 shadowOffset = ImVec2(ImCos(style.WindowShadowOffsetAngle), ImSin(style.WindowShadowOffsetAngle)) * style.WindowShadowOffsetDist;
|
||||
drawList->AddRectFilled(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_WindowBg) | 0xFF000000);
|
||||
drawList->AddRect(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_Border));
|
||||
drawList->AddShadowRect(windowPos, windowPos + windowSize, ImGui::GetColorU32(ImGuiCol_WindowShadow), style.WindowShadowSize, shadowOffset, ImDrawFlags_ShadowCutOutShapeBackground);
|
||||
drawList->AddText(nullptr, 0.0F, windowPos + margin, ImGui::GetColorU32(ImGuiCol_Text), text, nullptr, windowSize.x - margin.x * 2);
|
||||
ImGui::SetNextWindowPos(windowPos);
|
||||
ImGui::SetNextWindowSize(windowSize);
|
||||
ImGui::SetNextWindowViewport(ImGui::GetMainViewport()->ID);
|
||||
if (ImGui::Begin(unlocalizedText.c_str(), nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize)) {
|
||||
ImGui::BringWindowToDisplayFront(ImGui::GetCurrentWindowRead());
|
||||
ImGuiExt::TextFormattedWrapped("{}", text);
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
drawList->PopClipRect();
|
||||
|
||||
}
|
||||
s_highlightDisplays.clear();
|
||||
s_highlightDisplays->clear();
|
||||
}
|
||||
|
||||
void TutorialManager::drawMessageBox(std::optional<Tutorial::Step::Message> message) {
|
||||
@@ -195,7 +198,7 @@ namespace hex {
|
||||
void TutorialManager::drawTutorial() {
|
||||
drawHighlights();
|
||||
|
||||
if (s_currentTutorial == s_tutorials.end())
|
||||
if (s_currentTutorial == s_tutorials->end())
|
||||
return;
|
||||
|
||||
const auto ¤tStep = s_currentTutorial->second.m_currentStep;
|
||||
@@ -209,11 +212,11 @@ namespace hex {
|
||||
|
||||
|
||||
void TutorialManager::reset() {
|
||||
s_tutorials.clear();
|
||||
s_currentTutorial = s_tutorials.end();
|
||||
s_tutorials->clear();
|
||||
s_currentTutorial = s_tutorials->end();
|
||||
|
||||
s_highlights.clear();
|
||||
s_highlightDisplays.clear();
|
||||
s_highlights->clear();
|
||||
s_highlightDisplays->clear();
|
||||
}
|
||||
|
||||
TutorialManager::Tutorial::Step& TutorialManager::Tutorial::addStep() {
|
||||
@@ -251,7 +254,7 @@ namespace hex {
|
||||
}, id);
|
||||
}
|
||||
|
||||
s_highlights.emplace(idStack.get(), text);
|
||||
s_highlights->emplace(idStack.get(), text);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +273,7 @@ namespace hex {
|
||||
}, id);
|
||||
}
|
||||
|
||||
s_highlights.erase(idStack.get());
|
||||
s_highlights->erase(idStack.get());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +287,7 @@ namespace hex {
|
||||
if (m_parent->m_currentStep != m_parent->m_steps.end())
|
||||
m_parent->m_currentStep->addHighlights();
|
||||
else
|
||||
s_currentTutorial = s_tutorials.end();
|
||||
s_currentTutorial = s_tutorials->end();
|
||||
}
|
||||
|
||||
|
||||
@@ -368,9 +371,9 @@ namespace hex {
|
||||
}
|
||||
|
||||
void ImGuiTestEngineHook_ItemAdd(ImGuiContext*, ImGuiID id, const ImRect& bb, const ImGuiLastItemData*) {
|
||||
const auto element = hex::s_highlights.find(id);
|
||||
if (element != hex::s_highlights.end()) {
|
||||
hex::s_highlightDisplays.emplace_back(bb, element->second);
|
||||
const auto element = hex::s_highlights->find(id);
|
||||
if (element != hex::s_highlights->end()) {
|
||||
hex::s_highlightDisplays->emplace_back(bb, element->second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,43 +2,55 @@
|
||||
#include <hex/api/layout_manager.hpp>
|
||||
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
|
||||
#include <wolv/io/file.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <wolv/utils/string.hpp>
|
||||
|
||||
namespace hex {
|
||||
|
||||
std::map<std::string, WorkspaceManager::Workspace> WorkspaceManager::s_workspaces;
|
||||
decltype(WorkspaceManager::s_workspaces)::iterator WorkspaceManager::s_currentWorkspace = s_workspaces.end();
|
||||
decltype(WorkspaceManager::s_workspaces)::iterator WorkspaceManager::s_previousWorkspace = s_workspaces.end();
|
||||
AutoReset<std::map<std::string, WorkspaceManager::Workspace>> WorkspaceManager::s_workspaces;
|
||||
decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_currentWorkspace = s_workspaces->end();
|
||||
decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_previousWorkspace = s_workspaces->end();
|
||||
decltype(WorkspaceManager::s_workspaces)::Type::iterator WorkspaceManager::s_workspaceToRemove = s_workspaces->end();
|
||||
|
||||
void WorkspaceManager::createWorkspace(const std::string& name, const std::string &layout) {
|
||||
s_workspaces[name] = Workspace {
|
||||
.layout = layout.empty() ? LayoutManager::saveToString() : layout,
|
||||
.path = {}
|
||||
};
|
||||
s_currentWorkspace = s_workspaces->insert_or_assign(name, Workspace {
|
||||
.layout = layout.empty() ? LayoutManager::saveToString() : layout,
|
||||
.path = {},
|
||||
.builtin = false
|
||||
}).first;
|
||||
|
||||
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
|
||||
if (exportToFile(path / (name + ".hexws")))
|
||||
for (const auto &workspaceFolder : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
|
||||
const auto workspacePath = workspaceFolder / (name + ".hexws");
|
||||
if (exportToFile(workspacePath)) {
|
||||
s_currentWorkspace->second.path = workspacePath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WorkspaceManager::switchWorkspace(const std::string& name) {
|
||||
const auto newWorkspace = s_workspaces.find(name);
|
||||
if (newWorkspace != s_workspaces.end()) {
|
||||
const auto newWorkspace = s_workspaces->find(name);
|
||||
if (newWorkspace != s_workspaces->end()) {
|
||||
s_currentWorkspace = newWorkspace;
|
||||
log::info("Switching to workspace '{}'", name);
|
||||
}
|
||||
}
|
||||
|
||||
void WorkspaceManager::importFromFile(const std::fs::path& path) {
|
||||
if (std::ranges::any_of(*s_workspaces, [path](const auto &pair) { return pair.second.path == path; })) {
|
||||
return;
|
||||
}
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Read);
|
||||
if (!file.isValid()) {
|
||||
log::error("Failed to load workspace from file '{}'", path.string());
|
||||
file.remove();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,63 +58,122 @@ namespace hex {
|
||||
try {
|
||||
auto json = nlohmann::json::parse(content.begin(), content.end());
|
||||
|
||||
std::string name = json["name"];
|
||||
const std::string name = json["name"];
|
||||
std::string layout = json["layout"];
|
||||
const bool builtin = json.value("builtin", false);
|
||||
|
||||
s_workspaces[name] = Workspace {
|
||||
(*s_workspaces)[name] = Workspace {
|
||||
.layout = std::move(layout),
|
||||
.path = path
|
||||
.path = path,
|
||||
.builtin = builtin
|
||||
};
|
||||
} catch (nlohmann::json::exception &e) {
|
||||
log::error("Failed to load workspace from file '{}': {}", path.string(), e.what());
|
||||
file.remove();
|
||||
}
|
||||
}
|
||||
|
||||
bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName) {
|
||||
bool WorkspaceManager::exportToFile(std::fs::path path, std::string workspaceName, bool builtin) {
|
||||
if (path.empty()) {
|
||||
if (s_currentWorkspace == s_workspaces.end())
|
||||
if (s_currentWorkspace == s_workspaces->end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
path = s_currentWorkspace->second.path;
|
||||
}
|
||||
|
||||
if (workspaceName.empty())
|
||||
if (workspaceName.empty()) {
|
||||
workspaceName = s_currentWorkspace->first;
|
||||
}
|
||||
|
||||
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Create);
|
||||
|
||||
if (!file.isValid())
|
||||
if (!file.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nlohmann::json json;
|
||||
json["name"] = workspaceName;
|
||||
json["layout"] = LayoutManager::saveToString();
|
||||
json["builtin"] = builtin;
|
||||
|
||||
file.writeString(json.dump(4));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WorkspaceManager::removeWorkspace(const std::string& name) {
|
||||
bool deletedCurrentWorkspace = false;
|
||||
for (const auto &[workspaceName, workspace] : *s_workspaces) {
|
||||
if (workspaceName == name) {
|
||||
log::info("Removing workspace file '{}'", wolv::util::toUTF8String(workspace.path));
|
||||
if (wolv::io::fs::remove(workspace.path)) {
|
||||
log::info("Removed workspace '{}'", name);
|
||||
|
||||
if (workspaceName == s_currentWorkspace->first) {
|
||||
deletedCurrentWorkspace = true;
|
||||
}
|
||||
} else {
|
||||
log::error("Failed to remove workspace '{}'", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WorkspaceManager::reload();
|
||||
|
||||
if (deletedCurrentWorkspace && !s_workspaces->empty()) {
|
||||
s_currentWorkspace = s_workspaces->begin();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WorkspaceManager::process() {
|
||||
if (s_previousWorkspace != s_currentWorkspace) {
|
||||
if (s_previousWorkspace != s_workspaces.end())
|
||||
exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first);
|
||||
log::info("Updating workspace");
|
||||
if (s_previousWorkspace != s_workspaces->end()) {
|
||||
exportToFile(s_previousWorkspace->second.path, s_previousWorkspace->first, s_previousWorkspace->second.builtin);
|
||||
}
|
||||
|
||||
LayoutManager::closeAllViews();
|
||||
ImGui::LoadIniSettingsFromMemory(s_currentWorkspace->second.layout.c_str());
|
||||
|
||||
s_previousWorkspace = s_currentWorkspace;
|
||||
|
||||
if (s_workspaceToRemove != s_workspaces->end()) {
|
||||
s_workspaces->erase(s_workspaceToRemove);
|
||||
s_workspaceToRemove = s_workspaces->end();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void WorkspaceManager::reset() {
|
||||
s_workspaces.clear();
|
||||
s_currentWorkspace = s_workspaces.end();
|
||||
s_previousWorkspace = s_workspaces.end();
|
||||
s_workspaces->clear();
|
||||
s_currentWorkspace = s_workspaces->end();
|
||||
s_previousWorkspace = s_workspaces->end();
|
||||
}
|
||||
|
||||
void WorkspaceManager::reload() {
|
||||
WorkspaceManager::reset();
|
||||
|
||||
for (const auto &defaultPath : fs::getDefaultPaths(fs::ImHexPath::Workspaces)) {
|
||||
for (const auto &entry : std::fs::directory_iterator(defaultPath)) {
|
||||
if (!entry.is_regular_file()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &path = entry.path();
|
||||
if (path.extension() != ".hexws") {
|
||||
continue;
|
||||
}
|
||||
|
||||
WorkspaceManager::importFromFile(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace hex {
|
||||
m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping);
|
||||
m_tableContent = other.m_tableContent;
|
||||
m_longestSequence = other.m_longestSequence;
|
||||
m_shortestSequence = other.m_shortestSequence;
|
||||
m_valid = other.m_valid;
|
||||
m_name = other.m_name;
|
||||
}
|
||||
@@ -23,6 +24,7 @@ namespace hex {
|
||||
m_mapping = std::move(other.m_mapping);
|
||||
m_tableContent = std::move(other.m_tableContent);
|
||||
m_longestSequence = other.m_longestSequence;
|
||||
m_shortestSequence = other.m_shortestSequence;
|
||||
m_valid = other.m_valid;
|
||||
m_name = std::move(other.m_name);
|
||||
}
|
||||
@@ -66,6 +68,7 @@ namespace hex {
|
||||
m_mapping = std::make_unique<std::map<size_t, std::map<std::vector<u8>, std::string>>>(*other.m_mapping);
|
||||
m_tableContent = other.m_tableContent;
|
||||
m_longestSequence = other.m_longestSequence;
|
||||
m_shortestSequence = other.m_shortestSequence;
|
||||
m_valid = other.m_valid;
|
||||
m_name = other.m_name;
|
||||
|
||||
@@ -76,6 +79,7 @@ namespace hex {
|
||||
m_mapping = std::move(other.m_mapping);
|
||||
m_tableContent = std::move(other.m_tableContent);
|
||||
m_longestSequence = other.m_longestSequence;
|
||||
m_shortestSequence = other.m_shortestSequence;
|
||||
m_valid = other.m_valid;
|
||||
m_name = std::move(other.m_name);
|
||||
|
||||
@@ -98,7 +102,7 @@ namespace hex {
|
||||
return { ".", 1 };
|
||||
}
|
||||
|
||||
size_t EncodingFile::getEncodingLengthFor(std::span<u8> buffer) const {
|
||||
u64 EncodingFile::getEncodingLengthFor(std::span<u8> buffer) const {
|
||||
for (auto riter = m_mapping->crbegin(); riter != m_mapping->crend(); ++riter) {
|
||||
const auto &[size, mapping] = *riter;
|
||||
|
||||
@@ -140,10 +144,11 @@ namespace hex {
|
||||
if (!m_mapping->contains(fromBytes.size()))
|
||||
m_mapping->insert({ fromBytes.size(), {} });
|
||||
|
||||
auto keySize = fromBytes.size();
|
||||
u64 keySize = fromBytes.size();
|
||||
(*m_mapping)[keySize].insert({ std::move(fromBytes), to });
|
||||
|
||||
m_longestSequence = std::max(m_longestSequence, keySize);
|
||||
m_shortestSequence = std::min(m_shortestSequence, keySize);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,19 @@
|
||||
#include <hex/helpers/logger.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
#include <hex/helpers/utils_linux.hpp>
|
||||
|
||||
#include <xdg.hpp>
|
||||
#include <hex/helpers/auto_reset.hpp>
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#include <shellapi.h>
|
||||
#elif defined(OS_LINUX) || defined(OS_WEB)
|
||||
#include <xdg.hpp>
|
||||
# if defined(OS_FREEBSD)
|
||||
#include <sys/syslimits.h>
|
||||
# else
|
||||
#include <limits.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(OS_WEB)
|
||||
@@ -22,7 +26,6 @@
|
||||
#include <nfd.hpp>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
|
||||
#include <wolv/io/file.hpp>
|
||||
@@ -31,7 +34,7 @@
|
||||
|
||||
namespace hex::fs {
|
||||
|
||||
static std::function<void(const std::string&)> s_fileBrowserErrorCallback;
|
||||
static AutoReset<std::function<void(const std::string&)>> s_fileBrowserErrorCallback;
|
||||
void setFileBrowserErrorCallback(const std::function<void(const std::string&)> &callback) {
|
||||
s_fileBrowserErrorCallback = callback;
|
||||
}
|
||||
@@ -39,8 +42,9 @@ namespace hex::fs {
|
||||
// With help from https://github.com/owncloud/client/blob/cba22aa34b3677406e0499aadd126ce1d94637a2/src/gui/openfilemanager.cpp
|
||||
void openFileExternal(const std::fs::path &filePath) {
|
||||
// Make sure the file exists before trying to open it
|
||||
if (!wolv::io::fs::exists(filePath))
|
||||
if (!wolv::io::fs::exists(filePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
hex::unused(
|
||||
@@ -57,8 +61,9 @@ namespace hex::fs {
|
||||
|
||||
void openFolderExternal(const std::fs::path &dirPath) {
|
||||
// Make sure the folder exists before trying to open it
|
||||
if (!wolv::io::fs::exists(dirPath))
|
||||
if (!wolv::io::fs::exists(dirPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
hex::unused(system(
|
||||
@@ -75,8 +80,9 @@ namespace hex::fs {
|
||||
|
||||
void openFolderWithSelectionExternal(const std::fs::path &selectedFilePath) {
|
||||
// Make sure the file exists before trying to open it
|
||||
if (!wolv::io::fs::exists(selectedFilePath))
|
||||
if (!wolv::io::fs::exists(selectedFilePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
hex::unused(system(
|
||||
@@ -153,12 +159,14 @@ namespace hex::fs {
|
||||
for (let file of selector.files) {
|
||||
const fr = new FileReader();
|
||||
fr.onload = () => {
|
||||
let path = "/openedFiles/"+file.name;
|
||||
if (FS.analyzePath(path).exists) {
|
||||
FS.unlink(path);
|
||||
let folder = "/openedFiles/"+Math.random().toString(36).substring(2)+"/";
|
||||
FS.createPath("/", folder);
|
||||
if (FS.analyzePath(folder+file.name).exists) {
|
||||
console.log(`Error: ${folder+file.name} already exist`);
|
||||
} else {
|
||||
FS.createDataFile(folder, file.name, fr.result, true, true);
|
||||
Module._fileBrowserCallback(stringToNewUTF8(folder+file.name));
|
||||
}
|
||||
FS.createDataFile("/openedFiles/", file.name, fr.result, true, true);
|
||||
Module._fileBrowserCallback(stringToNewUTF8(path));
|
||||
};
|
||||
|
||||
fr.readAsBinaryString(file);
|
||||
@@ -201,6 +209,7 @@ namespace hex::fs {
|
||||
bool openFileBrowser(DialogMode mode, const std::vector<ItemFilter> &validExtensions, const std::function<void(std::fs::path)> &callback, const std::string &defaultPath, bool multiple) {
|
||||
// Turn the content of the ItemFilter objects into something NFD understands
|
||||
std::vector<nfdfilteritem_t> validExtensionsNfd;
|
||||
validExtensionsNfd.reserve(validExtensions.size());
|
||||
for (const auto &extension : validExtensions) {
|
||||
validExtensionsNfd.emplace_back(nfdfilteritem_t{ extension.name.c_str(), extension.spec.c_str() });
|
||||
}
|
||||
@@ -212,9 +221,9 @@ namespace hex::fs {
|
||||
if (NFD::Init() != NFD_OKAY) {
|
||||
// Handle errors if initialization failed
|
||||
log::error("NFD init returned an error: {}", NFD::GetError());
|
||||
if (s_fileBrowserErrorCallback != nullptr) {
|
||||
auto error = NFD::GetError();
|
||||
s_fileBrowserErrorCallback(error != nullptr ? error : "No details");
|
||||
if (*s_fileBrowserErrorCallback != nullptr) {
|
||||
const auto error = NFD::GetError();
|
||||
(*s_fileBrowserErrorCallback)(error != nullptr ? error : "No details");
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -264,9 +273,9 @@ namespace hex::fs {
|
||||
|
||||
log::error("Requested file dialog returned an error: {}", NFD::GetError());
|
||||
|
||||
if (s_fileBrowserErrorCallback != nullptr) {
|
||||
auto error = NFD::GetError();
|
||||
s_fileBrowserErrorCallback(error != nullptr ? error : "No details");
|
||||
if (*s_fileBrowserErrorCallback != nullptr) {
|
||||
const auto error = NFD::GetError();
|
||||
(*s_fileBrowserErrorCallback)(error != nullptr ? error : "No details");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +332,7 @@ namespace hex::fs {
|
||||
|
||||
// Add additional data directories to the path
|
||||
auto additionalDirs = ImHexApi::System::getAdditionalFolderPaths();
|
||||
std::copy(additionalDirs.begin(), additionalDirs.end(), std::back_inserter(paths));
|
||||
std::ranges::copy(additionalDirs, std::back_inserter(paths));
|
||||
|
||||
// Add the project file directory to the path, if one is loaded
|
||||
if (ProjectFile::hasPath()) {
|
||||
@@ -405,6 +414,9 @@ namespace hex::fs {
|
||||
case ImHexPath::Yara:
|
||||
result = appendPath(getDataPaths(), "yara");
|
||||
break;
|
||||
case ImHexPath::YaraAdvancedAnalysis:
|
||||
result = appendPath(getDefaultPaths(ImHexPath::Yara), "advanced_analysis");
|
||||
break;
|
||||
case ImHexPath::Recent:
|
||||
result = appendPath(getConfigPaths(), "recent");
|
||||
break;
|
||||
@@ -454,7 +466,7 @@ namespace hex::fs {
|
||||
// Try to create a new file in the given path
|
||||
// If that fails, or the file cannot be deleted anymore afterward; the path is not writable
|
||||
wolv::io::File file(path / TestFileName, wolv::io::File::Mode::Create);
|
||||
bool result = file.isValid();
|
||||
const bool result = file.isValid();
|
||||
if (!file.remove())
|
||||
return false;
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
#include <hex/helpers/logger.hpp>
|
||||
|
||||
#include <hex/api/task_manager.hpp>
|
||||
#include <hex/api/event_manager.hpp>
|
||||
#include <hex/helpers/fs.hpp>
|
||||
#include <hex/helpers/fmt.hpp>
|
||||
|
||||
#include <wolv/io/file.hpp>
|
||||
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
#include <fmt/chrono.h>
|
||||
|
||||
@@ -11,81 +15,153 @@
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
|
||||
namespace hex::log::impl {
|
||||
namespace hex::log {
|
||||
|
||||
static wolv::io::File s_loggerFile;
|
||||
std::mutex g_loggerMutex;
|
||||
namespace {
|
||||
|
||||
wolv::io::File s_loggerFile;
|
||||
bool s_colorOutputEnabled = false;
|
||||
std::recursive_mutex s_loggerMutex;
|
||||
bool s_loggingSuspended = false;
|
||||
bool s_debugLoggingEnabled = false;
|
||||
|
||||
FILE *getDestination() {
|
||||
if (s_loggerFile.isValid())
|
||||
return s_loggerFile.getHandle();
|
||||
else
|
||||
return stdout;
|
||||
}
|
||||
|
||||
wolv::io::File& getFile() {
|
||||
return s_loggerFile;
|
||||
void suspendLogging() {
|
||||
s_loggingSuspended = true;
|
||||
}
|
||||
|
||||
bool isRedirected() {
|
||||
return s_loggerFile.isValid();
|
||||
void resumeLogging() {
|
||||
s_loggingSuspended = false;
|
||||
}
|
||||
|
||||
void redirectToFile() {
|
||||
if (s_loggerFile.isValid()) return;
|
||||
void enableDebugLogging() {
|
||||
s_debugLoggingEnabled = true;
|
||||
}
|
||||
|
||||
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Logs, true)) {
|
||||
wolv::io::fs::createDirectories(path);
|
||||
s_loggerFile = wolv::io::File(path / hex::format("{0:%Y%m%d_%H%M%S}.log", fmt::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()))), wolv::io::File::Mode::Create);
|
||||
s_loggerFile.disableBuffering();
|
||||
namespace impl {
|
||||
|
||||
if (s_loggerFile.isValid()) break;
|
||||
void lockLoggerMutex() {
|
||||
s_loggerMutex.lock();
|
||||
}
|
||||
}
|
||||
|
||||
void enableColorPrinting() {
|
||||
#if defined(OS_WINDOWS)
|
||||
auto hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hConsole != INVALID_HANDLE_VALUE) {
|
||||
DWORD mode = 0;
|
||||
if (::GetConsoleMode(hConsole, &mode) == TRUE) {
|
||||
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
|
||||
::SetConsoleMode(hConsole, mode);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
void unlockLoggerMutex() {
|
||||
s_loggerMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
std::vector<LogEntry>& getLogEntries() {
|
||||
static std::vector<LogEntry> logEntries;
|
||||
return logEntries;
|
||||
}
|
||||
bool isLoggingSuspended() {
|
||||
return s_loggingSuspended;
|
||||
}
|
||||
|
||||
void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level, const char *projectName) {
|
||||
const auto now = fmt::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
|
||||
|
||||
fmt::print(dest, "[{0:%H:%M:%S}] ", now);
|
||||
|
||||
if (isRedirected())
|
||||
fmt::print(dest, "{0} ", level);
|
||||
else
|
||||
fmt::print(dest, ts, "{0} ", level);
|
||||
|
||||
fmt::print(dest, "[{0}] ", projectName);
|
||||
|
||||
auto projectNameLength = std::string_view(projectName).length();
|
||||
fmt::print(dest, "{}", std::string(projectNameLength > 10 ? 0 : 10 - projectNameLength, ' '));
|
||||
}
|
||||
|
||||
void assertionHandler(bool expr, const char* exprString, const char* file, int line) {
|
||||
if (!expr) {
|
||||
log::error("Assertion failed: {} at {}:{}", exprString, file, line);
|
||||
|
||||
#if defined (DEBUG)
|
||||
std::abort();
|
||||
bool isDebugLoggingEnabled() {
|
||||
#if defined(DEBUG)
|
||||
return true;
|
||||
#else
|
||||
return s_debugLoggingEnabled;
|
||||
#endif
|
||||
}
|
||||
|
||||
FILE *getDestination() {
|
||||
if (s_loggerFile.isValid())
|
||||
return s_loggerFile.getHandle();
|
||||
else
|
||||
return stdout;
|
||||
}
|
||||
|
||||
wolv::io::File& getFile() {
|
||||
return s_loggerFile;
|
||||
}
|
||||
|
||||
bool isRedirected() {
|
||||
return s_loggerFile.isValid();
|
||||
}
|
||||
|
||||
void redirectToFile() {
|
||||
if (s_loggerFile.isValid()) return;
|
||||
|
||||
for (const auto &path : fs::getDefaultPaths(fs::ImHexPath::Logs, true)) {
|
||||
wolv::io::fs::createDirectories(path);
|
||||
s_loggerFile = wolv::io::File(path / hex::format("{0:%Y%m%d_%H%M%S}.log", fmt::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()))), wolv::io::File::Mode::Create);
|
||||
s_loggerFile.disableBuffering();
|
||||
|
||||
if (s_loggerFile.isValid()) {
|
||||
s_colorOutputEnabled = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void enableColorPrinting() {
|
||||
s_colorOutputEnabled = true;
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
auto hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hConsole != INVALID_HANDLE_VALUE) {
|
||||
DWORD mode = 0;
|
||||
if (::GetConsoleMode(hConsole, &mode) == TRUE) {
|
||||
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | ENABLE_PROCESSED_OUTPUT;
|
||||
::SetConsoleMode(hConsole, mode);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
std::vector<LogEntry>& getLogEntries() {
|
||||
static std::vector<LogEntry> logEntries;
|
||||
return logEntries;
|
||||
}
|
||||
|
||||
void addLogEntry(std::string_view project, std::string_view level, std::string_view message) {
|
||||
getLogEntries().emplace_back(project.data(), level.data(), message.data());
|
||||
}
|
||||
|
||||
|
||||
void printPrefix(FILE *dest, const fmt::text_style &ts, const std::string &level, const char *projectName) {
|
||||
const auto now = fmt::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()));
|
||||
|
||||
fmt::print(dest, "[{0:%H:%M:%S}] ", now);
|
||||
|
||||
if (s_colorOutputEnabled)
|
||||
fmt::print(dest, ts, "{0} ", level);
|
||||
else
|
||||
fmt::print(dest, "{0} ", level);
|
||||
|
||||
std::string projectThreadTag = projectName;
|
||||
if (auto threadName = TaskManager::getCurrentThreadName(); !threadName.empty())
|
||||
projectThreadTag += fmt::format(" | {0}", threadName);
|
||||
|
||||
constexpr static auto MaxTagLength = 25;
|
||||
if (projectThreadTag.length() > MaxTagLength)
|
||||
projectThreadTag.resize(MaxTagLength);
|
||||
|
||||
fmt::print(dest, "[{0}] ", projectThreadTag);
|
||||
|
||||
const auto projectNameLength = projectThreadTag.length();
|
||||
fmt::print(dest, "{0}", std::string(projectNameLength > MaxTagLength ? 0 : MaxTagLength - projectNameLength, ' '));
|
||||
}
|
||||
|
||||
void assertionHandler(bool expr, const char* exprString, const char* file, int line) {
|
||||
if (!expr) {
|
||||
log::error("Assertion failed: {} at {}:{}", exprString, file, line);
|
||||
|
||||
#if defined (DEBUG)
|
||||
std::abort();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
namespace color {
|
||||
|
||||
fmt::color debug() { return fmt::color::medium_sea_green; }
|
||||
fmt::color info() { return fmt::color::steel_blue; }
|
||||
fmt::color warn() { return fmt::color::orange; }
|
||||
fmt::color error() { return fmt::color::indian_red; }
|
||||
fmt::color fatal() { return fmt::color::medium_purple; }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -95,50 +95,119 @@ namespace hex::magic {
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string getDescription(const std::vector<u8> &data) {
|
||||
std::string getDescription(const std::vector<u8> &data, bool firstEntryOnly) {
|
||||
if (data.empty()) return "";
|
||||
|
||||
auto magicFiles = getMagicFiles();
|
||||
|
||||
if (magicFiles.has_value()) {
|
||||
magic_t ctx = magic_open(MAGIC_NONE);
|
||||
magic_t ctx = magic_open(firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE);
|
||||
ON_SCOPE_EXIT { magic_close(ctx); };
|
||||
|
||||
if (magic_load(ctx, magicFiles->c_str()) == 0) {
|
||||
if (auto result = magic_buffer(ctx, data.data(), data.size()); result != nullptr)
|
||||
if (auto description = magic_buffer(ctx, data.data(), data.size()); description != nullptr) {
|
||||
auto result = wolv::util::replaceStrings(description, "\\012-", "\n-");
|
||||
if (result.ends_with("- data"))
|
||||
result = result.substr(0, result.size() - 6);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string getDescription(prv::Provider *provider, size_t size) {
|
||||
std::string getDescription(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {
|
||||
std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);
|
||||
provider->read(provider->getBaseAddress(), buffer.data(), buffer.size());
|
||||
provider->read(address, buffer.data(), buffer.size());
|
||||
|
||||
return getDescription(buffer);
|
||||
return getDescription(buffer, firstEntryOnly);
|
||||
}
|
||||
|
||||
std::string getMIMEType(const std::vector<u8> &data) {
|
||||
std::string getMIMEType(const std::vector<u8> &data, bool firstEntryOnly) {
|
||||
if (data.empty()) return "";
|
||||
|
||||
auto magicFiles = getMagicFiles();
|
||||
|
||||
if (magicFiles.has_value()) {
|
||||
magic_t ctx = magic_open(MAGIC_MIME_TYPE);
|
||||
magic_t ctx = magic_open(MAGIC_MIME_TYPE | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE));
|
||||
ON_SCOPE_EXIT { magic_close(ctx); };
|
||||
|
||||
if (magic_load(ctx, magicFiles->c_str()) == 0) {
|
||||
if (auto result = magic_buffer(ctx, data.data(), data.size()); result != nullptr)
|
||||
if (auto mimeType = magic_buffer(ctx, data.data(), data.size()); mimeType != nullptr) {
|
||||
auto result = wolv::util::replaceStrings(mimeType, "\\012-", "\n-");
|
||||
if (result.ends_with("- application/octet-stream"))
|
||||
result = result.substr(0, result.size() - 26);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string getMIMEType(prv::Provider *provider, size_t size) {
|
||||
std::string getMIMEType(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {
|
||||
std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);
|
||||
provider->read(provider->getBaseAddress(), buffer.data(), buffer.size());
|
||||
provider->read(address, buffer.data(), buffer.size());
|
||||
|
||||
return getMIMEType(buffer);
|
||||
return getMIMEType(buffer, firstEntryOnly);
|
||||
}
|
||||
|
||||
std::string getExtensions(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {
|
||||
std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);
|
||||
provider->read(address, buffer.data(), buffer.size());
|
||||
|
||||
return getExtensions(buffer, firstEntryOnly);
|
||||
}
|
||||
|
||||
std::string getExtensions(const std::vector<u8> &data, bool firstEntryOnly) {
|
||||
if (data.empty()) return "";
|
||||
|
||||
auto magicFiles = getMagicFiles();
|
||||
|
||||
if (magicFiles.has_value()) {
|
||||
magic_t ctx = magic_open(MAGIC_EXTENSION | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE));
|
||||
ON_SCOPE_EXIT { magic_close(ctx); };
|
||||
|
||||
if (magic_load(ctx, magicFiles->c_str()) == 0) {
|
||||
if (auto extension = magic_buffer(ctx, data.data(), data.size()); extension != nullptr) {
|
||||
auto result = wolv::util::replaceStrings(extension, "\\012-", "\n-");
|
||||
if (result.ends_with("- ???"))
|
||||
result = result.substr(0, result.size() - 5);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string getAppleCreatorType(prv::Provider *provider, u64 address, size_t size, bool firstEntryOnly) {
|
||||
std::vector<u8> buffer(std::min<u64>(provider->getSize(), size), 0x00);
|
||||
provider->read(address, buffer.data(), buffer.size());
|
||||
|
||||
return getAppleCreatorType(buffer, firstEntryOnly);
|
||||
}
|
||||
|
||||
std::string getAppleCreatorType(const std::vector<u8> &data, bool firstEntryOnly) {
|
||||
if (data.empty()) return "";
|
||||
|
||||
auto magicFiles = getMagicFiles();
|
||||
|
||||
if (magicFiles.has_value()) {
|
||||
magic_t ctx = magic_open(MAGIC_APPLE | (firstEntryOnly ? MAGIC_NONE : MAGIC_CONTINUE));
|
||||
ON_SCOPE_EXIT { magic_close(ctx); };
|
||||
|
||||
if (magic_load(ctx, magicFiles->c_str()) == 0) {
|
||||
if (auto result = magic_buffer(ctx, data.data(), data.size()); result != nullptr)
|
||||
return wolv::util::replaceStrings(result, "\\012-", "\n-");
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
bool isValidMIMEType(const std::string &mimeType) {
|
||||
|
||||
@@ -434,16 +434,16 @@ namespace hex::gl {
|
||||
int k = 0;
|
||||
int l = 0;
|
||||
for (u32 j = 0; j <= m_slices; ++j) {
|
||||
float z = 2.0f * float(j) / float(m_slices) - 1.0f;
|
||||
float z = 2.0F * float(j) / float(m_slices) - 1.0F;
|
||||
for (u32 i = 0; i <= m_slices; ++i) {
|
||||
m_vertices[k ] = 2.0f * float(i) / float(m_slices) - 1.0f;
|
||||
m_vertices[k + 1] = 0.0f;
|
||||
m_vertices[k ] = 2.0F * float(i) / float(m_slices) - 1.0F;
|
||||
m_vertices[k + 1] = 0.0F;
|
||||
m_vertices[k + 2] = z;
|
||||
k += 3;
|
||||
m_colors[l ] = 0.5f;
|
||||
m_colors[l + 1] = 0.5f;
|
||||
m_colors[l + 2] = 0.5f;
|
||||
m_colors[l + 3] = 0.3f;
|
||||
m_colors[l ] = 0.5F;
|
||||
m_colors[l + 1] = 0.5F;
|
||||
m_colors[l + 2] = 0.5F;
|
||||
m_colors[l + 3] = 0.3F;
|
||||
l += 4;
|
||||
}
|
||||
}
|
||||
@@ -540,10 +540,10 @@ namespace hex::gl {
|
||||
m_vertices[n + 2] = m_radius * z;
|
||||
|
||||
n = (i * m_resolution + j + 1) * 4;
|
||||
m_colors[n] = 1.0f;
|
||||
m_colors[n + 1] = 1.0f;
|
||||
m_colors[n + 2] = 1.0f;
|
||||
m_colors[n + 3] = 1.0f;
|
||||
m_colors[n] = 1.0F;
|
||||
m_colors[n + 1] = 1.0F;
|
||||
m_colors[n + 2] = 1.0F;
|
||||
m_colors[n + 3] = 1.0F;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace hex {
|
||||
result.push_back(addressBytes[2]);
|
||||
result.push_back(addressBytes[1]);
|
||||
result.push_back(addressBytes[0]);
|
||||
pushBytesBack<u16>(result, changeEndianess<u16>(bytes.size(), std::endian::big));
|
||||
pushBytesBack<u16>(result, changeEndianness<u16>(bytes.size(), std::endian::big));
|
||||
|
||||
for (auto byte : bytes)
|
||||
result.push_back(byte);
|
||||
@@ -189,7 +189,7 @@ namespace hex {
|
||||
result.push_back(addressBytes[2]);
|
||||
result.push_back(addressBytes[1]);
|
||||
result.push_back(addressBytes[0]);
|
||||
pushBytesBack<u16>(result, changeEndianess<u16>(bytes.size(), std::endian::big));
|
||||
pushBytesBack<u16>(result, changeEndianness<u16>(bytes.size(), std::endian::big));
|
||||
|
||||
for (auto byte : bytes)
|
||||
result.push_back(byte);
|
||||
|
||||
@@ -90,11 +90,7 @@ namespace hex {
|
||||
bool Tar::contains(const std::fs::path &path) const {
|
||||
mtar_header_t header;
|
||||
|
||||
auto fixedPath = path.string();
|
||||
#if defined(OS_WINDOWS)
|
||||
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
|
||||
#endif
|
||||
|
||||
const auto fixedPath = wolv::io::fs::toNormalizedPathString(path);
|
||||
return mtar_find(m_ctx.get(), fixedPath.c_str(), &header) == MTAR_ESUCCESS;
|
||||
}
|
||||
|
||||
@@ -115,10 +111,7 @@ namespace hex {
|
||||
std::vector<u8> Tar::readVector(const std::fs::path &path) const {
|
||||
mtar_header_t header;
|
||||
|
||||
auto fixedPath = path.string();
|
||||
#if defined(OS_WINDOWS)
|
||||
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
|
||||
#endif
|
||||
const auto fixedPath = wolv::io::fs::toNormalizedPathString(path);
|
||||
int ret = mtar_find(m_ctx.get(), fixedPath.c_str(), &header);
|
||||
if (ret != MTAR_ESUCCESS){
|
||||
log::debug("Failed to read vector from path {} in tarred file {}: {}",
|
||||
@@ -143,18 +136,12 @@ namespace hex {
|
||||
for (const auto &part : path.parent_path()) {
|
||||
pathPart /= part;
|
||||
|
||||
auto fixedPath = pathPart.string();
|
||||
#if defined(OS_WINDOWS)
|
||||
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
|
||||
#endif
|
||||
auto fixedPath = wolv::io::fs::toNormalizedPathString(pathPart);
|
||||
mtar_write_dir_header(m_ctx.get(), fixedPath.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
auto fixedPath = path.string();
|
||||
#if defined(OS_WINDOWS)
|
||||
std::replace(fixedPath.begin(), fixedPath.end(), '\\', '/');
|
||||
#endif
|
||||
const auto fixedPath = wolv::io::fs::toNormalizedPathString(path);
|
||||
mtar_write_file_header(m_ctx.get(), fixedPath.c_str(), data.size());
|
||||
mtar_write_data(m_ctx.get(), data.data(), data.size());
|
||||
}
|
||||
|
||||
@@ -11,11 +11,16 @@
|
||||
|
||||
#if defined(OS_WINDOWS)
|
||||
#include <windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <wolv/utils/guards.hpp>
|
||||
#elif defined(OS_LINUX)
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <hex/helpers/utils_linux.hpp>
|
||||
#elif defined(OS_MACOS)
|
||||
#include <unistd.h>
|
||||
#include <dlfcn.h>
|
||||
#include <hex/helpers/utils_macos.hpp>
|
||||
#elif defined(OS_WEB)
|
||||
#include "emscripten.h"
|
||||
@@ -550,6 +555,60 @@ namespace hex {
|
||||
return utf16;
|
||||
}
|
||||
|
||||
std::string utf16ToUtf8(const std::wstring& utf16) {
|
||||
std::vector<u32> unicodes;
|
||||
|
||||
for (size_t index = 0; index < utf16.size();) {
|
||||
u32 unicode = 0;
|
||||
|
||||
wchar_t wch = utf16[index];
|
||||
index += 1;
|
||||
|
||||
if (wch < 0xD800 || wch > 0xDFFF) {
|
||||
unicode = static_cast<u32>(wch);
|
||||
} else if (wch >= 0xD800 && wch <= 0xDBFF) {
|
||||
if (index == utf16.size())
|
||||
return "";
|
||||
|
||||
wchar_t nextWch = utf16[index];
|
||||
index += 1;
|
||||
|
||||
if (nextWch < 0xDC00 || nextWch > 0xDFFF)
|
||||
return "";
|
||||
|
||||
unicode = static_cast<u32>(((wch - 0xD800) << 10) + (nextWch - 0xDC00) + 0x10000);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
|
||||
unicodes.push_back(unicode);
|
||||
}
|
||||
|
||||
std::string utf8;
|
||||
|
||||
for (auto unicode : unicodes) {
|
||||
if (unicode <= 0x7F) {
|
||||
utf8 += static_cast<char>(unicode);
|
||||
} else if (unicode <= 0x7FF) {
|
||||
utf8 += static_cast<char>(0xC0 | ((unicode >> 6) & 0x1F));
|
||||
utf8 += static_cast<char>(0x80 | (unicode & 0x3F));
|
||||
} else if (unicode <= 0xFFFF) {
|
||||
utf8 += static_cast<char>(0xE0 | ((unicode >> 12) & 0x0F));
|
||||
utf8 += static_cast<char>(0x80 | ((unicode >> 6) & 0x3F));
|
||||
utf8 += static_cast<char>(0x80 | (unicode & 0x3F));
|
||||
} else if (unicode <= 0x10FFFF) {
|
||||
utf8 += static_cast<char>(0xF0 | ((unicode >> 18) & 0x07));
|
||||
utf8 += static_cast<char>(0x80 | ((unicode >> 12) & 0x3F));
|
||||
utf8 += static_cast<char>(0x80 | ((unicode >> 6) & 0x3F));
|
||||
utf8 += static_cast<char>(0x80 | (unicode & 0x3F));
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
return utf8;
|
||||
}
|
||||
|
||||
float float16ToFloat32(u16 float16) {
|
||||
u32 sign = float16 >> 15;
|
||||
u32 exponent = (float16 >> 10) & 0x1F;
|
||||
@@ -620,13 +679,23 @@ namespace hex {
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::optional<std::fs::path> fileToOpen;
|
||||
static std::optional<std::fs::path> s_fileToOpen;
|
||||
extern "C" void openFile(const char *path) {
|
||||
fileToOpen = path;
|
||||
log::info("Opening file: {0}", path);
|
||||
s_fileToOpen = path;
|
||||
}
|
||||
|
||||
std::optional<std::fs::path> getInitialFilePath() {
|
||||
return fileToOpen;
|
||||
return s_fileToOpen;
|
||||
}
|
||||
|
||||
static std::map<std::fs::path, std::string> s_fonts;
|
||||
extern "C" void registerFont(const char *fontName, const char *fontPath) {
|
||||
s_fonts[fontPath] = fontName;
|
||||
}
|
||||
|
||||
const std::map<std::fs::path, std::string>& getFonts() {
|
||||
return s_fonts;
|
||||
}
|
||||
|
||||
namespace {
|
||||
@@ -695,4 +764,46 @@ namespace hex {
|
||||
return generateHexViewImpl(offset, data.begin(), data.end());
|
||||
}
|
||||
|
||||
std::string formatSystemError(i32 error) {
|
||||
#if defined(OS_WINDOWS)
|
||||
wchar_t *message = nullptr;
|
||||
auto wLength = FormatMessageW(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
(wchar_t*)&message, 0,
|
||||
nullptr
|
||||
);
|
||||
ON_SCOPE_EXIT { LocalFree(message); };
|
||||
|
||||
auto length = ::WideCharToMultiByte(CP_UTF8, 0, message, wLength, nullptr, 0, nullptr, nullptr);
|
||||
std::string result(length, '\x00');
|
||||
::WideCharToMultiByte(CP_UTF8, 0, message, wLength, result.data(), length, nullptr, nullptr);
|
||||
|
||||
return result;
|
||||
#else
|
||||
return std::system_category().message(error);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void* getContainingModule(void* symbol) {
|
||||
#if defined(OS_WINDOWS)
|
||||
MEMORY_BASIC_INFORMATION mbi;
|
||||
if (VirtualQuery(symbol, &mbi, sizeof(mbi)))
|
||||
return mbi.AllocationBase;
|
||||
|
||||
return nullptr;
|
||||
#elif !defined(OS_WEB)
|
||||
Dl_info info = {};
|
||||
if (dladdr(symbol, &info) == 0)
|
||||
return nullptr;
|
||||
|
||||
return dlopen(info.dli_fname, RTLD_LAZY);
|
||||
#else
|
||||
hex::unused(symbol);
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,8 +3,9 @@
|
||||
#include <CoreFoundation/CFBundle.h>
|
||||
#include <ApplicationServices/ApplicationServices.h>
|
||||
#include <Foundation/NSUserDefaults.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <AppKit/NSScreen.h>
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <CoreText/CoreText.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
@@ -23,6 +24,7 @@
|
||||
}
|
||||
|
||||
void openFile(const char *path);
|
||||
void registerFont(const char *fontName, const char *fontPath);
|
||||
|
||||
void openWebpageMacos(const char *url) {
|
||||
CFURLRef urlRef = CFURLCreateWithBytes(NULL, (uint8_t*)(url), strlen(url), kCFStringEncodingASCII, NULL);
|
||||
@@ -44,9 +46,76 @@
|
||||
return [[NSScreen mainScreen] backingScaleFactor];
|
||||
}
|
||||
|
||||
void setupMacosWindowStyle(GLFWwindow *window) {
|
||||
void setupMacosWindowStyle(GLFWwindow *window, bool borderlessWindowMode) {
|
||||
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
|
||||
|
||||
cocoaWindow.titleVisibility = NSWindowTitleHidden;
|
||||
|
||||
if (borderlessWindowMode) {
|
||||
cocoaWindow.titlebarAppearsTransparent = YES;
|
||||
cocoaWindow.styleMask |= NSWindowStyleMaskFullSizeContentView;
|
||||
|
||||
[cocoaWindow setOpaque:NO];
|
||||
[cocoaWindow setHasShadow:YES];
|
||||
[cocoaWindow setBackgroundColor:[NSColor colorWithWhite: 0 alpha: 0.001f]];
|
||||
}
|
||||
}
|
||||
|
||||
bool isMacosFullScreenModeEnabled(GLFWwindow *window) {
|
||||
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
|
||||
return (cocoaWindow.styleMask & NSWindowStyleMaskFullScreen) == NSWindowStyleMaskFullScreen;
|
||||
}
|
||||
|
||||
void enumerateFontsMacos(void) {
|
||||
CFArrayRef fontDescriptors = CTFontManagerCopyAvailableFontFamilyNames();
|
||||
CFIndex count = CFArrayGetCount(fontDescriptors);
|
||||
|
||||
for (CFIndex i = 0; i < count; i++) {
|
||||
CFStringRef fontName = (CFStringRef)CFArrayGetValueAtIndex(fontDescriptors, i);
|
||||
|
||||
// Get font path
|
||||
CFDictionaryRef attributes = (__bridge CFDictionaryRef)@{ (__bridge NSString *)kCTFontNameAttribute : (__bridge NSString *)fontName };
|
||||
CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(attributes);
|
||||
CFURLRef fontURL = CTFontDescriptorCopyAttribute(descriptor, kCTFontURLAttribute);
|
||||
CFStringRef fontPath = CFURLCopyFileSystemPath(fontURL, kCFURLPOSIXPathStyle);
|
||||
|
||||
registerFont([(__bridge NSString *)fontName UTF8String], [(__bridge NSString *)fontPath UTF8String]);
|
||||
|
||||
CFRelease(descriptor);
|
||||
CFRelease(fontURL);
|
||||
}
|
||||
|
||||
CFRelease(fontDescriptors);
|
||||
}
|
||||
|
||||
void macosHandleTitlebarDoubleClickGesture(GLFWwindow *window) {
|
||||
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
|
||||
|
||||
// Consult user preferences: "System Settings -> Desktop & Dock -> Double-click a window's title bar to"
|
||||
NSString* action = [[NSUserDefaults standardUserDefaults] stringForKey:@"AppleActionOnDoubleClick"];
|
||||
|
||||
if (action == nil || [action isEqualToString:@"None"]) {
|
||||
// Nothing to do
|
||||
} else if ([action isEqualToString:@"Minimize"]) {
|
||||
if ([cocoaWindow isMiniaturizable]) {
|
||||
[cocoaWindow miniaturize:nil];
|
||||
}
|
||||
} else if ([action isEqualToString:@"Maximize"]) {
|
||||
// `[NSWindow zoom:_ sender]` takes over pumping the main runloop for the duration of the resize,
|
||||
// and would interfere with our renderer's frame logic. Schedule it for the next frame
|
||||
|
||||
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
|
||||
if ([cocoaWindow isZoomable]) {
|
||||
[cocoaWindow zoom:nil];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool macosIsWindowBeingResizedByUser(GLFWwindow *window) {
|
||||
NSWindow* cocoaWindow = glfwGetCocoaWindow(window);
|
||||
|
||||
return cocoaWindow.inLiveResize;
|
||||
}
|
||||
|
||||
@interface HexDocument : NSDocument
|
||||
@@ -59,6 +128,10 @@
|
||||
NSString* urlString = [url absoluteString];
|
||||
const char* utf8String = [urlString UTF8String];
|
||||
|
||||
const char *prefix = "file://";
|
||||
if (strncmp(utf8String, prefix, strlen(prefix)) == 0)
|
||||
utf8String += strlen(prefix);
|
||||
|
||||
openFile(utf8String);
|
||||
|
||||
return YES;
|
||||
|
||||
@@ -31,41 +31,4 @@ namespace hex::prv {
|
||||
m_data.resize(newSize);
|
||||
}
|
||||
|
||||
void MemoryProvider::insertRaw(u64 offset, u64 size) {
|
||||
auto oldSize = this->getActualSize();
|
||||
this->resizeRaw(oldSize + size);
|
||||
|
||||
std::vector<u8> buffer(0x1000);
|
||||
const std::vector<u8> zeroBuffer(0x1000);
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
const auto readSize = std::min<size_t>(position - offset, buffer.size());
|
||||
|
||||
position -= readSize;
|
||||
|
||||
this->readRaw(position, buffer.data(), readSize);
|
||||
this->writeRaw(position, zeroBuffer.data(), readSize);
|
||||
this->writeRaw(position + size, buffer.data(), readSize);
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryProvider::removeRaw(u64 offset, u64 size) {
|
||||
auto oldSize = this->getActualSize();
|
||||
std::vector<u8> buffer(0x1000);
|
||||
|
||||
const auto newSize = oldSize - size;
|
||||
auto position = offset;
|
||||
while (position < newSize) {
|
||||
const auto readSize = std::min<size_t>(newSize - position, buffer.size());
|
||||
|
||||
this->readRaw(position + size, buffer.data(), readSize);
|
||||
this->writeRaw(position, buffer.data(), readSize);
|
||||
|
||||
position += readSize;
|
||||
}
|
||||
|
||||
this->resizeRaw(oldSize - size);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,11 +43,17 @@ namespace hex::prv {
|
||||
}
|
||||
|
||||
void Provider::write(u64 offset, const void *buffer, size_t size) {
|
||||
if (!this->isWritable())
|
||||
return;
|
||||
|
||||
EventProviderDataModified::post(this, offset, size, static_cast<const u8*>(buffer));
|
||||
this->markDirty();
|
||||
}
|
||||
|
||||
void Provider::save() {
|
||||
if (!this->isWritable())
|
||||
return;
|
||||
|
||||
EventProviderSaved::post(this);
|
||||
}
|
||||
void Provider::saveAs(const std::fs::path &path) {
|
||||
@@ -68,7 +74,11 @@ namespace hex::prv {
|
||||
}
|
||||
}
|
||||
|
||||
void Provider::resize(u64 newSize) {
|
||||
bool Provider::resize(u64 newSize) {
|
||||
if (newSize >> 63) {
|
||||
log::error("new provider size is very large ({}). Is it a negative number ?", newSize);
|
||||
return false;
|
||||
}
|
||||
i64 difference = newSize - this->getActualSize();
|
||||
|
||||
if (difference > 0)
|
||||
@@ -77,6 +87,7 @@ namespace hex::prv {
|
||||
EventProviderDataRemoved::post(this, this->getActualSize() + difference, -difference);
|
||||
|
||||
this->markDirty();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Provider::insert(u64 offset, u64 size) {
|
||||
@@ -91,6 +102,52 @@ namespace hex::prv {
|
||||
this->markDirty();
|
||||
}
|
||||
|
||||
void Provider::insertRaw(u64 offset, u64 size) {
|
||||
auto oldSize = this->getActualSize();
|
||||
this->resizeRaw(oldSize + size);
|
||||
|
||||
std::vector<u8> buffer(0x1000);
|
||||
const std::vector<u8> zeroBuffer(0x1000);
|
||||
|
||||
auto position = oldSize;
|
||||
while (position > offset) {
|
||||
const auto readSize = std::min<size_t>(position - offset, buffer.size());
|
||||
|
||||
position -= readSize;
|
||||
|
||||
this->readRaw(position, buffer.data(), readSize);
|
||||
this->writeRaw(position, zeroBuffer.data(), readSize);
|
||||
this->writeRaw(position + size, buffer.data(), readSize);
|
||||
}
|
||||
}
|
||||
|
||||
void Provider::removeRaw(u64 offset, u64 size) {
|
||||
if (offset > this->getActualSize() || size == 0)
|
||||
return;
|
||||
|
||||
if ((offset + size) > this->getActualSize())
|
||||
size = this->getActualSize() - offset;
|
||||
|
||||
auto oldSize = this->getActualSize();
|
||||
|
||||
std::vector<u8> buffer(0x1000);
|
||||
|
||||
const auto newSize = oldSize - size;
|
||||
auto position = offset;
|
||||
while (position < newSize) {
|
||||
const auto readSize = std::min<size_t>(newSize - position, buffer.size());
|
||||
|
||||
this->readRaw(position + size, buffer.data(), readSize);
|
||||
this->writeRaw(position, buffer.data(), readSize);
|
||||
|
||||
position += readSize;
|
||||
}
|
||||
|
||||
this->resizeRaw(newSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Provider::applyOverlays(u64 offset, void *buffer, size_t size) const {
|
||||
for (auto &overlay : m_overlays) {
|
||||
auto overlayOffset = overlay->getAddress();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user