Compare commits
1362 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3020b58bc2 | |||
| 2e53d40353 | |||
| d9b394d42f | |||
| b462520f8a | |||
| 6dd52d7f93 | |||
| 4f01794713 | |||
| 3d9e3a16f1 | |||
| ab17b49e15 | |||
| fcd702c8a6 | |||
| 743928b379 | |||
| f908daf323 | |||
| 49e64b9e82 | |||
| 6828fbf658 | |||
| 0a2992839b | |||
| 1a6e53ec98 | |||
| 9ec2337192 | |||
| cbc8bf01a1 | |||
| 54fbde0de1 | |||
| d238791b7d | |||
| 42f56e357c | |||
| fbac1c304a | |||
| 71f69d1d48 | |||
| 34c49c77b2 | |||
| cc02ef64a5 | |||
| ff26bf411b | |||
| 7aa1707c2c | |||
| d6c4d8eec0 | |||
| c5755b3633 | |||
| 24e97b9e03 | |||
| 5848864f55 | |||
| ec41483e9b | |||
| 28a96f1543 | |||
| 435ea8d553 | |||
| 14ddeb07a5 | |||
| 15d0bdb7cf | |||
| d216398596 | |||
| 0477f8ed3c | |||
| 7ca2c444cc | |||
| c5074ba81f | |||
| ac53fc54dc | |||
| d5c879e8e7 | |||
| 2053de0885 | |||
| ef6a36241e | |||
| fbde1818a2 | |||
| 155f7a7d76 | |||
| 9b4868f017 | |||
| c951601fa4 | |||
| fb3dee0f3e | |||
| 5959411aeb | |||
| fbceec47b8 | |||
| 51a2b43d81 | |||
| 4231ea2eb6 | |||
| 8ee0ed84e4 | |||
| 32468adb53 | |||
| d770a42349 | |||
| 19d181fa4c | |||
| 83b541ea9d | |||
| af3e1fc580 | |||
| 7050575fdb | |||
| ee009ec99c | |||
| 198e9dce1d | |||
| 383c501a35 | |||
| 7c64c2f703 | |||
| d64e79bd9f | |||
| 489ded43b1 | |||
| d40aaf0391 | |||
| 6baba2875d | |||
| 1ea5e7404a | |||
| 55c324e062 | |||
| d496dbecad | |||
| 4872be9de3 | |||
| 688569e5c8 | |||
| 90f825aa49 | |||
| 78f7020ce6 | |||
| a92bf39efa | |||
| e836c33f37 | |||
| f271b650da | |||
| 14929a1497 | |||
| e768bdc80f | |||
| a605919729 | |||
| 39a3ed2d03 | |||
| 0a89f1db39 | |||
| 3cf0fb60a4 | |||
| fc2ef5529d | |||
| 8a3759f479 | |||
| a6208d2bd7 | |||
| 58d8d12665 | |||
| 8b31a9448a | |||
| 4819847c46 | |||
| d6bc6ec2c1 | |||
| 320fb5f94c | |||
| 4a0c4259f0 | |||
| cb627a6bd7 | |||
| e9364bd87b | |||
| 5e04a5f21d | |||
| 106de04485 | |||
| 8eb8755aea | |||
| 1b13b94095 | |||
| 69283accd0 | |||
| 2dd8a1afdb | |||
| 114dd2a723 | |||
| 6891371a79 | |||
| a285409f59 | |||
| a08e79f426 | |||
| f24491de84 | |||
| 83da7f9b63 | |||
| 1ae8850b3f | |||
| 140a581d23 | |||
| d3198ddf60 | |||
| 6d34dcd75f | |||
| 4aee4ed6d7 | |||
| 61de20a633 | |||
| de5dab35e0 | |||
| 47060cdc5f | |||
| 7dd349ae8e | |||
| 4e80858bed | |||
| e89a035e8b | |||
| f7c52d3bb7 | |||
| 6f8dd20f03 | |||
| b1a1c0251f | |||
| 0d0821e914 | |||
| 6891323c18 | |||
| 71c8885678 | |||
| 7c7cd4153e | |||
| ff0f1510e5 | |||
| 3338fdb936 | |||
| 6dfe32c6d2 | |||
| 95966a467e | |||
| ac0022483a | |||
| 17bfb3beaf | |||
| a122924c8e | |||
| 04d6c80da5 | |||
| a1fa10ac70 | |||
| 6e9b064a0f | |||
| 42c2df877f | |||
| c2ae7ee402 | |||
| 50f2be57f7 | |||
| b4a97c99d8 | |||
| 3aefd14b3d | |||
| 60f5929f81 | |||
| 6b323d7e2e | |||
| d7881c6714 | |||
| d0013679c0 | |||
| 0b3fbf1d4c | |||
| 69eb483089 | |||
| a2b4cf22b5 | |||
| d8ae94df5b | |||
| 03814e8d02 | |||
| 970d2a06e0 | |||
| 9853027f72 | |||
| 90c2324e47 | |||
| f5145943b2 | |||
| 960f26f84e | |||
| cdd4fd9867 | |||
| bfb3144f34 | |||
| 8f314c5101 | |||
| 2ec4f66427 | |||
| 7165001b68 | |||
| c077426e95 | |||
| c870bbb885 | |||
| 3fbc960c44 | |||
| 896c8991de | |||
| bec6c1a939 | |||
| fe9090bd39 | |||
| 44e34064de | |||
| a18ae0f6ac | |||
| 08b829dc22 | |||
| 9d27ac359b | |||
| 6c9c300fe0 | |||
| 17340c44cc | |||
| 90a3f3af30 | |||
| da1e97618b | |||
| 89bc333295 | |||
| 3350e7ee55 | |||
| ddebc334bf | |||
| 145ecb00fc | |||
| 75d0ff177d | |||
| 4d75e98647 | |||
| 3bfdcf698d | |||
| 51891039dd | |||
| 07fe5f87ae | |||
| de5a4fe083 | |||
| 5cf2d774f3 | |||
| ccc0383f75 | |||
| 680ca1d12a | |||
| c926868758 | |||
| 30269e5bad | |||
| 07f61db24a | |||
| 63f261a66d | |||
| 33f3a3e014 | |||
| 8646f51fa3 | |||
| 0aaae000b3 | |||
| db58cd7513 | |||
| d17093e65b | |||
| 34e01ba3d8 | |||
| 8b476b5bfa | |||
| ad97780c4f | |||
| 17cf24d0ed | |||
| c424652b43 | |||
| 3a9ae2df9e | |||
| 8276d29d4f | |||
| b9b06143d2 | |||
| 853c3f9e39 | |||
| c04c23f33d | |||
| 25a1d86986 | |||
| c2379b8337 | |||
| ae643135b5 | |||
| 91fd1465f2 | |||
| a4de495093 | |||
| 2cd551a222 | |||
| dd4ccb059e | |||
| 5810b29298 | |||
| 6702c14b88 | |||
| 8901cc9ae6 | |||
| 17ff1e41a5 | |||
| 782156b26f | |||
| 4f5e93bd09 | |||
| 662fe8cc95 | |||
| 695b4c1f06 | |||
| d366823949 | |||
| 33aa2f0b62 | |||
| 917ee472d4 | |||
| 2f7d621fc8 | |||
| 754cdea435 | |||
| 5f07f78c23 | |||
| b22f3d7a55 | |||
| 1344f15efd | |||
| f7803927d9 | |||
| 2ac773385f | |||
| 477c6fdedd | |||
| 78101a9e57 | |||
| d05071221e | |||
| 7d3a528871 | |||
| cc4bc41cf4 | |||
| 5fffef74a2 | |||
| ddbcbd0f1c | |||
| b60d3a3dae | |||
| 74c248150b | |||
| e77d5cf970 | |||
| d32a30bbeb | |||
| 868180eacd | |||
| c247ebad0c | |||
| 6a5ce475b8 | |||
| 3628a8593f | |||
| 58c42ad9c0 | |||
| fb52cbb8b2 | |||
| de465cb941 | |||
| dd5791a2cc | |||
| 380f90b097 | |||
| 86a34fbb45 | |||
| 5d87ef86b5 | |||
| df7d52b177 | |||
| f5addbf751 | |||
| a8c0dd649e | |||
| bbb3bdf008 | |||
| 6d2dcbd164 | |||
| be6b5922dd | |||
| 59be9dc36e | |||
| bd0d87ecaf | |||
| 61ca9df45c | |||
| 222c0e0fc5 | |||
| 12ecba7d49 | |||
| 214ea1e0f6 | |||
| 0870862898 | |||
| c58738807e | |||
| 223f17ac7e | |||
| c435b328c7 | |||
| 3792a85084 | |||
| 0d1cb8c878 | |||
| 16d7023a92 | |||
| 358afc045d | |||
| 0fbb8b9a18 | |||
| 9ef00b4a12 | |||
| 916851bd4a | |||
| 9bc0ec586a | |||
| f4eab09557 | |||
| f2980d57a1 | |||
| dbc9bdfe02 | |||
| 9e0b881b2b | |||
| 946ba3be29 | |||
| 0064976c43 | |||
| 4bdb6674eb | |||
| c7f7d7ef5b | |||
| 2c19fdf092 | |||
| 8fc9862614 | |||
| 55d84414ee | |||
| 8c0bdd71d9 | |||
| 3c9126d91f | |||
| 8b4836a12f | |||
| b2a7eee1ec | |||
| ed21bb309f | |||
| 7b3e4f5e3d | |||
| 30b3af0d12 | |||
| 541cb4062b | |||
| 4e9160104a | |||
| 86fa379c78 | |||
| 8733505d0a | |||
| 242611aa46 | |||
| baa5a261a5 | |||
| 3730b9d1da | |||
| 0d35363ea0 | |||
| 75504b3f5e | |||
| 4ca98e53a6 | |||
| 300a36d3d6 | |||
| 60c7a508a7 | |||
| 1fa18757fc | |||
| fbb7186e66 | |||
| 0961a43997 | |||
| 7037504dcf | |||
| 2385029eab | |||
| eed55a446c | |||
| bbcb6b6851 | |||
| b95630d84a | |||
| af1d3c2aa6 | |||
| ec3e5c547f | |||
| 963a87fed5 | |||
| 173d05b54f | |||
| 865bb47462 | |||
| 3e7ed8a831 | |||
| 3a49a77c07 | |||
| 6782b22637 | |||
| 4600094829 | |||
| daaa83b9ca | |||
| 3288050680 | |||
| b8c29ae957 | |||
| 47bacc0ef0 | |||
| 3d0dced4bd | |||
| 30bba09fed | |||
| 31b07f2c5c | |||
| f7063bb57b | |||
| e4ef85b6e0 | |||
| b7c2088489 | |||
| 59cd0a9c7f | |||
| af107df0b4 | |||
| 0e7e40f2cb | |||
| 0b788f5b43 | |||
| db5c2500c2 | |||
| a1496eb1f1 | |||
| 4aa89132d7 | |||
| 9b8eb089b1 | |||
| c1f08ce58d | |||
| 09a0d046f4 | |||
| 0741698f13 | |||
| c2ac05f734 | |||
| f8f5f9f418 | |||
| 7e6d6b45d4 | |||
| 361e80f7dc | |||
| 395b4bfa45 | |||
| ce2b790d8f | |||
| 1ffc1e82b0 | |||
| 8520ca7ef7 | |||
| 89fba1c385 | |||
| 00a0d37455 | |||
| dbb2251f93 | |||
| 7abf755a57 | |||
| 055f0e2e4a | |||
| 49f4da3016 | |||
| bf234dc668 | |||
| 11bd65c2b5 | |||
| e0d1ac676e | |||
| 494cbac78b | |||
| 0d46a8e786 | |||
| 626b9f93c4 | |||
| 53f9defb07 | |||
| 4bf8d1e41f | |||
| 936c817bd8 | |||
| 9c9495621c | |||
| d53673c6f7 | |||
| 882e949f80 | |||
| e6849a59a2 | |||
| 6918e265ec | |||
| e9f293f522 | |||
| 5fc36d64b6 | |||
| 95e6d78689 | |||
| f4a666345a | |||
| 7a5c88a8d1 | |||
| 0de0dded53 | |||
| 5e58cf6079 | |||
| b0737d1c90 | |||
| 779ed75a19 | |||
| dbf1573e89 | |||
| 96fd8556f2 | |||
| 8f324dfc0a | |||
| 34a6085cc0 | |||
| b3efa75206 | |||
| b90a7dc66a | |||
| 200557c036 | |||
| 472cb3b1c2 | |||
| d8ee6a2edb | |||
| ddcacb8258 | |||
| a8e1754fa4 | |||
| 5db45c3699 | |||
| 89dcafbe41 | |||
| 3d05e61f7e | |||
| 5146f3ec47 | |||
| 170a95f5df | |||
| c661578906 | |||
| f20e0c2898 | |||
| 64b2ae3cd5 | |||
| a11d66f52d | |||
| 304a4a1b61 | |||
| 76b7103fed | |||
| 1572d01c53 | |||
| 86bf61ad35 | |||
| 479aa91368 | |||
| bb398c4a08 | |||
| eab9d06981 | |||
| c63ff05e6e | |||
| 0129c54ab6 | |||
| c570410e06 | |||
| 9af71699a4 | |||
| 58860614e3 | |||
| e8bde583ef | |||
| 5b19908996 | |||
| 19f3bb8cb0 | |||
| ac64792d99 | |||
| efe140bc0c | |||
| df2fc13b7a | |||
| f5d450f24c | |||
| 2e5f8c0786 | |||
| 9bcc298b18 | |||
| 5a03db7d2e | |||
| 91ef202ee7 | |||
| 6d6b281270 | |||
| a3c49bea2c | |||
| 1d375cb13c | |||
| 20ad9a13b8 | |||
| fc0f98b5db | |||
| 3fe37ede97 | |||
| 55df3cdf67 | |||
| 6e8107a5cb | |||
| 7bee4064a6 | |||
| 12ab03b0b5 | |||
| 47202d5839 | |||
| 47b121a700 | |||
| d53b640bcf | |||
| 78ef467138 | |||
| b7f6bd4c91 | |||
| e272acd7c1 | |||
| 93d2ac887d | |||
| a84cc1d3bb | |||
| cc594ad353 | |||
| beab5291bb | |||
| 3907dd0444 | |||
| 3d64b65813 | |||
| ab4e66a0bf | |||
| 144f6809bf | |||
| d296e6021d | |||
| 45a855ef1f | |||
| e5f756205c | |||
| 6335fa8df1 | |||
| b2605206b1 | |||
| 33182650ca | |||
| 0ca1f82059 | |||
| 0c5cb4824b | |||
| cf83b796e1 | |||
| b338c81907 | |||
| 81b3e7e449 | |||
| 425ddd13e5 | |||
| eb8af974dc | |||
| 3067c8bd27 | |||
| 632c8872cd | |||
| 9650218bb8 | |||
| 0b51102167 | |||
| ca36d08893 | |||
| a358384900 | |||
| 77a75305c7 | |||
| 85b9809b39 | |||
| d36de7d54f | |||
| 2b1c757084 | |||
| e7db0c7d99 | |||
| 393e3b702f | |||
| 23ad687e7e | |||
| a04ecdbe0a | |||
| 9ee21af524 | |||
| b74cdcde00 | |||
| 4d83d749a0 | |||
| 8e66e719fa | |||
| c6e6f4afea | |||
| d0148728c6 | |||
| dfc564f837 | |||
| 2b0edf6cde | |||
| daa7520d1d | |||
| 3499d089e7 | |||
| 6b96a2022a | |||
| 3836b40bf7 | |||
| 571dbfb7b1 | |||
| 10e25eb362 | |||
| 45d0e821dc | |||
| e37a2a8f7d | |||
| 07c83f6e44 | |||
| 061824a7ec | |||
| abb56a354d | |||
| 9359b8c170 | |||
| 2d0f1f27a8 | |||
| 73312fc363 | |||
| deb590cb05 | |||
| 2033f3f7dc | |||
| 3e3160d7ac | |||
| dd919c76b8 | |||
| a6a0f13264 | |||
| b9a0c107ab | |||
| 5c93acefd9 | |||
| a428dac71c | |||
| 3901f08fa0 | |||
| 519f21db18 | |||
| 18d472b7e1 | |||
| bc098d6b9c | |||
| 22ff8abd7d | |||
| 871ccfd32c | |||
| 7a79e184c5 | |||
| 0e72cc879f | |||
| 13304bd18b | |||
| a4b824c50a | |||
| ccc87081f3 | |||
| 800ba9d902 | |||
| 015b9fdf8b | |||
| 83f93202d0 | |||
| ae2ea02377 | |||
| 4fe4521881 | |||
| b83575bcbb | |||
| 17175e1c23 | |||
| cc57719856 | |||
| b9b71ccbc1 | |||
| e789b969b0 | |||
| be44024a1a | |||
| c7d4a30646 | |||
| 837c5dddee | |||
| 669b1385cc | |||
| 01ffaab3e0 | |||
| d5fb7180d0 | |||
| b141b43c1c | |||
| cad91e114d | |||
| db51779682 | |||
| c18e71ca29 | |||
| 6a54b3929e | |||
| 07af701afe | |||
| 669d953529 | |||
| 22d8b57027 | |||
| 0e691e2c51 | |||
| ae1e9e78db | |||
| e00aedd1a0 | |||
| 4a10dcc549 | |||
| 553f67d7e3 | |||
| fac8a520f0 | |||
| f0358f85fc | |||
| 4b9fa8ec6d | |||
| 45cdf6af3b | |||
| 47685bef39 | |||
| b11f9e0405 | |||
| 84e7de0a78 | |||
| f39e2b999f | |||
| cd4b28b4d7 | |||
| 2db2114a45 | |||
| 4b11a62b4d | |||
| 0457f23b13 | |||
| a6ba7ed21c | |||
| 5326912bc0 | |||
| be46c82aaa | |||
| 43bae9f9d7 | |||
| 95231c4dee | |||
| a95e01290e | |||
| 0cbbd3a5b1 | |||
| d8f3354ce3 | |||
| e4105f46de | |||
| 9a1ee9e984 | |||
| 7c897d5019 | |||
| bfe2bccc51 | |||
| 11aa0be04a | |||
| d2cae96e8d | |||
| 8e955297ef | |||
| 205f6a9000 | |||
| 4558f93f92 | |||
| 634c411946 | |||
| d4d0d35e05 | |||
| f4f36a3cb1 | |||
| 21d70a75d5 | |||
| 47ed9aa1b9 | |||
| b60ac78c58 | |||
| 83e139cd1a | |||
| b415b0e737 | |||
| 2e510e5ca0 | |||
| 1262263f5c | |||
| 9b9e2bcafb | |||
| cde29aba64 | |||
| 34cf1ff7c7 | |||
| cc880b53aa | |||
| 8ad44dd00f | |||
| 9440e65452 | |||
| 5cb6815bf3 | |||
| caab400258 | |||
| d3f13ab8a3 | |||
| ca6064b109 | |||
| e57caefd09 | |||
| 7e99f6bc5f | |||
| a85caaee1d | |||
| 5b00aaf561 | |||
| 174f11bdbd | |||
| 05442bdb11 | |||
| 9ba3cea2b1 | |||
| 20d48289b8 | |||
| 8f9410ceff | |||
| 3a08dd7dde | |||
| cfb858a51c | |||
| 37d7fdbcae | |||
| 62c14e9437 | |||
| 90b8346312 | |||
| 167d6a08f8 | |||
| 649d68a00f | |||
| 8807cd06b7 | |||
| b533d57717 | |||
| 14a6269243 | |||
| 7b96190e6c | |||
| 010b75251f | |||
| da3f3fd1fb | |||
| 26e13b4f5f | |||
| a771fcb397 | |||
| 64a6fbe39f | |||
| 43b860d267 | |||
| 818df50bb5 | |||
| 2570911283 | |||
| f59a448f3f | |||
| 834712bb58 | |||
| d98f739043 | |||
| aa5dc5455e | |||
| dfc970c926 | |||
| 68ecf340c5 | |||
| 3d626e84ef | |||
| 95abf768a7 | |||
| cb00190c3e | |||
| 8fdf1b23a3 | |||
| 2e9405db4c | |||
| fe5828cb47 | |||
| e56d8db6a3 | |||
| e76b6e3f27 | |||
| 9a7ce2fd29 | |||
| 462b93f62c | |||
| f71f67c4ff | |||
| 8358bda133 | |||
| 85199e200b | |||
| 5681801b85 | |||
| 86ef07b166 | |||
| 00339cb99b | |||
| daf3760cd4 | |||
| c1450ad616 | |||
| 025a09ed87 | |||
| 1716db14ed | |||
| d0e171784c | |||
| a68983b551 | |||
| c7c0139788 | |||
| 37bc2b3bb9 | |||
| c85806c422 | |||
| ee98683538 | |||
| a822d990d2 | |||
| 33d3303bdf | |||
| 8b35df8327 | |||
| 0854ed2b51 | |||
| 713c1a7a86 | |||
| fb816a0eb9 | |||
| fff62df6a2 | |||
| e6a07e80d7 | |||
| 514a98321d | |||
| 36ac35361f | |||
| bc7d728eb3 | |||
| 66a43c35db | |||
| e9f057c91b | |||
| 196cdfafa6 | |||
| 590ca7115f | |||
| 650db9c962 | |||
| 5d7c454e52 | |||
| 4d5f98017f | |||
| 7ecf6cb014 | |||
| 9d1aebce74 | |||
| dc9671c639 | |||
| 93bfe8164c | |||
| d6329eaa1a | |||
| f21a3069a5 | |||
| a8b989b51d | |||
| 142bfb5250 | |||
| 334327df67 | |||
| 2400210144 | |||
| 1b1c300aad | |||
| 531b03e7af | |||
| 650c1c64f1 | |||
| 2fd4726919 | |||
| b86745ee4e | |||
| c04aa05398 | |||
| 3c9bab8563 | |||
| 0dc24799f1 | |||
| 6eb1786635 | |||
| bbf2a97d2e | |||
| 00c6a589a5 | |||
| c7dca1dbef | |||
| 1da732bbf5 | |||
| 00b0311c86 | |||
| 6a707f515e | |||
| c0409a819e | |||
| b2b5f564a1 | |||
| d54c2dd39a | |||
| e668ecd453 | |||
| 0020359b8f | |||
| a0176775ff | |||
| a18fddb831 | |||
| ea462ae7d7 | |||
| 6d14583f28 | |||
| 956117a74f | |||
| 0c1d4e43e4 | |||
| ae156801df | |||
| 844e09c40e | |||
| 81da93a302 | |||
| e0508c6c64 | |||
| db9618ac2f | |||
| a38182b967 | |||
| 474bd7beaf | |||
| d8c449aadf | |||
| 2c611ec0ba | |||
| b44f5e59ac | |||
| 0ad0510624 | |||
| d2fb69cda3 | |||
| 2047cb8d1d | |||
| 24f9f06279 | |||
| b671bb72dc | |||
| af11ad6253 | |||
| 0d9b89fb34 | |||
| 80fee5ac1e | |||
| a909bd6edd | |||
| eeb4d4d4de | |||
| 4c42043940 | |||
| 6136abd0f8 | |||
| 69a5f3d58b | |||
| 98ef419abd | |||
| 77354f0f90 | |||
| 108f87c08f | |||
| 08e2698b9c | |||
| 6e81db7b43 | |||
| e9f12ae30a | |||
| 2f977aae18 | |||
| 0e5a862d8a | |||
| 1e46cd255b | |||
| e5e6af41a6 | |||
| ce437eb8d2 | |||
| 4c019c55d2 | |||
| ae440a92ac | |||
| 54d2e8bfdd | |||
| 3f206981d9 | |||
| fa7407cd42 | |||
| 866b9524b3 | |||
| d1cde4d0a7 | |||
| b01e7e3362 | |||
| e9b020beaa | |||
| 2fcd38c2bc | |||
| 300b34e268 | |||
| ce0e87373d | |||
| 7e1c6a5819 | |||
| 613214cdca | |||
| 009f53a267 | |||
| f3ad922a8f | |||
| 7b27a8e993 | |||
| fcb7072fee | |||
| 7ab1792ef7 | |||
| 40d305b35d | |||
| 2d7e91ba8a | |||
| b05434bc6f | |||
| 7b4c044480 | |||
| c14eb4e54a | |||
| f6c8f46462 | |||
| faf9ed9fec | |||
| 930ce4af60 | |||
| d51a986dd4 | |||
| 02845f546e | |||
| fff34a7f0f | |||
| 21dd715b42 | |||
| d9a693019b | |||
| 607d2486ea | |||
| 77f58326af | |||
| bf14cbdb27 | |||
| 93db420f80 | |||
| a209d0d7fa | |||
| f564a72d03 | |||
| aeb01bd216 | |||
| bc4d73da02 | |||
| f9e996e006 | |||
| 4dbc546e7b | |||
| 9a1a7dd531 | |||
| 7ac96f7526 | |||
| f613901eac | |||
| 4cd5624fa7 | |||
| fc6891a0e3 | |||
| fb7ccbd7b4 | |||
| 17c399d4e8 | |||
| dd19622823 | |||
| 46fa6e47fe | |||
| 7fa42cfaad | |||
| 5d8640109d | |||
| 6f6b23e879 | |||
| d2116a187e | |||
| f58652e51c | |||
| bf53035e3f | |||
| 60c6bb9865 | |||
| 67b71c0f89 | |||
| 5766fb1f31 | |||
| dbd69536ed | |||
| 08f0047529 | |||
| 54a38aa927 | |||
| 173cc13d01 | |||
| 1899f399b6 | |||
| 4c5921a7a4 | |||
| be84dd73a9 | |||
| 9f3f07f3ae | |||
| d260ccfc96 | |||
| 1c3b9754aa | |||
| 37c26b77ee | |||
| ff772dd28b | |||
| 60e2928ec1 | |||
| f8e57147b0 | |||
| 1bef748dc4 | |||
| b2fa0fdf53 | |||
| ab198c0c5f | |||
| f577af452d | |||
| a7802e9377 | |||
| 3b03f9a010 | |||
| cb194e06ce | |||
| a24780fe5e | |||
| a7c28f5827 | |||
| 425821863b | |||
| 8db6555bdb | |||
| 6557bf1b20 | |||
| 6648084c64 | |||
| 5543441618 | |||
| 0508e1e638 | |||
| adf119e960 | |||
| 2bc57fbee3 | |||
| 42b06af577 | |||
| f9a69c449d | |||
| 3f9affd2ad | |||
| 23701248f3 | |||
| c9abab6828 | |||
| 9dc9378bcc | |||
| 5cc152b8dd | |||
| 9bd6d28514 | |||
| 58816e314c | |||
| e93fbc6900 | |||
| f8c2e198c5 | |||
| f69e252c7a | |||
| 96dfa9b526 | |||
| 2b24635ef7 | |||
| fde6099769 | |||
| 7b3693bb29 | |||
| df8fa74e2a | |||
| 9747ab61f9 | |||
| d7e72797a8 | |||
| 0dfd2eadc1 | |||
| 291ec1eeb2 | |||
| b24eb621ae | |||
| 0fa148e65e | |||
| 3f6b24d33a | |||
| 9fee2ebeb5 | |||
| 99828a8f15 | |||
| 2526626646 | |||
| 582913dc31 | |||
| 94c1b74a17 | |||
| cbd1b28d0d | |||
| 05ffb50037 | |||
| 72e13ddeb2 | |||
| 78f09d7645 | |||
| f69121116a | |||
| 0136215ef1 | |||
| ab534ac55d | |||
| 67bbd59924 | |||
| f0b1a587aa | |||
| ae8f858c56 | |||
| 7426eb285f | |||
| f91f2e3e6d | |||
| e39ee10105 | |||
| 2ef2f0f63b | |||
| e875c91121 | |||
| 6610abcd5f | |||
| aed9b31294 | |||
| 6597eb27c7 | |||
| 035a39a9a8 | |||
| 9c547d5eef | |||
| 504fa5ffda | |||
| ad66dbfd0b | |||
| 0adb579736 | |||
| d4c83e5dd2 | |||
| 821a8c6e65 | |||
| ef60809219 | |||
| a21ed06d27 | |||
| 97f59405aa | |||
| b586bd3b10 | |||
| c617b17037 | |||
| 73bbfcb246 | |||
| cfa7d04c49 | |||
| fb191693d1 | |||
| 743a19027a | |||
| ced539579e | |||
| 225af34ce9 | |||
| eb80b7ec34 | |||
| 3df960bb52 | |||
| 25fcde9507 | |||
| d8abe75526 | |||
| 15fe8f1c51 | |||
| ce7b55871b | |||
| 9d8fa28dd9 | |||
| 3a3e9770f3 | |||
| 2d037fa130 | |||
| ab595d2b95 | |||
| e8ba5876ce | |||
| 2cd5f63284 | |||
| d410494a7d | |||
| dcfe8e1abd | |||
| 7702eb2fa7 | |||
| bb563b3eb7 | |||
| 4913cac24f | |||
| 113b937263 | |||
| a5c431c2b7 | |||
| 5ba23ac4c4 | |||
| f7472d59aa | |||
| 3cd4256b9e | |||
| 08e9400870 | |||
| 420bfef214 | |||
| ae265f7586 | |||
| ff53ff3bbf | |||
| 4a512694d9 | |||
| 8202352452 | |||
| 73a2a937c4 | |||
| a51f82c949 | |||
| 3278062c29 | |||
| 74f2be7307 | |||
| 0ee454f44b | |||
| 9a38d63661 | |||
| bab537c7e2 | |||
| a426d32a36 | |||
| 9403948ce3 | |||
| 0405b860c6 | |||
| 83df572604 | |||
| 0698136c63 | |||
| 67f1fa086d | |||
| 06f6ac66fa | |||
| 34709eb399 | |||
| 5c60a99cc4 | |||
| cea0ff4598 | |||
| b7aed7bbde | |||
| 1a4e1919fc | |||
| 457c2d364f | |||
| c9e9fcd0a3 | |||
| 9f8eb88a9f | |||
| 2e47c42367 | |||
| 41d921808b | |||
| 9e7a42d057 | |||
| d43e06c0ea | |||
| c788331e45 | |||
| 3d935b64f2 | |||
| e9b8f94fb1 | |||
| b95ffaf4dd | |||
| ab97a0a5a3 | |||
| e74d6446c8 | |||
| cbb7753227 | |||
| 78fad1a372 | |||
| d793f8ad78 | |||
| dda274afb0 | |||
| f0d184f38b | |||
| 9fccb47e7c | |||
| fd1ca2f102 | |||
| 458968658f | |||
| 051dd388d7 | |||
| 233506480b | |||
| 37898d38e5 | |||
| be441bd3a7 | |||
| 2e9a543598 | |||
| 190a59ce03 | |||
| 253e7a90b8 | |||
| fd4a153eef | |||
| 645f0e63ab | |||
| c087a9534a | |||
| da87bb6710 | |||
| 3c064439b3 | |||
| 85f8a42bd2 | |||
| ef55d6fa0c | |||
| aa34fa8baa | |||
| 2629f9be44 | |||
| fc2a45d6d6 | |||
| 22af341ad3 | |||
| a69f19a9ab | |||
| d7662e54cc | |||
| 459a7ba5a2 | |||
| 9cb58a47cd | |||
| 3651e67a25 | |||
| 8a0f6f1cbc | |||
| 9cbaacb13f | |||
| 1887e2460f | |||
| 6e578c4fde | |||
| 47e5074dc5 | |||
| 74d8619240 | |||
| 89f676de75 | |||
| 061c92ba1f | |||
| 8034196c25 | |||
| f49a30bc1a | |||
| 1196e9decc | |||
| cd4f290d3d | |||
| 45fae363c6 | |||
| 9738ce0cf6 | |||
| e5ae32e5f3 | |||
| 81ab6ab8b4 | |||
| cfbfdd7b8a | |||
| 8d0e7adb62 | |||
| 96a893120d | |||
| a1c72de1eb | |||
| d11bb71a17 | |||
| 6561ddae41 | |||
| ef21e2253f | |||
| dd553331df | |||
| d86858da64 | |||
| fcefbf26c6 | |||
| 79a3b308d1 | |||
| 5ecc7d5360 | |||
| 321f50bf7b | |||
| d0b37777c3 | |||
| aea094730b | |||
| a29d7d3962 | |||
| 155f13ed54 | |||
| 82a296f900 | |||
| 0df861d7b5 | |||
| 0890cc3e49 | |||
| 0da9c8d20c | |||
| b3c4ec4b55 | |||
| 4ce8e9ba16 | |||
| 51ce8f6c20 | |||
| 72edf26fb8 | |||
| d2b3cf22e3 | |||
| 28f3e9a65b | |||
| 17318112eb | |||
| 590bb7cbe4 | |||
| 798f90dac2 | |||
| b2e906f4cc | |||
| 11317acdbe | |||
| 5c6cf77c57 | |||
| 13825c9ef7 | |||
| 6013b0782e | |||
| 5f180e0b30 | |||
| 84ae1c5214 | |||
| 86adc42739 | |||
| 30f276a49b | |||
| 98cf72e0fb | |||
| 75964c686f | |||
| 0cb6dee474 | |||
| db1a90d828 | |||
| 110974af0b | |||
| 69d3d47901 | |||
| b50d5107b3 | |||
| 6a9eef0382 | |||
| 5f66d0b996 | |||
| 701728ecd7 | |||
| 423fd4a388 | |||
| ea2970d6e5 | |||
| 392d91b604 | |||
| ac12fc8b44 | |||
| fe67d51e5e | |||
| 4d294e7a69 | |||
| 45dc7fbac8 | |||
| 72590e2c93 | |||
| d3df83a3ea | |||
| 03268b0838 | |||
| f77e9f8bc1 | |||
| 9cc52c75e3 | |||
| 907c800e87 | |||
| 8c9b816c3d | |||
| 40d1882db9 | |||
| 152ab8c13d | |||
| 4c8cc7d5c2 | |||
| fe8f6fb48a | |||
| 31cedb6192 | |||
| aca5c73fb3 | |||
| f84f4ccd38 | |||
| d6e4566418 | |||
| 8872421785 | |||
| 3f74bafa70 | |||
| e1a78abfc9 | |||
| 23d1ddbb8e | |||
| 25823a1f66 | |||
| 0c4cc34488 | |||
| 5c7a4b1405 | |||
| a3d1f7b7b0 | |||
| 9aa632bcc1 | |||
| 0dd10d1457 | |||
| 5dd5323ad0 | |||
| 3537671cb7 | |||
| 511ff9d12c | |||
| d2518caf2e | |||
| e5e95a31f1 | |||
| fab6951789 | |||
| 12da498001 | |||
| 444c64ae75 | |||
| 2556484c67 | |||
| 860b76452f | |||
| 6cb7a17814 | |||
| 0f433ea882 | |||
| affda37d04 | |||
| bda945218d | |||
| 01ea3def54 | |||
| 6ab69012e3 | |||
| 35c80ab9fc | |||
| 8183449fb1 | |||
| 761b74ec59 | |||
| 1c91dac211 | |||
| 3d284d3c8b | |||
| 11f5f29c87 | |||
| 6cfb184b1e | |||
| 195f24507b | |||
| 56b21fd411 | |||
| d5df4a4616 | |||
| e6d55a6692 | |||
| 63cadab042 | |||
| 05b9575981 | |||
| e82163bfb9 | |||
| a5874d76d1 | |||
| 83ffd7c9fb | |||
| 7f2e87580f | |||
| 18df292f90 | |||
| ccb23d70a5 | |||
| 786bcb0d1b | |||
| 0efc3c1c6c | |||
| d4ddc40988 | |||
| e8b4d332eb | |||
| 674acd8431 | |||
| 80fc471067 | |||
| 4bcafb3e75 | |||
| 7ccc5d3910 | |||
| 1c2a423b14 | |||
| f010b5b235 | |||
| d5107aab47 | |||
| bd8dfac713 | |||
| b613fb70f1 | |||
| 43b06c2a9c | |||
| c133db6721 | |||
| f54628fcfd | |||
| 7ac64a6725 | |||
| 45908b9dd8 | |||
| 81c8d3f27a | |||
| 3a1f1dbc1a | |||
| 88fef183a3 | |||
| 299f627321 | |||
| c26607de08 | |||
| e93c4c42b6 | |||
| d24ba0da90 | |||
| 7460bdfb1b | |||
| c5975d5f66 | |||
| 07f0c7b4a5 | |||
| 8df95dbe39 | |||
| 75017345be | |||
| 08c955d177 | |||
| 8209e6c1e3 | |||
| 3a5efe9809 | |||
| d4146e4e68 | |||
| 5ffb4766b0 | |||
| 450d45354c | |||
| df7b466656 | |||
| 4b5a30f53d | |||
| 0ec88bb65d | |||
| 3b27b3c182 | |||
| e1a75deb25 | |||
| c7db322873 | |||
| b78c6f7127 | |||
| 2b19031206 | |||
| fd754a4ddc | |||
| b1e8eefad0 | |||
| e8d76ede14 | |||
| e2478902c9 | |||
| b9ebf595e6 | |||
| 6bb32e4e80 | |||
| 6d7419be0e | |||
| bef67c8a2d | |||
| e33c01131e | |||
| 2845617053 | |||
| 95c6278863 | |||
| eba44261c7 | |||
| b349100d9d | |||
| 7ab210432e | |||
| f40dcbfa30 | |||
| 84ba7c3e73 | |||
| ff366f4e4e | |||
| 5f470b316a | |||
| c8252d2b28 | |||
| ac58953276 | |||
| f064a26382 | |||
| 16d7eb4f55 | |||
| 5f26c2fc8f | |||
| f6383f6e06 | |||
| 8055d83da2 | |||
| b0a83cdb56 | |||
| 3f5951c9f8 | |||
| c3c442ccb1 | |||
| a95a63a58a | |||
| 1bbb76fa20 | |||
| f42b830ce9 | |||
| 256dfc5729 | |||
| 19fd8c109e | |||
| 634f353ed4 | |||
| d7c3d1ce12 | |||
| a626bbf635 | |||
| 0f95736c36 | |||
| d05b6135c6 | |||
| 60ce85f840 | |||
| 8b2473251d | |||
| 24fc1027ea | |||
| 463e18f893 | |||
| f7cb1aea64 | |||
| 9b43203111 | |||
| ff5295de32 | |||
| 8060cf9b19 | |||
| ba9fdfa802 | |||
| df8a0517dc | |||
| d27c3eee24 | |||
| e447f071ce | |||
| cc12302dd4 | |||
| 30eb680c9d | |||
| fa782046f9 | |||
| f0f8f50e12 | |||
| 4ceaab10a5 | |||
| 402df68235 | |||
| db1b012a26 | |||
| b1f1a5885b | |||
| 2fd58dc7e8 | |||
| 7428bb3025 | |||
| 14f02b23c2 | |||
| 617746606d | |||
| 65bdcaed29 | |||
| a5fd7abcf7 | |||
| 94ab3ae7cf | |||
| 370c2ecac5 | |||
| af8d7b9e4e | |||
| 3ce7947dd3 | |||
| 3f20e0c5a8 | |||
| a01398eda0 | |||
| 78080fd782 | |||
| d3e3e10b7d | |||
| ac3b22902a | |||
| ea098a920e | |||
| e786192766 | |||
| 9b48074389 | |||
| 9ba0a9189b | |||
| 7cd15a6202 | |||
| 49ba4bbfb5 | |||
| 77aa3142a9 | |||
| 4c741e93c3 | |||
| dc3d38c255 | |||
| fb9b09d004 | |||
| d7c66fc949 | |||
| adefd1fd63 | |||
| 6d6d0e4e36 | |||
| d94986ff0d | |||
| b3cb827526 | |||
| ac79654d1b | |||
| 69d86e39b9 | |||
| 49daecc4ea | |||
| 8f0b55c2cb | |||
| ed7883ba07 | |||
| 7a1fd27ecc | |||
| d160c862c7 | |||
| 4c4b353766 | |||
| 22ec8c4dd2 | |||
| 219dba8aae | |||
| 7c2af5ceae | |||
| a1cc40e7b1 | |||
| 27ea4285ef | |||
| 74e806810d | |||
| d8dc4f4474 | |||
| ba8aa580a1 | |||
| c863da278f | |||
| 22e679e23e | |||
| 1ae334e500 | |||
| 65488078a4 | |||
| 2d99f3556e | |||
| d33bc540bc | |||
| ce7d62968e | |||
| 215a9f9e11 | |||
| b54ba47870 | |||
| 901f503a3a | |||
| 0fba38f691 | |||
| c4066b924b | |||
| 3ec156b2f2 | |||
| 78b9c64f67 | |||
| 77e88741bc | |||
| 4c789faea4 | |||
| 66835cf083 | |||
| ad12900397 | |||
| 909f3e9b3e | |||
| 632287afe7 | |||
| 08c9a7c184 | |||
| 149c08ba78 | |||
| cec75e8567 | |||
| c1a98f76cd | |||
| 6b471d50eb | |||
| e8984d976c | |||
| 8590b66b4b | |||
| 98d96fd649 | |||
| 37a97a71e3 | |||
| e4d1bdef07 | |||
| f1f53de24b | |||
| cd9bd79639 | |||
| 4bbe1b6bf3 | |||
| 0393c7f52c | |||
| e12a708036 | |||
| 51cd98c9f4 | |||
| 3ec1fe9ee7 | |||
| 70d3a6d840 | |||
| ba968048c9 | |||
| 79d114cc06 | |||
| 0f9f787a55 | |||
| 7ae2a8840b | |||
| 7d59f7e69c | |||
| 5b0fddd3e8 | |||
| f4a0ff19d5 | |||
| 65696bf6a3 | |||
| 44e62c5c92 | |||
| 620ec7ef25 | |||
| 34c2b8a55e | |||
| ef822d785e | |||
| f7d767d0e8 | |||
| 8fa3f0721e | |||
| 458e5fa4e1 | |||
| ee8fecea21 | |||
| 3bfe17a14f | |||
| ddd6ebc58d | |||
| 1b5c0c68dd | |||
| 3e6f9faf71 | |||
| ad72168143 | |||
| 4fc8276f0f | |||
| aad035c4b5 | |||
| 560d6776af | |||
| c7a9a8e8f1 | |||
| c2cd0c994f | |||
| c729d6d65a | |||
| 5785ebb384 | |||
| b32ad80c23 | |||
| 2b79a3691f | |||
| cff39ec3a9 | |||
| c4bdc930ca | |||
| 14b9b12211 | |||
| d45d8eb4bc | |||
| c5e6665010 | |||
| 1b5a479bbd | |||
| fc92040d7f | |||
| b3073bbf1e | |||
| 63a107acbc | |||
| 07646c2124 | |||
| b45cea1434 | |||
| 3648a058d1 | |||
| 90479eaad6 | |||
| 1de2ceca9b | |||
| 8fc281e034 | |||
| 2093134420 | |||
| 818dd88390 | |||
| be4bb48476 | |||
| 1c8cb3b7d3 | |||
| 1ede0f4706 | |||
| 78703cfe47 | |||
| c72a5a5775 | |||
| d09dd29054 | |||
| ed711c0e59 | |||
| fa6114719d | |||
| c8b9cc9fe5 |
@@ -0,0 +1,24 @@
|
|||||||
|
<!---
|
||||||
|
|
||||||
|
THIS IS NOT A SUPPORT FORUM, FOR SUPPORT GO TO:
|
||||||
|
https://forums.rpcs3.net
|
||||||
|
or our discord:
|
||||||
|
https://discord.me/RPCS3
|
||||||
|
|
||||||
|
PLEASE READ THE GUIDELINES BEFORE OPENING AN ISSUE:
|
||||||
|
https://github.com/RPCS3/rpcs3/blob/master/CONTRIBUTING.md
|
||||||
|
|
||||||
|
====================================================
|
||||||
|
|
||||||
|
When submitting an issue, please check the following:
|
||||||
|
|
||||||
|
- You have read the above.
|
||||||
|
- You have provided the version (commit hash) of RPCS3 you are using.
|
||||||
|
- You have provided sufficient detail for the issue to be reproduced.
|
||||||
|
- You have provided system specs.
|
||||||
|
- Please also provide:
|
||||||
|
- For crashes, a backtrace.
|
||||||
|
- For graphical issues, comparison screenshots with real hardware.
|
||||||
|
|
||||||
|
Remember that the GitHub Issue Tracker is not the place to ask for support or to submit Game Compatibility reports. You must use our forums for that.
|
||||||
|
--->
|
||||||
@@ -41,7 +41,10 @@
|
|||||||
/Vulkan/Vulkan-build
|
/Vulkan/Vulkan-build
|
||||||
/Vulkan/glslang-build
|
/Vulkan/glslang-build
|
||||||
|
|
||||||
/wxWidgets/lib
|
# Themes
|
||||||
|
!/bin/GuiConfigs/
|
||||||
|
/bin/GuiConfigs/*.ini
|
||||||
|
|
||||||
/bin/rpcs3.ini
|
/bin/rpcs3.ini
|
||||||
/bin/rpcs3.ipdb
|
/bin/rpcs3.ipdb
|
||||||
/bin/rpcs3.iobj
|
/bin/rpcs3.iobj
|
||||||
@@ -55,9 +58,12 @@
|
|||||||
rpcs3/git-version.h
|
rpcs3/git-version.h
|
||||||
|
|
||||||
# Visual Studio Files
|
# Visual Studio Files
|
||||||
|
.vs/*
|
||||||
|
*.ipch
|
||||||
*.vspx
|
*.vspx
|
||||||
*.psess
|
*.psess
|
||||||
*.VC.*
|
*.VC.*
|
||||||
|
*.vcxproj.user
|
||||||
|
|
||||||
# Copyrighted files
|
# Copyrighted files
|
||||||
/bin/data/
|
/bin/data/
|
||||||
@@ -69,6 +75,7 @@ rpcs3/git-version.h
|
|||||||
!/bin/dev_hdd0/game/
|
!/bin/dev_hdd0/game/
|
||||||
/bin/dev_hdd0/game/*
|
/bin/dev_hdd0/game/*
|
||||||
!/bin/dev_hdd0/game/TEST12345/
|
!/bin/dev_hdd0/game/TEST12345/
|
||||||
|
/bin/dev_hdd0/disc/*
|
||||||
|
|
||||||
# Ignore other system generated files
|
# Ignore other system generated files
|
||||||
bin/dev_hdd0/*.txt
|
bin/dev_hdd0/*.txt
|
||||||
@@ -85,3 +92,9 @@ CMakeCache.txt
|
|||||||
# cotire
|
# cotire
|
||||||
rpcs3/cotire/*
|
rpcs3/cotire/*
|
||||||
rpcs3/rpcs3_*_cotire.cmake
|
rpcs3/rpcs3_*_cotire.cmake
|
||||||
|
|
||||||
|
# Qt
|
||||||
|
moc_*.cpp
|
||||||
|
qrc_resources.cpp
|
||||||
|
rpcs3_automoc.cpp
|
||||||
|
ui_*.h
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
[submodule "wxWidgets"]
|
|
||||||
path = wxWidgets
|
|
||||||
url = https://github.com/wxWidgets/wxWidgets
|
|
||||||
ignore = dirty
|
|
||||||
[submodule "rpcs3-ffmpeg"]
|
[submodule "rpcs3-ffmpeg"]
|
||||||
path = 3rdparty/ffmpeg
|
path = 3rdparty/ffmpeg
|
||||||
url = https://github.com/hrydgard/ppsspp-ffmpeg
|
url = https://github.com/hrydgard/ppsspp-ffmpeg
|
||||||
@@ -12,16 +8,13 @@
|
|||||||
[submodule "llvm"]
|
[submodule "llvm"]
|
||||||
path = llvm
|
path = llvm
|
||||||
url = https://github.com/llvm-mirror/llvm
|
url = https://github.com/llvm-mirror/llvm
|
||||||
branch = release_38
|
branch = release_60
|
||||||
[submodule "rsx_program_decompiler"]
|
|
||||||
path = rsx_program_decompiler
|
|
||||||
url = https://github.com/RPCS3/rsx_program_decompiler
|
|
||||||
[submodule "GSL"]
|
[submodule "GSL"]
|
||||||
path = 3rdparty/GSL
|
path = 3rdparty/GSL
|
||||||
url = https://github.com/Microsoft/GSL.git
|
url = https://github.com/Microsoft/GSL.git
|
||||||
[submodule "libpng"]
|
[submodule "libpng"]
|
||||||
path = 3rdparty/libpng
|
path = 3rdparty/libpng
|
||||||
url = https://github.com/RPCS3/libpng
|
url = https://github.com/Zangetsu38/libpng
|
||||||
ignore = dirty
|
ignore = dirty
|
||||||
[submodule "Vulkan/glslang"]
|
[submodule "Vulkan/glslang"]
|
||||||
path = Vulkan/glslang
|
path = Vulkan/glslang
|
||||||
@@ -32,12 +25,23 @@
|
|||||||
[submodule "Utilities/yaml-cpp"]
|
[submodule "Utilities/yaml-cpp"]
|
||||||
path = Utilities/yaml-cpp
|
path = Utilities/yaml-cpp
|
||||||
url = https://github.com/RPCS3/yaml-cpp
|
url = https://github.com/RPCS3/yaml-cpp
|
||||||
[submodule "3rdparty/pugixml"]
|
|
||||||
path = 3rdparty/pugixml
|
|
||||||
url = https://github.com/RPCS3/pugixml
|
|
||||||
[submodule "3rdparty/cereal"]
|
[submodule "3rdparty/cereal"]
|
||||||
path = 3rdparty/cereal
|
path = 3rdparty/cereal
|
||||||
url = https://github.com/USCiLab/cereal.git
|
url = https://github.com/USCiLab/cereal.git
|
||||||
[submodule "rsx-debugger"]
|
[submodule "3rdparty/zlib"]
|
||||||
path = rsx-debugger
|
path = 3rdparty/zlib
|
||||||
url = https://github.com/RPCS3/rsx-debugger.git
|
url = https://github.com/madler/zlib
|
||||||
|
[submodule "3rdparty/hidapi"]
|
||||||
|
path = 3rdparty/hidapi
|
||||||
|
url = https://github.com/Zangetsu38/hidapi
|
||||||
|
branch = master
|
||||||
|
[submodule "3rdparty/Optional"]
|
||||||
|
path = 3rdparty/Optional
|
||||||
|
url = https://github.com/akrzemi1/Optional.git
|
||||||
|
[submodule "3rdparty/pugixml"]
|
||||||
|
path = 3rdparty/pugixml
|
||||||
|
url = https://github.com/zeux/pugixml
|
||||||
|
[submodule "polly"]
|
||||||
|
path = polly
|
||||||
|
url = https://github.com/llvm-mirror/polly
|
||||||
|
branch = release_60
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
language: cpp
|
language: cpp
|
||||||
sudo: false
|
sudo: required
|
||||||
dist: trusty
|
|
||||||
|
|
||||||
os:
|
os:
|
||||||
- linux
|
- linux
|
||||||
#- osx
|
#- osx
|
||||||
|
|
||||||
osx_image: xcode6.4
|
# osx_image: xcode6.4
|
||||||
|
|
||||||
compiler:
|
compiler:
|
||||||
- clang
|
- clang
|
||||||
@@ -17,80 +16,110 @@ cache:
|
|||||||
directories:
|
directories:
|
||||||
- $HOME/hombebrew_cache
|
- $HOME/hombebrew_cache
|
||||||
|
|
||||||
env:
|
|
||||||
global:
|
|
||||||
- secure: "Vf+FY48nip9JppMnq11105NealdErSWsoUhHo63/V3V+LKfA9guenxCp93/qoSIdSGC/sJwb0yIIMGvkTT/rxDJNh6Z+BWUTb2E0WEIIQbvTJNOSUzoq7dfF1LT61XjVjByFzcbC2xjtaBowmcAYEs1jGUUuEjYVCMmD5lY8hUg="
|
|
||||||
# Which Travis environment to run Coverity on
|
|
||||||
- coverity_scan_run_condition='"$TRAVIS_OS_NAME" = linux -a "$CC" = gcc'
|
|
||||||
# Test mode is for testing if it's working with Coverity. Change to true if testing, to avoid reaching the quota.
|
|
||||||
- coverity_scan_script_test_mode=false
|
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
exclude:
|
exclude:
|
||||||
- os: osx
|
- os: osx
|
||||||
compiler: gcc
|
compiler: gcc
|
||||||
|
|
||||||
git:
|
git:
|
||||||
|
depth: false # Unshallow clone to obtain proper GIT_VERSION
|
||||||
submodules: false
|
submodules: false
|
||||||
|
|
||||||
before_install:
|
before_install:
|
||||||
|
- if [ "$CC" = "clang" ]; then
|
||||||
|
export CXX="clang++-4.0" CC="clang-4.0";
|
||||||
|
fi;
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CXX" = "g++" ]; then
|
- if [ "$TRAVIS_OS_NAME" = "linux" ] && [ "$CXX" = "g++" ]; then
|
||||||
export CXX="g++-5" CC="gcc-5" CXXFLAGS="-Wno-format-security";
|
export CXX="g++-5" CC="gcc-5" CXXFLAGS="-Wno-format-security";
|
||||||
export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01';
|
|
||||||
fi;
|
fi;
|
||||||
# Add coverall for C++ so coverall.io could be triggered. Even it should be --coverage and gcov.
|
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||||
pip install --user cpp-coveralls requests[security];
|
wget http://download.qt.io/official_releases/online_installers/qt-unified-linux-x64-online.run ;
|
||||||
|
chmod a+x ./qt-unified-linux-x64-online.run ;
|
||||||
|
export QT_QPA_PLATFORM=minimal ;
|
||||||
|
travis_wait 60 ./qt-unified-linux-x64-online.run --script qt-installer-noninteractive.qs --no-force-installations --verbose ;
|
||||||
|
fi;
|
||||||
|
|
||||||
|
# Install updated libglew-dev since the version provided by trusty is outdated
|
||||||
|
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
||||||
|
wget https://mirrors.kernel.org/ubuntu/pool/universe/g/glew/libglew-dev_2.0.0-5_amd64.deb;
|
||||||
|
wget https://mirrors.kernel.org/ubuntu/pool/universe/g/glew/libglew2.0_2.0.0-5_amd64.deb;
|
||||||
|
wget https://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan1_1.0.61.1+dfsg1-1ubuntu1~16.04.1_amd64.deb;
|
||||||
|
wget https://mirrors.kernel.org/ubuntu/pool/universe/v/vulkan/libvulkan-dev_1.0.61.1+dfsg1-1ubuntu1~16.04.1_amd64.deb;
|
||||||
|
sudo dpkg -i libglew2.0_2.0.0-5_amd64.deb libglew-dev_2.0.0-5_amd64.deb libvulkan1_1.0.61.1+dfsg1-1ubuntu1~16.04.1_amd64.deb libvulkan-dev_1.0.61.1+dfsg1-1ubuntu1~16.04.1_amd64.deb;
|
||||||
else
|
else
|
||||||
brew update; brew update;
|
brew update;
|
||||||
brew install ccache glew wxwidgets llvm38;
|
brew install ccache glew llvm40;
|
||||||
fi;
|
fi;
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- git submodule update --init rsx_program_decompiler asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal
|
- git submodule update --init asmjit 3rdparty/ffmpeg 3rdparty/pugixml 3rdparty/GSL 3rdparty/libpng Utilities/yaml-cpp 3rdparty/cereal 3rdparty/hidapi 3rdparty/Optional Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers
|
||||||
- mkdir build
|
- mkdir build
|
||||||
- cd build
|
- cd build
|
||||||
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then
|
- export CMAKE_PREFIX_PATH=~/Qt/5.10.0/gcc_64/lib/cmake
|
||||||
cmake ..;
|
- export CXXFLAGS="$CXXFLAGS -DTRAVIS=true";
|
||||||
|
- if [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||||
|
export CXXFLAGS="$CXXFLAGS -DBRANCH=$TRAVIS_REPO_SLUG/$TRAVIS_BRANCH";
|
||||||
else
|
else
|
||||||
cmake .. -DLLVM_DIR=/usr/local/opt/llvm38/lib/llvm-3.8/share/llvm/cmake;
|
export CXXFLAGS="$CXXFLAGS -DBRANCH=$TRAVIS_REPO_SLUG/$TRAVIS_BRANCH/#$TRAVIS_PULL_REQUEST";
|
||||||
fi;
|
fi;
|
||||||
|
- cmake .. -DCMAKE_INSTALL_PREFIX=/usr -G Ninja;
|
||||||
|
- ninja
|
||||||
|
- # AppImage generation
|
||||||
|
- if [ -n "$UPLOAD_URL" ] && [ "$TRAVIS_BRANCH" = "master" ] && [ "$CC" = "clang-4.0" ] && [ "$TRAVIS_PULL_REQUEST" = false ]; then
|
||||||
|
export LD_LIBRARY_PATH=~/Qt/5.10.0/gcc_64/lib;
|
||||||
|
DESTDIR=appdir ninja install ; find appdir/ ;
|
||||||
|
find ../bin ;
|
||||||
|
wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" ;
|
||||||
|
chmod a+x linuxdeployqt*.AppImage ;
|
||||||
|
export PATH=~/Qt/5.10.0/gcc_64/bin/:${PATH} ;
|
||||||
|
./linuxdeployqt*.AppImage --appimage-extract ;
|
||||||
|
./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -bundle-non-qt-libs ;
|
||||||
|
mkdir ./appdir/usr/plugins/xcbglintegrations/ ;
|
||||||
|
mkdir ./appdir/usr/plugins/imageformats/ ;
|
||||||
|
cp ~/Qt/5.10.0/gcc_64/lib/libQt5Svg.so.5 ./appdir/usr/lib/ ;
|
||||||
|
cp ~/Qt/5.10.0/gcc_64/plugins/xcbglintegrations/* ./appdir/usr/plugins/xcbglintegrations/ ;
|
||||||
|
cp ~/Qt/5.10.0/gcc_64/plugins/imageformats/* ./appdir/usr/plugins/imageformats/ ;
|
||||||
|
cp ~/Qt/5.10.0/gcc_64/plugins/platforms/* ./appdir/usr/plugins/platforms/ ;
|
||||||
|
export PATH=${TRAVIS_BUILD_DIR}/build/squashfs-root/usr/bin/:${PATH} ;
|
||||||
|
./squashfs-root/usr/bin/appimagetool ${TRAVIS_BUILD_DIR}/build/appdir ;
|
||||||
|
find ./appdir -executable -type f -exec ldd {} \; | grep " => /usr" | cut -d " " -f 2-3 | sort | uniq ;
|
||||||
|
curl ${UPLOAD_URL}${TRAVIS_COMMIT:0:7}-${TRAVIS_BUILD_NUMBER}_linux64 --upload-file ./RPCS3*.AppImage;
|
||||||
|
fi;
|
||||||
|
|
||||||
script:
|
script:
|
||||||
# Add a command to show all the variables. May be useful for debugging Travis.
|
# Add a command to show all the variables. May be useful for debugging Travis.
|
||||||
#- echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
|
#- echo "--Shell Export Lists START--" ; export -p; echo "--Shell Export Lists STOP--";
|
||||||
# And to ensure the versions of toolchain
|
# And to ensure the versions of toolchain
|
||||||
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
|
- echo "--CXX version?"; "$CXX" --version; echo "--CXX version confirmed";
|
||||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j 3; fi
|
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
sources:
|
sources:
|
||||||
- ubuntu-toolchain-r-test
|
- ubuntu-toolchain-r-test
|
||||||
#- llvm-toolchain-trusty-3.8 temporarily disabled
|
- llvm-toolchain-trusty-4.0
|
||||||
|
- sourceline: 'ppa:jonathonf/binutils' # We need to update binutils to a newer version to link against the ffmpeg libs on.
|
||||||
packages:
|
packages:
|
||||||
|
- binutils
|
||||||
- cmake
|
- cmake
|
||||||
|
- ninja-build
|
||||||
|
- libasound2-dev
|
||||||
- libopenal-dev
|
- libopenal-dev
|
||||||
- freeglut3-dev
|
- freeglut3-dev
|
||||||
- libglew-dev
|
#- libglew-dev
|
||||||
|
#- libvulkan1
|
||||||
|
#- libvulkan-dev
|
||||||
- libc6-dev
|
- libc6-dev
|
||||||
#- llvm-3.8
|
- llvm-4.0
|
||||||
#- llvm-3.8-dev
|
- llvm-4.0-dev
|
||||||
|
- clang-4.0
|
||||||
- libedit-dev
|
- libedit-dev
|
||||||
- g++-5
|
- g++-5
|
||||||
- gcc-5
|
- gcc-5
|
||||||
#- clang-3.6
|
|
||||||
- libstdc++-5-dev
|
- libstdc++-5-dev
|
||||||
- lib32stdc++6
|
- lib32stdc++6
|
||||||
- zlib1g-dev
|
- zlib1g-dev
|
||||||
- libwxgtk3.0-dev
|
# We need to install qt 5.10.0 manually because the version trusty provides is too old.
|
||||||
coverity_scan:
|
#- qtbase5-dev
|
||||||
project:
|
- libudev-dev
|
||||||
name: $TRAVIS_REPO_SLUG
|
- libevdev-dev
|
||||||
description: "PS3 emulator/debugger"
|
- libpulse-dev
|
||||||
notification_email: nekotekina@gmail.com
|
|
||||||
build_command: "make -j 3"
|
|
||||||
branch_pattern: coverity_scan
|
|
||||||
|
|
||||||
after_success:
|
|
||||||
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "$TRAVIS_OS_NAME" = linux ]; then coveralls --extension .c --extension .cpp --extension .h; fi
|
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
cmake_minimum_required(VERSION 2.8.12)
|
cmake_minimum_required(VERSION 3.1)
|
||||||
|
|
||||||
|
option(WITH_GDB "WITH_GDB" OFF)
|
||||||
|
option(WITHOUT_LLVM "WITHOUT_LLVM" OFF)
|
||||||
|
|
||||||
|
if (WITH_GDB)
|
||||||
|
add_definitions(-DWITH_GDB_DEBUGGER)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(ASMJIT_STATIC TRUE)
|
set(ASMJIT_STATIC TRUE)
|
||||||
|
|
||||||
@@ -11,14 +18,6 @@ if(NOT CMAKE_SIZEOF_VOID_P EQUAL 8)
|
|||||||
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
|
message( FATAL_ERROR "RPCS3 can only be compiled on 64-bit platforms." )
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.1)
|
|
||||||
message( FATAL_ERROR "RPCS3 requires at least gcc-5.1." )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 6.1)
|
|
||||||
message( FATAL_ERROR "RPCS3 can't be compiled with gcc-6.1, see #1691." )
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_program(CCACHE_FOUND ccache)
|
find_program(CCACHE_FOUND ccache)
|
||||||
if (CCACHE_FOUND)
|
if (CCACHE_FOUND)
|
||||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
||||||
@@ -41,6 +40,20 @@ option(VULKAN_PREBUILT "" OFF)
|
|||||||
# TODO: do real installation, including copying directory structure
|
# TODO: do real installation, including copying directory structure
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_BINARY_DIR}/bin")
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${PROJECT_BINARY_DIR}/bin")
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_BINARY_DIR}/bin")
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${PROJECT_BINARY_DIR}/bin")
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${PROJECT_BINARY_DIR}/bin")
|
||||||
|
|
||||||
add_subdirectory( Vulkan )
|
add_subdirectory( Vulkan )
|
||||||
add_subdirectory( rpcs3 )
|
add_subdirectory( rpcs3 )
|
||||||
add_subdirectory( rsx_program_decompiler )
|
|
||||||
|
include_directories(3rdparty/hidapi/hidapi)
|
||||||
|
if(APPLE)
|
||||||
|
add_subdirectory(3rdparty/hidapi/mac)
|
||||||
|
#list(APPEND LIBS hidapi)
|
||||||
|
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||||
|
add_subdirectory(3rdparty/hidapi/linux)
|
||||||
|
elseif(MSVC)
|
||||||
|
add_subdirectory(3rdparty/hidapi/windows)
|
||||||
|
else()
|
||||||
|
add_subdirectory(3rdparty/hidapi/libusb)
|
||||||
|
#list(APPEND LIBS hidapi-libusb)
|
||||||
|
endif()
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Getting Started
|
||||||
|
|
||||||
|
Before getting started using the emulator, read the [Quickstart Guide](https://rpcs3.net/quickstart) and the [FAQ](https://rpcs3.net/faq). After reading those, if you need support, visit our [Forums](https://forums.rpcs3.net).
|
||||||
|
|
||||||
|
# Issue Reporting
|
||||||
|
|
||||||
|
**The GitHub Issue Tracker is not the place to ask for support or to submit [Game Compatibility](https://rpcs3.net/compatibility) reports.** Requests for support or incorrect reports will be closed. If you are not sure whether the issue you want to report is an actual issue that is not yet known, please use the forums to submit such report.
|
||||||
|
|
||||||
|
**Before reporting an issue:**
|
||||||
|
- Check if your system matches all the minimum requirements listed in the [Quickstart Guide](https://rpcs3.net/quickstart);
|
||||||
|
- Check if the issue is meaningful for the team (e.g. The Last of Us doesn't work is obvious and therefore useless);
|
||||||
|
- Search older issues/forum threads to see if your issue was already submitted.
|
||||||
|
- Use understandable English. It doesn't need to be perfect, but clear enough to understand your message.
|
||||||
|
- While reporting issues, don't forget to include details about your system (OS, CPU, GPU, etc.), as well as the RPCS3.log file.
|
||||||
|
|
||||||
|
Submitting your test results for Commercial Games must be done on our forums. Please read the [Game Compatibility](https://github.com/RPCS3/rpcs3/wiki/Game-Compatibility) wiki page before doing so.
|
||||||
|
|
||||||
|
# Contributing
|
||||||
|
|
||||||
|
Check the [Coding Style Guidelines](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information). If you have any questions, hit us up on our [Discord Server](https://discord.me/RPCS3) in the **#development** channel.
|
||||||
@@ -3,67 +3,104 @@ RPCS3
|
|||||||
|
|
||||||
[](https://travis-ci.org/RPCS3/rpcs3)
|
[](https://travis-ci.org/RPCS3/rpcs3)
|
||||||
[](https://ci.appveyor.com/project/rpcs3/rpcs3/branch/master)
|
[](https://ci.appveyor.com/project/rpcs3/rpcs3/branch/master)
|
||||||
[](https://scan.coverity.com/projects/3960)
|
|
||||||
|
|
||||||
An open-source PlayStation 3 emulator/debugger written in C++.
|
The world's first open-source PlayStation 3 emulator/debugger written in C++ for Windows and Linux.
|
||||||
|
|
||||||
You can find some basic information in the [FAQ](https://github.com/RPCS3/rpcs3/wiki/FAQ). For discussion about this emulator and PS3 emulation please visit the [official forums](http://www.emunewz.net/forum/forumdisplay.php?fid=172).
|
You can find some basic information in our [**website**](https://rpcs3.net/).
|
||||||
|
For discussion about this emulator and PS3 emulation please visit our [**forums**](https://forums.rpcs3.net) and our [**Discord server**](https://discord.me/RPCS3).
|
||||||
|
|
||||||
Official Discord server: https://discord.me/RPCS3
|
[**Support Lead Developers Nekotekina and kd-11 on Patreon**](https://www.patreon.com/Nekotekina)
|
||||||
|
|
||||||
Support Nekotekina on Patreon: https://www.patreon.com/Nekotekina
|
|
||||||
|
|
||||||
### Development
|
|
||||||
|
|
||||||
If you want to contribute please take a look at the [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information) pages. You should as well contact any of the developers in the forum in order to know about the current situation of the emulator.
|
|
||||||
|
|
||||||
|
|
||||||
### Dependencies
|
## Development
|
||||||
|
|
||||||
__Windows__
|
If you want to contribute please take a look at the [Coding Style](https://github.com/RPCS3/rpcs3/wiki/Coding-Style), [Roadmap](https://github.com/RPCS3/rpcs3/wiki/Roadmap) and [Developer Information](https://github.com/RPCS3/rpcs3/wiki/Developer-Information) pages. You should as well contact any of the developers in the forums or in Discord in order to know more about the current situation of the emulator.
|
||||||
* [Visual Studio 2015](https://www.visualstudio.com/en-us/downloads/download-visual-studio-vs.aspx)
|
|
||||||
* [Visual C++ Redistributable Packages for Visual Studio 2015](http://www.microsoft.com/en-us/download/details.aspx?id=48145)
|
|
||||||
* [Cmake 3.1.0+](http://www.cmake.org/download/) (required; add to PATH)
|
## Dependencies
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
* [Visual Studio 2017](https://www.visualstudio.com/en/downloads/)
|
||||||
|
* [Visual C++ Redistributable Packages for Visual Studio 2017](https://go.microsoft.com/fwlink/?LinkId=746572)
|
||||||
|
* [Cmake 3.1.0+](https://www.cmake.org/download/) (required; add to PATH)
|
||||||
* [Python 3.3+](https://www.python.org/downloads/) (required; add to PATH)
|
* [Python 3.3+](https://www.python.org/downloads/) (required; add to PATH)
|
||||||
|
* [Qt 5.9.3](https://www.qt.io/download-open-source/) (required; add QTDIR `<QtInstallFolder>\5.9.3\msvc2017_64\` environment variable if you do not want to use the Visual Studio Qt Plugin)
|
||||||
|
* [Visual Studio Qt Plugin](https://marketplace.visualstudio.com/items?itemName=TheQtCompany.QtVisualStudioTools2015) (optional; see above)
|
||||||
|
|
||||||
__Linux__
|
### Linux
|
||||||
|
* [Qt 5.7+](https://www.qt.io/download-open-source/)
|
||||||
* GCC 5.1+ or Clang 3.5.0+ ([not GCC 6.1](https://github.com/RPCS3/rpcs3/issues/1691))
|
* GCC 5.1+ or Clang 3.5.0+ ([not GCC 6.1](https://github.com/RPCS3/rpcs3/issues/1691))
|
||||||
* Debian & Ubuntu: `sudo apt-get install cmake build-essential libopenal-dev libwxgtk3.0-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev git`
|
* Debian & Ubuntu: `sudo apt-get install cmake build-essential libasound2-dev libpulse-dev libopenal-dev libglew-dev zlib1g-dev libedit-dev libvulkan-dev libudev-dev git qt5-default`
|
||||||
* Arch: `sudo pacman -S glew openal wxgtk cmake llvm`
|
* Arch: `sudo pacman -S glew openal cmake llvm qt5-base`
|
||||||
|
* Fedora: `sudo dnf install cmake glew glew-devel libatomic libudev-devel openal-devel qt5-devel vulkan-devel`
|
||||||
|
* OpenSUSE: `sudo zypper install git cmake libasound2 libpulse-devel openal-soft-devel glew-devel zlib-devel libedit-devel vulkan-devel libudev-devel libqt5-qtbase-devel libevdev-devel`
|
||||||
|
**If you have a NVIDIA GPU, you may need to install the libglvnd package.**
|
||||||
|
|
||||||
__Mac OSX__
|
### MacOS
|
||||||
Mac OSX is not supported at this moment because it doesn't meet system requirements (OpenGL 4.3)
|
MacOS is not supported at this moment because it doesn't meet system requirements (OpenGL 4.3)
|
||||||
* Xcode 6+ (tested with Xcode 6.4)
|
* Xcode 6+ (tested with Xcode 6.4)
|
||||||
* Install with Homebrew: `brew install glew wxwidgets`
|
* Install with Homebrew: `brew install glew llvm qt cmake`
|
||||||
* Remove '-framework QuickTime' from '_ldflags' in /usr/local/bin/wx-config
|
|
||||||
|
|
||||||
### Building
|
|
||||||
|
|
||||||
- __Windows__: </br>
|
## Building on Windows:
|
||||||
1) To initialize the repository don't forget to execute `git submodule update --init` to pull the submodules. </br>
|
To initialize the repository don't forget to execute `git submodule update --init` to pull the submodules.
|
||||||
2) Open the *.SLN* file. </br>
|
|
||||||
3) Build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*. </br>
|
### Configuring Qt
|
||||||
4) Press *BUILD* > *Build Solution* or *Rebuild Solution*. </br>
|
|
||||||
|
*If you're using Visual Studio 2017 without Qt plugin support (or simply dont want to use it):*
|
||||||
|
1) Add `QTDIR` environment variable and set it to `<QtInstallFolder>\5.9.3\msvc2017_64\` </br>
|
||||||
|
|
||||||
|
|
||||||
|
Open `rpcs3.sln`
|
||||||
|
|
||||||
|
|
||||||
|
*If you wish to use the Visual Studio plugin for Qt:* </br>
|
||||||
|
1) Go to the Qt5 menu and edit Qt5 options. Add the path to your Qt installation with compiler e.g. `C:\Qt\5.9.3\msvc2017_64`. </br>
|
||||||
|
2) While selecting the rpcs3qt project, go to Qt5->Project Setting and select the version you added.
|
||||||
|
|
||||||
|
### Building the projects
|
||||||
|
You may want to download precompiled [LLVM lib](https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRY2k3Q2Yya05lcm8) and extract to root rpcs3 folder (which contains `rpcs3.sln`), as well as download and extract [additional libs](https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRaHYxV3VOS2d0a0U) to `lib\%CONFIGURATION%-x64\` to speed up compilation time (unoptimised/debug libs are currently not available precompiled).
|
||||||
|
|
||||||
|
If you're not using precompiled libs, build the projects in *__BUILD_BEFORE* folder: right-click on every project > *Build*. </br></br>
|
||||||
|
|
||||||
|
`Build > Build Solution`</br>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Building on Linux & Mac OS:
|
||||||
|
|
||||||
- __Linux & Mac OSX__: </br>
|
|
||||||
1) `git clone https://github.com/RPCS3/rpcs3.git` </br>
|
1) `git clone https://github.com/RPCS3/rpcs3.git` </br>
|
||||||
2) `cd rpcs3/` </br>
|
2) `cd rpcs3/` </br>
|
||||||
3) `git submodule update --init` </br>
|
3) `git submodule update --init` </br>
|
||||||
4) `cmake CMakeLists.txt && make GitVersion && make` </br>
|
4) `cd ../ && mkdir rpcs3_build && cd rpcs3_build`
|
||||||
|
4) `cmake ../rpcs3/ && make GitVersion && make` </br>
|
||||||
5) Run RPCS3 with `./bin/rpcs3` </br>
|
5) Run RPCS3 with `./bin/rpcs3` </br>
|
||||||
|
|
||||||
If you are on OSX and want to build with llvm don't forget to add `-DLLVM_DIR=...` (or wherever llvm brew was installed) to cmake invocation.
|
If you are on MacOS and want to build with brew llvm and qt don't forget to add the following environment variables
|
||||||
|
|
||||||
|
* `LLVM_DIR=/usr/local/opt/llvm/` (or wherever llvm was installed).
|
||||||
|
* `Qt5_DIR=/usr/local/opt/qt/lib/cmake/Qt5` (or wherever qt was installed).
|
||||||
|
|
||||||
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
|
When using GDB, configure it to ignore SIGSEGV signal (`handle SIGSEGV nostop noprint`).
|
||||||
|
|
||||||
##### CMake Build Options (Linux & Mac OSX)
|
|
||||||
|
|
||||||
- ```-DUSE_SYSTEM_LIBPNG=ON/OFF``` (default = *OFF*) </br>
|
## CMake Build Options (Linux & Mac OS)
|
||||||
|
|
||||||
|
- ```-DUSE_SYSTEM_LIBPNG=ON/OFF``` (default = *OFF*)
|
||||||
Build against the shared libpng instead of using the builtin one. libpng 1.6+ highly recommended. Try this option if you get version conflict errors or only see black game icons.
|
Build against the shared libpng instead of using the builtin one. libpng 1.6+ highly recommended. Try this option if you get version conflict errors or only see black game icons.
|
||||||
|
|
||||||
- ```-DUSE_SYSTEM_FFMPEG=ON/OFF``` (default = *OFF*) </br>
|
- ```-DUSE_SYSTEM_FFMPEG=ON/OFF``` (default = *OFF*)
|
||||||
Build against the shared ffmpeg libraries instead of using the builtin patched version. Try this if the builtin version breaks the OpenGL renderer for you.
|
Build against the shared ffmpeg libraries instead of using the builtin patched version. Try this if the builtin version breaks the OpenGL renderer for you.
|
||||||
|
|
||||||
### License
|
- ```-DWITHOUT_LLVM=ON/OFF``` (default = *OFF*)
|
||||||
|
This forces RPCS3 to build without LLVM, not recommended.
|
||||||
|
|
||||||
|
- ```-DWITH_GDB=ON/OFF``` (default = *OFF*)
|
||||||
|
This Builds RPCS3 with support for debugging PS3 games using gdb.
|
||||||
|
|
||||||
|
- ```-DUSE_VULKAN=ON/OFF``` (default = *ON*)
|
||||||
|
This builds RPCS3 with Vulkan support.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
Most files are licensed under the terms of GNU GPLv2 License, see LICENSE file for details. Some files may be licensed differently, check appropriate file headers for details.
|
Most files are licensed under the terms of GNU GPLv2 License, see LICENSE file for details. Some files may be licensed differently, check appropriate file headers for details.
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
#include "stdafx.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Emu/System.h"
|
|
||||||
#include "AutoPause.h"
|
|
||||||
|
|
||||||
cfg::bool_entry g_cfg_debug_autopause_syscall(cfg::root.misc, "Auto Pause at System Call");
|
|
||||||
cfg::bool_entry g_cfg_debug_autopause_func_call(cfg::root.misc, "Auto Pause at Function Call");
|
|
||||||
|
|
||||||
debug::autopause& debug::autopause::get_instance()
|
|
||||||
{
|
|
||||||
// Use magic static
|
|
||||||
static autopause instance;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load Auto Pause Configuration from file "pause.bin"
|
|
||||||
void debug::autopause::reload(void)
|
|
||||||
{
|
|
||||||
auto& instance = get_instance();
|
|
||||||
|
|
||||||
instance.m_pause_function.clear();
|
|
||||||
instance.m_pause_syscall.clear();
|
|
||||||
|
|
||||||
// TODO: better format, possibly a config entry
|
|
||||||
if (fs::file list{ fs::get_config_dir() + "pause.bin" })
|
|
||||||
{
|
|
||||||
u32 num;
|
|
||||||
size_t fmax = list.size();
|
|
||||||
size_t fcur = 0;
|
|
||||||
list.seek(0);
|
|
||||||
while (fcur <= fmax - sizeof(u32))
|
|
||||||
{
|
|
||||||
list.read(&num, sizeof(u32));
|
|
||||||
fcur += sizeof(u32);
|
|
||||||
if (num == 0xFFFFFFFF) break;
|
|
||||||
|
|
||||||
if (num < 1024)
|
|
||||||
{
|
|
||||||
instance.m_pause_syscall.emplace(num);
|
|
||||||
LOG_WARNING(HLE, "Set autopause at syscall %lld", num);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
instance.m_pause_function.emplace(num);
|
|
||||||
LOG_WARNING(HLE, "Set autopause at function 0x%08x", num);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool debug::autopause::pause_syscall(u64 code)
|
|
||||||
{
|
|
||||||
if (g_cfg_debug_autopause_syscall && get_instance().m_pause_syscall.count(code) != 0)
|
|
||||||
{
|
|
||||||
Emu.Pause();
|
|
||||||
LOG_SUCCESS(HLE, "Autopause triggered at syscall %lld", code);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool debug::autopause::pause_function(u32 code)
|
|
||||||
{
|
|
||||||
if (g_cfg_debug_autopause_func_call && get_instance().m_pause_function.count(code) != 0)
|
|
||||||
{
|
|
||||||
Emu.Pause();
|
|
||||||
LOG_SUCCESS(HLE, "Autopause triggered at function 0x%08x", code);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
// Regarded as a Debugger Enchantment
|
|
||||||
namespace debug
|
|
||||||
{
|
|
||||||
// To store the pause function/call id, and let those pause there.
|
|
||||||
// Would be with a GUI to configure those.
|
|
||||||
class autopause
|
|
||||||
{
|
|
||||||
std::unordered_set<u64> m_pause_syscall;
|
|
||||||
std::unordered_set<u32> m_pause_function;
|
|
||||||
|
|
||||||
static autopause& get_instance();
|
|
||||||
public:
|
|
||||||
|
|
||||||
static void reload();
|
|
||||||
static bool pause_syscall(u64 code);
|
|
||||||
static bool pause_function(u32 code);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -301,6 +301,16 @@ union alignas(16) v128
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 SIZE_32(T) * (static_cast<u32>(arg) ^ static_cast<u32>(M));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
inline v128 operator|(const v128& left, const v128& right)
|
inline v128 operator|(const v128& left, const v128& right)
|
||||||
{
|
{
|
||||||
return v128::fromV(_mm_or_si128(left.vi, right.vi));
|
return v128::fromV(_mm_or_si128(left.vi, right.vi));
|
||||||
@@ -460,7 +470,7 @@ struct se_storage<T, 16, 16>
|
|||||||
|
|
||||||
static inline v128 swap(const v128& src)
|
static inline v128 swap(const v128& src)
|
||||||
{
|
{
|
||||||
return v128::fromV(_mm_shuffle_epi8(src.vi, _mm_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)));
|
return v128::from64(se_storage<u64>::swap(src._u64[1]), se_storage<u64>::swap(src._u64[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline v128 to(const T& src)
|
static inline v128 to(const T& src)
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ struct bf_base
|
|||||||
// Value mask
|
// Value mask
|
||||||
static constexpr utype vmask = static_cast<utype>(~utype{} >> (bitmax - bitsize));
|
static constexpr utype vmask = static_cast<utype>(~utype{} >> (bitmax - bitsize));
|
||||||
|
|
||||||
|
// All ones mask
|
||||||
|
static constexpr utype mask1 = static_cast<utype>(~utype{});
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
type m_data;
|
type m_data;
|
||||||
};
|
};
|
||||||
@@ -36,7 +39,7 @@ struct bf_t : bf_base<T, N>
|
|||||||
// Get bitmask of size N, at I pos
|
// Get bitmask of size N, at I pos
|
||||||
static constexpr utype data_mask()
|
static constexpr utype data_mask()
|
||||||
{
|
{
|
||||||
return static_cast<utype>(~utype{} >> (bf_t::bitmax - bf_t::bitsize) << bitpos);
|
return static_cast<utype>(static_cast<utype>(bf_t::mask1 >> (bf_t::bitmax - bf_t::bitsize)) << bitpos);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bitfield extraction helper
|
// Bitfield extraction helper
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
|
|
||||||
#include "yaml-cpp/yaml.h"
|
#include "yaml-cpp/yaml.h"
|
||||||
|
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
namespace cfg
|
namespace cfg
|
||||||
{
|
{
|
||||||
logs::channel cfg("CFG", logs::level::notice);
|
logs::channel cfg("CFG");
|
||||||
|
|
||||||
entry_base::entry_base(type _type)
|
_base::_base(type _type)
|
||||||
: m_type(_type)
|
: m_type(_type)
|
||||||
{
|
{
|
||||||
if (_type != type::node)
|
if (_type != type::node)
|
||||||
@@ -16,51 +18,41 @@ namespace cfg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entry_base::entry_base(type _type, node& owner, const std::string& name)
|
_base::_base(type _type, node* owner, const std::string& name)
|
||||||
: m_type(_type)
|
: m_type(_type)
|
||||||
{
|
{
|
||||||
if (!owner.m_nodes.emplace(name, this).second)
|
for (const auto& pair : owner->m_nodes)
|
||||||
{
|
{
|
||||||
fmt::throw_exception<std::logic_error>("Node already exists: %s" HERE, name);
|
if (pair.first == name)
|
||||||
}
|
{
|
||||||
}
|
fmt::throw_exception<std::logic_error>("Node already exists: %s" HERE, name);
|
||||||
|
}
|
||||||
entry_base& entry_base::operator[](const std::string& name) const
|
|
||||||
{
|
|
||||||
if (m_type == type::node)
|
|
||||||
{
|
|
||||||
return *static_cast<const node&>(*this).m_nodes.at(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt::throw_exception<std::logic_error>("Invalid node type" HERE);
|
owner->m_nodes.emplace_back(name, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
entry_base& entry_base::operator[](const char* name) const
|
bool _base::from_string(const std::string&)
|
||||||
{
|
|
||||||
if (m_type == type::node)
|
|
||||||
{
|
|
||||||
return *static_cast<const node&>(*this).m_nodes.at(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt::throw_exception<std::logic_error>("Invalid node type" HERE);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool entry_base::from_string(const std::string&)
|
|
||||||
{
|
{
|
||||||
fmt::throw_exception<std::logic_error>("from_string() purecall" HERE);
|
fmt::throw_exception<std::logic_error>("from_string() purecall" HERE);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool entry_base::from_list(std::vector<std::string>&&)
|
bool _base::from_list(std::vector<std::string>&&)
|
||||||
{
|
{
|
||||||
fmt::throw_exception<std::logic_error>("from_list() purecall" HERE);
|
fmt::throw_exception<std::logic_error>("from_list() purecall" HERE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit YAML
|
// Emit YAML
|
||||||
static void encode(YAML::Emitter& out, const class entry_base& rhs);
|
static void encode(YAML::Emitter& out, const class _base& rhs);
|
||||||
|
|
||||||
// Incrementally load config entries from YAML::Node.
|
// Incrementally load config entries from YAML::Node.
|
||||||
// The config value is preserved if the corresponding YAML node doesn't exist.
|
// The config value is preserved if the corresponding YAML node doesn't exist.
|
||||||
static void decode(const YAML::Node& data, class entry_base& rhs);
|
static void decode(const YAML::Node& data, class _base& rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> cfg::make_int_range(s64 min, s64 max)
|
||||||
|
{
|
||||||
|
return {std::to_string(min), std::to_string(max)};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
||||||
@@ -97,6 +89,8 @@ bool cfg::try_to_int64(s64* out, const std::string& value, s64 min, s64 max)
|
|||||||
|
|
||||||
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, const std::string& value)
|
bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) func, const std::string& value)
|
||||||
{
|
{
|
||||||
|
u64 max = -1;
|
||||||
|
|
||||||
for (u64 i = 0;; i++)
|
for (u64 i = 0;; i++)
|
||||||
{
|
{
|
||||||
std::string var;
|
std::string var;
|
||||||
@@ -114,17 +108,33 @@ bool cfg::try_to_enum_value(u64* out, decltype(&fmt_class_string<int>::format) f
|
|||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
max = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
const auto val = std::stoull(value, nullptr, 0);
|
std::size_t pos;
|
||||||
|
const auto val = std::stoull(value, &pos, 0);
|
||||||
|
|
||||||
|
if (pos != value.size())
|
||||||
|
{
|
||||||
|
if (out) cfg.error("cfg::try_to_enum_value('%s'): unexpected characters (pos=%zu)", value, pos);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val > max)
|
||||||
|
{
|
||||||
|
if (out) cfg.error("cfg::try_to_enum_value('%s'): out of bounds(0..%u)", value, max);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (out) *out = val;
|
if (out) *out = val;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (const std::exception& e)
|
||||||
{
|
{
|
||||||
|
if (out) cfg.error("cfg::try_to_enum_value('%s'): invalid enum value: %s", value, e.what());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,7 +161,7 @@ std::vector<std::string> cfg::try_to_enum_list(decltype(&fmt_class_string<int>::
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cfg::encode(YAML::Emitter& out, const cfg::entry_base& rhs)
|
void cfg::encode(YAML::Emitter& out, const cfg::_base& rhs)
|
||||||
{
|
{
|
||||||
switch (rhs.get_type())
|
switch (rhs.get_type())
|
||||||
{
|
{
|
||||||
@@ -161,7 +171,8 @@ void cfg::encode(YAML::Emitter& out, const cfg::entry_base& rhs)
|
|||||||
for (const auto& np : static_cast<const node&>(rhs).get_nodes())
|
for (const auto& np : static_cast<const node&>(rhs).get_nodes())
|
||||||
{
|
{
|
||||||
out << YAML::Key << np.first;
|
out << YAML::Key << np.first;
|
||||||
out << YAML::Value; encode(out, *np.second);
|
out << YAML::Value;
|
||||||
|
encode(out, *np.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
out << YAML::EndMap;
|
out << YAML::EndMap;
|
||||||
@@ -178,12 +189,28 @@ void cfg::encode(YAML::Emitter& out, const cfg::entry_base& rhs)
|
|||||||
out << YAML::EndSeq;
|
out << YAML::EndSeq;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
case type::log:
|
||||||
|
{
|
||||||
|
out << YAML::BeginMap;
|
||||||
|
for (const auto& np : static_cast<const log_entry&>(rhs).get_map())
|
||||||
|
{
|
||||||
|
if (np.second == logs::level::notice) continue;
|
||||||
|
out << YAML::Key << np.first;
|
||||||
|
out << YAML::Value << fmt::format("%s", np.second);
|
||||||
|
}
|
||||||
|
|
||||||
out << rhs.to_string();
|
out << YAML::EndMap;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
out << rhs.to_string();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cfg::decode(const YAML::Node& data, cfg::entry_base& rhs)
|
void cfg::decode(const YAML::Node& data, cfg::_base& rhs)
|
||||||
{
|
{
|
||||||
switch (rhs.get_type())
|
switch (rhs.get_type())
|
||||||
{
|
{
|
||||||
@@ -199,16 +226,12 @@ void cfg::decode(const YAML::Node& data, cfg::entry_base& rhs)
|
|||||||
if (!pair.first.IsScalar()) continue;
|
if (!pair.first.IsScalar()) continue;
|
||||||
|
|
||||||
// Find the key among existing nodes
|
// Find the key among existing nodes
|
||||||
const auto name = pair.first.Scalar();
|
for (const auto& _pair : static_cast<node&>(rhs).get_nodes())
|
||||||
const auto found = static_cast<node&>(rhs).get_nodes().find(name);
|
|
||||||
|
|
||||||
if (found != static_cast<node&>(rhs).get_nodes().cend())
|
|
||||||
{
|
{
|
||||||
decode(pair.second, *found->second);
|
if (_pair.first == pair.first.Scalar())
|
||||||
}
|
{
|
||||||
else
|
decode(pair.second, *_pair.second);
|
||||||
{
|
}
|
||||||
// ???
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,6 +248,29 @@ void cfg::decode(const YAML::Node& data, cfg::entry_base& rhs)
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case type::log:
|
||||||
|
{
|
||||||
|
if (data.IsScalar() || data.IsSequence())
|
||||||
|
{
|
||||||
|
return; // ???
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, logs::level> values;
|
||||||
|
|
||||||
|
for (const auto& pair : data)
|
||||||
|
{
|
||||||
|
if (!pair.first.IsScalar() || !pair.second.IsScalar()) continue;
|
||||||
|
|
||||||
|
u64 value;
|
||||||
|
if (cfg::try_to_enum_value(&value, &fmt_class_string<logs::level>::format, pair.second.Scalar()))
|
||||||
|
{
|
||||||
|
values.emplace(pair.first.Scalar(), static_cast<logs::level>(static_cast<int>(value)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static_cast<log_entry&>(rhs).set_map(std::move(values));
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
std::string value;
|
std::string value;
|
||||||
@@ -244,14 +290,19 @@ std::string cfg::node::to_string() const
|
|||||||
YAML::Emitter out;
|
YAML::Emitter out;
|
||||||
cfg::encode(out, *this);
|
cfg::encode(out, *this);
|
||||||
|
|
||||||
return{ out.c_str(), out.size() };
|
return {out.c_str(), out.size()};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool cfg::node::from_string(const std::string& value)
|
bool cfg::node::from_string(const std::string& value) try
|
||||||
{
|
{
|
||||||
cfg::decode(YAML::Load(value), *this);
|
cfg::decode(YAML::Load(value), *this);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
cfg.fatal("%s thrown: %s", typeid(e).name(), e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void cfg::node::from_default()
|
void cfg::node::from_default()
|
||||||
{
|
{
|
||||||
@@ -261,25 +312,32 @@ void cfg::node::from_default()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cfg::bool_entry::from_default()
|
void cfg::_bool::from_default()
|
||||||
{
|
{
|
||||||
value = def;
|
m_value = def;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cfg::string_entry::from_default()
|
void cfg::string::from_default()
|
||||||
{
|
{
|
||||||
*this = def;
|
m_value = def;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cfg::set_entry::from_default()
|
void cfg::set_entry::from_default()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
|
||||||
m_set = {};
|
m_set = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg::root_node& cfg::get_root()
|
void cfg::log_entry::set_map(std::map<std::string, logs::level>&& map)
|
||||||
{
|
{
|
||||||
// Magic static
|
logs::reset();
|
||||||
static root_node root;
|
|
||||||
return root;
|
for (auto&& pair : (m_map = std::move(map)))
|
||||||
|
{
|
||||||
|
logs::set_level(pair.first, pair.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void cfg::log_entry::from_default()
|
||||||
|
{
|
||||||
|
set_map({});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,20 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Utilities/types.h"
|
#include "Utilities/types.h"
|
||||||
#include "Utilities/Atomic.h"
|
|
||||||
#include "Utilities/StrFmt.h"
|
#include "Utilities/StrFmt.h"
|
||||||
|
#include "Utilities/Log.h"
|
||||||
|
|
||||||
#include <initializer_list>
|
|
||||||
#include <exception>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <unordered_map>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace cfg
|
namespace cfg
|
||||||
{
|
{
|
||||||
|
// Format min and max values
|
||||||
|
std::vector<std::string> make_int_range(s64 min, s64 max);
|
||||||
|
|
||||||
// Convert string to signed integer
|
// Convert string to signed integer
|
||||||
bool try_to_int64(s64* out, const std::string& value, s64 min, s64 max);
|
bool try_to_int64(s64* out, const std::string& value, s64 min, s64 max);
|
||||||
|
|
||||||
@@ -29,36 +28,33 @@ namespace cfg
|
|||||||
enum class type : uint
|
enum class type : uint
|
||||||
{
|
{
|
||||||
node = 0, // cfg::node type
|
node = 0, // cfg::node type
|
||||||
boolean, // cfg::bool_entry type
|
_bool, // cfg::_bool type
|
||||||
fixed_map, // cfg::map_entry type
|
_enum, // cfg::_enum type
|
||||||
enumeration, // cfg::enum_entry type
|
_int, // cfg::_int type
|
||||||
integer, // cfg::int_entry type
|
string, // cfg::string type
|
||||||
string, // cfg::string_entry type
|
|
||||||
set, // cfg::set_entry type
|
set, // cfg::set_entry type
|
||||||
|
log,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Config tree entry abstract base class
|
// Config tree entry abstract base class
|
||||||
class entry_base
|
class _base
|
||||||
{
|
{
|
||||||
const type m_type;
|
const type m_type;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Ownerless entry constructor
|
// Ownerless entry constructor
|
||||||
entry_base(type _type);
|
_base(type _type);
|
||||||
|
|
||||||
// Owned entry constructor
|
// Owned entry constructor
|
||||||
entry_base(type _type, class node& owner, const std::string& name);
|
_base(type _type, class node* owner, const std::string& name);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Disallow copy/move constructors and assignments
|
// Disallow copy/move constructors and assignments
|
||||||
entry_base(const entry_base&) = delete;
|
_base(const _base&) = delete;
|
||||||
|
|
||||||
// Get type
|
// Get type
|
||||||
type get_type() const { return m_type; }
|
type get_type() const { return m_type; }
|
||||||
|
|
||||||
// Access child node (must exist)
|
|
||||||
entry_base& operator [](const std::string& name) const; entry_base& operator [](const char* name) const;
|
|
||||||
|
|
||||||
// Reset defaults
|
// Reset defaults
|
||||||
virtual void from_default() = 0;
|
virtual void from_default() = 0;
|
||||||
|
|
||||||
@@ -82,27 +78,27 @@ namespace cfg
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Config tree node which contains another nodes
|
// Config tree node which contains another nodes
|
||||||
class node : public entry_base
|
class node : public _base
|
||||||
{
|
{
|
||||||
std::map<std::string, entry_base*> m_nodes;
|
std::vector<std::pair<std::string, _base*>> m_nodes;
|
||||||
|
|
||||||
friend class entry_base;
|
friend class _base;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Root node constructor
|
// Root node constructor
|
||||||
node()
|
node()
|
||||||
: entry_base(type::node)
|
: _base(type::node)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registered node constructor
|
// Registered node constructor
|
||||||
node(node& owner, const std::string& name)
|
node(node* owner, const std::string& name)
|
||||||
: entry_base(type::node, owner, name)
|
: _base(type::node, owner, name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get child nodes
|
// Get child nodes
|
||||||
const std::map<std::string, entry_base*>& get_nodes() const
|
const auto& get_nodes() const
|
||||||
{
|
{
|
||||||
return m_nodes;
|
return m_nodes;
|
||||||
}
|
}
|
||||||
@@ -117,160 +113,61 @@ namespace cfg
|
|||||||
void from_default() override;
|
void from_default() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct bool_entry final : public entry_base
|
class _bool final : public _base
|
||||||
{
|
{
|
||||||
atomic_t<bool> value;
|
bool m_value;
|
||||||
|
|
||||||
const bool def;
|
public:
|
||||||
|
bool def;
|
||||||
|
|
||||||
bool_entry(node& owner, const std::string& name, bool def = false)
|
_bool(node* owner, const std::string& name, bool def = false)
|
||||||
: entry_base(type::boolean, owner, name)
|
: _base(type::_bool, owner, name)
|
||||||
, value(def)
|
, m_value(def)
|
||||||
, def(def)
|
, def(def)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit operator bool() const
|
explicit operator bool() const
|
||||||
{
|
{
|
||||||
return value.load();
|
return m_value;
|
||||||
}
|
|
||||||
|
|
||||||
bool_entry& operator =(bool value)
|
|
||||||
{
|
|
||||||
value = value;
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_default() override;
|
void from_default() override;
|
||||||
|
|
||||||
std::string to_string() const override
|
std::string to_string() const override
|
||||||
{
|
{
|
||||||
return value.load() ? "true" : "false";
|
return m_value ? "true" : "false";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool from_string(const std::string& value) override
|
bool from_string(const std::string& value) override
|
||||||
{
|
{
|
||||||
if (value == "false")
|
if (value == "false")
|
||||||
this->value = false;
|
m_value = false;
|
||||||
else if (value == "true")
|
else if (value == "true")
|
||||||
this->value = true;
|
m_value = true;
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Value node with fixed set of possible values, each maps to a value of type T.
|
void set(const bool& value)
|
||||||
template<typename T>
|
|
||||||
struct map_entry final : public entry_base
|
|
||||||
{
|
|
||||||
using init_type = std::initializer_list<std::pair<std::string, T>>;
|
|
||||||
using map_type = std::unordered_map<std::string, T>;
|
|
||||||
using list_type = std::vector<std::string>;
|
|
||||||
using value_type = typename map_type::value_type;
|
|
||||||
|
|
||||||
static map_type make_map(init_type init)
|
|
||||||
{
|
{
|
||||||
map_type map(init.size());
|
m_value = value;
|
||||||
|
|
||||||
for (const auto& v : init)
|
|
||||||
{
|
|
||||||
// Ensure elements are unique
|
|
||||||
verify(HERE), map.emplace(v.first, v.second).second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
static list_type make_list(init_type init)
|
|
||||||
{
|
|
||||||
list_type list; list.reserve(init.size());
|
|
||||||
|
|
||||||
for (const auto& v : init)
|
|
||||||
{
|
|
||||||
list.emplace_back(v.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
const map_type map;
|
|
||||||
const list_type list; // Element list sorted in original order
|
|
||||||
const value_type& def; // Pointer to the default value
|
|
||||||
|
|
||||||
private:
|
|
||||||
atomic_t<const value_type*> m_value;
|
|
||||||
|
|
||||||
public:
|
|
||||||
map_entry(node& owner, const std::string& name, const std::string& def, init_type init)
|
|
||||||
: entry_base(type::fixed_map, owner, name)
|
|
||||||
, map(make_map(init))
|
|
||||||
, list(make_list(init))
|
|
||||||
, def(*map.find(def))
|
|
||||||
, m_value(&this->def)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
map_entry(node& owner, const std::string& name, std::size_t def_index, init_type init)
|
|
||||||
: map_entry(owner, name, (init.begin() + (def_index < init.size() ? def_index : 0))->first, init)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
map_entry(node& owner, const std::string& name, init_type init)
|
|
||||||
: map_entry(owner, name, 0, init)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const T& get() const
|
|
||||||
{
|
|
||||||
return m_value.load()->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void from_default() override
|
|
||||||
{
|
|
||||||
m_value = &def;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string to_string() const override
|
|
||||||
{
|
|
||||||
return m_value.load()->first;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool from_string(const std::string& value) override
|
|
||||||
{
|
|
||||||
const auto found = map.find(value);
|
|
||||||
|
|
||||||
if (found == map.end())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_value = &*found;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> to_list() const override
|
|
||||||
{
|
|
||||||
return list;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Value node with fixed set of possible values, each maps to an enum value of type T.
|
// Value node with fixed set of possible values, each maps to an enum value of type T.
|
||||||
template<typename T, bool External = false>
|
template <typename T>
|
||||||
class enum_entry final : public entry_base
|
class _enum final : public _base
|
||||||
{
|
{
|
||||||
// Value or reference
|
T m_value;
|
||||||
std::conditional_t<External, atomic_t<T>&, atomic_t<T>> m_value;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const T def;
|
const T def;
|
||||||
|
|
||||||
enum_entry(node& owner, const std::string& name, std::conditional_t<External, atomic_t<T>&, T> value)
|
_enum(node* owner, const std::string& name, T value = {})
|
||||||
: entry_base(type::enumeration, owner, name)
|
: _base(type::_enum, owner, name)
|
||||||
, m_value(value)
|
, m_value(value)
|
||||||
, def(value)
|
, def(value)
|
||||||
{
|
{
|
||||||
@@ -278,13 +175,7 @@ namespace cfg
|
|||||||
|
|
||||||
operator T() const
|
operator T() const
|
||||||
{
|
{
|
||||||
return m_value.load();
|
return m_value;
|
||||||
}
|
|
||||||
|
|
||||||
enum_entry& operator =(T value)
|
|
||||||
{
|
|
||||||
m_value = value;
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_default() override
|
void from_default() override
|
||||||
@@ -320,21 +211,21 @@ namespace cfg
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Signed 32/64-bit integer entry with custom Min/Max range.
|
// Signed 32/64-bit integer entry with custom Min/Max range.
|
||||||
template<s64 Min, s64 Max>
|
template <s64 Min, s64 Max>
|
||||||
class int_entry final : public entry_base
|
class _int final : public _base
|
||||||
{
|
{
|
||||||
static_assert(Min < Max, "Invalid cfg::int_entry range");
|
static_assert(Min < Max, "Invalid cfg::_int range");
|
||||||
|
|
||||||
// Prefer 32 bit type if possible
|
// Prefer 32 bit type if possible
|
||||||
using int_type = std::conditional_t<Min >= INT32_MIN && Max <= INT32_MAX, s32, s64>;
|
using int_type = std::conditional_t<Min >= INT32_MIN && Max <= INT32_MAX, s32, s64>;
|
||||||
|
|
||||||
atomic_t<int_type> m_value;
|
int_type m_value;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const int_type def;
|
int_type def;
|
||||||
|
|
||||||
int_entry(node& owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
|
_int(node* owner, const std::string& name, int_type def = std::min<int_type>(Max, std::max<int_type>(Min, 0)))
|
||||||
: entry_base(type::integer, owner, name)
|
: _base(type::_int, owner, name)
|
||||||
, m_value(def)
|
, m_value(def)
|
||||||
, def(def)
|
, def(def)
|
||||||
{
|
{
|
||||||
@@ -342,18 +233,7 @@ namespace cfg
|
|||||||
|
|
||||||
operator int_type() const
|
operator int_type() const
|
||||||
{
|
{
|
||||||
return m_value.load();
|
return m_value;
|
||||||
}
|
|
||||||
|
|
||||||
int_entry& operator =(int_type value)
|
|
||||||
{
|
|
||||||
if (value < Min || value > Max)
|
|
||||||
{
|
|
||||||
fmt::throw_exception("Value out of the valid range: %lld" HERE, s64{ value });
|
|
||||||
}
|
|
||||||
|
|
||||||
m_value = value;
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void from_default() override
|
void from_default() override
|
||||||
@@ -363,7 +243,7 @@ namespace cfg
|
|||||||
|
|
||||||
std::string to_string() const override
|
std::string to_string() const override
|
||||||
{
|
{
|
||||||
return std::to_string(m_value.load());
|
return std::to_string(m_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool from_string(const std::string& value) override
|
bool from_string(const std::string& value) override
|
||||||
@@ -377,25 +257,34 @@ namespace cfg
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set(const s64& value)
|
||||||
|
{
|
||||||
|
m_value = static_cast<int_type>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> to_list() const override
|
||||||
|
{
|
||||||
|
return make_int_range(Min, Max);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Alias for 32 bit int
|
// Alias for 32 bit int
|
||||||
using int32_entry = int_entry<INT32_MIN, INT32_MAX>;
|
using int32 = _int<INT32_MIN, INT32_MAX>;
|
||||||
|
|
||||||
// Alias for 64 bit int
|
// Alias for 64 bit int
|
||||||
using int64_entry = int_entry<INT64_MIN, INT64_MAX>;
|
using int64 = _int<INT64_MIN, INT64_MAX>;
|
||||||
|
|
||||||
// Simple string entry with mutex
|
// Simple string entry with mutex
|
||||||
class string_entry final : public entry_base
|
class string final : public _base
|
||||||
{
|
{
|
||||||
mutable std::mutex m_mutex;
|
|
||||||
std::string m_value;
|
std::string m_value;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const std::string def;
|
std::string def;
|
||||||
|
|
||||||
string_entry(node& owner, const std::string& name, const std::string& def = {})
|
string(node* owner, const std::string& name, const std::string& def = {})
|
||||||
: entry_base(type::string, owner, name)
|
: _base(type::string, owner, name)
|
||||||
, m_value(def)
|
, m_value(def)
|
||||||
, def(def)
|
, def(def)
|
||||||
{
|
{
|
||||||
@@ -403,25 +292,16 @@ namespace cfg
|
|||||||
|
|
||||||
operator std::string() const
|
operator std::string() const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
|
||||||
return m_value;
|
return m_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get() const
|
const std::string& get() const
|
||||||
{
|
{
|
||||||
return *this;
|
return m_value;
|
||||||
}
|
|
||||||
|
|
||||||
string_entry& operator =(const std::string& value)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
|
||||||
m_value = value;
|
|
||||||
return *this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t size() const
|
std::size_t size() const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
|
||||||
return m_value.size();
|
return m_value.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,41 +309,35 @@ namespace cfg
|
|||||||
|
|
||||||
std::string to_string() const override
|
std::string to_string() const override
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
|
||||||
return m_value;
|
return m_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool from_string(const std::string& value) override
|
bool from_string(const std::string& value) override
|
||||||
{
|
{
|
||||||
*this = value;
|
m_value = value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Simple set entry with mutex (TODO: template for various types)
|
// Simple set entry with mutex (TODO: template for various types)
|
||||||
class set_entry final : public entry_base
|
class set_entry final : public _base
|
||||||
{
|
{
|
||||||
mutable std::mutex m_mutex;
|
|
||||||
|
|
||||||
std::set<std::string> m_set;
|
std::set<std::string> m_set;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Default value is empty list in current implementation
|
// Default value is empty list in current implementation
|
||||||
set_entry(node& owner, const std::string& name)
|
set_entry(node* owner, const std::string& name)
|
||||||
: entry_base(type::set, owner, name)
|
: _base(type::set, owner, name)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<std::string> get_set() const
|
std::set<std::string> get_set() const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
|
||||||
|
|
||||||
return m_set;
|
return m_set;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_set(std::set<std::string>&& set)
|
void set_set(std::set<std::string>&& set)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
|
||||||
m_set = std::move(set);
|
m_set = std::move(set);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -471,40 +345,34 @@ namespace cfg
|
|||||||
|
|
||||||
std::vector<std::string> to_list() const override
|
std::vector<std::string> to_list() const override
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
|
||||||
|
|
||||||
return{ m_set.begin(), m_set.end() };
|
return{ m_set.begin(), m_set.end() };
|
||||||
}
|
}
|
||||||
|
|
||||||
bool from_list(std::vector<std::string>&& list) override
|
bool from_list(std::vector<std::string>&& list) override
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m_mutex);
|
|
||||||
m_set = { std::make_move_iterator(list.begin()), std::make_move_iterator(list.end()) };
|
m_set = { std::make_move_iterator(list.begin()), std::make_move_iterator(list.end()) };
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Root type with some predefined nodes. Don't change it, this is not mandatory for adding nodes.
|
class log_entry final : public _base
|
||||||
struct root_node : node
|
|
||||||
{
|
{
|
||||||
node core { *this, "Core" };
|
std::map<std::string, logs::level> m_map;
|
||||||
node vfs { *this, "VFS" };
|
|
||||||
node log { *this, "Log" };
|
public:
|
||||||
node video { *this, "Video" };
|
log_entry(node* owner, const std::string& name)
|
||||||
node audio { *this, "Audio" };
|
: _base(type::log, owner, name)
|
||||||
node io { *this, "Input/Output" };
|
{
|
||||||
node sys { *this, "System" };
|
}
|
||||||
node net { *this, "Net" };
|
|
||||||
node misc { *this, "Miscellaneous" };
|
std::map<std::string, logs::level> get_map() const
|
||||||
|
{
|
||||||
|
return m_map;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_map(std::map<std::string, logs::level>&& map);
|
||||||
|
|
||||||
|
void from_default() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get global configuration root instance
|
|
||||||
extern root_node& get_root();
|
|
||||||
|
|
||||||
// Global configuration root instance (cached reference)
|
|
||||||
static root_node& root = get_root();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Registered log channel
|
|
||||||
#define LOG_CHANNEL(name) extern logs::channel name; namespace logs { static cfg::enum_entry<logs::level, true> name(cfg::root.log, #name, ::name.enabled); }
|
|
||||||
|
|||||||
@@ -6,7 +6,11 @@
|
|||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
using namespace std::literals::string_literals;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
@@ -71,10 +75,25 @@ static time_t to_time(const FILETIME& ft)
|
|||||||
|
|
||||||
static FILETIME from_time(s64 _time)
|
static FILETIME from_time(s64 _time)
|
||||||
{
|
{
|
||||||
const ullong wtime = (_time + 11644473600ULL) * 10000000ULL;
|
|
||||||
FILETIME result;
|
FILETIME result;
|
||||||
result.dwLowDateTime = static_cast<DWORD>(wtime);
|
|
||||||
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
|
if (_time <= -11644473600ll)
|
||||||
|
{
|
||||||
|
result.dwLowDateTime = 0;
|
||||||
|
result.dwHighDateTime = 0;
|
||||||
|
}
|
||||||
|
else if (_time > INT64_MAX / 10000000ll - 11644473600ll)
|
||||||
|
{
|
||||||
|
result.dwLowDateTime = 0xffffffff;
|
||||||
|
result.dwHighDateTime = 0x7fffffff;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const ullong wtime = (_time + 11644473600ull) * 10000000ull;
|
||||||
|
result.dwLowDateTime = static_cast<DWORD>(wtime);
|
||||||
|
result.dwHighDateTime = static_cast<DWORD>(wtime >> 32);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +109,10 @@ static fs::error to_error(DWORD e)
|
|||||||
case ERROR_NEGATIVE_SEEK: return fs::error::inval;
|
case ERROR_NEGATIVE_SEEK: return fs::error::inval;
|
||||||
case ERROR_DIRECTORY: return fs::error::inval;
|
case ERROR_DIRECTORY: return fs::error::inval;
|
||||||
case ERROR_INVALID_NAME: return fs::error::inval;
|
case ERROR_INVALID_NAME: return fs::error::inval;
|
||||||
|
case ERROR_SHARING_VIOLATION: return fs::error::acces;
|
||||||
|
case ERROR_DIR_NOT_EMPTY: return fs::error::notempty;
|
||||||
|
case ERROR_NOT_READY: return fs::error::noent;
|
||||||
|
//case ERROR_INVALID_PARAMETER: return fs::error::inval;
|
||||||
default: fmt::throw_exception("Unknown Win32 error: %u.", e);
|
default: fmt::throw_exception("Unknown Win32 error: %u.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -99,6 +122,8 @@ static fs::error to_error(DWORD e)
|
|||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
|
#include <sys/file.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
@@ -106,10 +131,12 @@ static fs::error to_error(DWORD e)
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <utime.h>
|
#include <utime.h>
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
#if defined(__APPLE__)
|
||||||
#include <copyfile.h>
|
#include <copyfile.h>
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#else
|
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||||
|
#include <sys/socket.h> // sendfile
|
||||||
|
#elif defined(__linux__) || defined(__sun)
|
||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -121,6 +148,7 @@ static fs::error to_error(int e)
|
|||||||
case EEXIST: return fs::error::exist;
|
case EEXIST: return fs::error::exist;
|
||||||
case EINVAL: return fs::error::inval;
|
case EINVAL: return fs::error::inval;
|
||||||
case EACCES: return fs::error::acces;
|
case EACCES: return fs::error::acces;
|
||||||
|
case ENOTEMPTY: return fs::error::notempty;
|
||||||
default: fmt::throw_exception("Unknown system error: %d.", e);
|
default: fmt::throw_exception("Unknown system error: %d.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,6 +181,16 @@ namespace fs
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stat_t file_base::stat()
|
||||||
|
{
|
||||||
|
fmt::throw_exception("fs::file::stat() not supported for %s", typeid(*this).name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void file_base::sync()
|
||||||
|
{
|
||||||
|
// Do notning
|
||||||
|
}
|
||||||
|
|
||||||
dir_base::~dir_base()
|
dir_base::~dir_base()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -390,6 +428,45 @@ bool fs::is_dir(const std::string& path)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool fs::statfs(const std::string& path, fs::device_stat& info)
|
||||||
|
{
|
||||||
|
if (auto device = get_virtual_device(path))
|
||||||
|
{
|
||||||
|
return device->statfs(path, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
ULARGE_INTEGER avail_free;
|
||||||
|
ULARGE_INTEGER total_size;
|
||||||
|
ULARGE_INTEGER total_free;
|
||||||
|
|
||||||
|
if (!GetDiskFreeSpaceExW(to_wchar(path).get(), &avail_free, &total_size, &total_free))
|
||||||
|
{
|
||||||
|
g_tls_error = to_error(GetLastError());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.block_size = 4096; // TODO
|
||||||
|
info.total_size = total_size.QuadPart;
|
||||||
|
info.total_free = total_free.QuadPart;
|
||||||
|
info.avail_free = avail_free.QuadPart;
|
||||||
|
#else
|
||||||
|
struct ::statvfs buf;
|
||||||
|
if (::statvfs(path.c_str(), &buf) != 0)
|
||||||
|
{
|
||||||
|
g_tls_error = to_error(errno);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
info.block_size = buf.f_frsize;
|
||||||
|
info.total_size = info.block_size * buf.f_blocks;
|
||||||
|
info.total_free = info.block_size * buf.f_bfree;
|
||||||
|
info.avail_free = info.block_size * buf.f_bavail;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool fs::create_dir(const std::string& path)
|
bool fs::create_dir(const std::string& path)
|
||||||
{
|
{
|
||||||
if (auto device = get_virtual_device(path))
|
if (auto device = get_virtual_device(path))
|
||||||
@@ -454,7 +531,7 @@ bool fs::remove_dir(const std::string& path)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool fs::rename(const std::string& from, const std::string& to)
|
bool fs::rename(const std::string& from, const std::string& to, bool overwrite)
|
||||||
{
|
{
|
||||||
const auto device = get_virtual_device(from);
|
const auto device = get_virtual_device(from);
|
||||||
|
|
||||||
@@ -469,14 +546,43 @@ bool fs::rename(const std::string& from, const std::string& to)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
if (!MoveFileW(to_wchar(from).get(), to_wchar(to).get()))
|
const auto ws1 = to_wchar(from);
|
||||||
|
const auto ws2 = to_wchar(to);
|
||||||
|
|
||||||
|
if (!MoveFileExW(ws1.get(), ws2.get(), overwrite ? MOVEFILE_REPLACE_EXISTING : 0))
|
||||||
{
|
{
|
||||||
g_tls_error = to_error(GetLastError());
|
DWORD error1 = GetLastError();
|
||||||
|
|
||||||
|
if (overwrite && error1 == ERROR_ACCESS_DENIED && is_dir(from) && is_dir(to))
|
||||||
|
{
|
||||||
|
if (RemoveDirectoryW(ws2.get()))
|
||||||
|
{
|
||||||
|
if (MoveFileW(ws1.get(), ws2.get()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
error1 = GetLastError();
|
||||||
|
CreateDirectoryW(ws2.get(), NULL); // TODO
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
error1 = GetLastError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g_tls_error = to_error(error1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
|
if (!overwrite && exists(to))
|
||||||
|
{
|
||||||
|
g_tls_error = fs::error::exist;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (::rename(from.c_str(), to.c_str()) != 0)
|
if (::rename(from.c_str(), to.c_str()) != 0)
|
||||||
{
|
{
|
||||||
g_tls_error = to_error(errno);
|
g_tls_error = to_error(errno);
|
||||||
@@ -525,14 +631,20 @@ bool fs::copy_file(const std::string& from, const std::string& to, bool overwrit
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Here we use kernel-space copying for performance reasons
|
// Here we use kernel-space copying for performance reasons
|
||||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
#if defined(__APPLE__)
|
||||||
// fcopyfile works on FreeBSD and OS X 10.5+
|
// fcopyfile works on OS X 10.5+
|
||||||
if (::fcopyfile(input, output, 0, COPYFILE_ALL))
|
if (::fcopyfile(input, output, 0, COPYFILE_ALL))
|
||||||
#else
|
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||||
|
if (::sendfile(input, output, 0, 0, NULL, NULL, 0))
|
||||||
|
#elif defined(__linux__) || defined(__sun)
|
||||||
// sendfile will work with non-socket output (i.e. regular file) on Linux 2.6.33+
|
// sendfile will work with non-socket output (i.e. regular file) on Linux 2.6.33+
|
||||||
off_t bytes_copied = 0;
|
off_t bytes_copied = 0;
|
||||||
struct ::stat fileinfo = { 0 };
|
struct ::stat fileinfo = { 0 };
|
||||||
if (::fstat(input, &fileinfo) || ::sendfile(output, input, &bytes_copied, fileinfo.st_size))
|
if (::fstat(input, &fileinfo) || ::sendfile(output, input, &bytes_copied, fileinfo.st_size))
|
||||||
|
#else // NetBSD, OpenBSD, etc.
|
||||||
|
fmt::throw_exception("fs::copy_file() isn't implemented for this platform.\nFrom: %s\nTo: %s", from, to);
|
||||||
|
errno = 0;
|
||||||
|
if (true)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
const int err = errno;
|
const int err = errno;
|
||||||
@@ -584,18 +696,17 @@ bool fs::truncate_file(const std::string& path, u64 length)
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Open the file
|
// Open the file
|
||||||
const auto handle = CreateFileW(to_wchar(path).get(), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
const auto handle = CreateFileW(to_wchar(path).get(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
if (handle == INVALID_HANDLE_VALUE)
|
if (handle == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
g_tls_error = to_error(GetLastError());
|
g_tls_error = to_error(GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LARGE_INTEGER distance;
|
FILE_END_OF_FILE_INFO _eof;
|
||||||
distance.QuadPart = length;
|
_eof.EndOfFile.QuadPart = length;
|
||||||
|
|
||||||
// Seek and truncate
|
if (!SetFileInformationByHandle(handle, FileEndOfFileInfo, &_eof, sizeof(_eof)))
|
||||||
if (!SetFilePointerEx(handle, distance, NULL, FILE_BEGIN) || !SetEndOfFile(handle))
|
|
||||||
{
|
{
|
||||||
g_tls_error = to_error(GetLastError());
|
g_tls_error = to_error(GetLastError());
|
||||||
CloseHandle(handle);
|
CloseHandle(handle);
|
||||||
@@ -624,7 +735,7 @@ bool fs::utime(const std::string& path, s64 atime, s64 mtime)
|
|||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// Open the file
|
// Open the file
|
||||||
const auto handle = CreateFileW(to_wchar(path).get(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
const auto handle = CreateFileW(to_wchar(path).get(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
if (handle == INVALID_HANDLE_VALUE)
|
if (handle == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
g_tls_error = to_error(GetLastError());
|
g_tls_error = to_error(GetLastError());
|
||||||
@@ -703,7 +814,13 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
|||||||
disp = test(mode & fs::trunc) ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
disp = test(mode & fs::trunc) ? TRUNCATE_EXISTING : OPEN_EXISTING;
|
||||||
}
|
}
|
||||||
|
|
||||||
const HANDLE handle = CreateFileW(to_wchar(path).get(), access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
|
DWORD share = FILE_SHARE_READ;
|
||||||
|
if (!test(mode & fs::unshare))
|
||||||
|
{
|
||||||
|
share |= FILE_SHARE_WRITE | FILE_SHARE_DELETE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HANDLE handle = CreateFileW(to_wchar(path).get(), access, share, NULL, disp, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
|
||||||
if (handle == INVALID_HANDLE_VALUE)
|
if (handle == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
@@ -711,7 +828,7 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
class windows_file final : public file_base
|
class windows_file final : public file_base, public get_native_handle
|
||||||
{
|
{
|
||||||
const HANDLE m_handle;
|
const HANDLE m_handle;
|
||||||
|
|
||||||
@@ -742,25 +859,22 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sync() override
|
||||||
|
{
|
||||||
|
verify("file::sync" HERE), FlushFileBuffers(m_handle);
|
||||||
|
}
|
||||||
|
|
||||||
bool trunc(u64 length) override
|
bool trunc(u64 length) override
|
||||||
{
|
{
|
||||||
LARGE_INTEGER old, pos;
|
FILE_END_OF_FILE_INFO _eof;
|
||||||
|
_eof.EndOfFile.QuadPart = length;
|
||||||
|
|
||||||
pos.QuadPart = 0;
|
if (!SetFileInformationByHandle(m_handle, FileEndOfFileInfo, &_eof, sizeof(_eof)))
|
||||||
if (!SetFilePointerEx(m_handle, pos, &old, FILE_CURRENT)) // get old position
|
|
||||||
{
|
{
|
||||||
g_tls_error = to_error(GetLastError());
|
g_tls_error = to_error(GetLastError());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
pos.QuadPart = length;
|
|
||||||
if (!SetFilePointerEx(m_handle, pos, NULL, FILE_BEGIN)) // set new position
|
|
||||||
{
|
|
||||||
g_tls_error = to_error(GetLastError());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
verify("file::trunc" HERE), SetEndOfFile(m_handle), SetFilePointerEx(m_handle, old, NULL, FILE_BEGIN);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,7 +911,11 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
|||||||
whence == seek_end ? FILE_END :
|
whence == seek_end ? FILE_END :
|
||||||
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
|
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
|
||||||
|
|
||||||
verify("file::seek" HERE), SetFilePointerEx(m_handle, pos, &pos, mode);
|
if (!SetFilePointerEx(m_handle, pos, &pos, mode))
|
||||||
|
{
|
||||||
|
g_tls_error = to_error(GetLastError());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return pos.QuadPart;
|
return pos.QuadPart;
|
||||||
}
|
}
|
||||||
@@ -809,6 +927,11 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
|||||||
|
|
||||||
return size.QuadPart;
|
return size.QuadPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
native_handle get() override
|
||||||
|
{
|
||||||
|
return m_handle;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
m_file = std::make_unique<windows_file>(handle);
|
m_file = std::make_unique<windows_file>(handle);
|
||||||
@@ -832,7 +955,14 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
class unix_file final : public file_base
|
if (test(mode & fs::unshare) && ::flock(fd, LOCK_EX | LOCK_NB) != 0)
|
||||||
|
{
|
||||||
|
g_tls_error = errno == EWOULDBLOCK ? fs::error::acces : to_error(errno);
|
||||||
|
::close(fd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
class unix_file final : public file_base, public get_native_handle
|
||||||
{
|
{
|
||||||
const int m_fd;
|
const int m_fd;
|
||||||
|
|
||||||
@@ -863,6 +993,11 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sync() override
|
||||||
|
{
|
||||||
|
verify("file::sync" HERE), ::fsync(m_fd) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool trunc(u64 length) override
|
bool trunc(u64 length) override
|
||||||
{
|
{
|
||||||
if (::ftruncate(m_fd, length) != 0)
|
if (::ftruncate(m_fd, length) != 0)
|
||||||
@@ -899,7 +1034,12 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
|||||||
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
|
(fmt::throw_exception("Invalid whence (0x%x)" HERE, whence), 0);
|
||||||
|
|
||||||
const auto result = ::lseek(m_fd, offset, mode);
|
const auto result = ::lseek(m_fd, offset, mode);
|
||||||
verify("file::seek" HERE), result != -1;
|
|
||||||
|
if (result == -1)
|
||||||
|
{
|
||||||
|
g_tls_error = to_error(errno);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -911,6 +1051,11 @@ fs::file::file(const std::string& path, bs_t<open_mode> mode)
|
|||||||
|
|
||||||
return file_info.st_size;
|
return file_info.st_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
native_handle get() override
|
||||||
|
{
|
||||||
|
return m_fd;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
m_file = std::make_unique<unix_file>(fd);
|
m_file = std::make_unique<unix_file>(fd);
|
||||||
@@ -933,11 +1078,6 @@ fs::file::file(const void* ptr, std::size_t size)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::stat_t stat() override
|
|
||||||
{
|
|
||||||
fmt::raw_error("fs::file::memory_stream::stat(): not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool trunc(u64 length) override
|
bool trunc(u64 length) override
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -966,11 +1106,20 @@ fs::file::file(const void* ptr, std::size_t size)
|
|||||||
|
|
||||||
u64 seek(s64 offset, fs::seek_mode whence) override
|
u64 seek(s64 offset, fs::seek_mode whence) override
|
||||||
{
|
{
|
||||||
return
|
const s64 new_pos =
|
||||||
whence == fs::seek_set ? m_pos = offset :
|
whence == fs::seek_set ? offset :
|
||||||
whence == fs::seek_cur ? m_pos = offset + m_pos :
|
whence == fs::seek_cur ? offset + m_pos :
|
||||||
whence == fs::seek_end ? m_pos = offset + m_size :
|
whence == fs::seek_end ? offset + size() :
|
||||||
(fmt::raw_error("fs::file::memory_stream::seek(): invalid whence"), 0);
|
(fmt::raw_error("fs::file::memory_stream::seek(): invalid whence"), 0);
|
||||||
|
|
||||||
|
if (new_pos < 0)
|
||||||
|
{
|
||||||
|
fs::g_tls_error = fs::error::inval;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pos = new_pos;
|
||||||
|
return m_pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 size() override
|
u64 size() override
|
||||||
@@ -982,6 +1131,20 @@ fs::file::file(const void* ptr, std::size_t size)
|
|||||||
m_file = std::make_unique<memory_stream>(ptr, size);
|
m_file = std::make_unique<memory_stream>(ptr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs::native_handle fs::file::get_handle() const
|
||||||
|
{
|
||||||
|
if (auto getter = dynamic_cast<get_native_handle*>(m_file.get()))
|
||||||
|
{
|
||||||
|
return getter->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
#else
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void fs::dir::xnull() const
|
void fs::dir::xnull() const
|
||||||
{
|
{
|
||||||
fmt::throw_exception<std::logic_error>("fs::dir is null");
|
fmt::throw_exception<std::logic_error>("fs::dir is null");
|
||||||
@@ -1313,6 +1476,8 @@ void fmt_class_string<fs::error>::format(std::string& out, u64 arg)
|
|||||||
case fs::error::inval: return "Invalid arguments";
|
case fs::error::inval: return "Invalid arguments";
|
||||||
case fs::error::noent: return "Not found";
|
case fs::error::noent: return "Not found";
|
||||||
case fs::error::exist: return "Already exists";
|
case fs::error::exist: return "Already exists";
|
||||||
|
case fs::error::acces: return "Access violation";
|
||||||
|
case fs::error::notempty: return "Not empty";
|
||||||
}
|
}
|
||||||
|
|
||||||
return unknown;
|
return unknown;
|
||||||
|
|||||||
@@ -10,6 +10,12 @@
|
|||||||
|
|
||||||
namespace fs
|
namespace fs
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
using native_handle = void*;
|
||||||
|
#else
|
||||||
|
using native_handle = int;
|
||||||
|
#endif
|
||||||
|
|
||||||
// File open mode flags
|
// File open mode flags
|
||||||
enum class open_mode : u32
|
enum class open_mode : u32
|
||||||
{
|
{
|
||||||
@@ -19,6 +25,7 @@ namespace fs
|
|||||||
create,
|
create,
|
||||||
trunc,
|
trunc,
|
||||||
excl,
|
excl,
|
||||||
|
unshare,
|
||||||
|
|
||||||
__bitset_enum_max
|
__bitset_enum_max
|
||||||
};
|
};
|
||||||
@@ -29,6 +36,7 @@ namespace fs
|
|||||||
constexpr auto create = +open_mode::create; // Create file if it doesn't exist
|
constexpr auto create = +open_mode::create; // Create file if it doesn't exist
|
||||||
constexpr auto trunc = +open_mode::trunc; // Clear opened file if it's not empty
|
constexpr auto trunc = +open_mode::trunc; // Clear opened file if it's not empty
|
||||||
constexpr auto excl = +open_mode::excl; // Failure if the file already exists (used with `create`)
|
constexpr auto excl = +open_mode::excl; // Failure if the file already exists (used with `create`)
|
||||||
|
constexpr auto unshare = +open_mode::unshare; // Prevent opening the file twice
|
||||||
|
|
||||||
constexpr auto rewrite = open_mode::write + open_mode::create + open_mode::trunc;
|
constexpr auto rewrite = open_mode::write + open_mode::create + open_mode::trunc;
|
||||||
|
|
||||||
@@ -55,12 +63,19 @@ namespace fs
|
|||||||
s64 ctime;
|
s64 ctime;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Native handle getter
|
||||||
|
struct get_native_handle
|
||||||
|
{
|
||||||
|
virtual native_handle get() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
// File handle base
|
// File handle base
|
||||||
struct file_base
|
struct file_base
|
||||||
{
|
{
|
||||||
virtual ~file_base();
|
virtual ~file_base();
|
||||||
|
|
||||||
virtual stat_t stat() = 0;
|
virtual stat_t stat();
|
||||||
|
virtual void sync();
|
||||||
virtual bool trunc(u64 length) = 0;
|
virtual bool trunc(u64 length) = 0;
|
||||||
virtual u64 read(void* buffer, u64 size) = 0;
|
virtual u64 read(void* buffer, u64 size) = 0;
|
||||||
virtual u64 write(const void* buffer, u64 size) = 0;
|
virtual u64 write(const void* buffer, u64 size) = 0;
|
||||||
@@ -83,12 +98,22 @@ namespace fs
|
|||||||
virtual void rewind() = 0;
|
virtual void rewind() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Device information
|
||||||
|
struct device_stat
|
||||||
|
{
|
||||||
|
u64 block_size;
|
||||||
|
u64 total_size;
|
||||||
|
u64 total_free; // Total size of free space
|
||||||
|
u64 avail_free; // Free space available to unprivileged user
|
||||||
|
};
|
||||||
|
|
||||||
// Virtual device
|
// Virtual device
|
||||||
struct device_base
|
struct device_base
|
||||||
{
|
{
|
||||||
virtual ~device_base();
|
virtual ~device_base();
|
||||||
|
|
||||||
virtual bool stat(const std::string& path, stat_t& info) = 0;
|
virtual bool stat(const std::string& path, stat_t& info) = 0;
|
||||||
|
virtual bool statfs(const std::string& path, device_stat& info) = 0;
|
||||||
virtual bool remove_dir(const std::string& path) = 0;
|
virtual bool remove_dir(const std::string& path) = 0;
|
||||||
virtual bool create_dir(const std::string& path) = 0;
|
virtual bool create_dir(const std::string& path) = 0;
|
||||||
virtual bool rename(const std::string& from, const std::string& to) = 0;
|
virtual bool rename(const std::string& from, const std::string& to) = 0;
|
||||||
@@ -121,6 +146,9 @@ namespace fs
|
|||||||
// Check whether the directory exists and is NOT a file
|
// Check whether the directory exists and is NOT a file
|
||||||
bool is_dir(const std::string& path);
|
bool is_dir(const std::string& path);
|
||||||
|
|
||||||
|
// Get filesystem information
|
||||||
|
bool statfs(const std::string& path, device_stat& info);
|
||||||
|
|
||||||
// Delete empty directory
|
// Delete empty directory
|
||||||
bool remove_dir(const std::string& path);
|
bool remove_dir(const std::string& path);
|
||||||
|
|
||||||
@@ -131,7 +159,7 @@ namespace fs
|
|||||||
bool create_path(const std::string& path);
|
bool create_path(const std::string& path);
|
||||||
|
|
||||||
// Rename (move) file or directory
|
// Rename (move) file or directory
|
||||||
bool rename(const std::string& from, const std::string& to);
|
bool rename(const std::string& from, const std::string& to, bool overwrite);
|
||||||
|
|
||||||
// Copy file contents
|
// Copy file contents
|
||||||
bool copy_file(const std::string& from, const std::string& to, bool overwrite);
|
bool copy_file(const std::string& from, const std::string& to, bool overwrite);
|
||||||
@@ -206,6 +234,13 @@ namespace fs
|
|||||||
return m_file->stat();
|
return m_file->stat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync file buffers
|
||||||
|
void sync() const
|
||||||
|
{
|
||||||
|
if (!m_file) xnull();
|
||||||
|
return m_file->sync();
|
||||||
|
}
|
||||||
|
|
||||||
// Read the data from the file and return the amount of data written in buffer
|
// 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) const
|
||||||
{
|
{
|
||||||
@@ -326,6 +361,9 @@ namespace fs
|
|||||||
if (seek(0), !read(result)) xfail();
|
if (seek(0), !read(result)) xfail();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get native handle if available
|
||||||
|
native_handle get_handle() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class dir final
|
class dir final
|
||||||
@@ -384,7 +422,7 @@ namespace fs
|
|||||||
|
|
||||||
class iterator
|
class iterator
|
||||||
{
|
{
|
||||||
dir* m_parent;
|
const dir* m_parent;
|
||||||
dir_entry m_entry;
|
dir_entry m_entry;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -394,7 +432,7 @@ namespace fs
|
|||||||
from_current
|
from_current
|
||||||
};
|
};
|
||||||
|
|
||||||
iterator(dir* parent, mode mode_ = mode::from_first)
|
iterator(const dir* parent, mode mode_ = mode::from_first)
|
||||||
: m_parent(parent)
|
: m_parent(parent)
|
||||||
{
|
{
|
||||||
if (!m_parent)
|
if (!m_parent)
|
||||||
@@ -420,7 +458,7 @@ namespace fs
|
|||||||
|
|
||||||
iterator& operator++()
|
iterator& operator++()
|
||||||
{
|
{
|
||||||
*this = { m_parent, mode::from_current };
|
*this = {m_parent, mode::from_current};
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,14 +468,14 @@ namespace fs
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
iterator begin()
|
iterator begin() const
|
||||||
{
|
{
|
||||||
return{ m_dir ? this : nullptr };
|
return {m_dir ? this : nullptr};
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator end()
|
iterator end() const
|
||||||
{
|
{
|
||||||
return{ nullptr };
|
return {nullptr};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -464,6 +502,7 @@ namespace fs
|
|||||||
noent,
|
noent,
|
||||||
exist,
|
exist,
|
||||||
acces,
|
acces,
|
||||||
|
notempty,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Error code returned
|
// Error code returned
|
||||||
@@ -488,11 +527,6 @@ namespace fs
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
stat_t stat() override
|
|
||||||
{
|
|
||||||
fmt::raw_error("fs::container_stream<>::stat(): not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool trunc(u64 length) override
|
bool trunc(u64 length) override
|
||||||
{
|
{
|
||||||
obj.resize(length);
|
obj.resize(length);
|
||||||
@@ -547,11 +581,20 @@ namespace fs
|
|||||||
|
|
||||||
u64 seek(s64 offset, seek_mode whence) override
|
u64 seek(s64 offset, seek_mode whence) override
|
||||||
{
|
{
|
||||||
return
|
const s64 new_pos =
|
||||||
whence == fs::seek_set ? pos = offset :
|
whence == fs::seek_set ? offset :
|
||||||
whence == fs::seek_cur ? pos = offset + pos :
|
whence == fs::seek_cur ? offset + pos :
|
||||||
whence == fs::seek_end ? pos = offset + size() :
|
whence == fs::seek_end ? offset + size() :
|
||||||
(fmt::raw_error("fs::container_stream<>::seek(): invalid whence"), 0);
|
(fmt::raw_error("fs::container_stream<>::seek(): invalid whence"), 0);
|
||||||
|
|
||||||
|
if (new_pos < 0)
|
||||||
|
{
|
||||||
|
fs::g_tls_error = fs::error::inval;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = new_pos;
|
||||||
|
return pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 size() override
|
u64 size() override
|
||||||
@@ -567,4 +610,17 @@ namespace fs
|
|||||||
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container)));
|
result.reset(std::make_unique<container_stream<T>>(std::forward<T>(container)));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
bool write_file(const std::string& path, bs_t<fs::open_mode> mode, const Args&... args)
|
||||||
|
{
|
||||||
|
if (fs::file f{path, mode})
|
||||||
|
{
|
||||||
|
// Write args sequentially
|
||||||
|
int seq[]{ (f.write(args), 0)... };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,817 @@
|
|||||||
|
#include "stdafx.h"
|
||||||
|
#ifdef WITH_GDB_DEBUGGER
|
||||||
|
|
||||||
|
#include "GDBDebugServer.h"
|
||||||
|
#include "Log.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include "Emu/Memory/Memory.h"
|
||||||
|
#include "Emu/System.h"
|
||||||
|
#include "Emu/IdManager.h"
|
||||||
|
#include "Emu/CPU/CPUThread.h"
|
||||||
|
#include "Emu/Cell/PPUThread.h"
|
||||||
|
#include "Emu/Cell/RawSPUThread.h"
|
||||||
|
#include "Emu/Cell/SPUThread.h"
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#include"fcntl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern void ppu_set_breakpoint(u32 addr);
|
||||||
|
extern void ppu_remove_breakpoint(u32 addr);
|
||||||
|
|
||||||
|
logs::channel gdbDebugServer("gdbDebugServer");
|
||||||
|
|
||||||
|
int sock_init(void)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
WSADATA wsa_data;
|
||||||
|
return WSAStartup(MAKEWORD(1, 1), &wsa_data);
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int sock_quit(void)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return WSACleanup();
|
||||||
|
#else
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
int closesocket(socket_t s) {
|
||||||
|
return close(s);
|
||||||
|
}
|
||||||
|
const int SOCKET_ERROR = -1;
|
||||||
|
const socket_t INVALID_SOCKET = -1;
|
||||||
|
#define sscanf_s sscanf
|
||||||
|
#define HEX_U32 "x"
|
||||||
|
#define HEX_U64 "lx"
|
||||||
|
#else
|
||||||
|
#define HEX_U32 "lx"
|
||||||
|
#define HEX_U64 "llx"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool check_errno_again() {
|
||||||
|
#ifdef _WIN32
|
||||||
|
int err = GetLastError();
|
||||||
|
return (err == WSAEWOULDBLOCK);
|
||||||
|
#else
|
||||||
|
int err = errno;
|
||||||
|
return (err == EAGAIN) || (err == EWOULDBLOCK);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string u32_to_hex(u32 i) {
|
||||||
|
return fmt::format("%" HEX_U32, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string u64_to_padded_hex(u64 value) {
|
||||||
|
return fmt::format("%.16" HEX_U64, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string u32_to_padded_hex(u32 value) {
|
||||||
|
return fmt::format("%.8" HEX_U32, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 hex_to_u8(std::string val) {
|
||||||
|
u8 result;
|
||||||
|
sscanf_s(val.c_str(), "%02hhX", &result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 hex_to_u32(std::string val) {
|
||||||
|
u32 result;
|
||||||
|
sscanf_s(val.c_str(), "%" HEX_U32, &result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 hex_to_u64(std::string val) {
|
||||||
|
u64 result;
|
||||||
|
sscanf_s(val.c_str(), "%" HEX_U64, &result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDBDebugServer::start_server()
|
||||||
|
{
|
||||||
|
server_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
||||||
|
if (server_socket == INVALID_SOCKET) {
|
||||||
|
gdbDebugServer.error("Error creating server socket");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
{
|
||||||
|
int mode = 1;
|
||||||
|
ioctlsocket(server_socket, FIONBIO, (u_long FAR *)&mode);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
fcntl(server_socket, F_SETFL, fcntl(server_socket, F_GETFL) | O_NONBLOCK);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int err;
|
||||||
|
|
||||||
|
sockaddr_in server_saddr;
|
||||||
|
server_saddr.sin_family = AF_INET;
|
||||||
|
int port = g_cfg.misc.gdb_server_port;
|
||||||
|
server_saddr.sin_port = htons(port);
|
||||||
|
server_saddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||||
|
|
||||||
|
err = bind(server_socket, (struct sockaddr *) &server_saddr, sizeof(server_saddr));
|
||||||
|
if (err == SOCKET_ERROR) {
|
||||||
|
gdbDebugServer.error("Error binding to port %d", port);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = listen(server_socket, 1);
|
||||||
|
if (err == SOCKET_ERROR) {
|
||||||
|
gdbDebugServer.error("Error listening on port %d", port);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gdbDebugServer.success("GDB Debug Server listening on port %d", port);
|
||||||
|
}
|
||||||
|
|
||||||
|
int GDBDebugServer::read(void * buf, int cnt)
|
||||||
|
{
|
||||||
|
while (!stop) {
|
||||||
|
int result = recv(client_socket, reinterpret_cast<char*>(buf), cnt, 0);
|
||||||
|
|
||||||
|
if (result == SOCKET_ERROR) {
|
||||||
|
if (check_errno_again()) {
|
||||||
|
thread_ctrl::wait_for(50);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
gdbDebugServer.error("Error during socket read");
|
||||||
|
fmt::throw_exception("Error during socket read" HERE);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char GDBDebugServer::read_char()
|
||||||
|
{
|
||||||
|
char result;
|
||||||
|
int cnt = read(&result, 1);
|
||||||
|
if (!cnt) {
|
||||||
|
fmt::throw_exception("Tried to read char, but no data was available" HERE);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 GDBDebugServer::read_hexbyte()
|
||||||
|
{
|
||||||
|
char buf[2];
|
||||||
|
read(buf, 2);
|
||||||
|
return static_cast<u8>(strtol(buf, nullptr, 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDBDebugServer::try_read_cmd(gdb_cmd & out_cmd)
|
||||||
|
{
|
||||||
|
char c = read_char();
|
||||||
|
//interrupt
|
||||||
|
if (UNLIKELY(c == 0x03)) {
|
||||||
|
out_cmd.cmd = "\0x03";
|
||||||
|
out_cmd.data = "";
|
||||||
|
out_cmd.checksum = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (UNLIKELY(c != '$')) {
|
||||||
|
//gdb starts conversation with + for some reason
|
||||||
|
if (c == '+') {
|
||||||
|
c = read_char();
|
||||||
|
}
|
||||||
|
if (c != '$') {
|
||||||
|
fmt::throw_exception("Expected start of packet character '$', got '%c' instead" HERE, c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//clear packet data
|
||||||
|
out_cmd.cmd = "";
|
||||||
|
out_cmd.data = "";
|
||||||
|
out_cmd.checksum = 0;
|
||||||
|
bool cmd_part = true;
|
||||||
|
u8 checksum = 0;
|
||||||
|
while(true) {
|
||||||
|
c = read_char();
|
||||||
|
if (c == '#') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
checksum = (checksum + reinterpret_cast<u8&>(c)) % 256;
|
||||||
|
//escaped char
|
||||||
|
if (c == '}') {
|
||||||
|
c = read_char() ^ 0x20;
|
||||||
|
checksum = (checksum + reinterpret_cast<u8&>(c)) % 256;
|
||||||
|
}
|
||||||
|
//cmd-data splitters
|
||||||
|
if (cmd_part && ((c == ':') || (c == '.') || (c == ';'))) {
|
||||||
|
cmd_part = false;
|
||||||
|
}
|
||||||
|
if (cmd_part) {
|
||||||
|
out_cmd.cmd += c;
|
||||||
|
//only q and v commands can have multi-char command
|
||||||
|
if ((out_cmd.cmd.length() == 1) && (c != 'q') && (c != 'v')) {
|
||||||
|
cmd_part = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out_cmd.data += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out_cmd.checksum = read_hexbyte();
|
||||||
|
if (out_cmd.checksum != checksum) {
|
||||||
|
throw new wrong_checksum_exception("Wrong checksum for packet" HERE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::read_cmd(gdb_cmd & out_cmd)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
try_read_cmd(out_cmd);
|
||||||
|
ack(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
catch (wrong_checksum_exception) {
|
||||||
|
ack(false);
|
||||||
|
}
|
||||||
|
catch (std::runtime_error e) {
|
||||||
|
gdbDebugServer.error(e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDBDebugServer::send(const char * buf, int cnt)
|
||||||
|
{
|
||||||
|
gdbDebugServer.trace("Sending %s (%d bytes)", buf, cnt);
|
||||||
|
while (!stop) {
|
||||||
|
int res = ::send(client_socket, buf, cnt, 0);
|
||||||
|
if (res == SOCKET_ERROR) {
|
||||||
|
if (check_errno_again()) {
|
||||||
|
thread_ctrl::wait_for(50);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
gdbDebugServer.error("Failed sending %d bytes", cnt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDBDebugServer::send_char(char c)
|
||||||
|
{
|
||||||
|
send(&c, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDBDebugServer::ack(bool accepted)
|
||||||
|
{
|
||||||
|
send_char(accepted ? '+' : '-');
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDBDebugServer::send_cmd(const std::string & cmd)
|
||||||
|
{
|
||||||
|
u8 checksum = 0;
|
||||||
|
std::string buf;
|
||||||
|
buf.reserve(cmd.length() + 4);
|
||||||
|
buf += "$";
|
||||||
|
for (int i = 0; i < cmd.length(); ++i) {
|
||||||
|
checksum = (checksum + append_encoded_char(cmd[i], buf)) % 256;
|
||||||
|
}
|
||||||
|
buf += "#";
|
||||||
|
buf += to_hexbyte(checksum);
|
||||||
|
send(buf.c_str(), static_cast<int>(buf.length()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::send_cmd_ack(const std::string & cmd)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
send_cmd(cmd);
|
||||||
|
char c = read_char();
|
||||||
|
if (LIKELY(c == '+')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (UNLIKELY(c != '-')) {
|
||||||
|
gdbDebugServer.error("Wrong acknowledge character received %c", c);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
gdbDebugServer.warning("Client rejected our cmd");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 GDBDebugServer::append_encoded_char(char c, std::string & str)
|
||||||
|
{
|
||||||
|
u8 checksum = 0;
|
||||||
|
if (UNLIKELY((c == '#') || (c == '$') || (c == '}'))) {
|
||||||
|
str += '}';
|
||||||
|
c ^= 0x20;
|
||||||
|
checksum = '}';
|
||||||
|
}
|
||||||
|
checksum = (checksum + reinterpret_cast<u8&>(c)) % 256;
|
||||||
|
str += c;
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GDBDebugServer::to_hexbyte(u8 i)
|
||||||
|
{
|
||||||
|
std::string result = "00";
|
||||||
|
u8 i1 = i & 0xF;
|
||||||
|
u8 i2 = i >> 4;
|
||||||
|
result[0] = i2 > 9 ? 'a' + i2 - 10 : '0' + i2;
|
||||||
|
result[1] = i1 > 9 ? 'a' + i1 - 10 : '0' + i1;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::select_thread(u64 id)
|
||||||
|
{
|
||||||
|
//in case we have none at all
|
||||||
|
selected_thread.reset();
|
||||||
|
const auto on_select = [&](u32, cpu_thread& cpu)
|
||||||
|
{
|
||||||
|
return (id == ALL_THREADS) || (id == ANY_THREAD) || (cpu.id == id);
|
||||||
|
};
|
||||||
|
if (auto ppu = idm::select<ppu_thread>(on_select)) {
|
||||||
|
selected_thread = ppu.ptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GDBDebugServer::get_reg(std::shared_ptr<ppu_thread> thread, u32 rid)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
//ids from gdb/features/rs6000/powerpc-64.c
|
||||||
|
//pc
|
||||||
|
switch (rid) {
|
||||||
|
case 64:
|
||||||
|
return u64_to_padded_hex(thread->cia);
|
||||||
|
//msr?
|
||||||
|
case 65:
|
||||||
|
return std::string(16, 'x');
|
||||||
|
case 66:
|
||||||
|
return u32_to_padded_hex(thread->cr_pack());
|
||||||
|
case 67:
|
||||||
|
return u64_to_padded_hex(thread->lr);
|
||||||
|
case 68:
|
||||||
|
return u64_to_padded_hex(thread->ctr);
|
||||||
|
//xer
|
||||||
|
case 69:
|
||||||
|
return std::string(8, 'x');
|
||||||
|
//fpscr
|
||||||
|
case 70:
|
||||||
|
return std::string(8, 'x');
|
||||||
|
default:
|
||||||
|
if (rid > 70) return "";
|
||||||
|
return (rid > 31)
|
||||||
|
? u64_to_padded_hex(reinterpret_cast<u64&>(thread->fpr[rid - 32])) //fpr
|
||||||
|
: u64_to_padded_hex(thread->gpr[rid]); //gpr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::string value)
|
||||||
|
{
|
||||||
|
switch (rid) {
|
||||||
|
case 64:
|
||||||
|
thread->cia = static_cast<u32>(hex_to_u64(value));
|
||||||
|
return true;
|
||||||
|
//msr?
|
||||||
|
case 65:
|
||||||
|
return true;
|
||||||
|
case 66:
|
||||||
|
thread->cr_unpack(hex_to_u32(value));
|
||||||
|
return true;
|
||||||
|
case 67:
|
||||||
|
thread->lr = hex_to_u64(value);
|
||||||
|
return true;
|
||||||
|
case 68:
|
||||||
|
thread->ctr = hex_to_u64(value);
|
||||||
|
return true;
|
||||||
|
//xer
|
||||||
|
case 69:
|
||||||
|
return true;
|
||||||
|
//fpscr
|
||||||
|
case 70:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
if (rid > 70) return false;
|
||||||
|
if (rid > 31) {
|
||||||
|
u64 val = hex_to_u64(value);
|
||||||
|
thread->fpr[rid - 32] = reinterpret_cast<f64&>(val);
|
||||||
|
} else {
|
||||||
|
thread->gpr[rid] = hex_to_u64(value);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GDBDebugServer::get_reg_size(std::shared_ptr<ppu_thread> thread, u32 rid)
|
||||||
|
{
|
||||||
|
switch (rid) {
|
||||||
|
case 66:
|
||||||
|
case 69:
|
||||||
|
case 70:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
if (rid > 70) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::send_reason()
|
||||||
|
{
|
||||||
|
return send_cmd_ack("S05");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_extended_mode(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
return send_cmd_ack("OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_reason(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
return send_reason();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_supported(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
return send_cmd_ack("PacketSize=1200");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_thread_info(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
std::string result = "";
|
||||||
|
const auto on_select = [&](u32, cpu_thread& cpu)
|
||||||
|
{
|
||||||
|
if (result.length()) {
|
||||||
|
result += ",";
|
||||||
|
}
|
||||||
|
result += u64_to_padded_hex(static_cast<u64>(cpu.id));
|
||||||
|
};
|
||||||
|
idm::select<ppu_thread>(on_select);
|
||||||
|
idm::select<ARMv7Thread>(on_select);
|
||||||
|
idm::select<RawSPUThread>(on_select);
|
||||||
|
idm::select<SPUThread>(on_select);
|
||||||
|
|
||||||
|
//todo: this may exceed max command length
|
||||||
|
result = "m" + result + "l";
|
||||||
|
|
||||||
|
return send_cmd_ack(result);;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_current_thread(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
return send_cmd_ack(selected_thread.expired() ? "" : u64_to_padded_hex(selected_thread.lock()->id));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_read_register(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
select_thread(general_ops_thread_id);
|
||||||
|
auto th = selected_thread.lock();
|
||||||
|
if (th->id_type() == 1) {
|
||||||
|
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||||
|
u32 rid = hex_to_u32(cmd.data);
|
||||||
|
std::string result = get_reg(ppu, rid);
|
||||||
|
if (!result.length()) {
|
||||||
|
gdbDebugServer.warning("Wrong register id %d", rid);
|
||||||
|
return send_cmd_ack("E01");
|
||||||
|
}
|
||||||
|
return send_cmd_ack(result);
|
||||||
|
}
|
||||||
|
gdbDebugServer.warning("Unimplemented thread type %d", th->id_type());
|
||||||
|
return send_cmd_ack("");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_write_register(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
select_thread(general_ops_thread_id);
|
||||||
|
auto th = selected_thread.lock();
|
||||||
|
if (th->id_type() == 1) {
|
||||||
|
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||||
|
size_t eq_pos = cmd.data.find('=');
|
||||||
|
if (eq_pos == std::string::npos) {
|
||||||
|
gdbDebugServer.warning("Wrong write_register cmd data %s", cmd.data.c_str());
|
||||||
|
return send_cmd_ack("E02");
|
||||||
|
}
|
||||||
|
u32 rid = hex_to_u32(cmd.data.substr(0, eq_pos));
|
||||||
|
std::string value = cmd.data.substr(eq_pos + 1);
|
||||||
|
if (!set_reg(ppu, rid, value)) {
|
||||||
|
gdbDebugServer.warning("Wrong register id %d", rid);
|
||||||
|
return send_cmd_ack("E01");
|
||||||
|
}
|
||||||
|
return send_cmd_ack("OK");
|
||||||
|
}
|
||||||
|
gdbDebugServer.warning("Unimplemented thread type %d", th->id_type());
|
||||||
|
return send_cmd_ack("");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_read_memory(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
size_t s = cmd.data.find(',');
|
||||||
|
u32 addr = hex_to_u32(cmd.data.substr(0, s));
|
||||||
|
u32 len = hex_to_u32(cmd.data.substr(s + 1));
|
||||||
|
std::string result;
|
||||||
|
result.reserve(len * 2);
|
||||||
|
for (u32 i = 0; i < len; ++i) {
|
||||||
|
if (vm::check_addr(addr + i)) {
|
||||||
|
result += to_hexbyte(vm::read8(addr + i));
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
//result += "xx";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (len && !result.length()) {
|
||||||
|
//nothing read
|
||||||
|
return send_cmd_ack("E01");
|
||||||
|
}
|
||||||
|
return send_cmd_ack(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_write_memory(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
size_t s = cmd.data.find(',');
|
||||||
|
size_t s2 = cmd.data.find(':');
|
||||||
|
if ((s == std::string::npos) || (s2 == std::string::npos)) {
|
||||||
|
gdbDebugServer.warning("Malformed write memory request received: %s", cmd.data.c_str());
|
||||||
|
return send_cmd_ack("E01");
|
||||||
|
}
|
||||||
|
u32 addr = hex_to_u32(cmd.data.substr(0, s));
|
||||||
|
u32 len = hex_to_u32(cmd.data.substr(s + 1, s2 - s - 1));
|
||||||
|
const char* data_ptr = (cmd.data.c_str()) + s2 + 1;
|
||||||
|
for (u32 i = 0; i < len; ++i) {
|
||||||
|
if (vm::check_addr(addr + i)) {
|
||||||
|
u8 val;
|
||||||
|
int res = sscanf_s(data_ptr, "%02hhX", &val);
|
||||||
|
if (!res) {
|
||||||
|
gdbDebugServer.warning("Couldn't read u8 from string %s", data_ptr);
|
||||||
|
return send_cmd_ack("E02");
|
||||||
|
}
|
||||||
|
data_ptr += 2;
|
||||||
|
vm::write8(addr + i, val);
|
||||||
|
} else {
|
||||||
|
return send_cmd_ack("E03");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return send_cmd_ack("OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_read_all_registers(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
select_thread(general_ops_thread_id);
|
||||||
|
|
||||||
|
auto th = selected_thread.lock();
|
||||||
|
if (th->id_type() == 1) {
|
||||||
|
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||||
|
//68 64-bit registers, and 3 32-bit
|
||||||
|
result.reserve(68*16 + 3*8);
|
||||||
|
for (int i = 0; i < 71; ++i) {
|
||||||
|
result += get_reg(ppu, i);
|
||||||
|
}
|
||||||
|
return send_cmd_ack(result);
|
||||||
|
}
|
||||||
|
gdbDebugServer.warning("Unimplemented thread type %d", th->id_type());
|
||||||
|
return send_cmd_ack("");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_write_all_registers(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
select_thread(general_ops_thread_id);
|
||||||
|
auto th = selected_thread.lock();
|
||||||
|
if (th->id_type() == 1) {
|
||||||
|
auto ppu = std::static_pointer_cast<ppu_thread>(th);
|
||||||
|
int ptr = 0;
|
||||||
|
for (int i = 0; i < 71; ++i) {
|
||||||
|
int sz = get_reg_size(ppu, i);
|
||||||
|
set_reg(ppu, i, cmd.data.substr(ptr, sz * 2));
|
||||||
|
ptr += sz * 2;
|
||||||
|
}
|
||||||
|
return send_cmd_ack("OK");
|
||||||
|
}
|
||||||
|
gdbDebugServer.warning("Unimplemented thread type %d", th->id_type());
|
||||||
|
return send_cmd_ack("E01");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_set_thread_ops(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
char type = cmd.data[0];
|
||||||
|
std::string thread = cmd.data.substr(1);
|
||||||
|
u64 id = thread == "-1" ? ALL_THREADS : hex_to_u64(thread);
|
||||||
|
if (type == 'c') {
|
||||||
|
continue_ops_thread_id = id;
|
||||||
|
} else {
|
||||||
|
general_ops_thread_id = id;
|
||||||
|
}
|
||||||
|
if (select_thread(id)) {
|
||||||
|
return send_cmd_ack("OK");
|
||||||
|
}
|
||||||
|
gdbDebugServer.warning("Client asked to use thread %llx for %s, but no matching thread was found", id, type == 'c' ? "continue ops" : "general ops");
|
||||||
|
return send_cmd_ack("E01");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_attached_to_what(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
//creating processes from client is not available yet
|
||||||
|
return send_cmd_ack("1");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_kill(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
Emu.Stop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_continue_support(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
return send_cmd_ack("vCont;c;s;C;S");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_vcont(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
//todo: handle multiple actions and thread ids
|
||||||
|
this->from_breakpoint = false;
|
||||||
|
if (cmd.data[1] == 'c') {
|
||||||
|
select_thread(continue_ops_thread_id);
|
||||||
|
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||||
|
ppu->state -= cpu_flag::dbg_pause;
|
||||||
|
if (Emu.IsPaused()) {
|
||||||
|
Emu.Resume();
|
||||||
|
}
|
||||||
|
thread_ctrl::wait();
|
||||||
|
//we are in all-stop mode
|
||||||
|
Emu.Pause();
|
||||||
|
return send_reason();
|
||||||
|
} else if (cmd.data[1] == 's') {
|
||||||
|
select_thread(continue_ops_thread_id);
|
||||||
|
auto ppu = std::static_pointer_cast<ppu_thread>(selected_thread.lock());
|
||||||
|
ppu->state += cpu_flag::dbg_step;
|
||||||
|
ppu->state -= cpu_flag::dbg_pause;
|
||||||
|
if (Emu.IsPaused()) {
|
||||||
|
Emu.Resume();
|
||||||
|
} else {
|
||||||
|
ppu->notify();
|
||||||
|
}
|
||||||
|
thread_ctrl::wait();
|
||||||
|
//we are in all-stop mode
|
||||||
|
Emu.Pause();
|
||||||
|
return send_reason();
|
||||||
|
}
|
||||||
|
return send_cmd_ack("");
|
||||||
|
}
|
||||||
|
|
||||||
|
static const u32 INVALID_PTR = 0xffffffff;
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_set_breakpoint(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
char type = cmd.data[0];
|
||||||
|
//software breakpoint
|
||||||
|
if (type == '0') {
|
||||||
|
u32 addr = INVALID_PTR;
|
||||||
|
if (cmd.data.find(';') != std::string::npos) {
|
||||||
|
gdbDebugServer.warning("Received request to set breakpoint with condition, but they are not supported");
|
||||||
|
return send_cmd_ack("E01");
|
||||||
|
}
|
||||||
|
sscanf_s(cmd.data.c_str(), "0,%x", &addr);
|
||||||
|
if (addr == INVALID_PTR) {
|
||||||
|
gdbDebugServer.warning("Can't parse breakpoint request, data: %s", cmd.data.c_str());
|
||||||
|
return send_cmd_ack("E02");
|
||||||
|
}
|
||||||
|
ppu_set_breakpoint(addr);
|
||||||
|
return send_cmd_ack("OK");
|
||||||
|
}
|
||||||
|
//other breakpoint types are not supported
|
||||||
|
return send_cmd_ack("");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GDBDebugServer::cmd_remove_breakpoint(gdb_cmd & cmd)
|
||||||
|
{
|
||||||
|
char type = cmd.data[0];
|
||||||
|
//software breakpoint
|
||||||
|
if (type == '0') {
|
||||||
|
u32 addr = INVALID_PTR;
|
||||||
|
sscanf_s(cmd.data.c_str(), "0,%x", &addr);
|
||||||
|
if (addr == INVALID_PTR) {
|
||||||
|
gdbDebugServer.warning("Can't parse breakpoint remove request, data: %s", cmd.data.c_str());
|
||||||
|
return send_cmd_ack("E01");
|
||||||
|
}
|
||||||
|
ppu_remove_breakpoint(addr);
|
||||||
|
return send_cmd_ack("OK");
|
||||||
|
}
|
||||||
|
//other breakpoint types are not supported
|
||||||
|
return send_cmd_ack("");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PROCESS_CMD(cmds,handler) if (cmd.cmd == cmds) { if (!handler(cmd)) break; else continue; }
|
||||||
|
|
||||||
|
void GDBDebugServer::on_task()
|
||||||
|
{
|
||||||
|
sock_init();
|
||||||
|
|
||||||
|
start_server();
|
||||||
|
|
||||||
|
while (!stop) {
|
||||||
|
sockaddr_in client;
|
||||||
|
socklen_t client_len = sizeof(client);
|
||||||
|
client_socket = accept(server_socket, (struct sockaddr *) &client, &client_len);
|
||||||
|
|
||||||
|
if (client_socket == INVALID_SOCKET) {
|
||||||
|
if (check_errno_again()) {
|
||||||
|
thread_ctrl::wait_for(50);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
gdbDebugServer.error("Could not establish new connection\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//stop immediately
|
||||||
|
Emu.Pause();
|
||||||
|
|
||||||
|
try {
|
||||||
|
char hostbuf[32];
|
||||||
|
inet_ntop(client.sin_family, reinterpret_cast<void*>(&client.sin_addr), hostbuf, 32);
|
||||||
|
gdbDebugServer.success("Got connection to GDB debug server from %s:%d", hostbuf, client.sin_port);
|
||||||
|
|
||||||
|
gdb_cmd cmd;
|
||||||
|
|
||||||
|
while (!stop) {
|
||||||
|
if (!read_cmd(cmd)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
gdbDebugServer.trace("Command %s with data %s received", cmd.cmd.c_str(), cmd.data.c_str());
|
||||||
|
PROCESS_CMD("!", cmd_extended_mode);
|
||||||
|
PROCESS_CMD("?", cmd_reason);
|
||||||
|
PROCESS_CMD("qSupported", cmd_supported);
|
||||||
|
PROCESS_CMD("qfThreadInfo", cmd_thread_info);
|
||||||
|
PROCESS_CMD("qC", cmd_current_thread);
|
||||||
|
PROCESS_CMD("p", cmd_read_register);
|
||||||
|
PROCESS_CMD("P", cmd_write_register);
|
||||||
|
PROCESS_CMD("m", cmd_read_memory);
|
||||||
|
PROCESS_CMD("M", cmd_write_memory);
|
||||||
|
PROCESS_CMD("g", cmd_read_all_registers);
|
||||||
|
PROCESS_CMD("G", cmd_write_all_registers);
|
||||||
|
PROCESS_CMD("H", cmd_set_thread_ops);
|
||||||
|
PROCESS_CMD("qAttached", cmd_attached_to_what);
|
||||||
|
PROCESS_CMD("k", cmd_kill);
|
||||||
|
PROCESS_CMD("vCont?", cmd_continue_support);
|
||||||
|
PROCESS_CMD("vCont", cmd_vcont);
|
||||||
|
PROCESS_CMD("z", cmd_remove_breakpoint);
|
||||||
|
PROCESS_CMD("Z", cmd_set_breakpoint);
|
||||||
|
|
||||||
|
gdbDebugServer.trace("Unsupported command received %s", cmd.cmd.c_str());
|
||||||
|
if (!send_cmd_ack("")) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::runtime_error& e)
|
||||||
|
{
|
||||||
|
if (client_socket) {
|
||||||
|
closesocket(client_socket);
|
||||||
|
client_socket = 0;
|
||||||
|
}
|
||||||
|
gdbDebugServer.error(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef PROCESS_CMD
|
||||||
|
|
||||||
|
void GDBDebugServer::on_exit()
|
||||||
|
{
|
||||||
|
if (server_socket) {
|
||||||
|
closesocket(server_socket);
|
||||||
|
}
|
||||||
|
if (client_socket) {
|
||||||
|
closesocket(client_socket);
|
||||||
|
}
|
||||||
|
sock_quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GDBDebugServer::get_name() const
|
||||||
|
{
|
||||||
|
return "GDBDebugger";
|
||||||
|
}
|
||||||
|
|
||||||
|
void GDBDebugServer::on_stop()
|
||||||
|
{
|
||||||
|
this->stop = true;
|
||||||
|
//just in case we are waiting for breakpoint
|
||||||
|
this->notify();
|
||||||
|
named_thread::on_stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 g_gdb_debugger_id = 0;
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#undef sscanf_s
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#undef HEX_U32
|
||||||
|
#undef HEX_U64
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef WITH_GDB_DEBUGGER
|
||||||
|
|
||||||
|
#include "Thread.h"
|
||||||
|
#include <Emu/IdManager.h>
|
||||||
|
#include "Emu/CPU/CPUThread.h"
|
||||||
|
#include "Emu/Cell/PPUThread.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <winsock2.h>
|
||||||
|
#include <WS2tcpip.h>
|
||||||
|
#else
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
using socket_t = SOCKET;
|
||||||
|
#else
|
||||||
|
using socket_t = int;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
typedef struct gdb_cmd {
|
||||||
|
std::string cmd;
|
||||||
|
std::string data;
|
||||||
|
u8 checksum;
|
||||||
|
} gdb_cmd;
|
||||||
|
|
||||||
|
class wrong_checksum_exception : public std::runtime_error {
|
||||||
|
public:
|
||||||
|
wrong_checksum_exception(char const* const message) : runtime_error(message) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
const u64 ALL_THREADS = 0xffffffffffffffff;
|
||||||
|
const u64 ANY_THREAD = 0;
|
||||||
|
|
||||||
|
class GDBDebugServer : public named_thread {
|
||||||
|
|
||||||
|
socket_t server_socket;
|
||||||
|
socket_t client_socket;
|
||||||
|
std::weak_ptr<cpu_thread> selected_thread;
|
||||||
|
u64 continue_ops_thread_id = ANY_THREAD;
|
||||||
|
u64 general_ops_thread_id = ANY_THREAD;
|
||||||
|
|
||||||
|
//initialize server socket and start listening
|
||||||
|
void start_server();
|
||||||
|
//read at most cnt bytes to buf, returns nubmer of bytes actually read
|
||||||
|
int read(void* buf, int cnt);
|
||||||
|
//reads one character
|
||||||
|
char read_char();
|
||||||
|
//reads pairs of hex characters and returns their integer value
|
||||||
|
u8 read_hexbyte();
|
||||||
|
//tries to read command, throws exceptions if anything goes wrong
|
||||||
|
void try_read_cmd(gdb_cmd& out_cmd);
|
||||||
|
//reads commands until receiveing one with valid checksum
|
||||||
|
//in case of other exception (i.e. wrong first char of command)
|
||||||
|
//it will log exception text and return false
|
||||||
|
//in that case best for caller would be to stop reading, because
|
||||||
|
//chance of getting correct command is low
|
||||||
|
bool read_cmd(gdb_cmd& out_cmd);
|
||||||
|
//send cnt bytes from buf to client
|
||||||
|
void send(const char* buf, int cnt);
|
||||||
|
//send character to client
|
||||||
|
void send_char(char c);
|
||||||
|
//acknowledge packet, either as accepted or declined
|
||||||
|
void ack(bool accepted);
|
||||||
|
//sends command body cmd to client
|
||||||
|
void send_cmd(const std::string & cmd);
|
||||||
|
//sends command to client until receives positive acknowledgement
|
||||||
|
//returns false in case some error happened, and command wasn't sent
|
||||||
|
bool send_cmd_ack(const std::string & cmd);
|
||||||
|
//appends encoded char c to string str, and returns checksum. encoded byte can occupy 2 bytes
|
||||||
|
static u8 append_encoded_char(char c, std::string& str);
|
||||||
|
//convert u8 to 2 byte hexademical representation
|
||||||
|
static std::string to_hexbyte(u8 i);
|
||||||
|
//choose thread, support ALL_THREADS and ANY_THREAD values, returns true if some thread was selected
|
||||||
|
bool select_thread(u64 id);
|
||||||
|
//returns register value as hex string by register id (in gdb), in case of wrong id returns empty string
|
||||||
|
static std::string get_reg(std::shared_ptr<ppu_thread> thread, u32 rid);
|
||||||
|
//sets register value to hex string by register id (in gdb), in case of wrong id returns false
|
||||||
|
static bool set_reg(std::shared_ptr<ppu_thread> thread, u32 rid, std::string value);
|
||||||
|
//returns size of register with id rid in bytes, zero if invalid rid is provided
|
||||||
|
static u32 get_reg_size(std::shared_ptr<ppu_thread> thread, u32 rid);
|
||||||
|
//send reason of stop, returns false if sending response failed
|
||||||
|
bool send_reason();
|
||||||
|
|
||||||
|
//commands
|
||||||
|
bool cmd_extended_mode(gdb_cmd& cmd);
|
||||||
|
bool cmd_reason(gdb_cmd& cmd);
|
||||||
|
bool cmd_supported(gdb_cmd& cmd);
|
||||||
|
bool cmd_thread_info(gdb_cmd& cmd);
|
||||||
|
bool cmd_current_thread(gdb_cmd& cmd);
|
||||||
|
bool cmd_read_register(gdb_cmd& cmd);
|
||||||
|
bool cmd_write_register(gdb_cmd& cmd);
|
||||||
|
bool cmd_read_memory(gdb_cmd& cmd);
|
||||||
|
bool cmd_write_memory(gdb_cmd& cmd);
|
||||||
|
bool cmd_read_all_registers(gdb_cmd& cmd);
|
||||||
|
bool cmd_write_all_registers(gdb_cmd& cmd);
|
||||||
|
bool cmd_set_thread_ops(gdb_cmd& cmd);
|
||||||
|
bool cmd_attached_to_what(gdb_cmd& cmd);
|
||||||
|
bool cmd_kill(gdb_cmd& cmd);
|
||||||
|
bool cmd_continue_support(gdb_cmd& cmd);
|
||||||
|
bool cmd_vcont(gdb_cmd& cmd);
|
||||||
|
bool cmd_set_breakpoint(gdb_cmd& cmd);
|
||||||
|
bool cmd_remove_breakpoint(gdb_cmd& cmd);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void on_task() override final;
|
||||||
|
void on_exit() override final;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static const u32 id_base = 1;
|
||||||
|
static const u32 id_step = 1;
|
||||||
|
static const u32 id_count = 0x100000;
|
||||||
|
|
||||||
|
bool from_breakpoint = true;
|
||||||
|
bool stop = false;
|
||||||
|
|
||||||
|
virtual std::string get_name() const;
|
||||||
|
virtual void on_stop() override final;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern u32 g_gdb_debugger_id;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -5,11 +5,15 @@
|
|||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "StrFmt.h"
|
#include "StrFmt.h"
|
||||||
#include "File.h"
|
#include "File.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
#include "sysinfo.h"
|
||||||
|
#include "VirtualMemory.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(push, 0)
|
#pragma warning(push, 0)
|
||||||
@@ -19,6 +23,7 @@
|
|||||||
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
#include "llvm/ExecutionEngine/ExecutionEngine.h"
|
||||||
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
|
#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
|
||||||
#include "llvm/ExecutionEngine/JITEventListener.h"
|
#include "llvm/ExecutionEngine/JITEventListener.h"
|
||||||
|
#include "llvm/ExecutionEngine/ObjectCache.h"
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#pragma warning(pop)
|
#pragma warning(pop)
|
||||||
#endif
|
#endif
|
||||||
@@ -27,16 +32,12 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#else
|
#else
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "JIT.h"
|
#include "JIT.h"
|
||||||
|
|
||||||
// Global LLVM context (thread-unsafe)
|
// Memory manager mutex
|
||||||
llvm::LLVMContext g_llvm_ctx;
|
shared_mutex s_mutex;
|
||||||
|
|
||||||
// Size of virtual memory area reserved: 512 MB
|
// Size of virtual memory area reserved: 512 MB
|
||||||
static const u64 s_memory_size = 0x20000000;
|
static const u64 s_memory_size = 0x20000000;
|
||||||
@@ -44,39 +45,91 @@ static const u64 s_memory_size = 0x20000000;
|
|||||||
// Try to reserve a portion of virtual memory in the first 2 GB address space beforehand, if possible.
|
// Try to reserve a portion of virtual memory in the first 2 GB address space beforehand, if possible.
|
||||||
static void* const s_memory = []() -> void*
|
static void* const s_memory = []() -> void*
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
llvm::InitializeNativeTarget();
|
||||||
for (u64 addr = 0x10000000; addr <= 0x60000000; addr += 0x1000000)
|
llvm::InitializeNativeTargetAsmPrinter();
|
||||||
|
LLVMLinkInMCJIT();
|
||||||
|
|
||||||
|
#ifdef MAP_32BIT
|
||||||
|
auto ptr = ::mmap(nullptr, s_memory_size, PROT_NONE, MAP_ANON | MAP_PRIVATE | MAP_32BIT, -1, 0);
|
||||||
|
if (ptr != MAP_FAILED)
|
||||||
|
return ptr;
|
||||||
|
#else
|
||||||
|
for (u64 addr = 0x10000000; addr <= 0x80000000 - s_memory_size; addr += 0x1000000)
|
||||||
{
|
{
|
||||||
if (VirtualAlloc((void*)addr, s_memory_size, MEM_RESERVE, PAGE_NOACCESS))
|
if (auto ptr = utils::memory_reserve(s_memory_size, (void*)addr))
|
||||||
{
|
{
|
||||||
return (void*)addr;
|
return ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return utils::memory_reserve(s_memory_size);
|
||||||
|
}();
|
||||||
|
|
||||||
|
static void* s_next = s_memory;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
static std::deque<std::vector<RUNTIME_FUNCTION>> s_unwater;
|
||||||
|
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
|
||||||
|
#else
|
||||||
|
static std::deque<std::tuple<u8*, u64, std::size_t>> s_unfire;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Reset memory manager
|
||||||
|
extern void jit_finalize()
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
for (auto&& unwind : s_unwind)
|
||||||
|
{
|
||||||
|
if (!RtlDeleteFunctionTable(unwind.data()))
|
||||||
|
{
|
||||||
|
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable() failed! Error %u", GetLastError());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return VirtualAlloc(NULL, s_memory_size, MEM_RESERVE, PAGE_NOACCESS);
|
s_unwind.clear();
|
||||||
#else
|
#else
|
||||||
return ::mmap((void*)0x10000000, s_memory_size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
struct MemoryManager : llvm::RTDyldMemoryManager
|
||||||
|
{
|
||||||
|
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool finalizeMemory(std::string* = nullptr) override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} mem;
|
||||||
|
|
||||||
|
for (auto&& t : s_unfire)
|
||||||
|
{
|
||||||
|
mem.deregisterEHFrames(std::get<0>(t), std::get<1>(t), std::get<2>(t));
|
||||||
|
}
|
||||||
|
|
||||||
|
s_unfire.clear();
|
||||||
#endif
|
#endif
|
||||||
}();
|
|
||||||
|
|
||||||
// Code section
|
utils::memory_decommit(s_memory, s_memory_size);
|
||||||
static u8* s_code_addr;
|
|
||||||
static u64 s_code_size;
|
|
||||||
|
|
||||||
// EH frames
|
s_next = s_memory;
|
||||||
static u8* s_unwind_info;
|
}
|
||||||
static u64 s_unwind_size;
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
static std::vector<std::vector<RUNTIME_FUNCTION>> s_unwind; // .pdata
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Helper class
|
// Helper class
|
||||||
struct MemoryManager final : llvm::RTDyldMemoryManager
|
struct MemoryManager : llvm::RTDyldMemoryManager
|
||||||
{
|
{
|
||||||
std::unordered_map<std::string, std::uintptr_t>& m_link;
|
std::unordered_map<std::string, u64>& m_link;
|
||||||
|
|
||||||
MemoryManager(std::unordered_map<std::string, std::uintptr_t>& table)
|
std::array<u8, 16>* m_tramps{};
|
||||||
|
|
||||||
|
u8* m_code_addr{}; // TODO
|
||||||
|
|
||||||
|
MemoryManager(std::unordered_map<std::string, u64>& table)
|
||||||
: m_link(table)
|
: m_link(table)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -86,31 +139,70 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
|||||||
fmt::throw_exception("Null function" HERE);
|
fmt::throw_exception("Null function" HERE);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual u64 getSymbolAddress(const std::string& name) override
|
llvm::JITSymbol findSymbol(const std::string& name) override
|
||||||
{
|
{
|
||||||
const auto found = m_link.find(name);
|
auto& addr = m_link[name];
|
||||||
|
|
||||||
if (found != m_link.end())
|
// Find function address
|
||||||
|
if (!addr)
|
||||||
{
|
{
|
||||||
return found->second;
|
addr = RTDyldMemoryManager::getSymbolAddress(name);
|
||||||
|
|
||||||
|
if (addr)
|
||||||
|
{
|
||||||
|
LOG_WARNING(GENERAL, "LLVM: Symbol requested: %s -> 0x%016llx", name, addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_ERROR(GENERAL, "LLVM: Linkage failed: %s", name);
|
||||||
|
addr = (u64)null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (u64 addr = RTDyldMemoryManager::getSymbolAddress(name))
|
// Verify address for small code model
|
||||||
|
if ((u64)s_memory > 0x80000000 - s_memory_size ? (u64)addr - (u64)s_memory >= s_memory_size : addr >= 0x80000000)
|
||||||
{
|
{
|
||||||
// This may be bad if LLVM requests some built-in functions like fma.
|
// Lock memory manager
|
||||||
LOG_ERROR(GENERAL, "LLVM: Symbol requested: %s -> 0x%016llx", name, addr);
|
writer_lock lock(s_mutex);
|
||||||
return addr;
|
|
||||||
|
// Allocate memory for trampolines
|
||||||
|
if (!m_tramps)
|
||||||
|
{
|
||||||
|
m_tramps = reinterpret_cast<decltype(m_tramps)>(s_next);
|
||||||
|
utils::memory_commit(s_next, 4096, utils::protection::wx);
|
||||||
|
s_next = (u8*)((u64)s_next + 4096);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a trampoline
|
||||||
|
auto& data = *m_tramps++;
|
||||||
|
data[0x0] = 0xff; // JMP [rip+2]
|
||||||
|
data[0x1] = 0x25;
|
||||||
|
data[0x2] = 0x02;
|
||||||
|
data[0x3] = 0x00;
|
||||||
|
data[0x4] = 0x00;
|
||||||
|
data[0x5] = 0x00;
|
||||||
|
data[0x6] = 0x48; // MOV rax, imm64 (not executed)
|
||||||
|
data[0x7] = 0xb8;
|
||||||
|
std::memcpy(data.data() + 8, &addr, 8);
|
||||||
|
addr = (u64)&data;
|
||||||
|
|
||||||
|
// Reset pointer (memory page exhausted)
|
||||||
|
if (((u64)m_tramps % 4096) == 0)
|
||||||
|
{
|
||||||
|
m_tramps = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's fine if some function is never called, for example.
|
return {addr, llvm::JITSymbolFlags::Exported};
|
||||||
LOG_ERROR(GENERAL, "LLVM: Symbol not found: %s", name);
|
|
||||||
return (u64)null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
u8* allocateCodeSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name) override
|
||||||
{
|
{
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
// Simple allocation
|
// Simple allocation
|
||||||
const u64 next = ::align((u64)m_next + size, 4096);
|
const u64 next = ::align((u64)s_next + size, 4096);
|
||||||
|
|
||||||
if (next > (u64)s_memory + s_memory_size)
|
if (next > (u64)s_memory + s_memory_size)
|
||||||
{
|
{
|
||||||
@@ -118,27 +210,20 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
utils::memory_commit(s_next, size, utils::protection::wx);
|
||||||
if (!VirtualAlloc(m_next, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE))
|
m_code_addr = (u8*)s_next;
|
||||||
#else
|
|
||||||
if (::mprotect(m_next, size, PROT_READ | PROT_WRITE | PROT_EXEC))
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
LOG_FATAL(GENERAL, "LLVM: Failed to allocate memory at %p", m_next);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
s_code_addr = (u8*)m_next;
|
LOG_NOTICE(GENERAL, "LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), s_next, size, align);
|
||||||
s_code_size = size;
|
return (u8*)std::exchange(s_next, (void*)next);
|
||||||
|
|
||||||
LOG_NOTICE(GENERAL, "LLVM: Code section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x)", sec_id, sec_name.data(), m_next, size, align);
|
|
||||||
return (u8*)std::exchange(m_next, (void*)next);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
u8* allocateDataSection(std::uintptr_t size, uint align, uint sec_id, llvm::StringRef sec_name, bool is_ro) override
|
||||||
{
|
{
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
// Simple allocation
|
// Simple allocation
|
||||||
const u64 next = ::align((u64)m_next + size, 4096);
|
const u64 next = ::align((u64)s_next + size, 4096);
|
||||||
|
|
||||||
if (next > (u64)s_memory + s_memory_size)
|
if (next > (u64)s_memory + s_memory_size)
|
||||||
{
|
{
|
||||||
@@ -151,22 +236,17 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
|||||||
LOG_ERROR(GENERAL, "LLVM: Writeable data section not supported!");
|
LOG_ERROR(GENERAL, "LLVM: Writeable data section not supported!");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
utils::memory_commit(s_next, size);
|
||||||
if (!VirtualAlloc(m_next, size, MEM_COMMIT, PAGE_READWRITE))
|
|
||||||
#else
|
|
||||||
if (::mprotect(m_next, size, PROT_READ | PROT_WRITE))
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
LOG_FATAL(GENERAL, "LLVM: Failed to allocate memory at %p", m_next);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_NOTICE(GENERAL, "LLVM: Data section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x, %s)", sec_id, sec_name.data(), m_next, size, align, is_ro ? "ro" : "rw");
|
LOG_NOTICE(GENERAL, "LLVM: Data section %u '%s' allocated -> %p (size=0x%llx, aligned 0x%x, %s)", sec_id, sec_name.data(), s_next, size, align, is_ro ? "ro" : "rw");
|
||||||
return (u8*)std::exchange(m_next, (void*)next);
|
return (u8*)std::exchange(s_next, (void*)next);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool finalizeMemory(std::string* = nullptr) override
|
bool finalizeMemory(std::string* = nullptr) override
|
||||||
{
|
{
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
// TODO: make only read-only sections read-only
|
// TODO: make only read-only sections read-only
|
||||||
//#ifdef _WIN32
|
//#ifdef _WIN32
|
||||||
// DWORD op;
|
// DWORD op;
|
||||||
@@ -179,232 +259,274 @@ struct MemoryManager final : llvm::RTDyldMemoryManager
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
void registerEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
||||||
{
|
{
|
||||||
s_unwind_info = addr;
|
#ifdef _WIN32
|
||||||
s_unwind_size = size;
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
|
// Use s_memory as a BASE, compute the difference
|
||||||
|
const u64 unwind_diff = (u64)addr - (u64)s_memory;
|
||||||
|
|
||||||
|
// Fix RUNTIME_FUNCTION records (.pdata section)
|
||||||
|
auto pdata = std::move(s_unwater.front());
|
||||||
|
s_unwater.pop_front();
|
||||||
|
|
||||||
|
for (auto& rf : pdata)
|
||||||
|
{
|
||||||
|
rf.UnwindData += static_cast<DWORD>(unwind_diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register .xdata UNWIND_INFO structs
|
||||||
|
if (!RtlAddFunctionTable(pdata.data(), (DWORD)pdata.size(), (u64)s_memory))
|
||||||
|
{
|
||||||
|
LOG_ERROR(GENERAL, "RtlAddFunctionTable() failed! Error %u", GetLastError());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
s_unwind.emplace_back(std::move(pdata));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
s_unfire.push_front(std::make_tuple(addr, load_addr, size));
|
||||||
|
#endif
|
||||||
|
|
||||||
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
|
return RTDyldMemoryManager::registerEHFrames(addr, load_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual void deregisterEHFrames(u8* addr, u64 load_addr, std::size_t size) override
|
virtual void deregisterEHFrames() override
|
||||||
{
|
{
|
||||||
LOG_ERROR(GENERAL, "deregisterEHFrames() called"); // Not expected
|
|
||||||
|
|
||||||
return RTDyldMemoryManager::deregisterEHFrames(addr, load_addr, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~MemoryManager()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
for (auto&& unwind : s_unwind)
|
|
||||||
{
|
|
||||||
if (!RtlDeleteFunctionTable(unwind.data()))
|
|
||||||
{
|
|
||||||
LOG_FATAL(GENERAL, "RtlDeleteFunctionTable() failed! Error %u", GetLastError());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s_unwind.clear();
|
|
||||||
|
|
||||||
if (!VirtualFree(s_memory, 0, MEM_DECOMMIT))
|
|
||||||
{
|
|
||||||
LOG_FATAL(GENERAL, "VirtualFree(%p) failed! Error %u", s_memory, GetLastError());
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (!::mmap(s_memory, s_memory_size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0))
|
|
||||||
{
|
|
||||||
LOG_FATAL(GENERAL, "mmap(%p) failed! Error %d", s_memory, errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: unregister EH frames if necessary
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void* m_next = s_memory;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Helper class
|
// Helper class
|
||||||
struct EventListener final : llvm::JITEventListener
|
struct EventListener : llvm::JITEventListener
|
||||||
{
|
{
|
||||||
std::string path;
|
MemoryManager& m_mem;
|
||||||
|
|
||||||
virtual void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
EventListener(MemoryManager& mem)
|
||||||
|
: m_mem(mem)
|
||||||
{
|
{
|
||||||
if (!path.empty())
|
}
|
||||||
|
|
||||||
|
void NotifyObjectEmitted(const llvm::object::ObjectFile& obj, const llvm::RuntimeDyld::LoadedObjectInfo& inf) override
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
for (auto it = obj.section_begin(), end = obj.section_end(); it != end; ++it)
|
||||||
{
|
{
|
||||||
const llvm::StringRef elf = obj.getData();
|
llvm::StringRef name;
|
||||||
fs::file(path, fs::rewrite).write(elf.data(), elf.size());
|
it->getName(name);
|
||||||
|
|
||||||
|
if (name == ".pdata")
|
||||||
|
{
|
||||||
|
llvm::StringRef data;
|
||||||
|
it->getContents(data);
|
||||||
|
|
||||||
|
std::vector<RUNTIME_FUNCTION> rfs(data.size() / sizeof(RUNTIME_FUNCTION));
|
||||||
|
|
||||||
|
auto offsets = reinterpret_cast<DWORD*>(rfs.data());
|
||||||
|
|
||||||
|
// Initialize .pdata section using relocation info
|
||||||
|
for (auto ri = it->relocation_begin(), end = it->relocation_end(); ri != end; ++ri)
|
||||||
|
{
|
||||||
|
if (ri->getType() == 3 /*R_X86_64_GOT32*/)
|
||||||
|
{
|
||||||
|
const u64 value = *reinterpret_cast<const DWORD*>(data.data() + ri->getOffset());
|
||||||
|
offsets[ri->getOffset() / sizeof(DWORD)] = static_cast<DWORD>(value + ri->getSymbol()->getAddress().get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
|
// Use s_memory as a BASE, compute the difference
|
||||||
|
const u64 code_diff = (u64)m_mem.m_code_addr - (u64)s_memory;
|
||||||
|
|
||||||
|
// Fix RUNTIME_FUNCTION records (.pdata section)
|
||||||
|
for (auto& rf : rfs)
|
||||||
|
{
|
||||||
|
rf.BeginAddress += static_cast<DWORD>(code_diff);
|
||||||
|
rf.EndAddress += static_cast<DWORD>(code_diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
s_unwater.emplace_back(std::move(rfs));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static EventListener s_listener;
|
// Helper class
|
||||||
|
class ObjectCache final : public llvm::ObjectCache
|
||||||
static void dummy()
|
|
||||||
{
|
{
|
||||||
}
|
const std::string& m_path;
|
||||||
|
|
||||||
jit_compiler::jit_compiler(std::unordered_map<std::string, std::uintptr_t> init_linkage_info)
|
public:
|
||||||
: m_link(std::move(init_linkage_info))
|
ObjectCache(const std::string& path)
|
||||||
{
|
: m_path(path)
|
||||||
#ifdef _MSC_VER
|
|
||||||
m_link.emplace("__chkstk", (u64)&dummy);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
verify(HERE), s_memory;
|
|
||||||
|
|
||||||
// Initialization
|
|
||||||
llvm::InitializeNativeTarget();
|
|
||||||
llvm::InitializeNativeTargetAsmPrinter();
|
|
||||||
LLVMLinkInMCJIT();
|
|
||||||
m_cpu = llvm::sys::getHostCPUName();
|
|
||||||
|
|
||||||
if (m_cpu == "skylake")
|
|
||||||
{
|
{
|
||||||
m_cpu = "haswell";
|
}
|
||||||
|
|
||||||
|
~ObjectCache() override = default;
|
||||||
|
|
||||||
|
void notifyObjectCompiled(const llvm::Module* module, llvm::MemoryBufferRef obj) override
|
||||||
|
{
|
||||||
|
std::string name = m_path;
|
||||||
|
name.append(module->getName());
|
||||||
|
fs::file(name, fs::rewrite).write(obj.getBufferStart(), obj.getBufferSize());
|
||||||
|
LOG_SUCCESS(GENERAL, "LLVM: Created module: %s", module->getName().data());
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<llvm::WritableMemoryBuffer> load(const std::string& path)
|
||||||
|
{
|
||||||
|
if (fs::file cached{path, fs::read})
|
||||||
|
{
|
||||||
|
auto buf = llvm::WritableMemoryBuffer::getNewUninitMemBuffer(cached.size());
|
||||||
|
cached.read(const_cast<char*>(buf->getBufferStart()), buf->getBufferSize());
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<llvm::MemoryBuffer> getObject(const llvm::Module* module) override
|
||||||
|
{
|
||||||
|
std::string path = m_path;
|
||||||
|
path.append(module->getName());
|
||||||
|
|
||||||
|
if (auto buf = load(path))
|
||||||
|
{
|
||||||
|
LOG_SUCCESS(GENERAL, "LLVM: Loaded module: %s", module->getName().data());
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
jit_compiler::jit_compiler(const std::unordered_map<std::string, u64>& _link, std::string _cpu)
|
||||||
|
: m_link(_link)
|
||||||
|
, m_cpu(std::move(_cpu))
|
||||||
|
{
|
||||||
|
if (m_cpu.empty())
|
||||||
|
{
|
||||||
|
m_cpu = llvm::sys::getHostCPUName();
|
||||||
|
|
||||||
|
if (m_cpu == "sandybridge" ||
|
||||||
|
m_cpu == "ivybridge" ||
|
||||||
|
m_cpu == "haswell" ||
|
||||||
|
m_cpu == "broadwell" ||
|
||||||
|
m_cpu == "skylake" ||
|
||||||
|
m_cpu == "skylake-avx512" ||
|
||||||
|
m_cpu == "cannonlake")
|
||||||
|
{
|
||||||
|
if (!utils::has_avx())
|
||||||
|
{
|
||||||
|
m_cpu = "nehalem";
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string result;
|
std::string result;
|
||||||
|
|
||||||
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("", g_llvm_ctx))
|
if (m_link.empty())
|
||||||
.setErrorStr(&result)
|
{
|
||||||
.setMCJITMemoryManager(std::make_unique<MemoryManager>(m_link))
|
// Auxiliary JIT (does not use custom memory manager, only writes the objects)
|
||||||
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null_", m_context))
|
||||||
.setCodeModel((u64)s_memory <= 0x60000000 ? llvm::CodeModel::Small : llvm::CodeModel::Large) // TODO
|
.setErrorStr(&result)
|
||||||
.setMCPU(m_cpu)
|
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||||
.create());
|
.setCodeModel(llvm::CodeModel::Small)
|
||||||
|
.setMCPU(m_cpu)
|
||||||
|
.create());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Primary JIT
|
||||||
|
auto mem = std::make_unique<MemoryManager>(m_link);
|
||||||
|
m_jit_el = std::make_unique<EventListener>(*mem);
|
||||||
|
|
||||||
|
m_engine.reset(llvm::EngineBuilder(std::make_unique<llvm::Module>("null", m_context))
|
||||||
|
.setErrorStr(&result)
|
||||||
|
.setMCJITMemoryManager(std::move(mem))
|
||||||
|
.setOptLevel(llvm::CodeGenOpt::Aggressive)
|
||||||
|
.setCodeModel(llvm::CodeModel::Small)
|
||||||
|
.setMCPU(m_cpu)
|
||||||
|
.create());
|
||||||
|
|
||||||
|
if (m_engine)
|
||||||
|
{
|
||||||
|
m_engine->RegisterJITEventListener(m_jit_el.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!m_engine)
|
if (!m_engine)
|
||||||
{
|
{
|
||||||
fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
|
fmt::throw_exception("LLVM: Failed to create ExecutionEngine: %s", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_engine->setProcessAllSections(true); // ???
|
|
||||||
m_engine->RegisterJITEventListener(&s_listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void jit_compiler::load(std::unique_ptr<llvm::Module> module, std::unique_ptr<llvm::object::ObjectFile> object)
|
|
||||||
{
|
|
||||||
s_listener.path.clear();
|
|
||||||
|
|
||||||
auto* module_ptr = module.get();
|
|
||||||
|
|
||||||
m_engine->addModule(std::move(module));
|
|
||||||
m_engine->addObjectFile(std::move(object));
|
|
||||||
m_engine->finalizeObject();
|
|
||||||
|
|
||||||
m_map.clear();
|
|
||||||
|
|
||||||
for (auto& func : module_ptr->functions())
|
|
||||||
{
|
|
||||||
const std::string& name = func.getName();
|
|
||||||
|
|
||||||
if (!m_link.count(name))
|
|
||||||
{
|
|
||||||
// Register compiled function
|
|
||||||
m_map[name] = m_engine->getFunctionAddress(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void jit_compiler::make(std::unique_ptr<llvm::Module> module, std::string path)
|
|
||||||
{
|
|
||||||
s_listener.path = std::move(path);
|
|
||||||
|
|
||||||
auto* module_ptr = module.get();
|
|
||||||
|
|
||||||
m_engine->addModule(std::move(module));
|
|
||||||
m_engine->finalizeObject();
|
|
||||||
|
|
||||||
m_map.clear();
|
|
||||||
|
|
||||||
for (auto& func : module_ptr->functions())
|
|
||||||
{
|
|
||||||
if (!func.empty())
|
|
||||||
{
|
|
||||||
const std::string& name = func.getName();
|
|
||||||
|
|
||||||
// Register compiled function
|
|
||||||
m_map[name] = m_engine->getFunctionAddress(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete IR to lower memory consumption
|
|
||||||
func.deleteBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
void jit_compiler::init()
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
// Register .xdata UNWIND_INFO (.pdata section is empty for some reason)
|
|
||||||
std::set<u64> func_set;
|
|
||||||
|
|
||||||
for (const auto& pair : m_map)
|
|
||||||
{
|
|
||||||
func_set.emplace(pair.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
const u64 base = (u64)s_memory;
|
|
||||||
const u8* bits = s_unwind_info;
|
|
||||||
|
|
||||||
std::vector<RUNTIME_FUNCTION> unwind;
|
|
||||||
unwind.reserve(m_map.size());
|
|
||||||
|
|
||||||
for (const u64 addr : func_set)
|
|
||||||
{
|
|
||||||
// Find next function address
|
|
||||||
const auto _next = func_set.upper_bound(addr);
|
|
||||||
const u64 next = _next != func_set.end() ? *_next : (u64)s_code_addr + s_code_size;
|
|
||||||
|
|
||||||
// Generate RUNTIME_FUNCTION record
|
|
||||||
RUNTIME_FUNCTION uw;
|
|
||||||
uw.BeginAddress = static_cast<u32>(addr - base);
|
|
||||||
uw.EndAddress = static_cast<u32>(next - base);
|
|
||||||
uw.UnwindData = static_cast<u32>((u64)bits - base);
|
|
||||||
unwind.emplace_back(uw);
|
|
||||||
|
|
||||||
// Parse .xdata UNWIND_INFO record
|
|
||||||
const u8 flags = *bits++; // Version and flags
|
|
||||||
const u8 prolog = *bits++; // Size of prolog
|
|
||||||
const u8 count = *bits++; // Count of unwind codes
|
|
||||||
const u8 frame = *bits++; // Frame Reg + Off
|
|
||||||
bits += ::align(std::max<u8>(1, count), 2) * sizeof(u16); // UNWIND_CODE array
|
|
||||||
|
|
||||||
if (flags != 1)
|
|
||||||
{
|
|
||||||
// Can't happen for trivial code
|
|
||||||
LOG_ERROR(GENERAL, "LLVM: unsupported UNWIND_INFO version/flags (0x%02x)", flags);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_TRACE(GENERAL, "LLVM: .xdata at 0x%llx: function 0x%x..0x%x: p0x%02x, c0x%02x, f0x%02x", uw.UnwindData + base, uw.BeginAddress + base, uw.EndAddress + base, prolog, count, frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s_unwind_info + s_unwind_size != bits)
|
|
||||||
{
|
|
||||||
LOG_ERROR(GENERAL, "LLVM: .xdata analysis failed! (%p != %p)", s_unwind_info + s_unwind_size, bits);
|
|
||||||
}
|
|
||||||
else if (!RtlAddFunctionTable(unwind.data(), (DWORD)unwind.size(), base))
|
|
||||||
{
|
|
||||||
LOG_ERROR(GENERAL, "RtlAddFunctionTable(%p) failed! Error %u", s_unwind_info, GetLastError());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG_NOTICE(GENERAL, "LLVM: UNWIND_INFO registered (%p, size=0x%llx)", s_unwind_info, s_unwind_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
s_unwind.emplace_back(std::move(unwind));
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
jit_compiler::~jit_compiler()
|
jit_compiler::~jit_compiler()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void jit_compiler::add(std::unique_ptr<llvm::Module> module, const std::string& path)
|
||||||
|
{
|
||||||
|
ObjectCache cache{path};
|
||||||
|
m_engine->setObjectCache(&cache);
|
||||||
|
|
||||||
|
const auto ptr = module.get();
|
||||||
|
m_engine->addModule(std::move(module));
|
||||||
|
m_engine->generateCodeForModule(ptr);
|
||||||
|
m_engine->setObjectCache(nullptr);
|
||||||
|
|
||||||
|
for (auto& func : ptr->functions())
|
||||||
|
{
|
||||||
|
// Delete IR to lower memory consumption
|
||||||
|
func.deleteBody();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void jit_compiler::add(const std::string& path)
|
||||||
|
{
|
||||||
|
m_engine->addObjectFile(std::move(llvm::object::ObjectFile::createObjectFile(*ObjectCache::load(path)).get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void jit_compiler::fin()
|
||||||
|
{
|
||||||
|
m_engine->finalizeObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 jit_compiler::get(const std::string& name)
|
||||||
|
{
|
||||||
|
return m_engine->getGlobalValueAddress(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, u64> jit_compiler::add(std::unordered_map<std::string, std::string> data)
|
||||||
|
{
|
||||||
|
// Lock memory manager
|
||||||
|
writer_lock lock(s_mutex);
|
||||||
|
|
||||||
|
std::unordered_map<std::string, u64> result;
|
||||||
|
|
||||||
|
std::size_t size = 0;
|
||||||
|
|
||||||
|
for (auto&& pair : data)
|
||||||
|
{
|
||||||
|
size += ::align(pair.second.size(), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
utils::memory_commit(s_next, size, utils::protection::wx);
|
||||||
|
std::memset(s_next, 0xc3, ::align(size, 4096));
|
||||||
|
|
||||||
|
for (auto&& pair : data)
|
||||||
|
{
|
||||||
|
std::memcpy(s_next, pair.second.data(), pair.second.size());
|
||||||
|
result.emplace(pair.first, (u64)s_next);
|
||||||
|
s_next = (void*)::align((u64)s_next + pair.second.size(), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
s_next = (void*)::align((u64)s_next, 4096);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
#include "restore_new.h"
|
#include "restore_new.h"
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@@ -20,54 +21,60 @@
|
|||||||
#endif
|
#endif
|
||||||
#include "define_new_memleakdetect.h"
|
#include "define_new_memleakdetect.h"
|
||||||
|
|
||||||
extern llvm::LLVMContext g_llvm_ctx;
|
|
||||||
|
|
||||||
// Temporary compiler interface
|
// Temporary compiler interface
|
||||||
class jit_compiler final
|
class jit_compiler final
|
||||||
{
|
{
|
||||||
|
// Local LLVM context
|
||||||
|
llvm::LLVMContext m_context;
|
||||||
|
|
||||||
|
// JIT Event Listener
|
||||||
|
std::unique_ptr<struct EventListener> m_jit_el;
|
||||||
|
|
||||||
// Execution instance
|
// Execution instance
|
||||||
std::unique_ptr<llvm::ExecutionEngine> m_engine;
|
std::unique_ptr<llvm::ExecutionEngine> m_engine;
|
||||||
|
|
||||||
// Compiled functions
|
// Link table
|
||||||
std::unordered_map<std::string, std::uintptr_t> m_map;
|
std::unordered_map<std::string, u64> m_link;
|
||||||
|
|
||||||
// Linkage cache
|
|
||||||
std::unordered_map<std::string, std::uintptr_t> m_link;
|
|
||||||
|
|
||||||
// Arch
|
// Arch
|
||||||
std::string m_cpu;
|
std::string m_cpu;
|
||||||
|
|
||||||
// Internal
|
|
||||||
void init();
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
jit_compiler(std::unordered_map<std::string, std::uintptr_t>);
|
jit_compiler(const std::unordered_map<std::string, u64>& _link, std::string _cpu);
|
||||||
~jit_compiler();
|
~jit_compiler();
|
||||||
|
|
||||||
// Compile module
|
// Get LLVM context
|
||||||
void make(std::unique_ptr<llvm::Module>, std::string);
|
auto& get_context()
|
||||||
|
{
|
||||||
|
return m_context;
|
||||||
|
}
|
||||||
|
|
||||||
// Load object
|
// Add module (path to obj cache dir)
|
||||||
void load(std::unique_ptr<llvm::Module>, std::unique_ptr<llvm::object::ObjectFile>);
|
void add(std::unique_ptr<llvm::Module> module, const std::string& path);
|
||||||
|
|
||||||
|
// Add object (path to obj file)
|
||||||
|
void add(const std::string& path);
|
||||||
|
|
||||||
|
// Finalize
|
||||||
|
void fin();
|
||||||
|
|
||||||
// Get compiled function address
|
// Get compiled function address
|
||||||
std::uintptr_t get(const std::string& name) const
|
u64 get(const std::string& name);
|
||||||
{
|
|
||||||
const auto found = m_map.find(name);
|
|
||||||
|
|
||||||
if (found != m_map.end())
|
|
||||||
{
|
|
||||||
return found->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
// Add functions directly to the memory manager (name -> code)
|
||||||
}
|
static std::unordered_map<std::string, u64> add(std::unordered_map<std::string, std::string>);
|
||||||
|
|
||||||
// Get CPU info
|
// Get CPU info
|
||||||
const std::string& cpu() const
|
const std::string& cpu() const
|
||||||
{
|
{
|
||||||
return m_cpu;
|
return m_cpu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check JIT purpose
|
||||||
|
bool is_primary() const
|
||||||
|
{
|
||||||
|
return !m_link.empty();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
Boost Software License - Version 1.0 - August 17th, 2003
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person or organization
|
|
||||||
obtaining a copy of the software and accompanying documentation covered by
|
|
||||||
this license (the "Software") to use, reproduce, display, distribute,
|
|
||||||
execute, and transmit the Software, and to prepare derivative works of the
|
|
||||||
Software, and to permit third-parties to whom the Software is furnished to
|
|
||||||
do so, all subject to the following:
|
|
||||||
|
|
||||||
The copyright notices in the Software and this entire statement, including
|
|
||||||
the above license grant, this restriction and the following disclaimer,
|
|
||||||
must be included in all copies of the Software, in whole or in part, and
|
|
||||||
all derivative works of the Software, unless such copies or derivative
|
|
||||||
works are solely in the form of machine-executable object code generated by
|
|
||||||
a source language processor.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
||||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
||||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
||||||
@@ -0,0 +1,265 @@
|
|||||||
|
/*
|
||||||
|
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||||
|
* https://github.com/corporateshark/LUrlParser
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "LUrlParser.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstring>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// check if the scheme name is valid
|
||||||
|
static bool IsSchemeValid( const std::string& SchemeName )
|
||||||
|
{
|
||||||
|
for ( auto c : SchemeName )
|
||||||
|
{
|
||||||
|
if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
|
||||||
|
{
|
||||||
|
if ( !IsValid() ) { return false; }
|
||||||
|
|
||||||
|
int Port = atoi( m_Port.c_str() );
|
||||||
|
|
||||||
|
if ( Port <= 0 || Port > 65535 ) { return false; }
|
||||||
|
|
||||||
|
if ( OutPort ) { *OutPort = Port; }
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// based on RFC 1738 and RFC 3986
|
||||||
|
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
|
||||||
|
{
|
||||||
|
LUrlParser::clParseURL Result;
|
||||||
|
|
||||||
|
const char* CurrentString = URL.c_str();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <scheme>:<scheme-specific-part>
|
||||||
|
* <scheme> := [a-z\+\-\.]+
|
||||||
|
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
|
||||||
|
*/
|
||||||
|
|
||||||
|
// try to read scheme
|
||||||
|
{
|
||||||
|
const char* LocalString = strchr( CurrentString, ':' );
|
||||||
|
|
||||||
|
if ( !LocalString )
|
||||||
|
{
|
||||||
|
return clParseURL( LUrlParserError_NoUrlCharacter );
|
||||||
|
}
|
||||||
|
|
||||||
|
// save the scheme name
|
||||||
|
Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
if ( !IsSchemeValid( Result.m_Scheme ) )
|
||||||
|
{
|
||||||
|
return clParseURL( LUrlParserError_InvalidSchemeName );
|
||||||
|
}
|
||||||
|
|
||||||
|
// scheme should be lowercase
|
||||||
|
std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
|
||||||
|
|
||||||
|
// skip ':'
|
||||||
|
CurrentString = LocalString+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* //<user>:<password>@<host>:<port>/<url-path>
|
||||||
|
* any ":", "@" and "/" must be normalized
|
||||||
|
*/
|
||||||
|
|
||||||
|
// skip "//"
|
||||||
|
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
||||||
|
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
|
||||||
|
|
||||||
|
// check if the user name and password are specified
|
||||||
|
bool bHasUserName = false;
|
||||||
|
|
||||||
|
const char* LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString )
|
||||||
|
{
|
||||||
|
if ( *LocalString == '@' )
|
||||||
|
{
|
||||||
|
// user name and password are specified
|
||||||
|
bHasUserName = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ( *LocalString == '/' )
|
||||||
|
{
|
||||||
|
// end of <host>:<port> specification
|
||||||
|
bHasUserName = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalString++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// user name and password
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
if ( bHasUserName )
|
||||||
|
{
|
||||||
|
// read user name
|
||||||
|
while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
// proceed with the current pointer
|
||||||
|
CurrentString = LocalString;
|
||||||
|
|
||||||
|
if ( *CurrentString == ':' )
|
||||||
|
{
|
||||||
|
// skip ':'
|
||||||
|
CurrentString++;
|
||||||
|
|
||||||
|
// read password
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString && *LocalString != '@' ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
CurrentString = LocalString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip '@'
|
||||||
|
if ( *CurrentString != '@' )
|
||||||
|
{
|
||||||
|
return clParseURL( LUrlParserError_NoAtSign );
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentString++;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bHasBracket = ( *CurrentString == '[' );
|
||||||
|
|
||||||
|
// go ahead, read the host name
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString )
|
||||||
|
{
|
||||||
|
if ( bHasBracket && *LocalString == ']' )
|
||||||
|
{
|
||||||
|
// end of IPv6 address
|
||||||
|
LocalString++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
|
||||||
|
{
|
||||||
|
// port number is specified
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalString++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
CurrentString = LocalString;
|
||||||
|
|
||||||
|
// is port number specified?
|
||||||
|
if ( *CurrentString == ':' )
|
||||||
|
{
|
||||||
|
CurrentString++;
|
||||||
|
|
||||||
|
// read port number
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString && *LocalString != '/' ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
CurrentString = LocalString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// end of string
|
||||||
|
if ( !*CurrentString )
|
||||||
|
{
|
||||||
|
Result.m_ErrorCode = LUrlParserError_Ok;
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// skip '/'
|
||||||
|
if ( *CurrentString != '/' )
|
||||||
|
{
|
||||||
|
return clParseURL( LUrlParserError_NoSlash );
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentString++;
|
||||||
|
|
||||||
|
// parse the path
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
CurrentString = LocalString;
|
||||||
|
|
||||||
|
// check for query
|
||||||
|
if ( *CurrentString == '?' )
|
||||||
|
{
|
||||||
|
// skip '?'
|
||||||
|
CurrentString++;
|
||||||
|
|
||||||
|
// read query
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString && *LocalString != '#' ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
CurrentString = LocalString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for fragment
|
||||||
|
if ( *CurrentString == '#' )
|
||||||
|
{
|
||||||
|
// skip '#'
|
||||||
|
CurrentString++;
|
||||||
|
|
||||||
|
// read fragment
|
||||||
|
LocalString = CurrentString;
|
||||||
|
|
||||||
|
while ( *LocalString ) LocalString++;
|
||||||
|
|
||||||
|
Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
|
||||||
|
|
||||||
|
CurrentString = LocalString;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result.m_ErrorCode = LUrlParserError_Ok;
|
||||||
|
|
||||||
|
return Result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
|
||||||
|
* https://github.com/corporateshark/LUrlParser
|
||||||
|
*
|
||||||
|
* The MIT License (MIT)
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace LUrlParser
|
||||||
|
{
|
||||||
|
enum LUrlParserError
|
||||||
|
{
|
||||||
|
LUrlParserError_Ok = 0,
|
||||||
|
LUrlParserError_Uninitialized = 1,
|
||||||
|
LUrlParserError_NoUrlCharacter = 2,
|
||||||
|
LUrlParserError_InvalidSchemeName = 3,
|
||||||
|
LUrlParserError_NoDoubleSlash = 4,
|
||||||
|
LUrlParserError_NoAtSign = 5,
|
||||||
|
LUrlParserError_UnexpectedEndOfLine = 6,
|
||||||
|
LUrlParserError_NoSlash = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
class clParseURL
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LUrlParserError m_ErrorCode;
|
||||||
|
std::string m_Scheme;
|
||||||
|
std::string m_Host;
|
||||||
|
std::string m_Port;
|
||||||
|
std::string m_Path;
|
||||||
|
std::string m_Query;
|
||||||
|
std::string m_Fragment;
|
||||||
|
std::string m_UserName;
|
||||||
|
std::string m_Password;
|
||||||
|
|
||||||
|
clParseURL()
|
||||||
|
: m_ErrorCode( LUrlParserError_Uninitialized )
|
||||||
|
{}
|
||||||
|
|
||||||
|
/// return 'true' if the parsing was successful
|
||||||
|
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
|
||||||
|
|
||||||
|
/// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
|
||||||
|
bool GetPort( int* OutPort ) const;
|
||||||
|
|
||||||
|
/// parse the URL
|
||||||
|
static clParseURL ParseURL( const std::string& URL );
|
||||||
|
|
||||||
|
private:
|
||||||
|
explicit clParseURL( LUrlParserError ErrorCode )
|
||||||
|
: m_ErrorCode( ErrorCode )
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace LUrlParser
|
||||||
@@ -1,18 +1,34 @@
|
|||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "File.h"
|
#include "File.h"
|
||||||
#include "StrFmt.h"
|
#include "StrFmt.h"
|
||||||
|
#include "sema.h"
|
||||||
|
|
||||||
|
#include "Utilities/sysinfo.h"
|
||||||
|
#include "Utilities/Thread.h"
|
||||||
#include "rpcs3_version.h"
|
#include "rpcs3_version.h"
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
using namespace std::literals::chrono_literals;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
#define NOMINMAX
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#else
|
#else
|
||||||
#include <chrono>
|
#include <sys/mman.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <zlib.h>
|
||||||
|
|
||||||
|
static std::string empty_string()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
// Thread-specific log prefix provider
|
// Thread-specific log prefix provider
|
||||||
thread_local std::string(*g_tls_log_prefix)() = nullptr;
|
thread_local std::string(*g_tls_log_prefix)() = &empty_string;
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
|
void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
|
||||||
@@ -29,6 +45,7 @@ void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
|
|||||||
case logs::level::warning: return "Warning";
|
case logs::level::warning: return "Warning";
|
||||||
case logs::level::notice: return "Notice";
|
case logs::level::notice: return "Notice";
|
||||||
case logs::level::trace: return "Trace";
|
case logs::level::trace: return "Trace";
|
||||||
|
case logs::level::_uninit: return unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
return unknown;
|
return unknown;
|
||||||
@@ -37,38 +54,86 @@ void fmt_class_string<logs::level>::format(std::string& out, u64 arg)
|
|||||||
|
|
||||||
namespace logs
|
namespace logs
|
||||||
{
|
{
|
||||||
|
// Memory-mapped buffer size
|
||||||
|
constexpr u64 s_log_size = 32 * 1024 * 1024;
|
||||||
|
|
||||||
class file_writer
|
class file_writer
|
||||||
{
|
{
|
||||||
// Could be memory-mapped file
|
|
||||||
fs::file m_file;
|
fs::file m_file;
|
||||||
|
std::string m_name;
|
||||||
|
std::thread m_writer;
|
||||||
|
fs::file m_fout;
|
||||||
|
fs::file m_fout2;
|
||||||
|
u64 m_max_size;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
::HANDLE m_fmap;
|
||||||
|
#endif
|
||||||
|
uchar* m_fptr{};
|
||||||
|
z_stream m_zs{};
|
||||||
|
semaphore<> m_m;
|
||||||
|
|
||||||
|
alignas(128) atomic_t<u64> m_buf{0}; // MSB (40 bit): push begin, LSB (24 bis): push size
|
||||||
|
alignas(128) atomic_t<u64> m_out{0}; // Amount of bytes written to file
|
||||||
|
|
||||||
|
uchar m_zout[65536];
|
||||||
|
|
||||||
|
// Write buffered logs immediately
|
||||||
|
bool flush(u64 bufv);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
file_writer(const std::string& name);
|
file_writer(const std::string& name);
|
||||||
|
|
||||||
virtual ~file_writer() = default;
|
virtual ~file_writer();
|
||||||
|
|
||||||
// Append raw data
|
// Append raw data
|
||||||
void log(const char* text, std::size_t size);
|
void log(logs::level sev, const char* text, std::size_t size);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct channel_info
|
||||||
|
{
|
||||||
|
channel* pointer = nullptr;
|
||||||
|
level enabled = level::notice;
|
||||||
|
|
||||||
|
void set_level(level value)
|
||||||
|
{
|
||||||
|
enabled = value;
|
||||||
|
|
||||||
|
if (pointer)
|
||||||
|
{
|
||||||
|
pointer->enabled = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stored_message
|
||||||
|
{
|
||||||
|
message m;
|
||||||
|
u64 stamp;
|
||||||
|
std::string prefix;
|
||||||
|
std::string text;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct file_listener : public file_writer, public listener
|
struct file_listener : public file_writer, public listener
|
||||||
{
|
{
|
||||||
file_listener(const std::string& name)
|
file_listener(const std::string& name);
|
||||||
: file_writer(name)
|
|
||||||
, listener()
|
virtual ~file_listener() = default;
|
||||||
{
|
|
||||||
const std::string& start = fmt::format("\xEF\xBB\xBF" "RPCS3 v%s\n", rpcs3::version.to_string());
|
|
||||||
file_writer::log(start.data(), start.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode level, current thread name, channel name and write log message
|
// Encode level, current thread name, channel name and write log message
|
||||||
virtual void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) override;
|
virtual void log(u64 stamp, const message& msg, const std::string& prefix, const std::string& text) override;
|
||||||
|
|
||||||
|
// Channel registry
|
||||||
|
std::unordered_map<std::string, channel_info> channels;
|
||||||
|
|
||||||
|
// Messages for delayed listener initialization
|
||||||
|
std::vector<stored_message> messages;
|
||||||
};
|
};
|
||||||
|
|
||||||
static file_listener* get_logger()
|
static file_listener* get_logger()
|
||||||
{
|
{
|
||||||
// Use magic static
|
// Use magic static
|
||||||
static file_listener logger("RPCS3.log");
|
static file_listener logger("RPCS3");
|
||||||
return &logger;
|
return &logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,14 +170,48 @@ namespace logs
|
|||||||
return timebase.get();
|
return timebase.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
channel GENERAL(nullptr, level::notice);
|
channel GENERAL("");
|
||||||
channel LOADER("LDR", level::notice);
|
channel LOADER("LDR");
|
||||||
channel MEMORY("MEM", level::notice);
|
channel MEMORY("MEM");
|
||||||
channel RSX("RSX", level::notice);
|
channel RSX("RSX");
|
||||||
channel HLE("HLE", level::notice);
|
channel HLE("HLE");
|
||||||
channel PPU("PPU", level::notice);
|
channel PPU("PPU");
|
||||||
channel SPU("SPU", level::notice);
|
channel SPU("SPU");
|
||||||
channel ARMv7("ARMv7");
|
channel ARMv7("ARMv7");
|
||||||
|
|
||||||
|
// Channel registry mutex
|
||||||
|
semaphore<> g_mutex;
|
||||||
|
|
||||||
|
// Must be set to true in main()
|
||||||
|
atomic_t<bool> g_init{false};
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
semaphore_lock lock(g_mutex);
|
||||||
|
|
||||||
|
for (auto&& pair : get_logger()->channels)
|
||||||
|
{
|
||||||
|
pair.second.set_level(level::notice);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_level(const std::string& ch_name, level value)
|
||||||
|
{
|
||||||
|
semaphore_lock lock(g_mutex);
|
||||||
|
|
||||||
|
get_logger()->channels[ch_name].set_level(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Must be called in main() to stop accumulating messages in g_messages
|
||||||
|
void set_init()
|
||||||
|
{
|
||||||
|
if (!g_init)
|
||||||
|
{
|
||||||
|
semaphore_lock lock(g_mutex);
|
||||||
|
get_logger()->messages.clear();
|
||||||
|
g_init = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logs::listener::~listener()
|
logs::listener::~listener()
|
||||||
@@ -124,11 +223,19 @@ void logs::listener::add(logs::listener* _new)
|
|||||||
// Get first (main) listener
|
// Get first (main) listener
|
||||||
listener* lis = get_logger();
|
listener* lis = get_logger();
|
||||||
|
|
||||||
|
semaphore_lock lock(g_mutex);
|
||||||
|
|
||||||
// Install new listener at the end of linked list
|
// Install new listener at the end of linked list
|
||||||
while (lis->m_next || !lis->m_next.compare_and_swap_test(nullptr, _new))
|
while (lis->m_next || !lis->m_next.compare_and_swap_test(nullptr, _new))
|
||||||
{
|
{
|
||||||
lis = lis->m_next;
|
lis = lis->m_next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send initial messages
|
||||||
|
for (const auto& msg : get_logger()->messages)
|
||||||
|
{
|
||||||
|
_new->log(msg.stamp, msg.m, msg.prefix, msg.text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u64* args)
|
void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u64* args)
|
||||||
@@ -136,12 +243,55 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u
|
|||||||
// Get timestamp
|
// Get timestamp
|
||||||
const u64 stamp = get_stamp();
|
const u64 stamp = get_stamp();
|
||||||
|
|
||||||
std::string text; fmt::raw_append(text, fmt, sup, args);
|
// Register channel
|
||||||
std::string prefix(g_tls_log_prefix ? g_tls_log_prefix() : "");
|
if (ch->enabled == level::_uninit)
|
||||||
|
{
|
||||||
|
semaphore_lock lock(g_mutex);
|
||||||
|
|
||||||
|
auto& info = get_logger()->channels[ch->name];
|
||||||
|
|
||||||
|
if (info.pointer && info.pointer != ch)
|
||||||
|
{
|
||||||
|
fmt::throw_exception("logs::channel repetition: %s", ch->name);
|
||||||
|
}
|
||||||
|
else if (!info.pointer)
|
||||||
|
{
|
||||||
|
info.pointer = ch;
|
||||||
|
ch->enabled = info.enabled;
|
||||||
|
|
||||||
|
// Check level again
|
||||||
|
if (info.enabled < sev)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get text
|
||||||
|
thread_local std::string text; text.clear();
|
||||||
|
fmt::raw_append(text, fmt, sup, args);
|
||||||
|
std::string prefix = g_tls_log_prefix();
|
||||||
|
|
||||||
// Get first (main) listener
|
// Get first (main) listener
|
||||||
listener* lis = get_logger();
|
listener* lis = get_logger();
|
||||||
|
|
||||||
|
if (!g_init)
|
||||||
|
{
|
||||||
|
semaphore_lock lock(g_mutex);
|
||||||
|
|
||||||
|
if (!g_init)
|
||||||
|
{
|
||||||
|
while (lis)
|
||||||
|
{
|
||||||
|
lis->log(stamp, *this, prefix, text);
|
||||||
|
lis = lis->m_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store message additionally
|
||||||
|
get_logger()->messages.emplace_back(stored_message{*this, stamp, std::move(prefix), text});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Send message to all listeners
|
// Send message to all listeners
|
||||||
while (lis)
|
while (lis)
|
||||||
{
|
{
|
||||||
@@ -153,28 +303,280 @@ void logs::message::broadcast(const char* fmt, const fmt_type_info* sup, const u
|
|||||||
[[noreturn]] extern void catch_all_exceptions();
|
[[noreturn]] extern void catch_all_exceptions();
|
||||||
|
|
||||||
logs::file_writer::file_writer(const std::string& name)
|
logs::file_writer::file_writer(const std::string& name)
|
||||||
|
: m_name(name)
|
||||||
{
|
{
|
||||||
|
const std::string log_name = fs::get_config_dir() + name + ".log";
|
||||||
|
const std::string buf_name = fs::get_config_dir() + name + ".buf";
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (!m_file.open(fs::get_config_dir() + name, fs::rewrite + fs::append))
|
if (!m_file.open(buf_name, fs::read + fs::write + fs::create + fs::trunc + fs::unshare))
|
||||||
{
|
{
|
||||||
fmt::throw_exception("Can't create log file %s (error %s)", name, fs::g_tls_error);
|
if (fs::g_tls_error == fs::error::acces)
|
||||||
|
{
|
||||||
|
if (fs::exists(buf_name))
|
||||||
|
{
|
||||||
|
fmt::throw_exception("Another instance of %s is running. Close it or kill its process, if necessary.", name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fmt::throw_exception("Cannot create %s.log (access denied)."
|
||||||
|
#ifdef _WIN32
|
||||||
|
"\nNote that %s cannot be installed in Program Files or similar directory with limited permissions."
|
||||||
|
#else
|
||||||
|
"\nPlease, check %s permissions in '~/.config/'."
|
||||||
|
#endif
|
||||||
|
, name, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::throw_exception("Cannot create %s.log (error %s)", name, fs::g_tls_error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check free space
|
||||||
|
fs::device_stat stats{};
|
||||||
|
if (!fs::statfs(fs::get_config_dir(), stats) || stats.avail_free < s_log_size * 8)
|
||||||
|
{
|
||||||
|
fmt::throw_exception("Not enough free space (%f KB)", stats.avail_free / 1000000.);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit log size to ~25% of free space
|
||||||
|
m_max_size = stats.avail_free / 4;
|
||||||
|
|
||||||
|
// Initialize memory mapped file
|
||||||
|
#ifdef _WIN32
|
||||||
|
m_fmap = CreateFileMappingW(m_file.get_handle(), 0, PAGE_READWRITE, s_log_size >> 32, s_log_size & 0xffffffff, 0);
|
||||||
|
m_fptr = m_fmap ? (uchar*)MapViewOfFile(m_fmap, FILE_MAP_WRITE, 0, 0, 0) : nullptr;
|
||||||
|
#else
|
||||||
|
m_file.trunc(s_log_size);
|
||||||
|
m_fptr = (uchar*)::mmap(0, s_log_size, PROT_READ | PROT_WRITE, MAP_SHARED, m_file.get_handle(), 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
verify(name.c_str()), m_fptr;
|
||||||
|
|
||||||
|
// Rotate backups (TODO)
|
||||||
|
fs::remove_file(fs::get_config_dir() + name + "1.log.gz");
|
||||||
|
fs::create_dir(fs::get_config_dir() + "old_logs");
|
||||||
|
fs::rename(fs::get_config_dir() + m_name + ".log.gz", fs::get_config_dir() + "old_logs/" + m_name + ".log.gz", true);
|
||||||
|
|
||||||
|
// Actual log file (allowed to fail)
|
||||||
|
m_fout.open(log_name, fs::rewrite);
|
||||||
|
|
||||||
|
// Compressed log
|
||||||
|
if (!m_fout2.open(log_name + ".gz", fs::rewrite) || deflateInit2(&m_zs, 9, Z_DEFLATED, 16 + 15, 9, Z_DEFAULT_STRATEGY) != Z_OK)
|
||||||
|
{
|
||||||
|
m_fout2.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
std::thread([text = std::string{e.what()}]{ report_fatal_error(text); }).detach();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
catch_all_exceptions();
|
std::thread([]{ report_fatal_error("Unknown error" HERE); }).detach();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_writer = std::thread([this]()
|
||||||
|
{
|
||||||
|
thread_ctrl::set_native_priority(-1);
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const u64 bufv = m_buf;
|
||||||
|
|
||||||
|
if (bufv & 0xffffff)
|
||||||
|
{
|
||||||
|
// Wait if threads are writing logs
|
||||||
|
std::this_thread::yield();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!flush(bufv))
|
||||||
|
{
|
||||||
|
if (m_out == -1)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::this_thread::sleep_for(10ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logs::file_writer::~file_writer()
|
||||||
|
{
|
||||||
|
if (!m_fptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop writer thread
|
||||||
|
while (m_out << 24 < m_buf)
|
||||||
|
{
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_out = -1;
|
||||||
|
m_writer.join();
|
||||||
|
|
||||||
|
if (m_fout2)
|
||||||
|
{
|
||||||
|
m_zs.avail_in = 0;
|
||||||
|
m_zs.next_in = nullptr;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
m_zs.avail_out = sizeof(m_zout);
|
||||||
|
m_zs.next_out = m_zout;
|
||||||
|
|
||||||
|
if (deflate(&m_zs, Z_FINISH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (m_zs.avail_out == 0);
|
||||||
|
|
||||||
|
deflateEnd(&m_zs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
UnmapViewOfFile(m_fptr);
|
||||||
|
CloseHandle(m_fmap);
|
||||||
|
#else
|
||||||
|
::munmap(m_fptr, s_log_size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool logs::file_writer::flush(u64 bufv)
|
||||||
|
{
|
||||||
|
semaphore_lock lock(m_m);
|
||||||
|
|
||||||
|
const u64 st = +m_out;
|
||||||
|
const u64 end = std::min<u64>((st + s_log_size) & ~(s_log_size - 1), bufv >> 24);
|
||||||
|
|
||||||
|
if (end > st)
|
||||||
|
{
|
||||||
|
// Avoid writing too big fragments
|
||||||
|
const u64 size = std::min<u64>(end - st, sizeof(m_zout) / 2);
|
||||||
|
|
||||||
|
// Write uncompressed
|
||||||
|
if (m_fout && st < m_max_size && m_fout.write(m_fptr + st % s_log_size, size) != size)
|
||||||
|
{
|
||||||
|
m_fout.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write compressed
|
||||||
|
if (m_fout2 && st < m_max_size)
|
||||||
|
{
|
||||||
|
m_zs.avail_in = size;
|
||||||
|
m_zs.next_in = m_fptr + st % s_log_size;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
m_zs.avail_out = sizeof(m_zout);
|
||||||
|
m_zs.next_out = m_zout;
|
||||||
|
|
||||||
|
if (deflate(&m_zs, Z_NO_FLUSH) == Z_STREAM_ERROR || m_fout2.write(m_zout, sizeof(m_zout) - m_zs.avail_out) != sizeof(m_zout) - m_zs.avail_out)
|
||||||
|
{
|
||||||
|
deflateEnd(&m_zs);
|
||||||
|
m_fout2.close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (m_zs.avail_out == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_out += size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void logs::file_writer::log(logs::level sev, const char* text, std::size_t size)
|
||||||
|
{
|
||||||
|
if (!m_fptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: write bigger fragment directly in blocking manner
|
||||||
|
while (size && size <= 0xffffff)
|
||||||
|
{
|
||||||
|
u64 bufv;
|
||||||
|
|
||||||
|
const auto pos = m_buf.atomic_op([&](u64& v) -> uchar*
|
||||||
|
{
|
||||||
|
const u64 v1 = v >> 24;
|
||||||
|
const u64 v2 = v & 0xffffff;
|
||||||
|
|
||||||
|
if (UNLIKELY(v2 + size > 0xffffff || v1 + v2 + size >= m_out + s_log_size))
|
||||||
|
{
|
||||||
|
bufv = v;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
v += size;
|
||||||
|
return m_fptr + (v1 + v2) % s_log_size;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (UNLIKELY(!pos))
|
||||||
|
{
|
||||||
|
if ((bufv & 0xffffff) + size > 0xffffff || bufv & 0xffffff)
|
||||||
|
{
|
||||||
|
// Concurrency limit reached
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Queue is full, need to write out
|
||||||
|
flush(bufv);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos + size > m_fptr + s_log_size)
|
||||||
|
{
|
||||||
|
const auto frag = m_fptr + s_log_size - pos;
|
||||||
|
std::memcpy(pos, text, frag);
|
||||||
|
std::memcpy(m_fptr, text + frag, size - frag);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::memcpy(pos, text, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buf += (u64{size} << 24) - size;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void logs::file_writer::log(const char* text, std::size_t size)
|
logs::file_listener::file_listener(const std::string& name)
|
||||||
|
: file_writer(name)
|
||||||
|
, listener()
|
||||||
{
|
{
|
||||||
m_file.write(text, size);
|
// Write UTF-8 BOM
|
||||||
|
file_writer::log(logs::level::always, "\xEF\xBB\xBF", 3);
|
||||||
|
|
||||||
|
// Write initial message
|
||||||
|
stored_message ver;
|
||||||
|
ver.m.ch = nullptr;
|
||||||
|
ver.m.sev = level::always;
|
||||||
|
ver.stamp = 0;
|
||||||
|
ver.text = fmt::format("RPCS3 v%s | %s\n%s", rpcs3::version.to_string(), rpcs3::get_branch(), utils::get_system_info());
|
||||||
|
|
||||||
|
file_writer::log(logs::level::always, ver.text.data(), ver.text.size());
|
||||||
|
file_writer::log(logs::level::always, "\n", 1);
|
||||||
|
messages.emplace_back(std::move(ver));
|
||||||
}
|
}
|
||||||
|
|
||||||
void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::string& prefix, const std::string& _text)
|
void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::string& prefix, const std::string& _text)
|
||||||
{
|
{
|
||||||
std::string text; text.reserve(prefix.size() + _text.size() + 200);
|
thread_local std::string text;
|
||||||
|
|
||||||
// Used character: U+00B7 (Middle Dot)
|
// Used character: U+00B7 (Middle Dot)
|
||||||
switch (msg.sev)
|
switch (msg.sev)
|
||||||
@@ -187,6 +589,7 @@ void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::st
|
|||||||
case level::warning: text = u8"·W "; break;
|
case level::warning: text = u8"·W "; break;
|
||||||
case level::notice: text = u8"·! "; break;
|
case level::notice: text = u8"·! "; break;
|
||||||
case level::trace: text = u8"·T "; break;
|
case level::trace: text = u8"·T "; break;
|
||||||
|
case level::_uninit: text = u8"· "; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print miscosecond timestamp
|
// Print miscosecond timestamp
|
||||||
@@ -202,8 +605,8 @@ void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::st
|
|||||||
text += prefix;
|
text += prefix;
|
||||||
text += "} ";
|
text += "} ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.ch->name)
|
if (msg.ch && '\0' != *msg.ch->name)
|
||||||
{
|
{
|
||||||
text += msg.ch->name;
|
text += msg.ch->name;
|
||||||
text += msg.sev == level::todo ? " TODO: " : ": ";
|
text += msg.sev == level::todo ? " TODO: " : ": ";
|
||||||
@@ -212,9 +615,9 @@ void logs::file_listener::log(u64 stamp, const logs::message& msg, const std::st
|
|||||||
{
|
{
|
||||||
text += "TODO: ";
|
text += "TODO: ";
|
||||||
}
|
}
|
||||||
|
|
||||||
text += _text;
|
text += _text;
|
||||||
text += '\n';
|
text += '\n';
|
||||||
|
|
||||||
file_writer::log(text.data(), text.size());
|
file_writer::log(msg.sev, text.data(), text.size());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,19 +3,22 @@
|
|||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "Atomic.h"
|
#include "Atomic.h"
|
||||||
#include "StrFmt.h"
|
#include "StrFmt.h"
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
namespace logs
|
namespace logs
|
||||||
{
|
{
|
||||||
enum class level : uint
|
enum class level : uint
|
||||||
{
|
{
|
||||||
always, // highest level (unused, cannot be disabled)
|
always, // Highest log severity (unused, cannot be disabled)
|
||||||
fatal,
|
fatal,
|
||||||
error,
|
error,
|
||||||
todo,
|
todo,
|
||||||
success,
|
success,
|
||||||
warning,
|
warning,
|
||||||
notice,
|
notice,
|
||||||
trace, // lowest level (usually disabled)
|
trace, // Lowest severity (usually disabled)
|
||||||
|
|
||||||
|
_uninit = UINT_MAX, // Special value for delayed initialization
|
||||||
};
|
};
|
||||||
|
|
||||||
struct channel;
|
struct channel;
|
||||||
@@ -23,7 +26,7 @@ namespace logs
|
|||||||
// Message information (temporary data)
|
// Message information (temporary data)
|
||||||
struct message
|
struct message
|
||||||
{
|
{
|
||||||
const channel* ch;
|
channel* ch;
|
||||||
level sev;
|
level sev;
|
||||||
|
|
||||||
// Send log message to global logger instance
|
// Send log message to global logger instance
|
||||||
@@ -57,16 +60,16 @@ namespace logs
|
|||||||
// The lowest logging level enabled for this channel (used for early filtering)
|
// The lowest logging level enabled for this channel (used for early filtering)
|
||||||
atomic_t<level> enabled;
|
atomic_t<level> enabled;
|
||||||
|
|
||||||
// Constant initialization: name and initial log level
|
// Constant initialization: channel name
|
||||||
constexpr channel(const char* name, level enabled = level::trace)
|
constexpr channel(const char* name)
|
||||||
: name(name)
|
: name(name)
|
||||||
, enabled(enabled)
|
, enabled(level::_uninit)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formatting function
|
// Formatting function
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
SAFE_BUFFERS FORCE_INLINE void format(level sev, const char* fmt, const Args&... args) const
|
SAFE_BUFFERS FORCE_INLINE void format(level sev, const char* fmt, const Args&... args)
|
||||||
{
|
{
|
||||||
if (UNLIKELY(sev <= enabled))
|
if (UNLIKELY(sev <= enabled))
|
||||||
{
|
{
|
||||||
@@ -76,7 +79,7 @@ namespace logs
|
|||||||
|
|
||||||
#define GEN_LOG_METHOD(_sev)\
|
#define GEN_LOG_METHOD(_sev)\
|
||||||
template<typename... Args>\
|
template<typename... Args>\
|
||||||
SAFE_BUFFERS void _sev(const char* fmt, const Args&... args) const\
|
SAFE_BUFFERS void _sev(const char* fmt, const Args&... args)\
|
||||||
{\
|
{\
|
||||||
return format<Args...>(level::_sev, fmt, args...);\
|
return format<Args...>(level::_sev, fmt, args...);\
|
||||||
}
|
}
|
||||||
@@ -102,6 +105,12 @@ namespace logs
|
|||||||
extern channel PPU;
|
extern channel PPU;
|
||||||
extern channel SPU;
|
extern channel SPU;
|
||||||
extern channel ARMv7;
|
extern channel ARMv7;
|
||||||
|
|
||||||
|
// Log level control: set all channels to level::notice
|
||||||
|
void reset();
|
||||||
|
|
||||||
|
// Log level control: register channel if necessary, set channel level
|
||||||
|
void set_level(const std::string&, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Legacy:
|
// Legacy:
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
class MTProgressDialog : public wxDialog
|
|
||||||
{
|
|
||||||
wxGauge** m_gauge;
|
|
||||||
wxStaticText** m_msg;
|
|
||||||
|
|
||||||
wxArrayLong m_maximum;
|
|
||||||
const u8 m_cores;
|
|
||||||
|
|
||||||
static const uint layout = 16;
|
|
||||||
static const uint maxdial = 65536;
|
|
||||||
wxArrayInt m_lastupdate;
|
|
||||||
|
|
||||||
public:
|
|
||||||
MTProgressDialog(wxWindow* parent, const wxSize& size, const wxString& title,
|
|
||||||
const wxString& msg, const wxArrayLong& maximum, const u8 cores)
|
|
||||||
: wxDialog(parent, wxID_ANY, title, wxDefaultPosition)
|
|
||||||
, m_maximum(maximum)
|
|
||||||
, m_cores(cores)
|
|
||||||
{
|
|
||||||
wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
|
|
||||||
|
|
||||||
m_gauge = new wxGauge*[m_cores];
|
|
||||||
m_msg = new wxStaticText*[m_cores];
|
|
||||||
|
|
||||||
m_lastupdate.SetCount(cores);
|
|
||||||
|
|
||||||
for(uint i=0; i<m_cores; ++i)
|
|
||||||
{
|
|
||||||
m_lastupdate[i] = -1;
|
|
||||||
|
|
||||||
m_msg[i] = new wxStaticText(this, wxID_ANY, msg);
|
|
||||||
sizer->Add(m_msg[i], 0, wxLEFT | wxTOP, layout);
|
|
||||||
|
|
||||||
m_gauge[i] = new wxGauge(this, wxID_ANY, maxdial,
|
|
||||||
wxDefaultPosition, wxDefaultSize,
|
|
||||||
wxGA_HORIZONTAL );
|
|
||||||
|
|
||||||
sizer->Add(m_gauge[i], 0, wxLEFT | wxRIGHT | wxTOP | wxEXPAND, layout);
|
|
||||||
m_gauge[i]->SetValue(0);
|
|
||||||
|
|
||||||
sizer->AddSpacer(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetSizerAndFit(sizer);
|
|
||||||
if(size != wxDefaultSize)
|
|
||||||
{
|
|
||||||
SetSize(size);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
wxSize ws;
|
|
||||||
ws.x = 400;
|
|
||||||
ws.y = GetSize().y + 8;
|
|
||||||
SetSize(ws);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_maximum.SetCount(m_cores);
|
|
||||||
|
|
||||||
Show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update(const u8 thread_id, const u64 value, const wxString& msg)
|
|
||||||
{
|
|
||||||
if(thread_id > m_cores) return;
|
|
||||||
|
|
||||||
const int curupdate = (int)(((double)value/(double)m_maximum[thread_id])*1000);
|
|
||||||
if(curupdate == m_lastupdate[thread_id]) return;
|
|
||||||
m_lastupdate[thread_id] = curupdate;
|
|
||||||
|
|
||||||
m_msg[thread_id]->SetLabel(msg);
|
|
||||||
|
|
||||||
if(value >= (u32)m_maximum[thread_id]) return;
|
|
||||||
m_gauge[thread_id]->SetValue(((double)value / (double)m_maximum[thread_id]) * maxdial);
|
|
||||||
}
|
|
||||||
|
|
||||||
const u32 GetMaxValue(const uint thread_id) const
|
|
||||||
{
|
|
||||||
if(thread_id > m_cores) return 0;
|
|
||||||
return m_maximum[thread_id];
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetMaxFor(const uint thread_id, const u64 val)
|
|
||||||
{
|
|
||||||
if(thread_id > m_cores) return;
|
|
||||||
m_maximum[thread_id] = val;
|
|
||||||
m_lastupdate[thread_id] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void Close(bool force = false)
|
|
||||||
{
|
|
||||||
m_lastupdate.Empty();
|
|
||||||
m_maximum.Empty();
|
|
||||||
|
|
||||||
wxDialog::Close(force);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -257,8 +257,8 @@ struct fmt::cfmt_src
|
|||||||
|
|
||||||
void skip(std::size_t extra)
|
void skip(std::size_t extra)
|
||||||
{
|
{
|
||||||
++sup += extra;
|
sup += extra + 1;
|
||||||
++args += extra;
|
args += extra + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t fmt_string(std::string& out, std::size_t extra) const
|
std::size_t fmt_string(std::string& out, std::size_t extra) const
|
||||||
@@ -286,6 +286,15 @@ struct fmt::cfmt_src
|
|||||||
|
|
||||||
return 0;
|
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
void fmt::raw_append(std::string& out, const char* fmt, const fmt_type_info* sup, const u64* args) noexcept
|
void fmt::raw_append(std::string& out, const char* fmt, const fmt_type_info* sup, const u64* args) noexcept
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ namespace fmt
|
|||||||
template <typename T, typename>
|
template <typename T, typename>
|
||||||
struct fmt_unveil
|
struct fmt_unveil
|
||||||
{
|
{
|
||||||
static_assert(sizeof(T) > 0, "fmt_unveil<>: cannot pass forward-declared object");
|
static_assert(sizeof(T) > 0, "fmt_unveil<> error: incomplete type");
|
||||||
|
|
||||||
using type = T;
|
using type = T;
|
||||||
|
|
||||||
@@ -59,13 +59,24 @@ struct fmt_unveil<T, std::enable_if_t<std::is_floating_point<T>::value && sizeof
|
|||||||
{
|
{
|
||||||
using type = T;
|
using type = T;
|
||||||
|
|
||||||
// Convert FP to f64 and reinterpret (TODO?)
|
// Convert FP to f64 and reinterpret as u64
|
||||||
static inline u64 get(const f64 arg)
|
static inline u64 get(const f64& arg)
|
||||||
{
|
{
|
||||||
return *reinterpret_cast<const u64*>(reinterpret_cast<const u8*>(&arg));
|
return *reinterpret_cast<const u64*>(reinterpret_cast<const u8*>(&arg));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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>
|
template <typename T>
|
||||||
struct fmt_unveil<T, std::enable_if_t<std::is_enum<T>::value>>
|
struct fmt_unveil<T, std::enable_if_t<std::is_enum<T>::value>>
|
||||||
{
|
{
|
||||||
@@ -126,15 +137,27 @@ struct fmt_class_string
|
|||||||
return *reinterpret_cast<const T*>(static_cast<std::uintptr_t>(arg));
|
return *reinterpret_cast<const T*>(static_cast<std::uintptr_t>(arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enum -> string function type
|
||||||
|
using convert_t = const char*(*)(T value);
|
||||||
|
|
||||||
|
// Enum -> string function registered
|
||||||
|
static convert_t convert_enum;
|
||||||
|
|
||||||
// Helper function (safely converts arg to enum value)
|
// Helper function (safely converts arg to enum value)
|
||||||
static SAFE_BUFFERS FORCE_INLINE void format_enum(std::string& out, u64 arg, const char* (*get)(T value))
|
static SAFE_BUFFERS FORCE_INLINE void format_enum(std::string& out, u64 arg, convert_t convert)
|
||||||
{
|
{
|
||||||
|
// Save convert function
|
||||||
|
if (convert_enum == nullptr)
|
||||||
|
{
|
||||||
|
convert_enum = convert;
|
||||||
|
}
|
||||||
|
|
||||||
const auto value = static_cast<std::underlying_type_t<T>>(arg);
|
const auto value = static_cast<std::underlying_type_t<T>>(arg);
|
||||||
|
|
||||||
// Check narrowing
|
// Check narrowing
|
||||||
if (static_cast<u64>(value) == arg)
|
if (static_cast<u64>(value) == arg)
|
||||||
{
|
{
|
||||||
if (const char* str = get(static_cast<T>(value)))
|
if (const char* str = convert(static_cast<T>(value)))
|
||||||
{
|
{
|
||||||
out += str;
|
out += str;
|
||||||
return;
|
return;
|
||||||
@@ -180,6 +203,9 @@ struct fmt_class_string
|
|||||||
static constexpr const char* unknown = nullptr;
|
static constexpr const char* unknown = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T, typename V>
|
||||||
|
const char*(*fmt_class_string<T, V>::convert_enum)(T) = nullptr;
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
struct fmt_class_string<const void*, void>
|
struct fmt_class_string<const void*, void>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,6 +24,12 @@ inline void strcpy_trunc(char (&dst)[N], const char (&src)[N2])
|
|||||||
dst[count] = '\0';
|
dst[count] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <std::size_t N>
|
||||||
|
inline bool ends_with(const std::string& src, const char (&end)[N])
|
||||||
|
{
|
||||||
|
return src.size() >= N - 1 && src.compare(src.size() - (N - 1), N - 1, end, N - 1) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
namespace fmt
|
namespace fmt
|
||||||
{
|
{
|
||||||
std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
|
std::string replace_first(const std::string& src, const std::string& from, const std::string& to);
|
||||||
|
|||||||
@@ -3,7 +3,12 @@
|
|||||||
#include "Emu/System.h"
|
#include "Emu/System.h"
|
||||||
#include "Emu/IdManager.h"
|
#include "Emu/IdManager.h"
|
||||||
#include "Emu/Cell/RawSPUThread.h"
|
#include "Emu/Cell/RawSPUThread.h"
|
||||||
|
#include "Emu/Cell/lv2/sys_mmapper.h"
|
||||||
|
#include "Emu/Cell/lv2/sys_event.h"
|
||||||
#include "Thread.h"
|
#include "Thread.h"
|
||||||
|
#include "sysinfo.h"
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
@@ -13,6 +18,12 @@
|
|||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
#define _XOPEN_SOURCE
|
#define _XOPEN_SOURCE
|
||||||
#define __USE_GNU
|
#define __USE_GNU
|
||||||
|
#include <mach/thread_act.h>
|
||||||
|
#include <mach/thread_policy.h>
|
||||||
|
#endif
|
||||||
|
#if defined(__DragonFly__) || defined(__FreeBSD__)
|
||||||
|
#include <pthread_np.h>
|
||||||
|
#define cpu_set_t cpuset_t
|
||||||
#endif
|
#endif
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@@ -28,20 +39,6 @@ thread_local u64 g_tls_fault_all = 0;
|
|||||||
thread_local u64 g_tls_fault_rsx = 0;
|
thread_local u64 g_tls_fault_rsx = 0;
|
||||||
thread_local u64 g_tls_fault_spu = 0;
|
thread_local u64 g_tls_fault_spu = 0;
|
||||||
|
|
||||||
static void report_fatal_error(const std::string& msg)
|
|
||||||
{
|
|
||||||
std::string _msg = msg + "\n"
|
|
||||||
"HOW TO REPORT ERRORS: Check the FAQ, README, other sources.\n"
|
|
||||||
"Please, don't send incorrect reports. Thanks for understanding.\n";
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
_msg += "Press (Ctrl+C) to copy this message.";
|
|
||||||
MessageBoxA(0, _msg.c_str(), "Fatal error", MB_ICONERROR); // TODO: unicode message
|
|
||||||
#else
|
|
||||||
std::printf("Fatal error: \n%s", _msg.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[[noreturn]] void catch_all_exceptions()
|
[[noreturn]] void catch_all_exceptions()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -56,8 +53,6 @@ static void report_fatal_error(const std::string& msg)
|
|||||||
{
|
{
|
||||||
report_fatal_error("Unhandled exception (unknown)");
|
report_fatal_error("Unhandled exception (unknown)");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum x64_reg_t : u32
|
enum x64_reg_t : u32
|
||||||
@@ -756,6 +751,90 @@ uint64_t* darwin_x64reg(x64_context *context, int reg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||||
|
|
||||||
|
#define X64REG(context, reg) (freebsd_x64reg(context, reg))
|
||||||
|
#ifdef __DragonFly__
|
||||||
|
# define XMMREG(context, reg) (reinterpret_cast<v128*>(((union savefpu*)(context)->uc_mcontext.mc_fpregs)->sv_xmm.sv_xmm[reg]))
|
||||||
|
#else
|
||||||
|
# define XMMREG(context, reg) (reinterpret_cast<v128*>(((struct savefpu*)(context)->uc_mcontext.mc_fpstate)->sv_xmm[reg]))
|
||||||
|
#endif
|
||||||
|
#define EFLAGS(context) ((context)->uc_mcontext.mc_rflags)
|
||||||
|
|
||||||
|
register_t* freebsd_x64reg(x64_context *context, int reg)
|
||||||
|
{
|
||||||
|
auto *state = &context->uc_mcontext;
|
||||||
|
switch(reg)
|
||||||
|
{
|
||||||
|
case 0: return &state->mc_rax;
|
||||||
|
case 1: return &state->mc_rcx;
|
||||||
|
case 2: return &state->mc_rdx;
|
||||||
|
case 3: return &state->mc_rbx;
|
||||||
|
case 4: return &state->mc_rsp;
|
||||||
|
case 5: return &state->mc_rbp;
|
||||||
|
case 6: return &state->mc_rsi;
|
||||||
|
case 7: return &state->mc_rdi;
|
||||||
|
case 8: return &state->mc_r8;
|
||||||
|
case 9: return &state->mc_r9;
|
||||||
|
case 10: return &state->mc_r10;
|
||||||
|
case 11: return &state->mc_r11;
|
||||||
|
case 12: return &state->mc_r12;
|
||||||
|
case 13: return &state->mc_r13;
|
||||||
|
case 14: return &state->mc_r14;
|
||||||
|
case 15: return &state->mc_r15;
|
||||||
|
case 16: return &state->mc_rip;
|
||||||
|
default:
|
||||||
|
LOG_ERROR(GENERAL, "Invalid register index: %d", reg);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
|
||||||
|
#define X64REG(context, reg) (openbsd_x64reg(context, reg))
|
||||||
|
#define XMMREG(context, reg) (reinterpret_cast<v128*>((context)->sc_fpstate->fx_xmm[reg]))
|
||||||
|
#define EFLAGS(context) ((context)->sc_rflags)
|
||||||
|
|
||||||
|
long* openbsd_x64reg(x64_context *context, int reg)
|
||||||
|
{
|
||||||
|
auto *state = &context->uc_mcontext;
|
||||||
|
switch(reg)
|
||||||
|
{
|
||||||
|
case 0: return &state->sc_rax;
|
||||||
|
case 1: return &state->sc_rcx;
|
||||||
|
case 2: return &state->sc_rdx;
|
||||||
|
case 3: return &state->sc_rbx;
|
||||||
|
case 4: return &state->sc_rsp;
|
||||||
|
case 5: return &state->sc_rbp;
|
||||||
|
case 6: return &state->sc_rsi;
|
||||||
|
case 7: return &state->sc_rdi;
|
||||||
|
case 8: return &state->sc_r8;
|
||||||
|
case 9: return &state->sc_r9;
|
||||||
|
case 10: return &state->sc_r10;
|
||||||
|
case 11: return &state->sc_r11;
|
||||||
|
case 12: return &state->sc_r12;
|
||||||
|
case 13: return &state->sc_r13;
|
||||||
|
case 14: return &state->sc_r14;
|
||||||
|
case 15: return &state->sc_r15;
|
||||||
|
case 16: return &state->sc_rip;
|
||||||
|
default:
|
||||||
|
LOG_ERROR(GENERAL, "Invalid register index: %d", reg);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
|
||||||
|
static const decltype(_REG_RAX) reg_table[] =
|
||||||
|
{
|
||||||
|
_REG_RAX, _REG_RCX, _REG_RDX, _REG_RBX, _REG_RSP, _REG_RBP, _REG_RSI, _REG_RDI,
|
||||||
|
_REG_R8, _REG_R9, _REG_R10, _REG_R11, _REG_R12, _REG_R13, _REG_R14, _REG_R15, _REG_RIP
|
||||||
|
};
|
||||||
|
|
||||||
|
#define X64REG(context, reg) (&(context)->uc_mcontext.__gregs[reg_table[reg]])
|
||||||
|
#define XMM_sig(context, reg) (reinterpret_cast<v128*>(((struct fxsave64*)(context)->uc_mcontext.__fpregs)->fx_xmm[reg]))
|
||||||
|
#define EFLAGS(context) ((context)->uc_mcontext.__gregs[_REG_RFL])
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static const decltype(REG_RAX) reg_table[] =
|
static const decltype(REG_RAX) reg_table[] =
|
||||||
@@ -765,7 +844,11 @@ static const decltype(REG_RAX) reg_table[] =
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define X64REG(context, reg) (&(context)->uc_mcontext.gregs[reg_table[reg]])
|
#define X64REG(context, reg) (&(context)->uc_mcontext.gregs[reg_table[reg]])
|
||||||
|
#ifdef __sun
|
||||||
|
#define XMMREG(context, reg) (reinterpret_cast<v128*>(&(context)->uc_mcontext.fpregs.fp_reg_set.fpchip_state.xmm[reg_table[reg]]))
|
||||||
|
#else
|
||||||
#define XMMREG(context, reg) (reinterpret_cast<v128*>(&(context)->uc_mcontext.fpregs->_xmm[reg]))
|
#define XMMREG(context, reg) (reinterpret_cast<v128*>(&(context)->uc_mcontext.fpregs->_xmm[reg]))
|
||||||
|
#endif // __sun
|
||||||
#define EFLAGS(context) ((context)->uc_mcontext.gregs[REG_EFL])
|
#define EFLAGS(context) ((context)->uc_mcontext.gregs[REG_EFL])
|
||||||
|
|
||||||
#endif // __APPLE__
|
#endif // __APPLE__
|
||||||
@@ -882,8 +965,8 @@ bool put_x64_reg_value(x64_context* context, x64_reg_t reg, size_t d_size, u64 v
|
|||||||
// save the value into x64 register
|
// save the value into x64 register
|
||||||
switch (d_size)
|
switch (d_size)
|
||||||
{
|
{
|
||||||
case 1: *X64REG(context, reg - X64R_RAX) = value & 0xff | *X64REG(context, reg - X64R_RAX) & 0xffffff00; return true;
|
case 1: *X64REG(context, reg - X64R_RAX) = (value & 0xff) | (*X64REG(context, reg - X64R_RAX) & 0xffffff00); return true;
|
||||||
case 2: *X64REG(context, reg - X64R_RAX) = value & 0xffff | *X64REG(context, reg - X64R_RAX) & 0xffff0000; return true;
|
case 2: *X64REG(context, reg - X64R_RAX) = (value & 0xffff) | (*X64REG(context, reg - X64R_RAX) & 0xffff0000); return true;
|
||||||
case 4: *X64REG(context, reg - X64R_RAX) = value & 0xffffffff; return true;
|
case 4: *X64REG(context, reg - X64R_RAX) = value & 0xffffffff; return true;
|
||||||
case 8: *X64REG(context, reg - X64R_RAX) = value; return true;
|
case 8: *X64REG(context, reg - X64R_RAX) = value; return true;
|
||||||
}
|
}
|
||||||
@@ -1003,9 +1086,17 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
|
|||||||
{
|
{
|
||||||
g_tls_fault_all++;
|
g_tls_fault_all++;
|
||||||
|
|
||||||
|
const auto cpu = get_current_cpu_thread();
|
||||||
|
|
||||||
if (rsx::g_access_violation_handler && rsx::g_access_violation_handler(addr, is_writing))
|
if (rsx::g_access_violation_handler && rsx::g_access_violation_handler(addr, is_writing))
|
||||||
{
|
{
|
||||||
g_tls_fault_rsx++;
|
g_tls_fault_rsx++;
|
||||||
|
|
||||||
|
if (cpu)
|
||||||
|
{
|
||||||
|
cpu->test_state();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1140,14 +1231,70 @@ bool handle_access_violation(u32 addr, bool is_writing, x64_context* context)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm::check_addr(addr, d_size))
|
if (vm::check_addr(addr, std::max<std::size_t>(1, d_size), vm::page_allocated | (is_writing ? vm::page_writable : vm::page_readable)))
|
||||||
{
|
{
|
||||||
|
if (cpu)
|
||||||
|
{
|
||||||
|
cpu->test_state();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: allow recovering from a page fault as a feature of PS3 virtual memory
|
if (cpu)
|
||||||
if (const auto cpu = get_current_cpu_thread())
|
|
||||||
{
|
{
|
||||||
|
if (fxm::check<page_fault_notification_entries>())
|
||||||
|
{
|
||||||
|
for (const auto& entry : fxm::get<page_fault_notification_entries>()->entries)
|
||||||
|
{
|
||||||
|
auto mem = vm::get(vm::any, entry.start_addr);
|
||||||
|
if (!mem)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (entry.start_addr <= addr && addr <= addr + mem->size - 1)
|
||||||
|
{
|
||||||
|
// Place the page fault event onto table so that other functions [sys_mmapper_free_address and ppu pagefault funcs]
|
||||||
|
// know that this thread is page faulted and where.
|
||||||
|
|
||||||
|
auto pf_entries = fxm::get_always<page_fault_event_entries>();
|
||||||
|
{
|
||||||
|
semaphore_lock pf_lock(pf_entries->pf_mutex);
|
||||||
|
page_fault_event pf_event{ cpu->id, addr };
|
||||||
|
pf_entries->events.emplace_back(pf_event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, we notify the game that a page fault occurred so it can rectify it.
|
||||||
|
// Note, for data3, were the memory readable AND we got a page fault, it must be due to a write violation since reads are allowed.
|
||||||
|
be_t<u64> data1 = addr;
|
||||||
|
be_t<u64> data2 = (SYS_MEMORY_PAGE_FAULT_TYPE_PPU_THREAD << 32) + cpu->id; // TODO: fix hack for now that assumes PPU thread always.
|
||||||
|
be_t<u64> data3 = vm::check_addr(addr, a_size, vm::page_readable) ? SYS_MEMORY_PAGE_FAULT_CAUSE_READ_ONLY : SYS_MEMORY_PAGE_FAULT_CAUSE_NON_MAPPED;
|
||||||
|
|
||||||
|
LOG_ERROR(MEMORY, "Page_fault %s location 0x%x because of %s memory", is_writing ? "writing" : "reading",
|
||||||
|
addr, data3 == SYS_MEMORY_PAGE_FAULT_CAUSE_READ_ONLY ? "writing read-only" : "using unmapped");
|
||||||
|
|
||||||
|
error_code sending_error = sys_event_port_send(entry.port_id, data1, data2, data3);
|
||||||
|
|
||||||
|
// If we fail due to being busy, wait a bit and try again.
|
||||||
|
while (sending_error == CELL_EBUSY)
|
||||||
|
{
|
||||||
|
lv2_obj::sleep(*cpu, 1000);
|
||||||
|
thread_ctrl::wait_for(1000);
|
||||||
|
sending_error = sys_event_port_send(entry.port_id, data1, data2, data3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sending_error)
|
||||||
|
{
|
||||||
|
fmt::throw_exception("Unknown error %x while trying to pass page fault.", sending_error.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv2_obj::sleep(*cpu);
|
||||||
|
thread_ctrl::wait();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LOG_FATAL(MEMORY, "Access violation %s location 0x%x", is_writing ? "writing" : "reading", addr);
|
LOG_FATAL(MEMORY, "Access violation %s location 0x%x", is_writing ? "writing" : "reading", addr);
|
||||||
cpu->state += cpu_flag::dbg_pause;
|
cpu->state += cpu_flag::dbg_pause;
|
||||||
cpu->check_state();
|
cpu->check_state();
|
||||||
@@ -1216,7 +1363,8 @@ static bool is_leaf_function(u64 rip)
|
|||||||
|
|
||||||
static LONG exception_handler(PEXCEPTION_POINTERS pExp)
|
static LONG exception_handler(PEXCEPTION_POINTERS pExp)
|
||||||
{
|
{
|
||||||
const u64 addr64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
|
const u64 addr64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::g_base_addr;
|
||||||
|
const u64 exec64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::g_exec_addr;
|
||||||
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
|
const bool is_writing = pExp->ExceptionRecord->ExceptionInformation[0] != 0;
|
||||||
|
|
||||||
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && addr64 < 0x100000000ull)
|
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && addr64 < 0x100000000ull)
|
||||||
@@ -1227,6 +1375,14 @@ static LONG exception_handler(PEXCEPTION_POINTERS pExp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && exec64 < 0x100000000ull)
|
||||||
|
{
|
||||||
|
if (thread_ctrl::get_current() && handle_access_violation((u32)exec64, is_writing, pExp->ContextRecord))
|
||||||
|
{
|
||||||
|
return EXCEPTION_CONTINUE_EXECUTION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return EXCEPTION_CONTINUE_SEARCH;
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1236,7 +1392,6 @@ static LONG exception_filter(PEXCEPTION_POINTERS pExp)
|
|||||||
|
|
||||||
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
|
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
|
||||||
{
|
{
|
||||||
const u64 addr64 = pExp->ExceptionRecord->ExceptionInformation[1] - (u64)vm::base(0);
|
|
||||||
const auto cause = pExp->ExceptionRecord->ExceptionInformation[0] != 0 ? "writing" : "reading";
|
const auto cause = pExp->ExceptionRecord->ExceptionInformation[0] != 0 ? "writing" : "reading";
|
||||||
|
|
||||||
msg += fmt::format("Segfault %s location %p at %p.\n", cause, pExp->ExceptionRecord->ExceptionInformation[1], pExp->ExceptionRecord->ExceptionAddress);
|
msg += fmt::format("Segfault %s location %p at %p.\n", cause, pExp->ExceptionRecord->ExceptionInformation[1], pExp->ExceptionRecord->ExceptionAddress);
|
||||||
@@ -1304,13 +1459,6 @@ static LONG exception_filter(PEXCEPTION_POINTERS pExp)
|
|||||||
|
|
||||||
msg += fmt::format("RPCS3 image base: %p.\n", GetModuleHandle(NULL));
|
msg += fmt::format("RPCS3 image base: %p.\n", GetModuleHandle(NULL));
|
||||||
|
|
||||||
if (pExp->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION)
|
|
||||||
{
|
|
||||||
msg += "\n"
|
|
||||||
"Illegal instruction exception occured.\n"
|
|
||||||
"Note that your CPU must support SSSE3 extension.\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: print registers and the callstack
|
// TODO: print registers and the callstack
|
||||||
|
|
||||||
// Report fatal error
|
// Report fatal error
|
||||||
@@ -1323,13 +1471,11 @@ const bool s_exception_handler_set = []() -> bool
|
|||||||
if (!AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)exception_handler))
|
if (!AddVectoredExceptionHandler(1, (PVECTORED_EXCEPTION_HANDLER)exception_handler))
|
||||||
{
|
{
|
||||||
report_fatal_error("AddVectoredExceptionHandler() failed.");
|
report_fatal_error("AddVectoredExceptionHandler() failed.");
|
||||||
std::abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)exception_filter))
|
if (!SetUnhandledExceptionFilter((LPTOP_LEVEL_EXCEPTION_FILTER)exception_filter))
|
||||||
{
|
{
|
||||||
report_fatal_error("SetUnhandledExceptionFilter() failed.");
|
report_fatal_error("SetUnhandledExceptionFilter() failed.");
|
||||||
std::abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1343,11 +1489,18 @@ static void signal_handler(int sig, siginfo_t* info, void* uct)
|
|||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
const bool is_writing = context->uc_mcontext->__es.__err & 0x2;
|
const bool is_writing = context->uc_mcontext->__es.__err & 0x2;
|
||||||
|
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||||
|
const bool is_writing = context->uc_mcontext.mc_err & 0x2;
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
const bool is_writing = context->sc_err & 0x2;
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
const bool is_writing = context->uc_mcontext.__gregs[_REG_ERR] & 0x2;
|
||||||
#else
|
#else
|
||||||
const bool is_writing = context->uc_mcontext.gregs[REG_ERR] & 0x2;
|
const bool is_writing = context->uc_mcontext.gregs[REG_ERR] & 0x2;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const u64 addr64 = (u64)info->si_addr - (u64)vm::base(0);
|
const u64 addr64 = (u64)info->si_addr - (u64)vm::g_base_addr;
|
||||||
|
const u64 exec64 = (u64)info->si_addr - (u64)vm::g_exec_addr;
|
||||||
const auto cause = is_writing ? "writing" : "reading";
|
const auto cause = is_writing ? "writing" : "reading";
|
||||||
|
|
||||||
if (addr64 < 0x100000000ull)
|
if (addr64 < 0x100000000ull)
|
||||||
@@ -1359,9 +1512,16 @@ static void signal_handler(int sig, siginfo_t* info, void* uct)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (exec64 < 0x100000000ull)
|
||||||
|
{
|
||||||
|
if (thread_ctrl::get_current() && handle_access_violation((u32)exec64, is_writing, context))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO (debugger interaction)
|
// TODO (debugger interaction)
|
||||||
report_fatal_error(fmt::format("Segfault %s location %p at %p.", cause, info->si_addr, RIP(context)));
|
report_fatal_error(fmt::format("Segfault %s location %p at %p.", cause, info->si_addr, RIP(context)));
|
||||||
std::abort();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool s_exception_handler_set = []() -> bool
|
const bool s_exception_handler_set = []() -> bool
|
||||||
@@ -1389,6 +1549,8 @@ thread_local DECLARE(thread_ctrl::g_tls_this_thread) = nullptr;
|
|||||||
|
|
||||||
extern thread_local std::string(*g_tls_log_prefix)();
|
extern thread_local std::string(*g_tls_log_prefix)();
|
||||||
|
|
||||||
|
DECLARE(thread_ctrl::g_native_core_layout) { native_core_arrangement::undefined };
|
||||||
|
|
||||||
void thread_ctrl::start(const std::shared_ptr<thread_ctrl>& ctrl, task_stack task)
|
void thread_ctrl::start(const std::shared_ptr<thread_ctrl>& ctrl, task_stack task)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -1434,7 +1596,7 @@ void thread_ctrl::start(const std::shared_ptr<thread_ctrl>& ctrl, task_stack tas
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: this is unsafe and must be duplicated in thread_ctrl::initialize
|
// TODO: this is unsafe and must be duplicated in thread_ctrl::initialize
|
||||||
ctrl->m_thread = thread;
|
ctrl->m_thread = (uintptr_t)thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
void thread_ctrl::initialize()
|
void thread_ctrl::initialize()
|
||||||
@@ -1490,7 +1652,7 @@ void thread_ctrl::finalize(std::exception_ptr eptr) noexcept
|
|||||||
FILETIME ctime, etime, ktime, utime;
|
FILETIME ctime, etime, ktime, utime;
|
||||||
GetThreadTimes(GetCurrentThread(), &ctime, &etime, &ktime, &utime);
|
GetThreadTimes(GetCurrentThread(), &ctime, &etime, &ktime, &utime);
|
||||||
const u64 time = ((ktime.dwLowDateTime | (u64)ktime.dwHighDateTime << 32) + (utime.dwLowDateTime | (u64)utime.dwHighDateTime << 32)) * 100ull;
|
const u64 time = ((ktime.dwLowDateTime | (u64)ktime.dwHighDateTime << 32) + (utime.dwLowDateTime | (u64)utime.dwHighDateTime << 32)) * 100ull;
|
||||||
#elif __linux__
|
#elif defined(RUSAGE_THREAD)
|
||||||
const u64 cycles = 0; // Not supported
|
const u64 cycles = 0; // Not supported
|
||||||
struct ::rusage stats{};
|
struct ::rusage stats{};
|
||||||
::getrusage(RUSAGE_THREAD, &stats);
|
::getrusage(RUSAGE_THREAD, &stats);
|
||||||
@@ -1500,6 +1662,11 @@ void thread_ctrl::finalize(std::exception_ptr eptr) noexcept
|
|||||||
const u64 time = 0;
|
const u64 time = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
g_tls_log_prefix = []
|
||||||
|
{
|
||||||
|
return g_tls_this_thread->m_name;
|
||||||
|
};
|
||||||
|
|
||||||
LOG_NOTICE(GENERAL, "Thread time: %fs (%fGc); Faults: %u [rsx:%u, spu:%u];",
|
LOG_NOTICE(GENERAL, "Thread time: %fs (%fGc); Faults: %u [rsx:%u, spu:%u];",
|
||||||
time / 1000000000.,
|
time / 1000000000.,
|
||||||
cycles / 1000000000.,
|
cycles / 1000000000.,
|
||||||
@@ -1580,7 +1747,7 @@ bool thread_ctrl::_wait_for(u64 usec)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (_this->m_cond.wait(_lock, std::exchange(usec, usec == -1 ? -1 : 0)));
|
while (_this->m_cond.wait(_lock, std::exchange(usec, usec > cond_variable::max_timeout ? -1 : 0)));
|
||||||
|
|
||||||
// Timeout
|
// Timeout
|
||||||
return false;
|
return false;
|
||||||
@@ -1618,7 +1785,7 @@ thread_ctrl::~thread_ctrl()
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
CloseHandle((HANDLE)m_thread.raw());
|
CloseHandle((HANDLE)m_thread.raw());
|
||||||
#else
|
#else
|
||||||
pthread_detach(m_thread.raw());
|
pthread_detach((pthread_t)m_thread.raw());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1690,6 +1857,155 @@ void thread_ctrl::test()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void thread_ctrl::detect_cpu_layout()
|
||||||
|
{
|
||||||
|
if (!g_native_core_layout.compare_and_swap_test(native_core_arrangement::undefined, native_core_arrangement::generic))
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto system_id = utils::get_system_info();
|
||||||
|
if (system_id.find("Ryzen") != std::string::npos)
|
||||||
|
{
|
||||||
|
g_native_core_layout.store(native_core_arrangement::amd_ccx);
|
||||||
|
}
|
||||||
|
else if (system_id.find("i3") != std::string::npos || system_id.find("i7") != std::string::npos)
|
||||||
|
{
|
||||||
|
g_native_core_layout.store(native_core_arrangement::intel_ht);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 thread_ctrl::get_affinity_mask(thread_class group)
|
||||||
|
{
|
||||||
|
detect_cpu_layout();
|
||||||
|
|
||||||
|
if (const auto thread_count = std::thread::hardware_concurrency())
|
||||||
|
{
|
||||||
|
const u16 all_cores_mask = thread_count < 16 ? (u16)(~(UINT16_MAX << thread_count)): UINT16_MAX;
|
||||||
|
|
||||||
|
switch (g_native_core_layout)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case native_core_arrangement::generic:
|
||||||
|
{
|
||||||
|
return all_cores_mask;
|
||||||
|
}
|
||||||
|
case native_core_arrangement::amd_ccx:
|
||||||
|
{
|
||||||
|
u16 spu_mask, ppu_mask, rsx_mask;
|
||||||
|
if (thread_count >= 16)
|
||||||
|
{
|
||||||
|
// Threadripper, R7
|
||||||
|
// Assign threads 8-16
|
||||||
|
// It appears some windows code is bound to lower core addresses, binding 8-16 is alot faster than 0-7
|
||||||
|
ppu_mask = spu_mask = 0b1111111100000000;
|
||||||
|
rsx_mask = all_cores_mask;
|
||||||
|
}
|
||||||
|
else if (thread_count == 12)
|
||||||
|
{
|
||||||
|
// 1600/2600 (x)
|
||||||
|
ppu_mask = spu_mask = 0b111111000000;
|
||||||
|
rsx_mask = all_cores_mask;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// R5 & R3 don't seem to improve performance no matter how these are shuffled
|
||||||
|
ppu_mask = spu_mask = rsx_mask = 0b11111111 & all_cores_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (group)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case thread_class::general:
|
||||||
|
return all_cores_mask;
|
||||||
|
case thread_class::rsx:
|
||||||
|
return rsx_mask;
|
||||||
|
case thread_class::ppu:
|
||||||
|
return ppu_mask;
|
||||||
|
case thread_class::spu:
|
||||||
|
return spu_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case native_core_arrangement::intel_ht:
|
||||||
|
{
|
||||||
|
if (thread_count <= 4)
|
||||||
|
{
|
||||||
|
//i3 or worse
|
||||||
|
switch (group)
|
||||||
|
{
|
||||||
|
case thread_class::rsx:
|
||||||
|
case thread_class::ppu:
|
||||||
|
return (0b0101 & all_cores_mask);
|
||||||
|
case thread_class::spu:
|
||||||
|
return (0b1010 & all_cores_mask);
|
||||||
|
case thread_class::general:
|
||||||
|
return all_cores_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_cores_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return UINT16_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread_ctrl::set_native_priority(int priority)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE _this_thread = GetCurrentThread();
|
||||||
|
INT native_priority = THREAD_PRIORITY_NORMAL;
|
||||||
|
|
||||||
|
if (priority > 0)
|
||||||
|
native_priority = THREAD_PRIORITY_ABOVE_NORMAL;
|
||||||
|
if (priority < 0)
|
||||||
|
native_priority = THREAD_PRIORITY_BELOW_NORMAL;
|
||||||
|
|
||||||
|
if (!SetThreadPriority(_this_thread, native_priority))
|
||||||
|
{
|
||||||
|
LOG_ERROR(GENERAL, "SetThreadPriority() failed: 0x%x", GetLastError());
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int policy;
|
||||||
|
struct sched_param param;
|
||||||
|
|
||||||
|
pthread_getschedparam(pthread_self(), &policy, ¶m);
|
||||||
|
|
||||||
|
if (priority > 0)
|
||||||
|
param.sched_priority = sched_get_priority_max(policy);
|
||||||
|
if (priority < 0)
|
||||||
|
param.sched_priority = sched_get_priority_min(policy);
|
||||||
|
|
||||||
|
if (int err = pthread_setschedparam(pthread_self(), policy, ¶m))
|
||||||
|
{
|
||||||
|
LOG_ERROR(GENERAL, "pthraed_setschedparam() failed: %d", err);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void thread_ctrl::set_thread_affinity_mask(u16 mask)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE _this_thread = GetCurrentThread();
|
||||||
|
SetThreadAffinityMask(_this_thread, (DWORD_PTR)mask);
|
||||||
|
#elif __APPLE__
|
||||||
|
thread_affinity_policy_data_t policy = { static_cast<integer_t>(mask) };
|
||||||
|
thread_port_t mach_thread = pthread_mach_thread_np(pthread_self());
|
||||||
|
thread_policy_set(mach_thread, THREAD_AFFINITY_POLICY, (thread_policy_t)&policy, 1);
|
||||||
|
#elif defined(__linux__) || defined(__DragonFly__) || defined(__FreeBSD__)
|
||||||
|
cpu_set_t cs;
|
||||||
|
CPU_ZERO(&cs);
|
||||||
|
|
||||||
|
for (u32 core = 0; core < 16u; ++core)
|
||||||
|
{
|
||||||
|
if ((u32)mask & (1u << core))
|
||||||
|
{
|
||||||
|
CPU_SET(core, &cs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cs);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
named_thread::named_thread()
|
named_thread::named_thread()
|
||||||
{
|
{
|
||||||
@@ -1715,6 +2031,7 @@ void named_thread::start_thread(const std::shared_ptr<void>& _this)
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
LOG_TRACE(GENERAL, "Thread started");
|
LOG_TRACE(GENERAL, "Thread started");
|
||||||
|
on_spawn();
|
||||||
on_task();
|
on_task();
|
||||||
LOG_TRACE(GENERAL, "Thread ended");
|
LOG_TRACE(GENERAL, "Thread ended");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,29 @@
|
|||||||
#include "sema.h"
|
#include "sema.h"
|
||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
|
|
||||||
|
// Report error and call std::abort(), defined in main.cpp
|
||||||
|
[[noreturn]] void report_fatal_error(const std::string&);
|
||||||
|
|
||||||
// Will report exception and call std::abort() if put in catch(...)
|
// Will report exception and call std::abort() if put in catch(...)
|
||||||
[[noreturn]] void catch_all_exceptions();
|
[[noreturn]] void catch_all_exceptions();
|
||||||
|
|
||||||
|
// Hardware core layout
|
||||||
|
enum class native_core_arrangement : u32
|
||||||
|
{
|
||||||
|
undefined,
|
||||||
|
generic,
|
||||||
|
intel_ht,
|
||||||
|
amd_ccx
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class thread_class : u32
|
||||||
|
{
|
||||||
|
general,
|
||||||
|
rsx,
|
||||||
|
spu,
|
||||||
|
ppu
|
||||||
|
};
|
||||||
|
|
||||||
// Simple list of void() functors
|
// Simple list of void() functors
|
||||||
class task_stack
|
class task_stack
|
||||||
{
|
{
|
||||||
@@ -88,6 +108,9 @@ class thread_ctrl final
|
|||||||
// Current thread
|
// Current thread
|
||||||
static thread_local thread_ctrl* g_tls_this_thread;
|
static thread_local thread_ctrl* g_tls_this_thread;
|
||||||
|
|
||||||
|
// Target cpu core layout
|
||||||
|
static atomic_t<native_core_arrangement> g_native_core_layout;
|
||||||
|
|
||||||
// Self pointer
|
// Self pointer
|
||||||
std::shared_ptr<thread_ctrl> m_self;
|
std::shared_ptr<thread_ctrl> m_self;
|
||||||
|
|
||||||
@@ -230,6 +253,18 @@ public:
|
|||||||
|
|
||||||
thread_ctrl::start(out, std::forward<F>(func));
|
thread_ctrl::start(out, std::forward<F>(func));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect layout
|
||||||
|
static void detect_cpu_layout();
|
||||||
|
|
||||||
|
// Returns a core affinity mask. Set whether to generate the high priority set or not
|
||||||
|
static u16 get_affinity_mask(thread_class group);
|
||||||
|
|
||||||
|
// Sets the native thread priority
|
||||||
|
static void set_native_priority(int priority);
|
||||||
|
|
||||||
|
// Sets the preferred affinity mask for this thread
|
||||||
|
static void set_thread_affinity_mask(u16 mask);
|
||||||
};
|
};
|
||||||
|
|
||||||
class named_thread
|
class named_thread
|
||||||
@@ -258,6 +293,9 @@ protected:
|
|||||||
// Thread finalization (called after on_task)
|
// Thread finalization (called after on_task)
|
||||||
virtual void on_exit() {}
|
virtual void on_exit() {}
|
||||||
|
|
||||||
|
// Called once upon thread spawn within the thread's own context
|
||||||
|
virtual void on_spawn() {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// ID initialization
|
// ID initialization
|
||||||
virtual void on_init(const std::shared_ptr<void>& _this)
|
virtual void on_init(const std::shared_ptr<void>& _this)
|
||||||
|
|||||||
@@ -11,32 +11,78 @@
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace memory_helper
|
namespace utils
|
||||||
{
|
{
|
||||||
void* reserve_memory(std::size_t size)
|
// Convert memory protection (internal)
|
||||||
|
static auto operator +(protection prot)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return verify("reserve_memory" HERE, VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS));
|
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
|
#else
|
||||||
return verify("reserve_memory" HERE, ::mmap(nullptr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0));
|
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
|
||||||
|
auto ptr = ::mmap(use_addr, size, PROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||||
|
|
||||||
|
if (use_addr && ptr != use_addr)
|
||||||
|
{
|
||||||
|
::munmap(ptr, size);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void commit_page_memory(void* pointer, std::size_t size)
|
void memory_commit(void* pointer, std::size_t size, protection prot)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
verify(HERE), VirtualAlloc(pointer, size, MEM_COMMIT, PAGE_READWRITE);
|
verify(HERE), ::VirtualAlloc(pointer, size, MEM_COMMIT, +prot);
|
||||||
#else
|
#else
|
||||||
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), PROT_READ | PROT_WRITE) != -1;
|
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void free_reserved_memory(void* pointer, std::size_t size)
|
void memory_decommit(void* pointer, std::size_t size)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
verify(HERE), VirtualFree(pointer, 0, MEM_DECOMMIT);
|
verify(HERE), ::VirtualFree(pointer, size, MEM_DECOMMIT);
|
||||||
#else
|
#else
|
||||||
verify(HERE), ::mmap(pointer, size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
verify(HERE), ::mmap(pointer, size, PROT_NONE, MAP_FIXED | MAP_ANON | MAP_PRIVATE, -1, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void memory_protect(void* pointer, std::size_t size, protection prot)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
DWORD old;
|
||||||
|
verify(HERE), ::VirtualProtect(pointer, size, +prot, &old);
|
||||||
|
#else
|
||||||
|
verify(HERE), ::mprotect((void*)((u64)pointer & -4096), ::align(size, 4096), +prot) != -1;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,35 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace memory_helper
|
namespace utils
|
||||||
{
|
{
|
||||||
|
// Memory protection type
|
||||||
|
enum class protection
|
||||||
|
{
|
||||||
|
rw, // Read + write (default)
|
||||||
|
ro, // Read only
|
||||||
|
no, // No access
|
||||||
|
wx, // Read + write + execute
|
||||||
|
rx, // Read + execute
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reserve `size` bytes of virtual memory and returns it.
|
* Reserve `size` bytes of virtual memory and returns it.
|
||||||
* The memory should be commited before usage.
|
* The memory should be commited before usage.
|
||||||
*/
|
*/
|
||||||
void* reserve_memory(std::size_t size);
|
void* memory_reserve(std::size_t size, void* use_addr = nullptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commit `size` bytes of virtual memory starting at pointer.
|
* Commit `size` bytes of virtual memory starting at pointer.
|
||||||
* That is, bake reserved memory with physical memory.
|
* That is, bake reserved memory with physical memory.
|
||||||
* pointer should belong to a range of reserved memory.
|
* pointer should belong to a range of reserved memory.
|
||||||
*/
|
*/
|
||||||
void commit_page_memory(void* pointer, std::size_t size);
|
void memory_commit(void* pointer, std::size_t size, protection prot = protection::rw);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decommit all memory committed via commit_page_memory.
|
* Decommit all memory committed via commit_page_memory.
|
||||||
*/
|
*/
|
||||||
void free_reserved_memory(void* pointer, std::size_t size);
|
void memory_decommit(void* pointer, std::size_t size);
|
||||||
|
|
||||||
|
// Set memory protection
|
||||||
|
void memory_protect(void* pointer, std::size_t size, protection prot);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
#include "bin_patch.h"
|
||||||
|
#include "yaml-cpp/yaml.h"
|
||||||
|
#include "File.h"
|
||||||
|
#include "Config.h"
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void fmt_class_string<patch_type>::format(std::string& out, u64 arg)
|
||||||
|
{
|
||||||
|
format_enum(out, arg, [](patch_type value)
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case patch_type::load: return "load";
|
||||||
|
case patch_type::byte: return "byte";
|
||||||
|
case patch_type::le16: return "le16";
|
||||||
|
case patch_type::le32: return "le32";
|
||||||
|
case patch_type::le64: return "le64";
|
||||||
|
case patch_type::bef32: return "bef32";
|
||||||
|
case patch_type::bef64: return "bef64";
|
||||||
|
case patch_type::be16: return "be16";
|
||||||
|
case patch_type::be32: return "be32";
|
||||||
|
case patch_type::be64: return "be64";
|
||||||
|
case patch_type::lef32: return "lef32";
|
||||||
|
case patch_type::lef64: return "lef64";
|
||||||
|
}
|
||||||
|
|
||||||
|
return unknown;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void patch_engine::append(const std::string& patch)
|
||||||
|
{
|
||||||
|
if (fs::file f{patch})
|
||||||
|
{
|
||||||
|
auto root = YAML::Load(f.to_string());
|
||||||
|
|
||||||
|
for (auto pair : root)
|
||||||
|
{
|
||||||
|
auto& name = pair.first.Scalar();
|
||||||
|
auto& data = m_map[name];
|
||||||
|
|
||||||
|
for (auto patch : pair.second)
|
||||||
|
{
|
||||||
|
u64 type64 = 0;
|
||||||
|
cfg::try_to_enum_value(&type64, &fmt_class_string<patch_type>::format, patch[0].Scalar());
|
||||||
|
|
||||||
|
struct patch info{};
|
||||||
|
info.type = static_cast<patch_type>(type64);
|
||||||
|
info.offset = patch[1].as<u32>(0);
|
||||||
|
|
||||||
|
switch (info.type)
|
||||||
|
{
|
||||||
|
case patch_type::load:
|
||||||
|
{
|
||||||
|
// Special syntax: copy named sequence (must be loaded before)
|
||||||
|
const auto found = m_map.find(patch[1].Scalar());
|
||||||
|
|
||||||
|
if (found != m_map.end())
|
||||||
|
{
|
||||||
|
// Address modifier (optional)
|
||||||
|
const u32 mod = patch[2].as<u32>(0);
|
||||||
|
|
||||||
|
for (const auto& rd : found->second)
|
||||||
|
{
|
||||||
|
info = rd;
|
||||||
|
info.offset += mod;
|
||||||
|
data.emplace_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: error
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case patch_type::bef32:
|
||||||
|
case patch_type::lef32:
|
||||||
|
{
|
||||||
|
info.value_as<f32>() = patch[2].as<f32>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case patch_type::bef64:
|
||||||
|
case patch_type::lef64:
|
||||||
|
{
|
||||||
|
info.value_as<f64>() = patch[2].as<f64>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
info.value = patch[2].as<u64>();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data.emplace_back(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t patch_engine::apply(const std::string& name, u8* dst) const
|
||||||
|
{
|
||||||
|
const auto found = m_map.find(name);
|
||||||
|
|
||||||
|
if (found == m_map.cend())
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply modifications sequentially
|
||||||
|
for (const auto& p : found->second)
|
||||||
|
{
|
||||||
|
auto ptr = dst + p.offset;
|
||||||
|
|
||||||
|
switch (p.type)
|
||||||
|
{
|
||||||
|
case patch_type::load:
|
||||||
|
{
|
||||||
|
// Invalid in this context
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case patch_type::byte:
|
||||||
|
{
|
||||||
|
*ptr = static_cast<u8>(p.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case patch_type::le16:
|
||||||
|
{
|
||||||
|
*reinterpret_cast<le_t<u16, 1>*>(ptr) = static_cast<u16>(p.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case patch_type::le32:
|
||||||
|
case patch_type::lef32:
|
||||||
|
{
|
||||||
|
*reinterpret_cast<le_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case patch_type::le64:
|
||||||
|
case patch_type::lef64:
|
||||||
|
{
|
||||||
|
*reinterpret_cast<le_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case patch_type::be16:
|
||||||
|
{
|
||||||
|
*reinterpret_cast<be_t<u16, 1>*>(ptr) = static_cast<u16>(p.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case patch_type::be32:
|
||||||
|
case patch_type::bef32:
|
||||||
|
{
|
||||||
|
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case patch_type::be64:
|
||||||
|
case patch_type::bef64:
|
||||||
|
{
|
||||||
|
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return found->second.size();
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "BEType.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
enum class patch_type
|
||||||
|
{
|
||||||
|
load,
|
||||||
|
byte,
|
||||||
|
le16,
|
||||||
|
le32,
|
||||||
|
le64,
|
||||||
|
lef32,
|
||||||
|
lef64,
|
||||||
|
be16,
|
||||||
|
be32,
|
||||||
|
be64,
|
||||||
|
bef32,
|
||||||
|
bef64,
|
||||||
|
};
|
||||||
|
|
||||||
|
class patch_engine
|
||||||
|
{
|
||||||
|
struct patch
|
||||||
|
{
|
||||||
|
patch_type type;
|
||||||
|
u32 offset;
|
||||||
|
u64 value;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& value_as()
|
||||||
|
{
|
||||||
|
return *reinterpret_cast<T*>(reinterpret_cast<char*>(&value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Database
|
||||||
|
std::unordered_map<std::string, std::vector<patch>> m_map;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Load from file
|
||||||
|
void append(const std::string& path);
|
||||||
|
|
||||||
|
// Apply patch (returns the number of entries applied)
|
||||||
|
std::size_t apply(const std::string& name, u8* dst) const;
|
||||||
|
};
|
||||||
@@ -204,11 +204,11 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
|||||||
else if (fmt[0] == 'h')
|
else if (fmt[0] == 'h')
|
||||||
{
|
{
|
||||||
fmt++, ctx.size++;
|
fmt++, ctx.size++;
|
||||||
ctx.type = sizeof(char);
|
ctx.type = src.size_char;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ctx.type = sizeof(short);
|
ctx.type = src.size_short;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -223,11 +223,11 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
|||||||
else if (fmt[0] == 'l')
|
else if (fmt[0] == 'l')
|
||||||
{
|
{
|
||||||
fmt++, ctx.size++;
|
fmt++, ctx.size++;
|
||||||
ctx.type = sizeof(llong);
|
ctx.type = src.size_llong;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ctx.type = sizeof(long);
|
ctx.type = src.size_long;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -241,7 +241,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ctx.type = sizeof(std::size_t);
|
ctx.type = src.size_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -255,7 +255,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ctx.type = sizeof(std::intmax_t);
|
ctx.type = src.size_max;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -269,7 +269,7 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ctx.type = sizeof(std::ptrdiff_t);
|
ctx.type = src.size_diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -339,6 +339,11 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
|||||||
if (!ctx.type)
|
if (!ctx.type)
|
||||||
{
|
{
|
||||||
ctx.type = (u8)src.type(ctx.args);
|
ctx.type = (u8)src.type(ctx.args);
|
||||||
|
|
||||||
|
if (!ctx.type)
|
||||||
|
{
|
||||||
|
ctx.type = src.size_int;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign-extended argument expected
|
// Sign-extended argument expected
|
||||||
@@ -396,6 +401,11 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
|||||||
if (!ctx.type)
|
if (!ctx.type)
|
||||||
{
|
{
|
||||||
ctx.type = (u8)src.type(ctx.args);
|
ctx.type = (u8)src.type(ctx.args);
|
||||||
|
|
||||||
|
if (!ctx.type)
|
||||||
|
{
|
||||||
|
ctx.type = src.size_int;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 mask =
|
const u64 mask =
|
||||||
@@ -447,6 +457,11 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
|||||||
if (!ctx.type)
|
if (!ctx.type)
|
||||||
{
|
{
|
||||||
ctx.type = (u8)src.type(ctx.args);
|
ctx.type = (u8)src.type(ctx.args);
|
||||||
|
|
||||||
|
if (!ctx.type)
|
||||||
|
{
|
||||||
|
ctx.type = src.size_int;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 mask =
|
const u64 mask =
|
||||||
@@ -505,6 +520,11 @@ std::size_t cfmt_append(Dst& out, const Char* fmt, Src&& src)
|
|||||||
if (!ctx.type)
|
if (!ctx.type)
|
||||||
{
|
{
|
||||||
ctx.type = (u8)src.type(ctx.args);
|
ctx.type = (u8)src.type(ctx.args);
|
||||||
|
|
||||||
|
if (!ctx.type)
|
||||||
|
{
|
||||||
|
ctx.type = src.size_int;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 mask =
|
const u64 mask =
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include "cond.h"
|
#include "cond.h"
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
|
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#endif
|
#endif
|
||||||
@@ -8,12 +10,13 @@
|
|||||||
bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
|
bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
|
||||||
{
|
{
|
||||||
verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously
|
verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously
|
||||||
|
const bool is_inf = _timeout > max_timeout;
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
LARGE_INTEGER timeout;
|
LARGE_INTEGER timeout;
|
||||||
timeout.QuadPart = _timeout * -10;
|
timeout.QuadPart = _timeout * -10;
|
||||||
|
|
||||||
if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, _timeout == -1 ? nullptr : &timeout))
|
if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, is_inf ? nullptr : &timeout))
|
||||||
{
|
{
|
||||||
verify(HERE), rc == WAIT_TIMEOUT;
|
verify(HERE), rc == WAIT_TIMEOUT;
|
||||||
|
|
||||||
@@ -35,12 +38,12 @@ bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept
|
|||||||
|
|
||||||
for (u32 value = _old + 1;; value = m_value)
|
for (u32 value = _old + 1;; value = m_value)
|
||||||
{
|
{
|
||||||
const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, _timeout == -1 ? nullptr : &timeout, nullptr, 0) == 0
|
const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, is_inf ? nullptr : &timeout, nullptr, 0) == 0
|
||||||
? 0
|
? 0
|
||||||
: errno;
|
: errno;
|
||||||
|
|
||||||
// Normal or timeout wakeup
|
// Normal or timeout wakeup
|
||||||
if (!err || (_timeout != -1 && err == ETIMEDOUT))
|
if (!err || (!is_inf && err == ETIMEDOUT))
|
||||||
{
|
{
|
||||||
// Cleanup (remove waiter)
|
// Cleanup (remove waiter)
|
||||||
verify(HERE), m_value--;
|
verify(HERE), m_value--;
|
||||||
|
|||||||
@@ -47,4 +47,6 @@ public:
|
|||||||
imp_wake(-1);
|
imp_wake(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr u64 max_timeout = u64{UINT32_MAX} / 1000 * 1000000;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
Copyright (C) 2011-2012 Andrzej Krzemienski
|
|
||||||
|
|
||||||
Distributed under the Boost Software License, Version 1.0
|
|
||||||
(see accompanying file LICENSE_1_0.txt or a copy at
|
|
||||||
http://www.boost.org/LICENSE_1_0.txt)
|
|
||||||
|
|
||||||
The idea and interface is based on Boost.Optional library
|
|
||||||
authored by Fernando Luis Cacciola Carballal
|
|
||||||
|
|
||||||
Home at https://github.com/akrzemi1/Optional
|
|
||||||
@@ -731,9 +731,9 @@ struct area_base
|
|||||||
{
|
{
|
||||||
return{ x1 * size.width, y1 * size.height, x2 * size.width, y2 * size.height };
|
return{ x1 * size.width, y1 * size.height, x2 * size.width, y2 * size.height };
|
||||||
}
|
}
|
||||||
constexpr area_base operator * (const T& value) const
|
constexpr area_base operator * (const f32& value) const
|
||||||
{
|
{
|
||||||
return{ x1 * value, y1 * value, x2 * value, y2 * value };
|
return{ (T)(x1 * value), (T)(y1 * value), (T)(x2 * value), (T)(y2 * value) };
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename NT>
|
template<typename NT>
|
||||||
|
|||||||
@@ -13,9 +13,10 @@ rem // A copy of the GPL 2.0 should have been included with the program.
|
|||||||
rem // If not, see http://www.gnu.org/licenses/
|
rem // If not, see http://www.gnu.org/licenses/
|
||||||
|
|
||||||
rem // Official git repository and contact information can be found at
|
rem // Official git repository and contact information can be found at
|
||||||
rem // https://github.com/RPCS3/rpcs3 and http://rpcs3.net/.
|
rem // https://github.com/RPCS3/rpcs3 and https://rpcs3.net/.
|
||||||
|
|
||||||
setlocal ENABLEDELAYEDEXPANSION
|
setlocal ENABLEDELAYEDEXPANSION
|
||||||
|
setlocal ENABLEEXTENSIONS
|
||||||
|
|
||||||
set GIT_VERSION_FILE=%~p0..\rpcs3\git-version.h
|
set GIT_VERSION_FILE=%~p0..\rpcs3\git-version.h
|
||||||
if not defined GIT (
|
if not defined GIT (
|
||||||
@@ -47,14 +48,40 @@ if errorlevel 1 (
|
|||||||
echo // This is a generated file. > "%GIT_VERSION_FILE%"
|
echo // This is a generated file. > "%GIT_VERSION_FILE%"
|
||||||
echo. >> "%GIT_VERSION_FILE%"
|
echo. >> "%GIT_VERSION_FILE%"
|
||||||
echo #define RPCS3_GIT_VERSION "unknown" >> "%GIT_VERSION_FILE%"
|
echo #define RPCS3_GIT_VERSION "unknown" >> "%GIT_VERSION_FILE%"
|
||||||
|
echo #define RPCS3_GIT_BRANCH "unknown" >> "%GIT_VERSION_FILE%"
|
||||||
echo. >> "%GIT_VERSION_FILE%"
|
echo. >> "%GIT_VERSION_FILE%"
|
||||||
echo // If you don't want this file to update/recompile, change to 1. >> "%GIT_VERSION_FILE%"
|
echo // If you don't want this file to update/recompile, change to 1. >> "%GIT_VERSION_FILE%"
|
||||||
echo #define RPCS3_GIT_VERSION_NO_UPDATE 0 >> "%GIT_VERSION_FILE%"
|
echo #define RPCS3_GIT_VERSION_NO_UPDATE 0 >> "%GIT_VERSION_FILE%"
|
||||||
goto done
|
goto done
|
||||||
)
|
)
|
||||||
|
|
||||||
for /F %%I IN ('call %GIT% rev-list HEAD --count') do set GIT_VERSION=%%I
|
rem // Get commit count from (unshallowed) HEAD
|
||||||
for /F %%I IN ('call %GIT% rev-parse --short HEAD') do set GIT_VERSION=%GIT_VERSION%-%%I
|
for /F %%I IN ('call %GIT% rev-list HEAD --count') do set COMMIT_COUNT=%%I
|
||||||
|
|
||||||
|
rem // If we're in AppVeyor, building a non-master, pull request artifact
|
||||||
|
if defined APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH (
|
||||||
|
|
||||||
|
if "%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%"=="master" (
|
||||||
|
rem // If pull request comes from a master branch, GIT_BRANCH = username/branch in order to distinguish from upstream/master
|
||||||
|
for /f "tokens=1* delims=/" %%a in ("%APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME%") do set user=%%a
|
||||||
|
set "GIT_BRANCH=!user!/%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%"
|
||||||
|
) else (
|
||||||
|
rem // Otherwise, GIT_BRANCH=branch
|
||||||
|
set GIT_BRANCH=%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%
|
||||||
|
)
|
||||||
|
|
||||||
|
rem // Make GIT_VERSION the last commit (shortened); Don't include commit count on non-master builds
|
||||||
|
for /F %%I IN ('call %GIT% rev-parse --short HEAD') do set GIT_VERSION=%%I
|
||||||
|
|
||||||
|
) else (
|
||||||
|
rem // Get last commit (shortened) and concat after commit count in GIT_VERSION
|
||||||
|
for /F %%I IN ('call %GIT% rev-parse --short HEAD') do set GIT_VERSION=%COMMIT_COUNT%-%%I
|
||||||
|
|
||||||
|
for /F %%I IN ('call %GIT% rev-parse --abbrev-ref HEAD') do set GIT_BRANCH=%%I
|
||||||
|
)
|
||||||
|
|
||||||
|
rem // Echo obtained GIT_VERSION for debug purposes if needed
|
||||||
|
echo %GIT_VERSION%
|
||||||
|
|
||||||
rem // Don't modify the file if it already has the current version.
|
rem // Don't modify the file if it already has the current version.
|
||||||
if exist "%GIT_VERSION_FILE%" (
|
if exist "%GIT_VERSION_FILE%" (
|
||||||
@@ -67,6 +94,7 @@ if exist "%GIT_VERSION_FILE%" (
|
|||||||
echo // This is a generated file. > "%GIT_VERSION_FILE%"
|
echo // This is a generated file. > "%GIT_VERSION_FILE%"
|
||||||
echo. >> "%GIT_VERSION_FILE%"
|
echo. >> "%GIT_VERSION_FILE%"
|
||||||
echo #define RPCS3_GIT_VERSION "%GIT_VERSION%" >> "%GIT_VERSION_FILE%"
|
echo #define RPCS3_GIT_VERSION "%GIT_VERSION%" >> "%GIT_VERSION_FILE%"
|
||||||
|
echo #define RPCS3_GIT_BRANCH ^"%GIT_BRANCH%^" >> "%GIT_VERSION_FILE%"
|
||||||
echo. >> "%GIT_VERSION_FILE%"
|
echo. >> "%GIT_VERSION_FILE%"
|
||||||
echo // If you don't want this file to update/recompile, change to 1. >> "%GIT_VERSION_FILE%"
|
echo // If you don't want this file to update/recompile, change to 1. >> "%GIT_VERSION_FILE%"
|
||||||
echo #define RPCS3_GIT_VERSION_NO_UPDATE 0 >> "%GIT_VERSION_FILE%"
|
echo #define RPCS3_GIT_VERSION_NO_UPDATE 0 >> "%GIT_VERSION_FILE%"
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace rpcs3
|
||||||
|
{
|
||||||
|
template<typename T>
|
||||||
|
static size_t hash_base(T value)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
static size_t hash_struct(const T& value)
|
||||||
|
{
|
||||||
|
// FNV 64-bit
|
||||||
|
size_t result = 14695981039346656037ull;
|
||||||
|
const unsigned char *bytes = reinterpret_cast<const unsigned char*>(&value);
|
||||||
|
|
||||||
|
for (size_t n = 0; n < sizeof(T); ++n)
|
||||||
|
{
|
||||||
|
result ^= bytes[n];
|
||||||
|
result *= 1099511628211ull;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
#include "mutex.h"
|
#include "mutex.h"
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
// TLS variable for tracking owned mutexes
|
||||||
|
thread_local std::vector<shared_mutex*> g_tls_locks;
|
||||||
|
|
||||||
void shared_mutex::imp_lock_shared(s64 _old)
|
void shared_mutex::imp_lock_shared(s64 _old)
|
||||||
{
|
{
|
||||||
verify("shared_mutex overflow" HERE), _old <= c_max;
|
verify("shared_mutex overflow" HERE), _old <= c_max;
|
||||||
@@ -21,9 +28,12 @@ void shared_mutex::imp_lock_shared(s64 _old)
|
|||||||
// Acquire writer lock
|
// Acquire writer lock
|
||||||
imp_wait(m_value.load());
|
imp_wait(m_value.load());
|
||||||
|
|
||||||
// Convert value
|
// Convert to reader lock
|
||||||
s64 value = m_value.fetch_add(c_one - c_min);
|
s64 value = m_value.fetch_add(c_one - c_min);
|
||||||
|
|
||||||
|
// Proceed exclusively
|
||||||
|
return;
|
||||||
|
|
||||||
if (value != 0)
|
if (value != 0)
|
||||||
{
|
{
|
||||||
imp_unlock(value);
|
imp_unlock(value);
|
||||||
@@ -35,7 +45,14 @@ void shared_mutex::imp_lock_shared(s64 _old)
|
|||||||
NtWaitForKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
NtWaitForKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
while (true)
|
// Acquire writer lock
|
||||||
|
imp_wait(0);
|
||||||
|
|
||||||
|
// Convert to reader lock
|
||||||
|
m_value += c_one - c_min;
|
||||||
|
|
||||||
|
// Disabled code
|
||||||
|
while (false)
|
||||||
{
|
{
|
||||||
const s64 value0 = m_value.fetch_op([](s64& value)
|
const s64 value0 = m_value.fetch_op([](s64& value)
|
||||||
{
|
{
|
||||||
@@ -63,13 +80,13 @@ void shared_mutex::imp_lock_shared(s64 _old)
|
|||||||
|
|
||||||
value1 += c_one - c_min;
|
value1 += c_one - c_min;
|
||||||
|
|
||||||
if (value1 >= 0)
|
if (value1 > 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait as a reader if necessary
|
// Wait as a reader if necessary
|
||||||
while (futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_PRIVATE, int(value1 >> 32), nullptr, nullptr, 0))
|
while (futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_BITSET_PRIVATE, int(value1 >> 32), nullptr, nullptr, INT_MIN))
|
||||||
{
|
{
|
||||||
value1 = m_value.load();
|
value1 = m_value.load();
|
||||||
|
|
||||||
@@ -110,7 +127,7 @@ void shared_mutex::imp_unlock_shared(s64 _old)
|
|||||||
#else
|
#else
|
||||||
m_value -= c_sig;
|
m_value -= c_sig;
|
||||||
|
|
||||||
futex((int*)&m_value.raw() + IS_BE_MACHINE, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -145,7 +162,7 @@ void shared_mutex::imp_wait(s64)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
futex((int*)&m_value.raw() + IS_BE_MACHINE, FUTEX_WAIT_PRIVATE, int(value), nullptr, nullptr, 0);
|
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAIT_BITSET_PRIVATE, int(value >> 32), nullptr, nullptr, u32(c_sig >> 32));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -180,8 +197,9 @@ void shared_mutex::imp_unlock(s64 _old)
|
|||||||
{
|
{
|
||||||
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr);
|
||||||
}
|
}
|
||||||
else if (s64 count = -_old / c_min)
|
else if (s64 count = -_old / c_min * 0)
|
||||||
{
|
{
|
||||||
|
// Disabled code
|
||||||
while (count--)
|
while (count--)
|
||||||
{
|
{
|
||||||
NtReleaseKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
NtReleaseKeyedEvent(nullptr, (int*)&m_value + 1, false, nullptr);
|
||||||
@@ -192,23 +210,26 @@ void shared_mutex::imp_unlock(s64 _old)
|
|||||||
{
|
{
|
||||||
m_value -= c_sig;
|
m_value -= c_sig;
|
||||||
|
|
||||||
futex((int*)&m_value.raw() + IS_BE_MACHINE, FUTEX_WAKE_PRIVATE, 1, nullptr, nullptr, 0);
|
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, u32(c_sig >> 32));
|
||||||
}
|
}
|
||||||
else if (s64 count = -_old / c_min)
|
else if (false)
|
||||||
{
|
{
|
||||||
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0);
|
// Disabled code
|
||||||
|
futex((int*)&m_value.raw() + IS_LE_MACHINE, FUTEX_WAKE_BITSET_PRIVATE, INT_MAX, nullptr, nullptr, INT_MIN);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void shared_mutex::imp_lock_upgrade()
|
void shared_mutex::imp_lock_upgrade()
|
||||||
{
|
{
|
||||||
|
// TODO
|
||||||
unlock_shared();
|
unlock_shared();
|
||||||
lock();
|
lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void shared_mutex::imp_lock_degrade()
|
void shared_mutex::imp_lock_degrade()
|
||||||
{
|
{
|
||||||
|
// TODO
|
||||||
unlock();
|
unlock();
|
||||||
lock_shared();
|
lock_shared();
|
||||||
}
|
}
|
||||||
@@ -236,3 +257,78 @@ bool shared_mutex::try_lock_degrade()
|
|||||||
// TODO
|
// TODO
|
||||||
return m_value.compare_and_swap_test(0, c_one - c_min);
|
return m_value.compare_and_swap_test(0, c_one - c_min);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
safe_reader_lock::safe_reader_lock(shared_mutex& mutex)
|
||||||
|
: m_mutex(mutex)
|
||||||
|
, m_is_owned(false)
|
||||||
|
{
|
||||||
|
if (std::count(g_tls_locks.cbegin(), g_tls_locks.cend(), &m_mutex) == 0)
|
||||||
|
{
|
||||||
|
m_is_owned = true;
|
||||||
|
|
||||||
|
if (m_is_owned)
|
||||||
|
{
|
||||||
|
m_mutex.lock_shared();
|
||||||
|
g_tls_locks.emplace_back(&m_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: order locks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_reader_lock::~safe_reader_lock()
|
||||||
|
{
|
||||||
|
if (m_is_owned)
|
||||||
|
{
|
||||||
|
m_mutex.unlock_shared();
|
||||||
|
g_tls_locks.erase(std::remove(g_tls_locks.begin(), g_tls_locks.end(), &m_mutex), g_tls_locks.cend());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: order locks
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_writer_lock::safe_writer_lock(shared_mutex& mutex)
|
||||||
|
: m_mutex(mutex)
|
||||||
|
, m_is_owned(false)
|
||||||
|
, m_is_upgraded(false)
|
||||||
|
{
|
||||||
|
if (std::count(g_tls_locks.cbegin(), g_tls_locks.cend(), &m_mutex) == 0)
|
||||||
|
{
|
||||||
|
m_is_owned = true;
|
||||||
|
|
||||||
|
if (m_is_owned)
|
||||||
|
{
|
||||||
|
m_mutex.lock_shared();
|
||||||
|
g_tls_locks.emplace_back(&m_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: order locks
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_mutex.is_reading())
|
||||||
|
{
|
||||||
|
m_is_upgraded = true;
|
||||||
|
m_mutex.lock_upgrade();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_writer_lock::~safe_writer_lock()
|
||||||
|
{
|
||||||
|
if (m_is_upgraded)
|
||||||
|
{
|
||||||
|
m_mutex.lock_degrade();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_is_owned)
|
||||||
|
{
|
||||||
|
m_mutex.unlock();
|
||||||
|
g_tls_locks.erase(std::remove(g_tls_locks.begin(), g_tls_locks.end(), &m_mutex), g_tls_locks.cend());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: order locks
|
||||||
|
}
|
||||||
|
|||||||
@@ -95,6 +95,11 @@ public:
|
|||||||
imp_lock_degrade();
|
imp_lock_degrade();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_reading() const
|
||||||
|
{
|
||||||
|
return (m_value.load() % c_one) != 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Simplified shared (reader) lock implementation.
|
// Simplified shared (reader) lock implementation.
|
||||||
@@ -171,3 +176,56 @@ public:
|
|||||||
unlock();
|
unlock();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Safe reader lock. Can be recursive above other safe locks (reader or writer).
|
||||||
|
class safe_reader_lock final
|
||||||
|
{
|
||||||
|
shared_mutex& m_mutex;
|
||||||
|
bool m_is_owned;
|
||||||
|
|
||||||
|
void lock()
|
||||||
|
{
|
||||||
|
m_mutex.lock_shared();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock()
|
||||||
|
{
|
||||||
|
m_mutex.unlock_shared();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class cond_variable;
|
||||||
|
|
||||||
|
public:
|
||||||
|
safe_reader_lock(const safe_reader_lock&) = delete;
|
||||||
|
|
||||||
|
explicit safe_reader_lock(shared_mutex& mutex);
|
||||||
|
|
||||||
|
~safe_reader_lock();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Safe writer lock. Can be recursive above other safe locks. Performs upgrade and degrade operations above existing reader lock if necessary.
|
||||||
|
class safe_writer_lock final
|
||||||
|
{
|
||||||
|
shared_mutex& m_mutex;
|
||||||
|
bool m_is_owned;
|
||||||
|
bool m_is_upgraded;
|
||||||
|
|
||||||
|
void lock()
|
||||||
|
{
|
||||||
|
m_mutex.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock()
|
||||||
|
{
|
||||||
|
m_mutex.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
friend class cond_variable;
|
||||||
|
|
||||||
|
public:
|
||||||
|
safe_writer_lock(const safe_writer_lock&) = delete;
|
||||||
|
|
||||||
|
explicit safe_writer_lock(shared_mutex& mutex);
|
||||||
|
|
||||||
|
~safe_writer_lock();
|
||||||
|
};
|
||||||
|
|||||||
@@ -61,10 +61,9 @@ rXmlDocument::rXmlDocument() : handle()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void rXmlDocument::Load(const std::string & path)
|
void rXmlDocument::Read(const std::string& data)
|
||||||
{
|
{
|
||||||
// TODO: Unsure of use of c_str.
|
handle.load_buffer(data.data(), data.size());
|
||||||
handle.load_file(path.c_str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<rXmlNode> rXmlDocument::GetRoot()
|
std::shared_ptr<rXmlNode> rXmlDocument::GetRoot()
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ struct rXmlDocument
|
|||||||
rXmlDocument();
|
rXmlDocument();
|
||||||
rXmlDocument(const rXmlDocument& other) = delete;
|
rXmlDocument(const rXmlDocument& other) = delete;
|
||||||
rXmlDocument &operator=(const rXmlDocument& other) = delete;
|
rXmlDocument &operator=(const rXmlDocument& other) = delete;
|
||||||
void Load(const std::string & path);
|
void Read(const std::string& data);
|
||||||
std::shared_ptr<rXmlNode> GetRoot();
|
std::shared_ptr<rXmlNode> GetRoot();
|
||||||
|
|
||||||
pugi::xml_document handle;
|
pugi::xml_document handle;
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
#include "sysinfo.h"
|
||||||
|
#include "StrFmt.h"
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "windows.h"
|
||||||
|
#else
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool utils::has_ssse3()
|
||||||
|
{
|
||||||
|
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x200;
|
||||||
|
return g_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool utils::has_avx()
|
||||||
|
{
|
||||||
|
static const bool g_value = get_cpuid(0, 0)[0] >= 0x1 && get_cpuid(1, 0)[2] & 0x10000000;
|
||||||
|
return g_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool utils::has_avx2()
|
||||||
|
{
|
||||||
|
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && get_cpuid(7, 0)[1] & 0x20;
|
||||||
|
return g_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool utils::has_rtm()
|
||||||
|
{
|
||||||
|
// Check RTM and MPX extensions in order to filter out TSX on Haswell CPUs
|
||||||
|
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0x4800) == 0x4800;
|
||||||
|
return g_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool utils::has_512()
|
||||||
|
{
|
||||||
|
// Check AVX512F, AVX512CD, AVX512DQ, AVX512BW, AVX512VL extensions (Skylake-X level support)
|
||||||
|
static const bool g_value = get_cpuid(0, 0)[0] >= 0x7 && (get_cpuid(7, 0)[1] & 0xd0030000) == 0xd0030000;
|
||||||
|
return g_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string utils::get_system_info()
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
std::string brand;
|
||||||
|
|
||||||
|
if (get_cpuid(0x80000000, 0)[0] >= 0x80000004)
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
brand.append(reinterpret_cast<const char*>(get_cpuid(0x80000002 + i, 0).data()), 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
brand = "Unknown CPU";
|
||||||
|
}
|
||||||
|
|
||||||
|
brand.erase(0, brand.find_first_not_of(' '));
|
||||||
|
brand.erase(brand.find_last_not_of(' ') + 1);
|
||||||
|
|
||||||
|
while (auto found = brand.find(" ") + 1)
|
||||||
|
{
|
||||||
|
brand.erase(brand.begin() + found);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
::SYSTEM_INFO sysInfo;
|
||||||
|
::GetNativeSystemInfo(&sysInfo);
|
||||||
|
::MEMORYSTATUSEX memInfo;
|
||||||
|
memInfo.dwLength = sizeof(memInfo);
|
||||||
|
::GlobalMemoryStatusEx(&memInfo);
|
||||||
|
const u32 num_proc = sysInfo.dwNumberOfProcessors;
|
||||||
|
const u64 mem_total = memInfo.ullTotalPhys;
|
||||||
|
#else
|
||||||
|
const u32 num_proc = ::sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
const u64 mem_total = ::sysconf(_SC_PHYS_PAGES) * ::sysconf(_SC_PAGE_SIZE);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fmt::append(result, "%s | %d Threads | %.2f GiB RAM", brand, num_proc, mem_total / (1024.0f * 1024 * 1024));
|
||||||
|
|
||||||
|
if (has_avx())
|
||||||
|
{
|
||||||
|
result += " | AVX";
|
||||||
|
|
||||||
|
if (has_avx2())
|
||||||
|
{
|
||||||
|
result += '+';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_512())
|
||||||
|
{
|
||||||
|
result += '+';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_rtm())
|
||||||
|
{
|
||||||
|
result += " | TSX";
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
inline std::array<u32, 4> get_cpuid(u32 func, u32 subfunc)
|
||||||
|
{
|
||||||
|
int regs[4];
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
__cpuidex(regs, func, subfunc);
|
||||||
|
#else
|
||||||
|
__asm__ volatile("cpuid" : "=a" (regs[0]), "=b" (regs[1]), "=c" (regs[2]), "=d" (regs[3]) : "a" (func), "c" (subfunc));
|
||||||
|
#endif
|
||||||
|
return {0u+regs[0], 0u+regs[1], 0u+regs[2], 0u+regs[3]};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_ssse3();
|
||||||
|
|
||||||
|
bool has_avx();
|
||||||
|
|
||||||
|
bool has_avx2();
|
||||||
|
|
||||||
|
bool has_rtm();
|
||||||
|
|
||||||
|
bool has_512();
|
||||||
|
|
||||||
|
inline bool transaction_enter()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const auto status = _xbegin();
|
||||||
|
|
||||||
|
if (status == _XBEGIN_STARTED)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(status & _XABORT_RETRY))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_system_info();
|
||||||
|
}
|
||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
// Assume little-endian
|
// Assume little-endian
|
||||||
#define IS_LE_MACHINE 1
|
#define IS_LE_MACHINE 1
|
||||||
@@ -31,7 +32,6 @@
|
|||||||
#define SAFE_BUFFERS
|
#define SAFE_BUFFERS
|
||||||
#define NEVER_INLINE __attribute__((noinline))
|
#define NEVER_INLINE __attribute__((noinline))
|
||||||
#define FORCE_INLINE __attribute__((always_inline)) inline
|
#define FORCE_INLINE __attribute__((always_inline)) inline
|
||||||
#define thread_local __thread
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, "Invalid " #type " type size")
|
#define CHECK_SIZE(type, size) static_assert(sizeof(type) == size, "Invalid " #type " type size")
|
||||||
@@ -45,13 +45,10 @@
|
|||||||
// Return 32 bit alignof() to avoid widening/narrowing conversions with size_t
|
// Return 32 bit alignof() to avoid widening/narrowing conversions with size_t
|
||||||
#define ALIGN_32(...) static_cast<u32>(alignof(__VA_ARGS__))
|
#define ALIGN_32(...) static_cast<u32>(alignof(__VA_ARGS__))
|
||||||
|
|
||||||
// Return 32 bit offsetof()
|
|
||||||
#define OFFSET_32(type, x) static_cast<u32>(reinterpret_cast<std::uintptr_t>(&reinterpret_cast<const volatile char&>(reinterpret_cast<type*>(0ull)->x)))
|
|
||||||
|
|
||||||
#define CONCATENATE_DETAIL(x, y) x ## y
|
#define CONCATENATE_DETAIL(x, y) x ## y
|
||||||
#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
|
#define CONCATENATE(x, y) CONCATENATE_DETAIL(x, y)
|
||||||
|
|
||||||
#define STRINGIZE_DETAIL(x) #x
|
#define STRINGIZE_DETAIL(x) #x ""
|
||||||
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
|
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
|
||||||
|
|
||||||
#define HERE "\n(in file " __FILE__ ":" STRINGIZE(__LINE__) ")"
|
#define HERE "\n(in file " __FILE__ ":" STRINGIZE(__LINE__) ")"
|
||||||
@@ -72,6 +69,8 @@ using ulong = unsigned long;
|
|||||||
using ullong = unsigned long long;
|
using ullong = unsigned long long;
|
||||||
using llong = long long;
|
using llong = long long;
|
||||||
|
|
||||||
|
using uptr = std::uintptr_t;
|
||||||
|
|
||||||
using u8 = std::uint8_t;
|
using u8 = std::uint8_t;
|
||||||
using u16 = std::uint16_t;
|
using u16 = std::uint16_t;
|
||||||
using u32 = std::uint32_t;
|
using u32 = std::uint32_t;
|
||||||
@@ -450,6 +449,71 @@ constexpr T align(const T& value, ullong align)
|
|||||||
return static_cast<T>((value + (align - 1)) & ~(align - 1));
|
return static_cast<T>((value + (align - 1)) & ~(align - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T, typename T2>
|
||||||
|
inline u32 offset32(T T2::*const mptr)
|
||||||
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
static_assert(sizeof(mptr) == sizeof(u32), "Invalid pointer-to-member size");
|
||||||
|
return reinterpret_cast<const u32&>(mptr);
|
||||||
|
#elif __GNUG__
|
||||||
|
static_assert(sizeof(mptr) == sizeof(std::size_t), "Invalid pointer-to-member size");
|
||||||
|
return static_cast<u32>(reinterpret_cast<const std::size_t&>(mptr));
|
||||||
|
#else
|
||||||
|
static_assert(sizeof(mptr) == 0, "Invalid pointer-to-member size");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct offset32_array
|
||||||
|
{
|
||||||
|
static_assert(std::is_array<T>::value, "Invalid pointer-to-member type (array expected)");
|
||||||
|
|
||||||
|
template <typename Arg>
|
||||||
|
static inline u32 index32(const Arg& arg)
|
||||||
|
{
|
||||||
|
return SIZE_32(std::remove_extent_t<T>) * static_cast<u32>(arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, std::size_t N>
|
||||||
|
struct offset32_array<std::array<T, N>>
|
||||||
|
{
|
||||||
|
template <typename Arg>
|
||||||
|
static inline u32 index32(const Arg& arg)
|
||||||
|
{
|
||||||
|
return SIZE_32(T) * static_cast<u32>(arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Arg>
|
||||||
|
struct offset32_detail;
|
||||||
|
|
||||||
|
template <typename T, typename T2, typename Arg, typename... Args>
|
||||||
|
inline u32 offset32(T T2::*const mptr, const Arg& arg, const Args&... args)
|
||||||
|
{
|
||||||
|
return offset32_detail<Arg>::offset32(mptr, arg, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Arg>
|
||||||
|
struct offset32_detail
|
||||||
|
{
|
||||||
|
template <typename T, typename T2, typename... Args>
|
||||||
|
static inline u32 offset32(T T2::*const mptr, const Arg& arg, const Args&... args)
|
||||||
|
{
|
||||||
|
return ::offset32(mptr, args...) + offset32_array<T>::index32(arg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T3, typename T4>
|
||||||
|
struct offset32_detail<T3 T4::*>
|
||||||
|
{
|
||||||
|
template <typename T, typename T2, typename... Args>
|
||||||
|
static inline u32 offset32(T T2::*const mptr, T3 T4::*const mptr2, const Args&... args)
|
||||||
|
{
|
||||||
|
return ::offset32(mptr) + ::offset32(mptr2, args...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
inline u32 cntlz32(u32 arg, bool nonzero = false)
|
inline u32 cntlz32(u32 arg, bool nonzero = false)
|
||||||
{
|
{
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
@@ -470,6 +534,26 @@ inline u64 cntlz64(u64 arg, bool nonzero = false)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline u32 cnttz32(u32 arg, bool nonzero = false)
|
||||||
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
ulong res;
|
||||||
|
return _BitScanForward(&res, arg) || nonzero ? res : 32;
|
||||||
|
#else
|
||||||
|
return arg || nonzero ? __builtin_ctzll(arg) : 32;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u64 cnttz64(u64 arg, bool nonzero = false)
|
||||||
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
ulong res;
|
||||||
|
return _BitScanForward64(&res, arg) || nonzero ? res : 64;
|
||||||
|
#else
|
||||||
|
return arg || nonzero ? __builtin_ctzll(arg) : 64;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function, used by ""_u16, ""_u32, ""_u64
|
// Helper function, used by ""_u16, ""_u32, ""_u64
|
||||||
constexpr u8 to_u8(char c)
|
constexpr u8 to_u8(char c)
|
||||||
{
|
{
|
||||||
@@ -854,7 +938,7 @@ struct error_code
|
|||||||
error_code() = default;
|
error_code() = default;
|
||||||
|
|
||||||
// Implementation must be provided specially
|
// Implementation must be provided specially
|
||||||
static s32 error_report(const fmt_type_info* sup, u64 arg);
|
static s32 error_report(const fmt_type_info* sup, u64 arg, const fmt_type_info* sup2, u64 arg2);
|
||||||
|
|
||||||
// Helper type
|
// Helper type
|
||||||
enum class not_an_error : s32
|
enum class not_an_error : s32
|
||||||
@@ -875,7 +959,7 @@ struct error_code
|
|||||||
|
|
||||||
// Not an error constructor
|
// Not an error constructor
|
||||||
template<typename ET, typename = decltype(ET::__not_an_error)>
|
template<typename ET, typename = decltype(ET::__not_an_error)>
|
||||||
error_code(const ET& value, int = 0)
|
error_code(const ET& value, std::nullptr_t = nullptr)
|
||||||
: value(static_cast<s32>(value))
|
: value(static_cast<s32>(value))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -883,7 +967,14 @@ struct error_code
|
|||||||
// Error constructor
|
// Error constructor
|
||||||
template<typename ET, typename = std::enable_if_t<is_error<ET>::value>>
|
template<typename ET, typename = std::enable_if_t<is_error<ET>::value>>
|
||||||
error_code(const ET& value)
|
error_code(const ET& value)
|
||||||
: value(error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value)))
|
: value(error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value), nullptr, 0))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error constructor (2 args)
|
||||||
|
template<typename ET, typename T2, typename = std::enable_if_t<is_error<ET>::value>>
|
||||||
|
error_code(const ET& value, const T2& value2)
|
||||||
|
: value(error_report(fmt::get_type_info<fmt_unveil_t<ET>>(), fmt_unveil<ET>::get(value), fmt::get_type_info<fmt_unveil_t<T2>>(), fmt_unveil<T2>::get(value2)))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -909,45 +1000,85 @@ inline void busy_wait(std::size_t count = 100)
|
|||||||
// Rotate helpers
|
// Rotate helpers
|
||||||
#if defined(__GNUG__)
|
#if defined(__GNUG__)
|
||||||
|
|
||||||
inline u8 rol8(const u8 x, const u8 n)
|
inline u8 rol8(u8 x, u8 n)
|
||||||
{
|
{
|
||||||
u8 result = x;
|
u8 result = x;
|
||||||
__asm__("rolb %[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
__asm__("rolb %[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline u16 rol16(const u16 x, const u16 n)
|
inline u8 ror8(u8 x, u8 n)
|
||||||
|
{
|
||||||
|
u8 result = x;
|
||||||
|
__asm__("rorb %[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u16 rol16(u16 x, u16 n)
|
||||||
{
|
{
|
||||||
u16 result = x;
|
u16 result = x;
|
||||||
__asm__("rolw %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
__asm__("rolw %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline u32 rol32(const u32 x, const u32 n)
|
inline u16 ror16(u16 x, u16 n)
|
||||||
|
{
|
||||||
|
u16 result = x;
|
||||||
|
__asm__("rorw %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u32 rol32(u32 x, u32 n)
|
||||||
{
|
{
|
||||||
u32 result = x;
|
u32 result = x;
|
||||||
__asm__("roll %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
__asm__("roll %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline u64 rol64(const u64 x, const u64 n)
|
inline u32 ror32(u32 x, u32 n)
|
||||||
|
{
|
||||||
|
u32 result = x;
|
||||||
|
__asm__("rorl %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline u64 rol64(u64 x, u64 n)
|
||||||
{
|
{
|
||||||
u64 result = x;
|
u64 result = x;
|
||||||
__asm__("rolq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
__asm__("rolq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline u64 ror64(const u64 x, const u64 n)
|
inline u64 ror64(u64 x, u64 n)
|
||||||
{
|
{
|
||||||
u64 result = x;
|
u64 result = x;
|
||||||
__asm__("rorq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
__asm__("rorq %b[n], %[result]" : [result] "+g" (result) : [n] "c" (n));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline u64 umulh64(u64 a, u64 b)
|
||||||
|
{
|
||||||
|
u64 result;
|
||||||
|
__asm__("mulq %[b]" : "=d" (result) : [a] "a" (a), [b] "rm" (b));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline s64 mulh64(s64 a, s64 b)
|
||||||
|
{
|
||||||
|
s64 result;
|
||||||
|
__asm__("imulq %[b]" : "=d" (result) : [a] "a" (a), [b] "rm" (b));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
inline u8 rol8(const u8 x, const u8 n) { return _rotl8(x, n); }
|
inline u8 rol8(u8 x, u8 n) { return _rotl8(x, n); }
|
||||||
inline u16 rol16(const u16 x, const u16 n) { return _rotl16(x, (u8)n); }
|
inline u8 ror8(u8 x, u8 n) { return _rotr8(x, n); }
|
||||||
inline u32 rol32(const u32 x, const u32 n) { return _rotl(x, (int)n); }
|
inline u16 rol16(u16 x, u16 n) { return _rotl16(x, (u8)n); }
|
||||||
inline u64 rol64(const u64 x, const u64 n) { return _rotl64(x, (int)n); }
|
inline u16 ror16(u16 x, u16 n) { return _rotr16(x, (u8)n); }
|
||||||
inline u64 ror64(const u64 x, const u64 n) { return _rotr64(x, (int)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); }
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "recursive_wrapper.hpp"
|
#include "recursive_wrapper.hpp"
|
||||||
|
#include "variant_visitor.hpp"
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
// [[deprecated]] is only available in C++14, use this for the time being
|
// [[deprecated]] is only available in C++14, use this for the time being
|
||||||
@@ -849,6 +850,22 @@ namespace std {
|
|||||||
return detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
|
return detail::binary_dispatcher<F, V, R, Types...>::apply(v0, v1, std::forward<F>(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// match
|
||||||
|
// unary
|
||||||
|
template <typename... Fs>
|
||||||
|
auto VARIANT_INLINE match(Fs&&... fs) const
|
||||||
|
-> decltype(variant::visit(*this, ::std::make_visitor(std::forward<Fs>(fs)...)))
|
||||||
|
{
|
||||||
|
return variant::visit(*this, ::std::make_visitor(std::forward<Fs>(fs)...));
|
||||||
|
}
|
||||||
|
// non-const
|
||||||
|
template <typename... Fs>
|
||||||
|
auto VARIANT_INLINE match(Fs&&... fs)
|
||||||
|
-> decltype(variant::visit(*this, ::std::make_visitor(std::forward<Fs>(fs)...)))
|
||||||
|
{
|
||||||
|
return variant::visit(*this, ::std::make_visitor(std::forward<Fs>(fs)...));
|
||||||
|
}
|
||||||
|
|
||||||
~variant() noexcept // no-throw destructor
|
~variant() noexcept // no-throw destructor
|
||||||
{
|
{
|
||||||
helper_type::destroy(type_index, &data);
|
helper_type::destroy(type_index, &data);
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
#ifndef MAPBOX_UTIL_VARIANT_VISITOR_HPP
|
||||||
|
#define MAPBOX_UTIL_VARIANT_VISITOR_HPP
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
//namespace mapbox {
|
||||||
|
//namespace util {
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <typename... Fns>
|
||||||
|
struct visitor;
|
||||||
|
|
||||||
|
template <typename Fn>
|
||||||
|
struct visitor<Fn> : Fn
|
||||||
|
{
|
||||||
|
using Fn::operator();
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
visitor(T&& fn) : Fn(std::forward<T>(fn)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Fn, typename... Fns>
|
||||||
|
struct visitor<Fn, Fns...> : Fn, visitor<Fns...>
|
||||||
|
{
|
||||||
|
using Fn::operator();
|
||||||
|
using visitor<Fns...>::operator();
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
visitor(T&& fn, Ts&&... fns)
|
||||||
|
: Fn(std::forward<T>(fn))
|
||||||
|
, visitor<Fns...>(std::forward<Ts>(fns)...) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename... Fns>
|
||||||
|
visitor<typename std::decay<Fns>::type...> make_visitor(Fns&&... fns)
|
||||||
|
{
|
||||||
|
return visitor<typename std::decay<Fns>::type...>
|
||||||
|
(std::forward<Fns>(fns)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
//} // namespace util
|
||||||
|
//} // namespace mapbox
|
||||||
|
|
||||||
|
#endif // MAPBOX_UTIL_VARIANT_VISITOR_HPP
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
<PropertyGroup Label="Configuration">
|
<PropertyGroup Label="Configuration">
|
||||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<PlatformToolset>v140</PlatformToolset>
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
<ImportGroup Label="ExtensionSettings">
|
<ImportGroup Label="ExtensionSettings">
|
||||||
@@ -54,6 +54,7 @@
|
|||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="yaml-cpp\src\emitterutils.cpp">
|
<ClCompile Include="yaml-cpp\src\emitterutils.cpp">
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="yaml-cpp\src\exceptions.cpp" />
|
||||||
<ClCompile Include="yaml-cpp\src\exp.cpp">
|
<ClCompile Include="yaml-cpp\src\exp.cpp">
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="yaml-cpp\src\memory.cpp">
|
<ClCompile Include="yaml-cpp\src\memory.cpp">
|
||||||
|
|||||||
@@ -7,85 +7,88 @@
|
|||||||
</Filter>
|
</Filter>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="src\binary.cpp">
|
<ClCompile Include="yaml-cpp\src\binary.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\convert.cpp">
|
<ClCompile Include="yaml-cpp\src\convert.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\directives.cpp">
|
<ClCompile Include="yaml-cpp\src\directives.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\emit.cpp">
|
<ClCompile Include="yaml-cpp\src\emit.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\emitfromevents.cpp">
|
<ClCompile Include="yaml-cpp\src\emitfromevents.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\emitter.cpp">
|
<ClCompile Include="yaml-cpp\src\emitter.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\emitterstate.cpp">
|
<ClCompile Include="yaml-cpp\src\emitterstate.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\emitterutils.cpp">
|
<ClCompile Include="yaml-cpp\src\emitterutils.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\exp.cpp">
|
<ClCompile Include="yaml-cpp\src\exp.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\memory.cpp">
|
<ClCompile Include="yaml-cpp\src\memory.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\node.cpp">
|
<ClCompile Include="yaml-cpp\src\node.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\node_data.cpp">
|
<ClCompile Include="yaml-cpp\src\nodebuilder.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\nodebuilder.cpp">
|
<ClCompile Include="yaml-cpp\src\nodeevents.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\nodeevents.cpp">
|
<ClCompile Include="yaml-cpp\src\node_data.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\null.cpp">
|
<ClCompile Include="yaml-cpp\src\null.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\ostream_wrapper.cpp">
|
<ClCompile Include="yaml-cpp\src\ostream_wrapper.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\parse.cpp">
|
<ClCompile Include="yaml-cpp\src\parse.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\parser.cpp">
|
<ClCompile Include="yaml-cpp\src\parser.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\regex_yaml.cpp">
|
<ClCompile Include="yaml-cpp\src\regex_yaml.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\scanner.cpp">
|
<ClCompile Include="yaml-cpp\src\scanner.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\scanscalar.cpp">
|
<ClCompile Include="yaml-cpp\src\scanscalar.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\scantag.cpp">
|
<ClCompile Include="yaml-cpp\src\scantag.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\scantoken.cpp">
|
<ClCompile Include="yaml-cpp\src\scantoken.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\simplekey.cpp">
|
<ClCompile Include="yaml-cpp\src\simplekey.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\singledocparser.cpp">
|
<ClCompile Include="yaml-cpp\src\singledocparser.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\stream.cpp">
|
<ClCompile Include="yaml-cpp\src\stream.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="src\tag.cpp">
|
<ClCompile Include="yaml-cpp\src\tag.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="yaml-cpp\src\exceptions.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup />
|
|
||||||
</Project>
|
|
||||||
@@ -19,12 +19,12 @@
|
|||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Makefile</ConfigurationType>
|
<ConfigurationType>Makefile</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<PlatformToolset>v140</PlatformToolset>
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Makefile</ConfigurationType>
|
<ConfigurationType>Makefile</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<PlatformToolset>v140</PlatformToolset>
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
<ImportGroup Label="ExtensionSettings">
|
<ImportGroup Label="ExtensionSettings">
|
||||||
@@ -39,21 +39,25 @@
|
|||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<PropertyGroup Label="UserMacros" />
|
<PropertyGroup Label="UserMacros" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
<NMakeBuildCommandLine>start /W /B pre-build.bat
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release</NMakeBuildCommandLine>
|
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release /m</NMakeBuildCommandLine>
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release</NMakeCleanCommandLine>
|
<NMakeCleanCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /m</NMakeCleanCommandLine>
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release
|
<NMakeReBuildCommandLine>start /W /B pre-build.bat
|
||||||
|
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||||
|
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /m
|
||||||
</NMakeReBuildCommandLine>
|
</NMakeReBuildCommandLine>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
<NMakeBuildCommandLine>start /W /B pre-build.bat
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug</NMakeBuildCommandLine>
|
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /m</NMakeBuildCommandLine>
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug</NMakeReBuildCommandLine>
|
<NMakeReBuildCommandLine>start /W /B pre-build.bat
|
||||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug</NMakeCleanCommandLine>
|
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /m</NMakeReBuildCommandLine>
|
||||||
|
<NMakeCleanCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DBUILD_TESTS=OFF -DBUILD_DEMOS=OFF -DBUILD_LAYERS=OFF -DBUILD_VKJSON=OFF ../Vulkan-LoaderAndValidationLayers
|
||||||
|
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /m</NMakeCleanCommandLine>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
@ECHO OFF
|
||||||
|
|
||||||
|
REM Setting up dummy git helper
|
||||||
|
MKDIR ..\Vulkan-LoaderAndValidationLayers\external\glslang\External\spirv-tools
|
||||||
|
SET header="@ECHO off"
|
||||||
|
SET content="ECHO unused"
|
||||||
|
ECHO %header:"=% > git.bat
|
||||||
|
ECHO %content:"=% >> git.bat
|
||||||
|
COPY /Y git.bat ..\Vulkan-LoaderAndValidationLayers\external\glslang\External\spirv-tools\git.bat
|
||||||
|
|
||||||
|
REM Set up gitignore
|
||||||
|
SET ignored="*"
|
||||||
|
ECHO %ignored:"=% > ..\Vulkan-LoaderAndValidationLayers\external\glslang\External\spirv-tools\.gitignore
|
||||||
@@ -19,12 +19,12 @@
|
|||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Makefile</ConfigurationType>
|
<ConfigurationType>Makefile</ConfigurationType>
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
<PlatformToolset>v140</PlatformToolset>
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
<ConfigurationType>Makefile</ConfigurationType>
|
<ConfigurationType>Makefile</ConfigurationType>
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
<PlatformToolset>v140</PlatformToolset>
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
<ImportGroup Label="ExtensionSettings">
|
<ImportGroup Label="ExtensionSettings">
|
||||||
@@ -39,25 +39,25 @@
|
|||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
<PropertyGroup Label="UserMacros" />
|
<PropertyGroup Label="UserMacros" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
<NMakeBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
<NMakeBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release</NMakeBuildCommandLine>
|
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Release /m</NMakeBuildCommandLine>
|
||||||
<NMakeReBuildCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
<NMakeReBuildCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release</NMakeReBuildCommandLine>
|
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Release /m</NMakeReBuildCommandLine>
|
||||||
<NMakeCleanCommandLine>cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
<NMakeCleanCommandLine>cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release</NMakeCleanCommandLine>
|
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Release /m</NMakeCleanCommandLine>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
<NMakeBuildCommandLine>
|
<NMakeBuildCommandLine>
|
||||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug
|
msbuild.exe ALL_BUILD.vcxproj /t:build /p:Configuration=Debug /m
|
||||||
</NMakeBuildCommandLine>
|
</NMakeBuildCommandLine>
|
||||||
<NMakeReBuildCommandLine>
|
<NMakeReBuildCommandLine>
|
||||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug
|
msbuild.exe ALL_BUILD.vcxproj /t:rebuild /p:Configuration=Debug /m
|
||||||
</NMakeReBuildCommandLine>
|
</NMakeReBuildCommandLine>
|
||||||
<NMakeCleanCommandLine>
|
<NMakeCleanCommandLine>
|
||||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" ../glslang
|
||||||
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug
|
msbuild.exe ALL_BUILD.vcxproj /t:clean /p:Configuration=Debug /m
|
||||||
</NMakeCleanCommandLine>
|
</NMakeCleanCommandLine>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemDefinitionGroup>
|
<ItemDefinitionGroup>
|
||||||
|
|||||||
@@ -1,38 +1,80 @@
|
|||||||
version: '0.0.2-{build}'
|
#---------------------------------#
|
||||||
|
# general configuration #
|
||||||
|
#---------------------------------#
|
||||||
|
|
||||||
os: Visual Studio 2015
|
# version format
|
||||||
|
version: '0.0.4-{build}'
|
||||||
|
|
||||||
|
#---------------------------------#
|
||||||
|
# environment configuration #
|
||||||
|
#---------------------------------#
|
||||||
|
|
||||||
|
# Build worker image (VM template)
|
||||||
|
image: Visual Studio 2017
|
||||||
|
|
||||||
|
# clone directory
|
||||||
|
clone_folder: c:\projects\rpcs3
|
||||||
|
|
||||||
|
# environment variables
|
||||||
|
environment:
|
||||||
|
QTDIR: C:\Qt\5.9\msvc2017_64
|
||||||
|
|
||||||
|
# build cache to preserve files/folders between builds
|
||||||
|
cache:
|
||||||
|
- llvmlibs.7z -> appveyor.yml
|
||||||
|
- vulkan.7z -> appveyor.yml
|
||||||
|
|
||||||
|
# scripts that run after cloning repository
|
||||||
|
install:
|
||||||
|
- git submodule update --init 3rdparty/cereal 3rdparty/ffmpeg 3rdparty/GSL 3rdparty/hidapi 3rdparty/libpng 3rdparty/Optional 3rdparty/pugixml 3rdparty/zlib asmjit Utilities/yaml-cpp Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers
|
||||||
|
|
||||||
|
#---------------------------------#
|
||||||
|
# build configuration #
|
||||||
|
#---------------------------------#
|
||||||
|
|
||||||
|
# build platform, i.e. x86, x64, Any CPU. This setting is optional.
|
||||||
platform: x64
|
platform: x64
|
||||||
clone_folder: C:\rpcs3
|
|
||||||
clone_depth: 3
|
# build Configuration, i.e. Debug, Release, etc.
|
||||||
|
configuration: Release - LLVM
|
||||||
|
|
||||||
|
build:
|
||||||
|
parallel: true # enable MSBuild parallel builds
|
||||||
|
project: rpcs3.sln # path to Visual Studio solution or project
|
||||||
|
# MSBuild verbosity level
|
||||||
|
verbosity: normal
|
||||||
|
|
||||||
|
# scripts to run before build
|
||||||
|
before_build:
|
||||||
|
- set BRANCH=%APPVEYOR_REPO_NAME%/%APPVEYOR_REPO_BRANCH%/#%APPVEYOR_PULL_REQUEST_NUMBER%
|
||||||
|
- if "%APPVEYOR_PULL_REQUEST_NUMBER%"=="" (set BRANCH=%APPVEYOR_REPO_NAME%/%APPVEYOR_REPO_BRANCH%)
|
||||||
|
- set PATH=%PATH%;%QTDIR%
|
||||||
|
- if not exist llvmlibs.7z appveyor DownloadFile "https://ci.appveyor.com/api/buildjobs/1w8r1n6nh2w99aul/artifacts/llvmlibs.7z.zip" -FileName llvmlibs.7z.zip
|
||||||
|
- 7z x llvmlibs.7z.zip > null
|
||||||
|
- 7z x llvmlibs.7z -aos -o%APPVEYOR_BUILD_FOLDER% > null
|
||||||
|
- if not exist vulkan.7z appveyor DownloadFile "https://drive.google.com/uc?export=download&id=1A2eOMmCO714i0U7J0qI4aEMKnuWl8l_R" -FileName vulkan.7z
|
||||||
|
- 7z x vulkan.7z -aos -o"%APPVEYOR_BUILD_FOLDER%\lib\%CONFIGURATION%-%PLATFORM%" > null
|
||||||
|
|
||||||
|
# scripts to run *after* solution is built and *before* automatic packaging occurs (web apps, NuGet packages, Azure Cloud Services)
|
||||||
|
before_package:
|
||||||
|
- rm %APPVEYOR_BUILD_FOLDER%\bin\rpcs3.exp
|
||||||
|
- rm %APPVEYOR_BUILD_FOLDER%\bin\rpcs3.lib
|
||||||
|
- rm %APPVEYOR_BUILD_FOLDER%\bin\rpcs3.pdb
|
||||||
|
- set COMMIT_DATE=%APPVEYOR_REPO_COMMIT_TIMESTAMP:~0,10%
|
||||||
|
- set COMMIT_SHA=%APPVEYOR_REPO_COMMIT:~0,8%
|
||||||
|
|
||||||
|
#---------------------------------#
|
||||||
|
# tests configuration #
|
||||||
|
#---------------------------------#
|
||||||
|
|
||||||
|
# to disable automatic tests
|
||||||
test: off
|
test: off
|
||||||
|
|
||||||
configuration:
|
#---------------------------------#
|
||||||
# Release
|
# artifacts configuration #
|
||||||
- ReleaseLLVM
|
#---------------------------------#
|
||||||
|
|
||||||
before_build:
|
|
||||||
- ps: $env:Date="$(git show -s --date=short --format='%ad')"
|
|
||||||
- git submodule update --init 3rdparty/ffmpeg 3rdparty/pugixml asmjit 3rdparty/GSL 3rdparty/libpng Vulkan/glslang Vulkan/Vulkan-LoaderAndValidationLayers Utilities/yaml-cpp rsx_program_decompiler 3rdparty/cereal
|
|
||||||
- 7z x wxWidgets.7z -aos -oC:\rpcs3\wxWidgets > null
|
|
||||||
- 7z x zlib.7z -aos -oC:\rpcs3\ > null
|
|
||||||
- 7z x vulkan.7z -aos -oC:\rpcs3\Vulkan > null
|
|
||||||
- if %configuration%==Release (cmake -G "Visual Studio 14 Win64" -DZLIB_ROOT=C:/rpcs3/zlib/ -DVULKAN_PREBUILT=ON)
|
|
||||||
else (7z x llvmlibs.7z -aos -oC:\rpcs3 > null && cmake -G "Visual Studio 14 Win64" -DLLVM_DIR=C:/rpcs3/llvm_build/share/llvm/cmake -DZLIB_ROOT=C:/rpcs3/zlib/ -DVULKAN_PREBUILT=ON)
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- cmake --build . --config Release -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll"
|
|
||||||
|
|
||||||
install:
|
|
||||||
- appveyor DownloadFile "https://dl.dropboxusercontent.com/s/c1by0uulee6g8th/wxWidgets.7z?dl=0" -FileName wxWidgets.7z
|
|
||||||
- appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B-98fOyaZKJ5YWVnb29JZXFQWkU" -FileName llvmlibs.7z
|
|
||||||
- appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B8A6NaxhQAGRa21fbDQteTN1dGs" -FileName vulkan.7z
|
|
||||||
- appveyor DownloadFile "https://drive.google.com/uc?export=download&id=0B6v_qtb9hkicQ2hHa2dRbF83cE0" -FileName zlib.7z
|
|
||||||
- set WXWIN=C:\rpcs3\wxWidgets
|
|
||||||
- set OPENALDIR=C:\rpcs3\3rdparty\OpenAL
|
|
||||||
- set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;C:\wxWidgets;%PATH%
|
|
||||||
- set COMMIT_SHA=%APPVEYOR_REPO_COMMIT:~0,8%
|
|
||||||
|
|
||||||
|
# pushing entire folder as a zip archive
|
||||||
artifacts:
|
artifacts:
|
||||||
- path: bin
|
- path: bin
|
||||||
name: 'rpcs3-v0.0.2-$(Date)-$(COMMIT_SHA)_win64'
|
name: 'rpcs3-v0.0.4-$(COMMIT_DATE)-$(COMMIT_SHA)_win64'
|
||||||
type: zip
|
|
||||||
|
|||||||
@@ -23,47 +23,53 @@
|
|||||||
</ProjectConfiguration>
|
</ProjectConfiguration>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\base\arch.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\assembler.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\assembler.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\codegen.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\codebuilder.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\compiler.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\codecompiler.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\base\codeemitter.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\base\codeholder.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\constpool.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\constpool.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\containers.cpp" />
|
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\context.cpp" />
|
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\cpuinfo.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\cpuinfo.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\cputicks.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\func.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\error.cpp" />
|
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\globals.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\globals.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\intutil.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\inst.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\logger.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\logging.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\base\osutils.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\operand.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\operand.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\base\regalloc.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\runtime.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\runtime.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\string.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\string.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\vmem.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\vmem.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\zone.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\zone.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86assembler.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86assembler.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86compiler.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86compiler.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86context.cpp" />
|
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86cpuinfo.cpp" />
|
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86inst.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86inst.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86instimpl.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86internal.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86logging.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand_regs.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand_regs.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86scheduler.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86regalloc.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\base\arch.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\assembler.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\assembler.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\codegen.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\codebuilder.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\compiler.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\codecompiler.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\base\codeemitter.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\base\codeholder.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\constpool.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\constpool.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\containers.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\context_p.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\context_p.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\cpuinfo.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\cpuinfo.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\cputicks.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\func.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\error.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\logging.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\globals.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\globals.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\intutil.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\inst.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\lock.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\lock.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\logger.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\operand.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\operand.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\base\osutils.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\base\regalloc_p.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\runtime.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\runtime.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\string.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\string.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\vectypes.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\vectypes.h" />
|
||||||
@@ -71,11 +77,12 @@
|
|||||||
<ClInclude Include="..\asmjit\src\asmjit\base\zone.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\zone.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86assembler.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86assembler.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86compiler.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86compiler.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86context_p.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86cpuinfo.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86inst.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86inst.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86instimpl_p.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86internal_p.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86logging_p.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86operand.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86operand.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86scheduler_p.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86regalloc_p.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<ProjectGuid>{AC40FF01-426E-4838-A317-66354CEFAE88}</ProjectGuid>
|
<ProjectGuid>{AC40FF01-426E-4838-A317-66354CEFAE88}</ProjectGuid>
|
||||||
@@ -86,7 +93,7 @@
|
|||||||
<PropertyGroup Label="Configuration">
|
<PropertyGroup Label="Configuration">
|
||||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
<PlatformToolset>v140</PlatformToolset>
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
<ImportGroup Label="ExtensionSettings">
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
|||||||
@@ -1,58 +1,65 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\base\arch.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\assembler.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\assembler.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\codegen.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\codebuilder.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\compiler.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\codecompiler.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\base\codeemitter.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\base\codeholder.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\constpool.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\constpool.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\containers.cpp" />
|
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\context.cpp" />
|
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\cpuinfo.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\cpuinfo.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\cputicks.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\func.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\error.cpp" />
|
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\globals.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\globals.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\intutil.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\inst.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\logger.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\logging.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\base\osutils.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\operand.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\operand.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\base\regalloc.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\runtime.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\runtime.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\string.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\string.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\vmem.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\vmem.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\base\zone.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\base\zone.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86assembler.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86assembler.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86compiler.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86compiler.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86context.cpp" />
|
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86cpuinfo.cpp" />
|
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86inst.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86inst.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86instimpl.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86internal.cpp" />
|
||||||
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86logging.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand_regs.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86operand_regs.cpp" />
|
||||||
<ClCompile Include="..\asmjit\src\asmjit\x86\x86scheduler.cpp" />
|
<ClCompile Include="..\asmjit\src\asmjit\x86\x86regalloc.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86assembler.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\arch.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86compiler.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86context_p.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86cpuinfo.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86inst.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86operand.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\x86\x86scheduler_p.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\assembler.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\assembler.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\codegen.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\codebuilder.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\compiler.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\codecompiler.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\base\codeemitter.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\base\codeholder.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\constpool.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\constpool.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\containers.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\context_p.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\context_p.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\cpuinfo.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\cpuinfo.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\cputicks.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\func.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\error.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\logging.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\globals.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\globals.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\intutil.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\inst.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\lock.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\lock.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\logger.h" />
|
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\operand.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\operand.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\base\osutils.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\base\regalloc_p.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\runtime.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\runtime.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\string.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\string.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\vectypes.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\vectypes.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\vmem.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\vmem.h" />
|
||||||
<ClInclude Include="..\asmjit\src\asmjit\base\zone.h" />
|
<ClInclude Include="..\asmjit\src\asmjit\base\zone.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86assembler.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86compiler.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86inst.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86instimpl_p.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86internal_p.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86logging_p.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86operand.h" />
|
||||||
|
<ClInclude Include="..\asmjit\src\asmjit\x86\x86regalloc_p.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<PropertyGroup />
|
|
||||||
</Project>
|
|
||||||
@@ -0,0 +1,332 @@
|
|||||||
|
/*
|
||||||
|
TheMitoSan Dark-Theme for RPCS3
|
||||||
|
Based on Kuroi (Dark) by Ani
|
||||||
|
by themitosan @ twitter.com/themitosan
|
||||||
|
Ani @ https://github.com/AniLeo
|
||||||
|
(2017.11.14)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Every widget */
|
||||||
|
QWidget {
|
||||||
|
background-color: #131313;
|
||||||
|
color: #FFF;
|
||||||
|
alternate-background-color: #212121;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mouse tooltips */
|
||||||
|
QToolTip {
|
||||||
|
padding: 0.125em;
|
||||||
|
border: 0.0625em #444;
|
||||||
|
background-color: #222;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table headers */
|
||||||
|
QHeaderView::section {
|
||||||
|
background-color: #010101;
|
||||||
|
color: #ecf0f1;
|
||||||
|
padding-left: 0.25em;
|
||||||
|
border: 0.0525em solid #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Settings Dialog: Tabs */
|
||||||
|
QTabBar::tab {
|
||||||
|
color: #ecf0f1;
|
||||||
|
border: 0.0625em solid #444;
|
||||||
|
border-bottom-style: none;
|
||||||
|
background-color: #353535;
|
||||||
|
padding-left: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
padding-top: 0.25em;
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
margin-right: -0.125em;
|
||||||
|
border-top-left-radius: 0.25em;
|
||||||
|
border-top-right-radius: 0.25em;
|
||||||
|
}
|
||||||
|
QTabBar::tab:last {
|
||||||
|
margin-right: 0em;
|
||||||
|
}
|
||||||
|
QTabBar::tab:!selected {
|
||||||
|
color: #888;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
margin-top: 0.1875em;
|
||||||
|
background-color: #111;
|
||||||
|
}
|
||||||
|
QTabBar::tab:hover {
|
||||||
|
background-color: #353535;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkboxes */
|
||||||
|
QCheckBox::indicator {
|
||||||
|
border-radius: 0.125em;
|
||||||
|
border-top-right-radius: 0em;
|
||||||
|
border-bottom-left-radius: 0em;
|
||||||
|
border: 0.0625em solid #FFF;
|
||||||
|
width: 0.8em;
|
||||||
|
height: 0.8em;
|
||||||
|
margin-top: 0.0625em;
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:checked {
|
||||||
|
background-color: #FFF; /* White */
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:unchecked {
|
||||||
|
background-color: #000; /* Black */
|
||||||
|
}
|
||||||
|
QCheckBox::indicator::disabled {
|
||||||
|
background-color: #af4949; /* Red */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radio Buttons */
|
||||||
|
QRadioButton::indicator {
|
||||||
|
border: 0.0625em solid #FFF;
|
||||||
|
width: 0.8em;
|
||||||
|
height: 0.8em;
|
||||||
|
margin-top: 0.0625em;
|
||||||
|
border-radius: 0.270em;
|
||||||
|
}
|
||||||
|
QRadioButton::indicator:checked {
|
||||||
|
background-color: #FFF; /* Green */
|
||||||
|
}
|
||||||
|
QRadioButton::indicator:unchecked {
|
||||||
|
background-color: #000; /* Red */
|
||||||
|
}
|
||||||
|
QRadioButton::indicator::disabled {
|
||||||
|
background-color: #af4949; /* Gray */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Combo Boxes */
|
||||||
|
QComboBox {
|
||||||
|
background-color: #404040;
|
||||||
|
color: #fff;
|
||||||
|
border: 0.0625em solid #1e1e1e;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
padding-bottom: 0.125em;
|
||||||
|
padding-left: 0.25em;
|
||||||
|
}
|
||||||
|
QComboBox::disabled {
|
||||||
|
background-color: #252525;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Group Boxes (Settings Dialog) */
|
||||||
|
QGroupBox {
|
||||||
|
margin-top: 1em;
|
||||||
|
font-size: 8pt;
|
||||||
|
border: 0.05em solid #bdc3c7;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
QGroupBox::title {
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
subcontrol-position: top;
|
||||||
|
padding: 0.3em 0.5em 0.3em 0.5em;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
QPushButton {
|
||||||
|
background-color: #404040;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton::disabled {
|
||||||
|
background-color: #353535;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton::pressed {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Log and Debugger borders */
|
||||||
|
QTextEdit {
|
||||||
|
border: 0.05em solid #353535;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For dock buttons to be visible */
|
||||||
|
QDockWidget::close-button, QDockWidget::float-button {
|
||||||
|
background-color: #bdc3c7;
|
||||||
|
border: 0.05em solid #000;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable ugly borders */
|
||||||
|
QTabWidget::pane {
|
||||||
|
border: 0em solid #353535;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top menu bar */
|
||||||
|
QMenuBar::item:selected {
|
||||||
|
background: #454545;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
QMenu::item {
|
||||||
|
padding-left: 1.5em;
|
||||||
|
padding-right: 0.75em;
|
||||||
|
padding-top: 0.25em;
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
margin-bottom: 0.125em;
|
||||||
|
}
|
||||||
|
QMenu::item:selected {
|
||||||
|
background: #444;
|
||||||
|
border: 0.05em solid #bdc3c7;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
QMenu::item:disabled {
|
||||||
|
background-color: #313131;
|
||||||
|
color: #888888;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Libraries list */
|
||||||
|
QListWidget::item:selected {
|
||||||
|
background-color: #3d3d3d;
|
||||||
|
color: #ecf0f1;
|
||||||
|
border-radius: 0.125em;
|
||||||
|
}
|
||||||
|
QListWidget::item:hover {
|
||||||
|
background-color: #4c4b4b;
|
||||||
|
color: #ecf0f1;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pad Settings: Controller Image */
|
||||||
|
QLabel#l_controller {
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Game Grid Font */
|
||||||
|
QLabel#gamegrid_font {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 8pt;
|
||||||
|
font-family: Lucida Grande;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slider on toolbar */
|
||||||
|
QWidget#sizeSliderContainer {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debug UI Settings buttons */
|
||||||
|
QLabel#color_button {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seachbar on main toolbar */
|
||||||
|
QLineEdit#mw_searchbar {
|
||||||
|
margin-left: 0.7em;
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Uniform colors in Toolbar */
|
||||||
|
QToolButton {
|
||||||
|
background: transparent;
|
||||||
|
color: transparent;
|
||||||
|
border-radius: 0.125em;
|
||||||
|
padding: 0.09375em;
|
||||||
|
margin-left: 0.125em;
|
||||||
|
margin-top: 0.125em;
|
||||||
|
margin-bottom: 0.125em;
|
||||||
|
min-width: 0.063em;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
QToolButton::disabled {
|
||||||
|
background-color: #353535;
|
||||||
|
}
|
||||||
|
QToolButton::hover {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Theme UI colors */
|
||||||
|
QLabel#gamelist_icon_background_color {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
QLabel#gamelist_toolbar_icon_color {
|
||||||
|
color: #828790;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Windows Taskbar Thumbnail colors */
|
||||||
|
QLabel#thumbnail_icon_color {
|
||||||
|
color: #262626;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Log colors */
|
||||||
|
QTextEdit#log_frame {
|
||||||
|
background-color: #000; /* Black */
|
||||||
|
}
|
||||||
|
QLabel#log_level_always {
|
||||||
|
color: #00ffff; /* Cyan */
|
||||||
|
}
|
||||||
|
QLabel#log_level_fatal {
|
||||||
|
color: #ff00ff; /* Fuchsia */
|
||||||
|
}
|
||||||
|
QLabel#log_level_error {
|
||||||
|
color: #ff0000; /* Red */
|
||||||
|
}
|
||||||
|
QLabel#log_level_todo {
|
||||||
|
color: #ff6000; /* Orange */
|
||||||
|
}
|
||||||
|
QLabel#log_level_success {
|
||||||
|
color: #00ff00; /* Green */
|
||||||
|
}
|
||||||
|
QLabel#log_level_warning {
|
||||||
|
color: #ffff00; /* Yellow */
|
||||||
|
}
|
||||||
|
QLabel#log_level_notice {
|
||||||
|
color: #ffffff; /* White */
|
||||||
|
}
|
||||||
|
QLabel#log_level_trace {
|
||||||
|
color: #808080; /* Gray */
|
||||||
|
}
|
||||||
|
QLabel#log_stack {
|
||||||
|
color: #ffffff; /* White */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set TTY colors */
|
||||||
|
QTextEdit#tty_frame {
|
||||||
|
background-color: #000; /* Black */
|
||||||
|
}
|
||||||
|
QLabel#tty_text {
|
||||||
|
color: #fff; /* White */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RSX Debugger */
|
||||||
|
QLabel#rsx_debugger_display_buffer {
|
||||||
|
background-color: #131313;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kernel Explorer */
|
||||||
|
QDialog#kernel_explorer {
|
||||||
|
background-color: #131313;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Memory Viewer */
|
||||||
|
QDialog#memory_viewer {
|
||||||
|
background-color: #131313;
|
||||||
|
color: #FFF;
|
||||||
|
}
|
||||||
|
QLabel#memory_viewer_address_panel {
|
||||||
|
color: #00cbff; /* Font Color: Blue */
|
||||||
|
background-color: #131313;
|
||||||
|
}
|
||||||
|
QLabel#memory_viewer_hex_panel {
|
||||||
|
color: #FFF; /* Font Color: White */
|
||||||
|
background-color: #131313;
|
||||||
|
}
|
||||||
|
QLabel#memory_viewer_ascii_panel {
|
||||||
|
color: #FFF; /* Font Color: White */
|
||||||
|
background-color: #131313;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debugger colors */
|
||||||
|
QLabel#debugger_frame_breakpoint {
|
||||||
|
color: #000; /* Font Color: Black */
|
||||||
|
background-color: #ffff00; /* Yellow */
|
||||||
|
}
|
||||||
|
QLabel#debugger_frame_pc {
|
||||||
|
color: #000; /* Font Color: Black */
|
||||||
|
background-color: #00ff00; /* Green */
|
||||||
|
}
|
||||||
@@ -0,0 +1,330 @@
|
|||||||
|
/*
|
||||||
|
Kuroi (Dark) Theme for RPCS3
|
||||||
|
by Ani @ https://github.com/AniLeo
|
||||||
|
r1 (2017.08.15)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Color Scheme
|
||||||
|
(Darker to Lighter)
|
||||||
|
|
||||||
|
Black
|
||||||
|
> 292929
|
||||||
|
> 323232
|
||||||
|
> 333333
|
||||||
|
> 3d3d3d
|
||||||
|
> 404040
|
||||||
|
> 444444
|
||||||
|
|
||||||
|
Light Grey
|
||||||
|
> 828790
|
||||||
|
> bdc3c7
|
||||||
|
> ecf0f1
|
||||||
|
|
||||||
|
> b1b1b1
|
||||||
|
> 7f8c8d
|
||||||
|
|
||||||
|
Green
|
||||||
|
> 2ecc71
|
||||||
|
|
||||||
|
Red
|
||||||
|
> e74c3c
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Every widget */
|
||||||
|
QWidget {
|
||||||
|
background-color: #323232;
|
||||||
|
color: #bdc3c7;
|
||||||
|
alternate-background-color: #3d3d3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mouse tooltips */
|
||||||
|
QToolTip {
|
||||||
|
border: 0.05em solid #323232;
|
||||||
|
background-color: #323232;
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table headers */
|
||||||
|
QHeaderView::section {
|
||||||
|
background-color: #323232;
|
||||||
|
color: #ecf0f1;
|
||||||
|
padding-left: 0.2em;
|
||||||
|
border: 0.05em solid #323232;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Settings Dialog: Tabs */
|
||||||
|
QTabBar::tab {
|
||||||
|
color: #ecf0f1;
|
||||||
|
border: 0.05em solid #444;
|
||||||
|
border-bottom-style: none;
|
||||||
|
background-color: #333333;
|
||||||
|
padding-left: 0.9em;
|
||||||
|
padding-right: 0.9em;
|
||||||
|
padding-top: 0.2em;
|
||||||
|
padding-bottom: 0.2em;
|
||||||
|
margin-right: -0.2em;
|
||||||
|
}
|
||||||
|
QTabBar::tab:last {
|
||||||
|
margin-right: 0em;
|
||||||
|
}
|
||||||
|
QTabBar::tab:!selected {
|
||||||
|
color: #b1b1b1;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
margin-top: 0.15em;
|
||||||
|
background-color: #292929;
|
||||||
|
}
|
||||||
|
QTabBar::tab:hover {
|
||||||
|
background-color: #333333;
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkboxes */
|
||||||
|
QCheckBox::indicator {
|
||||||
|
border-radius: 0.1em;
|
||||||
|
border: 0.05em solid #ecf0f1;
|
||||||
|
margin-top: 0.05em;
|
||||||
|
width: 0.8em;
|
||||||
|
height: 0.8em;
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:checked {
|
||||||
|
background-color: #2ecc71; /* Green */
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:unchecked {
|
||||||
|
background-color: #e74c3c; /* Red */
|
||||||
|
}
|
||||||
|
QCheckBox::indicator::disabled {
|
||||||
|
background-color: #7f8c8d; /* Gray */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radio Buttons */
|
||||||
|
QRadioButton::indicator {
|
||||||
|
border-radius: 0.4em;
|
||||||
|
border: 0.05em solid;
|
||||||
|
border-color: white;
|
||||||
|
width: 0.8em;
|
||||||
|
height: 0.8em;
|
||||||
|
}
|
||||||
|
QRadioButton::indicator:checked {
|
||||||
|
background-color: #2ecc71; /* Green */
|
||||||
|
}
|
||||||
|
QRadioButton::indicator:unchecked {
|
||||||
|
background-color: #e74c3c; /* Red */
|
||||||
|
}
|
||||||
|
QRadioButton::indicator::disabled {
|
||||||
|
background-color: #7f8c8d; /* Gray */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Combo Boxes */
|
||||||
|
QComboBox {
|
||||||
|
background-color: #404040;
|
||||||
|
color: #fff;
|
||||||
|
border: 0.05em solid #1e1e1e;
|
||||||
|
border-radius: 0.15em;
|
||||||
|
padding-bottom: 0.2em;
|
||||||
|
padding-left: 0.4em;
|
||||||
|
}
|
||||||
|
QComboBox::disabled {
|
||||||
|
background-color: #828790;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Group Boxes (Settings Dialog) */
|
||||||
|
QGroupBox {
|
||||||
|
margin-top: 1em;
|
||||||
|
font-size: 8pt;
|
||||||
|
border: 0.05em solid #bdc3c7;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
QGroupBox::title {
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
subcontrol-position: top;
|
||||||
|
padding: 0.3em 0.5em 0.3em 0.5em;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
QPushButton {
|
||||||
|
background-color: #404040;
|
||||||
|
}
|
||||||
|
QPushButton::disabled {
|
||||||
|
background-color: #444444;
|
||||||
|
color: #828790;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Log and Debugger borders */
|
||||||
|
QTextEdit {
|
||||||
|
border: 0.05em solid #828790;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For dock buttons to be visible */
|
||||||
|
QDockWidget::close-button, QDockWidget::float-button {
|
||||||
|
background-color: #bdc3c7;
|
||||||
|
border: 0.05em solid #000;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable ugly borders */
|
||||||
|
QTabWidget::pane {
|
||||||
|
border: 0em solid #828790;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top menu bar */
|
||||||
|
QMenuBar::item:selected {
|
||||||
|
background: #444444;
|
||||||
|
}
|
||||||
|
QMenu::item {
|
||||||
|
padding-left: 1.5em;
|
||||||
|
padding-right: 0.75em;
|
||||||
|
padding-top: 0.25em;
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
}
|
||||||
|
QMenu::item:selected {
|
||||||
|
background: #444444;
|
||||||
|
border: 0.05em solid #bdc3c7;
|
||||||
|
}
|
||||||
|
QMenu::item:disabled {
|
||||||
|
background-color: #444444;
|
||||||
|
color: #828790;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Libraries list */
|
||||||
|
QListWidget::item:selected {
|
||||||
|
background-color: #3d3d3d;
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
QListWidget::item:hover {
|
||||||
|
background-color: #4c4b4b;
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pad Settings: Controller Image */
|
||||||
|
QLabel#l_controller {
|
||||||
|
color: #bdc3c7;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Game Grid Font */
|
||||||
|
QLabel#gamegrid_font {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 8pt;
|
||||||
|
font-family: Lucida Grande;
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slider on toolbar */
|
||||||
|
QWidget#sizeSliderContainer {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debug UI Settings buttons */
|
||||||
|
QLabel#color_button {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seachbar on main toolbar */
|
||||||
|
QLineEdit#mw_searchbar {
|
||||||
|
margin-left: 0.7em;
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Uniform colors in Toolbar */
|
||||||
|
QToolButton {
|
||||||
|
background: transparent;
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
QToolButton::disabled {
|
||||||
|
background-color: #828790;
|
||||||
|
}
|
||||||
|
QToolButton::hover {
|
||||||
|
background-color: #444444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Theme UI colors */
|
||||||
|
QLabel#gamelist_icon_background_color {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
QLabel#gamelist_toolbar_icon_color {
|
||||||
|
color: #828790;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Windows Taskbar Thumbnail colors */
|
||||||
|
QLabel#thumbnail_icon_color {
|
||||||
|
color: #444444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Log colors */
|
||||||
|
QTextEdit#log_frame {
|
||||||
|
background-color: #000000; /* Black */
|
||||||
|
}
|
||||||
|
QLabel#log_level_always {
|
||||||
|
color: #00ffff; /* Cyan */
|
||||||
|
}
|
||||||
|
QLabel#log_level_fatal {
|
||||||
|
color: #ff00ff; /* Fuchsia */
|
||||||
|
}
|
||||||
|
QLabel#log_level_error {
|
||||||
|
color: #ff0000; /* Red */
|
||||||
|
}
|
||||||
|
QLabel#log_level_todo {
|
||||||
|
color: #ff6000; /* Orange */
|
||||||
|
}
|
||||||
|
QLabel#log_level_success {
|
||||||
|
color: #00ff00; /* Green */
|
||||||
|
}
|
||||||
|
QLabel#log_level_warning {
|
||||||
|
color: #ffff00; /* Yellow */
|
||||||
|
}
|
||||||
|
QLabel#log_level_notice {
|
||||||
|
color: #ffffff; /* White */
|
||||||
|
}
|
||||||
|
QLabel#log_level_trace {
|
||||||
|
color: #808080; /* Gray */
|
||||||
|
}
|
||||||
|
QLabel#log_stack {
|
||||||
|
color: #ffffff; /* White */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set TTY colors */
|
||||||
|
QTextEdit#tty_frame {
|
||||||
|
background-color: #000000; /* Black */
|
||||||
|
}
|
||||||
|
QLabel#tty_text {
|
||||||
|
color: #ffffff; /* White */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RSX Debugger */
|
||||||
|
QLabel#rsx_debugger_display_buffer {
|
||||||
|
background-color: #323232;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kernel Explorer */
|
||||||
|
QDialog#kernel_explorer {
|
||||||
|
background-color: #323232;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Memory Viewer */
|
||||||
|
QDialog#memory_viewer {
|
||||||
|
background-color: #323232;
|
||||||
|
}
|
||||||
|
QLabel#memory_viewer_address_panel {
|
||||||
|
color: #00cbff; /* Font Color: Blue */
|
||||||
|
background-color: #323232;
|
||||||
|
}
|
||||||
|
QLabel#memory_viewer_hex_panel {
|
||||||
|
color: #bdc3c7; /* Font Color: Grey */
|
||||||
|
background-color: #323232;
|
||||||
|
}
|
||||||
|
QLabel#memory_viewer_ascii_panel {
|
||||||
|
color: #bdc3c7; /* Font Color: Grey */
|
||||||
|
background-color: #323232;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debugger colors */
|
||||||
|
QLabel#debugger_frame_breakpoint {
|
||||||
|
color: #000000; /* Font Color: Black */
|
||||||
|
background-color: #ffff00; /* Yellow */
|
||||||
|
}
|
||||||
|
QLabel#debugger_frame_pc {
|
||||||
|
color: #000000; /* Font Color: Black */
|
||||||
|
background-color: #00ff00; /* Green */
|
||||||
|
}
|
||||||
@@ -0,0 +1,329 @@
|
|||||||
|
/*
|
||||||
|
ModernBlue Theme for RPCS3
|
||||||
|
by themitosan @ twitter.com/themitosan
|
||||||
|
(2017.12.12)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Every widget */
|
||||||
|
QWidget {
|
||||||
|
background-color: #232e3a;
|
||||||
|
color: #fff;
|
||||||
|
alternate-background-color: #141e28;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mouse tooltips */
|
||||||
|
QToolTip {
|
||||||
|
padding: 0.125em;
|
||||||
|
border: 0.0625em #444;
|
||||||
|
background-color: #181d24;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Table headers */
|
||||||
|
QHeaderView::section {
|
||||||
|
background-color: #333b44;
|
||||||
|
color: #ecf0f1;
|
||||||
|
padding-left: 0.25em;
|
||||||
|
border: 0.0425em solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Settings Dialog: Tabs */
|
||||||
|
QTabBar::tab {
|
||||||
|
color: #ecf0f1;
|
||||||
|
border: 0.0625em solid #feffff;
|
||||||
|
border-bottom-style: none;
|
||||||
|
background-color: #232e3a;
|
||||||
|
padding-left: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
padding-top: 0.25em;
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
margin-right: -0.125em;
|
||||||
|
border-top-left-radius: 0.25em;
|
||||||
|
border-top-right-radius: 0.25em;
|
||||||
|
}
|
||||||
|
QTabBar::tab:last {
|
||||||
|
margin-right: 0em;
|
||||||
|
}
|
||||||
|
QTabBar::tab:!selected {
|
||||||
|
color: #666;
|
||||||
|
border-bottom-style: solid;
|
||||||
|
margin-top: 0.1875em;
|
||||||
|
background-color: #1b242f;
|
||||||
|
}
|
||||||
|
QTabBar::tab:hover {
|
||||||
|
background-color: #232e3a;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checkboxes */
|
||||||
|
QCheckBox::indicator {
|
||||||
|
border-radius: 0.125em;
|
||||||
|
border-top-right-radius: 0em;
|
||||||
|
border-bottom-left-radius: 0em;
|
||||||
|
border: 0.0625em solid #fff;
|
||||||
|
width: 0.8em;
|
||||||
|
height: 0.8em;
|
||||||
|
margin-top: 0.0625em;
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:checked {
|
||||||
|
background-color: #849ad2; /* Green */
|
||||||
|
}
|
||||||
|
QCheckBox::indicator:unchecked {
|
||||||
|
background-color: #203042; /* Red */
|
||||||
|
}
|
||||||
|
QCheckBox::indicator::disabled {
|
||||||
|
background-color: red; /* Gray */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Radio Buttons */
|
||||||
|
QRadioButton::indicator {
|
||||||
|
border: 0.0625em solid #fff;
|
||||||
|
width: 0.8em;
|
||||||
|
height: 0.8em;
|
||||||
|
margin-top: 0.0625em;
|
||||||
|
border-radius: 0.270em;
|
||||||
|
}
|
||||||
|
QRadioButton::indicator:checked {
|
||||||
|
background-color: #849ad2; /* Green */
|
||||||
|
}
|
||||||
|
QRadioButton::indicator:unchecked {
|
||||||
|
background-color: #203042; /* Red */
|
||||||
|
}
|
||||||
|
QRadioButton::indicator::disabled {
|
||||||
|
background-color: red; /* Gray */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Combo Boxes */
|
||||||
|
QComboBox {
|
||||||
|
background-color: #323c47;
|
||||||
|
color: #fff;
|
||||||
|
border: 0.0625em solid #ddd;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
padding-bottom: 0.125em;
|
||||||
|
padding-left: 0.25em;
|
||||||
|
}
|
||||||
|
QComboBox::disabled {
|
||||||
|
background-color: #11182b;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Group Boxes (Settings Dialog) */
|
||||||
|
QGroupBox {
|
||||||
|
margin-top: 1em;
|
||||||
|
font-size: 8pt;
|
||||||
|
border: 0.05em solid #bdc3c7;
|
||||||
|
border-radius: 0.3em;
|
||||||
|
}
|
||||||
|
QGroupBox::title {
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
subcontrol-position: top;
|
||||||
|
padding: 0.3em 0.5em 0.3em 0.5em;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
QPushButton {
|
||||||
|
background-color: #46598b;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton::disabled {
|
||||||
|
background-color: #20283c;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton::pressed {
|
||||||
|
background-color: #172038;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Log and Debugger borders */
|
||||||
|
QTextEdit {
|
||||||
|
border: 0.05em solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* For dock buttons to be visible */
|
||||||
|
QDockWidget::close-button, QDockWidget::float-button {
|
||||||
|
background-color: #bdc3c7;
|
||||||
|
border: 0.05em solid #777;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable ugly borders */
|
||||||
|
QTabWidget::pane {
|
||||||
|
border: 0em solid #333b44;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Top menu bar */
|
||||||
|
QMenuBar::item:selected {
|
||||||
|
background: #353e48;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
QMenu::item {
|
||||||
|
padding-left: 1.5em;
|
||||||
|
padding-right: 0.75em;
|
||||||
|
padding-top: 0.25em;
|
||||||
|
padding-bottom: 0.25em;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
margin-bottom: 0.125em;
|
||||||
|
}
|
||||||
|
QMenu::item:selected {
|
||||||
|
background: #354454;
|
||||||
|
border: 0.05em solid #bdc3c7;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
QMenu::item:disabled {
|
||||||
|
background-color: #181d24;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Libraries list */
|
||||||
|
QListWidget::item:selected {
|
||||||
|
background-color: #3d3d3d;
|
||||||
|
color: #ecf0f1;
|
||||||
|
border-radius: 0.125em;
|
||||||
|
}
|
||||||
|
QListWidget::item:hover {
|
||||||
|
background-color: #4c4b4b;
|
||||||
|
color: #ecf0f1;
|
||||||
|
border-radius: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Pad Settings: Controller Image */
|
||||||
|
QLabel#l_controller {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Game Grid Font */
|
||||||
|
QLabel#gamegrid_font {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 8pt;
|
||||||
|
font-family: Lucida Grande;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slider on toolbar */
|
||||||
|
QWidget#sizeSliderContainer {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debug UI Settings buttons */
|
||||||
|
QLabel#color_button {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seachbar on main toolbar */
|
||||||
|
QLineEdit#mw_searchbar {
|
||||||
|
margin-left: 0.7em;
|
||||||
|
color: #ecf0f1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Uniform colors in Toolbar */
|
||||||
|
QToolButton {
|
||||||
|
background: transparent;
|
||||||
|
color: transparent;
|
||||||
|
border-radius: 0.125em;
|
||||||
|
padding: 0.09375em;
|
||||||
|
margin-left: 0.125em;
|
||||||
|
margin-top: 0.125em;
|
||||||
|
margin-bottom: 0.125em;
|
||||||
|
min-width: 0.063em;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
QToolButton::disabled {
|
||||||
|
background-color: #11182b;
|
||||||
|
}
|
||||||
|
QToolButton::hover {
|
||||||
|
background-color: #3d475e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Theme UI colors */
|
||||||
|
QLabel#gamelist_icon_background_color {
|
||||||
|
color: transparent;
|
||||||
|
}
|
||||||
|
QLabel#gamelist_toolbar_icon_color {
|
||||||
|
color: #828790;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Windows Taskbar Thumbnail colors */
|
||||||
|
QLabel#thumbnail_icon_color {
|
||||||
|
color: #262626;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set Log colors */
|
||||||
|
QTextEdit#log_frame {
|
||||||
|
background-color: #181d24; /* Black */
|
||||||
|
}
|
||||||
|
QLabel#log_level_always {
|
||||||
|
color: #74ffda; /* Cyan */
|
||||||
|
}
|
||||||
|
QLabel#log_level_fatal {
|
||||||
|
color: #ff35ff; /* Fuchsia */
|
||||||
|
}
|
||||||
|
QLabel#log_level_error {
|
||||||
|
color: #ff2424; /* Red */
|
||||||
|
}
|
||||||
|
QLabel#log_level_todo {
|
||||||
|
color: #ff8c46; /* Orange */
|
||||||
|
}
|
||||||
|
QLabel#log_level_success {
|
||||||
|
color: #7aff7a; /* Green */
|
||||||
|
}
|
||||||
|
QLabel#log_level_warning {
|
||||||
|
color: #f7ff74; /* Yellow */
|
||||||
|
}
|
||||||
|
QLabel#log_level_notice {
|
||||||
|
color: #fff; /* White */
|
||||||
|
}
|
||||||
|
QLabel#log_level_trace {
|
||||||
|
color: #808080; /* Gray */
|
||||||
|
}
|
||||||
|
QLabel#log_stack {
|
||||||
|
color: #fff; /* White */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set TTY colors */
|
||||||
|
QTextEdit#tty_frame {
|
||||||
|
background-color: #181d24; /* Black */
|
||||||
|
}
|
||||||
|
QLabel#tty_text {
|
||||||
|
color: #fff; /* White */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* RSX Debugger */
|
||||||
|
QLabel#rsx_debugger_display_buffer {
|
||||||
|
background-color: #181d24;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kernel Explorer */
|
||||||
|
QDialog#kernel_explorer {
|
||||||
|
background-color: #333b44;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Memory Viewer */
|
||||||
|
QDialog#memory_viewer {
|
||||||
|
background-color: #333b44;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
QLabel#memory_viewer_address_panel {
|
||||||
|
color: #00cbff; /* Font Color: Blue */
|
||||||
|
background-color: #333b44;
|
||||||
|
}
|
||||||
|
QLabel#memory_viewer_hex_panel {
|
||||||
|
color: #fff; /* Font Color: Grey */
|
||||||
|
background-color: #333b44;
|
||||||
|
}
|
||||||
|
QLabel#memory_viewer_ascii_panel {
|
||||||
|
color: #fff; /* Font Color: Grey */
|
||||||
|
background-color: #333b44;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Debugger colors */
|
||||||
|
QLabel#debugger_frame_breakpoint {
|
||||||
|
color: #000; /* Font Color: Black */
|
||||||
|
background-color: #333b44; /* Yellow */
|
||||||
|
}
|
||||||
|
QLabel#debugger_frame_pc {
|
||||||
|
color: #000; /* Font Color: Black */
|
||||||
|
background-color: #333b44; /* Green */
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
@@ -1 +0,0 @@
|
|||||||
make_fself compiled.elf EBOOT.BIN
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
REM You need cmake and python to update the project files
|
REM You need cmake and python to update the project files
|
||||||
REM this script relies on CWD being the path that this script is in
|
REM this script relies on CWD being the path that this script is in
|
||||||
|
|
||||||
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=OFF -DLLVM_INCLUDE_UTILS=OFF -DWITH_POLLY=OFF -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_CONFIGURATION_TYPES="Debug;Release" -DLLVM_TARGETS_TO_BUILD=X86 -DLLVM_EXTERNAL_POLLY_SOURCE_DIR=../polly -DLLVM_BUILD_RUNTIME=OFF -DLLVM_BUILD_TOOLS=OFF -DLLVM_INCLUDE_DOCS=OFF -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DLLVM_INCLUDE_TOOLS=ON -DLLVM_INCLUDE_UTILS=ON -DWITH_POLLY=ON -DCMAKE_SYSTEM_VERSION=6.1 -DCMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION=8.1 ../llvm
|
||||||
|
|
||||||
RD /S /Q cmake
|
RD /S /Q cmake
|
||||||
RD /S /Q CMakeFiles
|
RD /S /Q CMakeFiles
|
||||||
|
|||||||