Compare commits
687 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8e4451d1ab | |||
| 0b5c6350ae | |||
| 48cd56acc2 | |||
| 71e16bfac3 | |||
| b7c579adf9 | |||
| 461fa6a88a | |||
| dfee46604a | |||
| a7c9827ad4 | |||
| f580bee32c | |||
| de9d859f4a | |||
| 3aaa0172d5 | |||
| 29e7eda887 | |||
| 7cb32a3680 | |||
| a4d4ab1df9 | |||
| 4bef176e9b | |||
| d2de8a87bf | |||
| 12cad7c393 | |||
| be1238cc95 | |||
| 67378c7dea | |||
| c13039396c | |||
| e940632d93 | |||
| 6db25aa7fa | |||
| 0012f39384 | |||
| 0e7e5b59ae | |||
| d459da1378 | |||
| 3063369322 | |||
| a90ad62fc0 | |||
| cd01a1eb09 | |||
| 090a769bf6 | |||
| 014846cf31 | |||
| b83cb2dbf5 | |||
| 112b7f6571 | |||
| 68a878264c | |||
| a771f168eb | |||
| 79b5b79de1 | |||
| 7cc8380330 | |||
| e91df31f8c | |||
| 663b2c9c5e | |||
| 801467e565 | |||
| 447d2b6f93 | |||
| 9ccf39b27f | |||
| d4af8dd89a | |||
| 932f31e37b | |||
| 0878db4e17 | |||
| 037586d7bb | |||
| cb7cf40b5e | |||
| 3632412c48 | |||
| 4b858648e5 | |||
| 85bebebb36 | |||
| 980be9e0e8 | |||
| ad1027455a | |||
| 9dc238187f | |||
| 73d45f3bf0 | |||
| 96400234a8 | |||
| 2e5f321583 | |||
| 5a9b29b73a | |||
| 1b2260132b | |||
| 1479468730 | |||
| f0b7afd7cc | |||
| 870d26f9d8 | |||
| f6d465667a | |||
| aaae30cb84 | |||
| fbb9396813 | |||
| b9a4abce0e | |||
| d0cc5c0fc7 | |||
| b7c2bfbcde | |||
| 8be2a55ccc | |||
| 81270f3142 | |||
| eb0d006168 | |||
| a47bda026b | |||
| 5129d0da45 | |||
| b86ec2ffcf | |||
| 20eb4352fb | |||
| e76d8eb046 | |||
| c8fefc4434 | |||
| 2c18d67769 | |||
| 428771f1d7 | |||
| 038148bf06 | |||
| 6786734d95 | |||
| 1446254a03 | |||
| 3bcd76bd4e | |||
| 0bcece4585 | |||
| 035ab590ec | |||
| 8e6e57de86 | |||
| 4c1026436f | |||
| 9f97bab717 | |||
| 76518d4c59 | |||
| eba7d3b172 | |||
| b8311caa6b | |||
| 5b044a93c1 | |||
| f009d36811 | |||
| f43260bd58 | |||
| cf384795d2 | |||
| 423cc1685c | |||
| 0a23a5ef50 | |||
| 629d608d4f | |||
| 00be247913 | |||
| 5ed0cc293d | |||
| 5401cb7392 | |||
| e26ae9899c | |||
| 48296c2ba6 | |||
| b858fceb4f | |||
| 195fb1cf66 | |||
| 52acc23ecf | |||
| 0a34fc4bcd | |||
| bec91aab7b | |||
| 5a049d41fd | |||
| 6f4dbf4fcd | |||
| bf66c36ba4 | |||
| 0c10f47e85 | |||
| 1bad9a939f | |||
| 8d6504d6e3 | |||
| 04c187deb5 | |||
| bc938ce630 | |||
| e5f95a450d | |||
| b31d38eef7 | |||
| db402cb8af | |||
| 1af6550db7 | |||
| 7795f5207f | |||
| a9b8a50c31 | |||
| a9418fa1e7 | |||
| 08f48d27e8 | |||
| 25a66da4ca | |||
| ff61bc9e7c | |||
| 3e8f4ba018 | |||
| f9eb6a31bd | |||
| 4f8cac731b | |||
| 3e496f6625 | |||
| 2d62f9c4c5 | |||
| 78811b8816 | |||
| e38cd5149a | |||
| dc2a1e270a | |||
| 44ab50553d | |||
| 7c683c368d | |||
| 62ca7a9b60 | |||
| 632e36ab44 | |||
| 0057c89eaa | |||
| 9e9a55df46 | |||
| 9463238490 | |||
| b7ff2ecffb | |||
| fad89f1c3b | |||
| 5063eefa96 | |||
| 1b34fa26ac | |||
| 25827c12a3 | |||
| cb32fd15ee | |||
| d6dcab443d | |||
| c32fec774f | |||
| ddac4686a7 | |||
| 2865865382 | |||
| 7ce835e878 | |||
| 7e0fdbbe99 | |||
| fd1f2ca1c6 | |||
| a6a92f7b6b | |||
| 4f85f151fd | |||
| 478ae0715e | |||
| 714eb6dcc5 | |||
| 89942ed92a | |||
| 6ed9251f44 | |||
| 2c32a721fc | |||
| 08aaae7999 | |||
| 78bad361ab | |||
| 2005c89baa | |||
| 8c6aa9db31 | |||
| 58eef983e2 | |||
| 390b192f0a | |||
| 7b40859eff | |||
| d0126f0fa0 | |||
| 0c034ad7de | |||
| aeeceb7d0b | |||
| d5f16ce272 | |||
| c89362f6a2 | |||
| 8a029159cd | |||
| 891ebd0cb1 | |||
| 382509d778 | |||
| f9ee8978ff | |||
| e71c2df39d | |||
| cfc7865f82 | |||
| cf6606065d | |||
| 9feb92df1b | |||
| be26810cd7 | |||
| df79b6c238 | |||
| 82c86ed2f7 | |||
| 9077563dac | |||
| 8d15f44b33 | |||
| b3fbada10e | |||
| 31cdd7bdd2 | |||
| c464e90d5d | |||
| 670d2b191c | |||
| 7bddb87306 | |||
| 2025f02105 | |||
| 1078626eea | |||
| 16c6b44f55 | |||
| 6b3b06df25 | |||
| e3b3b0cda7 | |||
| bb2cc196a6 | |||
| d3bc96a201 | |||
| 11ba6e45ab | |||
| a742501a4f | |||
| 70346028c5 | |||
| 3359458175 | |||
| 6784301376 | |||
| 51d19e7c31 | |||
| 0652870204 | |||
| 67dd6754a6 | |||
| 34274ec391 | |||
| 28feea0447 | |||
| 53267e283b | |||
| 15ed963a29 | |||
| c652d0b183 | |||
| 22c55cbcfa | |||
| c24d89dd34 | |||
| f10ea1fb9b | |||
| eb72cc9f04 | |||
| ee288340b0 | |||
| a69248299d | |||
| caa83d20f7 | |||
| 486d48e4f8 | |||
| 8480674708 | |||
| 7c62574e59 | |||
| 22584fb2d1 | |||
| 90ac9165e2 | |||
| bcb69a54b2 | |||
| f93cb262ba | |||
| 67949bb5b7 | |||
| e56da4eb46 | |||
| f7fdfe52bc | |||
| 7de0ff337b | |||
| e1c0a917fa | |||
| ead180aa60 | |||
| 58d367d704 | |||
| a1ab6c28c1 | |||
| 59e46f09b7 | |||
| 7766076042 | |||
| b96864c7e6 | |||
| 79513f06a4 | |||
| 00ff53499a | |||
| 142b44d4be | |||
| 7f28489c70 | |||
| a58c12db0b | |||
| 12e1be2626 | |||
| f81674232e | |||
| eb086b0e3f | |||
| b6b9085773 | |||
| 241989c5fa | |||
| f944573b3c | |||
| c66b155dcb | |||
| 5662867730 | |||
| dbecf0fa50 | |||
| 62cb532460 | |||
| f9bc682115 | |||
| 6cf0c5cd6d | |||
| 8a2a76da1e | |||
| e07484f057 | |||
| 0070fce286 | |||
| a2e8e3090c | |||
| 0ac68def49 | |||
| def364fe28 | |||
| c06707c891 | |||
| d7a8d385d8 | |||
| 3567c43fb5 | |||
| aaaeb66cc8 | |||
| 4ed76a15ff | |||
| b5837d1271 | |||
| e4c3b1c2bd | |||
| ea916fd7e1 | |||
| 4bee8dc37f | |||
| 8b01d05146 | |||
| 376e564cff | |||
| caf02d1841 | |||
| 6cf73fad13 | |||
| cfe660ab35 | |||
| 8eaf76394c | |||
| a692adefd7 | |||
| e05f1c37ad | |||
| 923fcc3b3d | |||
| c50b9bc4b4 | |||
| e1f95ceb54 | |||
| eb77495d5f | |||
| 270d602a26 | |||
| dd41ee68de | |||
| 46dc40f3f1 | |||
| 7c7af2e102 | |||
| 36189b8c3b | |||
| cde802b16c | |||
| 1500e5f588 | |||
| fbe91fb245 | |||
| 321e7d6977 | |||
| 56488a4ac9 | |||
| 4a75f44d47 | |||
| deb4b03200 | |||
| df94c906da | |||
| 4d50d9daa4 | |||
| 0624bdc72d | |||
| ccec6e53c0 | |||
| 68d411918d | |||
| f14d47bfe6 | |||
| 0af452720e | |||
| db8e6fe7a7 | |||
| 631d7d0ce7 | |||
| ed345e5b65 | |||
| d3a66af7d6 | |||
| 52deff06ba | |||
| 838cbe1840 | |||
| 70804e2ba6 | |||
| 07c925223b | |||
| c2cbc62be6 | |||
| 9d74d42f2a | |||
| a36f25c7b2 | |||
| 86fb5412fa | |||
| c5b9ab3aca | |||
| bf6f43ec3a | |||
| 36159c2bd4 | |||
| 72e1d03860 | |||
| ed1b8f2545 | |||
| 1ad0f24ecd | |||
| 586440622b | |||
| 6231b07d2a | |||
| 1dcf5e4f85 | |||
| 9ba8f4795c | |||
| 951fe3491e | |||
| 6ae1f1b02c | |||
| 673e84920b | |||
| d1e1c14dc3 | |||
| 11db3151ae | |||
| 65f81aca0f | |||
| 187216096d | |||
| 3f9b699eef | |||
| 5f8564c655 | |||
| cbe0000d23 | |||
| 0e278d2299 | |||
| 21d725daa5 | |||
| 247e90b3d0 | |||
| 2b8eb8deb6 | |||
| 7262f3d0b7 | |||
| 6f80fd0063 | |||
| 7a51b7a019 | |||
| d9eb31000d | |||
| f8589de476 | |||
| f1c61067bc | |||
| 8a5620592b | |||
| 5227e65808 | |||
| c4c6dc19a5 | |||
| 7fc26b1fab | |||
| c0e121abef | |||
| 7db13fdeff | |||
| e68027b464 | |||
| 220b69058c | |||
| 9c3e55787e | |||
| c952e99f3e | |||
| 35322b5d14 | |||
| 6b96807112 | |||
| 57621d1c4e | |||
| e04b2eebc3 | |||
| 02b684b810 | |||
| 76b1acf99d | |||
| 18c120ab9f | |||
| f87dd91b52 | |||
| 4a8a161a43 | |||
| e3dec420a2 | |||
| 1e32311a65 | |||
| 92e5bb88e8 | |||
| 66581d115b | |||
| 4399324955 | |||
| 733f31f90e | |||
| bef2c50b03 | |||
| a96b4412d3 | |||
| bee76fc8d1 | |||
| d9cb1a6319 | |||
| 567d23d856 | |||
| c94a98e15a | |||
| 72e6e87d82 | |||
| 0055d84bd6 | |||
| d17d22139e | |||
| 87b6284275 | |||
| 2c6b974f24 | |||
| 020acc8235 | |||
| a8e0d261b7 | |||
| 6357b9a04f | |||
| 43a58df8a0 | |||
| b7bf316c1a | |||
| 41ee792f95 | |||
| 6c1b6c1704 | |||
| acadaeed3a | |||
| 96bde3df81 | |||
| 5cb70ddbd5 | |||
| b9b9a0465f | |||
| bd269bccaf | |||
| 5f618814f6 | |||
| 43c87e99b0 | |||
| 74a09a6338 | |||
| 2b9c407dc3 | |||
| 59ed222205 | |||
| 2c06043617 | |||
| ef884642e4 | |||
| e449111c33 | |||
| 066e53da55 | |||
| c74b2cb085 | |||
| eec11bfba9 | |||
| d254a5736b | |||
| b655e4aa47 | |||
| 1adec3d8e5 | |||
| db9b7db531 | |||
| 05099e2ae1 | |||
| 77352a2a86 | |||
| ae633292c0 | |||
| d6042cf891 | |||
| 534c63bf57 | |||
| fb29933d3d | |||
| 360c4d1554 | |||
| 4cfa9b11f3 | |||
| 908465b274 | |||
| a7f10d142e | |||
| e855673802 | |||
| 112ac7bd13 | |||
| ec7963df3d | |||
| 343d006ae2 | |||
| ed8e5498d1 | |||
| 3cfead4ca6 | |||
| c8947eee6a | |||
| 6c1ca7d797 | |||
| 2172974db9 | |||
| ba03df8511 | |||
| fb47d1f788 | |||
| 3c86e14ea4 | |||
| c16fb61bea | |||
| 2acead357b | |||
| 235db57f0e | |||
| cfbde005fb | |||
| 0a865bd9dc | |||
| 6b3f722ff0 | |||
| 3dbecada4e | |||
| d21f87af5d | |||
| 0bfec59af8 | |||
| 035a76f26d | |||
| 42f4e831a2 | |||
| d3686dbb75 | |||
| fb1c790350 | |||
| 0ef5743261 | |||
| e82bef425f | |||
| 24c246a9f6 | |||
| e39348ad96 | |||
| ca5f0444bf | |||
| 4a883ba6ea | |||
| bfe1a8673a | |||
| 0445ef393f | |||
| 7eb16e13bb | |||
| 03ad5c6830 | |||
| ab23e5ca0e | |||
| 35ecd00559 | |||
| e5603fec1e | |||
| e321765c54 | |||
| 62fdcf50ea | |||
| 09d293592e | |||
| f83c2f0b6b | |||
| d775c8dc73 | |||
| 8e07b19bc7 | |||
| cb8ef46ec7 | |||
| ee814cfd0c | |||
| a6a5292cd7 | |||
| b59f142d4e | |||
| 666a18f5e5 | |||
| dff4392c10 | |||
| b09b7c1184 | |||
| 6e05dcadb6 | |||
| bc7acf9f7a | |||
| cb19316a17 | |||
| 33c3977036 | |||
| 7a015b6fc0 | |||
| aa3aef4beb | |||
| 12a48fc6d1 | |||
| 72284b4530 | |||
| 65c04e4ddd | |||
| b382d3b3e9 | |||
| 61b882b2a6 | |||
| 36c8654fb8 | |||
| d25c401aec | |||
| aac874a842 | |||
| 5d934c8759 | |||
| e055d16b2c | |||
| 38745e5782 | |||
| d0c271e534 | |||
| 33635c5723 | |||
| f8d2830ac7 | |||
| a7e5c255b4 | |||
| 062c605eb1 | |||
| 2602be426f | |||
| 51dcb4a79d | |||
| 24e4e329ed | |||
| eb66302907 | |||
| b16cc618b5 | |||
| 77aa9e58f2 | |||
| 3a0b3a85a5 | |||
| 15a12afe25 | |||
| 427cf91447 | |||
| af8c661a64 | |||
| 7b694ebeb4 | |||
| d1e7837422 | |||
| 8efc22bd45 | |||
| 983439076b | |||
| 019f566239 | |||
| 332410d20b | |||
| c2a85bde08 | |||
| 13a0b88755 | |||
| 32f39fec1c | |||
| cad3a6c547 | |||
| 2aa5c437e8 | |||
| 67f48ce21c | |||
| 845a7d9968 | |||
| 924774d3ef | |||
| e0635cf65c | |||
| 3b8e3a9714 | |||
| 2bee096246 | |||
| 872655369a | |||
| 0892758994 | |||
| 22a24446c1 | |||
| b5d498ffda | |||
| bd90e3e37f | |||
| 8b6d615aa6 | |||
| 9e352da052 | |||
| ca9898e838 | |||
| 95b8467a76 | |||
| 2b674d4d8c | |||
| 487b572aa6 | |||
| 600c0ea5ba | |||
| 00e64920c8 | |||
| 9efedbe76a | |||
| d8e9382573 | |||
| ab248e170c | |||
| 27097cbcfe | |||
| 43952e18e2 | |||
| 5076da8f77 | |||
| 8228a4adcd | |||
| 721883f64e | |||
| b55c759f71 | |||
| 3f028fbb83 | |||
| f0cba1371c | |||
| 14fa96fdef | |||
| b954a41f2a | |||
| f1c65dcefc | |||
| d4c17ecda5 | |||
| a4458e2c8c | |||
| 16cc88dd0e | |||
| 280f5f0b54 | |||
| e38eaa7cd7 | |||
| a27bd2275c | |||
| 7d00c69a4f | |||
| a2c189b45a | |||
| 41320688b7 | |||
| f85c8584b0 | |||
| 6cf50f8f6c | |||
| d70ab11477 | |||
| cab4c78b7b | |||
| 14358d5e84 | |||
| 7553429130 | |||
| 3ddfa288cf | |||
| e89a568765 | |||
| 71f1021648 | |||
| 60cff6f3d4 | |||
| 5f010c2f4c | |||
| c0f3d1f06d | |||
| 6c18e00977 | |||
| 72ac1aa537 | |||
| 5bcaa25c02 | |||
| 85880ffded | |||
| 5b207c3b95 | |||
| 3087f15dd8 | |||
| 36f1193497 | |||
| 292af1e4cd | |||
| d4d5dc99f3 | |||
| 3e1344e4e4 | |||
| 6d37bc62a5 | |||
| e9f7c100a0 | |||
| 190676c982 | |||
| 28cbba5a7d | |||
| 0e7a705254 | |||
| 68931b4c43 | |||
| a4e28df9cf | |||
| 65c81c5e83 | |||
| ccd0d2046d | |||
| 0fec99e75b | |||
| f8d1f070f5 | |||
| 3dd48a24ce | |||
| 984e97cbdc | |||
| 50ea1a12c9 | |||
| 992a372fc7 | |||
| d789250976 | |||
| d7e1cf7dd2 | |||
| ea5f5aea5f | |||
| eaf0bbc108 | |||
| b1710bb712 | |||
| dfae7bd073 | |||
| 333fe9a561 | |||
| badb3dc2dd | |||
| 3ac819ee70 | |||
| 00f877e812 | |||
| 1e269751bf | |||
| 5b69eda39a | |||
| 71ca413067 | |||
| 0c7f9458c0 | |||
| ab365fe494 | |||
| e48f160a29 | |||
| cfda4d0ade | |||
| debfae06a8 | |||
| e6f2f8ad0d | |||
| 67785a918c | |||
| 50d80c64fa | |||
| 1378645413 | |||
| 3888b0429c | |||
| 70761a4ef0 | |||
| e37df998e9 | |||
| c30cdfb3a7 | |||
| 350b704cd7 | |||
| ad013d59f4 | |||
| d391133524 | |||
| 4b823469f7 | |||
| 7cd1e767be | |||
| 829a697c39 | |||
| fefab50e06 | |||
| 6e27ab60ca | |||
| 74274f6d77 | |||
| 908b2703f3 | |||
| c047ab6147 | |||
| 52fa69d93d | |||
| a83df01bfd | |||
| b32d989a10 | |||
| d1ee7c651f | |||
| dcbe8ef5f4 | |||
| bd5253047b | |||
| cdaa8cb5c4 | |||
| 083397a555 | |||
| e98eb4659e | |||
| d1988d384c | |||
| bc61835d97 | |||
| 8a50880613 | |||
| b05d12df0e | |||
| 733b46d51a | |||
| 8bc9868c1f | |||
| 3507cd0a37 | |||
| 21ec32b465 | |||
| 1c99a2e7fb | |||
| b68bdafadc | |||
| bacfa9be19 | |||
| 5265eb81e0 | |||
| 516da4ecdd | |||
| 6fcc68699a | |||
| 3cb5fd8ebc | |||
| fd7ea82965 | |||
| 0bd989b118 | |||
| bd0b292b39 | |||
| 31f0a0c064 | |||
| 3e445eff8e | |||
| 6dcd482dd0 | |||
| a28ab0a408 | |||
| a3eb5c2d63 | |||
| e2303f3378 | |||
| acb17b0298 | |||
| fde7cf1fab | |||
| c2cb7e1004 | |||
| 4075f5c497 | |||
| debd44fa9b | |||
| 0a5742587a | |||
| ad4df2d946 | |||
| bfe9580551 | |||
| 1e45437498 | |||
| f9c8f78000 | |||
| 4554eedd19 | |||
| 557f0c5a8a | |||
| 9fb8d449fe | |||
| 1bb7c74c93 | |||
| cc07e5306e | |||
| 34fa010601 | |||
| 7d56069243 | |||
| fc77fb9b4c | |||
| ebdf93e5dc | |||
| 5248240e10 | |||
| b66628baca | |||
| ba5ed5f380 | |||
| 8d5e119582 | |||
| d022e7cd27 | |||
| 5d7acda80e | |||
| 9b595c68a5 | |||
| 2d38d8a639 | |||
| dea8aa675a | |||
| 027eba2b59 | |||
| 91b8e7504e | |||
| 088931525d | |||
| ef9638e094 |
+9
-15
@@ -1,25 +1,18 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
ccache -s # debug
|
||||
|
||||
# Pull all the submodules except llvm
|
||||
# Note: Tried to use git submodule status, but it takes over 20 seconds
|
||||
# shellcheck disable=SC2046
|
||||
git submodule -q update --init $(awk '/path/ && !/llvm/ { print $3 }' .gitmodules)
|
||||
git submodule -q update --init --depth 1 $(awk '/path/ && !/llvm/ { print $3 }' .gitmodules)
|
||||
|
||||
# XXX Drop after Travis upgrades FreeBSD to 12.2 (see also .ci/install-freebsd.sh)
|
||||
case $(${CXX:-c++} --version) in
|
||||
*version\ 8.0.*)
|
||||
fetch https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/libcxx-10.0.0.src.tar.xz
|
||||
tar xf libcxx-10.0.0.src.tar.xz
|
||||
export CC=clang10 CXX=clang++10
|
||||
export CXXFLAGS="$CXXFLAGS -nostdinc++ -isystem $PWD/libcxx-10.0.0.src/include"
|
||||
;;
|
||||
esac
|
||||
# Prefer newer Clang than in base system (see also .ci/install-freebsd.sh)
|
||||
# libc++ isn't in llvm* packages, so download manually
|
||||
fetch https://github.com/llvm/llvm-project/releases/download/llvmorg-11.0.0/libcxx-11.0.0.src.tar.xz
|
||||
tar xf libcxx-11.0.0.src.tar.xz
|
||||
export CC=clang11 CXX=clang++11
|
||||
export CXXFLAGS="$CXXFLAGS -nostdinc++ -isystem $PWD/libcxx-11.0.0.src/include"
|
||||
|
||||
CONFIGURE_ARGS="
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
-DWITH_LLVM=OFF
|
||||
-DUSE_PRECOMPILED_HEADERS=OFF
|
||||
-DUSE_NATIVE_INSTRUCTIONS=OFF
|
||||
@@ -32,4 +25,5 @@ CONFIGURE_ARGS="
|
||||
cmake -B build -G Ninja $CONFIGURE_ARGS
|
||||
cmake --build build
|
||||
|
||||
ccache -s # debug
|
||||
ccache --show-stats
|
||||
ccache --zero-stats
|
||||
|
||||
+3
-5
@@ -55,11 +55,9 @@ ninja; build_status=$?;
|
||||
|
||||
cd ..
|
||||
|
||||
# If it compiled succesfully let's deploy depending on the build pipeline (Travis, Azure Pipelines).
|
||||
# Travis only deploys on master, and it publishes to GitHub releases. Azure publishes PRs as artifacts
|
||||
# only.
|
||||
{ [ "$IS_AZURE" = "true" ] ||
|
||||
{ [ "$TRAVIS_BRANCH" = "master" ] && [ "$TRAVIS_PULL_REQUEST" = "false" ]; };
|
||||
# If it compiled succesfully let's deploy depending on the build pipeline (Azure Pipelines).
|
||||
# Azure publishes PRs as artifacts only.
|
||||
{ [ "$IS_AZURE" = "true" ];
|
||||
} && SHOULD_DEPLOY="true" || SHOULD_DEPLOY="false"
|
||||
|
||||
if [ "$build_status" -eq 0 ] && [ "$SHOULD_DEPLOY" = "true" ]; then
|
||||
|
||||
+3
-11
@@ -41,7 +41,6 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
|
||||
COMM_HASH="$(git rev-parse --short=8 HEAD)"
|
||||
RPCS3_APPIMAGE="rpcs3-v${COMM_TAG}-${COMM_COUNT}-${COMM_HASH}_linux64.AppImage"
|
||||
|
||||
curl -sLO https://github.com/hcorion/uploadtool/raw/master/upload.sh
|
||||
mv ./RPCS3*.AppImage "$RPCS3_APPIMAGE"
|
||||
|
||||
# If we're building using Azure Pipelines, let's copy over the AppImage artifact
|
||||
@@ -50,16 +49,9 @@ if [ "$DEPLOY_APPIMAGE" = "true" ]; then
|
||||
fi
|
||||
|
||||
FILESIZE=$(stat -c %s ./rpcs3*.AppImage)
|
||||
SHA256SUM=$(sha256sum ./rpcs3*.AppImage)
|
||||
if [ -n "$GITHUB_TOKEN" ]; then
|
||||
unset TRAVIS_REPO_SLUG
|
||||
REPO_SLUG=RPCS3/rpcs3-binaries-linux \
|
||||
UPLOADTOOL_BODY="$SHA256SUM;${FILESIZE}B"\
|
||||
RELEASE_NAME=build-${TRAVIS_COMMIT}\
|
||||
RELEASE_TITLE=${COMM_TAG}-${COMM_COUNT}\
|
||||
REPO_COMMIT=d812f1254a1157c80fd402f94446310560f54e5f\
|
||||
bash upload.sh rpcs3*.AppImage
|
||||
fi
|
||||
SHA256SUM=$(sha256sum ./rpcs3*.AppImage | awk '{ print $1 }')
|
||||
echo "${SHA256SUM};${FILESIZE}B" > /rpcs3/GitHubReleaseMessage.txt
|
||||
|
||||
fi
|
||||
|
||||
if [ "$DEPLOY_PPA" = "true" ]; then
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
# Variables set by Travis CI
|
||||
TRAVIS_PULL_REQUEST
|
||||
TRAVIS_BRANCH
|
||||
TRAVIS_COMMIT
|
||||
# Variables set by Azure Pipelines
|
||||
IS_AZURE
|
||||
BUILD_REASON
|
||||
@@ -9,8 +5,8 @@ BUILD_SOURCEVERSION
|
||||
BUILD_ARTIFACTSTAGINGDIRECTORY
|
||||
BUILD_REPOSITORY_NAME
|
||||
BUILD_SOURCEBRANCHNAME
|
||||
# Variables for Travis build matrix
|
||||
# Variables for build matrix
|
||||
COMPILER
|
||||
DEPLOY_APPIMAGE
|
||||
# Private Travis CI variables
|
||||
# Private variables
|
||||
GITHUB_TOKEN
|
||||
Regular → Executable
+3
-5
@@ -7,7 +7,7 @@ generate_post_data()
|
||||
cat <<EOF
|
||||
{
|
||||
"tag_name": "build-${BUILD_SOURCEVERSION}",
|
||||
"target_commitish": "7d09e3be30805911226241afbb14f8cdc2eb054e",
|
||||
"target_commitish": "${UPLOAD_COMMIT_HASH}",
|
||||
"name": "${AVVER}",
|
||||
"body": "$body",
|
||||
"draft": false,
|
||||
@@ -16,12 +16,10 @@ generate_post_data()
|
||||
EOF
|
||||
}
|
||||
|
||||
repo_full_name="RPCS3/rpcs3-binaries-win"
|
||||
|
||||
curl -s \
|
||||
-H "Authorization: token ${RPCS3_TOKEN}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
--data "$(generate_post_data)" "https://api.github.com/repos/$repo_full_name/releases" >> release.json
|
||||
--data "$(generate_post_data)" "https://api.github.com/repos/$UPLOAD_REPO_FULL_NAME/releases" >> release.json
|
||||
|
||||
cat release.json
|
||||
id=$(grep '"id"' release.json | cut -d ':' -f2 | head -n1 | awk '{$1=$1;print}')
|
||||
@@ -35,7 +33,7 @@ upload_file()
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"$2"/"$3" \
|
||||
"https://uploads.github.com/repos/$repo_full_name/releases/$1/assets?name=$3"
|
||||
"https://uploads.github.com/repos/$UPLOAD_REPO_FULL_NAME/releases/$1/assets?name=$3"
|
||||
}
|
||||
|
||||
for file in "$ARTIFACT_DIR"/*; do
|
||||
@@ -8,12 +8,8 @@ sed -i '' 's/quarterly/latest/' /etc/pkg/FreeBSD.conf
|
||||
export ASSUME_ALWAYS_YES=true
|
||||
pkg info # debug
|
||||
|
||||
# XXX Drop after Travis upgrades FreeBSD to 12.2 (see also .ci/build-freebsd.sh)
|
||||
case $(${CXX:-c++} --version) in
|
||||
*version\ 8.0.*)
|
||||
pkg install llvm10
|
||||
;;
|
||||
esac
|
||||
# Prefer newer Clang than in base system (see also .ci/build-freebsd.sh)
|
||||
pkg install llvm11
|
||||
|
||||
# Mandatory dependencies (qt5-dbus and qt5-gui are pulled via qt5-widgets)
|
||||
pkg install git ccache cmake ninja qt5-qmake qt5-buildtools qt5-widgets qt5-concurrent glew openal-soft ffmpeg
|
||||
|
||||
@@ -10,8 +10,9 @@ PR_NUMBER="$SYSTEM_PULLREQUEST_PULLREQUESTID"
|
||||
QT_HOST="http://mirrors.ocf.berkeley.edu/qt/"
|
||||
#QT_HOST="http://qt.mirror.constant.com/"
|
||||
QT_URL_VER=$(echo "$QT_VER" | sed "s/\.//g")
|
||||
QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt5_${QT_URL_VER}/qt.qt5.${QT_URL_VER}.win64_msvc2017_64/${QT_VER}-0-${QT_DATE}"
|
||||
QT_SUFFIX="-Windows-Windows_10-MSVC2017-Windows-Windows_10-X86_64.7z"
|
||||
QT_VER_MSVC_UP=$(echo ${QT_VER_MSVC} | tr '[:lower:]' '[:upper:]')
|
||||
QT_PREFIX="online/qtsdkrepository/windows_x86/desktop/qt${QT_VER_MAIN}_${QT_URL_VER}/qt.qt${QT_VER_MAIN}.${QT_URL_VER}.win64_${QT_VER_MSVC}_64/${QT_VER}-0-${QT_DATE}"
|
||||
QT_SUFFIX="-Windows-Windows_10-${QT_VER_MSVC_UP}-Windows-Windows_10-X86_64.7z"
|
||||
QT_BASE_URL="${QT_HOST}${QT_PREFIX}qtbase${QT_SUFFIX}"
|
||||
QT_WINE_URL="${QT_HOST}${QT_PREFIX}qtwinextras${QT_SUFFIX}"
|
||||
QT_DECL_URL="${QT_HOST}${QT_PREFIX}qtdeclarative${QT_SUFFIX}"
|
||||
|
||||
+2
-2
@@ -3,9 +3,9 @@ env:
|
||||
|
||||
freebsd_task:
|
||||
matrix:
|
||||
- name: FreeBSD 12.2 (snapshot)
|
||||
- name: FreeBSD 12.2
|
||||
freebsd_instance:
|
||||
image_family: freebsd-12-1-snap
|
||||
image_family: freebsd-12-2
|
||||
cpu: 8
|
||||
memory: 8G
|
||||
env:
|
||||
|
||||
+11
-11
@@ -1,20 +1,20 @@
|
||||
Standard: Cpp11
|
||||
Standard: c++20
|
||||
UseTab: ForIndentation
|
||||
TabWidth: 1
|
||||
IndentWidth: 1
|
||||
AccessModifierOffset: -1
|
||||
TabWidth: 4
|
||||
IndentWidth: 4
|
||||
AccessModifierOffset: -4
|
||||
PointerAlignment: Left
|
||||
NamespaceIndentation: All
|
||||
ColumnLimit: 200
|
||||
ColumnLimit: 0
|
||||
BreakBeforeBraces: Allman
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseLabelsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
Cpp11BracedListStyle: true
|
||||
IndentCaseLabels: false
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
root = true
|
||||
|
||||
[*.{h,cpp,hpp}]
|
||||
charset = utf-8-bom
|
||||
charset = utf-8
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
## How to test a PR build
|
||||
|
||||
Please take into account, that RPCS3 build usually takes some time (about 20 min), so you can't access a build, if a PR was just submitted.
|
||||
|
||||
- Open a PR you want to test
|
||||
- Scroll to the very bottom and locate "Checks" section
|
||||
- Expand all checks
|
||||
You are supposed to see something like this
|
||||

|
||||
- Click on **Linux_Build** or **Windows_Build** __Details__
|
||||
- Click **View more details on Azure Pipelines** at the very bottom
|
||||

|
||||
- Click on *1 artifact produced*
|
||||

|
||||
- Select a build for your OS and click three dots on the right, Click **Download artifacts**
|
||||

|
||||
|
||||
- Congratulations! You are now downloading RPCS3 build for specific PR.
|
||||
|
||||
Please note, that PR builds are not supposed to be stable, because they contain new changesets.
|
||||
@@ -0,0 +1,3 @@
|
||||
<!-- Please include a summary of the change and which issue is fixed. -->
|
||||
|
||||
[How to test this PR](.github/PR-BUILD.md)
|
||||
@@ -52,6 +52,9 @@
|
||||
/bin/GuiConfigs/*.dat
|
||||
/bin/GuiConfigs/*.dat.*
|
||||
|
||||
# Some data from git
|
||||
!/bin/git/
|
||||
|
||||
# Visual Studio Files
|
||||
.vs/*
|
||||
.vscode/*
|
||||
@@ -116,3 +119,5 @@ yaml-cpp.pc
|
||||
|
||||
# ssl certificate
|
||||
cacert.pem
|
||||
|
||||
_ReSharper.*/
|
||||
|
||||
+2
-1
@@ -4,7 +4,8 @@
|
||||
ignore = dirty
|
||||
[submodule "asmjit"]
|
||||
path = asmjit
|
||||
url = https://github.com/kobalicek/asmjit
|
||||
url = https://github.com/asmjit/asmjit
|
||||
branch = oldstable
|
||||
ignore = dirty
|
||||
[submodule "llvm"]
|
||||
path = llvm
|
||||
|
||||
-46
@@ -1,46 +0,0 @@
|
||||
language: cpp
|
||||
jobs:
|
||||
include:
|
||||
- os: linux
|
||||
dist: xenial
|
||||
env:
|
||||
- NAME="Linux build"
|
||||
- COMPILER="gcc"
|
||||
- DEPLOY_APPIMAGE="true"
|
||||
services: docker
|
||||
cache: ccache
|
||||
compiler: gcc
|
||||
install: "docker pull rpcs3/rpcs3-travis-xenial:1.6"
|
||||
script: 'travis_wait docker run -v $(pwd):/rpcs3 -v "$HOME/.ccache":/root/.ccache --env-file .ci/travis.env rpcs3/rpcs3-travis-xenial:1.6 /rpcs3/.ci/build-linux.sh'
|
||||
- os: linux
|
||||
dist: xenial
|
||||
env:
|
||||
- NAME="Linux build"
|
||||
- COMPILER="clang"
|
||||
services: docker
|
||||
cache: ccache
|
||||
compiler: clang
|
||||
install: "docker pull rpcs3/rpcs3-travis-xenial:1.6"
|
||||
script: 'travis_wait docker run -v $(pwd):/rpcs3 -v "$HOME/.ccache":/root/.ccache --env-file .ci/travis.env rpcs3/rpcs3-travis-xenial:1.6 /rpcs3/.ci/build-linux.sh'
|
||||
# - os: freebsd
|
||||
# compiler: clang
|
||||
# cache: ccache
|
||||
# install: "./.ci/install-freebsd.sh"
|
||||
# script: "./.ci/build-freebsd.sh"
|
||||
# - os: osx
|
||||
# osx_image: xcode11.3
|
||||
# addons:
|
||||
# homebrew:
|
||||
# packages:
|
||||
# - ccache
|
||||
# - glew
|
||||
# - ninja
|
||||
# - qt
|
||||
# script: "/bin/bash -ex .ci/build-mac.sh"
|
||||
# cache: ccache
|
||||
allow_failures:
|
||||
- os: osx
|
||||
|
||||
git:
|
||||
depth: false # Unshallow clone to obtain proper GIT_VERSION
|
||||
submodules: false
|
||||
Vendored
+2
-1
@@ -131,6 +131,7 @@ else()
|
||||
target_link_libraries(usb-1.0-shared INTERFACE PkgConfig::LIBUSB)
|
||||
else()
|
||||
# we don't have the system libusb, so we compile from submodule
|
||||
unset(LIBUSB_LIBRARIES CACHE)
|
||||
add_subdirectory(libusb_cmake EXCLUDE_FROM_ALL)
|
||||
endif()
|
||||
endif()
|
||||
@@ -354,7 +355,7 @@ target_link_libraries(3rdparty_openal INTERFACE ${OPENAL_LIBRARY})
|
||||
set(FAUDIO_TARGET 3rdparty_dummy_lib)
|
||||
if(USE_FAUDIO)
|
||||
# FAudio depends on SDL2
|
||||
pkg_check_modules(SDL2 sdl2)
|
||||
find_package(SDL2)
|
||||
if (NOT SDL2_FOUND OR SDL2_VERSION VERSION_LESS 2.0.9)
|
||||
message("-- RPCS3: FAudio requires SDL 2.0.9 or newer.")
|
||||
else()
|
||||
|
||||
+4
-4
@@ -8848,7 +8848,7 @@ bool VmaBlockMetadata_Generic::CheckAllocation(
|
||||
|
||||
// Check previous suballocations for BufferImageGranularity conflicts.
|
||||
// Make bigger alignment if necessary.
|
||||
if(bufferImageGranularity > 1)
|
||||
if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
|
||||
{
|
||||
bool bufferImageGranularityConflict = false;
|
||||
VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
|
||||
@@ -8932,7 +8932,7 @@ bool VmaBlockMetadata_Generic::CheckAllocation(
|
||||
|
||||
// Check next suballocations for BufferImageGranularity conflicts.
|
||||
// If conflict exists, we must mark more allocations lost or fail.
|
||||
if(bufferImageGranularity > 1)
|
||||
if(bufferImageGranularity > 1 && (allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity))
|
||||
{
|
||||
VmaSuballocationList::const_iterator nextSuballocItem = lastSuballocItem;
|
||||
++nextSuballocItem;
|
||||
@@ -8991,7 +8991,7 @@ bool VmaBlockMetadata_Generic::CheckAllocation(
|
||||
|
||||
// Check previous suballocations for BufferImageGranularity conflicts.
|
||||
// Make bigger alignment if necessary.
|
||||
if(bufferImageGranularity > 1)
|
||||
if(bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment)
|
||||
{
|
||||
bool bufferImageGranularityConflict = false;
|
||||
VmaSuballocationList::const_iterator prevSuballocItem = suballocItem;
|
||||
@@ -9031,7 +9031,7 @@ bool VmaBlockMetadata_Generic::CheckAllocation(
|
||||
|
||||
// Check next suballocations for BufferImageGranularity conflicts.
|
||||
// If conflict exists, allocation cannot be made here.
|
||||
if(bufferImageGranularity > 1)
|
||||
if(bufferImageGranularity > 1 && (allocSize % bufferImageGranularity || *pOffset % bufferImageGranularity))
|
||||
{
|
||||
VmaSuballocationList::const_iterator nextSuballocItem = suballocItem;
|
||||
++nextSuballocItem;
|
||||
|
||||
Vendored
+1
-1
Submodule 3rdparty/hidapi updated: 9220f5e77c...8961cf86eb
Vendored
+1
-1
Submodule 3rdparty/libpng updated: eddf902320...a40189cf88
Vendored
+9
-11
@@ -1,11 +1,13 @@
|
||||
add_library(3rdparty_qt5 INTERFACE)
|
||||
|
||||
find_package(Qt5 5.14 CONFIG COMPONENTS Widgets Concurrent)
|
||||
set(QT_MIN_VER 5.15.2)
|
||||
|
||||
find_package(Qt5 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent)
|
||||
if(WIN32)
|
||||
find_package(Qt5 5.14 COMPONENTS WinExtras REQUIRED)
|
||||
find_package(Qt5 ${QT_MIN_VER} COMPONENTS WinExtras REQUIRED)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Concurrent)
|
||||
else()
|
||||
find_package(Qt5 5.14 COMPONENTS DBus Gui)
|
||||
find_package(Qt5 ${QT_MIN_VER} COMPONENTS DBus Gui)
|
||||
if(Qt5DBus_FOUND)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Concurrent)
|
||||
target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS)
|
||||
@@ -16,16 +18,12 @@ else()
|
||||
endif()
|
||||
|
||||
if(NOT Qt5Widgets_FOUND)
|
||||
if(Qt5Widgets_VERSION VERSION_LESS 5.14.0)
|
||||
message("Minimum supported Qt5 version is 5.14.0! You have version ${Qt5Widgets_VERSION} installed, please upgrade!")
|
||||
if(Qt5Widgets_VERSION VERSION_LESS ${QT_MIN_VER})
|
||||
message("Minimum supported Qt5 version is ${QT_MIN_VER}! You have version ${Qt5Widgets_VERSION} installed, please upgrade!")
|
||||
if(CMAKE_SYSTEM MATCHES "Linux")
|
||||
message(FATAL_ERROR "Most distros do not provide an up-to-date version of Qt.
|
||||
If you're on Ubuntu or Linux Mint, there are PPAs you can use to install one of the latest qt5 versions.
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.14.1-bionic
|
||||
https://launchpad.net/~beineri/+archive/ubuntu/opt-qt-5.14.1-xenial
|
||||
just make sure to run
|
||||
source /opt/qt514/bin/qt514-env.sh
|
||||
before re-running cmake")
|
||||
Find the correct ppa at https://launchpad.net/~beineri and follow the instructions.")
|
||||
elseif(WIN32)
|
||||
message(FATAL_ERROR "You can download the latest version of Qt5 here: https://www.qt.io/download-open-source/")
|
||||
else()
|
||||
@@ -35,7 +33,7 @@ before re-running cmake")
|
||||
|
||||
message("CMake was unable to find Qt5!")
|
||||
if(WIN32)
|
||||
message(FATAL_ERROR "Make sure the QTDIR env variable has been set properly. (for example C:\\Qt\\5.14.1\\msvc2017_64\\)
|
||||
message(FATAL_ERROR "Make sure the QTDIR env variable has been set properly. (for example C:\\Qt\\${QT_MIN_VER}\\msvc2019_64\\)
|
||||
You can also try setting the Qt5_DIR preprocessor definiton.")
|
||||
elseif(CMAKE_SYSTEM MATCHES "Linux")
|
||||
message(FATAL_ERROR "Make sure to install your distro's qt5 package!")
|
||||
|
||||
+9
-9
@@ -9,19 +9,19 @@ Other instructions may be found [here](https://wiki.rpcs3.net/index.php?title=Bu
|
||||
|
||||
* [CMake 3.14.1+](https://www.cmake.org/download/) (add to PATH)
|
||||
* [Python 3.3+](https://www.python.org/downloads/) (add to PATH)
|
||||
* [Qt 5.14.2](https://www.qt.io/download-qt-installer)
|
||||
* [Qt 5.15.2](https://www.qt.io/download-qt-installer)
|
||||
* [Visual Studio 2019](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community)
|
||||
* [Vulkan SDK 1.1.126+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/windows/getting_started.html))
|
||||
|
||||
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\5.14.2\msvc2017_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)**
|
||||
**Either add the** `QTDIR` **environment variable, e.g.** `<QtInstallFolder>\5.15.2\msvc2019_64\` **, or use the [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2019)**
|
||||
|
||||
### Linux
|
||||
|
||||
These are the essentials tools to build RPCS3 on Linux. Some of them can be installed through your favorite package manager.
|
||||
|
||||
* Clang 9+ or GCC 9+
|
||||
* Clang 11+ or GCC 9+
|
||||
* [CMake 3.14.1+](https://www.cmake.org/download/)
|
||||
* [Qt 5.14.2](https://www.qt.io/download-qt-installer)
|
||||
* [Qt 5.15.2](https://www.qt.io/download-qt-installer)
|
||||
* [Vulkan SDK 1.1.126+](https://vulkan.lunarg.com/sdk/home) (See "Install the SDK" [here](https://vulkan.lunarg.com/doc/sdk/latest/linux/getting_started.html))
|
||||
* [SDL2](https://www.libsdl.org/download-2.0.php) (for the FAudio backend)
|
||||
|
||||
@@ -41,10 +41,10 @@ Ubuntu is usually horrendously out of date, and some packages need to be downloa
|
||||
Ubuntu usually does not have a new enough Qt package to suit rpcs3's needs. There is a PPA available to work around this. Run the following:
|
||||
```
|
||||
. /etc/os-release
|
||||
sudo add-apt-repository ppa:beineri/opt-qt-5.14.2-$UBUNTU_CODENAME
|
||||
sudo add-apt-repository ppa:beineri/opt-qt-5.15.2-$UBUNTU_CODENAME
|
||||
sudo apt-get update
|
||||
sudo apt-get install qt514-meta-minimal qt514svg
|
||||
. /opt/qt514/bin/qt514-env.sh >/dev/null 2>&1
|
||||
sudo apt-get install qt515-meta-minimal qt515svg
|
||||
. /opt/qt515/bin/qt515-env.sh >/dev/null 2>&1
|
||||
```
|
||||
|
||||
##### GCC 9.x installation
|
||||
@@ -82,7 +82,7 @@ sudo apt-get install cmake
|
||||
|
||||
#### Fedora
|
||||
|
||||
sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt5-devel vulkan-devel
|
||||
sudo dnf install alsa-lib-devel cmake glew glew-devel libatomic libevdev-devel libudev-devel openal-devel qt5-qtbase-devel qt5-qtbase-private-devel vulkan-devel
|
||||
|
||||
#### OpenSUSE
|
||||
|
||||
@@ -103,7 +103,7 @@ git submodule update --init
|
||||
#### Configuring the Qt plugin (if used)
|
||||
|
||||
1) Go to the Qt5 menu and edit Qt5 options.
|
||||
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\5.14.2\msvc2017_64`.
|
||||
2) Add the path to your Qt installation with compiler e.g. `<QtInstallFolder>\5.15.2\msvc2019_64`.
|
||||
3) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||
|
||||
#### Building the projects
|
||||
|
||||
+6
-4
@@ -7,8 +7,8 @@ if(CMAKE_COMPILER_IS_GNUCXX)
|
||||
message(FATAL_ERROR "RPCS3 requires at least gcc-9.")
|
||||
endif()
|
||||
elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
|
||||
message(FATAL_ERROR "RPCS3 requires at least clang-9.0.")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 11.0)
|
||||
message(FATAL_ERROR "RPCS3 requires at least clang-11.0.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -22,7 +22,7 @@ option(USE_LIBEVDEV "libevdev-based joystick support" ON)
|
||||
option(USE_DISCORD_RPC "Discord rich presence integration" ON)
|
||||
option(USE_SYSTEM_ZLIB "Prefer system ZLIB instead of the builtin one" ON)
|
||||
option(USE_VULKAN "Vulkan render backend" ON)
|
||||
option(USE_PRECOMPILED_HEADERS "Use precompiled headers" ON)
|
||||
option(USE_PRECOMPILED_HEADERS "Use precompiled headers" OFF)
|
||||
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/rpcs3/cmake_modules")
|
||||
|
||||
@@ -36,6 +36,8 @@ endif()
|
||||
|
||||
if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
add_definitions(-DNDEBUG)
|
||||
elseif(NOT MSVC)
|
||||
add_definitions(-D_DEBUG)
|
||||
endif()
|
||||
|
||||
if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
@@ -64,7 +66,7 @@ set(CMAKE_C_FLAGS -w)
|
||||
set(LLVM_ENABLE_WARNINGS OFF CACHE BOOL "")
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(/wd4530) # C++ exception handler used, but unwind semantics are not enabled
|
||||
add_compile_options(/wd4530 /utf-8) # C++ exception handler used, but unwind semantics are not enabled
|
||||
endif()
|
||||
|
||||
add_subdirectory(Vulkan EXCLUDE_FROM_ALL)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
RPCS3
|
||||
=====
|
||||
|
||||
[](https://travis-ci.org/RPCS3/rpcs3)
|
||||
[](https://dev.azure.com/nekotekina/nekotekina/_build?definitionId=4&branchName=master)
|
||||
[&logo=cirrus-ci)](https://cirrus-ci.com/github/RPCS3/rpcs3)
|
||||
[](https://discord.me/rpcs3)
|
||||
|
||||
@@ -1,511 +0,0 @@
|
||||
#ifndef BETYPE_H_GUARD
|
||||
#define BETYPE_H_GUARD
|
||||
|
||||
#include "types.h"
|
||||
#include "util/endian.hpp"
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
|
||||
#if __has_include(<bit>)
|
||||
#include <bit>
|
||||
#else
|
||||
#include <type_traits>
|
||||
#endif
|
||||
|
||||
// 128-bit vector type and also se_storage<> storage type
|
||||
union alignas(16) v128
|
||||
{
|
||||
uchar _bytes[16];
|
||||
char _chars[16];
|
||||
|
||||
template <typename T, std::size_t N, std::size_t M>
|
||||
struct masked_array_t // array type accessed as (index ^ M)
|
||||
{
|
||||
char m_data[16];
|
||||
|
||||
public:
|
||||
T& operator[](std::size_t index)
|
||||
{
|
||||
return reinterpret_cast<T*>(m_data)[index ^ M];
|
||||
}
|
||||
|
||||
const T& operator[](std::size_t index) const
|
||||
{
|
||||
return reinterpret_cast<const T*>(m_data)[index ^ M];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N = 16 / sizeof(T)>
|
||||
using normal_array_t = masked_array_t<T, N, std::endian::little == std::endian::native ? 0 : N - 1>;
|
||||
template <typename T, std::size_t N = 16 / sizeof(T)>
|
||||
using reversed_array_t = masked_array_t<T, N, std::endian::little == std::endian::native ? N - 1 : 0>;
|
||||
|
||||
normal_array_t<u64> _u64;
|
||||
normal_array_t<s64> _s64;
|
||||
reversed_array_t<u64> u64r;
|
||||
reversed_array_t<s64> s64r;
|
||||
|
||||
normal_array_t<u32> _u32;
|
||||
normal_array_t<s32> _s32;
|
||||
reversed_array_t<u32> u32r;
|
||||
reversed_array_t<s32> s32r;
|
||||
|
||||
normal_array_t<u16> _u16;
|
||||
normal_array_t<s16> _s16;
|
||||
reversed_array_t<u16> u16r;
|
||||
reversed_array_t<s16> s16r;
|
||||
|
||||
normal_array_t<u8> _u8;
|
||||
normal_array_t<s8> _s8;
|
||||
reversed_array_t<u8> u8r;
|
||||
reversed_array_t<s8> s8r;
|
||||
|
||||
normal_array_t<f32> _f;
|
||||
normal_array_t<f64> _d;
|
||||
reversed_array_t<f32> fr;
|
||||
reversed_array_t<f64> dr;
|
||||
|
||||
__m128 vf;
|
||||
__m128i vi;
|
||||
__m128d vd;
|
||||
|
||||
struct bit_array_128
|
||||
{
|
||||
char m_data[16];
|
||||
|
||||
public:
|
||||
class bit_element
|
||||
{
|
||||
u64& data;
|
||||
const u64 mask;
|
||||
|
||||
public:
|
||||
bit_element(u64& data, const u64 mask)
|
||||
: data(data)
|
||||
, mask(mask)
|
||||
{
|
||||
}
|
||||
|
||||
operator bool() const
|
||||
{
|
||||
return (data & mask) != 0;
|
||||
}
|
||||
|
||||
bit_element& operator=(const bool right)
|
||||
{
|
||||
if (right)
|
||||
{
|
||||
data |= mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
data &= ~mask;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bit_element& operator=(const bit_element& right)
|
||||
{
|
||||
if (right)
|
||||
{
|
||||
data |= mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
data &= ~mask;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Index 0 returns the MSB and index 127 returns the LSB
|
||||
bit_element operator[](u32 index)
|
||||
{
|
||||
const auto data_ptr = reinterpret_cast<u64*>(m_data);
|
||||
|
||||
if constexpr (std::endian::little == std::endian::native)
|
||||
{
|
||||
return bit_element(data_ptr[1 - (index >> 6)], 0x8000000000000000ull >> (index & 0x3F));
|
||||
}
|
||||
else
|
||||
{
|
||||
return bit_element(data_ptr[index >> 6], 0x8000000000000000ull >> (index & 0x3F));
|
||||
}
|
||||
}
|
||||
|
||||
// Index 0 returns the MSB and index 127 returns the LSB
|
||||
bool operator[](u32 index) const
|
||||
{
|
||||
const auto data_ptr = reinterpret_cast<const u64*>(m_data);
|
||||
|
||||
if constexpr (std::endian::little == std::endian::native)
|
||||
{
|
||||
return (data_ptr[1 - (index >> 6)] & (0x8000000000000000ull >> (index & 0x3F))) != 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (data_ptr[index >> 6] & (0x8000000000000000ull >> (index & 0x3F))) != 0;
|
||||
}
|
||||
}
|
||||
} _bit;
|
||||
|
||||
static v128 from64(u64 _0, u64 _1 = 0)
|
||||
{
|
||||
v128 ret;
|
||||
ret._u64[0] = _0;
|
||||
ret._u64[1] = _1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static v128 from64r(u64 _1, u64 _0 = 0)
|
||||
{
|
||||
return from64(_0, _1);
|
||||
}
|
||||
|
||||
static v128 from32(u32 _0, u32 _1 = 0, u32 _2 = 0, u32 _3 = 0)
|
||||
{
|
||||
v128 ret;
|
||||
ret._u32[0] = _0;
|
||||
ret._u32[1] = _1;
|
||||
ret._u32[2] = _2;
|
||||
ret._u32[3] = _3;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static v128 from32r(u32 _3, u32 _2 = 0, u32 _1 = 0, u32 _0 = 0)
|
||||
{
|
||||
return from32(_0, _1, _2, _3);
|
||||
}
|
||||
|
||||
static v128 from32p(u32 value)
|
||||
{
|
||||
v128 ret;
|
||||
ret.vi = _mm_set1_epi32(static_cast<s32>(value));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static v128 from16p(u16 value)
|
||||
{
|
||||
v128 ret;
|
||||
ret.vi = _mm_set1_epi16(static_cast<s16>(value));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static v128 from8p(u8 value)
|
||||
{
|
||||
v128 ret;
|
||||
ret.vi = _mm_set1_epi8(static_cast<s8>(value));
|
||||
return ret;
|
||||
}
|
||||
|
||||
static v128 fromBit(u32 bit)
|
||||
{
|
||||
v128 ret = {};
|
||||
ret._bit[bit] = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static v128 fromV(__m128i value)
|
||||
{
|
||||
v128 ret;
|
||||
ret.vi = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static v128 fromF(__m128 value)
|
||||
{
|
||||
v128 ret;
|
||||
ret.vf = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static v128 fromD(__m128d value)
|
||||
{
|
||||
v128 ret;
|
||||
ret.vd = value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Unaligned load with optional index offset
|
||||
static v128 loadu(const void* ptr, std::size_t index = 0)
|
||||
{
|
||||
v128 ret;
|
||||
std::memcpy(&ret, static_cast<const u8*>(ptr) + index * sizeof(v128), sizeof(v128));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Unaligned store with optional index offset
|
||||
static void storeu(v128 value, void* ptr, std::size_t index = 0)
|
||||
{
|
||||
std::memcpy(static_cast<u8*>(ptr) + index * sizeof(v128), &value, sizeof(v128));
|
||||
}
|
||||
|
||||
static inline v128 add8(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_add_epi8(left.vi, right.vi));
|
||||
}
|
||||
|
||||
static inline v128 add16(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_add_epi16(left.vi, right.vi));
|
||||
}
|
||||
|
||||
static inline v128 add32(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_add_epi32(left.vi, right.vi));
|
||||
}
|
||||
|
||||
static inline v128 addfs(const v128& left, const v128& right)
|
||||
{
|
||||
return fromF(_mm_add_ps(left.vf, right.vf));
|
||||
}
|
||||
|
||||
static inline v128 addfd(const v128& left, const v128& right)
|
||||
{
|
||||
return fromD(_mm_add_pd(left.vd, right.vd));
|
||||
}
|
||||
|
||||
static inline v128 sub8(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_sub_epi8(left.vi, right.vi));
|
||||
}
|
||||
|
||||
static inline v128 sub16(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_sub_epi16(left.vi, right.vi));
|
||||
}
|
||||
|
||||
static inline v128 sub32(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_sub_epi32(left.vi, right.vi));
|
||||
}
|
||||
|
||||
static inline v128 subfs(const v128& left, const v128& right)
|
||||
{
|
||||
return fromF(_mm_sub_ps(left.vf, right.vf));
|
||||
}
|
||||
|
||||
static inline v128 subfd(const v128& left, const v128& right)
|
||||
{
|
||||
return fromD(_mm_sub_pd(left.vd, right.vd));
|
||||
}
|
||||
|
||||
static inline v128 maxu8(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_max_epu8(left.vi, right.vi));
|
||||
}
|
||||
|
||||
static inline v128 minu8(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_min_epu8(left.vi, right.vi));
|
||||
}
|
||||
|
||||
static inline v128 eq8(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_cmpeq_epi8(left.vi, right.vi));
|
||||
}
|
||||
|
||||
static inline v128 eq16(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_cmpeq_epi16(left.vi, right.vi));
|
||||
}
|
||||
|
||||
static inline v128 eq32(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_cmpeq_epi32(left.vi, right.vi));
|
||||
}
|
||||
|
||||
static inline v128 eq32f(const v128& left, const v128& right)
|
||||
{
|
||||
return fromF(_mm_cmpeq_ps(left.vf, right.vf));
|
||||
}
|
||||
|
||||
static inline v128 eq64f(const v128& left, const v128& right)
|
||||
{
|
||||
return fromD(_mm_cmpeq_pd(left.vd, right.vd));
|
||||
}
|
||||
|
||||
static inline bool use_fma = false;
|
||||
|
||||
static inline v128 fma32f(v128 a, const v128& b, const v128& c)
|
||||
{
|
||||
#ifndef __FMA__
|
||||
if (use_fma) [[likely]]
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
a.vf = _mm_fmadd_ps(a.vf, b.vf, c.vf);
|
||||
return a;
|
||||
#else
|
||||
__asm__("vfmadd213ps %[c], %[b], %[a]"
|
||||
: [a] "+x" (a.vf)
|
||||
: [b] "x" (b.vf)
|
||||
, [c] "x" (c.vf));
|
||||
return a;
|
||||
#endif
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
a._f[i] = std::fmaf(a._f[i], b._f[i], c._f[i]);
|
||||
}
|
||||
return a;
|
||||
#else
|
||||
a.vf = _mm_fmadd_ps(a.vf, b.vf, c.vf);
|
||||
return a;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool operator==(const v128& right) const
|
||||
{
|
||||
return _mm_movemask_epi8(v128::eq32(*this, right).vi) == 0xffff;
|
||||
}
|
||||
|
||||
bool operator!=(const v128& right) const
|
||||
{
|
||||
return !operator==(right);
|
||||
}
|
||||
|
||||
// result = (~left) & (right)
|
||||
static inline v128 andnot(const v128& left, const v128& right)
|
||||
{
|
||||
return fromV(_mm_andnot_si128(left.vi, right.vi));
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
*this = {};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N, std::size_t M>
|
||||
struct offset32_array<v128::masked_array_t<T, N, M>>
|
||||
{
|
||||
template <typename Arg>
|
||||
static inline u32 index32(const Arg& arg)
|
||||
{
|
||||
return u32{sizeof(T)} * (static_cast<u32>(arg) ^ static_cast<u32>(M));
|
||||
}
|
||||
};
|
||||
|
||||
inline v128 operator|(const v128& left, const v128& right)
|
||||
{
|
||||
return v128::fromV(_mm_or_si128(left.vi, right.vi));
|
||||
}
|
||||
|
||||
inline v128 operator&(const v128& left, const v128& right)
|
||||
{
|
||||
return v128::fromV(_mm_and_si128(left.vi, right.vi));
|
||||
}
|
||||
|
||||
inline v128 operator^(const v128& left, const v128& right)
|
||||
{
|
||||
return v128::fromV(_mm_xor_si128(left.vi, right.vi));
|
||||
}
|
||||
|
||||
inline v128 operator~(const v128& other)
|
||||
{
|
||||
return other ^ v128::from32p(UINT32_MAX); // XOR with ones
|
||||
}
|
||||
|
||||
using stx::se_t;
|
||||
using stx::se_storage;
|
||||
|
||||
// se_t<> with native endianness
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
using nse_t = se_t<T, false, Align>;
|
||||
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
using be_t = se_t<T, std::endian::little == std::endian::native, Align>;
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
using le_t = se_t<T, std::endian::big == std::endian::native, Align>;
|
||||
|
||||
// Type converter: converts native endianness arithmetic/enum types to appropriate se_t<> type
|
||||
template <typename T, bool Se, typename = void>
|
||||
struct to_se
|
||||
{
|
||||
template <typename T2, typename = void>
|
||||
struct to_se_
|
||||
{
|
||||
using type = T2;
|
||||
};
|
||||
|
||||
template <typename T2>
|
||||
struct to_se_<T2, std::enable_if_t<std::is_arithmetic<T2>::value || std::is_enum<T2>::value>>
|
||||
{
|
||||
using type = std::conditional_t<(sizeof(T2) > 1), se_t<T2, Se>, T2>;
|
||||
};
|
||||
|
||||
// Convert arithmetic and enum types
|
||||
using type = typename to_se_<T>::type;
|
||||
};
|
||||
|
||||
template <bool Se>
|
||||
struct to_se<v128, Se>
|
||||
{
|
||||
using type = se_t<v128, Se>;
|
||||
};
|
||||
|
||||
template <bool Se>
|
||||
struct to_se<u128, Se>
|
||||
{
|
||||
using type = se_t<u128, Se>;
|
||||
};
|
||||
|
||||
template <bool Se>
|
||||
struct to_se<s128, Se>
|
||||
{
|
||||
using type = se_t<s128, Se>;
|
||||
};
|
||||
|
||||
template <typename T, bool Se>
|
||||
struct to_se<const T, Se, std::enable_if_t<!std::is_array<T>::value>>
|
||||
{
|
||||
// Move const qualifier
|
||||
using type = const typename to_se<T, Se>::type;
|
||||
};
|
||||
|
||||
template <typename T, bool Se>
|
||||
struct to_se<volatile T, Se, std::enable_if_t<!std::is_array<T>::value && !std::is_const<T>::value>>
|
||||
{
|
||||
// Move volatile qualifier
|
||||
using type = volatile typename to_se<T, Se>::type;
|
||||
};
|
||||
|
||||
template <typename T, bool Se>
|
||||
struct to_se<T[], Se>
|
||||
{
|
||||
// Move array qualifier
|
||||
using type = typename to_se<T, Se>::type[];
|
||||
};
|
||||
|
||||
template <typename T, bool Se, std::size_t N>
|
||||
struct to_se<T[N], Se>
|
||||
{
|
||||
// Move array qualifier
|
||||
using type = typename to_se<T, Se>::type[N];
|
||||
};
|
||||
|
||||
// BE/LE aliases for to_se<>
|
||||
template <typename T>
|
||||
using to_be_t = typename to_se<T, std::endian::little == std::endian::native>::type;
|
||||
template <typename T>
|
||||
using to_le_t = typename to_se<T, std::endian::big == std::endian::native>::type;
|
||||
|
||||
// BE/LE aliases for atomic_t
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
using atomic_be_t = atomic_t<be_t<T>, Align>;
|
||||
template <typename T, std::size_t Align = alignof(T)>
|
||||
using atomic_le_t = atomic_t<le_t<T>, Align>;
|
||||
|
||||
template <typename T, bool Se, std::size_t Align>
|
||||
struct fmt_unveil<se_t<T, Se, Align>, void>
|
||||
{
|
||||
using type = typename fmt_unveil<T>::type;
|
||||
|
||||
static inline auto get(const se_t<T, Se, Align>& arg)
|
||||
{
|
||||
return fmt_unveil<T>::get(arg);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // BETYPE_H_GUARD
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "Utilities/StrFmt.h"
|
||||
|
||||
template<typename T, uint N>
|
||||
struct bf_base
|
||||
|
||||
+11
-14
@@ -1,14 +1,11 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "Config.h"
|
||||
#include "Utilities/types.h"
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include "util/yaml.hpp"
|
||||
|
||||
#include <typeinfo>
|
||||
#include <charconv>
|
||||
|
||||
[[noreturn]] void report_fatal_error(const std::string&);
|
||||
|
||||
LOG_CHANNEL(cfg_log, "CFG");
|
||||
|
||||
namespace cfg
|
||||
@@ -18,7 +15,7 @@ namespace cfg
|
||||
{
|
||||
if (_type != type::node)
|
||||
{
|
||||
cfg_log.fatal("Invalid root node" HERE);
|
||||
cfg_log.fatal("Invalid root node");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +26,7 @@ namespace cfg
|
||||
{
|
||||
if (pair.first == name)
|
||||
{
|
||||
cfg_log.fatal("Node already exists: %s" HERE, name);
|
||||
cfg_log.fatal("Node already exists: %s", name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +35,12 @@ namespace cfg
|
||||
|
||||
bool _base::from_string(const std::string&, bool)
|
||||
{
|
||||
report_fatal_error("from_string() purecall" HERE);
|
||||
fmt::throw_exception("from_string() purecall");
|
||||
}
|
||||
|
||||
bool _base::from_list(std::vector<std::string>&&)
|
||||
{
|
||||
report_fatal_error("from_list() purecall" HERE);
|
||||
fmt::throw_exception("from_list() purecall");
|
||||
}
|
||||
|
||||
// Emit YAML
|
||||
@@ -84,7 +81,7 @@ bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
||||
|
||||
if (ret.ec != std::errc() || ret.ptr != end || (start[0] == '-' && sign < 0))
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_int('%s'): invalid integer", value);
|
||||
if (out) cfg_log.error("cfg::try_to_int64('%s'): invalid integer", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -92,7 +89,7 @@ bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_int('%s'): out of bounds (%d..%d)", value, min, max);
|
||||
if (out) cfg_log.error("cfg::try_to_int64('%s'): out of bounds (%d..%d)", value, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -123,13 +120,13 @@ bool cfg::try_to_uint64(u64* out, const std::string& value, u64 min, u64 max)
|
||||
|
||||
if (ret.ec != std::errc() || ret.ptr != end)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_int('%s'): invalid integer", value);
|
||||
if (out) cfg_log.error("cfg::try_to_uint64('%s'): invalid integer", value);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result < min || result > max)
|
||||
{
|
||||
if (out) cfg_log.error("cfg::try_to_int('%s'): out of bounds (%u..%u)", value, min, max);
|
||||
if (out) cfg_log.error("cfg::try_to_uint64('%s'): out of bounds (%u..%u)", value, min, max);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -380,7 +377,7 @@ void cfg::_bool::from_default()
|
||||
|
||||
void cfg::string::from_default()
|
||||
{
|
||||
m_value = m_value.make(def);
|
||||
m_value = def;
|
||||
}
|
||||
|
||||
void cfg::set_entry::from_default()
|
||||
|
||||
+7
-7
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "util/logs.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include "util/shared_cptr.hpp"
|
||||
#include "util/shared_ptr.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <string>
|
||||
@@ -393,7 +393,7 @@ namespace cfg
|
||||
{
|
||||
const std::string m_name;
|
||||
|
||||
stx::atomic_cptr<std::string> m_value;
|
||||
atomic_ptr<std::string> m_value;
|
||||
|
||||
public:
|
||||
std::string def;
|
||||
@@ -401,7 +401,7 @@ namespace cfg
|
||||
string(node* owner, std::string name, std::string def = {}, bool dynamic = false)
|
||||
: _base(type::string, owner, name, dynamic)
|
||||
, m_name(std::move(name))
|
||||
, m_value(m_value.make(def))
|
||||
, m_value(def)
|
||||
, def(std::move(def))
|
||||
{
|
||||
}
|
||||
@@ -411,7 +411,7 @@ namespace cfg
|
||||
return *m_value.load().get();
|
||||
}
|
||||
|
||||
std::pair<const std::string&, stx::shared_cptr<std::string>> get() const
|
||||
std::pair<const std::string&, shared_ptr<std::string>> get() const
|
||||
{
|
||||
auto v = m_value.load();
|
||||
|
||||
@@ -440,7 +440,7 @@ namespace cfg
|
||||
|
||||
bool from_string(const std::string& value, bool /*dynamic*/ = false) override
|
||||
{
|
||||
m_value = m_value.make(value);
|
||||
m_value = value;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
+181
-61
@@ -1,16 +1,16 @@
|
||||
#include "File.h"
|
||||
#include "File.h"
|
||||
#include "mutex.h"
|
||||
#include "StrFmt.h"
|
||||
#include "BEType.h"
|
||||
#include "Crypto/sha1.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cerrno>
|
||||
#include <typeinfo>
|
||||
#include <map>
|
||||
|
||||
#include "util/asm.hpp"
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
#ifdef _WIN32
|
||||
@@ -18,13 +18,18 @@ using namespace std::literals::string_literals;
|
||||
#include <cwchar>
|
||||
#include <Windows.h>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
u64 get_unique_tsc();
|
||||
}
|
||||
|
||||
static std::unique_ptr<wchar_t[]> to_wchar(const std::string& source)
|
||||
{
|
||||
// String size + null terminator
|
||||
const std::size_t buf_size = source.size() + 1;
|
||||
const usz buf_size = source.size() + 1;
|
||||
|
||||
// Safe size
|
||||
const int size = narrow<int>(buf_size, "to_wchar" HERE);
|
||||
const int size = narrow<int>(buf_size);
|
||||
|
||||
// Buffer for max possible output length
|
||||
std::unique_ptr<wchar_t[]> buffer(new wchar_t[buf_size + 8 + 32768]);
|
||||
@@ -41,10 +46,10 @@ static std::unique_ptr<wchar_t[]> to_wchar(const std::string& source)
|
||||
std::memcpy(buffer.get() + 32768 + 4, L"UNC\\", 4 * sizeof(wchar_t));
|
||||
}
|
||||
|
||||
verify("to_wchar" HERE), MultiByteToWideChar(CP_UTF8, 0, source.c_str(), size, buffer.get() + 32768 + (unc ? 8 : 4), size);
|
||||
ensure(MultiByteToWideChar(CP_UTF8, 0, source.c_str(), size, buffer.get() + 32768 + (unc ? 8 : 4), size)); // "to_wchar"
|
||||
|
||||
// Canonicalize wide path (replace '/', ".", "..", \\ repetitions, etc)
|
||||
verify("to_wchar" HERE), GetFullPathNameW(buffer.get() + 32768, 32768, buffer.get(), nullptr) - 1 < 32768 - 1;
|
||||
ensure(GetFullPathNameW(buffer.get() + 32768, 32768, buffer.get(), nullptr) - 1 < 32768 - 1); // "to_wchar"
|
||||
|
||||
return buffer;
|
||||
}
|
||||
@@ -52,10 +57,10 @@ static std::unique_ptr<wchar_t[]> to_wchar(const std::string& source)
|
||||
static void to_utf8(std::string& out, const wchar_t* source)
|
||||
{
|
||||
// String size
|
||||
const std::size_t length = std::wcslen(source);
|
||||
const usz length = std::wcslen(source);
|
||||
|
||||
// Safe buffer size for max possible output length (including null terminator)
|
||||
const int buf_size = narrow<int>(length * 3 + 1, "to_utf8" HERE);
|
||||
const int buf_size = narrow<int>(length * 3 + 1);
|
||||
|
||||
// Resize buffer
|
||||
out.resize(buf_size - 1);
|
||||
@@ -63,7 +68,7 @@ static void to_utf8(std::string& out, const wchar_t* source)
|
||||
const int result = WideCharToMultiByte(CP_UTF8, 0, source, static_cast<int>(length) + 1, &out.front(), buf_size, NULL, NULL);
|
||||
|
||||
// Fix the size
|
||||
out.resize(verify("to_utf8" HERE, result) - 1);
|
||||
out.resize(ensure(result) - 1);
|
||||
}
|
||||
|
||||
static time_t to_time(const ULARGE_INTEGER& ft)
|
||||
@@ -182,7 +187,7 @@ static std::string path_append(std::string_view path, std::string_view more)
|
||||
{
|
||||
std::string result;
|
||||
|
||||
if (const size_t src_slash_pos = path.find_last_not_of('/'); src_slash_pos != path.npos)
|
||||
if (const usz src_slash_pos = path.find_last_not_of('/'); src_slash_pos != path.npos)
|
||||
{
|
||||
path.remove_suffix(path.length() - src_slash_pos - 1);
|
||||
result = path;
|
||||
@@ -190,7 +195,7 @@ static std::string path_append(std::string_view path, std::string_view more)
|
||||
|
||||
result.push_back('/');
|
||||
|
||||
if (const size_t dst_slash_pos = more.find_first_not_of('/'); dst_slash_pos != more.npos)
|
||||
if (const usz dst_slash_pos = more.find_first_not_of('/'); dst_slash_pos != more.npos)
|
||||
{
|
||||
more.remove_prefix(dst_slash_pos);
|
||||
result.append(more);
|
||||
@@ -227,7 +232,7 @@ namespace fs
|
||||
|
||||
stat_t file_base::stat()
|
||||
{
|
||||
fmt::throw_exception("fs::file::stat() not supported for %s", typeid(*this).name());
|
||||
fmt::throw_exception("fs::file::stat() not supported.");
|
||||
}
|
||||
|
||||
void file_base::sync()
|
||||
@@ -315,19 +320,60 @@ std::shared_ptr<fs::device_base> fs::get_virtual_device(const std::string& path)
|
||||
|
||||
std::shared_ptr<fs::device_base> fs::set_virtual_device(const std::string& name, const std::shared_ptr<device_base>& device)
|
||||
{
|
||||
verify(HERE), name.starts_with("//"), name[2] != '/';
|
||||
ensure(name.starts_with("//") && name[2] != '/');
|
||||
|
||||
return get_device_manager().set_device(name, device);
|
||||
}
|
||||
|
||||
std::string fs::get_parent_dir(const std::string& path)
|
||||
{
|
||||
// Get (basically) processed path
|
||||
const auto real_path = fs::escape_path(path);
|
||||
std::string_view result = path;
|
||||
|
||||
const auto pos = real_path.find_last_of(delim);
|
||||
// Number of path components to remove
|
||||
usz to_remove = 1;
|
||||
|
||||
return real_path.substr(0, pos == umax ? 0 : pos);
|
||||
while (to_remove--)
|
||||
{
|
||||
// Trim contiguous delimiters at the end
|
||||
if (usz sz = result.find_last_not_of(delim) + 1)
|
||||
{
|
||||
result = result.substr(0, sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "/";
|
||||
}
|
||||
|
||||
const auto elem = result.substr(result.find_last_of(delim) + 1);
|
||||
|
||||
if (elem.empty() || elem.size() == result.size())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (elem == ".")
|
||||
{
|
||||
to_remove += 1;
|
||||
}
|
||||
|
||||
if (elem == "..")
|
||||
{
|
||||
to_remove += 2;
|
||||
}
|
||||
|
||||
result.remove_suffix(elem.size());
|
||||
}
|
||||
|
||||
if (usz sz = result.find_last_not_of(delim) + 1)
|
||||
{
|
||||
result = result.substr(0, sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "/";
|
||||
}
|
||||
|
||||
return std::string{result};
|
||||
}
|
||||
|
||||
bool fs::stat(const std::string& path, stat_t& info)
|
||||
@@ -355,7 +401,7 @@ bool fs::stat(const std::string& path, stat_t& info)
|
||||
if (!GetFileAttributesExW(to_wchar(std::string(epath) + '/').get(), GetFileExInfoStandard, &attrs))
|
||||
{
|
||||
g_tls_error = to_error(GetLastError());
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
info.is_directory = true; // Handle drives as directories
|
||||
@@ -404,7 +450,7 @@ bool fs::stat(const std::string& path, stat_t& info)
|
||||
if (const DWORD err = GetLastError(); err != ERROR_NO_MORE_FILES)
|
||||
{
|
||||
g_tls_error = to_error(err);
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
g_tls_error = fs::error::noent;
|
||||
@@ -506,7 +552,7 @@ bool fs::statfs(const std::string& path, fs::device_stat& info)
|
||||
// Keep cutting path from right until it's short enough
|
||||
while (str.size() > 256)
|
||||
{
|
||||
if (std::size_t x = str.find_last_of('\\') + 1)
|
||||
if (usz x = str.find_last_of('\\') + 1)
|
||||
str.resize(x - 1);
|
||||
else
|
||||
break;
|
||||
@@ -900,14 +946,29 @@ bool fs::utime(const std::string& path, s64 atime, s64 mtime)
|
||||
#endif
|
||||
}
|
||||
|
||||
void fs::file::xnull() const
|
||||
void fs::sync()
|
||||
{
|
||||
fmt::throw_exception("fs::file is null");
|
||||
#ifdef _WIN32
|
||||
fs::g_tls_error = fs::error::unknown;
|
||||
#else
|
||||
::sync();
|
||||
fs::g_tls_error = fs::error::ok;
|
||||
#endif
|
||||
}
|
||||
|
||||
void fs::file::xfail() const
|
||||
[[noreturn]] void fs::xnull(const src_loc& loc)
|
||||
{
|
||||
fmt::throw_exception("Unexpected fs::error %s", g_tls_error);
|
||||
fmt::throw_exception("Null object.%s", loc);
|
||||
}
|
||||
|
||||
[[noreturn]] void fs::xfail(const src_loc& loc)
|
||||
{
|
||||
fmt::throw_exception("Unexpected fs::error %s%s", g_tls_error, loc);
|
||||
}
|
||||
|
||||
[[noreturn]] void fs::xovfl()
|
||||
{
|
||||
fmt::throw_exception("Stream overflow.");
|
||||
}
|
||||
|
||||
fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
@@ -990,7 +1051,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
stat_t stat() override
|
||||
{
|
||||
FILE_BASIC_INFO basic_info;
|
||||
verify("file::stat" HERE), GetFileInformationByHandleEx(m_handle, FileBasicInfo, &basic_info, sizeof(FILE_BASIC_INFO));
|
||||
ensure(GetFileInformationByHandleEx(m_handle, FileBasicInfo, &basic_info, sizeof(FILE_BASIC_INFO))); // "file::stat"
|
||||
|
||||
stat_t info;
|
||||
info.is_directory = (basic_info.FileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
@@ -1008,7 +1069,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
void sync() override
|
||||
{
|
||||
verify("file::sync" HERE), FlushFileBuffers(m_handle);
|
||||
ensure(FlushFileBuffers(m_handle)); // "file::sync"
|
||||
}
|
||||
|
||||
bool trunc(u64 length) override
|
||||
@@ -1028,10 +1089,10 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
u64 read(void* buffer, u64 count) override
|
||||
{
|
||||
// TODO (call ReadFile multiple times if count is too big)
|
||||
const int size = narrow<int>(count, "file::read" HERE);
|
||||
const int size = narrow<int>(count);
|
||||
|
||||
DWORD nread;
|
||||
verify("file::read" HERE), ReadFile(m_handle, buffer, size, &nread, NULL);
|
||||
ensure(ReadFile(m_handle, buffer, size, &nread, NULL)); // "file::read"
|
||||
|
||||
return nread;
|
||||
}
|
||||
@@ -1039,24 +1100,27 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
u64 write(const void* buffer, u64 count) override
|
||||
{
|
||||
// TODO (call WriteFile multiple times if count is too big)
|
||||
const int size = narrow<int>(count, "file::write" HERE);
|
||||
const int size = narrow<int>(count);
|
||||
|
||||
DWORD nwritten;
|
||||
verify("file::write" HERE), WriteFile(m_handle, buffer, size, &nwritten, NULL);
|
||||
ensure(WriteFile(m_handle, buffer, size, &nwritten, NULL)); // "file::write"
|
||||
|
||||
return nwritten;
|
||||
}
|
||||
|
||||
u64 seek(s64 offset, seek_mode whence) override
|
||||
{
|
||||
if (whence > seek_end)
|
||||
{
|
||||
fmt::throw_exception("Invalid whence (0x%x)", whence);
|
||||
}
|
||||
|
||||
LARGE_INTEGER pos;
|
||||
pos.QuadPart = offset;
|
||||
|
||||
const DWORD mode =
|
||||
whence == seek_set ? FILE_BEGIN :
|
||||
whence == seek_cur ? FILE_CURRENT :
|
||||
whence == seek_end ? FILE_END :
|
||||
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
|
||||
whence == seek_cur ? FILE_CURRENT : FILE_END;
|
||||
|
||||
if (!SetFilePointerEx(m_handle, pos, &pos, mode))
|
||||
{
|
||||
@@ -1070,7 +1134,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
u64 size() override
|
||||
{
|
||||
LARGE_INTEGER size;
|
||||
verify("file::size" HERE), GetFileSizeEx(m_handle, &size);
|
||||
ensure(GetFileSizeEx(m_handle, &size)); // "file::size"
|
||||
|
||||
return size.QuadPart;
|
||||
}
|
||||
@@ -1091,13 +1155,22 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
if (mode & fs::append) flags |= O_APPEND;
|
||||
if (mode & fs::create) flags |= O_CREAT;
|
||||
if (mode & fs::trunc && !(mode & (fs::lock + fs::unread))) flags |= O_TRUNC;
|
||||
if (mode & fs::trunc && !(mode & fs::lock)) flags |= O_TRUNC;
|
||||
if (mode & fs::excl) flags |= O_EXCL;
|
||||
|
||||
int perm = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
|
||||
if (mode & fs::write && mode & fs::unread)
|
||||
{
|
||||
if (!(mode & (fs::excl + fs::lock)) && mode & fs::trunc)
|
||||
{
|
||||
// Alternative to truncation for "unread" flag (TODO)
|
||||
if (mode & fs::create)
|
||||
{
|
||||
::unlink(path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
perm = 0;
|
||||
}
|
||||
|
||||
@@ -1109,17 +1182,17 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode & fs::write && mode & (fs::lock + fs::unread) && ::flock(fd, LOCK_EX | LOCK_NB) != 0)
|
||||
if (mode & fs::write && mode & fs::lock && ::flock(fd, LOCK_EX | LOCK_NB) != 0)
|
||||
{
|
||||
g_tls_error = errno == EWOULDBLOCK ? fs::error::acces : to_error(errno);
|
||||
::close(fd);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode & fs::trunc && mode & (fs::lock + fs::unread) && mode & fs::write)
|
||||
if (mode & fs::trunc && mode & fs::lock && mode & fs::write)
|
||||
{
|
||||
// Postpone truncation in order to avoid using O_TRUNC on a locked file
|
||||
verify(HERE), ::ftruncate(fd, 0) == 0;
|
||||
ensure(::ftruncate(fd, 0) == 0);
|
||||
}
|
||||
|
||||
class unix_file final : public file_base
|
||||
@@ -1140,7 +1213,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
stat_t stat() override
|
||||
{
|
||||
struct ::stat file_info;
|
||||
verify("file::stat" HERE), ::fstat(m_fd, &file_info) == 0;
|
||||
ensure(::fstat(m_fd, &file_info) == 0); // "file::stat"
|
||||
|
||||
stat_t info;
|
||||
info.is_directory = S_ISDIR(file_info.st_mode);
|
||||
@@ -1158,7 +1231,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
|
||||
void sync() override
|
||||
{
|
||||
verify("file::sync" HERE), ::fsync(m_fd) == 0;
|
||||
ensure(::fsync(m_fd) == 0); // "file::sync"
|
||||
}
|
||||
|
||||
bool trunc(u64 length) override
|
||||
@@ -1175,7 +1248,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
u64 read(void* buffer, u64 count) override
|
||||
{
|
||||
const auto result = ::read(m_fd, buffer, count);
|
||||
verify("file::read" HERE), result != -1;
|
||||
ensure(result != -1); // "file::read"
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1183,18 +1256,21 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
u64 write(const void* buffer, u64 count) override
|
||||
{
|
||||
const auto result = ::write(m_fd, buffer, count);
|
||||
verify("file::write" HERE), result != -1;
|
||||
ensure(result != -1); // "file::write"
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
u64 seek(s64 offset, seek_mode whence) override
|
||||
{
|
||||
if (whence > seek_end)
|
||||
{
|
||||
fmt::throw_exception("Invalid whence (0x%x)", whence);
|
||||
}
|
||||
|
||||
const int mode =
|
||||
whence == seek_set ? SEEK_SET :
|
||||
whence == seek_cur ? SEEK_CUR :
|
||||
whence == seek_end ? SEEK_END :
|
||||
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
|
||||
whence == seek_cur ? SEEK_CUR : SEEK_END;
|
||||
|
||||
const auto result = ::lseek(m_fd, offset, mode);
|
||||
|
||||
@@ -1210,7 +1286,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
u64 size() override
|
||||
{
|
||||
struct ::stat file_info;
|
||||
verify("file::size" HERE), ::fstat(m_fd, &file_info) == 0;
|
||||
ensure(::fstat(m_fd, &file_info) == 0); // "file::size"
|
||||
|
||||
return file_info.st_size;
|
||||
}
|
||||
@@ -1226,7 +1302,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
static_assert(offsetof(iovec, iov_len) == offsetof(iovec_clone, iov_len), "Weird iovec::iov_len offset");
|
||||
|
||||
const auto result = ::writev(m_fd, reinterpret_cast<const iovec*>(buffers), buf_count);
|
||||
verify("file::write_gather" HERE), result != -1;
|
||||
ensure(result != -1); // "file::write_gather"
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1236,7 +1312,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
||||
#endif
|
||||
}
|
||||
|
||||
fs::file::file(const void* ptr, std::size_t size)
|
||||
fs::file::file(const void* ptr, usz size)
|
||||
{
|
||||
class memory_stream : public file_base
|
||||
{
|
||||
@@ -1318,11 +1394,6 @@ fs::native_handle fs::file::get_handle() const
|
||||
#endif
|
||||
}
|
||||
|
||||
void fs::dir::xnull() const
|
||||
{
|
||||
fmt::throw_exception("fs::dir is null");
|
||||
}
|
||||
|
||||
bool fs::dir::open(const std::string& path)
|
||||
{
|
||||
if (path.empty())
|
||||
@@ -1356,7 +1427,7 @@ bool fs::dir::open(const std::string& path)
|
||||
class windows_dir final : public dir_base
|
||||
{
|
||||
std::vector<dir_entry> m_entries;
|
||||
std::size_t m_pos = 0;
|
||||
usz m_pos = 0;
|
||||
|
||||
void add_entry(const WIN32_FIND_DATAW& found)
|
||||
{
|
||||
@@ -1386,7 +1457,7 @@ bool fs::dir::open(const std::string& path)
|
||||
add_entry(found);
|
||||
}
|
||||
|
||||
verify("dir::read" HERE), ERROR_NO_MORE_FILES == GetLastError();
|
||||
ensure(ERROR_NO_MORE_FILES == GetLastError()); // "dir::read"
|
||||
FindClose(handle);
|
||||
}
|
||||
|
||||
@@ -1606,13 +1677,13 @@ std::string fs::escape_path(std::string_view path)
|
||||
{
|
||||
std::string real; real.resize(path.size());
|
||||
|
||||
auto get_char = [&](std::size_t& from, std::size_t& to, std::size_t count)
|
||||
auto get_char = [&](usz& from, usz& to, usz count)
|
||||
{
|
||||
std::memcpy(&real[to], &path[from], count);
|
||||
from += count, to += count;
|
||||
};
|
||||
|
||||
std::size_t i = 0, j = -1, pos_nondelim = 0, after_delim = 0;
|
||||
usz i = 0, j = -1, pos_nondelim = 0, after_delim = 0;
|
||||
|
||||
if (i < path.size())
|
||||
{
|
||||
@@ -1649,7 +1720,7 @@ std::string fs::escape_path(std::string_view path)
|
||||
case '.':
|
||||
{
|
||||
bool remove_element = true;
|
||||
std::size_t k = 1;
|
||||
usz k = 1;
|
||||
|
||||
for (; k + i != path.size(); k++)
|
||||
{
|
||||
@@ -1714,7 +1785,7 @@ u64 fs::get_dir_size(const std::string& path, u64 rounding_alignment)
|
||||
|
||||
if (!root_dir)
|
||||
{
|
||||
return static_cast<u64>(umax);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (const auto& entry : root_dir)
|
||||
@@ -1726,7 +1797,7 @@ u64 fs::get_dir_size(const std::string& path, u64 rounding_alignment)
|
||||
|
||||
if (!entry.is_directory)
|
||||
{
|
||||
result += ::align(entry.size, rounding_alignment);
|
||||
result += utils::align(entry.size, rounding_alignment);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1859,6 +1930,55 @@ fs::file fs::make_gather(std::vector<fs::file> files)
|
||||
return result;
|
||||
}
|
||||
|
||||
fs::pending_file::pending_file(const std::string& path)
|
||||
{
|
||||
do
|
||||
{
|
||||
m_path = fmt::format(u8"%s/$%s.%s.tmp", get_parent_dir(path), std::string_view(path).substr(path.find_last_of(fs::delim) + 1), fmt::base57(utils::get_unique_tsc()));
|
||||
|
||||
if (file.open(m_path, fs::create + fs::write + fs::read + fs::excl))
|
||||
{
|
||||
m_dest = path;
|
||||
break;
|
||||
}
|
||||
|
||||
m_path.clear();
|
||||
}
|
||||
while (fs::g_tls_error == fs::error::exist); // Only retry if failed due to existing file
|
||||
}
|
||||
|
||||
fs::pending_file::~pending_file()
|
||||
{
|
||||
file.close();
|
||||
|
||||
if (!m_path.empty())
|
||||
{
|
||||
fs::remove_file(m_path);
|
||||
}
|
||||
}
|
||||
|
||||
bool fs::pending_file::commit(bool overwrite)
|
||||
{
|
||||
if (!file || m_path.empty())
|
||||
{
|
||||
fs::g_tls_error = fs::error::noent;
|
||||
return false;
|
||||
}
|
||||
|
||||
// The temporary file's contents must be on disk before rename
|
||||
file.sync();
|
||||
file.close();
|
||||
|
||||
if (fs::rename(m_path, m_dest, overwrite))
|
||||
{
|
||||
// Disable the destructor
|
||||
m_path.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
void fmt_class_string<fs::seek_mode>::format(std::string& out, u64 arg)
|
||||
{
|
||||
|
||||
+197
-63
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#pragma once // No BOM and only basic ASCII in this header, or a neko will die
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "bit_set.h"
|
||||
|
||||
#include <memory>
|
||||
@@ -73,7 +73,7 @@ namespace fs
|
||||
struct iovec_clone
|
||||
{
|
||||
const void* iov_base;
|
||||
std::size_t iov_len;
|
||||
usz iov_len;
|
||||
};
|
||||
|
||||
// File handle base
|
||||
@@ -96,6 +96,11 @@ namespace fs
|
||||
struct dir_entry : stat_t
|
||||
{
|
||||
std::string name;
|
||||
|
||||
dir_entry()
|
||||
: stat_t{}
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Directory handle base
|
||||
@@ -134,6 +139,12 @@ namespace fs
|
||||
virtual std::unique_ptr<dir_base> open_dir(const std::string& path) = 0;
|
||||
};
|
||||
|
||||
[[noreturn]] void xnull(const src_loc&);
|
||||
[[noreturn]] void xfail(const src_loc&);
|
||||
[[noreturn]] void xovfl();
|
||||
|
||||
constexpr struct pod_tag_t{} pod_tag;
|
||||
|
||||
// Get virtual device for specified path (nullptr for real path)
|
||||
std::shared_ptr<device_base> get_virtual_device(const std::string& path);
|
||||
|
||||
@@ -182,13 +193,13 @@ namespace fs
|
||||
// Set file access/modification time
|
||||
bool utime(const std::string& path, s64 atime, s64 mtime);
|
||||
|
||||
// Synchronize filesystems (TODO)
|
||||
void sync();
|
||||
|
||||
class file final
|
||||
{
|
||||
std::unique_ptr<file_base> m_file;
|
||||
|
||||
[[noreturn]] void xnull() const;
|
||||
[[noreturn]] void xfail() const;
|
||||
|
||||
public:
|
||||
// Default constructor
|
||||
file() = default;
|
||||
@@ -197,7 +208,7 @@ namespace fs
|
||||
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
|
||||
|
||||
// Open memory for read
|
||||
explicit file(const void* ptr, std::size_t size);
|
||||
explicit file(const void* ptr, usz size);
|
||||
|
||||
// Open file with specified args (forward to constructor)
|
||||
template <typename... Args>
|
||||
@@ -230,144 +241,227 @@ namespace fs
|
||||
}
|
||||
|
||||
// Change file size (possibly appending zero bytes)
|
||||
bool trunc(u64 length) const
|
||||
bool trunc(u64 length,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
if (!m_file) xnull({line, col, file, func});
|
||||
return m_file->trunc(length);
|
||||
}
|
||||
|
||||
// Get file information
|
||||
stat_t stat() const
|
||||
stat_t stat(
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
if (!m_file) xnull({line, col, file, func});
|
||||
return m_file->stat();
|
||||
}
|
||||
|
||||
// Sync file buffers
|
||||
void sync() const
|
||||
void sync(
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
if (!m_file) xnull({line, col, file, func});
|
||||
return m_file->sync();
|
||||
}
|
||||
|
||||
// Read the data from the file and return the amount of data written in buffer
|
||||
u64 read(void* buffer, u64 count) const
|
||||
u64 read(void* buffer, u64 count,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
if (!m_file) xnull({line, col, file, func});
|
||||
return m_file->read(buffer, count);
|
||||
}
|
||||
|
||||
// Write the data to the file and return the amount of data actually written
|
||||
u64 write(const void* buffer, u64 count) const
|
||||
u64 write(const void* buffer, u64 count,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
if (!m_file) xnull({line, col, file, func});
|
||||
return m_file->write(buffer, count);
|
||||
}
|
||||
|
||||
// Change current position, returns resulting position
|
||||
u64 seek(s64 offset, seek_mode whence = seek_set) const
|
||||
u64 seek(s64 offset, seek_mode whence = seek_set,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
if (!m_file) xnull({line, col, file, func});
|
||||
return m_file->seek(offset, whence);
|
||||
}
|
||||
|
||||
// Get file size
|
||||
u64 size() const
|
||||
u64 size(
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
if (!m_file) xnull({line, col, file, func});
|
||||
return m_file->size();
|
||||
}
|
||||
|
||||
// Get current position
|
||||
u64 pos() const
|
||||
u64 pos(
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
if (!m_file) xnull({line, col, file, func});
|
||||
return m_file->seek(0, seek_cur);
|
||||
}
|
||||
|
||||
// Write std::string unconditionally
|
||||
const file& write(const std::string& str) const
|
||||
// Write std::basic_string unconditionally
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, const file&> write(const std::basic_string<T>& str,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (write(str.data(), str.size()) != str.size()) xfail();
|
||||
if (write(str.data(), str.size() * sizeof(T), line, col, file, func) != str.size() * sizeof(T)) xfail({line, col, file, func});
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Write POD unconditionally
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, const file&> write(const T& data) const
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, const file&> write(const T& data,
|
||||
pod_tag_t = pod_tag,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (write(std::addressof(data), sizeof(T)) != sizeof(T)) xfail();
|
||||
if (write(std::addressof(data), sizeof(T), line, col, file, func) != sizeof(T)) xfail({line, col, file, func});
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Write POD std::vector unconditionally
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, const file&> write(const std::vector<T>& vec) const
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, const file&> write(const std::vector<T>& vec,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (write(vec.data(), vec.size() * sizeof(T)) != vec.size() * sizeof(T)) xfail();
|
||||
if (write(vec.data(), vec.size() * sizeof(T), line, col, file, func) != vec.size() * sizeof(T)) xfail({line, col, file, func});
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Read std::string, size must be set by resize() method
|
||||
bool read(std::string& str) const
|
||||
// Read std::basic_string, size must be set by resize() method
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::basic_string<T>& str,
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION(),
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN()) const
|
||||
{
|
||||
return read(&str[0], str.size()) == str.size();
|
||||
return read(&str[0], str.size() * sizeof(T), line, col, file, func) == str.size() * sizeof(T);
|
||||
}
|
||||
|
||||
// Read std::string
|
||||
bool read(std::string& str, std::size_t size) const
|
||||
// Read std::basic_string
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::basic_string<T>& str, usz size,
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION(),
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN()) const
|
||||
{
|
||||
str.resize(size);
|
||||
return read(&str[0], size) == size;
|
||||
return read(&str[0], size * sizeof(T), line, col, file, func) == size * sizeof(T);
|
||||
}
|
||||
|
||||
// Read POD, sizeof(T) is used
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(T& data) const
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(T& data,
|
||||
pod_tag_t = pod_tag,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
return read(&data, sizeof(T)) == sizeof(T);
|
||||
return read(&data, sizeof(T), line, col, file, func) == sizeof(T);
|
||||
}
|
||||
|
||||
// Read POD std::vector, size must be set by resize() method
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::vector<T>& vec) const
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::vector<T>& vec,
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION(),
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN()) const
|
||||
{
|
||||
return read(vec.data(), sizeof(T) * vec.size()) == sizeof(T) * vec.size();
|
||||
return read(vec.data(), sizeof(T) * vec.size(), line, col, file, func) == sizeof(T) * vec.size();
|
||||
}
|
||||
|
||||
// Read POD std::vector
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::vector<T>& vec, std::size_t size) const
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, bool> read(std::vector<T>& vec, usz size,
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION(),
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN()) const
|
||||
{
|
||||
vec.resize(size);
|
||||
return read(vec.data(), sizeof(T) * size) == sizeof(T) * size;
|
||||
return read(vec.data(), sizeof(T) * size, line, col, file, func) == sizeof(T) * size;
|
||||
}
|
||||
|
||||
// Read POD (experimental)
|
||||
template <typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, T> read() const
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, T> read(
|
||||
pod_tag_t = pod_tag,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
T result;
|
||||
if (!read(result)) xfail();
|
||||
if (!read(result, pod_tag, line, col, file, func)) xfail({line, col, file, func});
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read full file to std::string
|
||||
std::string to_string() const
|
||||
// Read full file to std::basic_string
|
||||
template <typename T = char>
|
||||
std::basic_string<T> to_string(
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
std::string result;
|
||||
result.resize(size());
|
||||
if (seek(0), !read(result)) xfail();
|
||||
std::basic_string<T> result;
|
||||
result.resize(size() / sizeof(T));
|
||||
if (seek(0), !read(result, file, func, line, col)) xfail({line, col, file, func});
|
||||
return result;
|
||||
}
|
||||
|
||||
// Read full file to std::vector
|
||||
template<typename T>
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, std::vector<T>> to_vector() const
|
||||
std::enable_if_t<std::is_trivially_copyable_v<T> && !std::is_pointer_v<T>, std::vector<T>> to_vector(
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
std::vector<T> result;
|
||||
result.resize(size() / sizeof(T));
|
||||
if (seek(0), !read(result)) xfail();
|
||||
if (seek(0), !read(result, file, func, line, col)) xfail({line, col, file, func});
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -375,9 +469,13 @@ namespace fs
|
||||
native_handle get_handle() const;
|
||||
|
||||
// Gathered write
|
||||
u64 write_gather(const iovec_clone* buffers, u64 buf_count) const
|
||||
u64 write_gather(const iovec_clone* buffers, u64 buf_count,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (!m_file) xnull();
|
||||
if (!m_file) xnull({line, col, file, func});
|
||||
return m_file->write_gather(buffers, buf_count);
|
||||
}
|
||||
};
|
||||
@@ -386,8 +484,6 @@ namespace fs
|
||||
{
|
||||
std::unique_ptr<dir_base> m_dir;
|
||||
|
||||
[[noreturn]] void xnull() const;
|
||||
|
||||
public:
|
||||
dir() = default;
|
||||
|
||||
@@ -423,16 +519,24 @@ namespace fs
|
||||
}
|
||||
|
||||
// Get next directory entry
|
||||
bool read(dir_entry& out) const
|
||||
bool read(dir_entry& out,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (!m_dir) xnull();
|
||||
if (!m_dir) xnull({line, col, file, func});
|
||||
return m_dir->read(out);
|
||||
}
|
||||
|
||||
// Reset to the beginning
|
||||
void rewind() const
|
||||
void rewind(
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION()) const
|
||||
{
|
||||
if (!m_dir) xnull();
|
||||
if (!m_dir) xnull({line, col, file, func});
|
||||
return m_dir->rewind();
|
||||
}
|
||||
|
||||
@@ -501,6 +605,22 @@ namespace fs
|
||||
// Get common cache directory
|
||||
const std::string& get_cache_dir();
|
||||
|
||||
// Unique pending file creation destined to be renamed to the destination file
|
||||
struct pending_file
|
||||
{
|
||||
fs::file file;
|
||||
|
||||
// This is meant to modify files atomically, overwriting is likely
|
||||
bool commit(bool overwrite = true);
|
||||
|
||||
pending_file(const std::string& path);
|
||||
~pending_file();
|
||||
|
||||
private:
|
||||
std::string m_path; // Pending file path
|
||||
std::string m_dest; // Destination file path
|
||||
};
|
||||
|
||||
// Get real path for comparisons (TODO: investigate std::filesystem::path::compare implementation)
|
||||
std::string escape_path(std::string_view path);
|
||||
|
||||
@@ -578,7 +698,7 @@ namespace fs
|
||||
|
||||
if (old_size + size < old_size)
|
||||
{
|
||||
fmt::raw_error("fs::container_stream<>::write(): overflow");
|
||||
xovfl();
|
||||
}
|
||||
|
||||
if (pos > old_size)
|
||||
@@ -631,14 +751,28 @@ namespace fs
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
template <bool Flush = false, typename... Args>
|
||||
bool write_file(const std::string& path, bs_t<fs::open_mode> mode, const Args&... args)
|
||||
{
|
||||
// Always use write flag, remove read flag
|
||||
if (fs::file f{path, mode + fs::write - fs::read})
|
||||
{
|
||||
// Write args sequentially
|
||||
(f.write(args), ...);
|
||||
if constexpr (sizeof...(args) == 2u && (std::is_pointer_v<Args> || ...))
|
||||
{
|
||||
// Specialization for [const void*, usz] args
|
||||
f.write(args...);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Write args sequentially
|
||||
(f.write(args), ...);
|
||||
}
|
||||
|
||||
if constexpr (Flush)
|
||||
{
|
||||
f.sync();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+119
-43
@@ -1,16 +1,17 @@
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "util/sysinfo.hpp"
|
||||
#include "JIT.h"
|
||||
#include "StrFmt.h"
|
||||
#include "File.h"
|
||||
#include "util/logs.hpp"
|
||||
#include "mutex.h"
|
||||
#include "sysinfo.h"
|
||||
#include "VirtualMemory.h"
|
||||
#include "util/vm.hpp"
|
||||
#include "util/asm.hpp"
|
||||
#include <charconv>
|
||||
#include <immintrin.h>
|
||||
#include <zlib.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/mman.h>
|
||||
#define CAN_OVERCOMMIT
|
||||
#endif
|
||||
|
||||
@@ -22,7 +23,6 @@ static u8* get_jit_memory()
|
||||
static void* const s_memory2 = []() -> void*
|
||||
{
|
||||
void* ptr = utils::memory_reserve(0x80000000);
|
||||
|
||||
#ifdef CAN_OVERCOMMIT
|
||||
utils::memory_commit(ptr, 0x80000000);
|
||||
utils::memory_protect(ptr, 0x40000000, utils::protection::wx);
|
||||
@@ -40,7 +40,7 @@ static atomic_t<u64> s_code_pos{0}, s_data_pos{0};
|
||||
static std::vector<u8> s_code_init, s_data_init;
|
||||
|
||||
template <atomic_t<u64>& Ctr, uint Off, utils::protection Prot>
|
||||
static u8* add_jit_memory(std::size_t size, uint align)
|
||||
static u8* add_jit_memory(usz size, uint align)
|
||||
{
|
||||
// Select subrange
|
||||
u8* pointer = get_jit_memory() + Off;
|
||||
@@ -56,8 +56,8 @@ static u8* add_jit_memory(std::size_t size, uint align)
|
||||
// Simple allocation by incrementing pointer to the next free data
|
||||
const u64 pos = Ctr.atomic_op([&](u64& ctr) -> u64
|
||||
{
|
||||
const u64 _pos = ::align(ctr & 0xffff'ffff, align);
|
||||
const u64 _new = ::align(_pos + size, align);
|
||||
const u64 _pos = utils::align(ctr & 0xffff'ffff, align);
|
||||
const u64 _new = utils::align(_pos + size, align);
|
||||
|
||||
if (_new > 0x40000000) [[unlikely]]
|
||||
{
|
||||
@@ -73,7 +73,7 @@ static u8* add_jit_memory(std::size_t size, uint align)
|
||||
// Check the necessity to commit more memory
|
||||
if (_new > olda) [[unlikely]]
|
||||
{
|
||||
newa = ::align(_new, 0x100000);
|
||||
newa = utils::align(_new, 0x200000);
|
||||
}
|
||||
|
||||
ctr += _new - (ctr & 0xffff'ffff);
|
||||
@@ -82,15 +82,13 @@ static u8* add_jit_memory(std::size_t size, uint align)
|
||||
|
||||
if (pos == umax) [[unlikely]]
|
||||
{
|
||||
jit_log.error("JIT: Out of memory (size=0x%x, align=0x%x, off=0x%x)", size, align, Off);
|
||||
jit_log.error("Out of memory (size=0x%x, align=0x%x, off=0x%x)", size, align, Off);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (olda != newa) [[unlikely]]
|
||||
{
|
||||
#ifdef CAN_OVERCOMMIT
|
||||
madvise(pointer + olda, newa - olda, MADV_WILLNEED);
|
||||
#else
|
||||
#ifndef CAN_OVERCOMMIT
|
||||
// Commit more memory
|
||||
utils::memory_commit(pointer + olda, newa - olda, Prot);
|
||||
#endif
|
||||
@@ -118,7 +116,7 @@ jit_runtime::~jit_runtime()
|
||||
|
||||
asmjit::Error jit_runtime::_add(void** dst, asmjit::CodeHolder* code) noexcept
|
||||
{
|
||||
std::size_t codeSize = code->getCodeSize();
|
||||
usz codeSize = code->getCodeSize();
|
||||
if (!codeSize) [[unlikely]]
|
||||
{
|
||||
*dst = nullptr;
|
||||
@@ -132,7 +130,7 @@ asmjit::Error jit_runtime::_add(void** dst, asmjit::CodeHolder* code) noexcept
|
||||
return asmjit::kErrorNoVirtualMemory;
|
||||
}
|
||||
|
||||
std::size_t relocSize = code->relocate(p);
|
||||
usz relocSize = code->relocate(p);
|
||||
if (!relocSize) [[unlikely]]
|
||||
{
|
||||
*dst = nullptr;
|
||||
@@ -150,7 +148,7 @@ asmjit::Error jit_runtime::_release(void* ptr) noexcept
|
||||
return asmjit::kErrorOk;
|
||||
}
|
||||
|
||||
u8* jit_runtime::alloc(std::size_t size, uint align, bool exec) noexcept
|
||||
u8* jit_runtime::alloc(usz size, uint align, bool exec) noexcept
|
||||
{
|
||||
if (exec)
|
||||
{
|
||||
@@ -222,28 +220,29 @@ asmjit::Runtime& asmjit::get_global_runtime()
|
||||
|
||||
asmjit::Error _add(void** dst, asmjit::CodeHolder* code) noexcept override
|
||||
{
|
||||
std::size_t codeSize = code->getCodeSize();
|
||||
usz codeSize = code->getCodeSize();
|
||||
if (!codeSize) [[unlikely]]
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorNoCodeGenerated;
|
||||
}
|
||||
|
||||
void* p = m_pos.fetch_add(::align(codeSize, 4096));
|
||||
void* p = m_pos.fetch_add(utils::align(codeSize, 4096));
|
||||
if (!p || m_pos > m_max) [[unlikely]]
|
||||
{
|
||||
*dst = nullptr;
|
||||
jit_log.fatal("Out of memory (static asmjit)");
|
||||
return asmjit::kErrorNoVirtualMemory;
|
||||
}
|
||||
|
||||
std::size_t relocSize = code->relocate(p);
|
||||
usz relocSize = code->relocate(p);
|
||||
if (!relocSize) [[unlikely]]
|
||||
{
|
||||
*dst = nullptr;
|
||||
return asmjit::kErrorInvalidState;
|
||||
}
|
||||
|
||||
utils::memory_protect(p, ::align(codeSize, 4096), utils::protection::rx);
|
||||
utils::memory_protect(p, utils::align(codeSize, 4096), utils::protection::rx);
|
||||
flush(p, relocSize);
|
||||
*dst = p;
|
||||
|
||||
@@ -310,14 +309,74 @@ const bool jit_initialize = []() -> bool
|
||||
return true;
|
||||
}();
|
||||
|
||||
[[noreturn]] static void null(const char* name)
|
||||
{
|
||||
fmt::throw_exception("Null function: %s", name);
|
||||
}
|
||||
|
||||
namespace vm
|
||||
{
|
||||
extern u8* const g_sudo_addr;
|
||||
}
|
||||
|
||||
static shared_mutex null_mtx;
|
||||
|
||||
static std::unordered_map<std::string, u64> null_funcs;
|
||||
|
||||
static u64 make_null_function(const std::string& name)
|
||||
{
|
||||
if (name.starts_with("__0x"))
|
||||
{
|
||||
u32 addr = -1;
|
||||
auto res = std::from_chars(name.c_str() + 4, name.c_str() + name.size(), addr, 16);
|
||||
|
||||
if (res.ec == std::errc() && res.ptr == name.c_str() + name.size() && addr < 0x8000'0000)
|
||||
{
|
||||
// Point the garbage to reserved, non-executable memory
|
||||
return reinterpret_cast<u64>(vm::g_sudo_addr + addr);
|
||||
}
|
||||
}
|
||||
|
||||
std::lock_guard lock(null_mtx);
|
||||
|
||||
if (u64& func_ptr = null_funcs[name]) [[likely]]
|
||||
{
|
||||
// Already exists
|
||||
return func_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
using namespace asmjit;
|
||||
|
||||
// Build a "null" function that contains its name
|
||||
const auto func = build_function_asm<void (*)()>([&](X86Assembler& c, auto& args)
|
||||
{
|
||||
Label data = c.newLabel();
|
||||
c.lea(args[0], x86::qword_ptr(data, 0));
|
||||
c.jmp(imm_ptr(&null));
|
||||
c.align(kAlignCode, 16);
|
||||
c.bind(data);
|
||||
|
||||
// Copy function name bytes
|
||||
for (char ch : name)
|
||||
c.db(ch);
|
||||
c.db(0);
|
||||
c.align(kAlignData, 16);
|
||||
});
|
||||
|
||||
func_ptr = reinterpret_cast<u64>(func);
|
||||
return func_ptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple memory manager
|
||||
struct MemoryManager1 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
// 256 MiB for code or data
|
||||
static constexpr u64 c_max_size = 0x20000000 / 2;
|
||||
|
||||
// Allocation unit
|
||||
static constexpr u64 c_page_size = 4096;
|
||||
// Allocation unit (2M)
|
||||
static constexpr u64 c_page_size = 2 * 1024 * 1024;
|
||||
|
||||
// Reserve 512 MiB
|
||||
u8* const ptr = static_cast<u8*>(utils::memory_reserve(c_max_size * 2));
|
||||
@@ -332,45 +391,45 @@ struct MemoryManager1 : llvm::RTDyldMemoryManager
|
||||
utils::memory_release(ptr, c_max_size * 2);
|
||||
}
|
||||
|
||||
[[noreturn]] static void null()
|
||||
{
|
||||
fmt::throw_exception("Null function" HERE);
|
||||
}
|
||||
|
||||
llvm::JITSymbol findSymbol(const std::string& name) override
|
||||
{
|
||||
u64 addr = RTDyldMemoryManager::getSymbolAddress(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
addr = reinterpret_cast<uptr>(&null);
|
||||
addr = make_null_function(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
fmt::throw_exception("Failed to link '%s'", name);
|
||||
}
|
||||
}
|
||||
|
||||
return {addr, llvm::JITSymbolFlags::Exported};
|
||||
}
|
||||
|
||||
u8* allocate(u64& oldp, std::uintptr_t size, uint align, utils::protection prot)
|
||||
u8* allocate(u64& oldp, uptr size, uint align, utils::protection prot)
|
||||
{
|
||||
if (align > c_page_size)
|
||||
{
|
||||
jit_log.fatal("JIT: Unsupported alignment (size=0x%x, align=0x%x)", size, align);
|
||||
jit_log.fatal("Unsupported alignment (size=0x%x, align=0x%x)", size, align);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const u64 olda = ::align(oldp, align);
|
||||
const u64 newp = ::align(olda + size, align);
|
||||
const u64 olda = utils::align(oldp, align);
|
||||
const u64 newp = utils::align(olda + size, align);
|
||||
|
||||
if ((newp - 1) / c_max_size != oldp / c_max_size)
|
||||
{
|
||||
jit_log.fatal("JIT: Out of memory (size=0x%x, align=0x%x)", size, align);
|
||||
jit_log.fatal("Out of memory (size=0x%x, align=0x%x)", size, align);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((oldp - 1) / c_page_size != (newp - 1) / c_page_size)
|
||||
{
|
||||
// Allocate pages on demand
|
||||
const u64 pagea = ::align(oldp, c_page_size);
|
||||
const u64 psize = ::align(newp - pagea, c_page_size);
|
||||
const u64 pagea = utils::align(oldp, c_page_size);
|
||||
const u64 psize = utils::align(newp - pagea, c_page_size);
|
||||
utils::memory_commit(this->ptr + pagea, psize, prot);
|
||||
}
|
||||
|
||||
@@ -380,12 +439,12 @@ struct MemoryManager1 : llvm::RTDyldMemoryManager
|
||||
return this->ptr + olda;
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
u8* allocateCodeSection(uptr size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
return allocate(code_ptr, size, align, utils::protection::wx);
|
||||
}
|
||||
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
u8* allocateDataSection(uptr size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
return allocate(data_ptr, size, align, utils::protection::rw);
|
||||
}
|
||||
@@ -395,7 +454,7 @@ struct MemoryManager1 : llvm::RTDyldMemoryManager
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
void registerEHFrames(u8* addr, u64 load_addr, usz size) override
|
||||
{
|
||||
}
|
||||
|
||||
@@ -413,12 +472,29 @@ struct MemoryManager2 : llvm::RTDyldMemoryManager
|
||||
{
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
llvm::JITSymbol findSymbol(const std::string& name) override
|
||||
{
|
||||
u64 addr = RTDyldMemoryManager::getSymbolAddress(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
addr = make_null_function(name);
|
||||
|
||||
if (!addr)
|
||||
{
|
||||
fmt::throw_exception("Failed to link '%s' (MM2)", name);
|
||||
}
|
||||
}
|
||||
|
||||
return {addr, llvm::JITSymbolFlags::Exported};
|
||||
}
|
||||
|
||||
u8* allocateCodeSection(uptr size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, true);
|
||||
}
|
||||
|
||||
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
u8* allocateDataSection(uptr size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||
{
|
||||
return jit_runtime::alloc(size, align, false);
|
||||
}
|
||||
@@ -428,7 +504,7 @@ struct MemoryManager2 : llvm::RTDyldMemoryManager
|
||||
return false;
|
||||
}
|
||||
|
||||
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||
void registerEHFrames(u8* addr, u64 load_addr, usz size) override
|
||||
{
|
||||
}
|
||||
|
||||
@@ -458,7 +534,7 @@ public:
|
||||
name.append(".gz");
|
||||
|
||||
z_stream zs{};
|
||||
uLong zsz = compressBound(::narrow<u32>(obj.getBufferSize(), HERE)) + 256;
|
||||
uLong zsz = compressBound(::narrow<u32>(obj.getBufferSize())) + 256;
|
||||
auto zbuf = std::make_unique<uchar[]>(zsz);
|
||||
#ifndef _MSC_VER
|
||||
#pragma GCC diagnostic push
|
||||
@@ -683,7 +759,7 @@ jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, co
|
||||
|
||||
for (auto&& [name, addr] : _link)
|
||||
{
|
||||
m_engine->addGlobalMapping(name, addr);
|
||||
m_engine->updateGlobalMapping(name, addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-5
@@ -41,7 +41,7 @@ struct jit_runtime final : asmjit::HostRuntime
|
||||
asmjit::Error _release(void* p) noexcept override;
|
||||
|
||||
// Allocate memory
|
||||
static u8* alloc(std::size_t size, uint align, bool exec = true) noexcept;
|
||||
static u8* alloc(usz size, uint align, bool exec = true) noexcept;
|
||||
|
||||
// Should be called at least once after global initialization
|
||||
static void initialize();
|
||||
@@ -148,7 +148,7 @@ inline FT build_function_asm(F&& builder)
|
||||
|
||||
X86Assembler compiler(&code);
|
||||
builder(std::ref(compiler), args);
|
||||
ASSERT(compiler.getLastError() == 0);
|
||||
ensure(compiler.getLastError() == 0);
|
||||
|
||||
FT result;
|
||||
|
||||
@@ -167,10 +167,9 @@ inline FT build_function_asm(F&& builder)
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "mutex.h"
|
||||
|
||||
#include "restore_new.h"
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 0)
|
||||
#else
|
||||
@@ -178,6 +177,10 @@ inline FT build_function_asm(F&& builder)
|
||||
#pragma GCC diagnostic ignored "-Wall"
|
||||
#pragma GCC diagnostic ignored "-Wextra"
|
||||
#pragma GCC diagnostic ignored "-Wold-style-cast"
|
||||
#pragma GCC diagnostic ignored "-Wsuggest-override"
|
||||
#ifdef __clang__
|
||||
#pragma clang diagnostic ignored "-Winconsistent-missing-override"
|
||||
#endif
|
||||
#endif
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
@@ -187,7 +190,6 @@ inline FT build_function_asm(F&& builder)
|
||||
#else
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
#include "define_new_memleakdetect.h"
|
||||
|
||||
// Temporary compiler interface
|
||||
class jit_compiler final
|
||||
|
||||
+108
-86
@@ -1,8 +1,9 @@
|
||||
#include "StrFmt.h"
|
||||
#include "BEType.h"
|
||||
#include "StrFmt.h"
|
||||
#include "StrUtil.h"
|
||||
#include "cfmt.h"
|
||||
#include "util/endian.hpp"
|
||||
#include "util/logs.hpp"
|
||||
#include "util/v128.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
@@ -78,14 +79,14 @@ void fmt_class_string<fmt::base57>::format(std::string& out, u64 arg)
|
||||
static constexpr u8 s_tail[8] = {0, 2, 3, 5, 6, 7, 9, 10};
|
||||
|
||||
// Get full output size
|
||||
const std::size_t out_size = _arg.size / 8 * 11 + s_tail[_arg.size % 8];
|
||||
const usz out_size = _arg.size / 8 * 11 + s_tail[_arg.size % 8];
|
||||
|
||||
out.resize(out.size() + out_size);
|
||||
|
||||
const auto ptr = &out.front() + (out.size() - out_size);
|
||||
|
||||
// Each 8 bytes of input data produce 11 bytes of base57 output
|
||||
for (std::size_t i = 0, p = 0; i < _arg.size; i += 8, p += 11)
|
||||
for (usz i = 0, p = 0; i < _arg.size; i += 8, p += 11)
|
||||
{
|
||||
// Load up to 8 bytes
|
||||
be_t<u64> be_value;
|
||||
@@ -235,6 +236,12 @@ void fmt_class_string<bool>::format(std::string& out, u64 arg)
|
||||
out += arg ? "true" : "false";
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<b8>::format(std::string& out, u64 arg)
|
||||
{
|
||||
out += get_object(arg) ? "true" : "false";
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<v128>::format(std::string& out, u64 arg)
|
||||
{
|
||||
@@ -242,70 +249,76 @@ void fmt_class_string<v128>::format(std::string& out, u64 arg)
|
||||
fmt::append(out, "0x%016llx%016llx", vec._u64[1], vec._u64[0]);
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<u128>::format(std::string& out, u64 arg)
|
||||
{
|
||||
// TODO: it should be supported as full-fledged integral type (with %u, %d, etc, fmt)
|
||||
const u128& num = get_object(arg);
|
||||
#ifdef _MSC_VER
|
||||
fmt::append(out, "0x%016llx%016llx", num.hi, num.lo);
|
||||
#else
|
||||
fmt::append(out, "0x%016llx%016llx", static_cast<u64>(num >> 64), static_cast<u64>(num));
|
||||
#endif
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<src_loc>::format(std::string& out, u64 arg)
|
||||
{
|
||||
const src_loc& loc = get_object(arg);
|
||||
|
||||
if (loc.col != umax)
|
||||
{
|
||||
fmt::append(out, "\n(in file %s:%u[:%u]", loc.file, loc.line, loc.col);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::append(out, "\n(in file %s:%u", loc.file, loc.line);
|
||||
}
|
||||
|
||||
if (loc.func && *loc.func)
|
||||
{
|
||||
fmt::append(out, ", in function %s)", loc.func);
|
||||
}
|
||||
else
|
||||
{
|
||||
out += ')';
|
||||
}
|
||||
|
||||
// Print error code (may be irrelevant)
|
||||
#ifdef _WIN32
|
||||
if (DWORD error = GetLastError())
|
||||
{
|
||||
fmt::append(out, " (e=0x%08x[%u])", error, error);
|
||||
}
|
||||
#else
|
||||
if (int error = errno)
|
||||
{
|
||||
fmt::append(out, " (errno=%d)", error);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
void raw_error(const char* msg)
|
||||
{
|
||||
thread_ctrl::emergency_exit(msg);
|
||||
}
|
||||
|
||||
void raw_verify_error(const char* msg, const fmt_type_info* sup, u64 arg)
|
||||
[[noreturn]] void raw_verify_error(const src_loc& loc)
|
||||
{
|
||||
std::string out{"Verification failed"};
|
||||
|
||||
// Print error code (may be irrelevant)
|
||||
#ifdef _WIN32
|
||||
if (DWORD error = GetLastError())
|
||||
{
|
||||
fmt::append(out, " (e=%#x)", error);
|
||||
}
|
||||
#else
|
||||
if (int error = errno)
|
||||
{
|
||||
fmt::append(out, " (e=%d)", error);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sup)
|
||||
{
|
||||
out += " (";
|
||||
sup->fmt_string(out, arg); // Print value
|
||||
out += ")";
|
||||
}
|
||||
|
||||
if (msg)
|
||||
{
|
||||
out += ": ";
|
||||
out += msg;
|
||||
}
|
||||
|
||||
fmt::append(out, "%s", loc);
|
||||
thread_ctrl::emergency_exit(out);
|
||||
}
|
||||
|
||||
void raw_narrow_error(const char* msg, const fmt_type_info* sup, u64 arg)
|
||||
[[noreturn]] void raw_narrow_error(const src_loc& loc)
|
||||
{
|
||||
std::string out{"Narrow error"};
|
||||
|
||||
if (sup)
|
||||
{
|
||||
out += " (";
|
||||
sup->fmt_string(out, arg); // Print value
|
||||
out += ")";
|
||||
}
|
||||
|
||||
if (msg)
|
||||
{
|
||||
out += ": ";
|
||||
out += msg;
|
||||
}
|
||||
|
||||
std::string out{"Narrowing error"};
|
||||
fmt::append(out, "%s", loc);
|
||||
thread_ctrl::emergency_exit(out);
|
||||
}
|
||||
|
||||
void raw_throw_exception(const char* fmt, const fmt_type_info* sup, const u64* args)
|
||||
[[noreturn]] void raw_throw_exception(const src_loc& loc, const char* fmt, const fmt_type_info* sup, const u64* args)
|
||||
{
|
||||
std::string out;
|
||||
raw_append(out, fmt, sup, args);
|
||||
fmt::append(out, "%s", loc);
|
||||
thread_ctrl::emergency_exit(out);
|
||||
}
|
||||
|
||||
@@ -318,7 +331,7 @@ struct fmt::cfmt_src
|
||||
const fmt_type_info* sup;
|
||||
const u64* args;
|
||||
|
||||
bool test(std::size_t index) const
|
||||
bool test(usz index) const
|
||||
{
|
||||
if (!sup[index].fmt_string)
|
||||
{
|
||||
@@ -329,26 +342,26 @@ struct fmt::cfmt_src
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get(std::size_t index) const
|
||||
T get(usz index) const
|
||||
{
|
||||
return *reinterpret_cast<const T*>(reinterpret_cast<const u8*>(args + index));
|
||||
}
|
||||
|
||||
void skip(std::size_t extra)
|
||||
void skip(usz extra)
|
||||
{
|
||||
sup += extra + 1;
|
||||
args += extra + 1;
|
||||
}
|
||||
|
||||
std::size_t fmt_string(std::string& out, std::size_t extra) const
|
||||
usz fmt_string(std::string& out, usz extra) const
|
||||
{
|
||||
const std::size_t start = out.size();
|
||||
const usz start = out.size();
|
||||
sup[extra].fmt_string(out, args[extra]);
|
||||
return out.size() - start;
|
||||
}
|
||||
|
||||
// Returns type size (0 if unknown, pointer, unsigned, assumed max)
|
||||
std::size_t type(std::size_t extra) const
|
||||
usz type(usz extra) const
|
||||
{
|
||||
// Hack: use known function pointers to determine type
|
||||
#define TYPE(type) \
|
||||
@@ -366,14 +379,14 @@ struct fmt::cfmt_src
|
||||
return 0;
|
||||
}
|
||||
|
||||
static constexpr std::size_t size_char = 1;
|
||||
static constexpr std::size_t size_short = 2;
|
||||
static constexpr std::size_t size_int = 0;
|
||||
static constexpr std::size_t size_long = sizeof(ulong);
|
||||
static constexpr std::size_t size_llong = sizeof(ullong);
|
||||
static constexpr std::size_t size_size = sizeof(std::size_t);
|
||||
static constexpr std::size_t size_max = sizeof(std::uintmax_t);
|
||||
static constexpr std::size_t size_diff = sizeof(std::ptrdiff_t);
|
||||
static constexpr usz size_char = 1;
|
||||
static constexpr usz size_short = 2;
|
||||
static constexpr usz size_int = 0;
|
||||
static constexpr usz size_long = sizeof(ulong);
|
||||
static constexpr usz size_llong = sizeof(ullong);
|
||||
static constexpr usz size_size = sizeof(usz);
|
||||
static constexpr usz size_max = sizeof(std::uintmax_t);
|
||||
static constexpr usz size_diff = sizeof(std::ptrdiff_t);
|
||||
};
|
||||
|
||||
void fmt::raw_append(std::string& out, const char* fmt, const fmt_type_info* sup, const u64* args) noexcept
|
||||
@@ -405,32 +418,41 @@ std::string fmt::replace_all(const std::string& src, const std::string& from, co
|
||||
return target;
|
||||
}
|
||||
|
||||
std::vector<std::string> fmt::split(const std::string& source, std::initializer_list<std::string> separators, bool is_skip_empty)
|
||||
std::vector<std::string> fmt::split(std::string_view source, std::initializer_list<std::string_view> separators, bool is_skip_empty)
|
||||
{
|
||||
std::vector<std::string> result;
|
||||
|
||||
size_t cursor_begin = 0;
|
||||
|
||||
for (size_t cursor_end = 0; cursor_end < source.length(); ++cursor_end)
|
||||
for (usz index = 0; index < source.size();)
|
||||
{
|
||||
usz pos = -1;
|
||||
usz sep_size = 0;
|
||||
|
||||
for (auto& separator : separators)
|
||||
{
|
||||
if (strncmp(source.c_str() + cursor_end, separator.c_str(), separator.length()) == 0)
|
||||
if (usz pos0 = source.find(separator, index); pos0 != umax)
|
||||
{
|
||||
std::string candidate = source.substr(cursor_begin, cursor_end - cursor_begin);
|
||||
if (!is_skip_empty || !candidate.empty())
|
||||
result.push_back(candidate);
|
||||
|
||||
cursor_begin = cursor_end + separator.length();
|
||||
cursor_end = cursor_begin - 1;
|
||||
pos = pos0;
|
||||
sep_size = separator.size();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cursor_begin != source.length())
|
||||
{
|
||||
result.push_back(source.substr(cursor_begin));
|
||||
if (!sep_size)
|
||||
{
|
||||
result.emplace_back(&source[index], source.size() - index);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string_view piece = {&source[index], pos - index};
|
||||
|
||||
index = pos + sep_size;
|
||||
|
||||
if (piece.empty() && is_skip_empty)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.emplace_back(std::string(piece));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -438,7 +460,7 @@ std::vector<std::string> fmt::split(const std::string& source, std::initializer_
|
||||
|
||||
std::string fmt::trim(const std::string& source, const std::string& values)
|
||||
{
|
||||
std::size_t begin = source.find_first_not_of(values);
|
||||
usz begin = source.find_first_not_of(values);
|
||||
|
||||
if (begin == source.npos)
|
||||
return {};
|
||||
@@ -464,7 +486,7 @@ std::string fmt::to_lower(const std::string& string)
|
||||
|
||||
bool fmt::match(const std::string& source, const std::string& mask)
|
||||
{
|
||||
std::size_t source_position = 0, mask_position = 0;
|
||||
usz source_position = 0, mask_position = 0;
|
||||
|
||||
for (; source_position < source.size() && mask_position < mask.size(); ++mask_position, ++source_position)
|
||||
{
|
||||
@@ -473,7 +495,7 @@ bool fmt::match(const std::string& source, const std::string& mask)
|
||||
case '?': break;
|
||||
|
||||
case '*':
|
||||
for (std::size_t test_source_position = source_position; test_source_position < source.size(); ++test_source_position)
|
||||
for (usz test_source_position = source_position; test_source_position < source.size(); ++test_source_position)
|
||||
{
|
||||
if (match(source.substr(test_source_position), mask.substr(mask_position + 1)))
|
||||
{
|
||||
|
||||
+48
-42
@@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
#pragma once // No BOM and only basic ASCII in this header, or a neko will die
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
template <typename CharT, std::size_t N, typename... Args>
|
||||
template <typename CharT, usz N, typename... Args>
|
||||
static std::string format(const CharT(&)[N], const Args&...);
|
||||
}
|
||||
|
||||
template <typename T, typename>
|
||||
template <typename T, typename = void>
|
||||
struct fmt_unveil
|
||||
{
|
||||
static_assert(sizeof(T) > 0, "fmt_unveil<> error: incomplete type");
|
||||
@@ -19,7 +19,7 @@ struct fmt_unveil
|
||||
|
||||
static inline u64 get(const T& arg)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(&arg);
|
||||
return reinterpret_cast<uptr>(&arg);
|
||||
}
|
||||
|
||||
// Temporary value container (can possibly be created by other fmt_unveil<> specializations)
|
||||
@@ -30,7 +30,7 @@ struct fmt_unveil
|
||||
// Allow implicit conversion
|
||||
operator u64() const
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(&arg);
|
||||
return reinterpret_cast<uptr>(&arg);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -64,17 +64,6 @@ struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt_unveil<f16, void>
|
||||
{
|
||||
using type = f16;
|
||||
|
||||
static inline u64 get(const f16& arg)
|
||||
{
|
||||
return fmt_unveil<f64>::get(arg.operator float());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct fmt_unveil<T, std::enable_if_t<std::is_enum<T>::value>>
|
||||
{
|
||||
@@ -93,29 +82,29 @@ struct fmt_unveil<T*, void>
|
||||
|
||||
static inline u64 get(type arg)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(arg);
|
||||
return reinterpret_cast<uptr>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N>
|
||||
template <typename T, usz N>
|
||||
struct fmt_unveil<T[N], void>
|
||||
{
|
||||
using type = std::add_const_t<T>*;
|
||||
|
||||
static inline u64 get(type arg)
|
||||
{
|
||||
return reinterpret_cast<std::uintptr_t>(arg);
|
||||
return reinterpret_cast<uptr>(arg);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt_unveil<b8, void>
|
||||
template <typename T, bool Se, usz Align>
|
||||
struct fmt_unveil<se_t<T, Se, Align>, void>
|
||||
{
|
||||
using type = bool;
|
||||
using type = typename fmt_unveil<T>::type;
|
||||
|
||||
static inline u64 get(const b8& value)
|
||||
static inline auto get(const se_t<T, Se, Align>& arg)
|
||||
{
|
||||
return fmt_unveil<bool>::get(value);
|
||||
return fmt_unveil<T>::get(arg);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -130,16 +119,16 @@ struct fmt_class_string
|
||||
using type = T;
|
||||
|
||||
// Helper function (converts arg to object reference)
|
||||
static SAFE_BUFFERS FORCE_INLINE const T& get_object(u64 arg)
|
||||
static FORCE_INLINE SAFE_BUFFERS(const T&) get_object(u64 arg)
|
||||
{
|
||||
return *reinterpret_cast<const T*>(static_cast<std::uintptr_t>(arg));
|
||||
return *reinterpret_cast<const T*>(static_cast<uptr>(arg));
|
||||
}
|
||||
|
||||
// Enum -> string function type
|
||||
using convert_t = const char*(*)(T value);
|
||||
|
||||
// Helper function (safely converts arg to enum value)
|
||||
static SAFE_BUFFERS FORCE_INLINE void format_enum(std::string& out, u64 arg, convert_t convert)
|
||||
static FORCE_INLINE SAFE_BUFFERS(void) format_enum(std::string& out, u64 arg, convert_t convert)
|
||||
{
|
||||
const auto value = static_cast<std::underlying_type_t<T>>(arg);
|
||||
|
||||
@@ -158,7 +147,7 @@ struct fmt_class_string
|
||||
}
|
||||
|
||||
// Helper function (bitset formatting)
|
||||
static SAFE_BUFFERS FORCE_INLINE void format_bitset(std::string& out, u64 arg, const char* prefix, const char* delim, const char* suffix, void (*fmt)(std::string&, u64))
|
||||
static FORCE_INLINE SAFE_BUFFERS(void) format_bitset(std::string& out, u64 arg, const char* prefix, const char* delim, const char* suffix, void (*fmt)(std::string&, u64))
|
||||
{
|
||||
// Start from raw value
|
||||
fmt_class_string<u64>::format(out, arg);
|
||||
@@ -234,13 +223,16 @@ struct fmt_type_info
|
||||
template <typename... Args>
|
||||
using fmt_args_t = const u64(&&)[sizeof...(Args) + 1];
|
||||
|
||||
template <typename Arg>
|
||||
using fmt_unveil_t = typename fmt_unveil<Arg>::type;
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
// Base-57 format helper
|
||||
struct base57
|
||||
{
|
||||
const uchar* data;
|
||||
std::size_t size;
|
||||
usz size;
|
||||
|
||||
template <typename T>
|
||||
base57(const T& arg)
|
||||
@@ -249,7 +241,7 @@ namespace fmt
|
||||
{
|
||||
}
|
||||
|
||||
base57(const uchar* data, std::size_t size)
|
||||
base57(const uchar* data, usz size)
|
||||
: data(data)
|
||||
, size(size)
|
||||
{
|
||||
@@ -257,7 +249,7 @@ namespace fmt
|
||||
};
|
||||
|
||||
template <typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE const fmt_type_info* get_type_info()
|
||||
FORCE_INLINE SAFE_BUFFERS(const fmt_type_info*) get_type_info()
|
||||
{
|
||||
// Constantly initialized null-terminated list of type-specific information
|
||||
static constexpr fmt_type_info result[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
@@ -272,16 +264,16 @@ namespace fmt
|
||||
void raw_append(std::string& out, const char*, const fmt_type_info*, const u64*) noexcept;
|
||||
|
||||
// Formatting function
|
||||
template <typename CharT, std::size_t N, typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE void append(std::string& out, const CharT(&fmt)[N], const Args&... args)
|
||||
template <typename CharT, usz N, typename... Args>
|
||||
FORCE_INLINE SAFE_BUFFERS(void) append(std::string& out, const CharT(&fmt)[N], const Args&... args)
|
||||
{
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
raw_append(out, reinterpret_cast<const char*>(fmt), type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
|
||||
// Formatting function
|
||||
template <typename CharT, std::size_t N, typename... Args>
|
||||
SAFE_BUFFERS FORCE_INLINE std::string format(const CharT(&fmt)[N], const Args&... args)
|
||||
template <typename CharT, usz N, typename... Args>
|
||||
FORCE_INLINE SAFE_BUFFERS(std::string) format(const CharT(&fmt)[N], const Args&... args)
|
||||
{
|
||||
std::string result;
|
||||
append(result, fmt, args...);
|
||||
@@ -289,13 +281,27 @@ namespace fmt
|
||||
}
|
||||
|
||||
// Internal exception message formatting template, must be explicitly specialized or instantiated in cpp to minimize code bloat
|
||||
[[noreturn]] void raw_throw_exception(const char*, const fmt_type_info*, const u64*);
|
||||
[[noreturn]] void raw_throw_exception(const src_loc&, const char*, const fmt_type_info*, const u64*);
|
||||
|
||||
// Throw exception with formatting
|
||||
template <typename... Args>
|
||||
[[noreturn]] SAFE_BUFFERS FORCE_INLINE void throw_exception(const char* fmt, const Args&... args)
|
||||
template <typename CharT, usz N, typename... Args>
|
||||
struct throw_exception
|
||||
{
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
raw_throw_exception(fmt, type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
[[noreturn]] FORCE_INLINE SAFE_BUFFERS() throw_exception(const CharT(&fmt)[N], const Args&... args,
|
||||
u32 line = __builtin_LINE(),
|
||||
u32 col = __builtin_COLUMN(),
|
||||
const char* file = __builtin_FILE(),
|
||||
const char* func = __builtin_FUNCTION())
|
||||
{
|
||||
static constexpr fmt_type_info type_list[sizeof...(Args) + 1]{fmt_type_info::make<fmt_unveil_t<Args>>()...};
|
||||
raw_throw_exception({line, col, file, func}, reinterpret_cast<const char*>(fmt), type_list, fmt_args_t<Args...>{fmt_unveil<Args>::get(args)...});
|
||||
}
|
||||
|
||||
#ifndef _MSC_VER
|
||||
[[noreturn]] ~throw_exception();
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename CharT, usz N, typename... Args>
|
||||
throw_exception(const CharT(&)[N], const Args&...) -> throw_exception<CharT, N, Args...>;
|
||||
}
|
||||
|
||||
+10
-10
@@ -17,7 +17,7 @@ std::string utf8_path_to_ansi_path(const std::string& src);
|
||||
template <typename D, typename T>
|
||||
inline void strcpy_trunc(D& dst, const T& src)
|
||||
{
|
||||
const std::size_t count = std::size(src) >= std::size(dst) ? std::size(dst) - 1 : std::size(src);
|
||||
const usz count = std::size(src) >= std::size(dst) ? std::size(dst) - 1 : std::size(src);
|
||||
std::memcpy(std::data(dst), std::data(src), count);
|
||||
std::memset(std::data(dst) + count, 0, std::size(dst) - count);
|
||||
}
|
||||
@@ -27,14 +27,14 @@ namespace fmt
|
||||
std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
|
||||
std::string replace_all(const std::string& src, const std::string& from, const std::string& to);
|
||||
|
||||
template <size_t list_size>
|
||||
template <usz list_size>
|
||||
std::string replace_all(std::string src, const std::pair<std::string, std::string> (&list)[list_size])
|
||||
{
|
||||
for (size_t pos = 0; pos < src.length(); ++pos)
|
||||
for (usz pos = 0; pos < src.length(); ++pos)
|
||||
{
|
||||
for (size_t i = 0; i < list_size; ++i)
|
||||
for (usz i = 0; i < list_size; ++i)
|
||||
{
|
||||
const size_t comp_length = list[i].first.length();
|
||||
const usz comp_length = list[i].first.length();
|
||||
|
||||
if (src.length() - pos < comp_length)
|
||||
continue;
|
||||
@@ -51,14 +51,14 @@ namespace fmt
|
||||
return src;
|
||||
}
|
||||
|
||||
template <size_t list_size>
|
||||
template <usz list_size>
|
||||
std::string replace_all(std::string src, const std::pair<std::string, std::function<std::string()>> (&list)[list_size])
|
||||
{
|
||||
for (size_t pos = 0; pos < src.length(); ++pos)
|
||||
for (usz pos = 0; pos < src.length(); ++pos)
|
||||
{
|
||||
for (size_t i = 0; i < list_size; ++i)
|
||||
for (usz i = 0; i < list_size; ++i)
|
||||
{
|
||||
const size_t comp_length = list[i].first.length();
|
||||
const usz comp_length = list[i].first.length();
|
||||
|
||||
if (src.length() - pos < comp_length)
|
||||
continue;
|
||||
@@ -75,7 +75,7 @@ namespace fmt
|
||||
return src;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& source, std::initializer_list<std::string> separators, bool is_skip_empty = true);
|
||||
std::vector<std::string> split(std::string_view source, std::initializer_list<std::string_view> separators, bool is_skip_empty = true);
|
||||
std::string trim(const std::string& source, const std::string& values = " \t");
|
||||
|
||||
template <typename T>
|
||||
|
||||
+691
-253
File diff suppressed because it is too large
Load Diff
+435
-117
@@ -1,20 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include "util/shared_cptr.hpp"
|
||||
#include "util/shared_ptr.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
|
||||
#include "mutex.h"
|
||||
#include "cond.h"
|
||||
#include "lockless.h"
|
||||
|
||||
// Report error and call std::abort(), defined in main.cpp
|
||||
[[noreturn]] void report_fatal_error(const std::string&);
|
||||
|
||||
// Hardware core layout
|
||||
enum class native_core_arrangement : u32
|
||||
{
|
||||
@@ -34,10 +30,11 @@ enum class thread_class : u32
|
||||
|
||||
enum class thread_state : u32
|
||||
{
|
||||
created, // Initial state
|
||||
aborting, // The thread has been joined in the destructor or explicitly aborted
|
||||
errored, // Set after the emergency_exit call
|
||||
finished // Final state, always set at the end of thread execution
|
||||
created = 0, // Initial state
|
||||
aborting = 1, // The thread has been joined in the destructor or explicitly aborted
|
||||
errored = 2, // Set after the emergency_exit call
|
||||
finished = 3, // Final state, always set at the end of thread execution
|
||||
mask = 3
|
||||
};
|
||||
|
||||
class need_wakeup {};
|
||||
@@ -45,10 +42,22 @@ class need_wakeup {};
|
||||
template <class Context>
|
||||
class named_thread;
|
||||
|
||||
template <typename T>
|
||||
class thread_base;
|
||||
|
||||
template <typename Ctx, typename X = void, typename... Args>
|
||||
struct result_storage
|
||||
{
|
||||
static_assert(std::is_default_constructible_v<T> && noexcept(T()));
|
||||
static constexpr bool empty = true;
|
||||
|
||||
using type = void;
|
||||
};
|
||||
|
||||
template <typename Ctx, typename... Args>
|
||||
struct result_storage<Ctx, std::enable_if_t<!std::is_void_v<std::invoke_result_t<Ctx, Args&&...>>>, Args...>
|
||||
{
|
||||
using T = std::invoke_result_t<Ctx, Args&&...>;
|
||||
|
||||
static_assert(std::is_default_constructible_v<T>);
|
||||
|
||||
alignas(T) std::byte data[sizeof(T)];
|
||||
|
||||
@@ -56,33 +65,53 @@ struct result_storage
|
||||
|
||||
using type = T;
|
||||
|
||||
T* get()
|
||||
T* _get()
|
||||
{
|
||||
return reinterpret_cast<T*>(&data);
|
||||
}
|
||||
|
||||
const T* get() const
|
||||
const T* _get() const
|
||||
{
|
||||
return reinterpret_cast<const T*>(&data);
|
||||
}
|
||||
|
||||
void init() noexcept
|
||||
{
|
||||
new (data) T();
|
||||
}
|
||||
|
||||
void destroy() noexcept
|
||||
{
|
||||
get()->~T();
|
||||
_get()->~T();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct result_storage<void>
|
||||
// Base class for task queue (linked list)
|
||||
class thread_future
|
||||
{
|
||||
static constexpr bool empty = true;
|
||||
friend class thread_base;
|
||||
|
||||
using type = void;
|
||||
shared_ptr<thread_future> next{};
|
||||
|
||||
shared_ptr<thread_future>* prev{};
|
||||
|
||||
protected:
|
||||
atomic_t<void(*)(thread_base*, thread_future*)> exec{};
|
||||
|
||||
public:
|
||||
// Get reference to the atomic variable for inspection and waiting for
|
||||
const auto& get_wait() const
|
||||
{
|
||||
return exec;
|
||||
}
|
||||
|
||||
// Wait (preset)
|
||||
void wait() const
|
||||
{
|
||||
exec.wait<atomic_wait::op_ne>(nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Context, typename... Args>
|
||||
using result_storage_t = result_storage<std::invoke_result_t<Context, Args...>>;
|
||||
|
||||
template <typename T, typename = void>
|
||||
struct thread_thread_name : std::bool_constant<false> {};
|
||||
|
||||
@@ -92,6 +121,7 @@ struct thread_thread_name<T, std::void_t<decltype(named_thread<T>::thread_name)>
|
||||
// Thread base class
|
||||
class thread_base
|
||||
{
|
||||
public:
|
||||
// Native thread entry point function type
|
||||
#ifdef _WIN32
|
||||
using native_entry = uint(__stdcall*)(void* arg);
|
||||
@@ -99,38 +129,38 @@ class thread_base
|
||||
using native_entry = void*(*)(void* arg);
|
||||
#endif
|
||||
|
||||
const native_entry entry_point;
|
||||
|
||||
private:
|
||||
// Thread handle (platform-specific)
|
||||
atomic_t<std::uintptr_t> m_thread{0};
|
||||
atomic_t<u64> m_thread{0};
|
||||
|
||||
// Thread playtoy, that shouldn't be used
|
||||
atomic_t<u32> m_signal{0};
|
||||
|
||||
// Thread state
|
||||
atomic_t<thread_state> m_state = thread_state::created;
|
||||
|
||||
// Thread state notification info
|
||||
atomic_t<const void*> m_state_notifier{nullptr};
|
||||
// Thread state and cycles
|
||||
atomic_t<u64> m_sync{0};
|
||||
|
||||
// Thread name
|
||||
stx::atomic_cptr<std::string> m_tname;
|
||||
atomic_ptr<std::string> m_tname;
|
||||
|
||||
//
|
||||
atomic_t<u64> m_cycles = 0;
|
||||
// Thread task queue (reversed linked list)
|
||||
atomic_ptr<thread_future> m_taskq{};
|
||||
|
||||
// Start thread
|
||||
void start(native_entry);
|
||||
void start();
|
||||
|
||||
// Called at the thread start
|
||||
void initialize(void (*error_cb)(), bool(*wait_cb)(const void*));
|
||||
void initialize(void (*error_cb)());
|
||||
|
||||
// May be called in destructor
|
||||
void notify_abort() noexcept;
|
||||
|
||||
// Called at the thread end, returns true if needs destruction
|
||||
bool finalize(thread_state result) noexcept;
|
||||
// Called at the thread end, returns self handle
|
||||
u64 finalize(thread_state result) noexcept;
|
||||
|
||||
// Cleanup after possibly deleting the thread instance
|
||||
static void finalize() noexcept;
|
||||
static native_entry finalize(u64 _self) noexcept;
|
||||
|
||||
// Set name for debugger
|
||||
static void set_name(std::string);
|
||||
|
||||
// Make entry point
|
||||
static native_entry make_trampoline(u64(*)(thread_base*));
|
||||
|
||||
friend class thread_ctrl;
|
||||
|
||||
@@ -138,7 +168,7 @@ class thread_base
|
||||
friend class named_thread;
|
||||
|
||||
protected:
|
||||
thread_base(std::string_view name);
|
||||
thread_base(native_entry, std::string_view name);
|
||||
|
||||
~thread_base();
|
||||
|
||||
@@ -147,10 +177,20 @@ public:
|
||||
u64 get_cycles();
|
||||
|
||||
// Wait for the thread (it does NOT change thread state, and can be called from multiple threads)
|
||||
bool join() const;
|
||||
bool join(bool dtor = false) const;
|
||||
|
||||
// Notify the thread
|
||||
void notify();
|
||||
|
||||
// Get thread id
|
||||
u64 get_native_id() const;
|
||||
|
||||
// Add work to the queue
|
||||
void push(shared_ptr<thread_future>);
|
||||
|
||||
private:
|
||||
// Clear task queue (execute unless aborting)
|
||||
void exec();
|
||||
};
|
||||
|
||||
// Collection of global function for current thread
|
||||
@@ -190,14 +230,14 @@ public:
|
||||
// Set current thread name (not recommended)
|
||||
static void set_name(std::string_view name)
|
||||
{
|
||||
g_tls_this_thread->m_tname.store(stx::shared_cptr<std::string>::make(name));
|
||||
g_tls_this_thread->m_tname.store(make_single<std::string>(name));
|
||||
}
|
||||
|
||||
// Set thread name (not recommended)
|
||||
template <typename T>
|
||||
static void set_name(named_thread<T>& thread, std::string_view name)
|
||||
{
|
||||
static_cast<thread_base&>(thread).m_tname.store(stx::shared_cptr<std::string>::make(name));
|
||||
static_cast<thread_base&>(thread).m_tname.store(make_single<std::string>(name));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -213,16 +253,13 @@ public:
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static void raw_notify(named_thread<T>& thread)
|
||||
static u64 get_native_id(named_thread<T>& thread)
|
||||
{
|
||||
static_cast<thread_base&>(thread).notify_abort();
|
||||
return static_cast<thread_base&>(thread).get_native_id();
|
||||
}
|
||||
|
||||
// Read current state
|
||||
static inline thread_state state()
|
||||
{
|
||||
return g_tls_this_thread->m_state;
|
||||
}
|
||||
// Read current state, possibly executing some tasks
|
||||
static thread_state state();
|
||||
|
||||
// Wait once with timeout. May spuriously return false.
|
||||
static inline void wait_for(u64 usec, bool alert = true)
|
||||
@@ -236,6 +273,24 @@ public:
|
||||
_wait_for(-1, true);
|
||||
}
|
||||
|
||||
// Wait for both thread sync var and provided atomic var
|
||||
template <atomic_wait::op Op = atomic_wait::op::eq, typename T, typename U>
|
||||
static inline void wait_on(T& wait, U old, u64 usec = -1)
|
||||
{
|
||||
auto _this = g_tls_this_thread;
|
||||
|
||||
if (_this->m_sync.bit_test_reset(2) || _this->m_taskq)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_wait::list<3> list{};
|
||||
list.set<0, Op>(wait, old);
|
||||
list.set<1>(_this->m_sync, 0, 4 + 1);
|
||||
list.set<2>(_this->m_taskq, nullptr);
|
||||
list.wait(atomic_wait_timeout{usec <= 0xffff'ffff'ffff'ffff / 1000 ? usec * 1000 : 0xffff'ffff'ffff'ffff});
|
||||
}
|
||||
|
||||
// Exit.
|
||||
[[noreturn]] static void emergency_exit(std::string_view reason);
|
||||
|
||||
@@ -263,6 +318,27 @@ public:
|
||||
// Miscellaneous
|
||||
static u64 get_thread_affinity_mask();
|
||||
|
||||
// Get current thread stack addr and size
|
||||
static std::pair<void*, usz> get_thread_stack();
|
||||
|
||||
// Sets the native thread priority and returns it to zero at destructor
|
||||
struct scoped_priority
|
||||
{
|
||||
explicit scoped_priority(int prio)
|
||||
{
|
||||
set_native_priority(prio);
|
||||
}
|
||||
|
||||
scoped_priority(const scoped_priority&) = delete;
|
||||
|
||||
scoped_priority& operator=(const scoped_priority&) = delete;
|
||||
|
||||
~scoped_priority()
|
||||
{
|
||||
set_native_priority(0);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// Miscellaneous
|
||||
static const u64 process_affinity_mask;
|
||||
@@ -270,82 +346,54 @@ private:
|
||||
|
||||
// Derived from the callable object Context, possibly a lambda
|
||||
template <class Context>
|
||||
class named_thread final : public Context, result_storage_t<Context>, thread_base
|
||||
class named_thread final : public Context, result_storage<Context>, thread_base
|
||||
{
|
||||
using result = result_storage_t<Context>;
|
||||
using result = result_storage<Context>;
|
||||
using thread = thread_base;
|
||||
|
||||
// Type-erased thread entry point
|
||||
#ifdef _WIN32
|
||||
static inline uint __stdcall entry_point(void* arg)
|
||||
#else
|
||||
static inline void* entry_point(void* arg)
|
||||
#endif
|
||||
static u64 entry_point(thread_base* _base)
|
||||
{
|
||||
const auto _this = static_cast<named_thread*>(static_cast<thread*>(arg));
|
||||
|
||||
// Perform self-cleanup if necessary
|
||||
if (_this->entry_point())
|
||||
{
|
||||
delete _this;
|
||||
}
|
||||
|
||||
thread::finalize();
|
||||
return 0;
|
||||
return static_cast<named_thread*>(_base)->entry_point2();
|
||||
}
|
||||
|
||||
bool entry_point()
|
||||
u64 entry_point2()
|
||||
{
|
||||
auto tls_error_cb = []()
|
||||
thread::initialize([]()
|
||||
{
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
// Construct using default constructor in the case of failure
|
||||
new (static_cast<result*>(static_cast<named_thread*>(thread_ctrl::get_current()))->get()) typename result::type();
|
||||
static_cast<result*>(static_cast<named_thread*>(thread_ctrl::get_current()))->init();
|
||||
}
|
||||
};
|
||||
|
||||
thread::initialize(tls_error_cb, [](const void* data)
|
||||
{
|
||||
const auto _this = thread_ctrl::get_current();
|
||||
|
||||
if (_this->m_state >= thread_state::aborting)
|
||||
{
|
||||
_this->m_state_notifier.store(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!data)
|
||||
{
|
||||
_this->m_state_notifier.release(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
_this->m_state_notifier.store(data);
|
||||
|
||||
if (_this->m_state >= thread_state::aborting)
|
||||
{
|
||||
_this->m_state_notifier.release(nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if constexpr (result::empty)
|
||||
{
|
||||
// No result
|
||||
Context::operator()();
|
||||
if constexpr (std::is_invocable_v<Context>)
|
||||
{
|
||||
Context::operator()();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default event loop
|
||||
while (thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
thread_ctrl::wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Construct the result using placement new (copy elision should happen)
|
||||
new (result::get()) typename result::type(Context::operator()());
|
||||
new (result::_get()) decltype(auto)(Context::operator()());
|
||||
}
|
||||
|
||||
return thread::finalize(thread_state::finished);
|
||||
}
|
||||
|
||||
static inline thread::native_entry trampoline = thread::make_trampoline(entry_point);
|
||||
|
||||
friend class thread_ctrl;
|
||||
|
||||
public:
|
||||
@@ -353,26 +401,26 @@ public:
|
||||
template <bool Valid = std::is_default_constructible_v<Context> && thread_thread_name<Context>(), typename = std::enable_if_t<Valid>>
|
||||
named_thread()
|
||||
: Context()
|
||||
, thread(Context::thread_name)
|
||||
, thread(trampoline, Context::thread_name)
|
||||
{
|
||||
thread::start(&named_thread::entry_point);
|
||||
thread::start();
|
||||
}
|
||||
|
||||
// Normal forwarding constructor
|
||||
template <typename... Args, typename = std::enable_if_t<std::is_constructible_v<Context, Args&&...>>>
|
||||
named_thread(std::string_view name, Args&&... args)
|
||||
: Context(std::forward<Args>(args)...)
|
||||
, thread(name)
|
||||
, thread(trampoline, name)
|
||||
{
|
||||
thread::start(&named_thread::entry_point);
|
||||
thread::start();
|
||||
}
|
||||
|
||||
// Lambda constructor, also the implicit deduction guide candidate
|
||||
named_thread(std::string_view name, Context&& f)
|
||||
: Context(std::forward<Context>(f))
|
||||
, thread(name)
|
||||
, thread(trampoline, name)
|
||||
{
|
||||
thread::start(&named_thread::entry_point);
|
||||
thread::start();
|
||||
}
|
||||
|
||||
named_thread(const named_thread&) = delete;
|
||||
@@ -386,7 +434,7 @@ public:
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
return *result::get();
|
||||
return *result::_get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -397,26 +445,296 @@ public:
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
return *result::get();
|
||||
return *result::_get();
|
||||
}
|
||||
}
|
||||
|
||||
// Send command to the thread to invoke directly (references should be passed via std::ref())
|
||||
template <bool Discard = true, typename Arg, typename... Args>
|
||||
auto operator()(Arg&& arg, Args&&... args)
|
||||
{
|
||||
// Overloaded operator() of the Context.
|
||||
constexpr bool v1 = std::is_invocable_v<Context, Arg&&, Args&&...>;
|
||||
|
||||
// Anything invocable, not necessarily involving the Context.
|
||||
constexpr bool v2 = std::is_invocable_v<Arg&&, Args&&...>;
|
||||
|
||||
// Could be pointer to a non-static member function (or data member) of the Context.
|
||||
constexpr bool v3 = std::is_member_pointer_v<std::decay_t<Arg>> && std::is_invocable_v<Arg, Context&, Args&&...>;
|
||||
|
||||
// Only one invocation type shall be valid, otherwise we don't know.
|
||||
static_assert((v1 + v2 + v3) == 1, "Ambiguous or invalid named_thread call.");
|
||||
|
||||
if constexpr (v1)
|
||||
{
|
||||
class future : public thread_future, result_storage<Context, void, Arg, Args...>
|
||||
{
|
||||
// A tuple to store arguments
|
||||
decltype(std::make_tuple(std::forward<Arg, Args...>(arg, args...))) m_args;
|
||||
|
||||
public:
|
||||
future(Arg&& arg, Args&&... args)
|
||||
: m_args(std::forward<Arg, Args...>(arg, args...))
|
||||
{
|
||||
thread_future::exec.raw() = +[](thread_base* tb, thread_future* tf)
|
||||
{
|
||||
const auto _this = static_cast<future*>(tf);
|
||||
|
||||
if (!tb) [[unlikely]]
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
_this->init();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (future::empty || Discard)
|
||||
{
|
||||
std::apply(*static_cast<Context*>(static_cast<named_thread*>(tb)), _this->m_args);
|
||||
}
|
||||
else
|
||||
{
|
||||
new (_this->_get()) decltype(auto)(std::apply(*static_cast<Context*>(static_cast<named_thread*>(tb)), _this->m_args));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
future(const future&) = delete;
|
||||
|
||||
future& operator=(const future&) = delete;
|
||||
|
||||
~future()
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
// Should be set to null if executed
|
||||
if (!this->exec)
|
||||
{
|
||||
this->destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decltype(auto) get()
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
return *this->_get();
|
||||
}
|
||||
}
|
||||
|
||||
decltype(auto) get() const
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
return *this->_get();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
single_ptr<future> target = make_single<future>(std::forward<Arg, Args...>(arg, args...));
|
||||
|
||||
if constexpr (!Discard)
|
||||
{
|
||||
shared_ptr<future> result = std::move(target);
|
||||
|
||||
// Copy result
|
||||
thread::push(result);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Move target
|
||||
thread::push(std::move(target));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if constexpr (v2)
|
||||
{
|
||||
class future : public thread_future, result_storage<std::decay_t<Arg>, void, Args...>
|
||||
{
|
||||
decltype(std::make_tuple(std::forward<Args...>(args...))) m_args;
|
||||
|
||||
std::decay_t<Arg> m_func;
|
||||
|
||||
public:
|
||||
future(Arg func, Args&&... args)
|
||||
: m_args(std::forward<Args...>(args...))
|
||||
, m_func(func)
|
||||
{
|
||||
thread_future::exec.raw() = +[](thread_base* tb, thread_future* tf)
|
||||
{
|
||||
const auto _this = static_cast<future*>(tf);
|
||||
|
||||
if (!tb) [[unlikely]]
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
_this->init();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (future::empty || Discard)
|
||||
{
|
||||
std::apply(_this->m_func, _this->m_args);
|
||||
}
|
||||
else
|
||||
{
|
||||
new (_this->_get()) decltype(auto)(std::apply(_this->m_func, _this->m_args));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
future(const future&) = delete;
|
||||
|
||||
future& operator=(const future&) = delete;
|
||||
|
||||
~future()
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
if (!this->exec)
|
||||
{
|
||||
this->destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decltype(auto) get()
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
return *this->_get();
|
||||
}
|
||||
}
|
||||
|
||||
decltype(auto) get() const
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
return *this->_get();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
single_ptr<future> target = make_single<future>(std::forward<Arg, Args...>(arg, args...));
|
||||
|
||||
if constexpr (!Discard)
|
||||
{
|
||||
shared_ptr<future> result = std::move(target);
|
||||
thread::push(result);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
thread::push(std::move(target));
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if constexpr (v3)
|
||||
{
|
||||
class future : public thread_future, result_storage<std::decay_t<Arg>, void, Context&, Args...>
|
||||
{
|
||||
decltype(std::make_tuple(std::forward<Args...>(args...))) m_args;
|
||||
|
||||
std::decay_t<Arg> m_func;
|
||||
|
||||
public:
|
||||
future(Arg func, Args&&... args)
|
||||
: m_args(std::forward<Args...>(args...))
|
||||
, m_func(func)
|
||||
{
|
||||
thread_future::exec.raw() = +[](thread_base* tb, thread_future* tf)
|
||||
{
|
||||
const auto _this = static_cast<future*>(tf);
|
||||
|
||||
if (!tb) [[unlikely]]
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
_this->init();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (future::empty || Discard)
|
||||
{
|
||||
std::apply(_this->m_func, *static_cast<Context*>(static_cast<named_thread*>(tb)), _this->m_args);
|
||||
}
|
||||
else
|
||||
{
|
||||
new (_this->_get()) decltype(auto)(std::apply(_this->m_func, *static_cast<Context*>(static_cast<named_thread*>(tb)), _this->m_args));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
future(const future&) = delete;
|
||||
|
||||
future& operator=(const future&) = delete;
|
||||
|
||||
~future()
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
if (!this->exec)
|
||||
{
|
||||
this->destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decltype(auto) get()
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
return *this->_get();
|
||||
}
|
||||
}
|
||||
|
||||
decltype(auto) get() const
|
||||
{
|
||||
if constexpr (!future::empty && !Discard)
|
||||
{
|
||||
return *this->_get();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
single_ptr<future> target = make_single<future>(std::forward<Arg, Args...>(arg, args...));
|
||||
|
||||
if constexpr (!Discard)
|
||||
{
|
||||
shared_ptr<future> result = std::move(target);
|
||||
thread::push(result);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
thread::push(std::move(target));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Access thread state
|
||||
operator thread_state() const
|
||||
{
|
||||
return thread::m_state.load();
|
||||
return static_cast<thread_state>(thread::m_sync.load() & 3);
|
||||
}
|
||||
|
||||
// Try to abort by assigning thread_state::aborting (UB if assigning different state)
|
||||
named_thread& operator=(thread_state s)
|
||||
{
|
||||
ASSUME(s == thread_state::aborting);
|
||||
|
||||
if (s == thread_state::aborting && thread::m_state.compare_and_swap_test(thread_state::created, s))
|
||||
if (s == thread_state::aborting && thread::m_sync.fetch_op([](u64& v){ return !(v & 3) && (v |= 1); }).second)
|
||||
{
|
||||
if (s == thread_state::aborting)
|
||||
{
|
||||
thread::notify_abort();
|
||||
thread::m_sync.notify_one(1);
|
||||
}
|
||||
|
||||
if constexpr (std::is_base_of_v<need_wakeup, Context>)
|
||||
@@ -433,7 +751,7 @@ public:
|
||||
{
|
||||
// Assign aborting state forcefully
|
||||
operator=(thread_state::aborting);
|
||||
thread::join();
|
||||
thread::join(true);
|
||||
|
||||
if constexpr (!result::empty)
|
||||
{
|
||||
|
||||
+9
-9
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
@@ -8,8 +8,8 @@ class Timer
|
||||
{
|
||||
private:
|
||||
bool m_stopped;
|
||||
std::chrono::steady_clock::time_point m_start;
|
||||
std::chrono::steady_clock::time_point m_end;
|
||||
steady_clock::time_point m_start;
|
||||
steady_clock::time_point m_end;
|
||||
|
||||
public:
|
||||
Timer() : m_stopped(false)
|
||||
@@ -19,13 +19,13 @@ public:
|
||||
void Start()
|
||||
{
|
||||
m_stopped = false;
|
||||
m_start = std::chrono::steady_clock::now();
|
||||
m_start = steady_clock::now();
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
m_stopped = true;
|
||||
m_end = std::chrono::steady_clock::now();
|
||||
m_end = steady_clock::now();
|
||||
}
|
||||
|
||||
double GetElapsedTimeInSec() const
|
||||
@@ -40,21 +40,21 @@ public:
|
||||
|
||||
u64 GetElapsedTimeInMicroSec() const
|
||||
{
|
||||
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
|
||||
steady_clock::time_point now = m_stopped ? m_end : steady_clock::now();
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::microseconds>(now - m_start).count();
|
||||
}
|
||||
|
||||
u64 GetElapsedTimeInNanoSec() const
|
||||
{
|
||||
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
|
||||
steady_clock::time_point now = m_stopped ? m_end : steady_clock::now();
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(now - m_start).count();
|
||||
}
|
||||
|
||||
u64 GetMsSince(std::chrono::steady_clock::time_point timestamp)
|
||||
u64 GetMsSince(steady_clock::time_point timestamp)
|
||||
{
|
||||
std::chrono::steady_clock::time_point now = m_stopped ? m_end : std::chrono::steady_clock::now();
|
||||
steady_clock::time_point now = m_stopped ? m_end : steady_clock::now();
|
||||
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(now - timestamp).count();
|
||||
}
|
||||
|
||||
@@ -1,312 +0,0 @@
|
||||
#include "stdafx.h"
|
||||
#include "util/logs.hpp"
|
||||
#include "VirtualMemory.h"
|
||||
#ifdef _WIN32
|
||||
#include <Windows.h>
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#ifdef __NR_memfd_create
|
||||
#elif __x86_64__
|
||||
#define __NR_memfd_create 319
|
||||
#elif __aarch64__
|
||||
#define __NR_memfd_create 279
|
||||
#endif
|
||||
|
||||
static int memfd_create_(const char *name, uint flags)
|
||||
{
|
||||
return syscall(__NR_memfd_create, name, flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace utils
|
||||
{
|
||||
// Convert memory protection (internal)
|
||||
static auto operator +(protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD _prot = PAGE_NOACCESS;
|
||||
switch (prot)
|
||||
{
|
||||
case protection::rw: _prot = PAGE_READWRITE; break;
|
||||
case protection::ro: _prot = PAGE_READONLY; break;
|
||||
case protection::no: break;
|
||||
case protection::wx: _prot = PAGE_EXECUTE_READWRITE; break;
|
||||
case protection::rx: _prot = PAGE_EXECUTE_READ; break;
|
||||
}
|
||||
#else
|
||||
int _prot = PROT_NONE;
|
||||
switch (prot)
|
||||
{
|
||||
case protection::rw: _prot = PROT_READ | PROT_WRITE; break;
|
||||
case protection::ro: _prot = PROT_READ; break;
|
||||
case protection::no: break;
|
||||
case protection::wx: _prot = PROT_READ | PROT_WRITE | PROT_EXEC; break;
|
||||
case protection::rx: _prot = PROT_READ | PROT_EXEC; break;
|
||||
}
|
||||
#endif
|
||||
|
||||
return _prot;
|
||||
}
|
||||
|
||||
void* memory_reserve(std::size_t size, void* use_addr)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return ::VirtualAlloc(use_addr, size, MEM_RESERVE, PAGE_NOACCESS);
|
||||
#else
|
||||
if (use_addr && reinterpret_cast<uptr>(use_addr) % 0x10000)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!use_addr)
|
||||
{
|
||||
// Hack: Ensure aligned 64k allocations
|
||||
size += 0x10000;
|
||||
}
|
||||
|
||||
auto ptr = ::mmap(use_addr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||
|
||||
if (ptr == reinterpret_cast<void*>(-1))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (use_addr && ptr != use_addr)
|
||||
{
|
||||
::munmap(ptr, size);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!use_addr && ptr)
|
||||
{
|
||||
// Continuation of the hack above
|
||||
const auto misalign = reinterpret_cast<uptr>(ptr) % 0x10000;
|
||||
::munmap(ptr, 0x10000 - misalign);
|
||||
|
||||
if (misalign)
|
||||
{
|
||||
::munmap(static_cast<u8*>(ptr) + size - misalign, misalign);
|
||||
}
|
||||
|
||||
ptr = static_cast<u8*>(ptr) + (0x10000 - misalign);
|
||||
}
|
||||
|
||||
return ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_commit(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualAlloc(pointer, size, MEM_COMMIT, +prot);
|
||||
#else
|
||||
const u64 ptr64 = reinterpret_cast<u64>(pointer);
|
||||
verify(HERE), ::mprotect(reinterpret_cast<void*>(ptr64 & -4096), size + (ptr64 & 4095), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_decommit(void* pointer, std::size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualFree(pointer, size, MEM_DECOMMIT);
|
||||
#else
|
||||
verify(HERE), ::mmap(pointer, size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0) != reinterpret_cast<void*>(-1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_reset(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
memory_decommit(pointer, size);
|
||||
memory_commit(pointer, size, prot);
|
||||
#else
|
||||
verify(HERE), ::mmap(pointer, size, +prot, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0) != reinterpret_cast<void*>(-1);
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_release(void* pointer, std::size_t size)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
verify(HERE), ::VirtualFree(pointer, 0, MEM_RELEASE);
|
||||
#else
|
||||
verify(HERE), ::munmap(pointer, size) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
void memory_protect(void* pointer, std::size_t size, protection prot)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
for (u64 addr = reinterpret_cast<u64>(pointer), end = addr + size; addr < end;)
|
||||
{
|
||||
const u64 boundary = (addr + 0x10000) & -0x10000;
|
||||
const u64 block_size = std::min(boundary, end) - addr;
|
||||
|
||||
DWORD old;
|
||||
if (!::VirtualProtect(reinterpret_cast<LPVOID>(addr), block_size, +prot, &old))
|
||||
{
|
||||
fmt::throw_exception("VirtualProtect failed (%p, 0x%x, addr=0x%x, error=%#x)", pointer, size, addr, GetLastError());
|
||||
}
|
||||
|
||||
// Next region
|
||||
addr += block_size;
|
||||
}
|
||||
#else
|
||||
const u64 ptr64 = reinterpret_cast<u64>(pointer);
|
||||
verify(HERE), ::mprotect(reinterpret_cast<void*>(ptr64 & -4096), size + (ptr64 & 4095), +prot) != -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
shm::shm(u32 size, u32 flags)
|
||||
: m_size(::align(size, 0x10000))
|
||||
, m_flags(flags)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
m_handle = ::CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, PAGE_EXECUTE_READWRITE, 0, m_size, NULL);
|
||||
verify(HERE), m_handle != INVALID_HANDLE_VALUE;
|
||||
#elif __linux__
|
||||
m_file = ::memfd_create_("", 0);
|
||||
verify(HERE), m_file >= 0;
|
||||
verify(HERE), ::ftruncate(m_file, m_size) >= 0;
|
||||
#else
|
||||
while ((m_file = ::shm_open("/rpcs3-mem1", O_RDWR | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) == -1)
|
||||
{
|
||||
if (errno == EMFILE)
|
||||
{
|
||||
fmt::throw_exception("Too many open files. Raise the limit and try again.");
|
||||
}
|
||||
|
||||
verify(HERE), errno == EEXIST;
|
||||
}
|
||||
|
||||
verify(HERE), ::shm_unlink("/rpcs3-mem1") >= 0;
|
||||
verify(HERE), ::ftruncate(m_file, m_size) >= 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
shm::~shm()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::CloseHandle(m_handle);
|
||||
#else
|
||||
::close(m_file);
|
||||
#endif
|
||||
}
|
||||
|
||||
u8* shm::map(void* ptr, protection prot) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
DWORD access = FILE_MAP_WRITE;
|
||||
switch (prot)
|
||||
{
|
||||
case protection::rw:
|
||||
case protection::ro:
|
||||
case protection::no:
|
||||
break;
|
||||
case protection::wx:
|
||||
case protection::rx:
|
||||
access |= FILE_MAP_EXECUTE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (auto ret = static_cast<u8*>(::MapViewOfFileEx(m_handle, access, 0, 0, m_size, ptr)))
|
||||
{
|
||||
if (prot != protection::rw && prot != protection::wx)
|
||||
{
|
||||
DWORD old;
|
||||
if (!::VirtualProtect(ret, m_size, +prot, &old))
|
||||
{
|
||||
::UnmapViewOfFile(ret);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
#else
|
||||
const u64 ptr64 = reinterpret_cast<u64>(ptr);
|
||||
return static_cast<u8*>(::mmap(reinterpret_cast<void*>(ptr64 & -0x10000), m_size, +prot, MAP_SHARED | (ptr ? MAP_FIXED : 0), m_file, 0));
|
||||
#endif
|
||||
}
|
||||
|
||||
u8* shm::map_critical(void* ptr, protection prot)
|
||||
{
|
||||
const auto target = reinterpret_cast<u8*>(reinterpret_cast<u64>(ptr) & -0x10000);
|
||||
|
||||
#ifdef _WIN32
|
||||
::MEMORY_BASIC_INFORMATION mem;
|
||||
if (!::VirtualQuery(target, &mem, sizeof(mem)) || mem.State != MEM_RESERVE || !::VirtualFree(mem.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto base = (u8*)mem.AllocationBase;
|
||||
const auto size = mem.RegionSize + (target - base);
|
||||
|
||||
if (base < target && !::VirtualAlloc(base, target - base, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (target + m_size < base + size && !::VirtualAlloc(target + m_size, base + size - target - m_size, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
return this->map(target, prot);
|
||||
}
|
||||
|
||||
void shm::unmap(void* ptr) const
|
||||
{
|
||||
#ifdef _WIN32
|
||||
::UnmapViewOfFile(ptr);
|
||||
#else
|
||||
::munmap(ptr, m_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
void shm::unmap_critical(void* ptr)
|
||||
{
|
||||
const auto target = reinterpret_cast<u8*>(reinterpret_cast<u64>(ptr) & -0x10000);
|
||||
|
||||
this->unmap(target);
|
||||
|
||||
#ifdef _WIN32
|
||||
::MEMORY_BASIC_INFORMATION mem, mem2;
|
||||
if (!::VirtualQuery(target - 1, &mem, sizeof(mem)) || !::VirtualQuery(target + m_size, &mem2, sizeof(mem2)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem.State == MEM_RESERVE && !::VirtualFree(mem.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem2.State == MEM_RESERVE && !::VirtualFree(mem2.AllocationBase, 0, MEM_RELEASE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto size1 = mem.State == MEM_RESERVE ? target - (u8*)mem.AllocationBase : 0;
|
||||
const auto size2 = mem2.State == MEM_RESERVE ? mem2.RegionSize : 0;
|
||||
|
||||
if (!::VirtualAlloc(mem.State == MEM_RESERVE ? mem.AllocationBase : target, m_size + size1 + size2, MEM_RESERVE, PAGE_NOACCESS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
+11
-11
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "StrFmt.h"
|
||||
#include <vector>
|
||||
|
||||
@@ -92,7 +92,7 @@ namespace utils
|
||||
void set_length(const u32 new_length)
|
||||
{
|
||||
end = start + new_length - 1;
|
||||
ASSERT(valid());
|
||||
ensure(valid());
|
||||
}
|
||||
|
||||
u32 next_address() const
|
||||
@@ -282,7 +282,7 @@ namespace utils
|
||||
|
||||
public:
|
||||
// Wrapped functions
|
||||
inline void reserve(size_t nr) { data.reserve(nr); }
|
||||
inline void reserve(usz nr) { data.reserve(nr); }
|
||||
inline void clear() { data.clear(); }
|
||||
inline size_type size() const { return data.size(); }
|
||||
inline bool empty() const { return data.empty(); }
|
||||
@@ -456,9 +456,9 @@ namespace utils
|
||||
// Will fail if ranges within the vector overlap our touch each-other
|
||||
bool check_consistency() const
|
||||
{
|
||||
size_t _size = data.size();
|
||||
usz _size = data.size();
|
||||
|
||||
for (size_t i = 0; i < _size; ++i)
|
||||
for (usz i = 0; i < _size; ++i)
|
||||
{
|
||||
const auto &r1 = data[i];
|
||||
if (!r1.valid())
|
||||
@@ -466,7 +466,7 @@ namespace utils
|
||||
continue;
|
||||
}
|
||||
|
||||
for (size_t j = i + 1; j < _size; ++j)
|
||||
for (usz j = i + 1; j < _size; ++j)
|
||||
{
|
||||
const auto &r2 = data[j];
|
||||
if (!r2.valid())
|
||||
@@ -582,15 +582,15 @@ namespace utils
|
||||
|
||||
namespace std
|
||||
{
|
||||
static_assert(sizeof(size_t) >= 2 * sizeof(u32), "size_t must be at least twice the size of u32");
|
||||
static_assert(sizeof(usz) >= 2 * sizeof(u32), "usz must be at least twice the size of u32");
|
||||
|
||||
template <>
|
||||
struct hash<utils::address_range>
|
||||
{
|
||||
std::size_t operator()(const utils::address_range& k) const
|
||||
usz operator()(const utils::address_range& k) const
|
||||
{
|
||||
// we can guarantee a unique hash since our type is 64 bits and size_t as well
|
||||
return (size_t{ k.start } << 32) | size_t{ k.end };
|
||||
// we can guarantee a unique hash since our type is 64 bits and usz as well
|
||||
return (usz{ k.start } << 32) | usz{ k.end };
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
-279
@@ -1,279 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
|
||||
extern bool g_use_rtm;
|
||||
extern u64 g_rtm_tx_limit1;
|
||||
|
||||
namespace utils
|
||||
{
|
||||
// Transaction helper (result = pair of success and op result, or just bool)
|
||||
template <typename F, typename R = std::invoke_result_t<F>>
|
||||
inline auto tx_start(F op)
|
||||
{
|
||||
uint status = -1;
|
||||
|
||||
for (auto stamp0 = __rdtsc(), stamp1 = stamp0; g_use_rtm && stamp1 - stamp0 <= g_rtm_tx_limit1; stamp1 = __rdtsc())
|
||||
{
|
||||
#ifndef _MSC_VER
|
||||
__asm__ goto ("xbegin %l[retry];" ::: "memory" : retry);
|
||||
#else
|
||||
status = _xbegin();
|
||||
|
||||
if (status != _XBEGIN_STARTED) [[unlikely]]
|
||||
{
|
||||
goto retry;
|
||||
}
|
||||
#endif
|
||||
|
||||
if constexpr (std::is_void_v<R>)
|
||||
{
|
||||
std::invoke(op);
|
||||
#ifndef _MSC_VER
|
||||
__asm__ volatile ("xend;" ::: "memory");
|
||||
#else
|
||||
_xend();
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto result = std::invoke(op);
|
||||
#ifndef _MSC_VER
|
||||
__asm__ volatile ("xend;" ::: "memory");
|
||||
#else
|
||||
_xend();
|
||||
#endif
|
||||
return std::make_pair(true, std::move(result));
|
||||
}
|
||||
|
||||
retry:
|
||||
#ifndef _MSC_VER
|
||||
__asm__ volatile ("movl %%eax, %0;" : "=r" (status) :: "memory");
|
||||
#endif
|
||||
if (!status) [[unlikely]]
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (std::is_void_v<R>)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return std::make_pair(false, R());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Rotate helpers
|
||||
#if defined(__GNUG__)
|
||||
|
||||
inline u8 rol8(u8 x, u8 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft8)
|
||||
return __builtin_rotateleft8(x, n);
|
||||
#else
|
||||
u8 result = x;
|
||||
__asm__("rolb %[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u8 ror8(u8 x, u8 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright8)
|
||||
return __builtin_rotateright8(x, n);
|
||||
#else
|
||||
u8 result = x;
|
||||
__asm__("rorb %[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u16 rol16(u16 x, u16 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft16)
|
||||
return __builtin_rotateleft16(x, n);
|
||||
#else
|
||||
u16 result = x;
|
||||
__asm__("rolw %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u16 ror16(u16 x, u16 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright16)
|
||||
return __builtin_rotateright16(x, n);
|
||||
#else
|
||||
u16 result = x;
|
||||
__asm__("rorw %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 rol32(u32 x, u32 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft32)
|
||||
return __builtin_rotateleft32(x, n);
|
||||
#else
|
||||
u32 result = x;
|
||||
__asm__("roll %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u32 ror32(u32 x, u32 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright32)
|
||||
return __builtin_rotateright32(x, n);
|
||||
#else
|
||||
u32 result = x;
|
||||
__asm__("rorl %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 rol64(u64 x, u64 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateleft64)
|
||||
return __builtin_rotateleft64(x, n);
|
||||
#else
|
||||
u64 result = x;
|
||||
__asm__("rolq %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline u64 ror64(u64 x, u64 n)
|
||||
{
|
||||
#if __has_builtin(__builtin_rotateright64)
|
||||
return __builtin_rotateright64(x, n);
|
||||
#else
|
||||
u64 result = x;
|
||||
__asm__("rorq %b[n], %[result]" : [result] "+g"(result) : [n] "c"(n));
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
constexpr u64 umulh64(u64 a, u64 b)
|
||||
{
|
||||
const __uint128_t x = a;
|
||||
const __uint128_t y = b;
|
||||
return (x * y) >> 64;
|
||||
}
|
||||
|
||||
constexpr s64 mulh64(s64 a, s64 b)
|
||||
{
|
||||
const __int128_t x = a;
|
||||
const __int128_t y = b;
|
||||
return (x * y) >> 64;
|
||||
}
|
||||
|
||||
constexpr s64 div128(s64 high, s64 low, s64 divisor, s64* remainder = nullptr)
|
||||
{
|
||||
const __int128_t x = (__uint128_t{u64(high)} << 64) | u64(low);
|
||||
const __int128_t r = x / divisor;
|
||||
|
||||
if (remainder)
|
||||
{
|
||||
*remainder = x % divisor;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr u64 udiv128(u64 high, u64 low, u64 divisor, u64* remainder = nullptr)
|
||||
{
|
||||
const __uint128_t x = (__uint128_t{high} << 64) | low;
|
||||
const __uint128_t r = x / divisor;
|
||||
|
||||
if (remainder)
|
||||
{
|
||||
*remainder = x % divisor;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
#elif defined(_MSC_VER)
|
||||
inline u8 rol8(u8 x, u8 n)
|
||||
{
|
||||
return _rotl8(x, n);
|
||||
}
|
||||
|
||||
inline u8 ror8(u8 x, u8 n)
|
||||
{
|
||||
return _rotr8(x, n);
|
||||
}
|
||||
|
||||
inline u16 rol16(u16 x, u16 n)
|
||||
{
|
||||
return _rotl16(x, (u8)n);
|
||||
}
|
||||
|
||||
inline u16 ror16(u16 x, u16 n)
|
||||
{
|
||||
return _rotr16(x, (u8)n);
|
||||
}
|
||||
|
||||
inline u32 rol32(u32 x, u32 n)
|
||||
{
|
||||
return _rotl(x, (int)n);
|
||||
}
|
||||
|
||||
inline u32 ror32(u32 x, u32 n)
|
||||
{
|
||||
return _rotr(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 rol64(u64 x, u64 n)
|
||||
{
|
||||
return _rotl64(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 ror64(u64 x, u64 n)
|
||||
{
|
||||
return _rotr64(x, (int)n);
|
||||
}
|
||||
|
||||
inline u64 umulh64(u64 x, u64 y)
|
||||
{
|
||||
return __umulh(x, y);
|
||||
}
|
||||
|
||||
inline s64 mulh64(s64 x, s64 y)
|
||||
{
|
||||
return __mulh(x, y);
|
||||
}
|
||||
|
||||
inline s64 div128(s64 high, s64 low, s64 divisor, s64* remainder = nullptr)
|
||||
{
|
||||
s64 rem;
|
||||
s64 r = _div128(high, low, divisor, &rem);
|
||||
|
||||
if (remainder)
|
||||
{
|
||||
*remainder = rem;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
inline u64 udiv128(u64 high, u64 low, u64 divisor, u64* remainder = nullptr)
|
||||
{
|
||||
u64 rem;
|
||||
u64 r = _udiv128(high, low, divisor, &rem);
|
||||
|
||||
if (remainder)
|
||||
{
|
||||
*remainder = rem;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
#endif
|
||||
} // namespace utils
|
||||
+61
-152
@@ -1,15 +1,13 @@
|
||||
#include "bin_patch.h"
|
||||
#include "bin_patch.h"
|
||||
#include "File.h"
|
||||
#include "Config.h"
|
||||
#include "version.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
LOG_CHANNEL(patch_log);
|
||||
#include "util/types.hpp"
|
||||
#include "util/endian.hpp"
|
||||
|
||||
namespace config_key
|
||||
{
|
||||
static const std::string enable_legacy_patches = "Enable Legacy Patches";
|
||||
}
|
||||
LOG_CHANNEL(patch_log, "PAT");
|
||||
|
||||
template <>
|
||||
void fmt_class_string<YAML::NodeType::value>::format(std::string& out, u64 arg)
|
||||
@@ -46,9 +44,12 @@ void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
|
||||
case patch_type::bef64: return "bef64";
|
||||
case patch_type::be16: return "be16";
|
||||
case patch_type::be32: return "be32";
|
||||
case patch_type::bd32: return "bd32";
|
||||
case patch_type::be64: return "be64";
|
||||
case patch_type::bd64: return "bd64";
|
||||
case patch_type::lef32: return "lef32";
|
||||
case patch_type::lef64: return "lef64";
|
||||
case patch_type::utf8: return "utf8";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
@@ -125,16 +126,14 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
}
|
||||
|
||||
// Load patch config to determine which patches are enabled
|
||||
bool enable_legacy_patches = false;
|
||||
patch_map patch_config;
|
||||
|
||||
if (!importing)
|
||||
{
|
||||
patch_config = load_config(enable_legacy_patches);
|
||||
patch_config = load_config();
|
||||
}
|
||||
|
||||
std::string version;
|
||||
bool is_legacy_patch = false;
|
||||
|
||||
if (const auto version_node = root[patch_key::version])
|
||||
{
|
||||
@@ -150,17 +149,12 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
// We don't need the Version node in local memory anymore
|
||||
root.remove(patch_key::version);
|
||||
}
|
||||
else if (importing)
|
||||
else
|
||||
{
|
||||
append_log_message(log_messages, fmt::format("Error: No '%s' entry found. Patch engine version = %s (file: %s)", patch_key::version, patch_engine_version, path));
|
||||
patch_log.error("No '%s' entry found. Patch engine version = %s (file: %s)", patch_key::version, patch_engine_version, path);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
patch_log.warning("Patch engine version %s: Reading legacy patch file %s", patch_engine_version, path);
|
||||
is_legacy_patch = true;
|
||||
}
|
||||
|
||||
bool is_valid = true;
|
||||
|
||||
@@ -169,30 +163,6 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
{
|
||||
const auto& main_key = pair.first.Scalar();
|
||||
|
||||
// Use old logic and yaml layout if this is a legacy patch
|
||||
if (is_legacy_patch)
|
||||
{
|
||||
struct patch_info info{};
|
||||
info.hash = main_key;
|
||||
info.is_enabled = enable_legacy_patches;
|
||||
info.is_legacy = true;
|
||||
info.source_path = path;
|
||||
|
||||
if (!read_patch_node(info, pair.second, root, log_messages))
|
||||
{
|
||||
is_valid = false;
|
||||
}
|
||||
|
||||
// Find or create an entry matching the key/hash in our map
|
||||
auto& container = patches_map[main_key];
|
||||
container.hash = main_key;
|
||||
container.is_legacy = true;
|
||||
container.patch_info_map["legacy"] = info;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use new logic and yaml layout
|
||||
|
||||
if (const auto yml_type = pair.second.Type(); yml_type != YAML::NodeType::Map)
|
||||
{
|
||||
append_log_message(log_messages, fmt::format("Error: Skipping key %s: expected Map, found %s", main_key, yml_type));
|
||||
@@ -209,7 +179,6 @@ bool patch_engine::load(patch_map& patches_map, const std::string& path, std::st
|
||||
|
||||
// Find or create an entry matching the key/hash in our map
|
||||
auto& container = patches_map[main_key];
|
||||
container.is_legacy = false;
|
||||
container.hash = main_key;
|
||||
container.version = version;
|
||||
|
||||
@@ -428,35 +397,6 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
|
||||
{
|
||||
// Special syntax: anchors (named sequence)
|
||||
|
||||
// Most legacy patches don't use the anchor syntax correctly, so try to sanitize it.
|
||||
if (info.is_legacy)
|
||||
{
|
||||
if (const auto yml_type = addr_node.Type(); yml_type == YAML::NodeType::Scalar)
|
||||
{
|
||||
if (!root)
|
||||
{
|
||||
patch_log.fatal("Trying to parse legacy patch with invalid root."); // Sanity Check
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto anchor = addr_node.Scalar();
|
||||
const auto anchor_node = root[anchor];
|
||||
|
||||
if (anchor_node)
|
||||
{
|
||||
addr_node = anchor_node;
|
||||
append_log_message(log_messages, fmt::format("Incorrect anchor syntax found in legacy patch: %s (key: %s)", anchor, info.hash));
|
||||
patch_log.warning("Incorrect anchor syntax found in legacy patch: %s (key: %s)", anchor, info.hash);
|
||||
}
|
||||
else
|
||||
{
|
||||
append_log_message(log_messages, fmt::format("Anchor not found in legacy patch: %s (key: %s)", anchor, info.hash));
|
||||
patch_log.error("Anchor not found in legacy patch: %s (key: %s)", anchor, info.hash);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the anchor was resolved.
|
||||
if (const auto yml_type = addr_node.Type(); yml_type != YAML::NodeType::Sequence)
|
||||
{
|
||||
@@ -490,6 +430,10 @@ bool patch_engine::add_patch_data(YAML::Node node, patch_info& info, u32 modifie
|
||||
|
||||
switch (p_data.type)
|
||||
{
|
||||
case patch_type::utf8:
|
||||
{
|
||||
break;
|
||||
}
|
||||
case patch_type::bef32:
|
||||
case patch_type::lef32:
|
||||
case patch_type::bef64:
|
||||
@@ -524,7 +468,7 @@ bool patch_engine::read_patch_node(patch_info& info, YAML::Node node, const YAML
|
||||
if (!node)
|
||||
{
|
||||
append_log_message(log_messages, fmt::format("Skipping invalid patch node %s. (key: %s)", info.description, info.hash));
|
||||
patch_log.error("Skipping invalid patch node %s. (key: %s)" HERE, info.description, info.hash);
|
||||
patch_log.error("Skipping invalid patch node %s. (key: %s)", info.description, info.hash);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -550,10 +494,7 @@ bool patch_engine::read_patch_node(patch_info& info, YAML::Node node, const YAML
|
||||
|
||||
void patch_engine::append_global_patches()
|
||||
{
|
||||
// Legacy patch.yml
|
||||
load(m_map, fs::get_config_dir() + "patch.yml");
|
||||
|
||||
// New patch.yml
|
||||
// Regular patch.yml
|
||||
load(m_map, get_patches_path() + "patch.yml");
|
||||
|
||||
// Imported patch.yml
|
||||
@@ -567,45 +508,30 @@ void patch_engine::append_title_patches(const std::string& title_id)
|
||||
return;
|
||||
}
|
||||
|
||||
// Legacy patch.yml
|
||||
load(m_map, fs::get_config_dir() + "data/" + title_id + "/patch.yml");
|
||||
|
||||
// New patch.yml
|
||||
// Regular patch.yml
|
||||
load(m_map, get_patches_path() + title_id + "_patch.yml");
|
||||
}
|
||||
|
||||
std::size_t patch_engine::apply(const std::string& name, u8* dst)
|
||||
static std::basic_string<u32> apply_modification(const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
|
||||
{
|
||||
return apply_patch<false>(name, dst, 0, 0);
|
||||
}
|
||||
|
||||
std::size_t patch_engine::apply_with_ls_check(const std::string& name, u8* dst, u32 filesz, u32 ls_addr)
|
||||
{
|
||||
return apply_patch<true>(name, dst, filesz, ls_addr);
|
||||
}
|
||||
|
||||
template <bool check_local_storage>
|
||||
static std::size_t apply_modification(const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 ls_addr)
|
||||
{
|
||||
size_t applied = 0;
|
||||
std::basic_string<u32> applied;
|
||||
|
||||
for (const auto& p : patch.data_list)
|
||||
{
|
||||
u32 offset = p.offset;
|
||||
|
||||
if constexpr (check_local_storage)
|
||||
if (offset < min_addr || offset - min_addr >= filesz)
|
||||
{
|
||||
if (offset < ls_addr || offset >= (ls_addr + filesz))
|
||||
{
|
||||
// This patch is out of range for this segment
|
||||
continue;
|
||||
}
|
||||
|
||||
offset -= ls_addr;
|
||||
// This patch is out of range for this segment
|
||||
continue;
|
||||
}
|
||||
|
||||
offset -= min_addr;
|
||||
|
||||
auto ptr = dst + offset;
|
||||
|
||||
u32 resval = -1;
|
||||
|
||||
switch (p.type)
|
||||
{
|
||||
case patch_type::invalid:
|
||||
@@ -649,9 +575,15 @@ static std::size_t apply_modification(const patch_engine::patch_info& patch, u8*
|
||||
*reinterpret_cast<be_t<u16, 1>*>(ptr) = static_cast<u16>(p.value.long_value);
|
||||
break;
|
||||
}
|
||||
case patch_type::bd32:
|
||||
{
|
||||
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value.long_value);
|
||||
break;
|
||||
}
|
||||
case patch_type::be32:
|
||||
{
|
||||
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value.long_value);
|
||||
if (offset % 4 == 0) resval = offset;
|
||||
break;
|
||||
}
|
||||
case patch_type::bef32:
|
||||
@@ -659,9 +591,22 @@ static std::size_t apply_modification(const patch_engine::patch_info& patch, u8*
|
||||
*reinterpret_cast<be_t<u32, 1>*>(ptr) = std::bit_cast<u32, f32>(static_cast<f32>(p.value.double_value));
|
||||
break;
|
||||
}
|
||||
case patch_type::bd64:
|
||||
{
|
||||
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value.long_value);
|
||||
break;
|
||||
}
|
||||
case patch_type::be64:
|
||||
{
|
||||
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value.long_value);
|
||||
|
||||
if (offset % 4)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
resval = offset;
|
||||
applied.push_back((offset + 7) & -4); // Two 32-bit locations
|
||||
break;
|
||||
}
|
||||
case patch_type::bef64:
|
||||
@@ -669,29 +614,33 @@ static std::size_t apply_modification(const patch_engine::patch_info& patch, u8*
|
||||
*reinterpret_cast<be_t<u64, 1>*>(ptr) = std::bit_cast<u64, f64>(p.value.double_value);
|
||||
break;
|
||||
}
|
||||
case patch_type::utf8:
|
||||
{
|
||||
std::memcpy(ptr, p.original_value.data(), p.original_value.size());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
++applied;
|
||||
// Possibly an executable instruction
|
||||
applied.push_back(resval);
|
||||
}
|
||||
|
||||
return applied;
|
||||
}
|
||||
|
||||
template <bool check_local_storage>
|
||||
std::size_t patch_engine::apply_patch(const std::string& name, u8* dst, u32 filesz, u32 ls_addr)
|
||||
std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32 filesz, u32 min_addr)
|
||||
{
|
||||
if (m_map.find(name) == m_map.cend())
|
||||
{
|
||||
return 0;
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t applied_total = 0;
|
||||
std::basic_string<u32> applied_total;
|
||||
const auto& container = m_map.at(name);
|
||||
const auto serial = Emu.GetTitleID();
|
||||
const auto app_version = Emu.GetAppVersion();
|
||||
|
||||
// Different containers in order to seperate the patches
|
||||
std::vector<patch_engine::patch_info> legacy_patches;
|
||||
std::vector<patch_engine::patch_info> patches_for_this_serial_and_this_version;
|
||||
std::vector<patch_engine::patch_info> patches_for_this_serial_and_all_versions;
|
||||
std::vector<patch_engine::patch_info> patches_for_all_serials_and_this_version;
|
||||
@@ -700,17 +649,6 @@ std::size_t patch_engine::apply_patch(const std::string& name, u8* dst, u32 file
|
||||
// Sort patches into different vectors based on their serial and version
|
||||
for (const auto& [description, patch] : container.patch_info_map)
|
||||
{
|
||||
// Find out if this legacy patch is enabled
|
||||
if (patch.is_legacy)
|
||||
{
|
||||
if (patch.is_enabled)
|
||||
{
|
||||
legacy_patches.push_back(patch);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find out if this patch is enabled
|
||||
for (const auto& [title, serials] : patch.titles)
|
||||
{
|
||||
@@ -777,7 +715,6 @@ std::size_t patch_engine::apply_patch(const std::string& name, u8* dst, u32 file
|
||||
|
||||
// Sort specific patches in front of global patches
|
||||
std::vector<patch_engine::patch_info> sorted_patches;
|
||||
sorted_patches.insert(sorted_patches.end(), legacy_patches.begin(), legacy_patches.end());
|
||||
sorted_patches.insert(sorted_patches.end(), patches_for_this_serial_and_this_version.begin(), patches_for_this_serial_and_this_version.end());
|
||||
sorted_patches.insert(sorted_patches.end(), patches_for_this_serial_and_all_versions.begin(), patches_for_this_serial_and_all_versions.end());
|
||||
sorted_patches.insert(sorted_patches.end(), patches_for_all_serials_and_this_version.begin(), patches_for_all_serials_and_this_version.end());
|
||||
@@ -796,23 +733,17 @@ std::size_t patch_engine::apply_patch(const std::string& name, u8* dst, u32 file
|
||||
m_applied_groups.insert(patch.patch_group);
|
||||
}
|
||||
|
||||
const size_t applied = apply_modification<check_local_storage>(patch, dst, filesz, ls_addr);
|
||||
auto applied = apply_modification(patch, dst, filesz, min_addr);
|
||||
|
||||
applied_total += applied;
|
||||
|
||||
if (patch.is_legacy)
|
||||
{
|
||||
patch_log.success("Applied legacy patch (hash='%s')(<- %d)", patch.hash, applied);
|
||||
}
|
||||
else
|
||||
{
|
||||
patch_log.success("Applied patch (hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s') (<- %d)", patch.hash, patch.description, patch.author, patch.patch_version, patch.version, applied);
|
||||
}
|
||||
patch_log.success("Applied patch (hash='%s', description='%s', author='%s', patch_version='%s', file_version='%s') (<- %u)", patch.hash, patch.description, patch.author, patch.patch_version, patch.version, applied.size());
|
||||
}
|
||||
|
||||
return applied_total;
|
||||
}
|
||||
|
||||
void patch_engine::save_config(const patch_map& patches_map, bool enable_legacy_patches)
|
||||
void patch_engine::save_config(const patch_map& patches_map)
|
||||
{
|
||||
const std::string path = get_patch_config_path();
|
||||
patch_log.notice("Saving patch config file %s", path);
|
||||
@@ -827,26 +758,13 @@ void patch_engine::save_config(const patch_map& patches_map, bool enable_legacy_
|
||||
YAML::Emitter out;
|
||||
out << YAML::BeginMap;
|
||||
|
||||
// Save "Enable Legacy Patches"
|
||||
out << config_key::enable_legacy_patches << enable_legacy_patches;
|
||||
|
||||
// Save 'enabled' state per hash, description, serial and app_version
|
||||
patch_map config_map;
|
||||
|
||||
for (const auto& [hash, container] : patches_map)
|
||||
{
|
||||
if (container.is_legacy)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& [description, patch] : container.patch_info_map)
|
||||
{
|
||||
if (patch.is_legacy)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& [title, serials] : patch.titles)
|
||||
{
|
||||
for (const auto& [serial, app_versions] : serials)
|
||||
@@ -903,7 +821,7 @@ void patch_engine::save_config(const patch_map& patches_map, bool enable_legacy_
|
||||
file.write(out.c_str(), out.size());
|
||||
}
|
||||
|
||||
static void append_patches(patch_engine::patch_map& existing_patches, const patch_engine::patch_map& new_patches, size_t& count, size_t& total, std::stringstream* log_messages)
|
||||
static void append_patches(patch_engine::patch_map& existing_patches, const patch_engine::patch_map& new_patches, usz& count, usz& total, std::stringstream* log_messages)
|
||||
{
|
||||
for (const auto& [hash, new_container] : new_patches)
|
||||
{
|
||||
@@ -1055,7 +973,7 @@ bool patch_engine::save_patches(const patch_map& patches, const std::string& pat
|
||||
return true;
|
||||
}
|
||||
|
||||
bool patch_engine::import_patches(const patch_engine::patch_map& patches, const std::string& path, size_t& count, size_t& total, std::stringstream* log_messages)
|
||||
bool patch_engine::import_patches(const patch_engine::patch_map& patches, const std::string& path, usz& count, usz& total, std::stringstream* log_messages)
|
||||
{
|
||||
patch_engine::patch_map existing_patches;
|
||||
|
||||
@@ -1089,10 +1007,8 @@ bool patch_engine::remove_patch(const patch_info& info)
|
||||
return false;
|
||||
}
|
||||
|
||||
patch_engine::patch_map patch_engine::load_config(bool& enable_legacy_patches)
|
||||
patch_engine::patch_map patch_engine::load_config()
|
||||
{
|
||||
enable_legacy_patches = true; // Default to true
|
||||
|
||||
patch_map config_map;
|
||||
|
||||
const std::string path = get_patch_config_path();
|
||||
@@ -1108,13 +1024,6 @@ patch_engine::patch_map patch_engine::load_config(bool& enable_legacy_patches)
|
||||
return config_map;
|
||||
}
|
||||
|
||||
// Try to load "Enable Legacy Patches" (default to true)
|
||||
if (auto enable_legacy_node = root[config_key::enable_legacy_patches])
|
||||
{
|
||||
enable_legacy_patches = enable_legacy_node.as<bool>(true);
|
||||
root.remove(config_key::enable_legacy_patches); // Remove the node in order to skip it in the next part
|
||||
}
|
||||
|
||||
for (const auto pair : root)
|
||||
{
|
||||
const auto& hash = pair.first.Scalar();
|
||||
|
||||
+10
-17
@@ -1,10 +1,10 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "BEType.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "util/types.hpp"
|
||||
#include "util/yaml.hpp"
|
||||
|
||||
namespace patch_key
|
||||
@@ -34,9 +34,12 @@ enum class patch_type
|
||||
lef64,
|
||||
be16,
|
||||
be32,
|
||||
bd32, // be32 with data hint (non-code)
|
||||
be64,
|
||||
bd64, // be64 with data hint (non-code)
|
||||
bef32,
|
||||
bef64,
|
||||
utf8, // Text of string (not null-terminated automatically)
|
||||
};
|
||||
|
||||
class patch_engine
|
||||
@@ -53,7 +56,7 @@ public:
|
||||
f64 double_value;
|
||||
} value { 0 };
|
||||
};
|
||||
|
||||
|
||||
using patch_app_versions = std::unordered_map<std::string /*app_version*/, bool /*enabled*/>;
|
||||
using patch_serials = std::unordered_map<std::string /*serial*/, patch_app_versions>;
|
||||
using patch_titles = std::unordered_map<std::string /*serial*/, patch_serials>;
|
||||
@@ -73,8 +76,6 @@ public:
|
||||
// Redundant information for accessibility (see patch_container)
|
||||
std::string hash;
|
||||
std::string version;
|
||||
bool is_legacy = false;
|
||||
bool is_enabled = false; // only for legacy patches
|
||||
};
|
||||
|
||||
struct patch_container
|
||||
@@ -82,7 +83,6 @@ public:
|
||||
std::unordered_map<std::string /*description*/, patch_info> patch_info_map;
|
||||
std::string hash;
|
||||
std::string version;
|
||||
bool is_legacy = false;
|
||||
};
|
||||
|
||||
using patch_map = std::unordered_map<std::string /*hash*/, patch_container>;
|
||||
@@ -111,19 +111,19 @@ public:
|
||||
static bool add_patch_data(YAML::Node node, patch_info& info, u32 modifier, const YAML::Node& root, std::stringstream* log_messages = nullptr);
|
||||
|
||||
// Save to patch_config.yml
|
||||
static void save_config(const patch_map& patches_map, bool enable_legacy_patches);
|
||||
static void save_config(const patch_map& patches_map);
|
||||
|
||||
// Save a patch file
|
||||
static bool save_patches(const patch_map& patches, const std::string& path, std::stringstream* log_messages = nullptr);
|
||||
|
||||
// Create or append patches to a file
|
||||
static bool import_patches(const patch_map& patches, const std::string& path, size_t& count, size_t& total, std::stringstream* log_messages = nullptr);
|
||||
static bool import_patches(const patch_map& patches, const std::string& path, usz& count, usz& total, std::stringstream* log_messages = nullptr);
|
||||
|
||||
// Remove a patch from a file
|
||||
static bool remove_patch(const patch_info& info);
|
||||
|
||||
// Load patch_config.yml
|
||||
static patch_map load_config(bool& enable_legacy_patches);
|
||||
static patch_map load_config();
|
||||
|
||||
// Load from file and append to member patches map
|
||||
void append_global_patches();
|
||||
@@ -132,16 +132,9 @@ public:
|
||||
void append_title_patches(const std::string& title_id);
|
||||
|
||||
// Apply patch (returns the number of entries applied)
|
||||
std::size_t apply(const std::string& name, u8* dst);
|
||||
|
||||
// Apply patch with a check that the address exists in SPU local storage
|
||||
std::size_t apply_with_ls_check(const std::string& name, u8* dst, u32 filesz, u32 ls_addr);
|
||||
std::basic_string<u32> apply(const std::string& name, u8* dst, u32 filesz = UINT32_MAX, u32 min_addr = 0);
|
||||
|
||||
private:
|
||||
// Internal: Apply patch (returns the number of entries applied)
|
||||
template <bool check_local_storage>
|
||||
std::size_t apply_patch(const std::string& name, u8* dst, u32 filesz, u32 ls_addr);
|
||||
|
||||
// Database
|
||||
patch_map m_map;
|
||||
|
||||
|
||||
+10
-10
@@ -21,8 +21,9 @@ Examples:
|
||||
Intersection (&) and symmetric difference (^) is also available.
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include "Utilities/StrFmt.h"
|
||||
|
||||
template <typename T>
|
||||
class atomic_bs_t;
|
||||
@@ -48,8 +49,8 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
static constexpr std::size_t bitmax = sizeof(T) * 8;
|
||||
static constexpr std::size_t bitsize = static_cast<under>(T::__bitset_enum_max);
|
||||
static constexpr usz bitmax = sizeof(T) * 8;
|
||||
static constexpr usz bitsize = static_cast<under>(T::__bitset_enum_max);
|
||||
|
||||
static_assert(std::is_enum<T>::value, "bs_t<> error: invalid type (must be enum)");
|
||||
static_assert(bitsize <= bitmax, "bs_t<> error: invalid __bitset_enum_max");
|
||||
@@ -205,7 +206,7 @@ constexpr bs_t<T> operator ^(T lhs, T rhs)
|
||||
|
||||
// Atomic bitset specialization with optimized operations
|
||||
template <typename T>
|
||||
class atomic_bs_t : public atomic_t<::bs_t<T>> // TODO: true specialization
|
||||
class atomic_bs_t : public atomic_t<::bs_t<T>>
|
||||
{
|
||||
// Corresponding bitset type
|
||||
using bs_t = ::bs_t<T>;
|
||||
@@ -379,11 +380,6 @@ public:
|
||||
return lhs.m_data != rhs.load().m_data;
|
||||
}
|
||||
|
||||
bool test(const bs_t& rhs)
|
||||
{
|
||||
return base::load().test(rhs);
|
||||
}
|
||||
|
||||
bool test_and_set(T rhs)
|
||||
{
|
||||
return atomic_storage<under>::bts(m_data.m_data, static_cast<uint>(static_cast<under>(rhs)));
|
||||
@@ -394,10 +390,14 @@ public:
|
||||
return atomic_storage<under>::btr(m_data.m_data, static_cast<uint>(static_cast<under>(rhs)));
|
||||
}
|
||||
|
||||
bool test_and_complement(T rhs)
|
||||
bool test_and_invert(T rhs)
|
||||
{
|
||||
return atomic_storage<under>::btc(m_data.m_data, static_cast<uint>(static_cast<under>(rhs)));
|
||||
}
|
||||
|
||||
bool bit_test_set(uint bit) = delete;
|
||||
bool bit_test_reset(uint bit) = delete;
|
||||
bool bit_test_invert(uint bit) = delete;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
|
||||
+22
-22
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
static const size_t size_dropped = std::numeric_limits<size_t>::max();
|
||||
static const usz size_dropped = -1;
|
||||
|
||||
/*
|
||||
C-style format parser. Appends formatted string to `out`, returns number of characters written.
|
||||
@@ -15,13 +15,13 @@ C-style format parser. Appends formatted string to `out`, returns number of char
|
||||
`src`: rvalue reference to argument provider.
|
||||
*/
|
||||
template<typename Dst, typename Char, typename Src>
|
||||
std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
usz cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
{
|
||||
const std::size_t start_pos = out.size();
|
||||
const usz start_pos = out.size();
|
||||
|
||||
struct cfmt_context
|
||||
{
|
||||
std::size_t size; // Size of current format sequence
|
||||
usz size; // Size of current format sequence
|
||||
|
||||
u8 args; // Number of extra args used
|
||||
u8 type; // Integral type bytesize
|
||||
@@ -80,7 +80,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const auto write_decimal = [&](u64 value, s64 min_size)
|
||||
{
|
||||
const std::size_t start = out.size();
|
||||
const usz start = out.size();
|
||||
|
||||
do
|
||||
{
|
||||
@@ -90,7 +90,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
while (0 < --min_size || value);
|
||||
|
||||
// Revert written characters
|
||||
for (std::size_t i = start, j = out.size() - 1; i < j; i++, j--)
|
||||
for (usz i = start, j = out.size() - 1; i < j; i++, j--)
|
||||
{
|
||||
std::swap(out[i], out[j]);
|
||||
}
|
||||
@@ -285,7 +285,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
break;
|
||||
}
|
||||
|
||||
const std::size_t start = out.size();
|
||||
const usz start = out.size();
|
||||
out.push_back(src.template get<Char>(ctx.args));
|
||||
|
||||
if (1 < ctx.width)
|
||||
@@ -307,8 +307,8 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
break;
|
||||
}
|
||||
|
||||
const std::size_t start = out.size();
|
||||
const std::size_t size1 = src.fmt_string(out, ctx.args);
|
||||
const usz start = out.size();
|
||||
const usz size1 = src.fmt_string(out, ctx.args);
|
||||
|
||||
if (ctx.dot && size1 > ctx.prec)
|
||||
{
|
||||
@@ -316,7 +316,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
out.resize(start + ctx.prec);
|
||||
}
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
const usz size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
@@ -352,7 +352,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
const u64 val = src.template get<u64>(ctx.args);
|
||||
const bool negative = ctx.type && static_cast<s64>(val) < 0;
|
||||
|
||||
const std::size_t start = out.size();
|
||||
const usz start = out.size();
|
||||
|
||||
if (!ctx.dot || ctx.prec)
|
||||
{
|
||||
@@ -372,7 +372,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
write_decimal(negative ? 0 - val : val, ctx.prec);
|
||||
}
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
const usz size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
@@ -419,7 +419,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
|
||||
const std::size_t start = out.size();
|
||||
const usz start = out.size();
|
||||
|
||||
if (ctx.alter)
|
||||
{
|
||||
@@ -435,7 +435,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
write_octal(val, ctx.prec);
|
||||
}
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
const usz size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
@@ -476,7 +476,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
|
||||
const std::size_t start = out.size();
|
||||
const usz start = out.size();
|
||||
|
||||
if (ctx.alter)
|
||||
{
|
||||
@@ -493,7 +493,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
write_hex(val, ch == 'X', ctx.prec);
|
||||
}
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
const usz size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
@@ -540,14 +540,14 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
// Trunc sign-extended signed types
|
||||
const u64 val = src.template get<u64>(ctx.args) & mask;
|
||||
|
||||
const std::size_t start = out.size();
|
||||
const usz start = out.size();
|
||||
|
||||
if (!ctx.dot || ctx.prec)
|
||||
{
|
||||
write_decimal(val, ctx.prec);
|
||||
}
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
const usz size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
@@ -570,11 +570,11 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
|
||||
const u64 val = src.template get<u64>(ctx.args);
|
||||
|
||||
const std::size_t start = out.size();
|
||||
const usz start = out.size();
|
||||
|
||||
write_hex(val, false, sizeof(void*) * 2);
|
||||
|
||||
const std::size_t size2 = out.size() - start;
|
||||
const usz size2 = out.size() - start;
|
||||
|
||||
if (size2 < ctx.width)
|
||||
{
|
||||
@@ -610,7 +610,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
||||
const u64 arg1 = ctx.args >= 1 ? src.template get<u64>(1) : 0;
|
||||
const u64 arg2 = ctx.args >= 2 ? src.template get<u64>(2) : 0;
|
||||
|
||||
if (const std::size_t _size = std::snprintf(0, 0, _fmt.c_str(), arg0, arg1, arg2))
|
||||
if (const usz _size = std::snprintf(0, 0, _fmt.c_str(), arg0, arg1, arg2))
|
||||
{
|
||||
out.resize(out.size() + _size);
|
||||
std::snprintf(&out.front() + out.size() - _size, _size + 1, _fmt.c_str(), arg0, arg1, arg2);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "cheat_info.h"
|
||||
#include "Config.h"
|
||||
#include "StrUtil.h"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
enum class cheat_type : u8
|
||||
{
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
#include "cond.h"
|
||||
#include "cond.h"
|
||||
#include "sync.h"
|
||||
#include "lockless.h"
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
void cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
|
||||
{
|
||||
// Not supposed to fail
|
||||
verify(HERE), _old;
|
||||
ensure(_old);
|
||||
|
||||
// Wait with timeout
|
||||
m_value.wait(_old, c_signal_mask, atomic_wait_timeout{_timeout > max_timeout ? UINT64_MAX : _timeout * 1000});
|
||||
@@ -50,10 +50,10 @@ void cond_variable::imp_wake(u32 _count) noexcept
|
||||
if (_count > 1 || ((_old + (c_signal_mask & (0 - c_signal_mask))) & c_signal_mask) == c_signal_mask)
|
||||
{
|
||||
// Resort to notify_all if signal count reached max
|
||||
m_value.notify_all();
|
||||
m_value.notify_all(c_signal_mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_value.notify_one();
|
||||
m_value.notify_one(c_signal_mask);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include <shared_mutex>
|
||||
|
||||
// Lightweight condition variable
|
||||
class cond_variable
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <cmath>
|
||||
|
||||
|
||||
+19
-9
@@ -1,33 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include "util/types.hpp"
|
||||
|
||||
namespace rpcs3
|
||||
{
|
||||
constexpr usz fnv_seed = 14695981039346656037ull;
|
||||
constexpr usz fnv_prime = 1099511628211ull;
|
||||
|
||||
template<typename T>
|
||||
static size_t hash_base(T value)
|
||||
static usz hash_base(T value)
|
||||
{
|
||||
return static_cast<size_t>(value);
|
||||
return static_cast<usz>(value);
|
||||
}
|
||||
|
||||
template<typename T, typename = std::enable_if_t<std::is_integral<T>::value, bool>>
|
||||
static inline usz hash64(usz hash_value, const T data)
|
||||
{
|
||||
hash_value ^= data;
|
||||
hash_value *= fnv_prime;
|
||||
return hash_value;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
static size_t hash_struct_base(const T& value)
|
||||
static usz hash_struct_base(const T& value)
|
||||
{
|
||||
// FNV 64-bit
|
||||
size_t result = 14695981039346656037ull;
|
||||
usz result = fnv_seed;
|
||||
const U *bits = reinterpret_cast<const U*>(&value);
|
||||
|
||||
for (size_t n = 0; n < (sizeof(T) / sizeof(U)); ++n)
|
||||
for (usz n = 0; n < (sizeof(T) / sizeof(U)); ++n)
|
||||
{
|
||||
result ^= bits[n];
|
||||
result *= 1099511628211ull;
|
||||
result = hash64(result, bits[n]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static size_t hash_struct(const T& value)
|
||||
static usz hash_struct(const T& value)
|
||||
{
|
||||
static constexpr auto block_sz = sizeof(T);
|
||||
|
||||
|
||||
+20
-67
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
//! Simple sizeless array base for concurrent access. Cannot shrink, only growths automatically.
|
||||
@@ -8,7 +8,7 @@
|
||||
//!
|
||||
//! T is the type of elements. Currently, default constructor of T shall be constexpr.
|
||||
//! N is initial element count, available without any memory allocation and only stored contiguously.
|
||||
template <typename T, std::size_t N>
|
||||
template <typename T, usz N>
|
||||
class lf_array
|
||||
{
|
||||
// Data (default-initialized)
|
||||
@@ -28,7 +28,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
T& operator [](std::size_t index)
|
||||
T& operator [](usz index)
|
||||
{
|
||||
if (index < N) [[likely]]
|
||||
{
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
|
||||
//! Simple lock-free FIFO queue base. Based on lf_array<T, N> itself. Currently uses 32-bit counters.
|
||||
//! There is no "push_end" or "pop_begin" provided, the queue element must signal its state on its own.
|
||||
template<typename T, std::size_t N>
|
||||
template<typename T, usz N>
|
||||
class lf_fifo : public lf_array<T, N>
|
||||
{
|
||||
// LSB 32-bit: push, MSB 32-bit: pop
|
||||
@@ -307,14 +307,25 @@ public:
|
||||
delete m_head.load();
|
||||
}
|
||||
|
||||
void wait() noexcept
|
||||
template <atomic_wait::op Flags = atomic_wait::op::eq>
|
||||
void wait(std::nullptr_t null = nullptr) noexcept
|
||||
{
|
||||
if (m_head == nullptr)
|
||||
{
|
||||
m_head.wait(nullptr);
|
||||
m_head.template wait<Flags>(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
const volatile void* observe() const noexcept
|
||||
{
|
||||
return m_head.load();
|
||||
}
|
||||
|
||||
explicit operator bool() const noexcept
|
||||
{
|
||||
return m_head != nullptr;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void push(Args&&... args)
|
||||
{
|
||||
@@ -343,9 +354,9 @@ public:
|
||||
|
||||
// Apply func(data) to each element, return the total length
|
||||
template <typename F>
|
||||
std::size_t apply(F func)
|
||||
usz apply(F func)
|
||||
{
|
||||
std::size_t count = 0;
|
||||
usz count = 0;
|
||||
|
||||
for (auto slice = pop_all(); slice; slice.pop_front())
|
||||
{
|
||||
@@ -354,64 +365,6 @@ public:
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
// Iterator that enables direct endless range-for loop: for (auto* ptr : queue) ...
|
||||
class iterator
|
||||
{
|
||||
lf_queue* _this = nullptr;
|
||||
|
||||
lf_queue_slice<T> m_data;
|
||||
|
||||
public:
|
||||
constexpr iterator() = default;
|
||||
|
||||
explicit iterator(lf_queue* _this)
|
||||
: _this(_this)
|
||||
{
|
||||
m_data = _this->pop_all();
|
||||
}
|
||||
|
||||
bool operator !=(const iterator& rhs) const
|
||||
{
|
||||
return _this != rhs._this;
|
||||
}
|
||||
|
||||
T* operator *() const
|
||||
{
|
||||
return m_data ? m_data.get() : nullptr;
|
||||
}
|
||||
|
||||
iterator& operator ++()
|
||||
{
|
||||
if (m_data)
|
||||
{
|
||||
m_data.pop_front();
|
||||
}
|
||||
|
||||
if (!m_data)
|
||||
{
|
||||
m_data = _this->pop_all();
|
||||
|
||||
if (!m_data)
|
||||
{
|
||||
_this->wait();
|
||||
m_data = _this->pop_all();
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
return iterator{this};
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return iterator{};
|
||||
}
|
||||
};
|
||||
|
||||
// Concurrent linked list, elements remain until destroyed.
|
||||
|
||||
+11
-9
@@ -1,8 +1,10 @@
|
||||
#include "mutex.h"
|
||||
|
||||
#include "util/asm.hpp"
|
||||
|
||||
void shared_mutex::imp_lock_shared(u32 val)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
ensure(val < c_err); // "shared_mutex underflow"
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
@@ -23,14 +25,14 @@ void shared_mutex::imp_lock_shared(u32 val)
|
||||
return;
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
ensure((old % c_sig) + c_one < c_sig); // "shared_mutex overflow"
|
||||
imp_wait();
|
||||
lock_downgrade();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock_shared(u32 old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - 1 < c_err;
|
||||
ensure(old - 1 < c_err); // "shared_mutex underflow"
|
||||
|
||||
// Check reader count, notify the writer if necessary
|
||||
if ((old - 1) % c_one == 0)
|
||||
@@ -59,19 +61,19 @@ void shared_mutex::imp_wait()
|
||||
break;
|
||||
}
|
||||
|
||||
m_value.wait(old);
|
||||
m_value.wait(old, c_sig);
|
||||
}
|
||||
}
|
||||
|
||||
void shared_mutex::imp_signal()
|
||||
{
|
||||
m_value += c_sig;
|
||||
m_value.notify_one();
|
||||
m_value.notify_one(c_sig);
|
||||
}
|
||||
|
||||
void shared_mutex::imp_lock(u32 val)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), val < c_err;
|
||||
ensure(val < c_err); // "shared_mutex underflow"
|
||||
|
||||
for (int i = 0; i < 10; i++)
|
||||
{
|
||||
@@ -90,13 +92,13 @@ void shared_mutex::imp_lock(u32 val)
|
||||
return;
|
||||
}
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one < c_sig;
|
||||
ensure((old % c_sig) + c_one < c_sig); // "shared_mutex overflow"
|
||||
imp_wait();
|
||||
}
|
||||
|
||||
void shared_mutex::imp_unlock(u32 old)
|
||||
{
|
||||
verify("shared_mutex underflow" HERE), old - c_one < c_err;
|
||||
ensure(old - c_one < c_err); // "shared_mutex underflow"
|
||||
|
||||
// 1) Notify the next writer if necessary
|
||||
// 2) Notify all readers otherwise if necessary (currently indistinguishable from writers)
|
||||
@@ -121,7 +123,7 @@ void shared_mutex::imp_lock_upgrade()
|
||||
// Convert to writer lock
|
||||
const u32 old = m_value.fetch_add(c_one - 1);
|
||||
|
||||
verify("shared_mutex overflow" HERE), (old % c_sig) + c_one - 1 < c_sig;
|
||||
ensure((old % c_sig) + c_one - 1 < c_sig); // "shared_mutex overflow"
|
||||
|
||||
if (old % c_one == 1)
|
||||
{
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
// Shared mutex with small size (u32).
|
||||
|
||||
+1
-4
@@ -1,7 +1,5 @@
|
||||
#include "stdafx.h"
|
||||
#include "restore_new.h"
|
||||
#include "Utilities/rXml.h"
|
||||
#include "define_new_memleakdetect.h"
|
||||
|
||||
rXmlNode::rXmlNode() : handle()
|
||||
{
|
||||
@@ -54,7 +52,7 @@ std::string rXmlNode::GetAttribute(const std::string &name)
|
||||
|
||||
std::string rXmlNode::GetNodeContent()
|
||||
{
|
||||
return handle.text().get();
|
||||
return handle.text().get();
|
||||
}
|
||||
|
||||
rXmlDocument::rXmlDocument() : handle()
|
||||
@@ -70,4 +68,3 @@ std::shared_ptr<rXmlNode> rXmlDocument::GetRoot()
|
||||
{
|
||||
return std::make_shared<rXmlNode>(handle.root());
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include <pugixml.hpp>
|
||||
#include <memory>
|
||||
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
#include "sema.h"
|
||||
|
||||
#include "util/asm.hpp"
|
||||
|
||||
void semaphore_base::imp_wait()
|
||||
{
|
||||
for (int i = 0; i < 10; i++)
|
||||
@@ -52,7 +54,7 @@ void semaphore_base::imp_wait()
|
||||
|
||||
void semaphore_base::imp_post(s32 _old)
|
||||
{
|
||||
verify("semaphore_base: overflow" HERE), _old < 0;
|
||||
ensure(_old < 0); // "semaphore_base: overflow"
|
||||
|
||||
m_value.notify_one();
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
|
||||
// Lightweight semaphore helper class
|
||||
|
||||
+7
-3
@@ -2,11 +2,14 @@
|
||||
|
||||
/* For internal use. Don't include. */
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "util/atomic.hpp"
|
||||
#include "dynamic_library.h"
|
||||
#include "util/dyn_lib.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#include <Windows.h>
|
||||
#include <time.h>
|
||||
#elif __linux__
|
||||
@@ -15,6 +18,7 @@
|
||||
#include <linux/futex.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
@@ -67,7 +71,7 @@ inline int futex(volatile void* uaddr, int futex_op, uint val, const timespec* t
|
||||
};
|
||||
|
||||
std::mutex mutex;
|
||||
std::unordered_multimap<volatile void*, waiter*, pointer_hash<volatile void, alignof(int)>> map;
|
||||
std::unordered_multimap<volatile void*, waiter*> map;
|
||||
|
||||
int operator()(volatile void* uaddr, int futex_op, uint val, const timespec* timeout, uint mask)
|
||||
{
|
||||
|
||||
-1046
File diff suppressed because it is too large
Load Diff
-1093
File diff suppressed because it is too large
Load Diff
@@ -67,7 +67,7 @@ namespace utils
|
||||
int vnum2 = 0;
|
||||
|
||||
// Loop until both strings are processed
|
||||
for (size_t i = 0, j = 0; (i < v1.length() || j < v2.length());)
|
||||
for (usz i = 0, j = 0; (i < v1.length() || j < v2.length());)
|
||||
{
|
||||
// Storing numeric part of version 1 in vnum1
|
||||
while (i < v1.length() && v1[i] != '.')
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "types.h"
|
||||
#include "util/types.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace utils
|
||||
|
||||
+1
-1
Submodule asmjit updated: fc251c914e...723f58581a
@@ -5,10 +5,6 @@
|
||||
<Configuration>Debug - LLVM</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug - MemLeak|x64">
|
||||
<Configuration>Debug - MemLeak</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
@@ -108,10 +104,6 @@
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - MemLeak|x64'" Label="PropertySheets">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
<Import Project="..\rpcs3_memleak.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug - LLVM|x64'" Label="PropertySheets">
|
||||
<Import Project="..\rpcs3_debug.props" />
|
||||
<Import Project="..\rpcs3_llvm.props" />
|
||||
|
||||
+22
-11
@@ -17,10 +17,12 @@ jobs:
|
||||
COMPILER: clang
|
||||
GCC:
|
||||
COMPILER: gcc
|
||||
DEPLOY_APPIMAGE: true
|
||||
variables:
|
||||
CCACHE_DIR: $(Pipeline.Workspace)/ccache
|
||||
IS_AZURE: true
|
||||
UPLOAD_COMMIT_HASH: d812f1254a1157c80fd402f94446310560f54e5f
|
||||
UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-linux"
|
||||
DEPLOY_APPIMAGE: true
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
steps:
|
||||
@@ -31,30 +33,40 @@ jobs:
|
||||
displayName: ccache
|
||||
|
||||
- bash: |
|
||||
docker pull --quiet rpcs3/rpcs3-travis-xenial:1.6
|
||||
docker pull --quiet rpcs3/rpcs3-travis-xenial:1.7
|
||||
docker run \
|
||||
-v $(pwd):/rpcs3 \
|
||||
--env-file .ci/travis.env \
|
||||
--env-file .ci/docker.env \
|
||||
-v $CCACHE_DIR:/root/.ccache \
|
||||
-v $BUILD_ARTIFACTSTAGINGDIRECTORY:/root/artifacts \
|
||||
rpcs3/rpcs3-travis-xenial:1.6 \
|
||||
rpcs3/rpcs3-travis-xenial:1.7 \
|
||||
/rpcs3/.ci/build-linux.sh
|
||||
displayName: Docker setup and build
|
||||
|
||||
- publish: $(Build.ArtifactStagingDirectory)
|
||||
condition: and(succeeded(), eq(variables['COMPILER'], 'gcc'))
|
||||
artifact: RPCS3 for Linux
|
||||
condition: succeeded()
|
||||
artifact: RPCS3 for Linux ($(COMPILER))
|
||||
|
||||
- bash: .ci/github-upload.sh
|
||||
condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(variables['COMPILER'], 'gcc'))
|
||||
displayName: Push build to GitHub
|
||||
env:
|
||||
RPCS3_TOKEN: $(RPCS3-Token)
|
||||
|
||||
- job: Windows_Build
|
||||
variables:
|
||||
COMPILER: msvc
|
||||
QT_VER: '5.14.2'
|
||||
QT_DATE: '202003291224'
|
||||
QTDIR: C:\Qt\$(QT_VER)\msvc2017_64
|
||||
QT_VER_MAIN: '5'
|
||||
QT_VER: '5.15.2'
|
||||
QT_VER_MSVC: 'msvc2019'
|
||||
QT_DATE: '202011130602'
|
||||
QTDIR: C:\Qt\$(QT_VER)\$(QT_VER_MSVC)_64
|
||||
VULKAN_VER: '1.2.154.1'
|
||||
VULKAN_SDK_SHA: 'b64471f3a720e649c1fae6535ea83b8c642655ebed1485bfdf15bf4d88f746d9'
|
||||
VULKAN_SDK: C:\VulkanSDK\$(VULKAN_VER)
|
||||
CACHE_DIR: ./cache
|
||||
UPLOAD_COMMIT_HASH: 7d09e3be30805911226241afbb14f8cdc2eb054e
|
||||
UPLOAD_REPO_FULL_NAME: "RPCS3/rpcs3-binaries-win"
|
||||
|
||||
pool:
|
||||
vmImage: "windows-latest"
|
||||
@@ -91,7 +103,6 @@ jobs:
|
||||
maximumCpuCount: true
|
||||
platform: x64
|
||||
configuration: 'Release - LLVM'
|
||||
msbuildArgs: '/p:VCToolsVersion=14.25.28610'
|
||||
displayName: Compile RPCS3
|
||||
|
||||
- bash: .ci/deploy-windows.sh
|
||||
@@ -101,7 +112,7 @@ jobs:
|
||||
condition: succeeded()
|
||||
artifact: RPCS3 for Windows
|
||||
|
||||
- bash: .ci/github-upload-windows.sh
|
||||
- bash: .ci/github-upload.sh
|
||||
condition: and(ne(variables['Build.Reason'], 'PullRequest'), eq(variables['Build.Repository.Name'], 'RPCS3/rpcs3'), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
displayName: Push build to GitHub
|
||||
env:
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 331 KiB |
@@ -0,0 +1 @@
|
||||
Cached data from GitHub API.
|
||||
+11776
File diff suppressed because one or more lines are too long
+1
-1
Submodule llvm updated: 8c02f52a12...716bb292ba
@@ -98,7 +98,6 @@ EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug - LLVM|x64 = Debug - LLVM|x64
|
||||
Debug - MemLeak|x64 = Debug - MemLeak|x64
|
||||
Debug|x64 = Debug|x64
|
||||
Release - LLVM|x64 = Release - LLVM|x64
|
||||
Release|x64 = Release|x64
|
||||
@@ -106,8 +105,6 @@ Global
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{AC40FF01-426E-4838-A317-66354CEFAE88}.Debug - LLVM|x64.ActiveCfg = Debug - LLVM|x64
|
||||
{AC40FF01-426E-4838-A317-66354CEFAE88}.Debug - LLVM|x64.Build.0 = Debug - LLVM|x64
|
||||
{AC40FF01-426E-4838-A317-66354CEFAE88}.Debug - MemLeak|x64.ActiveCfg = Debug - MemLeak|x64
|
||||
{AC40FF01-426E-4838-A317-66354CEFAE88}.Debug - MemLeak|x64.Build.0 = Debug - MemLeak|x64
|
||||
{AC40FF01-426E-4838-A317-66354CEFAE88}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{AC40FF01-426E-4838-A317-66354CEFAE88}.Debug|x64.Build.0 = Debug|x64
|
||||
{AC40FF01-426E-4838-A317-66354CEFAE88}.Release - LLVM|x64.ActiveCfg = Release - LLVM|x64
|
||||
@@ -116,8 +113,6 @@ Global
|
||||
{AC40FF01-426E-4838-A317-66354CEFAE88}.Release|x64.Build.0 = Release|x64
|
||||
{C4A10229-4712-4BD2-B63E-50D93C67A038}.Debug - LLVM|x64.ActiveCfg = Debug - LLVM|x64
|
||||
{C4A10229-4712-4BD2-B63E-50D93C67A038}.Debug - LLVM|x64.Build.0 = Debug - LLVM|x64
|
||||
{C4A10229-4712-4BD2-B63E-50D93C67A038}.Debug - MemLeak|x64.ActiveCfg = Debug - MemLeak|x64
|
||||
{C4A10229-4712-4BD2-B63E-50D93C67A038}.Debug - MemLeak|x64.Build.0 = Debug - MemLeak|x64
|
||||
{C4A10229-4712-4BD2-B63E-50D93C67A038}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{C4A10229-4712-4BD2-B63E-50D93C67A038}.Debug|x64.Build.0 = Debug|x64
|
||||
{C4A10229-4712-4BD2-B63E-50D93C67A038}.Release - LLVM|x64.ActiveCfg = Release - LLVM|x64
|
||||
@@ -125,14 +120,11 @@ Global
|
||||
{C4A10229-4712-4BD2-B63E-50D93C67A038}.Release|x64.ActiveCfg = Release|x64
|
||||
{C4A10229-4712-4BD2-B63E-50D93C67A038}.Release|x64.Build.0 = Release|x64
|
||||
{8BC303AB-25BE-4276-8E57-73F171B2D672}.Debug - LLVM|x64.ActiveCfg = Debug|x64
|
||||
{8BC303AB-25BE-4276-8E57-73F171B2D672}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{8BC303AB-25BE-4276-8E57-73F171B2D672}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8BC303AB-25BE-4276-8E57-73F171B2D672}.Release - LLVM|x64.ActiveCfg = Release|x64
|
||||
{8BC303AB-25BE-4276-8E57-73F171B2D672}.Release|x64.ActiveCfg = Release|x64
|
||||
{3384223A-6D97-4799-9862-359F85312892}.Debug - LLVM|x64.ActiveCfg = Debug - LLVM|x64
|
||||
{3384223A-6D97-4799-9862-359F85312892}.Debug - LLVM|x64.Build.0 = Debug - LLVM|x64
|
||||
{3384223A-6D97-4799-9862-359F85312892}.Debug - MemLeak|x64.ActiveCfg = Debug - MemLeak|x64
|
||||
{3384223A-6D97-4799-9862-359F85312892}.Debug - MemLeak|x64.Build.0 = Debug - MemLeak|x64
|
||||
{3384223A-6D97-4799-9862-359F85312892}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{3384223A-6D97-4799-9862-359F85312892}.Debug|x64.Build.0 = Debug|x64
|
||||
{3384223A-6D97-4799-9862-359F85312892}.Release - LLVM|x64.ActiveCfg = Release - LLVM|x64
|
||||
@@ -141,8 +133,6 @@ Global
|
||||
{3384223A-6D97-4799-9862-359F85312892}.Release|x64.Build.0 = Release|x64
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D}.Debug - LLVM|x64.ActiveCfg = Debug - LLVM|x64
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D}.Debug - LLVM|x64.Build.0 = Debug - LLVM|x64
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D}.Debug - MemLeak|x64.ActiveCfg = Debug - MemLeak|x64
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D}.Debug - MemLeak|x64.Build.0 = Debug - MemLeak|x64
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D}.Debug|x64.Build.0 = Debug|x64
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D}.Release - LLVM|x64.ActiveCfg = Release - LLVM|x64
|
||||
@@ -151,8 +141,6 @@ Global
|
||||
{78CB2F39-B809-4A06-8329-8C0A19119D3D}.Release|x64.Build.0 = Release|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Debug - LLVM|x64.ActiveCfg = Debug - LLVM|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Debug - LLVM|x64.Build.0 = Debug - LLVM|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Debug - MemLeak|x64.ActiveCfg = Debug - MemLeak|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Debug - MemLeak|x64.Build.0 = Debug - MemLeak|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Debug|x64.Build.0 = Debug|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Release - LLVM|x64.ActiveCfg = Release - LLVM|x64
|
||||
@@ -160,8 +148,6 @@ Global
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Release|x64.ActiveCfg = Release|x64
|
||||
{30A05C4D-F5FD-421C-A864-17A64BDEAA75}.Release|x64.Build.0 = Release|x64
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Debug - LLVM|x64.ActiveCfg = Release|x64
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Debug - MemLeak|x64.ActiveCfg = Release|x64
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Debug - MemLeak|x64.Build.0 = Release|x64
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Debug|x64.ActiveCfg = Release|x64
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Debug|x64.Build.0 = Release|x64
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Release - LLVM|x64.ActiveCfg = Release|x64
|
||||
@@ -170,8 +156,6 @@ Global
|
||||
{EB33566E-DA7F-4D28-9077-88C0B7C77E35}.Release|x64.Build.0 = Release|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug - LLVM|x64.ActiveCfg = Debug Library|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug - LLVM|x64.Build.0 = Debug Library|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug - MemLeak|x64.ActiveCfg = Debug Library|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug - MemLeak|x64.Build.0 = Debug Library|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug|x64.ActiveCfg = Debug Library|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Debug|x64.Build.0 = Debug Library|x64
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release - LLVM|x64.ActiveCfg = Release Library|x64
|
||||
@@ -180,8 +164,6 @@ Global
|
||||
{D6973076-9317-4EF2-A0B8-B7A18AC0713E}.Release|x64.Build.0 = Release Library|x64
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Debug - LLVM|x64.ActiveCfg = Debug|x64
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Debug - LLVM|x64.Build.0 = Debug|x64
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Debug - MemLeak|x64.Build.0 = Debug|x64
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Debug|x64.Build.0 = Debug|x64
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Release - LLVM|x64.ActiveCfg = Release|x64
|
||||
@@ -189,14 +171,11 @@ Global
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Release|x64.ActiveCfg = Release|x64
|
||||
{60F89955-91C6-3A36-8000-13C592FEC2DF}.Release|x64.Build.0 = Release|x64
|
||||
{8F85B6CC-250F-4ACA-A617-E820A74E3E3C}.Debug - LLVM|x64.ActiveCfg = Debug|x64
|
||||
{8F85B6CC-250F-4ACA-A617-E820A74E3E3C}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{8F85B6CC-250F-4ACA-A617-E820A74E3E3C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{8F85B6CC-250F-4ACA-A617-E820A74E3E3C}.Release - LLVM|x64.ActiveCfg = Release|x64
|
||||
{8F85B6CC-250F-4ACA-A617-E820A74E3E3C}.Release|x64.ActiveCfg = Release|x64
|
||||
{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}.Debug - LLVM|x64.ActiveCfg = Debug - LLVM|x64
|
||||
{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}.Debug - LLVM|x64.Build.0 = Debug - LLVM|x64
|
||||
{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}.Debug - MemLeak|x64.ActiveCfg = Debug - MemLeak|x64
|
||||
{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}.Debug - MemLeak|x64.Build.0 = Debug - MemLeak|x64
|
||||
{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}.Debug|x64.Build.0 = Debug|x64
|
||||
{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}.Release - LLVM|x64.ActiveCfg = Release - LLVM|x64
|
||||
@@ -205,8 +184,6 @@ Global
|
||||
{3EE5F075-B546-42C4-B6A8-E3CCEF38B78D}.Release|x64.Build.0 = Release|x64
|
||||
{FDC361C5-7734-493B-8CFB-037308B35122}.Debug - LLVM|x64.ActiveCfg = Debug|x64
|
||||
{FDC361C5-7734-493B-8CFB-037308B35122}.Debug - LLVM|x64.Build.0 = Debug|x64
|
||||
{FDC361C5-7734-493B-8CFB-037308B35122}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{FDC361C5-7734-493B-8CFB-037308B35122}.Debug - MemLeak|x64.Build.0 = Debug|x64
|
||||
{FDC361C5-7734-493B-8CFB-037308B35122}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{FDC361C5-7734-493B-8CFB-037308B35122}.Debug|x64.Build.0 = Debug|x64
|
||||
{FDC361C5-7734-493B-8CFB-037308B35122}.Release - LLVM|x64.ActiveCfg = Release|x64
|
||||
@@ -215,8 +192,6 @@ Global
|
||||
{FDC361C5-7734-493B-8CFB-037308B35122}.Release|x64.Build.0 = Release|x64
|
||||
{70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}.Debug - LLVM|x64.ActiveCfg = Debug - LLVM|x64
|
||||
{70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}.Debug - LLVM|x64.Build.0 = Debug - LLVM|x64
|
||||
{70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}.Debug - MemLeak|x64.Build.0 = Debug|x64
|
||||
{70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}.Debug|x64.Build.0 = Debug|x64
|
||||
{70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}.Release - LLVM|x64.ActiveCfg = Release - LLVM|x64
|
||||
@@ -225,8 +200,6 @@ Global
|
||||
{70CD65B0-91D6-4FAE-9A7B-4AF55D0D1B12}.Release|x64.Build.0 = Release|x64
|
||||
{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug - LLVM|x64.ActiveCfg = Debug|x64
|
||||
{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug - LLVM|x64.Build.0 = Debug|x64
|
||||
{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug - MemLeak|x64.Build.0 = Debug|x64
|
||||
{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Debug|x64.Build.0 = Debug|x64
|
||||
{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release - LLVM|x64.ActiveCfg = Release|x64
|
||||
@@ -235,8 +208,6 @@ Global
|
||||
{A107C21C-418A-4697-BB10-20C3AA60E2E4}.Release|x64.Build.0 = Release|x64
|
||||
{939FE206-1182-ABC3-1234-FEAB88E98404}.Debug - LLVM|x64.ActiveCfg = Debug|x64
|
||||
{939FE206-1182-ABC3-1234-FEAB88E98404}.Debug - LLVM|x64.Build.0 = Debug|x64
|
||||
{939FE206-1182-ABC3-1234-FEAB88E98404}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{939FE206-1182-ABC3-1234-FEAB88E98404}.Debug - MemLeak|x64.Build.0 = Debug|x64
|
||||
{939FE206-1182-ABC3-1234-FEAB88E98404}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{939FE206-1182-ABC3-1234-FEAB88E98404}.Debug|x64.Build.0 = Debug|x64
|
||||
{939FE206-1182-ABC3-1234-FEAB88E98404}.Release - LLVM|x64.ActiveCfg = Release|x64
|
||||
@@ -245,8 +216,6 @@ Global
|
||||
{939FE206-1182-ABC3-1234-FEAB88E98404}.Release|x64.Build.0 = Release|x64
|
||||
{349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug - LLVM|x64.ActiveCfg = Debug|x64
|
||||
{349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug - LLVM|x64.Build.0 = Debug|x64
|
||||
{349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug - MemLeak|x64.Build.0 = Debug|x64
|
||||
{349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Debug|x64.Build.0 = Debug|x64
|
||||
{349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release - LLVM|x64.ActiveCfg = Release|x64
|
||||
@@ -255,8 +224,6 @@ Global
|
||||
{349EE8F9-7D25-4909-AAF5-FF3FADE72187}.Release|x64.Build.0 = Release|x64
|
||||
{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}.Debug - LLVM|x64.ActiveCfg = Debug|x64
|
||||
{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}.Debug - LLVM|x64.Build.0 = Debug|x64
|
||||
{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}.Debug - MemLeak|x64.Build.0 = Debug|x64
|
||||
{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}.Debug|x64.Build.0 = Debug|x64
|
||||
{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}.Release - LLVM|x64.ActiveCfg = Release|x64
|
||||
@@ -265,8 +232,6 @@ Global
|
||||
{5B146DEA-9ACE-4D32-A7FD-3F42464DD69C}.Release|x64.Build.0 = Release|x64
|
||||
{73973223-5EE8-41CA-8E88-1D60E89A237B}.Debug - LLVM|x64.ActiveCfg = Debug|x64
|
||||
{73973223-5EE8-41CA-8E88-1D60E89A237B}.Debug - LLVM|x64.Build.0 = Debug|x64
|
||||
{73973223-5EE8-41CA-8E88-1D60E89A237B}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{73973223-5EE8-41CA-8E88-1D60E89A237B}.Debug - MemLeak|x64.Build.0 = Debug|x64
|
||||
{73973223-5EE8-41CA-8E88-1D60E89A237B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{73973223-5EE8-41CA-8E88-1D60E89A237B}.Debug|x64.Build.0 = Debug|x64
|
||||
{73973223-5EE8-41CA-8E88-1D60E89A237B}.Release - LLVM|x64.ActiveCfg = Release|x64
|
||||
@@ -275,8 +240,6 @@ Global
|
||||
{73973223-5EE8-41CA-8E88-1D60E89A237B}.Release|x64.Build.0 = Release|x64
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}.Debug - LLVM|x64.ActiveCfg = Debug - LLVM|x64
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}.Debug - LLVM|x64.Build.0 = Debug - LLVM|x64
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}.Debug - MemLeak|x64.Build.0 = Debug|x64
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}.Debug|x64.Build.0 = Debug|x64
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}.Release - LLVM|x64.ActiveCfg = Release - LLVM|x64
|
||||
@@ -284,7 +247,6 @@ Global
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}.Release|x64.ActiveCfg = Release|x64
|
||||
{DA6F56B4-06A4-441D-AD70-AC5A7D51FADB}.Release|x64.Build.0 = Release|x64
|
||||
{4CBD3DDD-5555-49A4-A44D-DD3D8CB516A1}.Debug - LLVM|x64.ActiveCfg = Debug|x64
|
||||
{4CBD3DDD-5555-49A4-A44D-DD3D8CB516A1}.Debug - MemLeak|x64.ActiveCfg = Debug|x64
|
||||
{4CBD3DDD-5555-49A4-A44D-DD3D8CB516A1}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{4CBD3DDD-5555-49A4-A44D-DD3D8CB516A1}.Release - LLVM|x64.ActiveCfg = Release|x64
|
||||
{4CBD3DDD-5555-49A4-A44D-DD3D8CB516A1}.Release|x64.ActiveCfg = Release|x64
|
||||
|
||||
@@ -58,7 +58,9 @@ set(RPCS3_SRC
|
||||
Input/basic_mouse_handler.cpp
|
||||
Input/ds3_pad_handler.cpp
|
||||
Input/ds4_pad_handler.cpp
|
||||
Input/dualsense_pad_handler.cpp
|
||||
Input/evdev_joystick_handler.cpp
|
||||
Input/hid_pad_handler.cpp
|
||||
Input/keyboard_pad_handler.cpp
|
||||
Input/mm_joystick_handler.cpp
|
||||
Input/pad_thread.cpp
|
||||
@@ -133,11 +135,19 @@ if(APPLE)
|
||||
${CMAKE_SOURCE_DIR}/bin/Icons $<TARGET_FILE_DIR:rpcs3>/../Resources/Icons
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/../Resources/GuiConfigs
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/bin/git $<TARGET_FILE_DIR:rpcs3>/../Resources/git
|
||||
COMMAND "${Qt5_DIR}/../../../bin/macdeployqt" "${PROJECT_BINARY_DIR}/bin/rpcs3.app")
|
||||
elseif(UNIX)
|
||||
add_custom_command(TARGET rpcs3 POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/bin/Icons $<TARGET_FILE_DIR:rpcs3>/Icons)
|
||||
add_custom_command(TARGET rpcs3 POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/bin/GuiConfigs $<TARGET_FILE_DIR:rpcs3>/GuiConfigs)
|
||||
add_custom_command(TARGET rpcs3 POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_SOURCE_DIR}/bin/git $<TARGET_FILE_DIR:rpcs3>/git)
|
||||
elseif(WIN32)
|
||||
add_custom_command(TARGET rpcs3 POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_directory "${CMAKE_SOURCE_DIR}/bin" "$<TARGET_FILE_DIR:rpcs3>"
|
||||
@@ -162,4 +172,6 @@ if(UNIX AND NOT APPLE)
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/rpcs3)
|
||||
install(DIRECTORY ../bin/GuiConfigs
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/rpcs3)
|
||||
install(DIRECTORY ../bin/git
|
||||
DESTINATION ${CMAKE_INSTALL_PREFIX}/share/rpcs3)
|
||||
endif()
|
||||
|
||||
+105
-102
@@ -3,8 +3,9 @@
|
||||
// http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
||||
|
||||
#include "utils.h"
|
||||
#include <string>
|
||||
|
||||
void bn_print(char *name, u8 *a, u32 n)
|
||||
void bn_print(char* name, u8* a, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
@@ -16,20 +17,22 @@ void bn_print(char *name, u8 *a, u32 n)
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static void bn_zero(u8 *d, u32 n)
|
||||
static void bn_zero(u8* d, u32 n)
|
||||
{
|
||||
memset(d, 0, n);
|
||||
}
|
||||
void bn_copy(u8 *d, u8 *a, u32 n)
|
||||
|
||||
void bn_copy(u8* d, u8* a, u32 n)
|
||||
{
|
||||
memcpy(d, a, n);
|
||||
}
|
||||
|
||||
int bn_compare(u8 *a, u8 *b, u32 n)
|
||||
int bn_compare(u8* a, u8* b, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
if (a[i] < b[i])
|
||||
return -1;
|
||||
if (a[i] > b[i])
|
||||
@@ -39,45 +42,47 @@ int bn_compare(u8 *a, u8 *b, u32 n)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 bn_add_1(u8 *d, u8 *a, u8 *b, u32 n)
|
||||
static u8 bn_add_1(u8* d, u8* a, u8* b, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
u32 dig;
|
||||
u8 c;
|
||||
|
||||
c = 0;
|
||||
for (i = n - 1; i < n; i--) {
|
||||
dig = a[i] + b[i] + c;
|
||||
c = dig >> 8;
|
||||
for (i = n - 1; i < n; i--)
|
||||
{
|
||||
dig = a[i] + b[i] + c;
|
||||
c = dig >> 8;
|
||||
d[i] = dig;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
static u8 bn_sub_1(u8 *d, u8 *a, u8 *b, u32 n)
|
||||
static u8 bn_sub_1(u8* d, u8* a, u8* b, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
u32 dig;
|
||||
u8 c;
|
||||
|
||||
c = 1;
|
||||
for (i = n - 1; i < n; i--) {
|
||||
dig = a[i] + 255 - b[i] + c;
|
||||
c = dig >> 8;
|
||||
for (i = n - 1; i < n; i--)
|
||||
{
|
||||
dig = a[i] + 255 - b[i] + c;
|
||||
c = dig >> 8;
|
||||
d[i] = dig;
|
||||
}
|
||||
|
||||
return 1 - c;
|
||||
}
|
||||
|
||||
void bn_reduce(u8 *d, u8 *N, u32 n)
|
||||
void bn_reduce(u8* d, u8* N, u32 n)
|
||||
{
|
||||
if (bn_compare(d, N, n) >= 0)
|
||||
bn_sub_1(d, d, N, n);
|
||||
}
|
||||
|
||||
void bn_add(u8 *d, u8 *a, u8 *b, u8 *N, u32 n)
|
||||
void bn_add(u8* d, u8* a, u8* b, u8* N, u32 n)
|
||||
{
|
||||
if (bn_add_1(d, a, b, n))
|
||||
bn_sub_1(d, d, N, n);
|
||||
@@ -85,13 +90,13 @@ void bn_add(u8 *d, u8 *a, u8 *b, u8 *N, u32 n)
|
||||
bn_reduce(d, N, n);
|
||||
}
|
||||
|
||||
void bn_sub(u8 *d, u8 *a, u8 *b, u8 *N, u32 n)
|
||||
void bn_sub(u8* d, u8* a, u8* b, u8* N, u32 n)
|
||||
{
|
||||
if (bn_sub_1(d, a, b, n))
|
||||
bn_add_1(d, d, N, n);
|
||||
}
|
||||
|
||||
static const u8 inv256[0x80] = {
|
||||
static constexpr u8 inv256[0x80] = {
|
||||
0x01, 0xab, 0xcd, 0xb7, 0x39, 0xa3, 0xc5, 0xef,
|
||||
0xf1, 0x1b, 0x3d, 0xa7, 0x29, 0x13, 0x35, 0xdf,
|
||||
0xe1, 0x8b, 0xad, 0x97, 0x19, 0x83, 0xa5, 0xcf,
|
||||
@@ -110,19 +115,20 @@ static const u8 inv256[0x80] = {
|
||||
0x11, 0x3b, 0x5d, 0xc7, 0x49, 0x33, 0x55, 0xff,
|
||||
};
|
||||
|
||||
static void bn_mon_muladd_dig(u8 *d, u8 *a, u8 b, u8 *N, u32 n)
|
||||
static void bn_mon_muladd_dig(u8* d, u8* a, u8 b, u8* N, u32 n)
|
||||
{
|
||||
u32 dig;
|
||||
u32 i;
|
||||
|
||||
u8 z = -(d[n-1] + a[n-1]*b) * inv256[N[n-1]/2];
|
||||
u8 z = -(d[n - 1] + a[n - 1] * b) * inv256[N[n - 1] / 2];
|
||||
|
||||
dig = d[n-1] + a[n-1]*b + N[n-1]*z;
|
||||
dig = d[n - 1] + a[n - 1] * b + N[n - 1] * z;
|
||||
dig >>= 8;
|
||||
|
||||
for (i = n - 2; i < n; i--) {
|
||||
dig += d[i] + a[i]*b + N[i]*z;
|
||||
d[i+1] = dig;
|
||||
for (i = n - 2; i < n; i--)
|
||||
{
|
||||
dig += d[i] + a[i] * b + N[i] * z;
|
||||
d[i + 1] = dig;
|
||||
dig >>= 8;
|
||||
}
|
||||
|
||||
@@ -135,7 +141,7 @@ static void bn_mon_muladd_dig(u8 *d, u8 *a, u8 b, u8 *N, u32 n)
|
||||
bn_reduce(d, N, n);
|
||||
}
|
||||
|
||||
void bn_mon_mul(u8 *d, u8 *a, u8 *b, u8 *N, u32 n)
|
||||
void bn_mon_mul(u8* d, u8* a, u8* b, u8* N, u32 n)
|
||||
{
|
||||
u8 t[512];
|
||||
u32 i;
|
||||
@@ -148,35 +154,36 @@ void bn_mon_mul(u8 *d, u8 *a, u8 *b, u8 *N, u32 n)
|
||||
bn_copy(d, t, n);
|
||||
}
|
||||
|
||||
void bn_to_mon(u8 *d, u8 *N, u32 n)
|
||||
void bn_to_mon(u8* d, u8* N, u32 n)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < 8*n; i++)
|
||||
for (i = 0; i < 8 * n; i++)
|
||||
bn_add(d, d, d, N, n);
|
||||
}
|
||||
|
||||
void bn_from_mon(u8 *d, u8 *N, u32 n)
|
||||
void bn_from_mon(u8* d, u8* N, u32 n)
|
||||
{
|
||||
u8 t[512];
|
||||
|
||||
bn_zero(t, n);
|
||||
t[n-1] = 1;
|
||||
t[n - 1] = 1;
|
||||
bn_mon_mul(d, d, t, N, n);
|
||||
}
|
||||
|
||||
static void bn_mon_exp(u8 *d, u8 *a, u8 *N, u32 n, u8 *e, u32 en)
|
||||
static void bn_mon_exp(u8* d, u8* a, u8* N, u32 n, u8* e, u32 en)
|
||||
{
|
||||
u8 t[512];
|
||||
u32 i;
|
||||
u8 mask;
|
||||
|
||||
bn_zero(d, n);
|
||||
d[n-1] = 1;
|
||||
d[n - 1] = 1;
|
||||
bn_to_mon(d, N, n);
|
||||
|
||||
for (i = 0; i < en; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1) {
|
||||
for (mask = 0x80; mask != 0; mask >>= 1)
|
||||
{
|
||||
bn_mon_mul(t, d, d, N, n);
|
||||
if ((e[i] & mask) != 0)
|
||||
bn_mon_mul(d, t, a, N, n);
|
||||
@@ -185,50 +192,41 @@ static void bn_mon_exp(u8 *d, u8 *a, u8 *N, u32 n, u8 *e, u32 en)
|
||||
}
|
||||
}
|
||||
|
||||
void bn_mon_inv(u8 *d, u8 *a, u8 *N, u32 n)
|
||||
void bn_mon_inv(u8* d, u8* a, u8* N, u32 n)
|
||||
{
|
||||
u8 t[512], s[512];
|
||||
|
||||
bn_zero(s, n);
|
||||
s[n-1] = 2;
|
||||
s[n - 1] = 2;
|
||||
bn_sub_1(t, N, s, n);
|
||||
bn_mon_exp(d, a, N, n, t, n);
|
||||
}
|
||||
|
||||
void bn_copy(u8 *d, u8 *a, u32 n);
|
||||
int bn_compare(u8 *a, u8 *b, u32 n);
|
||||
void bn_reduce(u8 *d, u8 *N, u32 n);
|
||||
void bn_add(u8 *d, u8 *a, u8 *b, u8 *N, u32 n);
|
||||
void bn_sub(u8 *d, u8 *a, u8 *b, u8 *N, u32 n);
|
||||
void bn_to_mon(u8 *d, u8 *N, u32 n);
|
||||
void bn_from_mon(u8 *d, u8 *N, u32 n);
|
||||
void bn_mon_mul(u8 *d, u8 *a, u8 *b, u8 *N, u32 n);
|
||||
void bn_mon_inv(u8 *d, u8 *a, u8 *N, u32 n);
|
||||
|
||||
struct point {
|
||||
struct point
|
||||
{
|
||||
u8 x[20];
|
||||
u8 y[20];
|
||||
};
|
||||
|
||||
static u8 ec_p[20];
|
||||
static u8 ec_a[20]; // mon
|
||||
static u8 ec_b[20]; // mon
|
||||
static u8 ec_N[21];
|
||||
static struct point ec_G; // mon
|
||||
static struct point ec_Q; // mon
|
||||
static u8 ec_k[21];
|
||||
static thread_local u8 ec_p[20]{};
|
||||
static thread_local u8 ec_a[20]{}; // mon
|
||||
static thread_local u8 ec_b[20]{}; // mon
|
||||
static thread_local u8 ec_N[21]{};
|
||||
static thread_local point ec_G{}; // mon
|
||||
static thread_local point ec_Q{}; // mon
|
||||
static thread_local u8 ec_k[21]{};
|
||||
|
||||
static void elt_copy(u8 *d, u8 *a)
|
||||
static void elt_copy(u8* d, u8* a)
|
||||
{
|
||||
memcpy(d, a, 20);
|
||||
}
|
||||
|
||||
static void elt_zero(u8 *d)
|
||||
static void elt_zero(u8* d)
|
||||
{
|
||||
memset(d, 0, 20);
|
||||
}
|
||||
|
||||
static int elt_is_zero(u8 *d)
|
||||
static int elt_is_zero(u8* d)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
@@ -239,47 +237,47 @@ static int elt_is_zero(u8 *d)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void elt_add(u8 *d, u8 *a, u8 *b)
|
||||
static void elt_add(u8* d, u8* a, u8* b)
|
||||
{
|
||||
bn_add(d, a, b, ec_p, 20);
|
||||
}
|
||||
|
||||
static void elt_sub(u8 *d, u8 *a, u8 *b)
|
||||
static void elt_sub(u8* d, u8* a, u8* b)
|
||||
{
|
||||
bn_sub(d, a, b, ec_p, 20);
|
||||
}
|
||||
|
||||
static void elt_mul(u8 *d, u8 *a, u8 *b)
|
||||
static void elt_mul(u8* d, u8* a, u8* b)
|
||||
{
|
||||
bn_mon_mul(d, a, b, ec_p, 20);
|
||||
}
|
||||
|
||||
static void elt_square(u8 *d, u8 *a)
|
||||
static void elt_square(u8* d, u8* a)
|
||||
{
|
||||
elt_mul(d, a, a);
|
||||
}
|
||||
|
||||
static void elt_inv(u8 *d, u8 *a)
|
||||
static void elt_inv(u8* d, u8* a)
|
||||
{
|
||||
u8 s[20];
|
||||
elt_copy(s, a);
|
||||
bn_mon_inv(d, s, ec_p, 20);
|
||||
}
|
||||
|
||||
static void point_to_mon(struct point *p)
|
||||
static void point_to_mon(point* p)
|
||||
{
|
||||
bn_to_mon(p->x, ec_p, 20);
|
||||
bn_to_mon(p->y, ec_p, 20);
|
||||
}
|
||||
|
||||
static void point_from_mon(struct point *p)
|
||||
static void point_from_mon(point* p)
|
||||
{
|
||||
bn_from_mon(p->x, ec_p, 20);
|
||||
bn_from_mon(p->y, ec_p, 20);
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int point_is_on_curve(u8 *p)
|
||||
static int point_is_on_curve(u8* p)
|
||||
{
|
||||
u8 s[20], t[20];
|
||||
u8 *x, *y;
|
||||
@@ -302,21 +300,21 @@ static int point_is_on_curve(u8 *p)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void point_zero(struct point *p)
|
||||
static void point_zero(point* p)
|
||||
{
|
||||
elt_zero(p->x);
|
||||
elt_zero(p->y);
|
||||
}
|
||||
|
||||
static int point_is_zero(struct point *p)
|
||||
static int point_is_zero(point* p)
|
||||
{
|
||||
return elt_is_zero(p->x) && elt_is_zero(p->y);
|
||||
}
|
||||
|
||||
static void point_double(struct point *r, struct point *p)
|
||||
static void point_double(point* r, point* p)
|
||||
{
|
||||
u8 s[20], t[20];
|
||||
struct point pp;
|
||||
point pp;
|
||||
u8 *px, *py, *rx, *ry;
|
||||
|
||||
pp = *p;
|
||||
@@ -326,33 +324,34 @@ static void point_double(struct point *r, struct point *p)
|
||||
rx = r->x;
|
||||
ry = r->y;
|
||||
|
||||
if (elt_is_zero(py)) {
|
||||
if (elt_is_zero(py))
|
||||
{
|
||||
point_zero(r);
|
||||
return;
|
||||
}
|
||||
|
||||
elt_square(t, px); // t = px*px
|
||||
elt_add(s, t, t); // s = 2*px*px
|
||||
elt_add(s, s, t); // s = 3*px*px
|
||||
elt_add(s, s, ec_a); // s = 3*px*px + a
|
||||
elt_add(t, py, py); // t = 2*py
|
||||
elt_inv(t, t); // t = 1/(2*py)
|
||||
elt_mul(s, s, t); // s = (3*px*px+a)/(2*py)
|
||||
elt_square(t, px); // t = px*px
|
||||
elt_add(s, t, t); // s = 2*px*px
|
||||
elt_add(s, s, t); // s = 3*px*px
|
||||
elt_add(s, s, ec_a); // s = 3*px*px + a
|
||||
elt_add(t, py, py); // t = 2*py
|
||||
elt_inv(t, t); // t = 1/(2*py)
|
||||
elt_mul(s, s, t); // s = (3*px*px+a)/(2*py)
|
||||
|
||||
elt_square(rx, s); // rx = s*s
|
||||
elt_add(t, px, px); // t = 2*px
|
||||
elt_sub(rx, rx, t); // rx = s*s - 2*px
|
||||
elt_square(rx, s); // rx = s*s
|
||||
elt_add(t, px, px); // t = 2*px
|
||||
elt_sub(rx, rx, t); // rx = s*s - 2*px
|
||||
|
||||
elt_sub(t, px, rx); // t = -(rx-px)
|
||||
elt_mul(ry, s, t); // ry = -s*(rx-px)
|
||||
elt_sub(ry, ry, py); // ry = -s*(rx-px) - py
|
||||
elt_sub(t, px, rx); // t = -(rx-px)
|
||||
elt_mul(ry, s, t); // ry = -s*(rx-px)
|
||||
elt_sub(ry, ry, py); // ry = -s*(rx-px) - py
|
||||
}
|
||||
|
||||
static void point_add(struct point *r, struct point *p, struct point *q)
|
||||
static void point_add(point* r, point* p, point* q)
|
||||
{
|
||||
u8 s[20], t[20], u[20];
|
||||
u8 *px, *py, *qx, *qy, *rx, *ry;
|
||||
struct point pp, qq;
|
||||
point pp, qq;
|
||||
|
||||
pp = *p;
|
||||
qq = *q;
|
||||
@@ -364,13 +363,15 @@ static void point_add(struct point *r, struct point *p, struct point *q)
|
||||
rx = r->x;
|
||||
ry = r->y;
|
||||
|
||||
if (point_is_zero(&pp)) {
|
||||
if (point_is_zero(&pp))
|
||||
{
|
||||
elt_copy(rx, qx);
|
||||
elt_copy(ry, qy);
|
||||
return;
|
||||
}
|
||||
|
||||
if (point_is_zero(&qq)) {
|
||||
if (point_is_zero(&qq))
|
||||
{
|
||||
elt_copy(rx, px);
|
||||
elt_copy(ry, py);
|
||||
return;
|
||||
@@ -378,7 +379,8 @@ static void point_add(struct point *r, struct point *p, struct point *q)
|
||||
|
||||
elt_sub(u, qx, px);
|
||||
|
||||
if (elt_is_zero(u)) {
|
||||
if (elt_is_zero(u))
|
||||
{
|
||||
elt_sub(u, qy, py);
|
||||
if (elt_is_zero(u))
|
||||
point_double(r, &pp);
|
||||
@@ -388,20 +390,20 @@ static void point_add(struct point *r, struct point *p, struct point *q)
|
||||
return;
|
||||
}
|
||||
|
||||
elt_inv(t, u); // t = 1/(qx-px)
|
||||
elt_sub(u, qy, py); // u = qy-py
|
||||
elt_mul(s, t, u); // s = (qy-py)/(qx-px)
|
||||
elt_inv(t, u); // t = 1/(qx-px)
|
||||
elt_sub(u, qy, py); // u = qy-py
|
||||
elt_mul(s, t, u); // s = (qy-py)/(qx-px)
|
||||
|
||||
elt_square(rx, s); // rx = s*s
|
||||
elt_add(t, px, qx); // t = px+qx
|
||||
elt_sub(rx, rx, t); // rx = s*s - (px+qx)
|
||||
elt_square(rx, s); // rx = s*s
|
||||
elt_add(t, px, qx); // t = px+qx
|
||||
elt_sub(rx, rx, t); // rx = s*s - (px+qx)
|
||||
|
||||
elt_sub(t, px, rx); // t = -(rx-px)
|
||||
elt_mul(ry, s, t); // ry = -s*(rx-px)
|
||||
elt_sub(ry, ry, py); // ry = -s*(rx-px) - py
|
||||
elt_sub(t, px, rx); // t = -(rx-px)
|
||||
elt_mul(ry, s, t); // ry = -s*(rx-px)
|
||||
elt_sub(ry, ry, py); // ry = -s*(rx-px) - py
|
||||
}
|
||||
|
||||
static void point_mul(struct point *d, u8 *a, struct point *b) // a is bignum
|
||||
static void point_mul(point* d, u8* a, point* b) // a is bignum
|
||||
{
|
||||
u32 i;
|
||||
u8 mask;
|
||||
@@ -409,14 +411,15 @@ static void point_mul(struct point *d, u8 *a, struct point *b) // a is bignum
|
||||
point_zero(d);
|
||||
|
||||
for (i = 0; i < 21; i++)
|
||||
for (mask = 0x80; mask != 0; mask >>= 1) {
|
||||
for (mask = 0x80; mask != 0; mask >>= 1)
|
||||
{
|
||||
point_double(d, d);
|
||||
if ((a[i] & mask) != 0)
|
||||
point_add(d, d, b);
|
||||
}
|
||||
}
|
||||
|
||||
static int check_ecdsa(struct point *Q, u8 *R, u8 *S, u8 *hash)
|
||||
static int check_ecdsa(struct point* Q, u8* R, u8* S, u8* hash)
|
||||
{
|
||||
u8 Sinv[21];
|
||||
u8 e[21];
|
||||
@@ -458,13 +461,13 @@ static int check_ecdsa(struct point *Q, u8 *R, u8 *S, u8 *hash)
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void ec_priv_to_pub(u8 *k, u8 *Q)
|
||||
static void ec_priv_to_pub(u8* k, u8* Q)
|
||||
{
|
||||
point_mul(Q, k, ec_G);
|
||||
}
|
||||
#endif
|
||||
|
||||
int ecdsa_set_curve(u8* p, u8* a, u8* b, u8* N, u8* Gx, u8* Gy)
|
||||
int ecdsa_set_curve(const u8* p, const u8* a, const u8* b, const u8* N, const u8* Gx, const u8* Gy)
|
||||
{
|
||||
memcpy(ec_p, p, 20);
|
||||
memcpy(ec_a, a, 20);
|
||||
@@ -481,19 +484,19 @@ int ecdsa_set_curve(u8* p, u8* a, u8* b, u8* N, u8* Gx, u8* Gy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ecdsa_set_pub(u8 *Q)
|
||||
void ecdsa_set_pub(const u8* Q)
|
||||
{
|
||||
memcpy(ec_Q.x, Q, 20);
|
||||
memcpy(ec_Q.y, Q+20, 20);
|
||||
point_to_mon(&ec_Q);
|
||||
}
|
||||
|
||||
void ecdsa_set_priv(u8 *k)
|
||||
void ecdsa_set_priv(const u8* k)
|
||||
{
|
||||
memcpy(ec_k, k, sizeof ec_k);
|
||||
}
|
||||
|
||||
int ecdsa_verify(u8 *hash, u8 *R, u8 *S)
|
||||
int ecdsa_verify(u8* hash, u8* R, u8* S)
|
||||
{
|
||||
return check_ecdsa(&ec_Q, R, S, hash);
|
||||
}
|
||||
|
||||
+5
-6
@@ -4,10 +4,9 @@
|
||||
// Licensed under the terms of the GNU GPL, version 3
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "util/types.hpp"
|
||||
|
||||
int ecdsa_set_curve(unsigned char *p, unsigned char *a, unsigned char *b, unsigned char *N, unsigned char *Gx, unsigned char *Gy);
|
||||
void ecdsa_set_pub(unsigned char *Q);
|
||||
void ecdsa_set_priv(unsigned char *k);
|
||||
int ecdsa_verify(unsigned char *hash, unsigned char *R, unsigned char *S);
|
||||
int ecdsa_set_curve(const u8* p, const u8* a, const u8* b, const u8* N, const u8* Gx, const u8* Gy);
|
||||
void ecdsa_set_pub(const u8* Q);
|
||||
void ecdsa_set_priv(const u8* k);
|
||||
int ecdsa_verify(u8* hash, u8* R, u8* S);
|
||||
|
||||
+817
-815
File diff suppressed because it is too large
Load Diff
+221
-221
@@ -1,221 +1,221 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
enum SELF_KEY_TYPE
|
||||
{
|
||||
KEY_LV0 = 1,
|
||||
KEY_LV1,
|
||||
KEY_LV2,
|
||||
KEY_APP,
|
||||
KEY_ISO,
|
||||
KEY_LDR,
|
||||
KEY_UNK7,
|
||||
KEY_NPDRM
|
||||
};
|
||||
|
||||
struct SELF_KEY
|
||||
{
|
||||
u64 version_start;
|
||||
u64 version_end;
|
||||
u16 revision;
|
||||
u32 self_type;
|
||||
u8 erk[0x20];
|
||||
u8 riv[0x10];
|
||||
u8 pub[0x28];
|
||||
u8 priv[0x15];
|
||||
u32 curve_type;
|
||||
|
||||
SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, const std::string& e, const std::string& r, const std::string& pb, const std::string& pr, u32 ct);
|
||||
};
|
||||
|
||||
static u8 PKG_AES_KEY_IDU[0x10] = {
|
||||
0x5d, 0xb9, 0x11, 0xe6, 0xb7, 0xe5, 0x0a, 0x7d, 0x32, 0x15, 0x38, 0xfd, 0x7c, 0x66, 0xf1, 0x7b
|
||||
};
|
||||
|
||||
static u8 PKG_AES_KEY[0x10] = {
|
||||
0x2e, 0x7b, 0x71, 0xd7, 0xc9, 0xc9, 0xa1, 0x4e, 0xa3, 0x22, 0x1f, 0x18, 0x88, 0x28, 0xb8, 0xf8
|
||||
};
|
||||
|
||||
static u8 PKG_AES_KEY2[0x10] = {
|
||||
0x07, 0xf2, 0xc6, 0x82, 0x90, 0xb5, 0x0d, 0x2c, 0x33, 0x81, 0x8d, 0x70, 0x9b, 0x60, 0xe6, 0x2b
|
||||
};
|
||||
|
||||
static u8 PKG_AES_KEY_VITA_1[0x10] = {
|
||||
0xE3, 0x1A, 0x70, 0xC9, 0xCE, 0x1D, 0xD7, 0x2B, 0xF3, 0xC0, 0x62, 0x29, 0x63, 0xF2, 0xEC, 0xCB
|
||||
};
|
||||
|
||||
static u8 PKG_AES_KEY_VITA_2[0x10] = {
|
||||
0x42, 0x3A, 0xCA, 0x3A, 0x2B, 0xD5, 0x64, 0x9F, 0x96, 0x86, 0xAB, 0xAD, 0x6F, 0xD8, 0x80, 0x1F
|
||||
};
|
||||
|
||||
static u8 PKG_AES_KEY_VITA_3[0x10] = {
|
||||
0xAF, 0x07, 0xFD, 0x59, 0x65, 0x25, 0x27, 0xBA, 0xF1, 0x33, 0x89, 0x66, 0x8B, 0x17, 0xD9, 0xEA
|
||||
};
|
||||
|
||||
static u8 NP_IDPS[0x10] = {
|
||||
0x5E, 0x06, 0xE0, 0x4F, 0xD9, 0x4A, 0x71, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||
};
|
||||
|
||||
static u8 NP_KLIC_FREE[0x10] = {
|
||||
0x72, 0xF9, 0x90, 0x78, 0x8F, 0x9C, 0xFF, 0x74, 0x57, 0x25, 0xF0, 0x8E, 0x4C, 0x12, 0x83, 0x87
|
||||
};
|
||||
|
||||
static u8 NP_OMAC_KEY_2[0x10] = {
|
||||
0x6B, 0xA5, 0x29, 0x76, 0xEF, 0xDA, 0x16, 0xEF, 0x3C, 0x33, 0x9F, 0xB2, 0x97, 0x1E, 0x25, 0x6B
|
||||
};
|
||||
|
||||
static u8 NP_OMAC_KEY_3[0x10] = {
|
||||
0x9B, 0x51, 0x5F, 0xEA, 0xCF, 0x75, 0x06, 0x49, 0x81, 0xAA, 0x60, 0x4D, 0x91, 0xA5, 0x4E, 0x97
|
||||
};
|
||||
|
||||
static u8 NP_KLIC_KEY[0x10] = {
|
||||
0xF2, 0xFB, 0xCA, 0x7A, 0x75, 0xB0, 0x4E, 0xDC, 0x13, 0x90, 0x63, 0x8C, 0xCD, 0xFD, 0xD1, 0xEE
|
||||
};
|
||||
|
||||
static u8 NP_RIF_KEY[0x10] = {
|
||||
0xDA, 0x7D, 0x4B, 0x5E, 0x49, 0x9A, 0x4F, 0x53, 0xB1, 0xC1, 0xA1, 0x4A, 0x74, 0x84, 0x44, 0x3B
|
||||
};
|
||||
|
||||
// PSP Minis
|
||||
static u8 NP_PSP_KEY_1[0x10] = {
|
||||
0x2A, 0x6A, 0xFB, 0xCF, 0x43, 0xD1, 0x57, 0x9F, 0x7D, 0x73, 0x87, 0x41, 0xA1, 0x3B, 0xD4, 0x2E
|
||||
};
|
||||
|
||||
// PSP Remasters
|
||||
static u8 NP_PSP_KEY_2[0x10] = {
|
||||
0x0D, 0xB8, 0x57, 0x32, 0x36, 0x6C, 0xD7, 0x34, 0xFC, 0x87, 0x9E, 0x74, 0x33, 0x43, 0xBB, 0x4F
|
||||
};
|
||||
|
||||
static u8 NP_PSX_KEY[0x10] = {
|
||||
0x52, 0xC0, 0xB5, 0xCA, 0x76, 0xD6, 0x13, 0x4B, 0xB4, 0x5F, 0xC6, 0x6C, 0xA6, 0x37, 0xF2, 0xC1
|
||||
};
|
||||
|
||||
static u8 RAP_KEY[0x10] = {
|
||||
0x86, 0x9F, 0x77, 0x45, 0xC1, 0x3F, 0xD8, 0x90, 0xCC, 0xF2, 0x91, 0x88, 0xE3, 0xCC, 0x3E, 0xDF
|
||||
};
|
||||
|
||||
static u8 RAP_PBOX[0x10] = {
|
||||
0x0C, 0x03, 0x06, 0x04, 0x01, 0x0B, 0x0F, 0x08, 0x02, 0x07, 0x00, 0x05, 0x0A, 0x0E, 0x0D, 0x09
|
||||
};
|
||||
|
||||
static u8 RAP_E1[0x10] = {
|
||||
0xA9, 0x3E, 0x1F, 0xD6, 0x7C, 0x55, 0xA3, 0x29, 0xB7, 0x5F, 0xDD, 0xA6, 0x2A, 0x95, 0xC7, 0xA5
|
||||
};
|
||||
|
||||
static u8 RAP_E2[0x10] = {
|
||||
0x67, 0xD4, 0x5D, 0xA3, 0x29, 0x6D, 0x00, 0x6A, 0x4E, 0x7C, 0x53, 0x7B, 0xF5, 0x53, 0x8C, 0x74
|
||||
};
|
||||
|
||||
static u8 SDAT_KEY[0x10] = {
|
||||
0x0D, 0x65, 0x5E, 0xF8, 0xE6, 0x74, 0xA9, 0x8A, 0xB8, 0x50, 0x5C, 0xFA, 0x7D, 0x01, 0x29, 0x33
|
||||
};
|
||||
|
||||
static u8 EDAT_KEY_0[0x10] = {
|
||||
0xBE, 0x95, 0x9C, 0xA8, 0x30, 0x8D, 0xEF, 0xA2, 0xE5, 0xE1, 0x80, 0xC6, 0x37, 0x12, 0xA9, 0xAE
|
||||
};
|
||||
|
||||
static u8 EDAT_HASH_0[0x10] = {
|
||||
0xEF, 0xFE, 0x5B, 0xD1, 0x65, 0x2E, 0xEB, 0xC1, 0x19, 0x18, 0xCF, 0x7C, 0x04, 0xD4, 0xF0, 0x11
|
||||
};
|
||||
|
||||
static u8 EDAT_KEY_1[0x10] = {
|
||||
0x4C, 0xA9, 0xC1, 0x4B, 0x01, 0xC9, 0x53, 0x09, 0x96, 0x9B, 0xEC, 0x68, 0xAA, 0x0B, 0xC0, 0x81
|
||||
};
|
||||
|
||||
static u8 EDAT_HASH_1[0x10] = {
|
||||
0x3D, 0x92, 0x69, 0x9B, 0x70, 0x5B, 0x07, 0x38, 0x54, 0xD8, 0xFC, 0xC6, 0xC7, 0x67, 0x27, 0x47
|
||||
};
|
||||
|
||||
static u8 EDAT_IV[0x10] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
static u8 VSH_CURVE_P[0x14] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
static u8 VSH_CURVE_A[0x14] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC
|
||||
};
|
||||
|
||||
static u8 VSH_CURVE_B[0x14] = {
|
||||
0xA6, 0x8B, 0xED, 0xC3, 0x34, 0x18, 0x02, 0x9C, 0x1D, 0x3C, 0xE3, 0x3B, 0x9A, 0x32, 0x1F, 0xCC, 0xBB, 0x9E, 0x0F, 0x0B
|
||||
};
|
||||
|
||||
static u8 VSH_CURVE_N[0x15] = {
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xB5, 0xAE, 0x3C, 0x52, 0x3E, 0x63, 0x94, 0x4F, 0x21, 0x27
|
||||
};
|
||||
|
||||
static u8 VSH_CURVE_GX[0x14] = {
|
||||
0x12, 0x8E, 0xC4, 0x25, 0x64, 0x87, 0xFD, 0x8F, 0xDF, 0x64, 0xE2, 0x43, 0x7B, 0xC0, 0xA1, 0xF6, 0xD5, 0xAF, 0xDE, 0x2C
|
||||
};
|
||||
|
||||
static u8 VSH_CURVE_GY[0x14] = {
|
||||
0x59, 0x58, 0x55, 0x7E, 0xB1, 0xDB, 0x00, 0x12, 0x60, 0x42, 0x55, 0x24, 0xDB, 0xC3, 0x79, 0xD5, 0xAC, 0x5F, 0x4A, 0xDF
|
||||
};
|
||||
|
||||
static u8 VSH_PUB[0x28] = {
|
||||
0x62, 0x27, 0xB0, 0x0A, 0x02, 0x85, 0x6F, 0xB0, 0x41, 0x08, 0x87, 0x67, 0x19, 0xE0, 0xA0, 0x18, 0x32, 0x91, 0xEE, 0xB9,
|
||||
0x6E, 0x73, 0x6A, 0xBF, 0x81, 0xF7, 0x0E, 0xE9, 0x16, 0x1B, 0x0D, 0xDE, 0xB0, 0x26, 0x76, 0x1A, 0xFF, 0x7B, 0xC8, 0x5B
|
||||
};
|
||||
|
||||
static u8 SCEPKG_RIV[0x10] = {
|
||||
0x4A, 0xCE, 0xF0, 0x12, 0x24, 0xFB, 0xEE, 0xDF, 0x82, 0x45, 0xF8, 0xFF, 0x10, 0x21, 0x1E, 0x6E
|
||||
};
|
||||
|
||||
static u8 SCEPKG_ERK[0x20] = {
|
||||
0xA9, 0x78, 0x18, 0xBD, 0x19, 0x3A, 0x67, 0xA1, 0x6F, 0xE8, 0x3A, 0x85, 0x5E, 0x1B, 0xE9, 0xFB, 0x56, 0x40, 0x93, 0x8D,
|
||||
0x4D, 0xBC, 0xB2, 0xCB, 0x52, 0xC5, 0xA2, 0xF8, 0xB0, 0x2B, 0x10, 0x31
|
||||
};
|
||||
|
||||
static u8 PUP_KEY[0x40] = {
|
||||
0xF4, 0x91, 0xAD, 0x94, 0xC6, 0x81, 0x10, 0x96, 0x91, 0x5F, 0xD5, 0xD2, 0x44, 0x81, 0xAE, 0xDC, 0xED, 0xED, 0xBE, 0x6B,
|
||||
0xE5, 0x13, 0x72, 0x4D, 0xD8, 0xF7, 0xB6, 0x91, 0xE8, 0x8A, 0x38, 0xF4, 0xB5, 0x16, 0x2B, 0xFB, 0xEC, 0xBE, 0x3A, 0x62,
|
||||
0x18, 0x5D, 0xD7, 0xC9, 0x4D, 0xA2, 0x22, 0x5A, 0xDA, 0x3F, 0xBF, 0xCE, 0x55, 0x5B, 0x9E, 0xA9, 0x64, 0x98, 0x29, 0xEB,
|
||||
0x30, 0xCE, 0x83, 0x66
|
||||
};
|
||||
|
||||
class KeyVault
|
||||
{
|
||||
std::vector<SELF_KEY> sk_LV0_arr;
|
||||
std::vector<SELF_KEY> sk_LV1_arr;
|
||||
std::vector<SELF_KEY> sk_LV2_arr;
|
||||
std::vector<SELF_KEY> sk_APP_arr;
|
||||
std::vector<SELF_KEY> sk_ISO_arr;
|
||||
std::vector<SELF_KEY> sk_LDR_arr;
|
||||
std::vector<SELF_KEY> sk_UNK7_arr;
|
||||
std::vector<SELF_KEY> sk_NPDRM_arr;
|
||||
std::unique_ptr<u8[]> klicensee_key;
|
||||
|
||||
public:
|
||||
KeyVault();
|
||||
SELF_KEY FindSelfKey(u32 type, u16 revision, u64 version);
|
||||
void SetKlicenseeKey(u8 *key);
|
||||
u8 *GetKlicenseeKey();
|
||||
|
||||
private:
|
||||
void LoadSelfLV0Keys();
|
||||
void LoadSelfLDRKeys();
|
||||
void LoadSelfLV1Keys();
|
||||
void LoadSelfLV2Keys();
|
||||
void LoadSelfISOKeys();
|
||||
void LoadSelfAPPKeys();
|
||||
void LoadSelfUNK7Keys();
|
||||
void LoadSelfNPDRMKeys();
|
||||
SELF_KEY GetSelfLV0Key() const;
|
||||
SELF_KEY GetSelfLDRKey() const;
|
||||
SELF_KEY GetSelfLV1Key(u64 version) const;
|
||||
SELF_KEY GetSelfLV2Key(u64 version) const;
|
||||
SELF_KEY GetSelfISOKey(u16 revision, u64 version) const;
|
||||
SELF_KEY GetSelfAPPKey(u16 revision) const;
|
||||
SELF_KEY GetSelfUNK7Key(u64 version) const;
|
||||
SELF_KEY GetSelfNPDRMKey(u16 revision) const;
|
||||
};
|
||||
|
||||
// RAP to RIF function.
|
||||
void rap_to_rif(unsigned char* rap, unsigned char* rif);
|
||||
#pragma once
|
||||
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
enum SELF_KEY_TYPE
|
||||
{
|
||||
KEY_LV0 = 1,
|
||||
KEY_LV1,
|
||||
KEY_LV2,
|
||||
KEY_APP,
|
||||
KEY_ISO,
|
||||
KEY_LDR,
|
||||
KEY_UNK7,
|
||||
KEY_NPDRM
|
||||
};
|
||||
|
||||
struct SELF_KEY
|
||||
{
|
||||
u64 version_start;
|
||||
u64 version_end;
|
||||
u16 revision;
|
||||
u32 self_type;
|
||||
u8 erk[0x20];
|
||||
u8 riv[0x10];
|
||||
u8 pub[0x28];
|
||||
u8 priv[0x15];
|
||||
u32 curve_type;
|
||||
|
||||
SELF_KEY(u64 ver_start, u64 ver_end, u16 rev, u32 type, const std::string& e, const std::string& r, const std::string& pb, const std::string& pr, u32 ct);
|
||||
};
|
||||
|
||||
constexpr u8 PKG_AES_KEY_IDU[0x10] = {
|
||||
0x5d, 0xb9, 0x11, 0xe6, 0xb7, 0xe5, 0x0a, 0x7d, 0x32, 0x15, 0x38, 0xfd, 0x7c, 0x66, 0xf1, 0x7b
|
||||
};
|
||||
|
||||
constexpr u8 PKG_AES_KEY[0x10] = {
|
||||
0x2e, 0x7b, 0x71, 0xd7, 0xc9, 0xc9, 0xa1, 0x4e, 0xa3, 0x22, 0x1f, 0x18, 0x88, 0x28, 0xb8, 0xf8
|
||||
};
|
||||
|
||||
constexpr u8 PKG_AES_KEY2[0x10] = {
|
||||
0x07, 0xf2, 0xc6, 0x82, 0x90, 0xb5, 0x0d, 0x2c, 0x33, 0x81, 0x8d, 0x70, 0x9b, 0x60, 0xe6, 0x2b
|
||||
};
|
||||
|
||||
constexpr u8 PKG_AES_KEY_VITA_1[0x10] = {
|
||||
0xE3, 0x1A, 0x70, 0xC9, 0xCE, 0x1D, 0xD7, 0x2B, 0xF3, 0xC0, 0x62, 0x29, 0x63, 0xF2, 0xEC, 0xCB
|
||||
};
|
||||
|
||||
constexpr u8 PKG_AES_KEY_VITA_2[0x10] = {
|
||||
0x42, 0x3A, 0xCA, 0x3A, 0x2B, 0xD5, 0x64, 0x9F, 0x96, 0x86, 0xAB, 0xAD, 0x6F, 0xD8, 0x80, 0x1F
|
||||
};
|
||||
|
||||
constexpr u8 PKG_AES_KEY_VITA_3[0x10] = {
|
||||
0xAF, 0x07, 0xFD, 0x59, 0x65, 0x25, 0x27, 0xBA, 0xF1, 0x33, 0x89, 0x66, 0x8B, 0x17, 0xD9, 0xEA
|
||||
};
|
||||
|
||||
constexpr u8 NP_IDPS[0x10] = {
|
||||
0x5E, 0x06, 0xE0, 0x4F, 0xD9, 0x4A, 0x71, 0xBF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01
|
||||
};
|
||||
|
||||
constexpr u8 NP_KLIC_FREE[0x10] = {
|
||||
0x72, 0xF9, 0x90, 0x78, 0x8F, 0x9C, 0xFF, 0x74, 0x57, 0x25, 0xF0, 0x8E, 0x4C, 0x12, 0x83, 0x87
|
||||
};
|
||||
|
||||
constexpr u8 NP_OMAC_KEY_2[0x10] = {
|
||||
0x6B, 0xA5, 0x29, 0x76, 0xEF, 0xDA, 0x16, 0xEF, 0x3C, 0x33, 0x9F, 0xB2, 0x97, 0x1E, 0x25, 0x6B
|
||||
};
|
||||
|
||||
constexpr u8 NP_OMAC_KEY_3[0x10] = {
|
||||
0x9B, 0x51, 0x5F, 0xEA, 0xCF, 0x75, 0x06, 0x49, 0x81, 0xAA, 0x60, 0x4D, 0x91, 0xA5, 0x4E, 0x97
|
||||
};
|
||||
|
||||
constexpr u8 NP_KLIC_KEY[0x10] = {
|
||||
0xF2, 0xFB, 0xCA, 0x7A, 0x75, 0xB0, 0x4E, 0xDC, 0x13, 0x90, 0x63, 0x8C, 0xCD, 0xFD, 0xD1, 0xEE
|
||||
};
|
||||
|
||||
constexpr u8 NP_RIF_KEY[0x10] = {
|
||||
0xDA, 0x7D, 0x4B, 0x5E, 0x49, 0x9A, 0x4F, 0x53, 0xB1, 0xC1, 0xA1, 0x4A, 0x74, 0x84, 0x44, 0x3B
|
||||
};
|
||||
|
||||
// PSP Minis
|
||||
constexpr u8 NP_PSP_KEY_1[0x10] = {
|
||||
0x2A, 0x6A, 0xFB, 0xCF, 0x43, 0xD1, 0x57, 0x9F, 0x7D, 0x73, 0x87, 0x41, 0xA1, 0x3B, 0xD4, 0x2E
|
||||
};
|
||||
|
||||
// PSP Remasters
|
||||
constexpr u8 NP_PSP_KEY_2[0x10] = {
|
||||
0x0D, 0xB8, 0x57, 0x32, 0x36, 0x6C, 0xD7, 0x34, 0xFC, 0x87, 0x9E, 0x74, 0x33, 0x43, 0xBB, 0x4F
|
||||
};
|
||||
|
||||
constexpr u8 NP_PSX_KEY[0x10] = {
|
||||
0x52, 0xC0, 0xB5, 0xCA, 0x76, 0xD6, 0x13, 0x4B, 0xB4, 0x5F, 0xC6, 0x6C, 0xA6, 0x37, 0xF2, 0xC1
|
||||
};
|
||||
|
||||
constexpr u8 RAP_KEY[0x10] = {
|
||||
0x86, 0x9F, 0x77, 0x45, 0xC1, 0x3F, 0xD8, 0x90, 0xCC, 0xF2, 0x91, 0x88, 0xE3, 0xCC, 0x3E, 0xDF
|
||||
};
|
||||
|
||||
constexpr u8 RAP_PBOX[0x10] = {
|
||||
0x0C, 0x03, 0x06, 0x04, 0x01, 0x0B, 0x0F, 0x08, 0x02, 0x07, 0x00, 0x05, 0x0A, 0x0E, 0x0D, 0x09
|
||||
};
|
||||
|
||||
constexpr u8 RAP_E1[0x10] = {
|
||||
0xA9, 0x3E, 0x1F, 0xD6, 0x7C, 0x55, 0xA3, 0x29, 0xB7, 0x5F, 0xDD, 0xA6, 0x2A, 0x95, 0xC7, 0xA5
|
||||
};
|
||||
|
||||
constexpr u8 RAP_E2[0x10] = {
|
||||
0x67, 0xD4, 0x5D, 0xA3, 0x29, 0x6D, 0x00, 0x6A, 0x4E, 0x7C, 0x53, 0x7B, 0xF5, 0x53, 0x8C, 0x74
|
||||
};
|
||||
|
||||
constexpr u8 SDAT_KEY[0x10] = {
|
||||
0x0D, 0x65, 0x5E, 0xF8, 0xE6, 0x74, 0xA9, 0x8A, 0xB8, 0x50, 0x5C, 0xFA, 0x7D, 0x01, 0x29, 0x33
|
||||
};
|
||||
|
||||
constexpr u8 EDAT_KEY_0[0x10] = {
|
||||
0xBE, 0x95, 0x9C, 0xA8, 0x30, 0x8D, 0xEF, 0xA2, 0xE5, 0xE1, 0x80, 0xC6, 0x37, 0x12, 0xA9, 0xAE
|
||||
};
|
||||
|
||||
constexpr u8 EDAT_HASH_0[0x10] = {
|
||||
0xEF, 0xFE, 0x5B, 0xD1, 0x65, 0x2E, 0xEB, 0xC1, 0x19, 0x18, 0xCF, 0x7C, 0x04, 0xD4, 0xF0, 0x11
|
||||
};
|
||||
|
||||
constexpr u8 EDAT_KEY_1[0x10] = {
|
||||
0x4C, 0xA9, 0xC1, 0x4B, 0x01, 0xC9, 0x53, 0x09, 0x96, 0x9B, 0xEC, 0x68, 0xAA, 0x0B, 0xC0, 0x81
|
||||
};
|
||||
|
||||
constexpr u8 EDAT_HASH_1[0x10] = {
|
||||
0x3D, 0x92, 0x69, 0x9B, 0x70, 0x5B, 0x07, 0x38, 0x54, 0xD8, 0xFC, 0xC6, 0xC7, 0x67, 0x27, 0x47
|
||||
};
|
||||
|
||||
constexpr u8 EDAT_IV[0x10] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
constexpr u8 VSH_CURVE_P[0x14] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
constexpr u8 VSH_CURVE_A[0x14] = {
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC
|
||||
};
|
||||
|
||||
constexpr u8 VSH_CURVE_B[0x14] = {
|
||||
0xA6, 0x8B, 0xED, 0xC3, 0x34, 0x18, 0x02, 0x9C, 0x1D, 0x3C, 0xE3, 0x3B, 0x9A, 0x32, 0x1F, 0xCC, 0xBB, 0x9E, 0x0F, 0x0B
|
||||
};
|
||||
|
||||
constexpr u8 VSH_CURVE_N[0x15] = {
|
||||
0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xB5, 0xAE, 0x3C, 0x52, 0x3E, 0x63, 0x94, 0x4F, 0x21, 0x27
|
||||
};
|
||||
|
||||
constexpr u8 VSH_CURVE_GX[0x14] = {
|
||||
0x12, 0x8E, 0xC4, 0x25, 0x64, 0x87, 0xFD, 0x8F, 0xDF, 0x64, 0xE2, 0x43, 0x7B, 0xC0, 0xA1, 0xF6, 0xD5, 0xAF, 0xDE, 0x2C
|
||||
};
|
||||
|
||||
constexpr u8 VSH_CURVE_GY[0x14] = {
|
||||
0x59, 0x58, 0x55, 0x7E, 0xB1, 0xDB, 0x00, 0x12, 0x60, 0x42, 0x55, 0x24, 0xDB, 0xC3, 0x79, 0xD5, 0xAC, 0x5F, 0x4A, 0xDF
|
||||
};
|
||||
|
||||
constexpr u8 VSH_PUB[0x28] = {
|
||||
0x62, 0x27, 0xB0, 0x0A, 0x02, 0x85, 0x6F, 0xB0, 0x41, 0x08, 0x87, 0x67, 0x19, 0xE0, 0xA0, 0x18, 0x32, 0x91, 0xEE, 0xB9,
|
||||
0x6E, 0x73, 0x6A, 0xBF, 0x81, 0xF7, 0x0E, 0xE9, 0x16, 0x1B, 0x0D, 0xDE, 0xB0, 0x26, 0x76, 0x1A, 0xFF, 0x7B, 0xC8, 0x5B
|
||||
};
|
||||
|
||||
constexpr u8 SCEPKG_RIV[0x10] = {
|
||||
0x4A, 0xCE, 0xF0, 0x12, 0x24, 0xFB, 0xEE, 0xDF, 0x82, 0x45, 0xF8, 0xFF, 0x10, 0x21, 0x1E, 0x6E
|
||||
};
|
||||
|
||||
constexpr u8 SCEPKG_ERK[0x20] = {
|
||||
0xA9, 0x78, 0x18, 0xBD, 0x19, 0x3A, 0x67, 0xA1, 0x6F, 0xE8, 0x3A, 0x85, 0x5E, 0x1B, 0xE9, 0xFB, 0x56, 0x40, 0x93, 0x8D,
|
||||
0x4D, 0xBC, 0xB2, 0xCB, 0x52, 0xC5, 0xA2, 0xF8, 0xB0, 0x2B, 0x10, 0x31
|
||||
};
|
||||
|
||||
constexpr u8 PUP_KEY[0x40] = {
|
||||
0xF4, 0x91, 0xAD, 0x94, 0xC6, 0x81, 0x10, 0x96, 0x91, 0x5F, 0xD5, 0xD2, 0x44, 0x81, 0xAE, 0xDC, 0xED, 0xED, 0xBE, 0x6B,
|
||||
0xE5, 0x13, 0x72, 0x4D, 0xD8, 0xF7, 0xB6, 0x91, 0xE8, 0x8A, 0x38, 0xF4, 0xB5, 0x16, 0x2B, 0xFB, 0xEC, 0xBE, 0x3A, 0x62,
|
||||
0x18, 0x5D, 0xD7, 0xC9, 0x4D, 0xA2, 0x22, 0x5A, 0xDA, 0x3F, 0xBF, 0xCE, 0x55, 0x5B, 0x9E, 0xA9, 0x64, 0x98, 0x29, 0xEB,
|
||||
0x30, 0xCE, 0x83, 0x66
|
||||
};
|
||||
|
||||
class KeyVault
|
||||
{
|
||||
std::vector<SELF_KEY> sk_LV0_arr;
|
||||
std::vector<SELF_KEY> sk_LV1_arr;
|
||||
std::vector<SELF_KEY> sk_LV2_arr;
|
||||
std::vector<SELF_KEY> sk_APP_arr;
|
||||
std::vector<SELF_KEY> sk_ISO_arr;
|
||||
std::vector<SELF_KEY> sk_LDR_arr;
|
||||
std::vector<SELF_KEY> sk_UNK7_arr;
|
||||
std::vector<SELF_KEY> sk_NPDRM_arr;
|
||||
std::unique_ptr<u8[]> klicensee_key;
|
||||
|
||||
public:
|
||||
KeyVault();
|
||||
SELF_KEY FindSelfKey(u32 type, u16 revision, u64 version);
|
||||
void SetKlicenseeKey(u8* key);
|
||||
u8* GetKlicenseeKey();
|
||||
|
||||
private:
|
||||
void LoadSelfLV0Keys();
|
||||
void LoadSelfLDRKeys();
|
||||
void LoadSelfLV1Keys();
|
||||
void LoadSelfLV2Keys();
|
||||
void LoadSelfISOKeys();
|
||||
void LoadSelfAPPKeys();
|
||||
void LoadSelfUNK7Keys();
|
||||
void LoadSelfNPDRMKeys();
|
||||
SELF_KEY GetSelfLV0Key() const;
|
||||
SELF_KEY GetSelfLDRKey() const;
|
||||
SELF_KEY GetSelfLV1Key(u64 version) const;
|
||||
SELF_KEY GetSelfLV2Key(u64 version) const;
|
||||
SELF_KEY GetSelfISOKey(u16 revision, u64 version) const;
|
||||
SELF_KEY GetSelfAPPKey(u16 revision) const;
|
||||
SELF_KEY GetSelfUNK7Key(u64 version) const;
|
||||
SELF_KEY GetSelfNPDRMKey(u16 revision) const;
|
||||
};
|
||||
|
||||
// RAP to RIF function.
|
||||
void rap_to_rif(unsigned char* rap, unsigned char* rif);
|
||||
|
||||
+65
-56
@@ -1,10 +1,15 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "key_vault.h"
|
||||
#include "unedat.h"
|
||||
#include "sha1.h"
|
||||
#include "lz.h"
|
||||
#include "ec.h"
|
||||
|
||||
#include "Utilities/mutex.h"
|
||||
#include <cmath>
|
||||
|
||||
#include "util/asm.hpp"
|
||||
|
||||
LOG_CHANNEL(edat_log, "EDAT");
|
||||
|
||||
// Static variables are being modified concurrently in ec.cpp, for now use a mutex
|
||||
@@ -13,12 +18,14 @@ static shared_mutex ec_mtx;
|
||||
void generate_key(int crypto_mode, int version, unsigned char *key_final, unsigned char *iv_final, unsigned char *key, unsigned char *iv)
|
||||
{
|
||||
int mode = crypto_mode & 0xF0000000;
|
||||
uchar temp_iv[16]{};
|
||||
switch (mode)
|
||||
{
|
||||
case 0x10000000:
|
||||
// Encrypted ERK.
|
||||
// Decrypt the key with EDAT_KEY + EDAT_IV and copy the original IV.
|
||||
aescbc128_decrypt(version ? EDAT_KEY_1 : EDAT_KEY_0, EDAT_IV, key, key_final, 0x10);
|
||||
memcpy(temp_iv, EDAT_IV, 0x10);
|
||||
aescbc128_decrypt(const_cast<u8*>(version ? EDAT_KEY_1 : EDAT_KEY_0), temp_iv, key, key_final, 0x10);
|
||||
memcpy(iv_final, iv, 0x10);
|
||||
break;
|
||||
case 0x20000000:
|
||||
@@ -39,12 +46,14 @@ void generate_key(int crypto_mode, int version, unsigned char *key_final, unsign
|
||||
void generate_hash(int hash_mode, int version, unsigned char *hash_final, unsigned char *hash)
|
||||
{
|
||||
int mode = hash_mode & 0xF0000000;
|
||||
uchar temp_iv[16]{};
|
||||
switch (mode)
|
||||
{
|
||||
case 0x10000000:
|
||||
// Encrypted HASH.
|
||||
// Decrypt the hash with EDAT_KEY + EDAT_IV.
|
||||
aescbc128_decrypt(version ? EDAT_KEY_1 : EDAT_KEY_0, EDAT_IV, hash, hash_final, 0x10);
|
||||
memcpy(temp_iv, EDAT_IV, 0x10);
|
||||
aescbc128_decrypt(const_cast<u8*>(version ? EDAT_KEY_1 : EDAT_KEY_0), temp_iv, hash, hash_final, 0x10);
|
||||
break;
|
||||
case 0x20000000:
|
||||
// Default HASH.
|
||||
@@ -135,15 +144,15 @@ std::tuple<u64, s32, s32> dec_section(unsigned char* metadata)
|
||||
return std::make_tuple(offset, length, compression_end);
|
||||
}
|
||||
|
||||
v128 get_block_key(int block, NPD_HEADER *npd)
|
||||
u128 get_block_key(int block, NPD_HEADER *npd)
|
||||
{
|
||||
unsigned char empty_key[0x10] = {};
|
||||
unsigned char *src_key = (npd->version <= 1) ? empty_key : npd->dev_hash;
|
||||
v128 dest_key{};
|
||||
memcpy(dest_key._bytes, src_key, 0xC);
|
||||
u128 dest_key{};
|
||||
std::memcpy(&dest_key, src_key, 0xC);
|
||||
|
||||
s32 swappedBlock = swap32(block);
|
||||
memcpy(&dest_key._bytes[0xC], &swappedBlock, sizeof(swappedBlock));
|
||||
std::memcpy(reinterpret_cast<uchar*>(&dest_key) + 0xC, &swappedBlock, sizeof(swappedBlock));
|
||||
return dest_key;
|
||||
}
|
||||
|
||||
@@ -248,7 +257,7 @@ s64 decrypt_block(const fs::file* in, u8* out, EDAT_HEADER *edat, NPD_HEADER *np
|
||||
auto b_key = get_block_key(block_num, npd);
|
||||
|
||||
// Encrypt the block key with the crypto key.
|
||||
aesecb128_encrypt(crypt_key, b_key._bytes, key_result);
|
||||
aesecb128_encrypt(crypt_key, reinterpret_cast<uchar*>(&b_key), key_result);
|
||||
if ((edat->flags & EDAT_FLAG_0x10) != 0)
|
||||
aesecb128_encrypt(crypt_key, key_result, hash); // If FLAG 0x10 is set, encrypt again to get the final hash.
|
||||
else
|
||||
@@ -537,7 +546,7 @@ int check_data(unsigned char *key, EDAT_HEADER *edat, NPD_HEADER *npd, const fs:
|
||||
return 0;
|
||||
}
|
||||
|
||||
int validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
|
||||
bool validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
|
||||
{
|
||||
unsigned char dev[0x60] = { 0 };
|
||||
|
||||
@@ -553,30 +562,31 @@ int validate_dev_klic(const u8* klicensee, NPD_HEADER *npd)
|
||||
memcpy(dev + 0xC, &type, 4);
|
||||
|
||||
// Check for an empty dev_hash (can't validate if devklic is NULL);
|
||||
auto klic = v128::loadu(klicensee);
|
||||
u128 klic;
|
||||
std::memcpy(&klic, klicensee, sizeof(klic));
|
||||
|
||||
if (klic == v128{})
|
||||
if (!klic)
|
||||
{
|
||||
// Allow empty dev hash.
|
||||
return 1;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Generate klicensee xor key.
|
||||
auto key = klic ^ std::bit_cast<v128>(NP_OMAC_KEY_2);
|
||||
|
||||
// Hash with generated key and compare with dev_hash.
|
||||
return cmac_hash_compare(key._bytes, 0x10, dev, 0x60, npd->dev_hash, 0x10);
|
||||
}
|
||||
// Generate klicensee xor key.
|
||||
u128 key = klic ^ std::bit_cast<u128>(NP_OMAC_KEY_2);
|
||||
|
||||
// Hash with generated key and compare with dev_hash.
|
||||
return cmac_hash_compare(reinterpret_cast<uchar*>(&key), 0x10, dev, 0x60, npd->dev_hash, 0x10);
|
||||
}
|
||||
|
||||
int validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER *npd, bool verbose)
|
||||
bool validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER *npd, bool verbose)
|
||||
{
|
||||
int title_hash_result = 0;
|
||||
int dev_hash_result = 0;
|
||||
if (!file_name)
|
||||
{
|
||||
fmt::throw_exception("Empty filename");
|
||||
}
|
||||
|
||||
const s32 file_name_length = ::narrow<s32>(std::strlen(file_name), HERE);
|
||||
const std::size_t buf_len = 0x30 + file_name_length;
|
||||
const usz file_name_length = std::strlen(file_name);
|
||||
const usz buf_len = 0x30 + file_name_length;
|
||||
|
||||
std::unique_ptr<u8[]> buf(new u8[buf_len]);
|
||||
std::unique_ptr<u8[]> buf_lower(new u8[buf_len]);
|
||||
@@ -589,7 +599,7 @@ int validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER *
|
||||
std::memcpy(buf_lower.get(), buf.get(), buf_len);
|
||||
std::memcpy(buf_upper.get(), buf.get(), buf_len);
|
||||
|
||||
for (std::size_t i = std::basic_string_view<u8>(buf.get() + 0x30, file_name_length).find_last_of('.'); i < buf_len; i++)
|
||||
for (usz i = std::basic_string_view<u8>(buf.get() + 0x30, file_name_length).find_last_of('.'); i < buf_len; i++)
|
||||
{
|
||||
const u8 c = static_cast<u8>(buf[i]);
|
||||
buf_upper[i] = std::toupper(c);
|
||||
@@ -598,10 +608,10 @@ int validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER *
|
||||
|
||||
// Hash with NPDRM_OMAC_KEY_3 and compare with title_hash.
|
||||
// Try to ignore case sensivity with file extension
|
||||
title_hash_result =
|
||||
cmac_hash_compare(NP_OMAC_KEY_3, 0x10, buf.get(), buf_len, npd->title_hash, 0x10) ||
|
||||
cmac_hash_compare(NP_OMAC_KEY_3, 0x10, buf_lower.get(), buf_len, npd->title_hash, 0x10) ||
|
||||
cmac_hash_compare(NP_OMAC_KEY_3, 0x10, buf_upper.get(), buf_len, npd->title_hash, 0x10);
|
||||
const bool title_hash_result =
|
||||
cmac_hash_compare(const_cast<u8*>(NP_OMAC_KEY_3), 0x10, buf.get(), buf_len, npd->title_hash, 0x10) ||
|
||||
cmac_hash_compare(const_cast<u8*>(NP_OMAC_KEY_3), 0x10, buf_lower.get(), buf_len, npd->title_hash, 0x10) ||
|
||||
cmac_hash_compare(const_cast<u8*>(NP_OMAC_KEY_3), 0x10, buf_upper.get(), buf_len, npd->title_hash, 0x10);
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
@@ -611,10 +621,9 @@ int validate_npd_hashes(const char* file_name, const u8* klicensee, NPD_HEADER *
|
||||
edat_log.warning("EDAT: NPD title hash is invalid!");
|
||||
}
|
||||
|
||||
const bool dev_hash_result = validate_dev_klic(klicensee, npd);
|
||||
|
||||
dev_hash_result = validate_dev_klic(klicensee, npd);
|
||||
|
||||
return (title_hash_result && dev_hash_result);
|
||||
return title_hash_result && dev_hash_result;
|
||||
}
|
||||
|
||||
void read_npd_edat_header(const fs::file* input, NPD_HEADER& NPD, EDAT_HEADER& EDAT)
|
||||
@@ -665,7 +674,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
}
|
||||
|
||||
// Set decryption key.
|
||||
v128 key{};
|
||||
u128 key{};
|
||||
|
||||
// Check EDAT/SDAT flag.
|
||||
if ((EDAT.flags & SDAT_FLAG) == SDAT_FLAG)
|
||||
@@ -679,7 +688,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
}
|
||||
|
||||
// Generate SDAT key.
|
||||
key = std::bit_cast<v128>(NPD.dev_hash) ^ std::bit_cast<v128>(SDAT_KEY);
|
||||
key = std::bit_cast<u128>(NPD.dev_hash) ^ std::bit_cast<u128>(SDAT_KEY);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -712,7 +721,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
memcpy(&key, rifkey, 0x10);
|
||||
|
||||
// Make sure we don't have an empty RIF key.
|
||||
if (key == v128{})
|
||||
if (!key)
|
||||
{
|
||||
edat_log.error("EDAT: A valid RAP file is needed for this EDAT file! (local activation)");
|
||||
return 1;
|
||||
@@ -723,7 +732,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
memcpy(&key, rifkey, 0x10);
|
||||
|
||||
// Make sure we don't have an empty RIF key.
|
||||
if (key == v128{})
|
||||
if (!key)
|
||||
{
|
||||
edat_log.error("EDAT: A valid RAP file is needed for this EDAT file! (network activation)");
|
||||
return 1;
|
||||
@@ -732,7 +741,7 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
be_t<v128> data;
|
||||
be_t<u128> data;
|
||||
|
||||
std::memcpy(&data, devklic, sizeof(data));
|
||||
edat_log.notice("DEVKLIC: %s", data);
|
||||
@@ -743,18 +752,18 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
edat_log.notice("DECRYPTION KEY: %s", std::bit_cast<be_t<v128>>(key));
|
||||
edat_log.notice("DECRYPTION KEY: %s", std::bit_cast<be_t<u128>>(key));
|
||||
}
|
||||
|
||||
input->seek(0);
|
||||
if (check_data(key._bytes, &EDAT, &NPD, input, verbose))
|
||||
if (check_data(reinterpret_cast<uchar*>(&key), &EDAT, &NPD, input, verbose))
|
||||
{
|
||||
edat_log.error("EDAT: Data parsing failed!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
input->seek(0);
|
||||
if (decrypt_data(input, output, &EDAT, &NPD, key._bytes, verbose))
|
||||
if (decrypt_data(input, output, &EDAT, &NPD, reinterpret_cast<uchar*>(&key), verbose))
|
||||
{
|
||||
edat_log.error("EDAT: Data decryption failed!");
|
||||
return 1;
|
||||
@@ -763,14 +772,14 @@ bool extract_all_data(const fs::file* input, const fs::file* output, const char*
|
||||
return 0;
|
||||
}
|
||||
|
||||
v128 GetEdatRifKeyFromRapFile(const fs::file& rap_file)
|
||||
u128 GetEdatRifKeyFromRapFile(const fs::file& rap_file)
|
||||
{
|
||||
v128 rapkey{};
|
||||
v128 rifkey{};
|
||||
u128 rapkey{};
|
||||
u128 rifkey{};
|
||||
|
||||
rap_file.read<v128>(rapkey);
|
||||
rap_file.read<u128>(rapkey);
|
||||
|
||||
rap_to_rif(rapkey._bytes, rifkey._bytes);
|
||||
rap_to_rif(reinterpret_cast<uchar*>(&rapkey), reinterpret_cast<uchar*>(&rifkey));
|
||||
|
||||
return rifkey;
|
||||
}
|
||||
@@ -821,8 +830,8 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
|
||||
input.seek(0);
|
||||
|
||||
// Set keys (RIF and DEVKLIC).
|
||||
v128 rifKey{};
|
||||
v128 devklic{};
|
||||
u128 rifKey{};
|
||||
u128 devklic{};
|
||||
|
||||
// Select the EDAT key mode.
|
||||
switch (mode)
|
||||
@@ -876,7 +885,7 @@ fs::file DecryptEDAT(const fs::file& input, const std::string& input_file_name,
|
||||
|
||||
// Delete the bad output file if any errors arise.
|
||||
fs::file output = fs::make_stream<std::vector<u8>>();
|
||||
if (extract_all_data(&input, &output, input_file_name.c_str(), devklic._bytes, rifKey._bytes, verbose))
|
||||
if (extract_all_data(&input, &output, input_file_name.c_str(), reinterpret_cast<uchar*>(&devklic), reinterpret_cast<uchar*>(&rifKey), verbose))
|
||||
{
|
||||
output.release();
|
||||
return fs::file{};
|
||||
@@ -902,12 +911,12 @@ bool EDATADecrypter::ReadHeader()
|
||||
if ((edatHeader.flags & SDAT_FLAG) == SDAT_FLAG)
|
||||
{
|
||||
// Generate SDAT key.
|
||||
dec_key = std::bit_cast<v128>(npdHeader.dev_hash) ^ std::bit_cast<v128>(SDAT_KEY);
|
||||
dec_key = std::bit_cast<u128>(npdHeader.dev_hash) ^ std::bit_cast<u128>(SDAT_KEY);
|
||||
}
|
||||
else
|
||||
{
|
||||
// verify key
|
||||
if (validate_dev_klic(dev_key._bytes, &npdHeader) == 0)
|
||||
if (!validate_dev_klic(reinterpret_cast<uchar*>(&dev_key), &npdHeader))
|
||||
{
|
||||
edat_log.error("EDAT: Failed validating klic");
|
||||
return false;
|
||||
@@ -920,16 +929,16 @@ bool EDATADecrypter::ReadHeader()
|
||||
{
|
||||
dec_key = std::move(rif_key);
|
||||
|
||||
if (dec_key == v128{})
|
||||
if (!dec_key)
|
||||
{
|
||||
edat_log.warning("EDAT: Empty Dec key for local actívation!");
|
||||
edat_log.warning("EDAT: Empty Dec key for local activation!");
|
||||
}
|
||||
}
|
||||
else if ((npdHeader.license & 0x1) == 0x1) // Type 1: Use network activation.
|
||||
{
|
||||
dec_key = std::move(rif_key);
|
||||
|
||||
if (dec_key == v128{})
|
||||
if (!dec_key)
|
||||
{
|
||||
edat_log.warning("EDAT: Empty Dec key for network activation!");
|
||||
}
|
||||
@@ -947,7 +956,7 @@ bool EDATADecrypter::ReadHeader()
|
||||
}*/
|
||||
|
||||
file_size = edatHeader.file_size;
|
||||
total_blocks = ::aligned_div(edatHeader.file_size, edatHeader.block_size);
|
||||
total_blocks = utils::aligned_div(edatHeader.file_size, edatHeader.block_size);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -960,7 +969,7 @@ u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
|
||||
// now we need to offset things to account for the actual 'range' requested
|
||||
const u64 startOffset = pos % edatHeader.block_size;
|
||||
|
||||
const u32 num_blocks = static_cast<u32>(::aligned_div(startOffset + size, edatHeader.block_size));
|
||||
const u32 num_blocks = static_cast<u32>(utils::aligned_div(startOffset + size, edatHeader.block_size));
|
||||
const u64 bufSize = num_blocks*edatHeader.block_size;
|
||||
if (data_buf_size < (bufSize))
|
||||
{
|
||||
@@ -975,7 +984,7 @@ u64 EDATADecrypter::ReadData(u64 pos, u8* data, u64 size)
|
||||
for (u32 i = starting_block; i < ending_block; ++i)
|
||||
{
|
||||
edata_file.seek(0);
|
||||
u64 res = decrypt_block(&edata_file, &data_buf[writeOffset], &edatHeader, &npdHeader, dec_key._bytes, i, total_blocks, edatHeader.file_size);
|
||||
u64 res = decrypt_block(&edata_file, &data_buf[writeOffset], &edatHeader, &npdHeader, reinterpret_cast<uchar*>(&dec_key), i, total_blocks, edatHeader.file_size);
|
||||
if (res == umax)
|
||||
{
|
||||
edat_log.error("Error Decrypting data");
|
||||
|
||||
+10
-9
@@ -4,7 +4,6 @@
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include "Utilities/BEType.h"
|
||||
#include "Utilities/File.h"
|
||||
|
||||
constexpr u32 SDAT_FLAG = 0x01000000;
|
||||
@@ -17,8 +16,8 @@ constexpr u32 EDAT_DEBUG_DATA_FLAG = 0x80000000;
|
||||
|
||||
struct loaded_npdrm_keys
|
||||
{
|
||||
atomic_t<v128> devKlic{};
|
||||
atomic_t<v128> rifKey{};
|
||||
atomic_t<u128> devKlic{};
|
||||
atomic_t<u128> rifKey{};
|
||||
atomic_t<u32> npdrm_fds{0};
|
||||
};
|
||||
|
||||
@@ -48,7 +47,7 @@ extern fs::file DecryptEDAT(const fs::file& input, const std::string& input_file
|
||||
|
||||
extern bool VerifyEDATHeaderWithKLicense(const fs::file& input, const std::string& input_file_name, const u8* custom_klic, std::string* contentID);
|
||||
|
||||
v128 GetEdatRifKeyFromRapFile(const fs::file& rap_file);
|
||||
u128 GetEdatRifKeyFromRapFile(const fs::file& rap_file);
|
||||
|
||||
struct EDATADecrypter final : fs::file_base
|
||||
{
|
||||
@@ -65,18 +64,20 @@ struct EDATADecrypter final : fs::file_base
|
||||
std::unique_ptr<u8[]> data_buf;
|
||||
u64 data_buf_size{0};
|
||||
|
||||
v128 dec_key{};
|
||||
u128 dec_key{};
|
||||
|
||||
// edat usage
|
||||
v128 rif_key{};
|
||||
v128 dev_key{};
|
||||
u128 rif_key{};
|
||||
u128 dev_key{};
|
||||
public:
|
||||
// SdataByFd usage
|
||||
EDATADecrypter(fs::file&& input)
|
||||
: edata_file(std::move(input)) {}
|
||||
// Edat usage
|
||||
EDATADecrypter(fs::file&& input, const v128& dev_key, const v128& rif_key)
|
||||
: edata_file(std::move(input)), rif_key(rif_key), dev_key(dev_key) {}
|
||||
EDATADecrypter(fs::file&& input, const u128& dev_key, const u128& rif_key)
|
||||
: edata_file(std::move(input))
|
||||
, rif_key(rif_key)
|
||||
, dev_key(dev_key) {}
|
||||
|
||||
~EDATADecrypter() override {}
|
||||
// false if invalid
|
||||
|
||||
+284
-262
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "aes.h"
|
||||
#include "sha1.h"
|
||||
#include "key_vault.h"
|
||||
@@ -20,7 +20,7 @@ package_reader::package_reader(const std::string& path)
|
||||
return;
|
||||
}
|
||||
|
||||
filelist.emplace_back(fs::file{ path });
|
||||
m_filelist.emplace_back(fs::file{path});
|
||||
|
||||
m_is_valid = read_header();
|
||||
|
||||
@@ -30,6 +30,18 @@ package_reader::package_reader(const std::string& path)
|
||||
}
|
||||
|
||||
m_is_valid = read_metadata();
|
||||
|
||||
if (!m_is_valid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const bool param_sfo_found = read_param_sfo();
|
||||
|
||||
if (!param_sfo_found)
|
||||
{
|
||||
pkg_log.notice("PKG does not contain a PARAM.SFO");
|
||||
}
|
||||
}
|
||||
|
||||
package_reader::~package_reader()
|
||||
@@ -38,34 +50,34 @@ package_reader::~package_reader()
|
||||
|
||||
bool package_reader::read_header()
|
||||
{
|
||||
if (m_path.empty() || filelist.empty())
|
||||
if (m_path.empty() || m_filelist.empty())
|
||||
{
|
||||
pkg_log.error("Reading PKG header: no file to read!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (archive_read(&header, sizeof(header)) != sizeof(header))
|
||||
if (archive_read(&m_header, sizeof(m_header)) != sizeof(m_header))
|
||||
{
|
||||
pkg_log.error("Reading PKG header: file is too short!");
|
||||
return false;
|
||||
}
|
||||
|
||||
pkg_log.notice("Header: pkg_magic = 0x%x = \"%s\"", +header.pkg_magic, std::string(reinterpret_cast<const char*>(&header.pkg_magic), 4));
|
||||
pkg_log.notice("Header: pkg_type = 0x%x = %d", header.pkg_type, header.pkg_type);
|
||||
pkg_log.notice("Header: pkg_platform = 0x%x = %d", header.pkg_platform, header.pkg_platform);
|
||||
pkg_log.notice("Header: meta_offset = 0x%x = %d", header.meta_offset, header.meta_offset);
|
||||
pkg_log.notice("Header: meta_count = 0x%x = %d", header.meta_count, header.meta_count);
|
||||
pkg_log.notice("Header: meta_size = 0x%x = %d", header.meta_size, header.meta_size);
|
||||
pkg_log.notice("Header: file_count = 0x%x = %d", header.file_count, header.file_count);
|
||||
pkg_log.notice("Header: pkg_size = 0x%x = %d", header.pkg_size, header.pkg_size);
|
||||
pkg_log.notice("Header: data_offset = 0x%x = %d", header.data_offset, header.data_offset);
|
||||
pkg_log.notice("Header: data_size = 0x%x = %d", header.data_size, header.data_size);
|
||||
pkg_log.notice("Header: title_id = %s", header.title_id);
|
||||
pkg_log.notice("Header: qa_digest = 0x%x 0x%x", header.qa_digest[0], header.qa_digest[1]);
|
||||
pkg_log.notice("Header: pkg_magic = 0x%x = \"%s\"", +m_header.pkg_magic, std::string(reinterpret_cast<const char*>(&m_header.pkg_magic), 4));
|
||||
pkg_log.notice("Header: pkg_type = 0x%x = %d", m_header.pkg_type, m_header.pkg_type);
|
||||
pkg_log.notice("Header: pkg_platform = 0x%x = %d", m_header.pkg_platform, m_header.pkg_platform);
|
||||
pkg_log.notice("Header: meta_offset = 0x%x = %d", m_header.meta_offset, m_header.meta_offset);
|
||||
pkg_log.notice("Header: meta_count = 0x%x = %d", m_header.meta_count, m_header.meta_count);
|
||||
pkg_log.notice("Header: meta_size = 0x%x = %d", m_header.meta_size, m_header.meta_size);
|
||||
pkg_log.notice("Header: file_count = 0x%x = %d", m_header.file_count, m_header.file_count);
|
||||
pkg_log.notice("Header: pkg_size = 0x%x = %d", m_header.pkg_size, m_header.pkg_size);
|
||||
pkg_log.notice("Header: data_offset = 0x%x = %d", m_header.data_offset, m_header.data_offset);
|
||||
pkg_log.notice("Header: data_size = 0x%x = %d", m_header.data_size, m_header.data_size);
|
||||
pkg_log.notice("Header: title_id = %s", m_header.title_id);
|
||||
pkg_log.notice("Header: qa_digest = 0x%x 0x%x", m_header.qa_digest[0], m_header.qa_digest[1]);
|
||||
//pkg_log.notice("Header: klicensee = 0x%x = %d", header.klicensee, header.klicensee);
|
||||
|
||||
// Get extended PKG information for PSP or PSVita
|
||||
if (header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA)
|
||||
if (m_header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA)
|
||||
{
|
||||
PKGExtHeader ext_header;
|
||||
|
||||
@@ -90,13 +102,13 @@ bool package_reader::read_header()
|
||||
//pkg_log.notice("Extended header: padding2 = 0x%x = %d", ext_header.padding2, ext_header.padding2);
|
||||
}
|
||||
|
||||
if (header.pkg_magic != std::bit_cast<le_t<u32>>("\x7FPKG"_u32))
|
||||
if (m_header.pkg_magic != std::bit_cast<le_t<u32>>("\x7FPKG"_u32))
|
||||
{
|
||||
pkg_log.error("Not a PKG file!");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (const u16 type = header.pkg_type)
|
||||
switch (const u16 type = m_header.pkg_type)
|
||||
{
|
||||
case PKG_RELEASE_TYPE_DEBUG: break;
|
||||
case PKG_RELEASE_TYPE_RELEASE: break;
|
||||
@@ -107,7 +119,7 @@ bool package_reader::read_header()
|
||||
}
|
||||
}
|
||||
|
||||
switch (const u16 platform = header.pkg_platform)
|
||||
switch (const u16 platform = m_header.pkg_platform)
|
||||
{
|
||||
case PKG_PLATFORM_TYPE_PS3: break;
|
||||
case PKG_PLATFORM_TYPE_PSP_PSVITA: break;
|
||||
@@ -118,20 +130,20 @@ bool package_reader::read_header()
|
||||
}
|
||||
}
|
||||
|
||||
if (header.pkg_size > filelist[0].size())
|
||||
if (m_header.pkg_size > m_filelist[0].size())
|
||||
{
|
||||
// Check if multi-files pkg
|
||||
if (!m_path.ends_with("_00.pkg"))
|
||||
{
|
||||
pkg_log.error("PKG file size mismatch (pkg_size=0x%llx)", header.pkg_size);
|
||||
pkg_log.error("PKG file size mismatch (pkg_size=0x%llx)", m_header.pkg_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string name_wo_number = m_path.substr(0, m_path.size() - 7);
|
||||
u64 cursize = filelist[0].size();
|
||||
while (cursize < header.pkg_size)
|
||||
u64 cursize = m_filelist[0].size();
|
||||
while (cursize < m_header.pkg_size)
|
||||
{
|
||||
const std::string archive_filename = fmt::format("%s_%02d.pkg", name_wo_number, filelist.size());
|
||||
const std::string archive_filename = fmt::format("%s_%02d.pkg", name_wo_number, m_filelist.size());
|
||||
|
||||
fs::file archive_file(archive_filename);
|
||||
if (!archive_file)
|
||||
@@ -141,13 +153,13 @@ bool package_reader::read_header()
|
||||
}
|
||||
|
||||
cursize += archive_file.size();
|
||||
filelist.emplace_back(std::move(archive_file));
|
||||
m_filelist.emplace_back(std::move(archive_file));
|
||||
}
|
||||
}
|
||||
|
||||
if (header.data_size + header.data_offset > header.pkg_size)
|
||||
if (m_header.data_size + m_header.data_offset > m_header.pkg_size)
|
||||
{
|
||||
pkg_log.error("PKG data size mismatch (data_size=0x%llx, data_offset=0x%llx, file_size=0x%llx)", header.data_size, header.data_offset, header.pkg_size);
|
||||
pkg_log.error("PKG data size mismatch (data_size=0x%llx, data_offset=0x%llx, file_size=0x%llx)", m_header.data_size, m_header.data_offset, m_header.pkg_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -156,21 +168,21 @@ bool package_reader::read_header()
|
||||
|
||||
bool package_reader::read_metadata()
|
||||
{
|
||||
if (!m_is_valid)
|
||||
if (!decrypt_data())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read title ID and use it as an installation directory
|
||||
install_dir.resize(9);
|
||||
m_install_dir.resize(9);
|
||||
archive_seek(55);
|
||||
archive_read(&install_dir.front(), install_dir.size());
|
||||
archive_read(&m_install_dir.front(), m_install_dir.size());
|
||||
|
||||
// Read package metadata
|
||||
|
||||
archive_seek(header.meta_offset);
|
||||
archive_seek(m_header.meta_offset);
|
||||
|
||||
for (u32 i = 0; i < header.meta_count; i++)
|
||||
for (u32 i = 0; i < m_header.meta_count; i++)
|
||||
{
|
||||
struct packet_T
|
||||
{
|
||||
@@ -185,40 +197,38 @@ bool package_reader::read_metadata()
|
||||
{
|
||||
case 0x1:
|
||||
{
|
||||
if (packet.size == sizeof(metadata.drm_type))
|
||||
if (packet.size == sizeof(m_metadata.drm_type))
|
||||
{
|
||||
archive_read(&metadata.drm_type, sizeof(metadata.drm_type));
|
||||
pkg_log.notice("Metadata: DRM Type = 0x%x = %d", metadata.drm_type, metadata.drm_type);
|
||||
archive_read(&m_metadata.drm_type, sizeof(m_metadata.drm_type));
|
||||
pkg_log.notice("Metadata: DRM Type = 0x%x = %d", m_metadata.drm_type, m_metadata.drm_type);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
pkg_log.error("Metadata: DRM Type size mismatch (0x%x)", packet.size);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 0x2:
|
||||
{
|
||||
if (packet.size == sizeof(metadata.content_type))
|
||||
if (packet.size == sizeof(m_metadata.content_type))
|
||||
{
|
||||
archive_read(&metadata.content_type, sizeof(metadata.content_type));
|
||||
pkg_log.notice("Metadata: Content Type = 0x%x = %d", metadata.content_type, metadata.content_type);
|
||||
archive_read(&m_metadata.content_type, sizeof(m_metadata.content_type));
|
||||
pkg_log.notice("Metadata: Content Type = 0x%x = %d", m_metadata.content_type, m_metadata.content_type);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
pkg_log.error("Metadata: Content Type size mismatch (0x%x)", packet.size);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 0x3:
|
||||
{
|
||||
if (packet.size == sizeof(metadata.package_type))
|
||||
if (packet.size == sizeof(m_metadata.package_type))
|
||||
{
|
||||
archive_read(&metadata.package_type, sizeof(metadata.package_type));
|
||||
pkg_log.notice("Metadata: Package Type = 0x%x = %d", metadata.package_type, metadata.package_type);
|
||||
archive_read(&m_metadata.package_type, sizeof(m_metadata.package_type));
|
||||
pkg_log.notice("Metadata: Package Type = 0x%x = %d", m_metadata.package_type, m_metadata.package_type);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -229,10 +239,10 @@ bool package_reader::read_metadata()
|
||||
}
|
||||
case 0x4:
|
||||
{
|
||||
if (packet.size == sizeof(metadata.package_size))
|
||||
if (packet.size == sizeof(m_metadata.package_size))
|
||||
{
|
||||
archive_read(&metadata.package_size, sizeof(metadata.package_size));
|
||||
pkg_log.notice("Metadata: Package Size = 0x%x = %d", metadata.package_size, metadata.package_size);
|
||||
archive_read(&m_metadata.package_size, sizeof(m_metadata.package_size));
|
||||
pkg_log.notice("Metadata: Package Size = 0x%x = %d", m_metadata.package_size, m_metadata.package_size);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -243,11 +253,11 @@ bool package_reader::read_metadata()
|
||||
}
|
||||
case 0x5:
|
||||
{
|
||||
if (packet.size == sizeof(metadata.package_revision.data))
|
||||
if (packet.size == sizeof(m_metadata.package_revision.data))
|
||||
{
|
||||
archive_read(&metadata.package_revision.data, sizeof(metadata.package_revision.data));
|
||||
metadata.package_revision.interpret_data();
|
||||
pkg_log.notice("Metadata: Package Revision = %s", metadata.package_revision.to_string());
|
||||
archive_read(&m_metadata.package_revision.data, sizeof(m_metadata.package_revision.data));
|
||||
m_metadata.package_revision.interpret_data();
|
||||
pkg_log.notice("Metadata: Package Revision = %s", m_metadata.package_revision.to_string());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -258,13 +268,13 @@ bool package_reader::read_metadata()
|
||||
}
|
||||
case 0x6:
|
||||
{
|
||||
metadata.title_id.resize(12);
|
||||
m_metadata.title_id.resize(12);
|
||||
|
||||
if (packet.size == metadata.title_id.size())
|
||||
if (packet.size == m_metadata.title_id.size())
|
||||
{
|
||||
archive_read(&metadata.title_id, metadata.title_id.size());
|
||||
metadata.title_id = fmt::trim(metadata.title_id);
|
||||
pkg_log.notice("Metadata: Title ID = %s", metadata.title_id);
|
||||
archive_read(&m_metadata.title_id, m_metadata.title_id.size());
|
||||
m_metadata.title_id = fmt::trim(m_metadata.title_id);
|
||||
pkg_log.notice("Metadata: Title ID = %s", m_metadata.title_id);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -275,10 +285,10 @@ bool package_reader::read_metadata()
|
||||
}
|
||||
case 0x7:
|
||||
{
|
||||
if (packet.size == sizeof(metadata.qa_digest))
|
||||
if (packet.size == sizeof(m_metadata.qa_digest))
|
||||
{
|
||||
archive_read(&metadata.qa_digest, sizeof(metadata.qa_digest));
|
||||
pkg_log.notice("Metadata: QA Digest = 0x%x", metadata.qa_digest);
|
||||
archive_read(&m_metadata.qa_digest, sizeof(m_metadata.qa_digest));
|
||||
pkg_log.notice("Metadata: QA Digest = 0x%x", m_metadata.qa_digest);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -289,11 +299,11 @@ bool package_reader::read_metadata()
|
||||
}
|
||||
case 0x8:
|
||||
{
|
||||
if (packet.size == sizeof(metadata.software_revision.data))
|
||||
if (packet.size == sizeof(m_metadata.software_revision.data))
|
||||
{
|
||||
archive_read(&metadata.software_revision.data, sizeof(metadata.software_revision.data));
|
||||
metadata.software_revision.interpret_data();
|
||||
pkg_log.notice("Metadata: Software Revision = %s", metadata.software_revision.to_string());
|
||||
archive_read(&m_metadata.software_revision.data, sizeof(m_metadata.software_revision.data));
|
||||
m_metadata.software_revision.interpret_data();
|
||||
pkg_log.notice("Metadata: Software Revision = %s", m_metadata.software_revision.to_string());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -304,10 +314,10 @@ bool package_reader::read_metadata()
|
||||
}
|
||||
case 0x9:
|
||||
{
|
||||
if (packet.size == sizeof(metadata.unk_0x9))
|
||||
if (packet.size == sizeof(m_metadata.unk_0x9))
|
||||
{
|
||||
archive_read(&metadata.unk_0x9, sizeof(metadata.unk_0x9));
|
||||
pkg_log.notice("Metadata: unk_0x9 = 0x%x = %d", metadata.unk_0x9, metadata.unk_0x9);
|
||||
archive_read(&m_metadata.unk_0x9, sizeof(m_metadata.unk_0x9));
|
||||
pkg_log.notice("Metadata: unk_0x9 = 0x%x = %d", m_metadata.unk_0x9, m_metadata.unk_0x9);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -321,26 +331,25 @@ bool package_reader::read_metadata()
|
||||
if (packet.size > 8)
|
||||
{
|
||||
// Read an actual installation directory (DLC)
|
||||
install_dir.resize(packet.size);
|
||||
archive_read(&install_dir.front(), packet.size);
|
||||
install_dir = install_dir.c_str() + 8;
|
||||
metadata.install_dir = install_dir;
|
||||
pkg_log.notice("Metadata: Install Dir = %s", metadata.install_dir);
|
||||
m_install_dir.resize(packet.size);
|
||||
archive_read(&m_install_dir.front(), packet.size);
|
||||
m_install_dir = m_install_dir.c_str() + 8;
|
||||
m_metadata.install_dir = m_install_dir;
|
||||
pkg_log.notice("Metadata: Install Dir = %s", m_metadata.install_dir);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
pkg_log.error("Metadata: Install Dir size mismatch (0x%x)", packet.size);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 0xB:
|
||||
{
|
||||
if (packet.size == sizeof(metadata.unk_0xB))
|
||||
if (packet.size == sizeof(m_metadata.unk_0xB))
|
||||
{
|
||||
archive_read(&metadata.unk_0xB, sizeof(metadata.unk_0xB));
|
||||
pkg_log.notice("Metadata: unk_0xB = 0x%x = %d", metadata.unk_0xB, metadata.unk_0xB);
|
||||
archive_read(&m_metadata.unk_0xB, sizeof(m_metadata.unk_0xB));
|
||||
pkg_log.notice("Metadata: unk_0xB = 0x%x = %d", m_metadata.unk_0xB, m_metadata.unk_0xB);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -356,10 +365,10 @@ bool package_reader::read_metadata()
|
||||
}
|
||||
case 0xD: // PSVita stuff
|
||||
{
|
||||
if (packet.size == sizeof(metadata.item_info))
|
||||
if (packet.size == sizeof(m_metadata.item_info))
|
||||
{
|
||||
archive_read(&metadata.item_info, sizeof(metadata.item_info));
|
||||
pkg_log.notice("Metadata: PSVita item info = %s", metadata.item_info.to_string());
|
||||
archive_read(&m_metadata.item_info, sizeof(m_metadata.item_info));
|
||||
pkg_log.notice("Metadata: PSVita item info = %s", m_metadata.item_info.to_string());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -370,10 +379,10 @@ bool package_reader::read_metadata()
|
||||
}
|
||||
case 0xE: // PSVita stuff
|
||||
{
|
||||
if (packet.size == sizeof(metadata.sfo_info))
|
||||
if (packet.size == sizeof(m_metadata.sfo_info))
|
||||
{
|
||||
archive_read(&metadata.sfo_info, sizeof(metadata.sfo_info));
|
||||
pkg_log.notice("Metadata: PSVita sfo info = %s", metadata.sfo_info.to_string());
|
||||
archive_read(&m_metadata.sfo_info, sizeof(m_metadata.sfo_info));
|
||||
pkg_log.notice("Metadata: PSVita sfo info = %s", m_metadata.sfo_info.to_string());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -384,10 +393,10 @@ bool package_reader::read_metadata()
|
||||
}
|
||||
case 0xF: // PSVita stuff
|
||||
{
|
||||
if (packet.size == sizeof(metadata.unknown_data_info))
|
||||
if (packet.size == sizeof(m_metadata.unknown_data_info))
|
||||
{
|
||||
archive_read(&metadata.unknown_data_info, sizeof(metadata.unknown_data_info));
|
||||
pkg_log.notice("Metadata: PSVita unknown data info = %s", metadata.unknown_data_info.to_string());
|
||||
archive_read(&m_metadata.unknown_data_info, sizeof(m_metadata.unknown_data_info));
|
||||
pkg_log.notice("Metadata: PSVita unknown data info = %s", m_metadata.unknown_data_info.to_string());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -398,10 +407,10 @@ bool package_reader::read_metadata()
|
||||
}
|
||||
case 0x10: // PSVita stuff
|
||||
{
|
||||
if (packet.size == sizeof(metadata.entirety_info))
|
||||
if (packet.size == sizeof(m_metadata.entirety_info))
|
||||
{
|
||||
archive_read(&metadata.entirety_info, sizeof(metadata.entirety_info));
|
||||
pkg_log.notice("Metadata: PSVita entirety info = %s", metadata.entirety_info.to_string());
|
||||
archive_read(&m_metadata.entirety_info, sizeof(m_metadata.entirety_info));
|
||||
pkg_log.notice("Metadata: PSVita entirety info = %s", m_metadata.entirety_info.to_string());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -412,23 +421,24 @@ bool package_reader::read_metadata()
|
||||
}
|
||||
case 0x11: // PSVita stuff
|
||||
{
|
||||
if (packet.size == sizeof(metadata.version_info))
|
||||
if (packet.size == sizeof(m_metadata.version_info))
|
||||
{
|
||||
archive_read(&metadata.version_info, sizeof(metadata.version_info));
|
||||
pkg_log.notice("Metadata: PSVita version info = %s", metadata.version_info.to_string());
|
||||
archive_read(&m_metadata.version_info, sizeof(m_metadata.version_info));
|
||||
pkg_log.notice("Metadata: PSVita version info = %s", m_metadata.version_info.to_string());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
pkg_log.error("Metadata: Version info size mismatch (0x%x)", packet.size);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x12: // PSVita stuff
|
||||
{
|
||||
if (packet.size == sizeof(metadata.self_info))
|
||||
if (packet.size == sizeof(m_metadata.self_info))
|
||||
{
|
||||
archive_read(&metadata.self_info, sizeof(metadata.self_info));
|
||||
pkg_log.notice("Metadata: PSVita self info = %s", metadata.self_info.to_string());
|
||||
archive_read(&m_metadata.self_info, sizeof(m_metadata.self_info));
|
||||
pkg_log.notice("Metadata: PSVita self info = %s", m_metadata.self_info.to_string());
|
||||
continue;
|
||||
}
|
||||
else
|
||||
@@ -457,36 +467,35 @@ bool package_reader::decrypt_data()
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA && metadata.content_type >= 0x15 && metadata.content_type <= 0x17)
|
||||
if (m_header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA && m_metadata.content_type >= 0x15 && m_metadata.content_type <= 0x17)
|
||||
{
|
||||
// PSVita
|
||||
// TODO: Not all the keys seem to match the content types. I was only able to install a dlc (0x16) with PKG_AES_KEY_VITA_1
|
||||
|
||||
aes_context ctx;
|
||||
aes_setkey_enc(&ctx, metadata.content_type == 0x15u ? PKG_AES_KEY_VITA_1 : metadata.content_type == 0x16u ? PKG_AES_KEY_VITA_2 : PKG_AES_KEY_VITA_3, 128);
|
||||
aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast<const uchar*>(&header.klicensee), dec_key.data());
|
||||
decrypt(0, header.file_count * sizeof(PKGEntry), dec_key.data());
|
||||
aes_setkey_enc(&ctx, m_metadata.content_type == 0x15u ? PKG_AES_KEY_VITA_1 : m_metadata.content_type == 0x16u ? PKG_AES_KEY_VITA_2 : PKG_AES_KEY_VITA_3, 128);
|
||||
aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast<const uchar*>(&m_header.klicensee), m_dec_key.data());
|
||||
decrypt(0, m_header.file_count * sizeof(PKGEntry), m_dec_key.data());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy(dec_key.data(), PKG_AES_KEY, dec_key.size());
|
||||
decrypt(0, header.file_count * sizeof(PKGEntry), header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA ? PKG_AES_KEY2 : dec_key.data());
|
||||
std::memcpy(m_dec_key.data(), PKG_AES_KEY, m_dec_key.size());
|
||||
decrypt(0, m_header.file_count * sizeof(PKGEntry), m_header.pkg_platform == PKG_PLATFORM_TYPE_PSP_PSVITA ? PKG_AES_KEY2 : m_dec_key.data());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: maybe also check if VERSION matches
|
||||
package_error package_reader::check_target_app_version()
|
||||
bool package_reader::read_param_sfo()
|
||||
{
|
||||
if (!decrypt_data())
|
||||
{
|
||||
return package_error::other;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<PKGEntry> entries(header.file_count);
|
||||
std::vector<PKGEntry> entries(m_header.file_count);
|
||||
|
||||
std::memcpy(entries.data(), buf.get(), entries.size() * sizeof(PKGEntry));
|
||||
std::memcpy(entries.data(), m_buf.get(), entries.size() * sizeof(PKGEntry));
|
||||
|
||||
for (const auto& entry : entries)
|
||||
{
|
||||
@@ -498,11 +507,11 @@ package_error package_reader::check_target_app_version()
|
||||
|
||||
const bool is_psp = (entry.type & PKG_FILE_ENTRY_PSP) != 0u;
|
||||
|
||||
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : dec_key.data());
|
||||
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data());
|
||||
|
||||
const std::string name{ reinterpret_cast<char*>(buf.get()), entry.name_size };
|
||||
const std::string name{reinterpret_cast<char*>(m_buf.get()), entry.name_size};
|
||||
|
||||
// We're looking for the PARAM.SFO file, if there is any
|
||||
// We're looking for the PARAM.SFO file, if there is any
|
||||
if (name != "PARAM.SFO")
|
||||
{
|
||||
continue;
|
||||
@@ -515,133 +524,146 @@ package_error package_reader::check_target_app_version()
|
||||
{
|
||||
const u64 block_size = std::min<u64>(BUF_SIZE, entry.file_size - pos);
|
||||
|
||||
if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : dec_key.data()) != block_size)
|
||||
if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data()) != block_size)
|
||||
{
|
||||
pkg_log.error("Failed to decrypt PARAM.SFO file");
|
||||
return package_error::other;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tmp.write(buf.get(), block_size) != block_size)
|
||||
if (tmp.write(m_buf.get(), block_size) != block_size)
|
||||
{
|
||||
pkg_log.error("Failed to write to temporary PARAM.SFO file");
|
||||
return package_error::other;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tmp.seek(0);
|
||||
|
||||
const auto psf = psf::load_object(tmp);
|
||||
m_psf = psf::load_object(tmp);
|
||||
|
||||
const auto category = psf::get_string(psf, "CATEGORY", "");
|
||||
const auto title_id = psf::get_string(psf, "TITLE_ID", "");
|
||||
const auto app_ver = psf::get_string(psf, "APP_VER", "");
|
||||
const auto target_app_ver = psf::get_string(psf, "TARGET_APP_VER", "");
|
||||
|
||||
if (category != "GD")
|
||||
{
|
||||
// We allow anything that isn't an update for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
if (title_id.empty())
|
||||
{
|
||||
// Let's allow packages without ID for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
if (app_ver.empty())
|
||||
{
|
||||
if (!target_app_ver.empty())
|
||||
{
|
||||
// Let's see if this case exists
|
||||
pkg_log.fatal("Trying to install an unversioned patch with a target app version (%s). Please contact a developer!", target_app_ver);
|
||||
}
|
||||
|
||||
// This is probably not a version dependant patch, so we may install the package
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
const fs::file installed_sfo_file(Emu.GetHddDir() + "game/" + std::string(title_id) + "/PARAM.SFO");
|
||||
if (!installed_sfo_file)
|
||||
{
|
||||
if (!target_app_ver.empty())
|
||||
{
|
||||
// We are unable to compare anything with the target app version
|
||||
pkg_log.error("A target app version is required (%s), but no PARAM.SFO was found for %s", target_app_ver, title_id);
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
// There is nothing we need to compare, so we may install the package
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
const auto installed_psf = psf::load_object(installed_sfo_file);
|
||||
|
||||
const auto installed_title_id = psf::get_string(installed_psf, "TITLE_ID", "");
|
||||
const auto installed_app_ver = psf::get_string(installed_psf, "APP_VER", "");
|
||||
|
||||
if (title_id != installed_title_id || installed_app_ver.empty())
|
||||
{
|
||||
// Let's allow this package for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
std::add_pointer_t<char> ev0, ev1;
|
||||
const double old_version = std::strtod(installed_app_ver.data(), &ev0);
|
||||
|
||||
if (installed_app_ver.data() + installed_app_ver.size() != ev0)
|
||||
{
|
||||
pkg_log.error("Failed to convert the installed app version to double (%s)", installed_app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (target_app_ver.empty())
|
||||
{
|
||||
// This is most likely the first patch. Let's make sure its version is high enough for the installed game.
|
||||
|
||||
const double new_version = std::strtod(app_ver.data(), &ev1);
|
||||
|
||||
if (app_ver.data() + app_ver.size() != ev1)
|
||||
{
|
||||
pkg_log.error("Failed to convert the package's app version to double (%s)", app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (new_version >= old_version)
|
||||
{
|
||||
// Yay! The patch has a higher or equal version than the installed game.
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
pkg_log.error("The new app version (%s) is smaller than the installed app version (%s)", app_ver, installed_app_ver);
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
// Check if the installed app version matches the target app version
|
||||
|
||||
const double target_version = std::strtod(target_app_ver.data(), &ev1);
|
||||
|
||||
if (target_app_ver.data() + target_app_ver.size() != ev1)
|
||||
{
|
||||
pkg_log.error("Failed to convert the package's target app version to double (%s)", target_app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (target_version == old_version)
|
||||
{
|
||||
// Yay! This patch is for the installed game version.
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
pkg_log.error("The installed app version (%s) does not match the target app version (%s)", installed_app_ver, target_app_ver);
|
||||
return package_error::app_version;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
pkg_log.error("Failed to create temporary PARAM.SFO file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
pkg_log.error("Failed to create temporary PARAM.SFO file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: maybe also check if VERSION matches
|
||||
package_error package_reader::check_target_app_version()
|
||||
{
|
||||
if (!m_is_valid)
|
||||
{
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
return package_error::no_error;
|
||||
const auto category = psf::get_string(m_psf, "CATEGORY", "");
|
||||
const auto title_id = psf::get_string(m_psf, "TITLE_ID", "");
|
||||
const auto app_ver = psf::get_string(m_psf, "APP_VER", "");
|
||||
const auto target_app_ver = psf::get_string(m_psf, "TARGET_APP_VER", "");
|
||||
|
||||
if (category != "GD")
|
||||
{
|
||||
// We allow anything that isn't an update for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
if (title_id.empty())
|
||||
{
|
||||
// Let's allow packages without ID for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
if (app_ver.empty())
|
||||
{
|
||||
if (!target_app_ver.empty())
|
||||
{
|
||||
// Let's see if this case exists
|
||||
pkg_log.fatal("Trying to install an unversioned patch with a target app version (%s). Please contact a developer!", target_app_ver);
|
||||
}
|
||||
|
||||
// This is probably not a version dependant patch, so we may install the package
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
const fs::file installed_sfo_file(Emu.GetHddDir() + "game/" + std::string(title_id) + "/PARAM.SFO");
|
||||
if (!installed_sfo_file)
|
||||
{
|
||||
if (!target_app_ver.empty())
|
||||
{
|
||||
// We are unable to compare anything with the target app version
|
||||
pkg_log.error("A target app version is required (%s), but no PARAM.SFO was found for %s", target_app_ver, title_id);
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
// There is nothing we need to compare, so we may install the package
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
const auto installed_psf = psf::load_object(installed_sfo_file);
|
||||
|
||||
const auto installed_title_id = psf::get_string(installed_psf, "TITLE_ID", "");
|
||||
const auto installed_app_ver = psf::get_string(installed_psf, "APP_VER", "");
|
||||
|
||||
if (title_id != installed_title_id || installed_app_ver.empty())
|
||||
{
|
||||
// Let's allow this package for now
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
std::add_pointer_t<char> ev0, ev1;
|
||||
const double old_version = std::strtod(installed_app_ver.data(), &ev0);
|
||||
|
||||
if (installed_app_ver.data() + installed_app_ver.size() != ev0)
|
||||
{
|
||||
pkg_log.error("Failed to convert the installed app version to double (%s)", installed_app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (target_app_ver.empty())
|
||||
{
|
||||
// This is most likely the first patch. Let's make sure its version is high enough for the installed game.
|
||||
|
||||
const double new_version = std::strtod(app_ver.data(), &ev1);
|
||||
|
||||
if (app_ver.data() + app_ver.size() != ev1)
|
||||
{
|
||||
pkg_log.error("Failed to convert the package's app version to double (%s)", app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (new_version >= old_version)
|
||||
{
|
||||
// Yay! The patch has a higher or equal version than the installed game.
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
pkg_log.error("The new app version (%s) is smaller than the installed app version (%s)", app_ver, installed_app_ver);
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
// Check if the installed app version matches the target app version
|
||||
|
||||
const double target_version = std::strtod(target_app_ver.data(), &ev1);
|
||||
|
||||
if (target_app_ver.data() + target_app_ver.size() != ev1)
|
||||
{
|
||||
pkg_log.error("Failed to convert the package's target app version to double (%s)", target_app_ver);
|
||||
return package_error::other;
|
||||
}
|
||||
|
||||
if (target_version == old_version)
|
||||
{
|
||||
// Yay! This patch is for the installed game version.
|
||||
return package_error::no_error;
|
||||
}
|
||||
|
||||
pkg_log.error("The installed app version (%s) does not match the target app version (%s)", installed_app_ver, target_app_ver);
|
||||
return package_error::app_version;
|
||||
}
|
||||
|
||||
bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
@@ -655,7 +677,7 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
std::string dir = Emulator::GetHddDir();
|
||||
|
||||
// Based on https://www.psdevwiki.com/ps3/PKG_files#ContentType
|
||||
switch (metadata.content_type)
|
||||
switch (m_metadata.content_type)
|
||||
{
|
||||
case PKG_CONTENT_TYPE_THEME:
|
||||
dir += "theme/";
|
||||
@@ -681,7 +703,7 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
break;
|
||||
}
|
||||
|
||||
dir += install_dir + '/';
|
||||
dir += m_install_dir + '/';
|
||||
|
||||
// If false, an existing directory is being overwritten: cannot cancel the operation
|
||||
const bool was_null = !fs::is_dir(dir);
|
||||
@@ -697,11 +719,11 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t num_failures = 0;
|
||||
usz num_failures = 0;
|
||||
|
||||
std::vector<PKGEntry> entries(header.file_count);
|
||||
std::vector<PKGEntry> entries(m_header.file_count);
|
||||
|
||||
std::memcpy(entries.data(), buf.get(), entries.size() * sizeof(PKGEntry));
|
||||
std::memcpy(entries.data(), m_buf.get(), entries.size() * sizeof(PKGEntry));
|
||||
|
||||
for (const auto& entry : entries)
|
||||
{
|
||||
@@ -714,9 +736,9 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
|
||||
const bool is_psp = (entry.type & PKG_FILE_ENTRY_PSP) != 0u;
|
||||
|
||||
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : dec_key.data());
|
||||
decrypt(entry.name_offset, entry.name_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data());
|
||||
|
||||
const std::string name{ reinterpret_cast<char*>(buf.get()), entry.name_size };
|
||||
const std::string name{reinterpret_cast<char*>(m_buf.get()), entry.name_size};
|
||||
const std::string path = dir + vfs::escape(name);
|
||||
|
||||
pkg_log.notice("Entry 0x%08x: %s", entry.type, name);
|
||||
@@ -754,21 +776,21 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
{
|
||||
const u64 block_size = std::min<u64>(BUF_SIZE, entry.file_size - pos);
|
||||
|
||||
if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : dec_key.data()) != block_size)
|
||||
if (decrypt(entry.file_offset + pos, block_size, is_psp ? PKG_AES_KEY2 : m_dec_key.data()) != block_size)
|
||||
{
|
||||
extract_success = false;
|
||||
pkg_log.error("Failed to extract file %s", path);
|
||||
break;
|
||||
}
|
||||
|
||||
if (out.write(buf.get(), block_size) != block_size)
|
||||
if (out.write(m_buf.get(), block_size) != block_size)
|
||||
{
|
||||
extract_success = false;
|
||||
pkg_log.error("Failed to write file %s", path);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sync.fetch_add((block_size + 0.0) / header.data_size) < 0.)
|
||||
if (sync.fetch_add((block_size + 0.0) / m_header.data_size) < 0.)
|
||||
{
|
||||
if (was_null)
|
||||
{
|
||||
@@ -852,55 +874,55 @@ bool package_reader::extract_data(atomic_t<double>& sync)
|
||||
void package_reader::archive_seek(const s64 new_offset, const fs::seek_mode damode)
|
||||
{
|
||||
if (damode == fs::seek_set)
|
||||
cur_offset = new_offset;
|
||||
m_cur_offset = new_offset;
|
||||
else if (damode == fs::seek_cur)
|
||||
cur_offset += new_offset;
|
||||
m_cur_offset += new_offset;
|
||||
|
||||
u64 _offset = 0;
|
||||
for (size_t i = 0; i < filelist.size(); i++)
|
||||
for (usz i = 0; i < m_filelist.size(); i++)
|
||||
{
|
||||
if (cur_offset < (_offset + filelist[i].size()))
|
||||
if (m_cur_offset < (_offset + m_filelist[i].size()))
|
||||
{
|
||||
cur_file = i;
|
||||
cur_file_offset = cur_offset - _offset;
|
||||
filelist[i].seek(cur_file_offset);
|
||||
m_cur_file = i;
|
||||
m_cur_file_offset = m_cur_offset - _offset;
|
||||
m_filelist[i].seek(m_cur_file_offset);
|
||||
break;
|
||||
}
|
||||
_offset += filelist[i].size();
|
||||
_offset += m_filelist[i].size();
|
||||
}
|
||||
};
|
||||
|
||||
u64 package_reader::archive_read(void* data_ptr, const u64 num_bytes)
|
||||
{
|
||||
ASSERT(filelist.size() > cur_file && filelist[cur_file]);
|
||||
ensure(m_filelist.size() > m_cur_file && m_filelist[m_cur_file]);
|
||||
|
||||
const u64 num_bytes_left = filelist[cur_file].size() - cur_file_offset;
|
||||
const u64 num_bytes_left = m_filelist[m_cur_file].size() - m_cur_file_offset;
|
||||
|
||||
// check if it continues in another file
|
||||
if (num_bytes > num_bytes_left)
|
||||
{
|
||||
filelist[cur_file].read(data_ptr, num_bytes_left);
|
||||
m_filelist[m_cur_file].read(data_ptr, num_bytes_left);
|
||||
|
||||
if ((cur_file + 1) < filelist.size())
|
||||
if ((m_cur_file + 1) < m_filelist.size())
|
||||
{
|
||||
++cur_file;
|
||||
++m_cur_file;
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_offset += num_bytes_left;
|
||||
cur_file_offset = filelist[cur_file].size();
|
||||
m_cur_offset += num_bytes_left;
|
||||
m_cur_file_offset = m_filelist[m_cur_file].size();
|
||||
return num_bytes_left;
|
||||
}
|
||||
const u64 num_read = filelist[cur_file].read(static_cast<u8*>(data_ptr) + num_bytes_left, num_bytes - num_bytes_left);
|
||||
cur_offset += (num_read + num_bytes_left);
|
||||
cur_file_offset = num_read;
|
||||
const u64 num_read = m_filelist[m_cur_file].read(static_cast<u8*>(data_ptr) + num_bytes_left, num_bytes - num_bytes_left);
|
||||
m_cur_offset += (num_read + num_bytes_left);
|
||||
m_cur_file_offset = num_read;
|
||||
return (num_read + num_bytes_left);
|
||||
}
|
||||
|
||||
const u64 num_read = filelist[cur_file].read(data_ptr, num_bytes);
|
||||
const u64 num_read = m_filelist[m_cur_file].read(data_ptr, num_bytes);
|
||||
|
||||
cur_offset += num_read;
|
||||
cur_file_offset += num_read;
|
||||
m_cur_offset += num_read;
|
||||
m_cur_file_offset += num_read;
|
||||
|
||||
return num_read;
|
||||
};
|
||||
@@ -912,29 +934,29 @@ u64 package_reader::decrypt(u64 offset, u64 size, const uchar* key)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!buf)
|
||||
if (!m_buf)
|
||||
{
|
||||
// Allocate buffer with BUF_SIZE size or more if required
|
||||
buf.reset(new u128[std::max<u64>(BUF_SIZE, sizeof(PKGEntry) * header.file_count) / sizeof(u128)]);
|
||||
m_buf.reset(new u128[std::max<u64>(BUF_SIZE, sizeof(PKGEntry) * m_header.file_count) / sizeof(u128)]);
|
||||
}
|
||||
|
||||
archive_seek(header.data_offset + offset);
|
||||
archive_seek(m_header.data_offset + offset);
|
||||
|
||||
// Read the data and set available size
|
||||
const u64 read = archive_read(buf.get(), size);
|
||||
const u64 read = archive_read(m_buf.get(), size);
|
||||
|
||||
// Get block count
|
||||
const u64 blocks = (read + 15) / 16;
|
||||
|
||||
if (header.pkg_type == PKG_RELEASE_TYPE_DEBUG)
|
||||
if (m_header.pkg_type == PKG_RELEASE_TYPE_DEBUG)
|
||||
{
|
||||
// Debug key
|
||||
be_t<u64> input[8] =
|
||||
{
|
||||
header.qa_digest[0],
|
||||
header.qa_digest[0],
|
||||
header.qa_digest[1],
|
||||
header.qa_digest[1],
|
||||
m_header.qa_digest[0],
|
||||
m_header.qa_digest[0],
|
||||
m_header.qa_digest[1],
|
||||
m_header.qa_digest[1],
|
||||
};
|
||||
|
||||
for (u64 i = 0; i < blocks; i++)
|
||||
@@ -950,10 +972,10 @@ u64 package_reader::decrypt(u64 offset, u64 size, const uchar* key)
|
||||
|
||||
sha1(reinterpret_cast<const u8*>(input), sizeof(input), hash.data);
|
||||
|
||||
buf[i] ^= hash._v128;
|
||||
m_buf[i] ^= hash._v128;
|
||||
}
|
||||
}
|
||||
else if (header.pkg_type == PKG_RELEASE_TYPE_RELEASE)
|
||||
else if (m_header.pkg_type == PKG_RELEASE_TYPE_RELEASE)
|
||||
{
|
||||
aes_context ctx;
|
||||
|
||||
@@ -961,7 +983,7 @@ u64 package_reader::decrypt(u64 offset, u64 size, const uchar* key)
|
||||
aes_setkey_enc(&ctx, key, 128);
|
||||
|
||||
// Initialize stream cipher for start position
|
||||
be_t<u128> input = header.klicensee.value() + offset / 16;
|
||||
be_t<u128> input = m_header.klicensee.value() + offset / 16;
|
||||
|
||||
// Increment stream position for every block
|
||||
for (u64 i = 0; i < blocks; i++, input++)
|
||||
@@ -970,12 +992,12 @@ u64 package_reader::decrypt(u64 offset, u64 size, const uchar* key)
|
||||
|
||||
aes_crypt_ecb(&ctx, AES_ENCRYPT, reinterpret_cast<const u8*>(&input), reinterpret_cast<u8*>(&key));
|
||||
|
||||
buf[i] ^= key;
|
||||
m_buf[i] ^= key;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
pkg_log.error("Unknown release type (0x%x)", header.pkg_type);
|
||||
pkg_log.error("Unknown release type (0x%x)", m_header.pkg_type);
|
||||
}
|
||||
|
||||
// Return the amount of data written in buf
|
||||
|
||||
+22
-16
@@ -1,13 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/BEType.h"
|
||||
#include "Loader/PSF.h"
|
||||
#include "util/endian.hpp"
|
||||
#include "util/types.hpp"
|
||||
#include "Utilities/File.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
// Constants
|
||||
enum
|
||||
{
|
||||
PKG_HEADER_SIZE = 0xC0, //sizeof(pkg_header) + sizeof(pkg_unk_checksum)
|
||||
PKG_HEADER_SIZE = 0xC0, // sizeof(pkg_header) + sizeof(pkg_unk_checksum)
|
||||
PKG_HEADER_SIZE2 = 0x280,
|
||||
};
|
||||
|
||||
@@ -96,7 +99,7 @@ struct PKGExtHeader
|
||||
be_t<u32> ext_hdr_size; // Extended header size. ex: 0x40
|
||||
be_t<u32> ext_data_size; // ex: 0x180
|
||||
be_t<u32> main_and_ext_headers_hmac_offset; // ex: 0x100
|
||||
be_t<u32> metadata_header_hmac_offset; // ex: 0x360, 0x390, 0x490
|
||||
be_t<u32> metadata_header_hmac_offset; // ex: 0x360, 0x390, 0x490
|
||||
be_t<u64> tail_offset; // tail size seams to be always 0x1A0
|
||||
be_t<u32> padding1;
|
||||
be_t<u32> pkg_key_id; // Id of the AES key used for decryption. PSP = 0x1, PSVita = 0xC0000002, PSM = 0xC0000004
|
||||
@@ -118,16 +121,16 @@ struct PKGEntry
|
||||
struct PKGMetaData
|
||||
{
|
||||
private:
|
||||
static std::string to_hex_string(u8 buf[], size_t size)
|
||||
static std::string to_hex_string(u8 buf[], usz size)
|
||||
{
|
||||
std::stringstream sstream;
|
||||
for (size_t i = 0; i < size; i++)
|
||||
for (usz i = 0; i < size; i++)
|
||||
{
|
||||
sstream << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(buf[i]);
|
||||
}
|
||||
return sstream.str();
|
||||
}
|
||||
static std::string to_hex_string(u8 buf[], size_t size, size_t dotpos)
|
||||
static std::string to_hex_string(u8 buf[], usz size, usz dotpos)
|
||||
{
|
||||
std::string result = to_hex_string(buf, size);
|
||||
if (result.size() > dotpos)
|
||||
@@ -304,28 +307,31 @@ public:
|
||||
|
||||
package_error check_target_app_version();
|
||||
bool extract_data(atomic_t<double>& sync);
|
||||
psf::registry get_psf() const { return m_psf; }
|
||||
|
||||
private:
|
||||
bool read_header();
|
||||
bool read_metadata();
|
||||
bool read_param_sfo();
|
||||
bool decrypt_data();
|
||||
void archive_seek(const s64 new_offset, const fs::seek_mode damode = fs::seek_set);
|
||||
u64 archive_read(void* data_ptr, const u64 num_bytes);
|
||||
u64 decrypt(u64 offset, u64 size, const uchar* key);
|
||||
|
||||
const std::size_t BUF_SIZE = 8192 * 1024; // 8 MB
|
||||
const usz BUF_SIZE = 8192 * 1024; // 8 MB
|
||||
|
||||
bool m_is_valid = false;
|
||||
|
||||
std::string m_path;
|
||||
std::string install_dir;
|
||||
std::vector<fs::file> filelist;
|
||||
size_t cur_file = 0;
|
||||
u64 cur_offset = 0;
|
||||
u64 cur_file_offset = 0;
|
||||
std::unique_ptr<u128[]> buf;
|
||||
std::array<uchar, 16> dec_key{};
|
||||
std::string m_install_dir;
|
||||
std::vector<fs::file> m_filelist;
|
||||
usz m_cur_file = 0;
|
||||
u64 m_cur_offset = 0;
|
||||
u64 m_cur_file_offset = 0;
|
||||
std::unique_ptr<u128[]> m_buf;
|
||||
std::array<uchar, 16> m_dec_key{};
|
||||
|
||||
PKGHeader header{};
|
||||
PKGMetaData metadata{};
|
||||
PKGHeader m_header{};
|
||||
PKGMetaData m_metadata{};
|
||||
psf::registry m_psf;
|
||||
};
|
||||
|
||||
+14
-15
@@ -1,9 +1,8 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "aes.h"
|
||||
#include "sha1.h"
|
||||
#include "utils.h"
|
||||
#include "unself.h"
|
||||
#include "Utilities/BEType.h"
|
||||
#include "Emu/VFS.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
@@ -714,7 +713,7 @@ bool SCEDecrypter::LoadMetadata(const u8 erk[32], const u8 riv[16])
|
||||
}
|
||||
|
||||
// Perform AES-CTR encryption on the metadata headers.
|
||||
size_t ctr_nc_off = 0;
|
||||
usz ctr_nc_off = 0;
|
||||
u8 ctr_stream_block[0x10];
|
||||
aes_setkey_enc(&aes, meta_info.key, 128);
|
||||
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
|
||||
@@ -745,7 +744,7 @@ bool SCEDecrypter::DecryptData()
|
||||
// Calculate the total data size.
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
data_buf_length += ::narrow<u32>(meta_shdr[i].data_size, HERE);
|
||||
data_buf_length += ::narrow<u32>(meta_shdr[i].data_size);
|
||||
}
|
||||
|
||||
// Allocate a buffer to store decrypted data.
|
||||
@@ -757,7 +756,7 @@ bool SCEDecrypter::DecryptData()
|
||||
// Parse the metadata section headers to find the offsets of encrypted data.
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
size_t ctr_nc_off = 0;
|
||||
usz ctr_nc_off = 0;
|
||||
u8 ctr_stream_block[0x10];
|
||||
u8 data_key[0x10];
|
||||
u8 data_iv[0x10];
|
||||
@@ -799,7 +798,7 @@ bool SCEDecrypter::DecryptData()
|
||||
}
|
||||
|
||||
// Advance the buffer's offset.
|
||||
data_buf_offset += ::narrow<u32>(meta_shdr[i].data_size, HERE);
|
||||
data_buf_offset += ::narrow<u32>(meta_shdr[i].data_size);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -823,13 +822,13 @@ std::vector<fs::file> SCEDecrypter::MakeFile()
|
||||
// Decompress if necessary.
|
||||
if (meta_shdr[i].compressed == 2)
|
||||
{
|
||||
const size_t BUFSIZE = 32 * 1024;
|
||||
const usz BUFSIZE = 32 * 1024;
|
||||
u8 tempbuf[BUFSIZE];
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = ::narrow<uInt>(meta_shdr[i].data_size, HERE);
|
||||
strm.avail_in = ::narrow<uInt>(meta_shdr[i].data_size);
|
||||
strm.avail_out = BUFSIZE;
|
||||
strm.next_in = data_buf.get()+data_buf_offset;
|
||||
strm.next_out = tempbuf;
|
||||
@@ -874,7 +873,7 @@ std::vector<fs::file> SCEDecrypter::MakeFile()
|
||||
}
|
||||
|
||||
// Advance the data buffer offset by data size.
|
||||
data_buf_offset += ::narrow<u32>(meta_shdr[i].data_size, HERE);
|
||||
data_buf_offset += ::narrow<u32>(meta_shdr[i].data_size);
|
||||
|
||||
if (out_f.pos() != out_f.size())
|
||||
fmt::throw_exception("MakeELF written bytes (%llu) does not equal buffer size (%llu).", out_f.pos(), out_f.size());
|
||||
@@ -1209,7 +1208,7 @@ bool SELFDecrypter::LoadMetadata(u8* klic_key)
|
||||
}
|
||||
|
||||
// Perform AES-CTR encryption on the metadata headers.
|
||||
size_t ctr_nc_off = 0;
|
||||
usz ctr_nc_off = 0;
|
||||
u8 ctr_stream_block[0x10];
|
||||
aes_setkey_enc(&aes, meta_info.key, 128);
|
||||
aes_crypt_ctr(&aes, metadata_headers_size, &ctr_nc_off, meta_info.iv, ctr_stream_block, metadata_headers.get(), metadata_headers.get());
|
||||
@@ -1243,7 +1242,7 @@ bool SELFDecrypter::DecryptData()
|
||||
if (meta_shdr[i].encrypted == 3)
|
||||
{
|
||||
if ((meta_shdr[i].key_idx <= meta_hdr.key_count - 1) && (meta_shdr[i].iv_idx <= meta_hdr.key_count))
|
||||
data_buf_length += ::narrow<u32>(meta_shdr[i].data_size, HERE);
|
||||
data_buf_length += ::narrow<u32>(meta_shdr[i].data_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1256,7 +1255,7 @@ bool SELFDecrypter::DecryptData()
|
||||
// Parse the metadata section headers to find the offsets of encrypted data.
|
||||
for (unsigned int i = 0; i < meta_hdr.section_count; i++)
|
||||
{
|
||||
size_t ctr_nc_off = 0;
|
||||
usz ctr_nc_off = 0;
|
||||
u8 ctr_stream_block[0x10];
|
||||
u8 data_key[0x10];
|
||||
u8 data_iv[0x10];
|
||||
@@ -1289,7 +1288,7 @@ bool SELFDecrypter::DecryptData()
|
||||
memcpy(data_buf.get() + data_buf_offset, buf.get(), meta_shdr[i].data_size);
|
||||
|
||||
// Advance the buffer's offset.
|
||||
data_buf_offset += ::narrow<u32>(meta_shdr[i].data_size, HERE);
|
||||
data_buf_offset += ::narrow<u32>(meta_shdr[i].data_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1488,7 +1487,7 @@ bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key)
|
||||
return true;
|
||||
}
|
||||
|
||||
v128 get_default_self_klic()
|
||||
u128 get_default_self_klic()
|
||||
{
|
||||
return std::bit_cast<v128>(NP_KLIC_FREE);
|
||||
return std::bit_cast<u128>(NP_KLIC_FREE);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "key_vault.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "Utilities/File.h"
|
||||
#include "util/logs.hpp"
|
||||
|
||||
@@ -489,7 +489,7 @@ private:
|
||||
}
|
||||
|
||||
// Advance the data buffer offset by data size.
|
||||
data_buf_offset += ::narrow<u32>(meta_shdr[i].data_size, HERE);
|
||||
data_buf_offset += ::narrow<u32>(meta_shdr[i].data_size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -509,5 +509,4 @@ private:
|
||||
fs::file decrypt_self(fs::file elf_or_self, u8* klic_key = nullptr, SelfAdditionalInfo* additional_info = nullptr);
|
||||
bool verify_npdrm_self_headers(const fs::file& self, u8* klic_key = nullptr);
|
||||
|
||||
union v128;
|
||||
v128 get_default_self_klic();
|
||||
u128 get_default_self_klic();
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
|
||||
#include "utils.h"
|
||||
#include "aes.h"
|
||||
#include "sha1.h"
|
||||
#include <cstring>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
// Copyright (C) 2014 Hykem <hykem@hotmail.com>
|
||||
// Licensed under the terms of the GNU GPL, version 3
|
||||
// http://www.gnu.org/licenses/gpl-3.0.txt
|
||||
|
||||
#include "../../Utilities/types.h"
|
||||
#include "util/types.hpp"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "aes.h"
|
||||
#include "sha1.h"
|
||||
#include "lz.h"
|
||||
#include "ec.h"
|
||||
|
||||
enum { CRYPTO_MAX_PATH = 4096 };
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
|
||||
#include "OpenALBackend.h"
|
||||
|
||||
@@ -14,10 +14,12 @@ LOG_CHANNEL(OpenAL);
|
||||
OpenALBackend::OpenALBackend()
|
||||
: AudioBackend()
|
||||
{
|
||||
ALCint attribs[] = {ALC_FREQUENCY, DEFAULT_AUDIO_SAMPLING_RATE, 0, 0};
|
||||
|
||||
ALCdevice* m_device = alcOpenDevice(nullptr);
|
||||
checkForAlcError("alcOpenDevice");
|
||||
|
||||
ALCcontext* m_context = alcCreateContext(m_device, nullptr);
|
||||
ALCcontext* m_context = alcCreateContext(m_device, attribs);
|
||||
checkForAlcError("alcCreateContext");
|
||||
|
||||
alcMakeContextCurrent(m_context);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
#include "3rdparty/OpenAL/include/alext.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef HAVE_ALSA
|
||||
#ifndef HAVE_ALSA
|
||||
#error "ALSA support disabled but still being built."
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "AudioBackend.h"
|
||||
#include "Emu/system_config.h"
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "Utilities/StrFmt.h"
|
||||
|
||||
enum : u32
|
||||
{
|
||||
@@ -101,7 +102,7 @@ public:
|
||||
u32 get_sample_size() const;
|
||||
|
||||
u32 get_channels() const;
|
||||
|
||||
|
||||
bool get_convert_to_u16() const;
|
||||
|
||||
bool has_capability(u32 cap) const;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "AudioDumper.h"
|
||||
|
||||
#include "Utilities/date_time.h"
|
||||
@@ -33,7 +33,8 @@ void AudioDumper::WriteData(const void* buffer, u32 size)
|
||||
{
|
||||
if (GetCh())
|
||||
{
|
||||
verify(HERE), size, m_output.write(buffer, size) == size;
|
||||
ensure(size);
|
||||
ensure(m_output.write(buffer, size) == size);
|
||||
m_header.Size += size;
|
||||
m_header.RIFF.Size += size;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utilities/types.h"
|
||||
#include "util/types.hpp"
|
||||
#include "Utilities/File.h"
|
||||
|
||||
struct WAVHeader
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef HAVE_FAUDIO
|
||||
#ifndef HAVE_FAUDIO
|
||||
#error "FAudio support disabled but still being built."
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Audio/AudioBackend.h"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef HAVE_PULSE
|
||||
#ifndef HAVE_PULSE
|
||||
#error "PulseAudio support disabled but still being built."
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
#ifndef _WIN32
|
||||
#ifndef _WIN32
|
||||
#error "XAudio2 can only be built on Windows."
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include "util/logs.hpp"
|
||||
#include "Utilities/StrFmt.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#ifndef _WIN32
|
||||
#error "XAudio2 can only be built on Windows."
|
||||
|
||||
@@ -33,17 +33,18 @@ target_include_directories(rpcs3_emu
|
||||
# Utilities
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
../util/atomic.cpp
|
||||
../util/atomic2.cpp
|
||||
../util/shared_cptr.cpp
|
||||
../util/fixed_typemap.cpp
|
||||
../util/logs.cpp
|
||||
../util/yaml.cpp
|
||||
../util/cereal.cpp
|
||||
../util/vm_native.cpp
|
||||
../util/dyn_lib.cpp
|
||||
../util/sysinfo.cpp
|
||||
../util/cpu_stats.cpp
|
||||
../../Utilities/bin_patch.cpp
|
||||
../../Utilities/cheat_info.cpp
|
||||
../../Utilities/cond.cpp
|
||||
../../Utilities/Config.cpp
|
||||
../../Utilities/dynamic_library.cpp
|
||||
../../Utilities/File.cpp
|
||||
../../Utilities/JIT.cpp
|
||||
../../Utilities/LUrlParser.cpp
|
||||
@@ -51,10 +52,8 @@ target_sources(rpcs3_emu PRIVATE
|
||||
../../Utilities/rXml.cpp
|
||||
../../Utilities/sema.cpp
|
||||
../../Utilities/StrFmt.cpp
|
||||
../../Utilities/sysinfo.cpp
|
||||
../../Utilities/Thread.cpp
|
||||
../../Utilities/version.cpp
|
||||
../../Utilities/VirtualMemory.cpp
|
||||
)
|
||||
|
||||
target_include_directories(rpcs3_emu PUBLIC "${CMAKE_SOURCE_DIR}")
|
||||
@@ -76,6 +75,11 @@ else()
|
||||
set_source_files_properties("../util/yaml.cpp" PROPERTIES COMPILE_FLAGS -fexceptions)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
set_source_files_properties("../util/cereal.cpp" PROPERTIES COMPILE_FLAGS /GR)
|
||||
else()
|
||||
set_source_files_properties("../util/cereal.cpp" PROPERTIES COMPILE_FLAGS -frtti)
|
||||
endif()
|
||||
|
||||
# Crypto
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
@@ -124,9 +128,12 @@ if(USE_PULSE AND PULSE_FOUND)
|
||||
target_link_libraries(rpcs3_emu PUBLIC 3rdparty::pulse)
|
||||
endif()
|
||||
|
||||
if(USE_FAUDIO AND SDL2_FOUND AND NOT SDL2_VERSION VERSION_LESS 2.0.9)
|
||||
target_sources(rpcs3_emu PRIVATE Audio/FAudio/FAudioBackend.cpp)
|
||||
target_link_libraries(rpcs3_emu PUBLIC 3rdparty::faudio)
|
||||
if(USE_FAUDIO)
|
||||
find_package(SDL2)
|
||||
if(SDL2_FOUND AND NOT SDL2_VERSION VERSION_LESS 2.0.9)
|
||||
target_sources(rpcs3_emu PRIVATE Audio/FAudio/FAudioBackend.cpp)
|
||||
target_link_libraries(rpcs3_emu PUBLIC 3rdparty::faudio)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
@@ -360,11 +367,13 @@ target_sources(rpcs3_emu PRIVATE
|
||||
Io/usb_device.cpp
|
||||
Io/Skylander.cpp
|
||||
Io/GHLtar.cpp
|
||||
Io/Buzz.cpp
|
||||
)
|
||||
|
||||
# Np
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
NP/fb_helpers.cpp
|
||||
NP/np_contexts.cpp
|
||||
NP/np_handler.cpp
|
||||
NP/signaling_handler.cpp
|
||||
NP/np_structs_extra.cpp
|
||||
@@ -391,11 +400,14 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/RSXTexture.cpp
|
||||
RSX/RSXThread.cpp
|
||||
RSX/rsx_utils.cpp
|
||||
RSX/RSXDisAsm.cpp
|
||||
RSX/Common/BufferUtils.cpp
|
||||
RSX/Common/FragmentProgramDecompiler.cpp
|
||||
RSX/Common/GLSLCommon.cpp
|
||||
RSX/Common/ProgramStateCache.cpp
|
||||
RSX/Common/surface_store.cpp
|
||||
RSX/Common/TextureUtils.cpp
|
||||
RSX/Common/texture_cache.cpp
|
||||
RSX/Common/VertexProgramDecompiler.cpp
|
||||
RSX/Null/NullGSRender.cpp
|
||||
RSX/Overlays/overlay_animation.cpp
|
||||
@@ -421,17 +433,32 @@ target_sources(rpcs3_emu PRIVATE
|
||||
RSX/GL/GLFragmentProgram.cpp
|
||||
RSX/GL/GLGSRender.cpp
|
||||
RSX/GL/GLHelpers.cpp
|
||||
RSX/GL/GLPipelineCompiler.cpp
|
||||
RSX/GL/GLPresent.cpp
|
||||
RSX/GL/GLRenderTargets.cpp
|
||||
RSX/GL/GLShaderInterpreter.cpp
|
||||
RSX/GL/GLTexture.cpp
|
||||
RSX/GL/GLVertexBuffers.cpp
|
||||
RSX/GL/GLVertexProgram.cpp
|
||||
RSX/GL/GLTextureCache.cpp
|
||||
RSX/GL/OpenGL.cpp
|
||||
)
|
||||
|
||||
if(TARGET 3rdparty_vulkan)
|
||||
target_sources(rpcs3_emu PRIVATE
|
||||
RSX/VK/vkutils/barriers.cpp
|
||||
RSX/VK/vkutils/buffer_object.cpp
|
||||
RSX/VK/vkutils/chip_class.cpp
|
||||
RSX/VK/vkutils/commands.cpp
|
||||
RSX/VK/vkutils/data_heap.cpp
|
||||
RSX/VK/vkutils/image.cpp
|
||||
RSX/VK/vkutils/image_helpers.cpp
|
||||
RSX/VK/vkutils/scratch.cpp
|
||||
RSX/VK/vkutils/sync.cpp
|
||||
RSX/VK/vkutils/memory.cpp
|
||||
RSX/VK/vkutils/device.cpp
|
||||
RSX/VK/vkutils/sampler.cpp
|
||||
RSX/VK/vkutils/shared.cpp
|
||||
RSX/VK/VKCommandStream.cpp
|
||||
RSX/VK/VKCommonDecompiler.cpp
|
||||
RSX/VK/VKDMA.cpp
|
||||
@@ -442,6 +469,7 @@ if(TARGET 3rdparty_vulkan)
|
||||
RSX/VK/VKGSRender.cpp
|
||||
RSX/VK/VKHelpers.cpp
|
||||
RSX/VK/VKMemAlloc.cpp
|
||||
RSX/VK/VKPipelineCompiler.cpp
|
||||
RSX/VK/VKPresent.cpp
|
||||
RSX/VK/VKProgramPipeline.cpp
|
||||
RSX/VK/VKQueryPool.cpp
|
||||
@@ -452,6 +480,7 @@ if(TARGET 3rdparty_vulkan)
|
||||
RSX/VK/VKTexture.cpp
|
||||
RSX/VK/VKVertexBuffers.cpp
|
||||
RSX/VK/VKVertexProgram.cpp
|
||||
RSX/VK/VKTextureCache.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
+58
-29
@@ -1,60 +1,85 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <limits>
|
||||
#include "Utilities/StrFmt.h"
|
||||
|
||||
enum CPUDisAsmMode
|
||||
enum class cpu_disasm_mode
|
||||
{
|
||||
CPUDisAsm_DumpMode,
|
||||
CPUDisAsm_InterpreterMode,
|
||||
//CPUDisAsm_NormalMode,
|
||||
CPUDisAsm_CompilerElfMode,
|
||||
dump,
|
||||
interpreter,
|
||||
normal,
|
||||
compiler_elf,
|
||||
list, // RSX exclusive
|
||||
};
|
||||
|
||||
class cpu_thread;
|
||||
|
||||
class CPUDisAsm
|
||||
{
|
||||
protected:
|
||||
const CPUDisAsmMode m_mode;
|
||||
const cpu_disasm_mode m_mode;
|
||||
const std::add_pointer_t<const u8> m_offset;
|
||||
const std::add_pointer_t<const cpu_thread> m_cpu;
|
||||
u32 m_op = 0;
|
||||
|
||||
virtual void Write(const std::string& value)
|
||||
void Write(const std::string& value)
|
||||
{
|
||||
switch(m_mode)
|
||||
switch (m_mode)
|
||||
{
|
||||
case CPUDisAsm_DumpMode:
|
||||
case cpu_disasm_mode::dump:
|
||||
{
|
||||
last_opcode = fmt::format("\t%08x:\t%02x %02x %02x %02x\t%s\n", dump_pc,
|
||||
offset[dump_pc],
|
||||
offset[dump_pc + 1],
|
||||
offset[dump_pc + 2],
|
||||
offset[dump_pc + 3], value);
|
||||
break;
|
||||
static_cast<u8>(m_op >> 24),
|
||||
static_cast<u8>(m_op >> 16),
|
||||
static_cast<u8>(m_op >> 8),
|
||||
static_cast<u8>(m_op >> 0), value);
|
||||
break;
|
||||
}
|
||||
|
||||
case CPUDisAsm_InterpreterMode:
|
||||
case cpu_disasm_mode::interpreter:
|
||||
{
|
||||
last_opcode = fmt::format("[%08x] %02x %02x %02x %02x: %s", dump_pc,
|
||||
offset[dump_pc],
|
||||
offset[dump_pc + 1],
|
||||
offset[dump_pc + 2],
|
||||
offset[dump_pc + 3], value);
|
||||
break;
|
||||
static_cast<u8>(m_op >> 24),
|
||||
static_cast<u8>(m_op >> 16),
|
||||
static_cast<u8>(m_op >> 8),
|
||||
static_cast<u8>(m_op >> 0), value);
|
||||
break;
|
||||
}
|
||||
|
||||
case CPUDisAsm_CompilerElfMode:
|
||||
last_opcode = value + "\n";
|
||||
break;
|
||||
case cpu_disasm_mode::compiler_elf:
|
||||
{
|
||||
last_opcode = value + '\n';
|
||||
break;
|
||||
}
|
||||
case cpu_disasm_mode::normal:
|
||||
{
|
||||
last_opcode = value;
|
||||
break;
|
||||
}
|
||||
default: fmt::throw_exception("Unreachable");
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
std::string last_opcode;
|
||||
u32 dump_pc;
|
||||
const u8* offset;
|
||||
|
||||
template <typename T, std::enable_if_t<std::is_base_of_v<CPUDisAsm, T>, int> = 0>
|
||||
static T copy_and_change_mode(const T& dis, cpu_disasm_mode mode)
|
||||
{
|
||||
return T{mode, dis.m_offset, dis.m_cpu};
|
||||
}
|
||||
|
||||
protected:
|
||||
CPUDisAsm(CPUDisAsmMode mode)
|
||||
CPUDisAsm(cpu_disasm_mode mode, const u8* offset, const cpu_thread* cpu = nullptr)
|
||||
: m_mode(mode)
|
||||
, offset(0)
|
||||
, m_offset(offset)
|
||||
, m_cpu(cpu)
|
||||
{
|
||||
}
|
||||
|
||||
virtual u32 DisAsmBranchTarget(const s32 imm) = 0;
|
||||
virtual u32 DisAsmBranchTarget(const s32 imm) { return 0; };
|
||||
|
||||
// TODO: Add builtin fmt helpper for best performance
|
||||
template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
|
||||
@@ -79,9 +104,13 @@ protected:
|
||||
return fmt::format("%s%s", v < 0 ? "-" : "", av);
|
||||
}
|
||||
|
||||
static std::string FixOp(std::string op)
|
||||
std::string FixOp(std::string op) const
|
||||
{
|
||||
op.resize(std::max<std::size_t>(op.length(), 10), ' ');
|
||||
if (m_mode != cpu_disasm_mode::normal)
|
||||
{
|
||||
op.resize(std::max<usz>(op.length(), 10), ' ');
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
|
||||
+359
-232
@@ -1,4 +1,4 @@
|
||||
#include "stdafx.h"
|
||||
#include "stdafx.h"
|
||||
#include "CPUThread.h"
|
||||
|
||||
#include "Emu/System.h"
|
||||
@@ -8,13 +8,17 @@
|
||||
#include "Emu/GDB.h"
|
||||
#include "Emu/Cell/PPUThread.h"
|
||||
#include "Emu/Cell/SPUThread.h"
|
||||
#include "Emu/RSX/RSXThread.h"
|
||||
#include "Emu/perf_meter.hpp"
|
||||
|
||||
#include "util/asm.hpp"
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <numeric>
|
||||
#include <map>
|
||||
|
||||
#include <immintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
DECLARE(cpu_thread::g_threads_created){0};
|
||||
DECLARE(cpu_thread::g_threads_deleted){0};
|
||||
DECLARE(cpu_thread::g_suspend_counter){0};
|
||||
@@ -22,7 +26,10 @@ DECLARE(cpu_thread::g_suspend_counter){0};
|
||||
LOG_CHANNEL(profiler);
|
||||
LOG_CHANNEL(sys_log, "SYS");
|
||||
|
||||
static thread_local u64 s_tls_thread_slot = -1;
|
||||
static thread_local u32 s_tls_thread_slot = -1;
|
||||
|
||||
// Suspend counter stamp
|
||||
static thread_local u64 s_tls_sctr = -1;
|
||||
|
||||
extern thread_local void(*g_tls_log_control)(const char* fmt, u64 progress);
|
||||
|
||||
@@ -43,7 +50,6 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
|
||||
case cpu_flag::signal: return "sig";
|
||||
case cpu_flag::memory: return "mem";
|
||||
case cpu_flag::dbg_global_pause: return "G-PAUSE";
|
||||
case cpu_flag::dbg_global_stop: return "G-EXIT";
|
||||
case cpu_flag::dbg_pause: return "PAUSE";
|
||||
case cpu_flag::dbg_step: return "STEP";
|
||||
case cpu_flag::__bitset_enum_max: break;
|
||||
@@ -179,7 +185,7 @@ struct cpu_prof
|
||||
if (threads.empty())
|
||||
{
|
||||
// Wait for messages if no work (don't waste CPU)
|
||||
registered.wait();
|
||||
thread_ctrl::wait_on(registered, nullptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -248,39 +254,43 @@ using cpu_profiler = named_thread<cpu_prof>;
|
||||
|
||||
thread_local cpu_thread* g_tls_current_cpu_thread = nullptr;
|
||||
|
||||
struct cpu_counter
|
||||
// Total number of CPU threads
|
||||
static atomic_t<u64, 64> s_cpu_counter{0};
|
||||
|
||||
// List of posted tasks for suspend_all
|
||||
//static atomic_t<cpu_thread::suspend_work*> s_cpu_work[128]{};
|
||||
|
||||
// Linked list of pushed tasks for suspend_all
|
||||
static atomic_t<cpu_thread::suspend_work*> s_pushed{};
|
||||
|
||||
// Lock for suspend_all operations
|
||||
static shared_mutex s_cpu_lock;
|
||||
|
||||
// Bit allocator for threads which need to be suspended
|
||||
static atomic_t<u128> s_cpu_bits{};
|
||||
|
||||
// List of active threads which need to be suspended
|
||||
static atomic_t<cpu_thread*> s_cpu_list[128]{};
|
||||
|
||||
namespace cpu_counter
|
||||
{
|
||||
// For synchronizing suspend_all operation
|
||||
alignas(64) shared_mutex cpu_suspend_lock;
|
||||
|
||||
// Workload linked list
|
||||
alignas(64) atomic_t<cpu_thread::suspend_work*> cpu_suspend_work{};
|
||||
|
||||
// Semaphore for global thread array (global counter)
|
||||
alignas(64) atomic_t<u32> cpu_array_sema{0};
|
||||
|
||||
// Semaphore subdivision for each array slot (64 x N in total)
|
||||
alignas(64) atomic_t<u64> cpu_array_bits[3]{};
|
||||
|
||||
// Copy of array bits for internal use
|
||||
alignas(64) u64 cpu_copy_bits[3]{};
|
||||
|
||||
// All registered threads
|
||||
atomic_t<cpu_thread*> cpu_array[sizeof(cpu_array_bits) * 8]{};
|
||||
|
||||
u64 add(cpu_thread* _this, bool restore = false) noexcept
|
||||
void add(cpu_thread* _this) noexcept
|
||||
{
|
||||
u64 array_slot = -1;
|
||||
|
||||
if (!restore && !cpu_array_sema.try_inc(sizeof(cpu_counter::cpu_array_bits) * 8))
|
||||
switch (_this->id_type())
|
||||
{
|
||||
sys_log.fatal("Too many threads.");
|
||||
return array_slot;
|
||||
case 1:
|
||||
case 2:
|
||||
break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
for (u32 i = 0;; i = (i + 1) % ::size32(cpu_array_bits))
|
||||
std::lock_guard lock(s_cpu_lock);
|
||||
|
||||
u32 id = -1;
|
||||
|
||||
for (u64 i = 0;; i++)
|
||||
{
|
||||
const auto [bits, ok] = cpu_array_bits[i].fetch_op([](u64& bits) -> u64
|
||||
const auto [bits, ok] = s_cpu_bits.fetch_op([](u128& bits)
|
||||
{
|
||||
if (~bits) [[likely]]
|
||||
{
|
||||
@@ -295,95 +305,99 @@ struct cpu_counter
|
||||
if (ok) [[likely]]
|
||||
{
|
||||
// Get actual slot number
|
||||
array_slot = i * 64 + std::countr_one(bits);
|
||||
id = utils::ctz128(~bits);
|
||||
|
||||
// Register thread
|
||||
if (cpu_array[array_slot].compare_and_swap_test(nullptr, _this)) [[likely]]
|
||||
if (s_cpu_list[id].compare_and_swap_test(nullptr, _this)) [[likely]]
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
sys_log.fatal("Unexpected slot registration failure (%u).", array_slot);
|
||||
cpu_array_bits[array_slot / 64] &= ~(1ull << (array_slot % 64));
|
||||
sys_log.fatal("Unexpected slot registration failure (%u).", id);
|
||||
id = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i > 50)
|
||||
{
|
||||
sys_log.fatal("Too many threads.");
|
||||
return;
|
||||
}
|
||||
|
||||
busy_wait(300);
|
||||
}
|
||||
|
||||
if (!restore)
|
||||
{
|
||||
// First time (thread created)
|
||||
_this->state += cpu_flag::wait;
|
||||
cpu_suspend_lock.lock_unlock();
|
||||
}
|
||||
|
||||
return array_slot;
|
||||
s_tls_thread_slot = id;
|
||||
}
|
||||
|
||||
void remove(cpu_thread* _this, u64 slot) noexcept
|
||||
static void remove_cpu_bit(u32 bit)
|
||||
{
|
||||
// Unregister and wait if necessary
|
||||
_this->state += cpu_flag::wait;
|
||||
s_cpu_bits.atomic_op([=](u128& val)
|
||||
{
|
||||
val &= ~(u128{1} << (bit % 128));
|
||||
});
|
||||
}
|
||||
|
||||
std::lock_guard lock(cpu_suspend_lock);
|
||||
void remove(cpu_thread* _this) noexcept
|
||||
{
|
||||
// Return if not registered
|
||||
const u32 slot = s_tls_thread_slot;
|
||||
|
||||
if (!cpu_array[slot].compare_and_swap_test(_this, nullptr))
|
||||
if (slot == umax)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (slot >= std::size(s_cpu_list))
|
||||
{
|
||||
sys_log.fatal("Index out of bounds (%u).", slot);
|
||||
return;
|
||||
}
|
||||
|
||||
// Asynchronous unregister
|
||||
if (!s_cpu_list[slot].compare_and_swap_test(_this, nullptr))
|
||||
{
|
||||
sys_log.fatal("Inconsistency for array slot %u", slot);
|
||||
return;
|
||||
}
|
||||
|
||||
cpu_array_bits[slot / 64] &= ~(1ull << (slot % 64));
|
||||
cpu_array_sema--;
|
||||
remove_cpu_bit(slot);
|
||||
|
||||
s_tls_thread_slot = -1;
|
||||
}
|
||||
|
||||
// Remove temporarily
|
||||
void remove(cpu_thread* _this) noexcept
|
||||
template <typename F>
|
||||
u128 for_all_cpu(/*mutable*/ u128 copy, F func) noexcept
|
||||
{
|
||||
// Unregister temporarily (called from check_state)
|
||||
const u64 index = s_tls_thread_slot;
|
||||
|
||||
if (index >= std::size(cpu_array))
|
||||
for (u128 bits = copy; bits; bits &= bits - 1)
|
||||
{
|
||||
sys_log.fatal("Index out of bounds (%u).", index);
|
||||
return;
|
||||
}
|
||||
const u32 index = utils::ctz128(bits);
|
||||
|
||||
if (cpu_array[index].load() == _this && cpu_array[index].compare_and_swap_test(_this, nullptr))
|
||||
{
|
||||
cpu_array_bits[index / 64] &= ~(1ull << (index % 64));
|
||||
return;
|
||||
}
|
||||
|
||||
sys_log.fatal("Thread not found in cpu_array (%s).", _this->get_name());
|
||||
}
|
||||
};
|
||||
|
||||
template <bool UseCopy = false, typename F>
|
||||
void for_all_cpu(F func) noexcept
|
||||
{
|
||||
const auto ctr = g_fxo->get<cpu_counter>();
|
||||
|
||||
for (u32 i = 0; i < ::size32(ctr->cpu_array_bits); i++)
|
||||
{
|
||||
for (u64 bits = (UseCopy ? ctr->cpu_copy_bits[i] : ctr->cpu_array_bits[i].load()); bits; bits &= bits - 1)
|
||||
{
|
||||
const u64 index = i * 64 + std::countr_zero(bits);
|
||||
|
||||
if (cpu_thread* cpu = ctr->cpu_array[index].load())
|
||||
if (cpu_thread* cpu = s_cpu_list[index].load())
|
||||
{
|
||||
if constexpr (std::is_invocable_v<F, cpu_thread*, u64>)
|
||||
if constexpr (std::is_invocable_v<F, cpu_thread*, u32>)
|
||||
{
|
||||
func(cpu, index);
|
||||
if (!func(cpu, index))
|
||||
copy &= ~(u128{1} << index);
|
||||
continue;
|
||||
}
|
||||
|
||||
if constexpr (std::is_invocable_v<F, cpu_thread*>)
|
||||
{
|
||||
func(cpu);
|
||||
if (!func(cpu))
|
||||
copy &= ~(u128{1} << index);
|
||||
continue;
|
||||
}
|
||||
|
||||
sys_log.fatal("cpu_counter::for_all_cpu: bad callback");
|
||||
}
|
||||
else
|
||||
{
|
||||
copy &= ~(u128{1} << index);
|
||||
}
|
||||
}
|
||||
|
||||
return copy;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,10 +429,15 @@ void cpu_thread::operator()()
|
||||
}
|
||||
}
|
||||
|
||||
while (!g_fxo->get<cpu_counter>() && !g_fxo->get<cpu_profiler>())
|
||||
while (!g_fxo->get<cpu_profiler>())
|
||||
{
|
||||
if (Emu.IsStopped())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Can we have a little race, right? First thread is started concurrently with g_fxo->init()
|
||||
std::this_thread::sleep_for(1ms);
|
||||
thread_ctrl::wait_for(1000);
|
||||
}
|
||||
|
||||
switch (id_type())
|
||||
@@ -441,14 +460,9 @@ void cpu_thread::operator()()
|
||||
}
|
||||
|
||||
// Register thread in g_cpu_array
|
||||
s_tls_thread_slot = g_fxo->get<cpu_counter>()->add(this);
|
||||
s_cpu_counter++;
|
||||
|
||||
if (s_tls_thread_slot == umax)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
atomic_storage_futex::set_notify_callback([](const void*, u64 progress)
|
||||
atomic_wait_engine::set_notify_callback([](const void*, u64 progress)
|
||||
{
|
||||
static thread_local bool wait_set = false;
|
||||
|
||||
@@ -466,7 +480,7 @@ void cpu_thread::operator()()
|
||||
if (progress == umax && std::exchange(wait_set, false))
|
||||
{
|
||||
// Operation finished: need to clean wait flag
|
||||
verify(HERE), !_cpu->check_state();
|
||||
ensure(!_cpu->check_state());
|
||||
return;
|
||||
}
|
||||
});
|
||||
@@ -486,22 +500,16 @@ void cpu_thread::operator()()
|
||||
|
||||
if (progress == umax && std::exchange(wait_set, false))
|
||||
{
|
||||
verify(HERE), !_cpu->check_state();
|
||||
ensure(!_cpu->check_state());
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
static thread_local struct thread_cleanup_t
|
||||
{
|
||||
cpu_thread* _this;
|
||||
cpu_thread* _this = nullptr;
|
||||
std::string name;
|
||||
|
||||
thread_cleanup_t(cpu_thread* _this)
|
||||
: _this(_this)
|
||||
, name(thread_ctrl::get_name())
|
||||
{
|
||||
}
|
||||
|
||||
void cleanup()
|
||||
{
|
||||
if (_this == nullptr)
|
||||
@@ -514,11 +522,22 @@ void cpu_thread::operator()()
|
||||
ptr->compare_and_swap(_this, nullptr);
|
||||
}
|
||||
|
||||
atomic_storage_futex::set_notify_callback(nullptr);
|
||||
atomic_wait_engine::set_notify_callback(nullptr);
|
||||
|
||||
g_tls_log_control = [](const char*, u64){};
|
||||
|
||||
g_fxo->get<cpu_counter>()->remove(_this, s_tls_thread_slot);
|
||||
if (s_tls_thread_slot != umax)
|
||||
{
|
||||
cpu_counter::remove(_this);
|
||||
}
|
||||
|
||||
s_cpu_lock.lock_unlock();
|
||||
|
||||
s_cpu_counter--;
|
||||
|
||||
g_tls_current_cpu_thread = nullptr;
|
||||
|
||||
g_threads_deleted++;
|
||||
|
||||
_this = nullptr;
|
||||
}
|
||||
@@ -527,17 +546,27 @@ void cpu_thread::operator()()
|
||||
{
|
||||
if (_this)
|
||||
{
|
||||
sys_log.warning("CPU Thread '%s' terminated abnormally:\n%s", name, _this->dump_all());
|
||||
sys_log.warning("CPU Thread '%s' terminated abnormally!", name);
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
} cleanup{this};
|
||||
} cleanup;
|
||||
|
||||
cleanup._this = this;
|
||||
cleanup.name = thread_ctrl::get_name();
|
||||
|
||||
// Check thread status
|
||||
while (!(state & (cpu_flag::exit + cpu_flag::dbg_global_stop)) && thread_ctrl::state() != thread_state::aborting)
|
||||
while (!(state & cpu_flag::exit) && thread_ctrl::state() != thread_state::aborting)
|
||||
{
|
||||
// Check stop status
|
||||
if (!(state & cpu_flag::stop))
|
||||
const auto state0 = +state;
|
||||
|
||||
if (is_stopped(state0 - cpu_flag::stop))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!(state0 & cpu_flag::stop))
|
||||
{
|
||||
cpu_task();
|
||||
|
||||
@@ -549,7 +578,7 @@ void cpu_thread::operator()()
|
||||
continue;
|
||||
}
|
||||
|
||||
thread_ctrl::wait();
|
||||
thread_ctrl::wait_on(state, state0);
|
||||
|
||||
if (state & cpu_flag::ret && state.test_and_reset(cpu_flag::ret))
|
||||
{
|
||||
@@ -563,8 +592,6 @@ void cpu_thread::operator()()
|
||||
|
||||
cpu_thread::~cpu_thread()
|
||||
{
|
||||
vm::cleanup_unlock(*this);
|
||||
g_threads_deleted++;
|
||||
}
|
||||
|
||||
cpu_thread::cpu_thread(u32 id)
|
||||
@@ -573,29 +600,55 @@ cpu_thread::cpu_thread(u32 id)
|
||||
g_threads_created++;
|
||||
}
|
||||
|
||||
void cpu_thread::cpu_wait(bs_t<cpu_flag> old)
|
||||
{
|
||||
thread_ctrl::wait_on(state, old);
|
||||
}
|
||||
|
||||
bool cpu_thread::check_state() noexcept
|
||||
{
|
||||
if (state & cpu_flag::dbg_pause)
|
||||
{
|
||||
g_fxo->get<gdb_server>()->pause_from(this);
|
||||
}
|
||||
|
||||
bool cpu_sleep_called = false;
|
||||
bool cpu_can_stop = true;
|
||||
bool escape, retval;
|
||||
u64 susp_ctr = -1;
|
||||
|
||||
while (true)
|
||||
{
|
||||
// Process all flags in a single atomic op
|
||||
bs_t<cpu_flag> state1;
|
||||
const auto state0 = state.fetch_op([&](bs_t<cpu_flag>& flags)
|
||||
{
|
||||
bool store = false;
|
||||
|
||||
if (flags & cpu_flag::pause && !(flags & cpu_flag::wait))
|
||||
if (flags & cpu_flag::pause && s_tls_thread_slot != umax)
|
||||
{
|
||||
// Save value before state is saved and cpu_flag::wait is observed
|
||||
susp_ctr = g_suspend_counter;
|
||||
if (s_tls_sctr == umax)
|
||||
{
|
||||
u64 ctr = g_suspend_counter;
|
||||
|
||||
if (flags & cpu_flag::wait)
|
||||
{
|
||||
if ((ctr & 3) == 2)
|
||||
{
|
||||
s_tls_sctr = ctr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
s_tls_sctr = ctr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cleanup after asynchronous remove()
|
||||
if (flags & cpu_flag::pause && s_tls_thread_slot == umax)
|
||||
{
|
||||
flags -= cpu_flag::pause;
|
||||
store = true;
|
||||
}
|
||||
|
||||
s_tls_sctr = -1;
|
||||
}
|
||||
|
||||
if (flags & cpu_flag::temp) [[unlikely]]
|
||||
@@ -607,18 +660,38 @@ bool cpu_thread::check_state() noexcept
|
||||
store = true;
|
||||
}
|
||||
|
||||
if (flags & cpu_flag::signal)
|
||||
if (cpu_can_stop && flags & cpu_flag::signal)
|
||||
{
|
||||
flags -= cpu_flag::signal;
|
||||
cpu_sleep_called = false;
|
||||
store = true;
|
||||
}
|
||||
|
||||
// Atomically clean wait flag and escape
|
||||
if (!(flags & (cpu_flag::exit + cpu_flag::dbg_global_stop + cpu_flag::ret + cpu_flag::stop)))
|
||||
// Can't process dbg_step if we only paused temporarily
|
||||
if (cpu_can_stop && flags & cpu_flag::dbg_step)
|
||||
{
|
||||
// Check pause flags which hold thread inside check_state
|
||||
if (flags & (cpu_flag::pause + cpu_flag::suspend + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause + cpu_flag::memory))
|
||||
if (u32 pc = get_pc(), *pc2 = get_pc2(); pc != umax && pc2)
|
||||
{
|
||||
if (pc != *pc2)
|
||||
{
|
||||
flags -= cpu_flag::dbg_step;
|
||||
flags += cpu_flag::dbg_pause;
|
||||
store = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Can't test, ignore flag
|
||||
flags -= cpu_flag::dbg_step;
|
||||
store = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Atomically clean wait flag and escape
|
||||
if (!(flags & (cpu_flag::exit + cpu_flag::ret + cpu_flag::stop)))
|
||||
{
|
||||
// Check pause flags which hold thread inside check_state (ignore suspend on cpu_flag::temp)
|
||||
if (flags & (cpu_flag::pause + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause + cpu_flag::memory + (cpu_can_stop ? cpu_flag::suspend : cpu_flag::pause)))
|
||||
{
|
||||
if (!(flags & cpu_flag::wait))
|
||||
{
|
||||
@@ -627,6 +700,7 @@ bool cpu_thread::check_state() noexcept
|
||||
}
|
||||
|
||||
escape = false;
|
||||
state1 = flags;
|
||||
return store;
|
||||
}
|
||||
|
||||
@@ -649,50 +723,50 @@ bool cpu_thread::check_state() noexcept
|
||||
retval = cpu_can_stop;
|
||||
}
|
||||
|
||||
if (cpu_can_stop && flags & cpu_flag::dbg_step)
|
||||
{
|
||||
// Can't process dbg_step if we only paused temporarily
|
||||
flags += cpu_flag::dbg_pause;
|
||||
flags -= cpu_flag::dbg_step;
|
||||
store = true;
|
||||
}
|
||||
|
||||
escape = true;
|
||||
state1 = flags;
|
||||
return store;
|
||||
}).first;
|
||||
|
||||
if (escape)
|
||||
{
|
||||
if (s_tls_thread_slot == umax)
|
||||
if (s_tls_thread_slot == umax && !retval)
|
||||
{
|
||||
// Restore thread in the suspend list
|
||||
std::lock_guard lock(g_fxo->get<cpu_counter>()->cpu_suspend_lock);
|
||||
s_tls_thread_slot = g_fxo->get<cpu_counter>()->add(this, true);
|
||||
cpu_counter::add(this);
|
||||
}
|
||||
|
||||
verify(HERE), cpu_can_stop || !retval;
|
||||
if (retval)
|
||||
{
|
||||
cpu_on_stop();
|
||||
}
|
||||
|
||||
ensure(cpu_can_stop || !retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
if (!cpu_sleep_called && state0 & cpu_flag::suspend)
|
||||
if (cpu_can_stop && !cpu_sleep_called && state0 & cpu_flag::suspend)
|
||||
{
|
||||
cpu_sleep();
|
||||
cpu_sleep_called = true;
|
||||
|
||||
if (cpu_can_stop && s_tls_thread_slot != umax)
|
||||
if (s_tls_thread_slot != umax)
|
||||
{
|
||||
// Exclude inactive threads from the suspend list (optimization)
|
||||
std::lock_guard lock(g_fxo->get<cpu_counter>()->cpu_suspend_lock);
|
||||
g_fxo->get<cpu_counter>()->remove(this);
|
||||
s_tls_thread_slot = -1;
|
||||
cpu_counter::remove(this);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (state0 & (cpu_flag::suspend + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause))
|
||||
if (state0 & ((cpu_can_stop ? cpu_flag::suspend : cpu_flag::dbg_pause) + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause))
|
||||
{
|
||||
thread_ctrl::wait();
|
||||
if (state0 & cpu_flag::dbg_pause)
|
||||
{
|
||||
g_fxo->get<gdb_server>()->pause_from(this);
|
||||
}
|
||||
|
||||
cpu_wait(state1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -705,38 +779,29 @@ bool cpu_thread::check_state() noexcept
|
||||
// If only cpu_flag::pause was set, wait on suspend counter instead
|
||||
if (state0 & cpu_flag::pause)
|
||||
{
|
||||
if (state0 & cpu_flag::wait)
|
||||
{
|
||||
// Otherwise, value must be reliable because cpu_flag::wait hasn't been observed yet
|
||||
susp_ctr = -1;
|
||||
}
|
||||
|
||||
// Hard way
|
||||
if (susp_ctr == umax) [[unlikely]]
|
||||
{
|
||||
g_fxo->get<cpu_counter>()->cpu_suspend_lock.lock_unlock();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Wait for current suspend_all operation
|
||||
for (u64 i = 0;; i++)
|
||||
{
|
||||
if (i < 20)
|
||||
u64 ctr = g_suspend_counter;
|
||||
|
||||
if (ctr >> 2 == s_tls_sctr >> 2 && state & cpu_flag::pause)
|
||||
{
|
||||
busy_wait(300);
|
||||
if (i < 20 || ctr & 1)
|
||||
{
|
||||
busy_wait(300);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: fix the workaround
|
||||
g_suspend_counter.wait(ctr, -4, atomic_wait_timeout{100});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_suspend_counter.wait(susp_ctr);
|
||||
}
|
||||
|
||||
if (!(state & cpu_flag::pause))
|
||||
{
|
||||
s_tls_sctr = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
susp_ctr = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -753,14 +818,17 @@ void cpu_thread::notify()
|
||||
{
|
||||
thread_ctrl::notify(*static_cast<named_thread<spu_thread>*>(this));
|
||||
}
|
||||
else
|
||||
else if (id_type() != 0x55)
|
||||
{
|
||||
fmt::throw_exception("Invalid cpu_thread type" HERE);
|
||||
fmt::throw_exception("Invalid cpu_thread type");
|
||||
}
|
||||
}
|
||||
|
||||
void cpu_thread::abort()
|
||||
{
|
||||
state += cpu_flag::exit;
|
||||
state.notify_one(cpu_flag::exit);
|
||||
|
||||
// Downcast to correct type
|
||||
if (id_type() == 1)
|
||||
{
|
||||
@@ -772,7 +840,7 @@ void cpu_thread::abort()
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Invalid cpu_thread type" HERE);
|
||||
fmt::throw_exception("Invalid cpu_thread type");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -789,13 +857,71 @@ std::string cpu_thread::get_name() const
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt::throw_exception("Invalid cpu_thread type" HERE);
|
||||
fmt::throw_exception("Invalid cpu_thread type");
|
||||
}
|
||||
}
|
||||
|
||||
u32 cpu_thread::get_pc() const
|
||||
{
|
||||
const u32* pc = nullptr;
|
||||
|
||||
switch (id_type())
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
pc = &static_cast<const ppu_thread*>(this)->cia;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
pc = &static_cast<const spu_thread*>(this)->pc;
|
||||
break;
|
||||
}
|
||||
case 0x55:
|
||||
{
|
||||
const auto ctrl = static_cast<const rsx::thread*>(this)->ctrl;
|
||||
return ctrl ? ctrl->get : UINT32_MAX;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
return pc ? atomic_storage<u32>::load(*pc) : UINT32_MAX;
|
||||
}
|
||||
|
||||
u32* cpu_thread::get_pc2()
|
||||
{
|
||||
switch (id_type())
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
return &static_cast<ppu_thread*>(this)->dbg_step_pc;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
return &static_cast<spu_thread*>(this)->dbg_step_pc;
|
||||
}
|
||||
case 0x55:
|
||||
{
|
||||
const auto ctrl = static_cast<rsx::thread*>(this)->ctrl;
|
||||
return ctrl ? &static_cast<rsx::thread*>(this)->dbg_step_pc : nullptr;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string cpu_thread::dump_all() const
|
||||
{
|
||||
return {};
|
||||
std::string ret = cpu_thread::dump_misc();
|
||||
ret += '\n';
|
||||
ret += dump_misc();
|
||||
ret += '\n';
|
||||
ret += dump_regs();
|
||||
ret += '\n';
|
||||
ret += dump_callstack();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string cpu_thread::dump_regs() const
|
||||
@@ -805,7 +931,16 @@ std::string cpu_thread::dump_regs() const
|
||||
|
||||
std::string cpu_thread::dump_callstack() const
|
||||
{
|
||||
return {};
|
||||
std::string ret;
|
||||
|
||||
fmt::append(ret, "Call stack:\n=========\n0x%08x (0x0) called\n", get_pc());
|
||||
|
||||
for (const auto& sp : dump_callstack_list())
|
||||
{
|
||||
fmt::append(ret, "> from 0x%08x (sp=0x%08x)\n", sp.first, sp.second);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::pair<u32, u32>> cpu_thread::dump_callstack_list() const
|
||||
@@ -815,24 +950,18 @@ std::vector<std::pair<u32, u32>> cpu_thread::dump_callstack_list() const
|
||||
|
||||
std::string cpu_thread::dump_misc() const
|
||||
{
|
||||
return fmt::format("Type: %s\n" "State: %s\n", typeid(*this).name(), state.load());
|
||||
return fmt::format("Type: %s\n" "State: %s\n", id_type() == 1 ? "PPU" : id_type() == 2 ? "SPU" : "CPU", state.load());
|
||||
}
|
||||
|
||||
bool cpu_thread::suspend_work::push(cpu_thread* _this, bool cancel_if_not_suspended) noexcept
|
||||
bool cpu_thread::suspend_work::push(cpu_thread* _this) noexcept
|
||||
{
|
||||
// Can't allow pre-set wait bit (it'd be a problem)
|
||||
verify(HERE), !_this || !(_this->state & cpu_flag::wait);
|
||||
|
||||
// cpu_counter object
|
||||
const auto ctr = g_fxo->get<cpu_counter>();
|
||||
|
||||
// Try to push workload
|
||||
auto& queue = ctr->cpu_suspend_work;
|
||||
ensure(!_this || !(_this->state & cpu_flag::wait));
|
||||
|
||||
do
|
||||
{
|
||||
// Load current head
|
||||
next = queue.load();
|
||||
next = s_pushed.load();
|
||||
|
||||
if (!next && cancel_if_not_suspended) [[unlikely]]
|
||||
{
|
||||
@@ -843,11 +972,11 @@ bool cpu_thread::suspend_work::push(cpu_thread* _this, bool cancel_if_not_suspen
|
||||
if (!_this && next)
|
||||
{
|
||||
// If _this == nullptr, it only works if this is the first workload pushed
|
||||
ctr->cpu_suspend_lock.lock_unlock();
|
||||
s_cpu_lock.lock_unlock();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
while (!queue.compare_and_swap_test(next, this));
|
||||
while (!s_pushed.compare_and_swap_test(next, this));
|
||||
|
||||
if (!next)
|
||||
{
|
||||
@@ -855,63 +984,68 @@ bool cpu_thread::suspend_work::push(cpu_thread* _this, bool cancel_if_not_suspen
|
||||
perf_meter<"SUSPEND"_u64> perf0;
|
||||
|
||||
// First thread to push the work to the workload list pauses all threads and processes it
|
||||
std::lock_guard lock(ctr->cpu_suspend_lock);
|
||||
std::lock_guard lock(s_cpu_lock);
|
||||
|
||||
u128 copy = s_cpu_bits.load();
|
||||
|
||||
// Try to prefetch cpu->state earlier
|
||||
for_all_cpu([&](cpu_thread* cpu)
|
||||
copy = cpu_counter::for_all_cpu(copy, [&](cpu_thread* cpu)
|
||||
{
|
||||
if (cpu != _this)
|
||||
{
|
||||
_m_prefetchw(&cpu->state);
|
||||
utils::prefetch_write(&cpu->state);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Copy of thread bits
|
||||
decltype(ctr->cpu_copy_bits) copy2{};
|
||||
// Initialization (first increment)
|
||||
g_suspend_counter += 2;
|
||||
|
||||
for (u32 i = 0; i < ::size32(ctr->cpu_copy_bits); i++)
|
||||
{
|
||||
copy2[i] = ctr->cpu_copy_bits[i] = ctr->cpu_array_bits[i].load();
|
||||
}
|
||||
// Copy snapshot for finalization
|
||||
u128 copy2 = copy;
|
||||
|
||||
for_all_cpu([&](cpu_thread* cpu, u64 index)
|
||||
copy = cpu_counter::for_all_cpu(copy, [&](cpu_thread* cpu, u32 index)
|
||||
{
|
||||
if (cpu == _this || cpu->state.fetch_add(cpu_flag::pause) & cpu_flag::wait)
|
||||
if (cpu->state.fetch_add(cpu_flag::pause) & cpu_flag::wait)
|
||||
{
|
||||
// Clear bits as long as wait flag is set
|
||||
ctr->cpu_copy_bits[index / 64] &= ~(1ull << (index % 64));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cpu == _this)
|
||||
{
|
||||
copy2[index / 64] &= ~(1ull << (index % 64));
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
while (true)
|
||||
while (copy)
|
||||
{
|
||||
// Check only CPUs which haven't acknowledged their waiting state yet
|
||||
for_all_cpu<true>([&](cpu_thread* cpu, u64 index)
|
||||
copy = cpu_counter::for_all_cpu(copy, [&](cpu_thread* cpu, u32 index)
|
||||
{
|
||||
if (cpu->state & cpu_flag::wait)
|
||||
{
|
||||
ctr->cpu_copy_bits[index / 64] &= ~(1ull << (index % 64));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
if (!std::accumulate(std::begin(ctr->cpu_copy_bits), std::end(ctr->cpu_copy_bits), u64{0}, std::bit_or()))
|
||||
if (!copy)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
_mm_pause();
|
||||
utils::pause();
|
||||
}
|
||||
|
||||
// Extract queue and reverse element order (FILO to FIFO) (TODO: maybe leave order as is?)
|
||||
auto* head = queue.exchange(nullptr);
|
||||
// Second increment: all threads paused
|
||||
g_suspend_counter++;
|
||||
|
||||
s8 min_prio = head->prio;
|
||||
s8 max_prio = head->prio;
|
||||
// Extract queue and reverse element order (FILO to FIFO) (TODO: maybe leave order as is?)
|
||||
auto* head = s_pushed.exchange(nullptr);
|
||||
|
||||
u8 min_prio = head->prio;
|
||||
u8 max_prio = head->prio;
|
||||
|
||||
if (auto* prev = head->next)
|
||||
{
|
||||
@@ -925,8 +1059,8 @@ bool cpu_thread::suspend_work::push(cpu_thread* _this, bool cancel_if_not_suspen
|
||||
head = std::exchange(prev, pre2);
|
||||
|
||||
// Fill priority range
|
||||
min_prio = std::min<s8>(min_prio, head->prio);
|
||||
max_prio = std::max<s8>(max_prio, head->prio);
|
||||
min_prio = std::min<u8>(min_prio, head->prio);
|
||||
max_prio = std::max<u8>(max_prio, head->prio);
|
||||
}
|
||||
while (prev);
|
||||
}
|
||||
@@ -936,13 +1070,14 @@ bool cpu_thread::suspend_work::push(cpu_thread* _this, bool cancel_if_not_suspen
|
||||
{
|
||||
for (u32 i = 0; i < work->prf_size; i++)
|
||||
{
|
||||
_m_prefetchw(work->prf_list[0]);
|
||||
utils::prefetch_write(work->prf_list[0]);
|
||||
}
|
||||
}
|
||||
|
||||
for_all_cpu<true>([&](cpu_thread* cpu)
|
||||
cpu_counter::for_all_cpu(copy2, [&](cpu_thread* cpu)
|
||||
{
|
||||
_m_prefetchw(&cpu->state);
|
||||
utils::prefetch_write(&cpu->state);
|
||||
return true;
|
||||
});
|
||||
|
||||
// Execute all stored workload
|
||||
@@ -959,25 +1094,22 @@ bool cpu_thread::suspend_work::push(cpu_thread* _this, bool cancel_if_not_suspen
|
||||
}
|
||||
}
|
||||
|
||||
// Not sure if needed, may be overkill. Some workloads may execute instructions with non-temporal hint.
|
||||
_mm_sfence();
|
||||
// Finalization (last increment)
|
||||
ensure(g_suspend_counter++ & 1);
|
||||
|
||||
// Finalization
|
||||
g_suspend_counter++;
|
||||
|
||||
// Exact bitset for flag pause removal
|
||||
std::memcpy(ctr->cpu_copy_bits, copy2, sizeof(copy2));
|
||||
|
||||
for_all_cpu<true>([&](cpu_thread* cpu)
|
||||
cpu_counter::for_all_cpu(copy2, [&](cpu_thread* cpu)
|
||||
{
|
||||
cpu->state -= cpu_flag::pause;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// Seems safe to set pause on self because wait flag hasn't been observed yet
|
||||
_this->state += cpu_flag::pause + cpu_flag::temp;
|
||||
s_tls_sctr = g_suspend_counter;
|
||||
_this->state += cpu_flag::pause + cpu_flag::wait + cpu_flag::temp;
|
||||
_this->check_state();
|
||||
s_tls_sctr = -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -995,11 +1127,8 @@ void cpu_thread::stop_all() noexcept
|
||||
}
|
||||
else
|
||||
{
|
||||
std::lock_guard lock(g_fxo->get<cpu_counter>()->cpu_suspend_lock);
|
||||
|
||||
auto on_stop = [](u32, cpu_thread& cpu)
|
||||
{
|
||||
cpu.state += cpu_flag::dbg_global_stop;
|
||||
cpu.abort();
|
||||
};
|
||||
|
||||
@@ -1009,15 +1138,13 @@ void cpu_thread::stop_all() noexcept
|
||||
|
||||
sys_log.notice("All CPU threads have been signaled.");
|
||||
|
||||
while (g_fxo->get<cpu_counter>()->cpu_array_sema)
|
||||
while (s_cpu_counter)
|
||||
{
|
||||
std::this_thread::sleep_for(10ms);
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
sys_log.notice("All CPU threads have been stopped. [+: %u]", +g_threads_created);
|
||||
|
||||
std::lock_guard lock(g_fxo->get<cpu_counter>()->cpu_suspend_lock);
|
||||
|
||||
g_threads_deleted -= g_threads_created.load();
|
||||
g_threads_created = 0;
|
||||
}
|
||||
@@ -1026,7 +1153,7 @@ void cpu_thread::flush_profilers() noexcept
|
||||
{
|
||||
if (!g_fxo->get<cpu_profiler>())
|
||||
{
|
||||
profiler.fatal("cpu_thread::flush_profilers() has been called incorrectly." HERE);
|
||||
profiler.fatal("cpu_thread::flush_profilers() has been called incorrectly.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+69
-17
@@ -1,4 +1,4 @@
|
||||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "../Utilities/Thread.h"
|
||||
#include "../Utilities/bit_set.h"
|
||||
@@ -19,13 +19,24 @@ enum class cpu_flag : u32
|
||||
memory, // Thread must unlock memory mutex
|
||||
|
||||
dbg_global_pause, // Emulation paused
|
||||
dbg_global_stop, // Emulation stopped
|
||||
dbg_pause, // Thread paused
|
||||
dbg_step, // Thread forced to pause after one step (one instruction, etc)
|
||||
|
||||
__bitset_enum_max
|
||||
};
|
||||
|
||||
// Test stopped state
|
||||
constexpr bool is_stopped(bs_t<cpu_flag> state)
|
||||
{
|
||||
return !!(state & (cpu_flag::stop + cpu_flag::exit));
|
||||
}
|
||||
|
||||
// Test paused state
|
||||
constexpr bool is_paused(bs_t<cpu_flag> state)
|
||||
{
|
||||
return !!(state & (cpu_flag::suspend + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause));
|
||||
}
|
||||
|
||||
class cpu_thread
|
||||
{
|
||||
public:
|
||||
@@ -61,16 +72,25 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Test stopped state
|
||||
bool is_stopped() const
|
||||
// Wrappers
|
||||
static constexpr bool is_stopped(bs_t<cpu_flag> s)
|
||||
{
|
||||
return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::dbg_global_stop));
|
||||
return ::is_stopped(s);
|
||||
}
|
||||
|
||||
static constexpr bool is_paused(bs_t<cpu_flag> s)
|
||||
{
|
||||
return ::is_paused(s);
|
||||
}
|
||||
|
||||
bool is_stopped() const
|
||||
{
|
||||
return ::is_stopped(state);
|
||||
}
|
||||
|
||||
// Test paused state
|
||||
bool is_paused() const
|
||||
{
|
||||
return !!(state & (cpu_flag::suspend + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause));
|
||||
return ::is_paused(state);
|
||||
}
|
||||
|
||||
bool has_pause_flag() const
|
||||
@@ -84,6 +104,9 @@ public:
|
||||
return id >> 24;
|
||||
}
|
||||
|
||||
u32 get_pc() const;
|
||||
u32* get_pc2(); // Last PC before stepping for the debugger (may be null)
|
||||
|
||||
void notify();
|
||||
|
||||
private:
|
||||
@@ -120,11 +143,19 @@ public:
|
||||
// Callback for cpu_flag::ret
|
||||
virtual void cpu_return() {}
|
||||
|
||||
// Callback for thread_ctrl::wait or RSX wait
|
||||
virtual void cpu_wait(bs_t<cpu_flag> flags);
|
||||
|
||||
// Callback for function abortion stats on Emu.Stop()
|
||||
virtual void cpu_on_stop() {}
|
||||
|
||||
// For internal use
|
||||
struct suspend_work
|
||||
{
|
||||
// Task priority
|
||||
s8 prio;
|
||||
u8 prio;
|
||||
bool cancel_if_not_suspended;
|
||||
bool was_posted;
|
||||
|
||||
// Size of prefetch list workload
|
||||
u32 prf_size;
|
||||
@@ -140,16 +171,21 @@ public:
|
||||
suspend_work* next;
|
||||
|
||||
// Internal method
|
||||
bool push(cpu_thread* _this, bool cancel_if_not_suspended = false) noexcept;
|
||||
bool push(cpu_thread* _this) noexcept;
|
||||
|
||||
// Called after suspend_post
|
||||
void post() noexcept;
|
||||
};
|
||||
|
||||
// Suspend all threads and execute op (may be executed by other thread than caller!)
|
||||
template <s8 Prio = 0, typename F>
|
||||
template <u8 Prio = 0, typename F>
|
||||
static auto suspend_all(cpu_thread* _this, std::initializer_list<void*> hints, F op)
|
||||
{
|
||||
constexpr u8 prio = Prio > 3 ? 3 : Prio;
|
||||
|
||||
if constexpr (std::is_void_v<std::invoke_result_t<F>>)
|
||||
{
|
||||
suspend_work work{Prio, ::size32(hints), hints.begin(), &op, nullptr, [](void* func, void*)
|
||||
suspend_work work{prio, false, false, ::size32(hints), hints.begin(), &op, nullptr, [](void* func, void*)
|
||||
{
|
||||
std::invoke(*static_cast<F*>(func));
|
||||
}};
|
||||
@@ -161,7 +197,7 @@ public:
|
||||
{
|
||||
std::invoke_result_t<F> result;
|
||||
|
||||
suspend_work work{Prio, ::size32(hints), hints.begin(), &op, &result, [](void* func, void* res_buf)
|
||||
suspend_work work{prio, false, false, ::size32(hints), hints.begin(), &op, &result, [](void* func, void* res_buf)
|
||||
{
|
||||
*static_cast<std::invoke_result_t<F>*>(res_buf) = std::invoke(*static_cast<F*>(func));
|
||||
}};
|
||||
@@ -171,22 +207,38 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
template <u8 Prio = 0, typename F>
|
||||
static suspend_work suspend_post(cpu_thread* _this, std::initializer_list<void*> hints, F& op)
|
||||
{
|
||||
constexpr u8 prio = Prio > 3 ? 3 : Prio;
|
||||
|
||||
static_assert(std::is_void_v<std::invoke_result_t<F>>, "cpu_thread::suspend_post only supports void as return type");
|
||||
|
||||
return suspend_work{prio, false, true, ::size32(hints), hints.begin(), &op, nullptr, [](void* func, void*)
|
||||
{
|
||||
std::invoke(*static_cast<F*>(func));
|
||||
}};
|
||||
}
|
||||
|
||||
// Push the workload only if threads are being suspended by suspend_all()
|
||||
template <s8 Prio = 0, typename F>
|
||||
template <u8 Prio = 0, typename F>
|
||||
static bool if_suspended(cpu_thread* _this, std::initializer_list<void*> hints, F op)
|
||||
{
|
||||
static_assert(std::is_void_v<std::invoke_result_t<F>>, "Unimplemented (must return void)");
|
||||
constexpr u8 prio = Prio > 3 ? 3 : Prio;
|
||||
|
||||
static_assert(std::is_void_v<std::invoke_result_t<F>>, "cpu_thread::if_suspended only supports void as return type");
|
||||
|
||||
{
|
||||
suspend_work work{Prio, ::size32(hints), hints.begin(), &op, nullptr, [](void* func, void*)
|
||||
suspend_work work{prio, true, false, ::size32(hints), hints.begin(), &op, nullptr, [](void* func, void*)
|
||||
{
|
||||
std::invoke(*static_cast<F*>(func));
|
||||
}};
|
||||
|
||||
return work.push(_this, true);
|
||||
return work.push(_this);
|
||||
}
|
||||
}
|
||||
|
||||
// Stop all threads with cpu_flag::dbg_global_stop
|
||||
// Stop all threads with cpu_flag::exit
|
||||
static void stop_all() noexcept;
|
||||
|
||||
// Send signal to the profiler(s) to flush results
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user