mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-12-13 15:19:11 +00:00
Compare commits
518 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b2c012723 | ||
|
|
2e54afb295 | ||
|
|
c19b692a66 | ||
|
|
b1885badda | ||
|
|
2a05af22e1 | ||
|
|
4eb6304076 | ||
|
|
9298b074fc | ||
|
|
b520994960 | ||
|
|
171f755c13 | ||
|
|
5691046dcc | ||
|
|
36927a7bbd | ||
|
|
4d0c03fd4a | ||
|
|
f663176a5d | ||
|
|
ba1eb298de | ||
|
|
8bcdd9c068 | ||
|
|
46b1bafa17 | ||
|
|
a711f4d86e | ||
|
|
20ea0ee190 | ||
|
|
74c2c6c74f | ||
|
|
aa22d80c3f | ||
|
|
0e315cbd8f | ||
|
|
6b3746f3a6 | ||
|
|
bfb8b46d7d | ||
|
|
b5029ab940 | ||
|
|
8b7eed3ffc | ||
|
|
eaa18d4e3c | ||
|
|
f4dc8dca8d | ||
|
|
0efe9a4d0f | ||
|
|
f1aea5176d | ||
|
|
96560ed3ca | ||
|
|
dc52cfb9bc | ||
|
|
f62884ffda | ||
|
|
f4bf53402f | ||
|
|
ba109a4c53 | ||
|
|
3a9633f553 | ||
|
|
7bef4a5c70 | ||
|
|
e82c8d2f70 | ||
|
|
367f08c1b5 | ||
|
|
a4b35f275c | ||
|
|
c2adaf41c0 | ||
|
|
517d7f04c6 | ||
|
|
c59d5eef45 | ||
|
|
951128389d | ||
|
|
a583a9abe0 | ||
|
|
7a4244ac8b | ||
|
|
d59536a71c | ||
|
|
f4110c43a7 | ||
|
|
76483f9c7b | ||
|
|
4c7c703ea2 | ||
|
|
9061028ce5 | ||
|
|
0bdd21b4e4 | ||
|
|
636a90386b | ||
|
|
db868ea400 | ||
|
|
99c7fc4049 | ||
|
|
75db253370 | ||
|
|
6331eb1d8a | ||
|
|
169cbe90a5 | ||
|
|
f5c75a5f55 | ||
|
|
3b5d9459f3 | ||
|
|
bf995d659b | ||
|
|
5e5ca2138e | ||
|
|
63b50ff92c | ||
|
|
927f398658 | ||
|
|
f0cc8c0d54 | ||
|
|
be6840f6a9 | ||
|
|
15d10e47ea | ||
|
|
eac1daa5f7 | ||
|
|
b8aac357cb | ||
|
|
fd3bfdae80 | ||
|
|
0a23072938 | ||
|
|
76b4da6212 | ||
|
|
0885d8fce7 | ||
|
|
f975c0603c | ||
|
|
ad68318515 | ||
|
|
c9ba8e989e | ||
|
|
47ac8b6c03 | ||
|
|
bc0b42ee53 | ||
|
|
07baf17c2a | ||
|
|
229740f557 | ||
|
|
42dd79a32d | ||
|
|
c38e1635ea | ||
|
|
84a614dddc | ||
|
|
f1c70ce53c | ||
|
|
02f9d03f57 | ||
|
|
bc85357235 | ||
|
|
e1e697a3ff | ||
|
|
10486db091 | ||
|
|
e1ae052185 | ||
|
|
22ca57b1f2 | ||
|
|
4f1baece33 | ||
|
|
0aaeea4837 | ||
|
|
8a793f64bf | ||
|
|
9424047214 | ||
|
|
6860bb7349 | ||
|
|
5b3e156197 | ||
|
|
745cdd89fd | ||
|
|
6f5dfc576f | ||
|
|
54a1694a2b | ||
|
|
14717b8ecb | ||
|
|
3ed1c31678 | ||
|
|
b6baf78812 | ||
|
|
2af20b0d83 | ||
|
|
cc583b6189 | ||
|
|
da0ab005c7 | ||
|
|
50aa0f5564 | ||
|
|
f686b1df98 | ||
|
|
48c621532c | ||
|
|
7d756e79ae | ||
|
|
8447412c77 | ||
|
|
e67263cc0e | ||
|
|
41496e035a | ||
|
|
e40451a3f7 | ||
|
|
154473d3b3 | ||
|
|
fd3d3c4158 | ||
|
|
b06790dfe5 | ||
|
|
195b94c1f1 | ||
|
|
90c01f8d92 | ||
|
|
679f8f1f36 | ||
|
|
e9b44af469 | ||
|
|
0425bd0fd6 | ||
|
|
e13fb2e366 | ||
|
|
26bb3d40d9 | ||
|
|
7af9de353b | ||
|
|
b0169de7c4 | ||
|
|
e042710eaa | ||
|
|
2b9a8e5605 | ||
|
|
bdf4a5249d | ||
|
|
82cacec8eb | ||
|
|
290e127a4f | ||
|
|
32763b7af6 | ||
|
|
1781fcb9ef | ||
|
|
16451b01e8 | ||
|
|
bb6cca3056 | ||
|
|
b48975f116 | ||
|
|
1cc9e0d37f | ||
|
|
ad43ba5ec7 | ||
|
|
57bdb6cac2 | ||
|
|
1dca54c165 | ||
|
|
67a74a9357 | ||
|
|
7db30d12cb | ||
|
|
0f8bd509b2 | ||
|
|
d1e88c40d8 | ||
|
|
46ef678f55 | ||
|
|
d76210d24f | ||
|
|
4dfe05db24 | ||
|
|
3634026436 | ||
|
|
455b23c6f1 | ||
|
|
b2fb004414 | ||
|
|
1e7f651b1b | ||
|
|
c94cd531f0 | ||
|
|
40cd5c57c5 | ||
|
|
753a3072ee | ||
|
|
43191ff426 | ||
|
|
7728db0dd3 | ||
|
|
6e12642151 | ||
|
|
ebe2aadb4c | ||
|
|
c9d425dc08 | ||
|
|
5f2467b453 | ||
|
|
3b1840b7a9 | ||
|
|
50b27bebd8 | ||
|
|
7624e9482c | ||
|
|
642c0bc367 | ||
|
|
98eb8cb741 | ||
|
|
2188895b40 | ||
|
|
40eef6a066 | ||
|
|
b51c767296 | ||
|
|
843cd01308 | ||
|
|
22357f70c2 | ||
|
|
04fe3a79b9 | ||
|
|
34a4f6e60e | ||
|
|
15b520f4a2 | ||
|
|
5d4812d1a6 | ||
|
|
f3afbfbcec | ||
|
|
0e238c87cb | ||
|
|
89d349ae1c | ||
|
|
95d5343eb4 | ||
|
|
214eab2c52 | ||
|
|
8f2883a388 | ||
|
|
fb0871dbc8 | ||
|
|
9dc3e39fc2 | ||
|
|
a7a8ebcd77 | ||
|
|
d98face501 | ||
|
|
cb14431ee5 | ||
|
|
3c5cb093de | ||
|
|
cfe249debe | ||
|
|
78b4f10cc6 | ||
|
|
46cbee1585 | ||
|
|
78ea536c95 | ||
|
|
1a00b1af24 | ||
|
|
1eb0affdea | ||
|
|
0d498f12b9 | ||
|
|
f111395044 | ||
|
|
0c3260b1b4 | ||
|
|
e1f0cd65af | ||
|
|
e972a8805d | ||
|
|
f5d64239cb | ||
|
|
00465d5e41 | ||
|
|
126cb824ea | ||
|
|
ecfc940381 | ||
|
|
e757063d31 | ||
|
|
131b6f90e0 | ||
|
|
b879dd59c6 | ||
|
|
b6ad512e34 | ||
|
|
363604c6f0 | ||
|
|
e4598e8821 | ||
|
|
fffd373652 | ||
|
|
f8f732e78c | ||
|
|
e0d85a7e58 | ||
|
|
97441b62d1 | ||
|
|
8ad650582a | ||
|
|
02ad2b78fa | ||
|
|
cef7edaea9 | ||
|
|
83abaafdfa | ||
|
|
a55acae5ee | ||
|
|
8d4261efba | ||
|
|
56b2f6c4cf | ||
|
|
0d65ef6f6e | ||
|
|
c5cd4bc5cf | ||
|
|
460c266e04 | ||
|
|
831903799b | ||
|
|
1d8c607c15 | ||
|
|
83671ebf76 | ||
|
|
84c27eea2a | ||
|
|
e1550c9091 | ||
|
|
259d5ef60b | ||
|
|
9aa6c5b951 | ||
|
|
c4bfaa6031 | ||
|
|
f3810cebea | ||
|
|
fda8f1afa3 | ||
|
|
eed4de1da9 | ||
|
|
8aea0fc7ee | ||
|
|
ec0cf25097 | ||
|
|
49fc210833 | ||
|
|
f3c33b29dd | ||
|
|
8057ed408c | ||
|
|
ec7a541263 | ||
|
|
a55cec1954 | ||
|
|
6e58c6c513 | ||
|
|
647694d1b9 | ||
|
|
7ae1d73781 | ||
|
|
be74f65864 | ||
|
|
b5d63a31cc | ||
|
|
c77f62a738 | ||
|
|
52df7f6fe5 | ||
|
|
ad5bd91a13 | ||
|
|
e805b97520 | ||
|
|
c89c7e8fed | ||
|
|
19bbbf994c | ||
|
|
3a163002d7 | ||
|
|
132a9d7d35 | ||
|
|
d8b4114296 | ||
|
|
0b50e2e270 | ||
|
|
0358271b93 | ||
|
|
929e15260d | ||
|
|
4bb578f9fb | ||
|
|
aa847de043 | ||
|
|
9bad66b24d | ||
|
|
78a0a755c5 | ||
|
|
0575853be1 | ||
|
|
a78f8afe58 | ||
|
|
2cdd873681 | ||
|
|
4b93b8b574 | ||
|
|
8379922f8a | ||
|
|
d2127b38de | ||
|
|
2837d848ed | ||
|
|
665261efc5 | ||
|
|
191e64bfa1 | ||
|
|
6f04ea18e4 | ||
|
|
7d1631f9f4 | ||
|
|
46b5437fdf | ||
|
|
461148c227 | ||
|
|
3960283a67 | ||
|
|
f1bc3b4f3d | ||
|
|
a5a1253185 | ||
|
|
564dbc7b94 | ||
|
|
e433f3116d | ||
|
|
73b7d34460 | ||
|
|
7072dfc99f | ||
|
|
361532418c | ||
|
|
a51c8c17e0 | ||
|
|
56f4b8a2b8 | ||
|
|
b3c573f798 | ||
|
|
a4eba8e827 | ||
|
|
4f426b723f | ||
|
|
4d12de8149 | ||
|
|
9dcf40e261 | ||
|
|
0f69697acb | ||
|
|
481f420a89 | ||
|
|
d1b9a5adcc | ||
|
|
74710116f6 | ||
|
|
91444a0545 | ||
|
|
a8a779c79b | ||
|
|
e652369f22 | ||
|
|
cc4ddd28c3 | ||
|
|
0ebe817a28 | ||
|
|
cdca420a2e | ||
|
|
2e6c9b8f98 | ||
|
|
fb738bc247 | ||
|
|
cef92fbcaa | ||
|
|
cc2e13873f | ||
|
|
81e7e4b216 | ||
|
|
1fcfb07421 | ||
|
|
d7c2cb17f3 | ||
|
|
c015ac1344 | ||
|
|
e584444aa3 | ||
|
|
b3bce086b3 | ||
|
|
2968cf5a99 | ||
|
|
adbff4056f | ||
|
|
78ae9613c5 | ||
|
|
2a4798cfa6 | ||
|
|
5c62a00134 | ||
|
|
41b3942833 | ||
|
|
84a341dce5 | ||
|
|
3563b88d8c | ||
|
|
95a30b2b3e | ||
|
|
a3967ccdb4 | ||
|
|
e1132db197 | ||
|
|
0f93edb377 | ||
|
|
4fa501c8d5 | ||
|
|
d14e57f6a8 | ||
|
|
80092b6367 | ||
|
|
201f2817ca | ||
|
|
17ac63d23a | ||
|
|
c8bbecda26 | ||
|
|
a7d45231b7 | ||
|
|
ec0dfb32b5 | ||
|
|
746f2e091d | ||
|
|
269ce12614 | ||
|
|
388548ba47 | ||
|
|
3b92cd1c1a | ||
|
|
1ea5f8f092 | ||
|
|
c80151adde | ||
|
|
d361579618 | ||
|
|
12364b197a | ||
|
|
81ad575b22 | ||
|
|
9a956f5ed0 | ||
|
|
40385e13e7 | ||
|
|
90b04e8cc0 | ||
|
|
7b8177f48e | ||
|
|
a5440e0e43 | ||
|
|
e134fc5f1e | ||
|
|
99a04357d1 | ||
|
|
4e8c178aec | ||
|
|
0cee59cbe6 | ||
|
|
9e5b50c866 | ||
|
|
1d3427780a | ||
|
|
1e5b316ac4 | ||
|
|
c13b29662e | ||
|
|
3b474a12f9 | ||
|
|
eb49193309 | ||
|
|
8695383d35 | ||
|
|
56a6c95730 | ||
|
|
440a693fae | ||
|
|
4695aaa830 | ||
|
|
34a5f2319c | ||
|
|
da2b58f66e | ||
|
|
b3739bea92 | ||
|
|
1c3048ccc2 | ||
|
|
5a7d45fdfa | ||
|
|
53d0a309cc | ||
|
|
c10f9b8269 | ||
|
|
5040be1640 | ||
|
|
d94abffd9a | ||
|
|
4719d32295 | ||
|
|
4f2f9494b0 | ||
|
|
c6ab149c56 | ||
|
|
394331f206 | ||
|
|
8a309c30a9 | ||
|
|
466e071c97 | ||
|
|
82cb298c5c | ||
|
|
5810c88c00 | ||
|
|
62bbad62fc | ||
|
|
5ac7e70e4b | ||
|
|
5c845d4ecc | ||
|
|
6ec68f66a9 | ||
|
|
cfaea1ea6d | ||
|
|
4a21d94871 | ||
|
|
e656093d85 | ||
|
|
562ed2a025 | ||
|
|
4563b6379d | ||
|
|
725814ce01 | ||
|
|
93402620de | ||
|
|
65f9bbbfed | ||
|
|
0eee36cbc7 | ||
|
|
fc50567fc2 | ||
|
|
e791ff4c5c | ||
|
|
8f5bcb0f1c | ||
|
|
af8c748e9c | ||
|
|
3e5d4bb69c | ||
|
|
c055c80c6f | ||
|
|
a4c18b1434 | ||
|
|
4df0d9c035 | ||
|
|
32fc983ef8 | ||
|
|
b0d7feb292 | ||
|
|
c3ecf599ad | ||
|
|
86038e6a71 | ||
|
|
7bf467c653 | ||
|
|
869bf85e9a | ||
|
|
c08fc85b72 | ||
|
|
39b511070a | ||
|
|
5559f35905 | ||
|
|
6f3c767b99 | ||
|
|
121328eced | ||
|
|
fb67d948b6 | ||
|
|
7cdeb51670 | ||
|
|
8879380427 | ||
|
|
e5f638b378 | ||
|
|
4dcd7f0271 | ||
|
|
c0f57df4e6 | ||
|
|
8f33dfe4f1 | ||
|
|
79663789bd | ||
|
|
9d3143231c | ||
|
|
7459d9c333 | ||
|
|
f2f24bb2cd | ||
|
|
f42b8acf47 | ||
|
|
78a32a3c0f | ||
|
|
ddc658f8c8 | ||
|
|
7153bc8d8f | ||
|
|
8e8671323a | ||
|
|
c2be12f009 | ||
|
|
9434cae458 | ||
|
|
2951788afc | ||
|
|
345d55669e | ||
|
|
4e1733222f | ||
|
|
dcc662ff1a | ||
|
|
67c531298a | ||
|
|
77d2172441 | ||
|
|
f7a8e2409c | ||
|
|
55ccec4a38 | ||
|
|
c977011d2f | ||
|
|
b87bca2e46 | ||
|
|
55b50171f8 | ||
|
|
596f4cdf0e | ||
|
|
c25447097e | ||
|
|
6862c9aad7 | ||
|
|
099e685bff | ||
|
|
1926900945 | ||
|
|
33afc00c3a | ||
|
|
3109bd245f | ||
|
|
8b66e9f78d | ||
|
|
4e0757ed55 | ||
|
|
46720e756b | ||
|
|
40211642ca | ||
|
|
444016df7e | ||
|
|
15c9bb0e83 | ||
|
|
5631a31640 | ||
|
|
a76e8f0211 | ||
|
|
283442b42f | ||
|
|
d69341fd31 | ||
|
|
65cd3be4ca | ||
|
|
174b5c0f95 | ||
|
|
48c51bd9ef | ||
|
|
927dc6d95c | ||
|
|
f41829707d | ||
|
|
052473e048 | ||
|
|
e1cf1f500d | ||
|
|
41d64a200d | ||
|
|
284f473a52 | ||
|
|
62780e4e43 | ||
|
|
9091223396 | ||
|
|
dd3f24614b | ||
|
|
ac2e8c2602 | ||
|
|
38f1cc2652 | ||
|
|
1bc27135e3 | ||
|
|
ee72d99947 | ||
|
|
f09a95453e | ||
|
|
e952013fe0 | ||
|
|
f8177902a5 | ||
|
|
4b2db61120 | ||
|
|
248220fef3 | ||
|
|
62c47cb1b7 | ||
|
|
da9e45b582 | ||
|
|
202c1046a1 | ||
|
|
468d7ea80e | ||
|
|
ab7e794f23 | ||
|
|
ee974414d2 | ||
|
|
851995d444 | ||
|
|
e8b0fdd6cc | ||
|
|
a8b4e14bf5 | ||
|
|
8447d6ea1c | ||
|
|
63d2d1ebe8 | ||
|
|
668d5f65dc | ||
|
|
99e1e028c0 | ||
|
|
f0352d2f7d | ||
|
|
c8b704e4a3 | ||
|
|
fc2fd9fd3c | ||
|
|
817a62468e | ||
|
|
55f78a0b94 | ||
|
|
9aabe98e4e | ||
|
|
04933ac863 | ||
|
|
122fe22a32 | ||
|
|
49ffb7b120 | ||
|
|
3218c36b22 | ||
|
|
abe85fd3e0 | ||
|
|
f4c1d827d5 | ||
|
|
115aa61d04 | ||
|
|
333f35ef25 | ||
|
|
f95803664b | ||
|
|
e40ede5db2 | ||
|
|
ff4d37cbc8 | ||
|
|
0351b864d0 | ||
|
|
a86ee7e7f5 | ||
|
|
b1f74660df | ||
|
|
1c5947d93b | ||
|
|
cf84c46a49 | ||
|
|
0677d7a214 | ||
|
|
9b8de8d320 | ||
|
|
4e8b8887df | ||
|
|
5dd1e7c32e | ||
|
|
edc027a8bc | ||
|
|
0bb1c05aff | ||
|
|
cbb13fa39b | ||
|
|
91d4044162 | ||
|
|
e885088632 | ||
|
|
ba9273d1cc | ||
|
|
5f9abd1db4 | ||
|
|
0939082aca | ||
|
|
3a1a3fc75e |
@@ -10,7 +10,7 @@ if grep -nrI '\s$' src *.yml *.txt *.md Doxyfile .gitignore .gitmodules .ci* dis
|
||||
fi
|
||||
|
||||
# Default clang-format points to default 3.5 version one
|
||||
CLANG_FORMAT=clang-format-18
|
||||
CLANG_FORMAT=clang-format-19
|
||||
$CLANG_FORMAT --version
|
||||
|
||||
if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then
|
||||
|
||||
55
.github/ISSUE_TEMPLATE/app-bug-report.yaml
vendored
Normal file
55
.github/ISSUE_TEMPLATE/app-bug-report.yaml
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
|
||||
name: Application Bug Report
|
||||
description: Problem with the application itself (ie. bad file path handling, UX issue)
|
||||
title: "[APP BUG]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Important: Read First
|
||||
|
||||
**Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only.
|
||||
If you have a support request or are unsure about the nature of your issue please contact us on [discord](https://discord.gg/bFJxfftGW6).**
|
||||
|
||||
Please make an effort to make sure your issue isn't already reported.
|
||||
|
||||
Do not create issues involving software piracy, our rules specifically prohibit this. Otherwise your issue will be closed and you will be banned in this repository.
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Checklist
|
||||
options:
|
||||
- label: I have searched for a similar issue in this repository and did not find one.
|
||||
required: true
|
||||
- label: I am using an official build obtained from [releases](https://github.com/shadps4-emu/shadPS4/releases) or updated one of those builds using its in-app updater.
|
||||
required: true
|
||||
- type: textarea
|
||||
id: desc
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
description: "A clear and concise description of what the bug is"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
description: "Detailed steps to reproduce the behavior"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: "A clear and concise description of what you expected to happen"
|
||||
validations:
|
||||
required: false
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: Specify OS Version
|
||||
placeholder: "Example: Windows 11, Arch Linux, MacOS 15"
|
||||
validations:
|
||||
required: true
|
||||
10
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
10
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Discord
|
||||
url: https://discord.gg/bFJxfftGW6
|
||||
about: Get direct support and hang out with us
|
||||
- name: Wiki
|
||||
url: https://github.com/shadps4-emu/shadPS4/wiki
|
||||
about: Information, guides, etc.
|
||||
54
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
Normal file
54
.github/ISSUE_TEMPLATE/feature-request.yaml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
|
||||
name: Feature Request
|
||||
description: Suggest a new feature or improve an existing one
|
||||
title: "[Feature Request]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Important: Read First
|
||||
|
||||
Please make an effort to make sure your issue isn't already reported.
|
||||
|
||||
Do not create issues involving software piracy, our rules specifically prohibit this. Otherwise your issue will be closed and you will be banned in this repository.
|
||||
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Checklist
|
||||
options:
|
||||
- label: I have searched for a similar issue in this repository and did not find one.
|
||||
required: true
|
||||
- type: textarea
|
||||
id: desc
|
||||
attributes:
|
||||
label: Description
|
||||
description: |
|
||||
A concise description of the feature you want
|
||||
|
||||
Include step by step examples of how the feature should work under various circumstances
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: reason
|
||||
attributes:
|
||||
label: Reason
|
||||
description: |
|
||||
Give a reason why you want this feature
|
||||
- How will it make things easier for you?
|
||||
- How does this feature help your enjoyment of the emulator?
|
||||
- What does it provide that isn't being provided currently?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: examples
|
||||
attributes:
|
||||
label: Examples
|
||||
description: |
|
||||
Provide examples of the feature as implemented by other software
|
||||
|
||||
Include screenshots or video if you like to help demonstrate how you'd like this feature to work
|
||||
validations:
|
||||
required: false
|
||||
95
.github/ISSUE_TEMPLATE/game-bug-report.yaml
vendored
Normal file
95
.github/ISSUE_TEMPLATE/game-bug-report.yaml
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
# Docs - https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/syntax-for-githubs-form-schema
|
||||
name: Game Emulation Bug Report
|
||||
description: Problem in a game (ie. graphical artifacts, crashes, etc.)
|
||||
title: "[GAME BUG]: "
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Important: Read First
|
||||
|
||||
**Please do not make support requests on GitHub. Our issue tracker is for tracking bugs and feature requests only.
|
||||
If you have a support request or are unsure about the nature of your issue please contact us on [discord](https://discord.gg/bFJxfftGW6).**
|
||||
|
||||
This repository does not provide support for modded games. You should perform and test a clean game installation before submitting an issue.
|
||||
|
||||
This repository does not provide support for game patches. If you are having issues with patches please refer to [Cheats and Patches Repository](https://github.com/shadps4-emu/ps4_cheats).
|
||||
|
||||
Before submitting an issue please check [Game Compatibility Repository](https://github.com/shadps4-emu/shadps4-game-compatibility) for the information about the status of the game.
|
||||
|
||||
Please make an effort to make sure your issue isn't already reported.
|
||||
|
||||
Do not create issues involving software piracy, our rules specifically prohibit this. Otherwise your issue will be closed and you will be banned in this repository.
|
||||
- type: checkboxes
|
||||
id: checklist
|
||||
attributes:
|
||||
label: Checklist (we expect you to perform these steps before opening the issue)
|
||||
options:
|
||||
- label: I have searched for a similar issue in this repository and did not find one.
|
||||
required: true
|
||||
- label: I am using an official build obtained from [releases](https://github.com/shadps4-emu/shadPS4/releases) or updated one of those builds using its in-app updater.
|
||||
required: true
|
||||
- label: I have re-dumped the game and performed a clean install without mods and the issue is still present.
|
||||
required: true
|
||||
- label: I have disabled all patches and cheats and the issue is still present.
|
||||
required: true
|
||||
- label: I have all the required [system modules](https://github.com/shadps4-emu/shadps4-game-compatibility?tab=readme-ov-file#informations) installed.
|
||||
required: true
|
||||
- type: textarea
|
||||
id: desc
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
description: "A clear and concise description of what the bug is"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: repro
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
description: "Detailed steps to reproduce the behavior"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: os
|
||||
attributes:
|
||||
label: Specify OS Version
|
||||
placeholder: "Example: Windows 11, Arch Linux, MacOS 15"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: cpu
|
||||
attributes:
|
||||
label: CPU
|
||||
placeholder: "Example: Intel Core i7-8700"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: gpu
|
||||
attributes:
|
||||
label: GPU
|
||||
placeholder: "Example: nVidia GTX 1650"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: ram
|
||||
attributes:
|
||||
label: Amount of RAM in GB
|
||||
placeholder: "Example: 16 GB"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: vram
|
||||
attributes:
|
||||
label: Amount of VRAM in GB
|
||||
placeholder: "Example: 8 GB"
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: "Log File"
|
||||
description: Drag and drop the log file here. It can be found by right clicking on a game name -> Open Folder... -> Open Log Folder. Make sure that the log type is set to `sync`.
|
||||
validations:
|
||||
required: true
|
||||
104
.github/workflows/build.yml
vendored
104
.github/workflows/build.yml
vendored
@@ -14,14 +14,14 @@ env:
|
||||
|
||||
jobs:
|
||||
reuse:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: fsfe/reuse-action@v5
|
||||
|
||||
clang-format:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
continue-on-error: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -30,16 +30,16 @@ jobs:
|
||||
- name: Install
|
||||
run: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main'
|
||||
sudo add-apt-repository 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-19 main'
|
||||
sudo apt update
|
||||
sudo apt install clang-format-18
|
||||
sudo apt install clang-format-19
|
||||
- name: Build
|
||||
env:
|
||||
COMMIT_RANGE: ${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }}
|
||||
run: ./.ci/clang-format.sh
|
||||
|
||||
get-info:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
date: ${{ steps.vars.outputs.date }}
|
||||
shorthash: ${{ steps.vars.outputs.shorthash }}
|
||||
@@ -57,7 +57,7 @@ jobs:
|
||||
echo "fullhash=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
|
||||
|
||||
windows-sdl:
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2025
|
||||
needs: get-info
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -76,7 +76,7 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
|
||||
with:
|
||||
@@ -101,7 +101,7 @@ jobs:
|
||||
path: ${{github.workspace}}/build/shadPS4.exe
|
||||
|
||||
windows-qt:
|
||||
runs-on: windows-latest
|
||||
runs-on: windows-2025
|
||||
needs: get-info
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@@ -111,10 +111,10 @@ jobs:
|
||||
- name: Setup Qt
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: 6.7.3
|
||||
version: 6.9.0
|
||||
host: windows
|
||||
target: desktop
|
||||
arch: win64_msvc2019_64
|
||||
arch: win64_msvc2022_64
|
||||
archives: qtbase qttools
|
||||
modules: qtmultimedia
|
||||
|
||||
@@ -130,7 +130,7 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-build
|
||||
with:
|
||||
@@ -186,7 +186,7 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
env:
|
||||
cache-name: ${{runner.os}}-sdl-cache-cmake-build
|
||||
with:
|
||||
@@ -228,7 +228,7 @@ jobs:
|
||||
- name: Setup Qt
|
||||
uses: jurplel/install-qt-action@v4
|
||||
with:
|
||||
version: 6.7.3
|
||||
version: 6.9.0
|
||||
host: mac
|
||||
target: desktop
|
||||
arch: clang_64
|
||||
@@ -247,7 +247,7 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
env:
|
||||
cache-name: ${{runner.os}}-qt-cache-cmake-build
|
||||
with:
|
||||
@@ -296,7 +296,7 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-cache-cmake-build
|
||||
with:
|
||||
@@ -352,7 +352,7 @@ jobs:
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.14
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-cache-cmake-build
|
||||
with:
|
||||
@@ -376,6 +376,78 @@ jobs:
|
||||
name: shadps4-linux-qt-${{ needs.get-info.outputs.date }}-${{ needs.get-info.outputs.shorthash }}
|
||||
path: Shadps4-qt.AppImage
|
||||
|
||||
linux-sdl-gcc:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: get-info
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential libasound2-dev libpulse-dev libopenal-dev libudev-dev
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-gcc-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-sdl-gcc-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
|
||||
|
||||
linux-qt-gcc:
|
||||
runs-on: ubuntu-24.04
|
||||
needs: get-info
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Install dependencies
|
||||
run: sudo apt-get update && sudo apt install -y libx11-dev libxext-dev libwayland-dev libdecor-0-dev libxkbcommon-dev libglfw3-dev libgles2-mesa-dev libfuse2 gcc-14 build-essential qt6-base-dev qt6-tools-dev qt6-multimedia-dev libasound2-dev libpulse-dev libopenal-dev libudev-dev
|
||||
|
||||
- name: Cache CMake Configuration
|
||||
uses: actions/cache@v4
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-configuration
|
||||
with:
|
||||
path: |
|
||||
${{github.workspace}}/build
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
restore-keys: |
|
||||
${{ env.cache-name }}-
|
||||
|
||||
- name: Cache CMake Build
|
||||
uses: hendrikmuhs/ccache-action@v1.2.17
|
||||
env:
|
||||
cache-name: ${{ runner.os }}-qt-gcc-cache-cmake-build
|
||||
with:
|
||||
append-timestamp: false
|
||||
key: ${{ env.cache-name }}-${{ hashFiles('**/CMakeLists.txt', 'cmake/**') }}
|
||||
|
||||
- name: Configure CMake
|
||||
run: cmake --fresh -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DCMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 -DENABLE_QT_GUI=ON -DENABLE_UPDATER=ON -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel $(nproc)
|
||||
|
||||
pre-release:
|
||||
if: github.ref == 'refs/heads/main' && github.repository == 'shadps4-emu/shadPS4' && github.event_name == 'push'
|
||||
needs: [get-info, windows-sdl, windows-qt, macos-sdl, macos-qt, linux-sdl, linux-qt]
|
||||
|
||||
11
.github/workflows/scripts/update_translation.sh
vendored
Executable file
11
.github/workflows/scripts/update_translation.sh
vendored
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
sudo apt-get -y install qt6-l10n-tools python3
|
||||
|
||||
SCRIPT_PATH="src/qt_gui/translations/update_translation.sh"
|
||||
|
||||
chmod +x "$SCRIPT_PATH"
|
||||
|
||||
PATH=/usr/lib/qt6/bin:$PATH "$SCRIPT_PATH"
|
||||
30
.github/workflows/update_translation.yml
vendored
Normal file
30
.github/workflows/update_translation.yml
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
name: Update Translation
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *" # Every day at 12am UTC.
|
||||
workflow_dispatch: # As well as manually.
|
||||
|
||||
jobs:
|
||||
update:
|
||||
if: github.repository == 'shadps4-emu/shadPS4'
|
||||
name: "Update Translation"
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set execution permissions for the script
|
||||
run: chmod +x ./.github/workflows/scripts/update_translation.sh
|
||||
|
||||
- name: Update Base Translation
|
||||
run: ./.github/workflows/scripts/update_translation.sh
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
token: ${{ secrets.SHADPS4_TOKEN_REPO }}
|
||||
title: "Qt GUI: Update Translation"
|
||||
commit-message: "[ci skip] Qt GUI: Update Translation."
|
||||
body: "Daily update of translation sources."
|
||||
branch: update-translation
|
||||
delete-branch: true
|
||||
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -6,10 +6,6 @@
|
||||
path = externals/cryptopp
|
||||
url = https://github.com/shadps4-emu/ext-cryptopp.git
|
||||
shallow = true
|
||||
[submodule "externals/cryptoppwin"]
|
||||
path = externals/cryptoppwin
|
||||
url = https://github.com/shadps4-emu/ext-cryptoppwin.git
|
||||
shallow = true
|
||||
[submodule "externals/zlib-ng"]
|
||||
path = externals/zlib-ng
|
||||
url = https://github.com/shadps4-emu/ext-zlib-ng.git
|
||||
|
||||
194
CMakeLists.txt
194
CMakeLists.txt
@@ -8,7 +8,7 @@ set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
if(APPLE)
|
||||
enable_language(OBJC)
|
||||
list(APPEND ADDITIONAL_LANGUAGES OBJC)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 14)
|
||||
endif()
|
||||
|
||||
@@ -16,7 +16,7 @@ if (NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
|
||||
project(shadPS4 CXX C ASM)
|
||||
project(shadPS4 CXX C ASM ${ADDITIONAL_LANGUAGES})
|
||||
|
||||
# Forcing PIE makes sure that the base address is high enough so that it doesn't clash with the PS4 memory.
|
||||
if(UNIX AND NOT APPLE)
|
||||
@@ -37,8 +37,10 @@ option(ENABLE_UPDATER "Enables the options to updater" ON)
|
||||
# First, determine whether to use CMAKE_OSX_ARCHITECTURES or CMAKE_SYSTEM_PROCESSOR.
|
||||
if (APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||
set(BASE_ARCHITECTURE "${CMAKE_OSX_ARCHITECTURES}")
|
||||
else()
|
||||
elseif (CMAKE_SYSTEM_PROCESSOR)
|
||||
set(BASE_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}")
|
||||
else()
|
||||
set(BASE_ARCHITECTURE "${CMAKE_HOST_SYSTEM_PROCESSOR}")
|
||||
endif()
|
||||
|
||||
# Next, match common architecture strings down to a known common value.
|
||||
@@ -50,7 +52,12 @@ else()
|
||||
message(FATAL_ERROR "Unsupported CPU architecture: ${BASE_ARCHITECTURE}")
|
||||
endif()
|
||||
|
||||
if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
if (ARCHITECTURE STREQUAL "x86_64")
|
||||
# Set x86_64 target level to Sandy Bridge to generally match what is supported for PS4 guest code with CPU patches.
|
||||
add_compile_options(-march=sandybridge)
|
||||
endif()
|
||||
|
||||
if (APPLE AND ARCHITECTURE STREQUAL "x86_64" AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")
|
||||
# Exclude ARM homebrew path to avoid conflicts when cross compiling.
|
||||
list(APPEND CMAKE_IGNORE_PREFIX_PATH "/opt/homebrew")
|
||||
|
||||
@@ -106,7 +113,48 @@ git_describe(GIT_DESC --always --long --dirty)
|
||||
git_branch_name(GIT_BRANCH)
|
||||
string(TIMESTAMP BUILD_DATE "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp" @ONLY)
|
||||
# Try to get the upstream remote and branch
|
||||
execute_process(
|
||||
COMMAND git rev-parse --abbrev-ref --symbolic-full-name @{u}
|
||||
OUTPUT_VARIABLE GIT_REMOTE_NAME
|
||||
RESULT_VARIABLE GIT_BRANCH_RESULT
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
# If there's no upstream set or the command failed, check remote.pushDefault
|
||||
if (GIT_BRANCH_RESULT OR GIT_REMOTE_NAME STREQUAL "")
|
||||
execute_process(
|
||||
COMMAND git config --get remote.pushDefault
|
||||
OUTPUT_VARIABLE GIT_REMOTE_NAME
|
||||
RESULT_VARIABLE GIT_PUSH_DEFAULT_RESULT
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
# If remote.pushDefault is not set or fails, default to origin
|
||||
if (GIT_PUSH_DEFAULT_RESULT OR GIT_REMOTE_NAME STREQUAL "")
|
||||
set(GIT_REMOTE_NAME "origin")
|
||||
endif()
|
||||
else()
|
||||
# Extract remote name if the output contains a remote/branch format
|
||||
string(FIND "${GIT_REMOTE_NAME}" "/" INDEX)
|
||||
if (INDEX GREATER -1)
|
||||
string(SUBSTRING "${GIT_REMOTE_NAME}" 0 "${INDEX}" GIT_REMOTE_NAME)
|
||||
else()
|
||||
# If no remote is present (only a branch name), default to origin
|
||||
set(GIT_REMOTE_NAME "origin")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Get remote link
|
||||
execute_process(
|
||||
COMMAND git config --get remote.${GIT_REMOTE_NAME}.url
|
||||
OUTPUT_VARIABLE GIT_REMOTE_URL
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/src/common/scm_rev.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp" @ONLY)
|
||||
|
||||
find_package(Boost 1.84.0 CONFIG)
|
||||
find_package(FFmpeg 5.1.2 MODULE)
|
||||
@@ -120,7 +168,7 @@ find_package(SDL3 3.1.2 CONFIG)
|
||||
find_package(stb MODULE)
|
||||
find_package(toml11 4.2.0 CONFIG)
|
||||
find_package(tsl-robin-map 1.3.0 CONFIG)
|
||||
find_package(VulkanHeaders 1.4.303 CONFIG)
|
||||
find_package(VulkanHeaders 1.4.305 CONFIG)
|
||||
find_package(VulkanMemoryAllocator 3.1.0 CONFIG)
|
||||
find_package(xbyak 7.07 CONFIG)
|
||||
find_package(xxHash 0.8.2 MODULE)
|
||||
@@ -154,6 +202,7 @@ endif()
|
||||
|
||||
add_subdirectory(externals)
|
||||
include_directories(src)
|
||||
include_directories(Resources)
|
||||
|
||||
if(ENABLE_QT_GUI)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Widgets Concurrent LinguistTools Network Multimedia)
|
||||
@@ -188,6 +237,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp
|
||||
src/core/libraries/ajm/ajm_context.cpp
|
||||
src/core/libraries/ajm/ajm_context.h
|
||||
src/core/libraries/ajm/ajm_error.h
|
||||
src/core/libraries/ajm/ajm_instance_statistics.cpp
|
||||
src/core/libraries/ajm/ajm_instance_statistics.h
|
||||
src/core/libraries/ajm/ajm_instance.cpp
|
||||
src/core/libraries/ajm/ajm_instance.h
|
||||
src/core/libraries/ajm/ajm_mp3.cpp
|
||||
@@ -198,15 +249,16 @@ set(AUDIO_LIB src/core/libraries/audio/audioin.cpp
|
||||
src/core/libraries/audio/audioin.h
|
||||
src/core/libraries/audio/audioout.cpp
|
||||
src/core/libraries/audio/audioout.h
|
||||
src/core/libraries/audio/sdl_audio.cpp
|
||||
src/core/libraries/audio/sdl_audio.h
|
||||
src/core/libraries/audio/audioout_backend.h
|
||||
src/core/libraries/audio/audioout_error.h
|
||||
src/core/libraries/audio/sdl_audio.cpp
|
||||
src/core/libraries/ngs2/ngs2.cpp
|
||||
src/core/libraries/ngs2/ngs2.h
|
||||
)
|
||||
|
||||
set(GNM_LIB src/core/libraries/gnmdriver/gnmdriver.cpp
|
||||
src/core/libraries/gnmdriver/gnmdriver.h
|
||||
src/core/libraries/gnmdriver/gnmdriver_init.h
|
||||
src/core/libraries/gnmdriver/gnm_error.h
|
||||
)
|
||||
|
||||
@@ -247,10 +299,15 @@ set(KERNEL_LIB src/core/libraries/kernel/sync/mutex.cpp
|
||||
src/core/libraries/kernel/time.h
|
||||
src/core/libraries/kernel/orbis_error.h
|
||||
src/core/libraries/kernel/posix_error.h
|
||||
src/core/libraries/kernel/aio.cpp
|
||||
src/core/libraries/kernel/aio.h
|
||||
)
|
||||
|
||||
set(NETWORK_LIBS src/core/libraries/network/http.cpp
|
||||
src/core/libraries/network/http.h
|
||||
src/core/libraries/network/http_error.h
|
||||
src/core/libraries/network/http2.cpp
|
||||
src/core/libraries/network/http2.h
|
||||
src/core/libraries/network/net.cpp
|
||||
src/core/libraries/network/netctl.cpp
|
||||
src/core/libraries/network/netctl.h
|
||||
@@ -260,6 +317,8 @@ set(NETWORK_LIBS src/core/libraries/network/http.cpp
|
||||
src/core/libraries/network/net.h
|
||||
src/core/libraries/network/ssl.cpp
|
||||
src/core/libraries/network/ssl.h
|
||||
src/core/libraries/network/ssl2.cpp
|
||||
src/core/libraries/network/ssl2.h
|
||||
)
|
||||
|
||||
set(AVPLAYER_LIB src/core/libraries/avplayer/avplayer_common.cpp
|
||||
@@ -333,6 +392,10 @@ set(SYSTEM_LIBS src/core/libraries/system/commondialog.cpp
|
||||
src/core/libraries/share_play/shareplay.h
|
||||
src/core/libraries/razor_cpu/razor_cpu.cpp
|
||||
src/core/libraries/razor_cpu/razor_cpu.h
|
||||
src/core/libraries/mouse/mouse.cpp
|
||||
src/core/libraries/mouse/mouse.h
|
||||
src/core/libraries/web_browser_dialog/webbrowserdialog.cpp
|
||||
src/core/libraries/web_browser_dialog/webbrowserdialog.h
|
||||
)
|
||||
|
||||
set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
|
||||
@@ -343,8 +406,17 @@ set(VIDEOOUT_LIB src/core/libraries/videoout/buffer.h
|
||||
src/core/libraries/videoout/videoout_error.h
|
||||
)
|
||||
|
||||
set(LIBC_SOURCES src/core/libraries/libc_internal/libc_internal.cpp
|
||||
set(HLE_LIBC_INTERNAL_LIB src/core/libraries/libc_internal/libc_internal.cpp
|
||||
src/core/libraries/libc_internal/libc_internal.h
|
||||
src/core/libraries/libc_internal/libc_internal_io.cpp
|
||||
src/core/libraries/libc_internal/libc_internal_io.h
|
||||
src/core/libraries/libc_internal/libc_internal_memory.cpp
|
||||
src/core/libraries/libc_internal/libc_internal_memory.h
|
||||
src/core/libraries/libc_internal/libc_internal_str.cpp
|
||||
src/core/libraries/libc_internal/libc_internal_str.h
|
||||
src/core/libraries/libc_internal/libc_internal_math.cpp
|
||||
src/core/libraries/libc_internal/libc_internal_math.h
|
||||
src/core/libraries/libc_internal/printf.h
|
||||
)
|
||||
|
||||
set(IME_LIB src/core/libraries/ime/error_dialog.cpp
|
||||
@@ -410,7 +482,9 @@ set(VDEC_LIB src/core/libraries/videodec/videodec2_impl.cpp
|
||||
src/core/libraries/videodec/videodec_impl.h
|
||||
)
|
||||
|
||||
set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp
|
||||
set(NP_LIBS src/core/libraries/np_common/np_common.cpp
|
||||
src/core/libraries/np_common/np_common.h
|
||||
src/core/libraries/np_manager/np_manager.cpp
|
||||
src/core/libraries/np_manager/np_manager.h
|
||||
src/core/libraries/np_score/np_score.cpp
|
||||
src/core/libraries/np_score/np_score.h
|
||||
@@ -419,10 +493,25 @@ set(NP_LIBS src/core/libraries/np_manager/np_manager.cpp
|
||||
src/core/libraries/np_trophy/trophy_ui.cpp
|
||||
src/core/libraries/np_trophy/trophy_ui.h
|
||||
src/core/libraries/np_trophy/np_trophy_error.h
|
||||
src/core/libraries/np_web_api/np_web_api.cpp
|
||||
src/core/libraries/np_web_api/np_web_api.h
|
||||
src/core/libraries/np_party/np_party.cpp
|
||||
src/core/libraries/np_party/np_party.h
|
||||
)
|
||||
|
||||
set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp
|
||||
src/core/libraries/zlib/zlib_sce.h
|
||||
src/core/libraries/zlib/zlib_error.h
|
||||
)
|
||||
|
||||
set(VR_LIBS src/core/libraries/hmd/hmd.cpp
|
||||
src/core/libraries/hmd/hmd.h
|
||||
)
|
||||
|
||||
set(MISC_LIBS src/core/libraries/screenshot/screenshot.cpp
|
||||
src/core/libraries/screenshot/screenshot.h
|
||||
src/core/libraries/move/move.cpp
|
||||
src/core/libraries/move/move.h
|
||||
)
|
||||
|
||||
set(DEV_TOOLS src/core/devtools/layer.cpp
|
||||
@@ -502,6 +591,7 @@ set(COMMON src/common/logging/backend.cpp
|
||||
src/common/spin_lock.h
|
||||
src/common/stb.cpp
|
||||
src/common/stb.h
|
||||
src/common/string_literal.h
|
||||
src/common/string_util.cpp
|
||||
src/common/string_util.h
|
||||
src/common/thread.cpp
|
||||
@@ -517,7 +607,7 @@ set(COMMON src/common/logging/backend.cpp
|
||||
src/common/number_utils.cpp
|
||||
src/common/memory_patcher.h
|
||||
src/common/memory_patcher.cpp
|
||||
src/common/scm_rev.cpp
|
||||
${CMAKE_CURRENT_BINARY_DIR}/src/common/scm_rev.cpp
|
||||
src/common/scm_rev.h
|
||||
)
|
||||
|
||||
@@ -540,6 +630,16 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||
src/core/devices/logger.cpp
|
||||
src/core/devices/logger.h
|
||||
src/core/devices/nop_device.h
|
||||
src/core/devices/console_device.cpp
|
||||
src/core/devices/console_device.h
|
||||
src/core/devices/deci_tty6_device.cpp
|
||||
src/core/devices/deci_tty6_device.h
|
||||
src/core/devices/random_device.cpp
|
||||
src/core/devices/random_device.h
|
||||
src/core/devices/urandom_device.cpp
|
||||
src/core/devices/urandom_device.h
|
||||
src/core/devices/srandom_device.cpp
|
||||
src/core/devices/srandom_device.h
|
||||
src/core/file_format/pfs.h
|
||||
src/core/file_format/pkg.cpp
|
||||
src/core/file_format/pkg.h
|
||||
@@ -551,8 +651,6 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||
src/core/file_format/playgo_chunk.h
|
||||
src/core/file_format/trp.cpp
|
||||
src/core/file_format/trp.h
|
||||
src/core/file_format/splash.h
|
||||
src/core/file_format/splash.cpp
|
||||
src/core/file_sys/fs.cpp
|
||||
src/core/file_sys/fs.h
|
||||
src/core/loader.cpp
|
||||
@@ -572,7 +670,7 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||
${KERNEL_LIB}
|
||||
${NETWORK_LIBS}
|
||||
${SYSTEM_LIBS}
|
||||
${LIBC_SOURCES}
|
||||
${HLE_LIBC_INTERNAL_LIB}
|
||||
${PAD_LIB}
|
||||
${VIDEOOUT_LIB}
|
||||
${NP_LIBS}
|
||||
@@ -581,10 +679,12 @@ set(CORE src/core/aerolib/stubs.cpp
|
||||
${PLAYGO_LIB}
|
||||
${RANDOM_LIB}
|
||||
${USBD_LIB}
|
||||
${ZLIB_LIB}
|
||||
${MISC_LIBS}
|
||||
${IME_LIB}
|
||||
${FIBER_LIB}
|
||||
${VDEC_LIB}
|
||||
${VR_LIBS}
|
||||
${DEV_TOOLS}
|
||||
src/core/debug_state.cpp
|
||||
src/core/debug_state.h
|
||||
@@ -670,11 +770,12 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
|
||||
src/shader_recompiler/ir/passes/hull_shader_transform.cpp
|
||||
src/shader_recompiler/ir/passes/identity_removal_pass.cpp
|
||||
src/shader_recompiler/ir/passes/ir_passes.h
|
||||
src/shader_recompiler/ir/passes/lower_shared_mem_to_registers.cpp
|
||||
src/shader_recompiler/ir/passes/lower_buffer_format_to_raw.cpp
|
||||
src/shader_recompiler/ir/passes/resource_tracking_pass.cpp
|
||||
src/shader_recompiler/ir/passes/ring_access_elimination.cpp
|
||||
src/shader_recompiler/ir/passes/shader_info_collection_pass.cpp
|
||||
src/shader_recompiler/ir/passes/shared_memory_barrier_pass.cpp
|
||||
src/shader_recompiler/ir/passes/shared_memory_to_storage_pass.cpp
|
||||
src/shader_recompiler/ir/passes/ssa_rewrite_pass.cpp
|
||||
src/shader_recompiler/ir/abstract_syntax_list.h
|
||||
src/shader_recompiler/ir/attribute.cpp
|
||||
@@ -694,6 +795,7 @@ set(SHADER_RECOMPILER src/shader_recompiler/exception.h
|
||||
src/shader_recompiler/ir/post_order.h
|
||||
src/shader_recompiler/ir/program.cpp
|
||||
src/shader_recompiler/ir/program.h
|
||||
src/shader_recompiler/ir/reinterpret.h
|
||||
src/shader_recompiler/ir/reg.h
|
||||
src/shader_recompiler/ir/type.cpp
|
||||
src/shader_recompiler/ir/type.h
|
||||
@@ -723,8 +825,6 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
|
||||
src/video_core/renderer_vulkan/vk_common.h
|
||||
src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
|
||||
src/video_core/renderer_vulkan/vk_compute_pipeline.h
|
||||
src/video_core/renderer_vulkan/vk_descriptor_update_queue.cpp
|
||||
src/video_core/renderer_vulkan/vk_descriptor_update_queue.h
|
||||
src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
|
||||
src/video_core/renderer_vulkan/vk_graphics_pipeline.h
|
||||
src/video_core/renderer_vulkan/vk_instance.cpp
|
||||
@@ -751,6 +851,10 @@ set(VIDEO_CORE src/video_core/amdgpu/liverpool.cpp
|
||||
src/video_core/renderer_vulkan/vk_shader_util.h
|
||||
src/video_core/renderer_vulkan/vk_swapchain.cpp
|
||||
src/video_core/renderer_vulkan/vk_swapchain.h
|
||||
src/video_core/renderer_vulkan/host_passes/fsr_pass.cpp
|
||||
src/video_core/renderer_vulkan/host_passes/fsr_pass.h
|
||||
src/video_core/renderer_vulkan/host_passes/pp_pass.cpp
|
||||
src/video_core/renderer_vulkan/host_passes/pp_pass.h
|
||||
src/video_core/texture_cache/image.cpp
|
||||
src/video_core/texture_cache/image.h
|
||||
src/video_core/texture_cache/image_info.cpp
|
||||
@@ -788,6 +892,10 @@ set(IMGUI src/imgui/imgui_config.h
|
||||
|
||||
set(INPUT src/input/controller.cpp
|
||||
src/input/controller.h
|
||||
src/input/input_handler.cpp
|
||||
src/input/input_handler.h
|
||||
src/input/input_mouse.cpp
|
||||
src/input/input_mouse.h
|
||||
)
|
||||
|
||||
set(EMULATOR src/emulator.cpp
|
||||
@@ -816,6 +924,12 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
|
||||
src/qt_gui/cheats_patches.h
|
||||
src/qt_gui/compatibility_info.cpp
|
||||
src/qt_gui/compatibility_info.h
|
||||
src/qt_gui/control_settings.cpp
|
||||
src/qt_gui/control_settings.h
|
||||
src/qt_gui/control_settings.ui
|
||||
src/qt_gui/kbm_gui.cpp
|
||||
src/qt_gui/kbm_gui.h
|
||||
src/qt_gui/kbm_gui.ui
|
||||
src/qt_gui/main_window_ui.h
|
||||
src/qt_gui/main_window.cpp
|
||||
src/qt_gui/main_window.h
|
||||
@@ -837,6 +951,10 @@ set(QT_GUI src/qt_gui/about_dialog.cpp
|
||||
src/qt_gui/trophy_viewer.h
|
||||
src/qt_gui/elf_viewer.cpp
|
||||
src/qt_gui/elf_viewer.h
|
||||
src/qt_gui/kbm_config_dialog.cpp
|
||||
src/qt_gui/kbm_config_dialog.h
|
||||
src/qt_gui/kbm_help_dialog.cpp
|
||||
src/qt_gui/kbm_help_dialog.h
|
||||
src/qt_gui/main_window_themes.cpp
|
||||
src/qt_gui/main_window_themes.h
|
||||
src/qt_gui/settings_dialog.cpp
|
||||
@@ -884,7 +1002,7 @@ endif()
|
||||
create_target_directory_groups(shadps4)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers)
|
||||
target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml stb::headers cryptopp::cryptopp)
|
||||
|
||||
target_compile_definitions(shadps4 PRIVATE IMGUI_USER_CONFIG="imgui/imgui_config.h")
|
||||
target_compile_definitions(Dear_ImGui PRIVATE IMGUI_USER_CONFIG="${PROJECT_SOURCE_DIR}/src/imgui/imgui_config.h")
|
||||
@@ -901,11 +1019,19 @@ endif()
|
||||
if (APPLE)
|
||||
if (ENABLE_QT_GUI)
|
||||
# Include MoltenVK in the app bundle, along with an ICD file so it can be found by the system Vulkan loader if used for loading layers.
|
||||
target_sources(shadps4 PRIVATE externals/MoltenVK/MoltenVK_icd.json)
|
||||
set_source_files_properties(externals/MoltenVK/MoltenVK_icd.json
|
||||
PROPERTIES MACOSX_PACKAGE_LOCATION Resources/vulkan/icd.d)
|
||||
add_custom_command(TARGET shadps4 POST_BUILD
|
||||
COMMAND cmake -E copy $<TARGET_LINKER_FILE:MoltenVK> $<TARGET_BUNDLE_DIR:shadps4>/Contents/Frameworks/libMoltenVK.dylib)
|
||||
set(MVK_ICD ${CMAKE_CURRENT_SOURCE_DIR}/externals/MoltenVK/MoltenVK_icd.json)
|
||||
target_sources(shadps4 PRIVATE ${MVK_ICD})
|
||||
set_source_files_properties(${MVK_ICD} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/vulkan/icd.d)
|
||||
|
||||
set(MVK_DYLIB_SRC ${CMAKE_CURRENT_BINARY_DIR}/externals/MoltenVK/libMoltenVK.dylib)
|
||||
set(MVK_DYLIB_DST ${CMAKE_CURRENT_BINARY_DIR}/shadps4.app/Contents/Frameworks/libMoltenVK.dylib)
|
||||
add_custom_command(
|
||||
OUTPUT ${MVK_DYLIB_DST}
|
||||
DEPENDS ${MVK_DYLIB_SRC}
|
||||
COMMAND cmake -E copy ${MVK_DYLIB_SRC} ${MVK_DYLIB_DST})
|
||||
add_custom_target(CopyMoltenVK DEPENDS ${MVK_DYLIB_DST})
|
||||
add_dependencies(CopyMoltenVK MoltenVK)
|
||||
add_dependencies(shadps4 CopyMoltenVK)
|
||||
set_property(TARGET shadps4 APPEND PROPERTY BUILD_RPATH "@executable_path/../Frameworks")
|
||||
else()
|
||||
# For non-bundled SDL build, just do a normal library link.
|
||||
@@ -914,7 +1040,7 @@ if (APPLE)
|
||||
|
||||
if (ARCHITECTURE STREQUAL "x86_64")
|
||||
# Reserve system-managed memory space.
|
||||
target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,GUEST_SYSTEM,0x400000,-image_base,0x20000000000)
|
||||
target_link_options(shadps4 PRIVATE -Wl,-no_pie,-no_fixup_chains,-no_huge,-pagezero_size,0x4000,-segaddr,TCB_SPACE,0x4000,-segaddr,SYSTEM_MANAGED,0x400000,-segaddr,SYSTEM_RESERVED,0x7FFFFC000,-image_base,0x20000000000)
|
||||
endif()
|
||||
|
||||
# Replacement for std::chrono::time_zone
|
||||
@@ -925,12 +1051,6 @@ if (NOT ENABLE_QT_GUI)
|
||||
target_link_libraries(shadps4 PRIVATE SDL3::SDL3)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC)
|
||||
target_link_libraries(shadps4 PRIVATE cryptoppwin)
|
||||
else()
|
||||
target_link_libraries(shadps4 PRIVATE cryptopp::cryptopp)
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT_GUI)
|
||||
target_link_libraries(shadps4 PRIVATE Qt6::Widgets Qt6::Concurrent Qt6::Network Qt6::Multimedia)
|
||||
add_definitions(-DENABLE_QT_GUI)
|
||||
@@ -991,6 +1111,19 @@ add_subdirectory(${HOST_SHADERS_INCLUDE})
|
||||
add_dependencies(shadps4 host_shaders)
|
||||
target_include_directories(shadps4 PRIVATE ${HOST_SHADERS_INCLUDE})
|
||||
|
||||
# embed resources
|
||||
|
||||
include(CMakeRC)
|
||||
cmrc_add_resource_library(embedded-resources
|
||||
ALIAS res::embedded
|
||||
NAMESPACE res
|
||||
src/images/bronze.png
|
||||
src/images/gold.png
|
||||
src/images/platinum.png
|
||||
src/images/silver.png)
|
||||
|
||||
target_link_libraries(shadps4 PRIVATE res::embedded)
|
||||
|
||||
# ImGui resources
|
||||
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/imgui/renderer)
|
||||
add_dependencies(shadps4 ImGui_Resources)
|
||||
@@ -1026,7 +1159,6 @@ install(TARGETS shadps4 BUNDLE DESTINATION .)
|
||||
|
||||
if (ENABLE_QT_GUI AND CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
install(FILES "dist/net.shadps4.shadPS4.desktop" DESTINATION "share/applications")
|
||||
install(FILES "dist/net.shadps4.shadPS4.releases.xml" DESTINATION "share/metainfo/releases")
|
||||
install(FILES "dist/net.shadps4.shadPS4.metainfo.xml" DESTINATION "share/metainfo")
|
||||
install(FILES ".github/shadps4.png" DESTINATION "share/icons/hicolor/512x512/apps" RENAME "net.shadps4.shadPS4.png")
|
||||
install(FILES "src/images/net.shadps4.shadPS4.svg" DESTINATION "share/icons/hicolor/scalable/apps")
|
||||
|
||||
73
README.md
73
README.md
@@ -55,6 +55,9 @@ This project began as a fun project. Given our limited free time, it may take so
|
||||
|
||||
# Building
|
||||
|
||||
> [!IMPORTANT]
|
||||
> If you want to use shadPS4 to play your games, you don't have to follow the build instructions, you can simply download the emulator from either the [**release tab**](https://github.com/shadps4-emu/shadPS4/releases) or the [**action tab**](https://github.com/shadps4-emu/shadPS4/actions).
|
||||
|
||||
## Windows
|
||||
|
||||
Check the build instructions for [**Windows**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-windows.md).
|
||||
@@ -74,7 +77,10 @@ Check the build instructions for [**macOS**](https://github.com/shadps4-emu/shad
|
||||
|
||||
For more information on how to test, debug and report issues with the emulator or games, read the [**Debugging documentation**](https://github.com/shadps4-emu/shadPS4/blob/main/documents/Debugging/Debugging.md).
|
||||
|
||||
# Keyboard mapping
|
||||
# Keyboard and Mouse Mappings
|
||||
|
||||
> [!NOTE]
|
||||
> Some keyboards may also require you to hold the Fn key to use the F\* keys. Mac users should use the Command key instead of Control, and need to use Command+F11 for full screen to avoid conflicting with system key bindings.
|
||||
|
||||
| Button | Function |
|
||||
|-------------|-------------|
|
||||
@@ -86,32 +92,35 @@ F12 | Trigger RenderDoc Capture
|
||||
> [!NOTE]
|
||||
> Xbox and DualShock controllers work out of the box.
|
||||
|
||||
| Controller button | Keyboard equivelant | Mac alternative |
|
||||
|-------------|-------------|--------------|
|
||||
LEFT AXIS UP | W | |
|
||||
LEFT AXIS DOWN | S | |
|
||||
LEFT AXIS LEFT | A | |
|
||||
LEFT AXIS RIGHT | D | |
|
||||
RIGHT AXIS UP | I | |
|
||||
RIGHT AXIS DOWN | K | |
|
||||
RIGHT AXIS LEFT | J | |
|
||||
RIGHT AXIS RIGHT | L | |
|
||||
TRIANGLE | Numpad 8 | C |
|
||||
CIRCLE | Numpad 6 | B |
|
||||
CROSS | Numpad 2 | N |
|
||||
SQUARE | Numpad 4 | V |
|
||||
PAD UP | UP | |
|
||||
PAD DOWN | DOWN | |
|
||||
PAD LEFT | LEFT | |
|
||||
PAD RIGHT | RIGHT | |
|
||||
OPTIONS | RETURN | |
|
||||
BACK BUTTON / TOUCH PAD | SPACE | |
|
||||
L1 | Q | |
|
||||
R1 | U | |
|
||||
L2 | E | |
|
||||
R2 | O | |
|
||||
L3 | X | |
|
||||
R3 | M | |
|
||||
| Controller button | Keyboard equivalent |
|
||||
|-------------|-------------|
|
||||
LEFT AXIS UP | W |
|
||||
LEFT AXIS DOWN | S |
|
||||
LEFT AXIS LEFT | A |
|
||||
LEFT AXIS RIGHT | D |
|
||||
RIGHT AXIS UP | I |
|
||||
RIGHT AXIS DOWN | K |
|
||||
RIGHT AXIS LEFT | J |
|
||||
RIGHT AXIS RIGHT | L |
|
||||
TRIANGLE | Numpad 8 or C |
|
||||
CIRCLE | Numpad 6 or B |
|
||||
CROSS | Numpad 2 or N |
|
||||
SQUARE | Numpad 4 or V |
|
||||
PAD UP | UP |
|
||||
PAD DOWN | DOWN |
|
||||
PAD LEFT | LEFT |
|
||||
PAD RIGHT | RIGHT |
|
||||
OPTIONS | RETURN |
|
||||
BACK BUTTON / TOUCH PAD | SPACE |
|
||||
L1 | Q |
|
||||
R1 | U |
|
||||
L2 | E |
|
||||
R2 | O |
|
||||
L3 | X |
|
||||
R3 | M |
|
||||
|
||||
Keyboard and mouse inputs can be customized in the settings menu by clicking the Controller button, and further details and help on controls are also found there. Custom bindings are saved per-game. Inputs support up to three keys per binding, mouse buttons, mouse movement mapped to joystick input, and more.
|
||||
|
||||
|
||||
# Main team
|
||||
|
||||
@@ -121,6 +130,11 @@ R3 | M | |
|
||||
- [**skmp**](https://github.com/skmp)
|
||||
- [**wheremyfoodat**](https://github.com/wheremyfoodat)
|
||||
- [**raziel1000**](https://github.com/raziel1000)
|
||||
- [**viniciuslrangel**](https://github.com/viniciuslrangel)
|
||||
- [**roamic**](https://github.com/vladmikhalin)
|
||||
- [**poly**](https://github.com/polybiusproxy)
|
||||
- [**squidbus**](https://github.com/squidbus)
|
||||
- [**frodo**](https://github.com/baggins183)
|
||||
|
||||
Logo is done by [**Xphalnos**](https://github.com/Xphalnos)
|
||||
|
||||
@@ -129,6 +143,9 @@ Logo is done by [**Xphalnos**](https://github.com/Xphalnos)
|
||||
If you want to contribute, please look the [**CONTRIBUTING.md**](https://github.com/shadps4-emu/shadPS4/blob/main/CONTRIBUTING.md) file.\
|
||||
Open a PR and we'll check it :)
|
||||
|
||||
# Translations
|
||||
|
||||
If you want to translate shadPS4 to your language we use [**crowdin**](https://crowdin.com/project/shadps4-emulator).
|
||||
# Contributors
|
||||
|
||||
<a href="https://github.com/shadps4-emu/shadPS4/graphs/contributors">
|
||||
@@ -146,7 +163,7 @@ A few noteworthy teams/projects who've helped us along the way are:
|
||||
|
||||
- **yuzu**: Our shader compiler has been designed with yuzu's Hades compiler as a blueprint. This allowed us to focus on the challenges of emulating a modern AMD GPU while having a high-quality optimizing shader compiler implementation as a base.
|
||||
|
||||
- [**hydra**](https://github.com/hydra-emu/hydra): A multisystem, multiplatform emulator (chip-8, GB, NES, N64) from Paris.
|
||||
- [**felix86**](https://github.com/OFFTKP/felix86): A new x86-64 → RISC-V Linux userspace emulator
|
||||
|
||||
# License
|
||||
|
||||
|
||||
22
REUSE.toml
22
REUSE.toml
@@ -3,20 +3,27 @@ version = 1
|
||||
[[annotations]]
|
||||
path = [
|
||||
"REUSE.toml",
|
||||
"crowdin.yml",
|
||||
"CMakeSettings.json",
|
||||
".github/FUNDING.yml",
|
||||
".github/shadps4.png",
|
||||
".github/workflows/scripts/update_translation.sh",
|
||||
".github/workflows/update_translation.yml",
|
||||
".gitmodules",
|
||||
"dist/MacOSBundleInfo.plist.in",
|
||||
"dist/net.shadps4.shadPS4.desktop",
|
||||
"dist/net.shadps4.shadPS4_metadata.pot",
|
||||
"dist/net.shadps4.shadPS4.metainfo.xml",
|
||||
"dist/net.shadps4.shadPS4.releases.xml",
|
||||
"documents/changelog.md",
|
||||
"documents/Quickstart/2.png",
|
||||
"documents/Screenshots/*",
|
||||
"documents/Screenshots/Linux/*",
|
||||
"externals/MoltenVK/MoltenVK_icd.json",
|
||||
"scripts/ps4_names.txt",
|
||||
"src/images/bronze.png",
|
||||
"src/images/gold.png",
|
||||
"src/images/platinum.png",
|
||||
"src/images/silver.png",
|
||||
"src/images/about_icon.png",
|
||||
"src/images/controller_icon.png",
|
||||
"src/images/discord.png",
|
||||
@@ -32,12 +39,15 @@ path = [
|
||||
"src/images/folder_icon.png",
|
||||
"src/images/github.png",
|
||||
"src/images/grid_icon.png",
|
||||
"src/images/keyboard_icon.png",
|
||||
"src/images/iconsize_icon.png",
|
||||
"src/images/KBM.png",
|
||||
"src/images/ko-fi.png",
|
||||
"src/images/list_icon.png",
|
||||
"src/images/list_mode_icon.png",
|
||||
"src/images/pause_icon.png",
|
||||
"src/images/play_icon.png",
|
||||
"src/images/ps4_controller.png",
|
||||
"src/images/refresh_icon.png",
|
||||
"src/images/settings_icon.png",
|
||||
"src/images/stop_icon.png",
|
||||
@@ -51,6 +61,7 @@ path = [
|
||||
"src/images/website.png",
|
||||
"src/shadps4.qrc",
|
||||
"src/shadps4.rc",
|
||||
"src/qt_gui/translations/update_translation.sh",
|
||||
]
|
||||
precedence = "aggregate"
|
||||
SPDX-FileCopyrightText = "shadPS4 Emulator Project"
|
||||
@@ -97,3 +108,12 @@ path = "externals/gcn/include/**"
|
||||
SPDX-FileCopyrightText = "NONE"
|
||||
SPDX-License-Identifier = "CC0-1.0"
|
||||
|
||||
[[annotations]]
|
||||
path = "cmake/CMakeRC.cmake"
|
||||
SPDX-FileCopyrightText = "Copyright (c) 2017 vector-of-bool <vectorofbool@gmail.com>"
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
[[annotations]]
|
||||
path = "src/video_core/host_shaders/fsr/*"
|
||||
SPDX-FileCopyrightText = "Copyright (c) 2021 Advanced Micro Devices, Inc. All rights reserved."
|
||||
SPDX-License-Identifier = "MIT"
|
||||
|
||||
666
cmake/CMakeRC.cmake
Normal file
666
cmake/CMakeRC.cmake
Normal file
@@ -0,0 +1,666 @@
|
||||
# MIT License
|
||||
#
|
||||
# Copyright (c) 2017 vector-of-bool <vectorofbool@gmail.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.
|
||||
|
||||
# This block is executed when generating an intermediate resource file, not when
|
||||
# running in CMake configure mode
|
||||
if(_CMRC_GENERATE_MODE)
|
||||
# Read in the digits
|
||||
file(READ "${INPUT_FILE}" bytes HEX)
|
||||
# Format each pair into a character literal. Heuristics seem to favor doing
|
||||
# the conversion in groups of five for fastest conversion
|
||||
string(REGEX REPLACE "(..)(..)(..)(..)(..)" "'\\\\x\\1','\\\\x\\2','\\\\x\\3','\\\\x\\4','\\\\x\\5'," chars "${bytes}")
|
||||
# Since we did this in groups, we have some leftovers to clean up
|
||||
string(LENGTH "${bytes}" n_bytes2)
|
||||
math(EXPR n_bytes "${n_bytes2} / 2")
|
||||
math(EXPR remainder "${n_bytes} % 5") # <-- '5' is the grouping count from above
|
||||
set(cleanup_re "$")
|
||||
set(cleanup_sub )
|
||||
while(remainder)
|
||||
set(cleanup_re "(..)${cleanup_re}")
|
||||
set(cleanup_sub "'\\\\x\\${remainder}',${cleanup_sub}")
|
||||
math(EXPR remainder "${remainder} - 1")
|
||||
endwhile()
|
||||
if(NOT cleanup_re STREQUAL "$")
|
||||
string(REGEX REPLACE "${cleanup_re}" "${cleanup_sub}" chars "${chars}")
|
||||
endif()
|
||||
string(CONFIGURE [[
|
||||
namespace { const char file_array[] = { @chars@ 0 }; }
|
||||
namespace cmrc { namespace @NAMESPACE@ { namespace res_chars {
|
||||
extern const char* const @SYMBOL@_begin = file_array;
|
||||
extern const char* const @SYMBOL@_end = file_array + @n_bytes@;
|
||||
}}}
|
||||
]] code)
|
||||
file(WRITE "${OUTPUT_FILE}" "${code}")
|
||||
# Exit from the script. Nothing else needs to be processed
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(_version 2.0.0)
|
||||
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
include(CMakeParseArguments)
|
||||
|
||||
if(COMMAND cmrc_add_resource_library)
|
||||
if(NOT DEFINED _CMRC_VERSION OR NOT (_version STREQUAL _CMRC_VERSION))
|
||||
message(WARNING "More than one CMakeRC version has been included in this project.")
|
||||
endif()
|
||||
# CMakeRC has already been included! Don't do anything
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(_CMRC_VERSION "${_version}" CACHE INTERNAL "CMakeRC version. Used for checking for conflicts")
|
||||
|
||||
set(_CMRC_SCRIPT "${CMAKE_CURRENT_LIST_FILE}" CACHE INTERNAL "Path to CMakeRC script")
|
||||
|
||||
function(_cmrc_normalize_path var)
|
||||
set(path "${${var}}")
|
||||
file(TO_CMAKE_PATH "${path}" path)
|
||||
while(path MATCHES "//")
|
||||
string(REPLACE "//" "/" path "${path}")
|
||||
endwhile()
|
||||
string(REGEX REPLACE "/+$" "" path "${path}")
|
||||
set("${var}" "${path}" PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
get_filename_component(_inc_dir "${CMAKE_BINARY_DIR}/_cmrc/include" ABSOLUTE)
|
||||
set(CMRC_INCLUDE_DIR "${_inc_dir}" CACHE INTERNAL "Directory for CMakeRC include files")
|
||||
# Let's generate the primary include file
|
||||
file(MAKE_DIRECTORY "${CMRC_INCLUDE_DIR}/cmrc")
|
||||
set(hpp_content [==[
|
||||
#ifndef CMRC_CMRC_HPP_INCLUDED
|
||||
#define CMRC_CMRC_HPP_INCLUDED
|
||||
|
||||
#include <cassert>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <type_traits>
|
||||
|
||||
#if !(defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) || defined(CMRC_NO_EXCEPTIONS))
|
||||
#define CMRC_NO_EXCEPTIONS 1
|
||||
#endif
|
||||
|
||||
namespace cmrc { namespace detail { struct dummy; } }
|
||||
|
||||
#define CMRC_DECLARE(libid) \
|
||||
namespace cmrc { namespace detail { \
|
||||
struct dummy; \
|
||||
static_assert(std::is_same<dummy, ::cmrc::detail::dummy>::value, "CMRC_DECLARE() must only appear at the global namespace"); \
|
||||
} } \
|
||||
namespace cmrc { namespace libid { \
|
||||
cmrc::embedded_filesystem get_filesystem(); \
|
||||
} } static_assert(true, "")
|
||||
|
||||
namespace cmrc {
|
||||
|
||||
class file {
|
||||
const char* _begin = nullptr;
|
||||
const char* _end = nullptr;
|
||||
|
||||
public:
|
||||
using iterator = const char*;
|
||||
using const_iterator = iterator;
|
||||
iterator begin() const noexcept { return _begin; }
|
||||
iterator cbegin() const noexcept { return _begin; }
|
||||
iterator end() const noexcept { return _end; }
|
||||
iterator cend() const noexcept { return _end; }
|
||||
std::size_t size() const { return static_cast<std::size_t>(std::distance(begin(), end())); }
|
||||
|
||||
file() = default;
|
||||
file(iterator beg, iterator end) noexcept : _begin(beg), _end(end) {}
|
||||
};
|
||||
|
||||
class directory_entry;
|
||||
|
||||
namespace detail {
|
||||
|
||||
class directory;
|
||||
class file_data;
|
||||
|
||||
class file_or_directory {
|
||||
union _data_t {
|
||||
class file_data* file_data;
|
||||
class directory* directory;
|
||||
} _data;
|
||||
bool _is_file = true;
|
||||
|
||||
public:
|
||||
explicit file_or_directory(file_data& f) {
|
||||
_data.file_data = &f;
|
||||
}
|
||||
explicit file_or_directory(directory& d) {
|
||||
_data.directory = &d;
|
||||
_is_file = false;
|
||||
}
|
||||
bool is_file() const noexcept {
|
||||
return _is_file;
|
||||
}
|
||||
bool is_directory() const noexcept {
|
||||
return !is_file();
|
||||
}
|
||||
const directory& as_directory() const noexcept {
|
||||
assert(!is_file());
|
||||
return *_data.directory;
|
||||
}
|
||||
const file_data& as_file() const noexcept {
|
||||
assert(is_file());
|
||||
return *_data.file_data;
|
||||
}
|
||||
};
|
||||
|
||||
class file_data {
|
||||
public:
|
||||
const char* begin_ptr;
|
||||
const char* end_ptr;
|
||||
file_data(const file_data&) = delete;
|
||||
file_data(const char* b, const char* e) : begin_ptr(b), end_ptr(e) {}
|
||||
};
|
||||
|
||||
inline std::pair<std::string, std::string> split_path(const std::string& path) {
|
||||
auto first_sep = path.find("/");
|
||||
if (first_sep == path.npos) {
|
||||
return std::make_pair(path, "");
|
||||
} else {
|
||||
return std::make_pair(path.substr(0, first_sep), path.substr(first_sep + 1));
|
||||
}
|
||||
}
|
||||
|
||||
struct created_subdirectory {
|
||||
class directory& directory;
|
||||
class file_or_directory& index_entry;
|
||||
};
|
||||
|
||||
class directory {
|
||||
std::list<file_data> _files;
|
||||
std::list<directory> _dirs;
|
||||
std::map<std::string, file_or_directory> _index;
|
||||
|
||||
using base_iterator = std::map<std::string, file_or_directory>::const_iterator;
|
||||
|
||||
public:
|
||||
|
||||
directory() = default;
|
||||
directory(const directory&) = delete;
|
||||
|
||||
created_subdirectory add_subdir(std::string name) & {
|
||||
_dirs.emplace_back();
|
||||
auto& back = _dirs.back();
|
||||
auto& fod = _index.emplace(name, file_or_directory{back}).first->second;
|
||||
return created_subdirectory{back, fod};
|
||||
}
|
||||
|
||||
file_or_directory* add_file(std::string name, const char* begin, const char* end) & {
|
||||
assert(_index.find(name) == _index.end());
|
||||
_files.emplace_back(begin, end);
|
||||
return &_index.emplace(name, file_or_directory{_files.back()}).first->second;
|
||||
}
|
||||
|
||||
const file_or_directory* get(const std::string& path) const {
|
||||
auto pair = split_path(path);
|
||||
auto child = _index.find(pair.first);
|
||||
if (child == _index.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto& entry = child->second;
|
||||
if (pair.second.empty()) {
|
||||
// We're at the end of the path
|
||||
return &entry;
|
||||
}
|
||||
|
||||
if (entry.is_file()) {
|
||||
// We can't traverse into a file. Stop.
|
||||
return nullptr;
|
||||
}
|
||||
// Keep going down
|
||||
return entry.as_directory().get(pair.second);
|
||||
}
|
||||
|
||||
class iterator {
|
||||
base_iterator _base_iter;
|
||||
base_iterator _end_iter;
|
||||
public:
|
||||
using value_type = directory_entry;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = const value_type*;
|
||||
using reference = const value_type&;
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
|
||||
iterator() = default;
|
||||
explicit iterator(base_iterator iter, base_iterator end) : _base_iter(iter), _end_iter(end) {}
|
||||
|
||||
iterator begin() const noexcept {
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator end() const noexcept {
|
||||
return iterator(_end_iter, _end_iter);
|
||||
}
|
||||
|
||||
inline value_type operator*() const noexcept;
|
||||
|
||||
bool operator==(const iterator& rhs) const noexcept {
|
||||
return _base_iter == rhs._base_iter;
|
||||
}
|
||||
|
||||
bool operator!=(const iterator& rhs) const noexcept {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
iterator& operator++() noexcept {
|
||||
++_base_iter;
|
||||
return *this;
|
||||
}
|
||||
|
||||
iterator operator++(int) noexcept {
|
||||
auto cp = *this;
|
||||
++_base_iter;
|
||||
return cp;
|
||||
}
|
||||
};
|
||||
|
||||
using const_iterator = iterator;
|
||||
|
||||
iterator begin() const noexcept {
|
||||
return iterator(_index.begin(), _index.end());
|
||||
}
|
||||
|
||||
iterator end() const noexcept {
|
||||
return iterator();
|
||||
}
|
||||
};
|
||||
|
||||
inline std::string normalize_path(std::string path) {
|
||||
while (path.find("/") == 0) {
|
||||
path.erase(path.begin());
|
||||
}
|
||||
while (!path.empty() && (path.rfind("/") == path.size() - 1)) {
|
||||
path.pop_back();
|
||||
}
|
||||
auto off = path.npos;
|
||||
while ((off = path.find("//")) != path.npos) {
|
||||
path.erase(path.begin() + static_cast<std::string::difference_type>(off));
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
using index_type = std::map<std::string, const cmrc::detail::file_or_directory*>;
|
||||
|
||||
} // detail
|
||||
|
||||
class directory_entry {
|
||||
std::string _fname;
|
||||
const detail::file_or_directory* _item;
|
||||
|
||||
public:
|
||||
directory_entry() = delete;
|
||||
explicit directory_entry(std::string filename, const detail::file_or_directory& item)
|
||||
: _fname(filename)
|
||||
, _item(&item)
|
||||
{}
|
||||
|
||||
const std::string& filename() const & {
|
||||
return _fname;
|
||||
}
|
||||
std::string filename() const && {
|
||||
return std::move(_fname);
|
||||
}
|
||||
|
||||
bool is_file() const {
|
||||
return _item->is_file();
|
||||
}
|
||||
|
||||
bool is_directory() const {
|
||||
return _item->is_directory();
|
||||
}
|
||||
};
|
||||
|
||||
directory_entry detail::directory::iterator::operator*() const noexcept {
|
||||
assert(begin() != end());
|
||||
return directory_entry(_base_iter->first, _base_iter->second);
|
||||
}
|
||||
|
||||
using directory_iterator = detail::directory::iterator;
|
||||
|
||||
class embedded_filesystem {
|
||||
// Never-null:
|
||||
const cmrc::detail::index_type* _index;
|
||||
const detail::file_or_directory* _get(std::string path) const {
|
||||
path = detail::normalize_path(path);
|
||||
auto found = _index->find(path);
|
||||
if (found == _index->end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return found->second;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
explicit embedded_filesystem(const detail::index_type& index)
|
||||
: _index(&index)
|
||||
{}
|
||||
|
||||
file open(const std::string& path) const {
|
||||
auto entry_ptr = _get(path);
|
||||
if (!entry_ptr || !entry_ptr->is_file()) {
|
||||
#ifdef CMRC_NO_EXCEPTIONS
|
||||
fprintf(stderr, "Error no such file or directory: %s\n", path.c_str());
|
||||
abort();
|
||||
#else
|
||||
throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path);
|
||||
#endif
|
||||
}
|
||||
auto& dat = entry_ptr->as_file();
|
||||
return file{dat.begin_ptr, dat.end_ptr};
|
||||
}
|
||||
|
||||
bool is_file(const std::string& path) const noexcept {
|
||||
auto entry_ptr = _get(path);
|
||||
return entry_ptr && entry_ptr->is_file();
|
||||
}
|
||||
|
||||
bool is_directory(const std::string& path) const noexcept {
|
||||
auto entry_ptr = _get(path);
|
||||
return entry_ptr && entry_ptr->is_directory();
|
||||
}
|
||||
|
||||
bool exists(const std::string& path) const noexcept {
|
||||
return !!_get(path);
|
||||
}
|
||||
|
||||
directory_iterator iterate_directory(const std::string& path) const {
|
||||
auto entry_ptr = _get(path);
|
||||
if (!entry_ptr) {
|
||||
#ifdef CMRC_NO_EXCEPTIONS
|
||||
fprintf(stderr, "Error no such file or directory: %s\n", path.c_str());
|
||||
abort();
|
||||
#else
|
||||
throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path);
|
||||
#endif
|
||||
}
|
||||
if (!entry_ptr->is_directory()) {
|
||||
#ifdef CMRC_NO_EXCEPTIONS
|
||||
fprintf(stderr, "Error not a directory: %s\n", path.c_str());
|
||||
abort();
|
||||
#else
|
||||
throw std::system_error(make_error_code(std::errc::not_a_directory), path);
|
||||
#endif
|
||||
}
|
||||
return entry_ptr->as_directory().begin();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // CMRC_CMRC_HPP_INCLUDED
|
||||
]==])
|
||||
|
||||
set(cmrc_hpp "${CMRC_INCLUDE_DIR}/cmrc/cmrc.hpp" CACHE INTERNAL "")
|
||||
set(_generate 1)
|
||||
if(EXISTS "${cmrc_hpp}")
|
||||
file(READ "${cmrc_hpp}" _current)
|
||||
if(_current STREQUAL hpp_content)
|
||||
set(_generate 0)
|
||||
endif()
|
||||
endif()
|
||||
file(GENERATE OUTPUT "${cmrc_hpp}" CONTENT "${hpp_content}" CONDITION ${_generate})
|
||||
|
||||
add_library(cmrc-base INTERFACE)
|
||||
target_include_directories(cmrc-base INTERFACE $<BUILD_INTERFACE:${CMRC_INCLUDE_DIR}>)
|
||||
# Signal a basic C++11 feature to require C++11.
|
||||
target_compile_features(cmrc-base INTERFACE cxx_nullptr)
|
||||
set_property(TARGET cmrc-base PROPERTY INTERFACE_CXX_EXTENSIONS OFF)
|
||||
add_library(cmrc::base ALIAS cmrc-base)
|
||||
|
||||
function(cmrc_add_resource_library name)
|
||||
set(args ALIAS NAMESPACE TYPE)
|
||||
cmake_parse_arguments(ARG "" "${args}" "" "${ARGN}")
|
||||
# Generate the identifier for the resource library's namespace
|
||||
set(ns_re "[a-zA-Z_][a-zA-Z0-9_]*")
|
||||
if(NOT DEFINED ARG_NAMESPACE)
|
||||
# Check that the library name is also a valid namespace
|
||||
if(NOT name MATCHES "${ns_re}")
|
||||
message(SEND_ERROR "Library name is not a valid namespace. Specify the NAMESPACE argument")
|
||||
endif()
|
||||
set(ARG_NAMESPACE "${name}")
|
||||
else()
|
||||
if(NOT ARG_NAMESPACE MATCHES "${ns_re}")
|
||||
message(SEND_ERROR "NAMESPACE for ${name} is not a valid C++ namespace identifier (${ARG_NAMESPACE})")
|
||||
endif()
|
||||
endif()
|
||||
set(libname "${name}")
|
||||
# Check that type is either "STATIC" or "OBJECT", or default to "STATIC" if
|
||||
# not set
|
||||
if(NOT DEFINED ARG_TYPE)
|
||||
set(ARG_TYPE STATIC)
|
||||
elseif(NOT "${ARG_TYPE}" MATCHES "^(STATIC|OBJECT)$")
|
||||
message(SEND_ERROR "${ARG_TYPE} is not a valid TYPE (STATIC and OBJECT are acceptable)")
|
||||
set(ARG_TYPE STATIC)
|
||||
endif()
|
||||
# Generate a library with the compiled in character arrays.
|
||||
string(CONFIGURE [=[
|
||||
#include <cmrc/cmrc.hpp>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
namespace cmrc {
|
||||
namespace @ARG_NAMESPACE@ {
|
||||
|
||||
namespace res_chars {
|
||||
// These are the files which are available in this resource library
|
||||
$<JOIN:$<TARGET_PROPERTY:@libname@,CMRC_EXTERN_DECLS>,
|
||||
>
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const cmrc::detail::index_type&
|
||||
get_root_index() {
|
||||
static cmrc::detail::directory root_directory_;
|
||||
static cmrc::detail::file_or_directory root_directory_fod{root_directory_};
|
||||
static cmrc::detail::index_type root_index;
|
||||
root_index.emplace("", &root_directory_fod);
|
||||
struct dir_inl {
|
||||
class cmrc::detail::directory& directory;
|
||||
};
|
||||
dir_inl root_directory_dir{root_directory_};
|
||||
(void)root_directory_dir;
|
||||
$<JOIN:$<TARGET_PROPERTY:@libname@,CMRC_MAKE_DIRS>,
|
||||
>
|
||||
$<JOIN:$<TARGET_PROPERTY:@libname@,CMRC_MAKE_FILES>,
|
||||
>
|
||||
return root_index;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
cmrc::embedded_filesystem get_filesystem() {
|
||||
static auto& index = get_root_index();
|
||||
return cmrc::embedded_filesystem{index};
|
||||
}
|
||||
|
||||
} // @ARG_NAMESPACE@
|
||||
} // cmrc
|
||||
]=] cpp_content @ONLY)
|
||||
get_filename_component(libdir "${CMAKE_CURRENT_BINARY_DIR}/__cmrc_${name}" ABSOLUTE)
|
||||
get_filename_component(lib_tmp_cpp "${libdir}/lib_.cpp" ABSOLUTE)
|
||||
string(REPLACE "\n " "\n" cpp_content "${cpp_content}")
|
||||
file(GENERATE OUTPUT "${lib_tmp_cpp}" CONTENT "${cpp_content}")
|
||||
get_filename_component(libcpp "${libdir}/lib.cpp" ABSOLUTE)
|
||||
add_custom_command(OUTPUT "${libcpp}"
|
||||
DEPENDS "${lib_tmp_cpp}" "${cmrc_hpp}"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${lib_tmp_cpp}" "${libcpp}"
|
||||
COMMENT "Generating ${name} resource loader"
|
||||
)
|
||||
# Generate the actual static library. Each source file is just a single file
|
||||
# with a character array compiled in containing the contents of the
|
||||
# corresponding resource file.
|
||||
add_library(${name} ${ARG_TYPE} ${libcpp})
|
||||
set_property(TARGET ${name} PROPERTY CMRC_LIBDIR "${libdir}")
|
||||
set_property(TARGET ${name} PROPERTY CMRC_NAMESPACE "${ARG_NAMESPACE}")
|
||||
target_link_libraries(${name} PUBLIC cmrc::base)
|
||||
set_property(TARGET ${name} PROPERTY CMRC_IS_RESOURCE_LIBRARY TRUE)
|
||||
if(ARG_ALIAS)
|
||||
add_library("${ARG_ALIAS}" ALIAS ${name})
|
||||
endif()
|
||||
cmrc_add_resources(${name} ${ARG_UNPARSED_ARGUMENTS})
|
||||
endfunction()
|
||||
|
||||
function(_cmrc_register_dirs name dirpath)
|
||||
if(dirpath STREQUAL "")
|
||||
return()
|
||||
endif()
|
||||
# Skip this dir if we have already registered it
|
||||
get_target_property(registered "${name}" _CMRC_REGISTERED_DIRS)
|
||||
if(dirpath IN_LIST registered)
|
||||
return()
|
||||
endif()
|
||||
# Register the parent directory first
|
||||
get_filename_component(parent "${dirpath}" DIRECTORY)
|
||||
if(NOT parent STREQUAL "")
|
||||
_cmrc_register_dirs("${name}" "${parent}")
|
||||
endif()
|
||||
# Now generate the registration
|
||||
set_property(TARGET "${name}" APPEND PROPERTY _CMRC_REGISTERED_DIRS "${dirpath}")
|
||||
_cm_encode_fpath(sym "${dirpath}")
|
||||
if(parent STREQUAL "")
|
||||
set(parent_sym root_directory)
|
||||
else()
|
||||
_cm_encode_fpath(parent_sym "${parent}")
|
||||
endif()
|
||||
get_filename_component(leaf "${dirpath}" NAME)
|
||||
set_property(
|
||||
TARGET "${name}"
|
||||
APPEND PROPERTY CMRC_MAKE_DIRS
|
||||
"static auto ${sym}_dir = ${parent_sym}_dir.directory.add_subdir(\"${leaf}\")\;"
|
||||
"root_index.emplace(\"${dirpath}\", &${sym}_dir.index_entry)\;"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(cmrc_add_resources name)
|
||||
get_target_property(is_reslib ${name} CMRC_IS_RESOURCE_LIBRARY)
|
||||
if(NOT TARGET ${name} OR NOT is_reslib)
|
||||
message(SEND_ERROR "cmrc_add_resources called on target '${name}' which is not an existing resource library")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(options)
|
||||
set(args WHENCE PREFIX)
|
||||
set(list_args)
|
||||
cmake_parse_arguments(ARG "${options}" "${args}" "${list_args}" "${ARGN}")
|
||||
|
||||
if(NOT ARG_WHENCE)
|
||||
set(ARG_WHENCE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
endif()
|
||||
_cmrc_normalize_path(ARG_WHENCE)
|
||||
get_filename_component(ARG_WHENCE "${ARG_WHENCE}" ABSOLUTE)
|
||||
|
||||
# Generate the identifier for the resource library's namespace
|
||||
get_target_property(lib_ns "${name}" CMRC_NAMESPACE)
|
||||
|
||||
get_target_property(libdir ${name} CMRC_LIBDIR)
|
||||
get_target_property(target_dir ${name} SOURCE_DIR)
|
||||
file(RELATIVE_PATH reldir "${target_dir}" "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
if(reldir MATCHES "^\\.\\.")
|
||||
message(SEND_ERROR "Cannot call cmrc_add_resources in a parent directory from the resource library target")
|
||||
return()
|
||||
endif()
|
||||
|
||||
foreach(input IN LISTS ARG_UNPARSED_ARGUMENTS)
|
||||
_cmrc_normalize_path(input)
|
||||
get_filename_component(abs_in "${input}" ABSOLUTE)
|
||||
# Generate a filename based on the input filename that we can put in
|
||||
# the intermediate directory.
|
||||
file(RELATIVE_PATH relpath "${ARG_WHENCE}" "${abs_in}")
|
||||
if(relpath MATCHES "^\\.\\.")
|
||||
# For now we just error on files that exist outside of the soure dir.
|
||||
message(SEND_ERROR "Cannot add file '${input}': File must be in a subdirectory of ${ARG_WHENCE}")
|
||||
continue()
|
||||
endif()
|
||||
if(DEFINED ARG_PREFIX)
|
||||
_cmrc_normalize_path(ARG_PREFIX)
|
||||
endif()
|
||||
if(ARG_PREFIX AND NOT ARG_PREFIX MATCHES "/$")
|
||||
set(ARG_PREFIX "${ARG_PREFIX}/")
|
||||
endif()
|
||||
get_filename_component(dirpath "${ARG_PREFIX}${relpath}" DIRECTORY)
|
||||
_cmrc_register_dirs("${name}" "${dirpath}")
|
||||
get_filename_component(abs_out "${libdir}/intermediate/${ARG_PREFIX}${relpath}.cpp" ABSOLUTE)
|
||||
# Generate a symbol name relpath the file's character array
|
||||
_cm_encode_fpath(sym "${relpath}")
|
||||
# Get the symbol name for the parent directory
|
||||
if(dirpath STREQUAL "")
|
||||
set(parent_sym root_directory)
|
||||
else()
|
||||
_cm_encode_fpath(parent_sym "${dirpath}")
|
||||
endif()
|
||||
# Generate the rule for the intermediate source file
|
||||
_cmrc_generate_intermediate_cpp(${lib_ns} ${sym} "${abs_out}" "${abs_in}")
|
||||
target_sources(${name} PRIVATE "${abs_out}")
|
||||
set_property(TARGET ${name} APPEND PROPERTY CMRC_EXTERN_DECLS
|
||||
"// Pointers to ${input}"
|
||||
"extern const char* const ${sym}_begin\;"
|
||||
"extern const char* const ${sym}_end\;"
|
||||
)
|
||||
get_filename_component(leaf "${relpath}" NAME)
|
||||
set_property(
|
||||
TARGET ${name}
|
||||
APPEND PROPERTY CMRC_MAKE_FILES
|
||||
"root_index.emplace("
|
||||
" \"${ARG_PREFIX}${relpath}\","
|
||||
" ${parent_sym}_dir.directory.add_file("
|
||||
" \"${leaf}\","
|
||||
" res_chars::${sym}_begin,"
|
||||
" res_chars::${sym}_end"
|
||||
" )"
|
||||
")\;"
|
||||
)
|
||||
endforeach()
|
||||
endfunction()
|
||||
|
||||
function(_cmrc_generate_intermediate_cpp lib_ns symbol outfile infile)
|
||||
add_custom_command(
|
||||
# This is the file we will generate
|
||||
OUTPUT "${outfile}"
|
||||
# These are the primary files that affect the output
|
||||
DEPENDS "${infile}" "${_CMRC_SCRIPT}"
|
||||
COMMAND
|
||||
"${CMAKE_COMMAND}"
|
||||
-D_CMRC_GENERATE_MODE=TRUE
|
||||
-DNAMESPACE=${lib_ns}
|
||||
-DSYMBOL=${symbol}
|
||||
"-DINPUT_FILE=${infile}"
|
||||
"-DOUTPUT_FILE=${outfile}"
|
||||
-P "${_CMRC_SCRIPT}"
|
||||
COMMENT "Generating intermediate file for ${infile}"
|
||||
)
|
||||
endfunction()
|
||||
|
||||
function(_cm_encode_fpath var fpath)
|
||||
string(MAKE_C_IDENTIFIER "${fpath}" ident)
|
||||
string(MD5 hash "${fpath}")
|
||||
string(SUBSTRING "${hash}" 0 4 hash)
|
||||
set(${var} f_${hash}_${ident} PARENT_SCOPE)
|
||||
endfunction()
|
||||
3
crowdin.yml
Normal file
3
crowdin.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
files:
|
||||
- source: /src/qt_gui/translations/en_US.ts
|
||||
translation: /%original_path%/%locale_with_underscore%.ts
|
||||
2
dist/net.shadps4.shadPS4.desktop
vendored
2
dist/net.shadps4.shadPS4.desktop
vendored
@@ -5,5 +5,5 @@ Terminal=false
|
||||
Type=Application
|
||||
Icon=net.shadps4.shadPS4
|
||||
Comment=PlayStation 4 emulator
|
||||
Categories=Game;
|
||||
Categories=Game;Emulator;
|
||||
StartupWMClass=shadps4;
|
||||
|
||||
35
dist/net.shadps4.shadPS4.metainfo.xml
vendored
35
dist/net.shadps4.shadPS4.metainfo.xml
vendored
@@ -26,7 +26,7 @@
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image type="source" translate="no">https://cdn.jsdelivr.net/gh/shadps4-emu/shadps4@main/documents/Screenshots/3.png</image>
|
||||
<caption>Yakuza Kiwami</caption>
|
||||
<caption>Yakuza 0</caption>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image type="source" translate="no">https://cdn.jsdelivr.net/gh/shadps4-emu/shadps4@main/documents/Screenshots/4.png</image>
|
||||
@@ -36,9 +36,36 @@
|
||||
<categories>
|
||||
<category translate="no">Game</category>
|
||||
</categories>
|
||||
<releases type="external" url="https://cdn.jsdelivr.net/gh/fpiesche/flatpak-builds/apps/net.shadps4.shadPS4/net.shadps4.shadPS4.releases.xml">
|
||||
<release version="v.0.4.0" date="2024-11-03">
|
||||
<description></description>
|
||||
<releases>
|
||||
<release version="0.7.0" date="2025-03-23">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.7.0</url>
|
||||
</release>
|
||||
<release version="0.6.0" date="2025-01-31">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.6.0</url>
|
||||
</release>
|
||||
<release version="0.5.0" date="2024-12-25">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.5.0</url>
|
||||
</release>
|
||||
<release version="0.4.0" date="2024-10-31">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.4.0</url>
|
||||
</release>
|
||||
<release version="0.3.0" date="2024-09-23">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.3.0</url>
|
||||
</release>
|
||||
<release version="0.2.0" date="2024-08-15">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.2.0</url>
|
||||
</release>
|
||||
<release version="0.1.0" date="2024-07-01">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/0.1.0</url>
|
||||
</release>
|
||||
<release version="0.0.3" date="2024-03-23">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.3</url>
|
||||
</release>
|
||||
<release version="0.0.2" date="2023-10-21">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.2</url>
|
||||
</release>
|
||||
<release version="0.0.1" date="2024-09-29">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.1</url>
|
||||
</release>
|
||||
</releases>
|
||||
<content_rating type="oars-1.1"/>
|
||||
|
||||
23
dist/net.shadps4.shadPS4.releases.xml
vendored
23
dist/net.shadps4.shadPS4.releases.xml
vendored
@@ -1,23 +0,0 @@
|
||||
<releases>
|
||||
<release version="0.4.0" date="2024-10-31">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.4.0</url>
|
||||
</release>
|
||||
<release version="0.3.0" date="2024-09-23">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.3.0</url>
|
||||
</release>
|
||||
<release version="0.2.0" date="2024-08-15">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v.0.2.0</url>
|
||||
</release>
|
||||
<release version="0.1.0" date="2024-07-01">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/0.1.0</url>
|
||||
</release>
|
||||
<release version="0.0.3" date="2024-03-23">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.3</url>
|
||||
</release>
|
||||
<release version="0.0.2" date="2023-10-21">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.2</url>
|
||||
</release>
|
||||
<release version="0.0.1" date="2024-09-29">
|
||||
<url>https://github.com/shadps4-emu/shadPS4/releases/tag/v0.0.1</url>
|
||||
</release>
|
||||
</releases>
|
||||
@@ -22,7 +22,10 @@ SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
- A processor with at least 4 cores and 6 threads
|
||||
- Above 2.5 GHz frequency
|
||||
- required support AVX2 extension or Rosetta 2 on ARM
|
||||
- A CPU supporting the following instruction sets: MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, F16C, CLMUL, AES, BMI1, MOVBE, XSAVE, ABM
|
||||
- **Intel**: Haswell generation or newer
|
||||
- **AMD**: Jaguar generation or newer
|
||||
- **Apple**: Rosetta 2 on macOS 15 or newer
|
||||
|
||||
### GPU
|
||||
|
||||
|
||||
BIN
documents/Screenshots/Linux/1.png
Normal file
BIN
documents/Screenshots/Linux/1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
BIN
documents/Screenshots/Linux/2.png
Normal file
BIN
documents/Screenshots/Linux/2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
documents/Screenshots/Linux/3.png
Normal file
BIN
documents/Screenshots/Linux/3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
documents/Screenshots/Linux/4.png
Normal file
BIN
documents/Screenshots/Linux/4.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 16 KiB |
BIN
documents/Screenshots/Linux/5.png
Normal file
BIN
documents/Screenshots/Linux/5.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -3,59 +3,156 @@ SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
-->
|
||||
|
||||
## Build shadPS4 for Linux
|
||||
## Build shadPS4 for Linux
|
||||
|
||||
### Install the necessary tools to build shadPS4:
|
||||
First and foremost, Clang 18 is the **recommended compiler** as it is used for official builds and CI. If you build with GCC, you might encounter issues — please report any you find. Additionally, if you choose to use GCC, please build shadPS4 with Clang at least once before creating an `[APP BUG]` issue or submitting a pull request.
|
||||
|
||||
## Preparatory steps
|
||||
|
||||
### Installing dependencies
|
||||
|
||||
#### Debian & Ubuntu
|
||||
```
|
||||
sudo apt install build-essential clang git cmake libasound2-dev libpulse-dev libopenal-dev libssl-dev zlib1g-dev libedit-dev libudev-dev libevdev-dev libsdl2-dev libjack-dev libsndio-dev qt6-base-dev qt6-tools-dev qt6-multimedia-dev libvulkan-dev vulkan-validationlayers
|
||||
|
||||
```bash
|
||||
sudo apt install build-essential clang git cmake libasound2-dev \
|
||||
libpulse-dev libopenal-dev libssl-dev zlib1g-dev libedit-dev \
|
||||
libudev-dev libevdev-dev libsdl2-dev libjack-dev libsndio-dev \
|
||||
qt6-base-dev qt6-tools-dev qt6-multimedia-dev libvulkan-dev \
|
||||
vulkan-validationlayers libpng-dev
|
||||
```
|
||||
|
||||
#### Fedora
|
||||
```
|
||||
sudo dnf install clang git cmake libatomic alsa-lib-devel pipewire-jack-audio-connection-kit-devel openal-devel openssl-devel libevdev-devel libudev-devel libXext-devel qt6-qtbase-devel qt6-qtbase-private-devel qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel vulkan-devel vulkan-validation-layers
|
||||
|
||||
```bash
|
||||
sudo dnf install clang git cmake libatomic alsa-lib-devel \
|
||||
pipewire-jack-audio-connection-kit-devel openal-devel \
|
||||
openssl-devel libevdev-devel libudev-devel libXext-devel \
|
||||
qt6-qtbase-devel qt6-qtbase-private-devel \
|
||||
qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel \
|
||||
vulkan-devel vulkan-validation-layers libpng-devel
|
||||
```
|
||||
|
||||
#### Arch Linux
|
||||
|
||||
```bash
|
||||
sudo pacman -S base-devel clang git cmake sndio jack2 openal \
|
||||
qt6-base qt6-declarative qt6-multimedia qt6-tools sdl2 \
|
||||
vulkan-validation-layers libpng
|
||||
```
|
||||
sudo pacman -S base-devel clang git cmake sndio jack2 openal qt6-base qt6-declarative qt6-multimedia sdl2 vulkan-validation-layers
|
||||
```
|
||||
|
||||
**Note**: The `shadps4-git` AUR package is not maintained by any of the developers, and it uses the default compiler, which is often set to GCC. Use at your own discretion.
|
||||
|
||||
#### OpenSUSE
|
||||
```
|
||||
sudo zypper install clang git cmake libasound2 libpulse-devel libsndio7 libjack-devel openal-soft-devel libopenssl-devel zlib-devel libedit-devel systemd-devel libevdev-devel qt6-base-devel qt6-multimedia-devel qt6-svg-devel qt6-linguist-devel qt6-gui-private-devel vulkan-devel vulkan-validationlayers
|
||||
```
|
||||
### Cloning and compiling:
|
||||
|
||||
Clone the repository recursively:
|
||||
```bash
|
||||
sudo zypper install clang git cmake libasound2 libpulse-devel \
|
||||
libsndio7 libjack-devel openal-soft-devel libopenssl-devel \
|
||||
zlib-devel libedit-devel systemd-devel libevdev-devel \
|
||||
qt6-base-devel qt6-multimedia-devel qt6-svg-devel \
|
||||
qt6-linguist-devel qt6-gui-private-devel vulkan-devel \
|
||||
vulkan-validationlayers libpng-devel
|
||||
```
|
||||
|
||||
#### NixOS
|
||||
|
||||
```bash
|
||||
nix-shell shell.nix
|
||||
```
|
||||
|
||||
#### Other Linux distributions
|
||||
|
||||
You can try one of two methods:
|
||||
|
||||
- Search the packages by name and install them with your package manager, or
|
||||
- Install [distrobox](https://distrobox.it/), create a container using any of the distributions cited above as a base, for Arch Linux you'd do:
|
||||
|
||||
```bash
|
||||
distrobox create --name archlinux --init --image archlinux:latest
|
||||
```
|
||||
|
||||
and install the dependencies on that container as cited above.
|
||||
This option is **highly recommended** for distributions with immutable/atomic filesystems (example: Fedora Kinoite, SteamOS).
|
||||
|
||||
### Cloning
|
||||
|
||||
```bash
|
||||
git clone --recursive https://github.com/shadps4-emu/shadPS4.git
|
||||
cd shadPS4
|
||||
```
|
||||
|
||||
Generate the build directory in the shadPS4 directory. To disable the QT GUI, remove the ```-DENABLE_QT_GUI=ON``` flag:
|
||||
## Building
|
||||
|
||||
**Note**: Clang is the compiler used for official builds and CI. If you build with GCC, you might encounter issues—please report any you find. If you choose to use GCC, we recommend building with Clang at least once before submitting a pull request.
|
||||
```
|
||||
There are 3 options you can choose from. Option 1 is **highly recommended**.
|
||||
|
||||
#### Option 1: Terminal-only
|
||||
|
||||
1. Generate the build directory in the shadPS4 directory.
|
||||
|
||||
```bash
|
||||
cmake -S . -B build/ -DENABLE_QT_GUI=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++
|
||||
```
|
||||
|
||||
Enter the directory:
|
||||
```
|
||||
cd build/
|
||||
To disable the Qt GUI, remove the `-DENABLE_QT_GUI=ON` flag. To change the build type (for debugging), add `-DCMAKE_BUILD_TYPE=Debug`.
|
||||
|
||||
2. Use CMake to build the project:
|
||||
|
||||
```bash
|
||||
cmake --build ./build --parallel$(nproc)
|
||||
```
|
||||
|
||||
Use make to build the project:
|
||||
```
|
||||
cmake --build . --parallel$(nproc)
|
||||
If your computer freezes during this step, this could be caused by excessive system resource usage. In that case, remove `--parallel$(nproc)`.
|
||||
|
||||
Now run the emulator. If Qt was enabled at configure time:
|
||||
|
||||
```bash
|
||||
./build/shadps4
|
||||
```
|
||||
|
||||
Now run the emulator. If QT is enabled:
|
||||
```
|
||||
./shadps4
|
||||
```
|
||||
Otherwise, specify the path to your PKG's boot file:
|
||||
|
||||
```bash
|
||||
./build/shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin
|
||||
```
|
||||
./shadps4 /"PATH"/"TO"/"GAME"/"FOLDER"/eboot.bin
|
||||
```
|
||||
|
||||
You can also specify the Game ID as an argument for which game to boot, as long as the folder containing the games is specified in config.toml (example: Bloodborne (US) is CUSA00900).
|
||||
#### Option 2: Configuring with cmake-gui
|
||||
|
||||
`cmake-gui` should be installed by default alongside `cmake`, if not search for the package in your package manager and install it.
|
||||
|
||||
Open `cmake-gui` and specify the source code and build directories. If you cloned the source code to your Home directory, it would be `/home/user/shadPS4` and `/home/user/shadPS4/build`.
|
||||
|
||||
Click on Configure, select "Unix Makefiles", select "Specify native compilers", click Next and choose `clang` and `clang++` as the C and CXX compilers. Usually they are located in `/bin/clang` and `/bin/clang++`. Click on Finish and let it configure the project.
|
||||
|
||||
Now every option should be displayed in red. Change anything you want, then click on Generate to make the changes permanent, then open a terminal window and do step 2 of Option 1.
|
||||
|
||||
#### Option 3: Visual Studio Code
|
||||
|
||||
This option is pretty convoluted and should only be used if you have VSCode as your default IDE, or just prefer building and debugging projects through it. This also assumes that you're using an Arch Linux environment, as the naming for some options might differ from other distros.
|
||||
|
||||
[Download Visual Studio Code for your platform](https://code.visualstudio.com/download), or use [Code - OSS](https://github.com/microsoft/vscode) if you'd like. Code - OSS is available on most Linux distributions' package repositories (on Arch Linux it is simply named `code`).
|
||||
|
||||
Once set up, go to Extensions and install "CMake Tools":
|
||||
|
||||

|
||||
|
||||
You can also install other CMake and Clang related extensions if you'd like, but this one is what enables you to configure and build CMake projects directly within VSCode.
|
||||
|
||||
Go to Settings, filter by `@ext:ms-vscode.cmake-tools configure` and disable this option:
|
||||
|
||||

|
||||
|
||||
If you wish to build with the Qt GUI, add `-DENABLE_QT_GUI=ON` to the configure arguments:
|
||||
|
||||

|
||||
|
||||
On the CMake tab, change the options as you wish, but make sure that it looks similar to or exactly like this:
|
||||
|
||||

|
||||
|
||||
When hovering over Project Status > Configure, there should be an icon titled "Configure". Click on it and let it configure the project, then do the same for Project Status > Build.
|
||||
|
||||
If you want to debug it, change the build type under Project Status > Configure to Debug (it should be the default) and compile it, then click on the icon in Project Status > Debug. If you simply want to launch the shadPS4 executable from within VSCode, click on the icon in Project Status > Launch.
|
||||
|
||||
Don't forget to change the launch target for both options to the shadPS4 executable inside shadPS4/build:
|
||||
|
||||

|
||||
|
||||
@@ -25,7 +25,7 @@ Once you are within the installer:
|
||||
|
||||
Beware, this requires you to create a Qt account. If you do not want to do this, please follow the MSYS2/MinGW compilation method instead.
|
||||
|
||||
1. Under the current, non beta version of Qt (at the time of writing 6.7.3), select the option `MSVC 2022 64-bit` or similar.
|
||||
1. Under the current, non beta version of Qt (at the time of writing 6.8.2), select the option `MSVC 2022 64-bit` or similar, as well as `QT Multimedia`.
|
||||
If you are on Windows on ARM / Qualcomm Snapdragon Elite X, select `MSVC 2022 ARM64` instead.
|
||||
|
||||
Go through the installation normally. If you know what you are doing, you may unselect individual components that eat up too much disk space.
|
||||
@@ -35,7 +35,7 @@ Beware, this requires you to create a Qt account. If you do not want to do this,
|
||||
Once you are finished, you will have to configure Qt within Visual Studio:
|
||||
|
||||
1. Tools -> Options -> Qt -> Versions
|
||||
2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.7.3\msvc2022_64`
|
||||
2. Add a new Qt version and navigate it to the correct folder. Should look like so: `C:\Qt\6.8.2\msvc2022_64`
|
||||
3. Enable the default checkmark on the new version you just created.
|
||||
|
||||
### (Prerequisite) Download [**Git for Windows**](https://git-scm.com/download/win)
|
||||
@@ -55,7 +55,7 @@ Go through the Git for Windows installation as normal
|
||||
3. If you want to build shadPS4 with the Qt Gui:
|
||||
1. Click x64-Clang-Release and select "Manage Configurations"
|
||||
2. Look for "CMake command arguments" and add to the text field
|
||||
`-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.7.3\msvc2022_64`
|
||||
`-DENABLE_QT_GUI=ON -DCMAKE_PREFIX_PATH=C:\Qt\6.8.2\msvc2022_64`
|
||||
(Change Qt path if you've installed it to non-default path)
|
||||
3. Press CTRL+S to save and wait a moment for CMake generation
|
||||
4. Change the project to build to shadps4.exe
|
||||
@@ -64,11 +64,14 @@ Go through the Git for Windows installation as normal
|
||||
Your shadps4.exe will be in `C:\path\to\source\Build\x64-Clang-Release\`
|
||||
|
||||
To automatically populate the necessary files to run shadPS4.exe, run in a command prompt or terminal:
|
||||
`C:\Qt\6.7.3\msvc2022_64\bin\windeployqt.exe "C:\path\to\shadps4.exe"`
|
||||
`C:\Qt\6.8.2\msvc2022_64\bin\windeployqt6.exe "C:\path\to\shadps4.exe"`
|
||||
(Change Qt path if you've installed it to non-default path)
|
||||
|
||||
## Option 2: MSYS2/MinGW
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Building with MSYS2 is broken as of right now, the only way to build on Windows is to use [Option 1: Visual Studio 2022](https://github.com/shadps4-emu/shadPS4/blob/main/documents/building-windows.md#option-1-visual-studio-2022).
|
||||
|
||||
### (Prerequisite) Download [**MSYS2**](https://www.msys2.org/)
|
||||
|
||||
Go through the MSYS2 installation as normal
|
||||
|
||||
42
externals/CMakeLists.txt
vendored
42
externals/CMakeLists.txt
vendored
@@ -18,8 +18,6 @@ endif()
|
||||
|
||||
# Boost
|
||||
if (NOT TARGET Boost::headers)
|
||||
set(BOOST_ROOT "${CMAKE_SOURCE_DIR}/externals/ext-boost" CACHE STRING "")
|
||||
set(Boost_NO_SYSTEM_PATHS ON CACHE BOOL "")
|
||||
add_subdirectory(ext-boost)
|
||||
endif()
|
||||
|
||||
@@ -28,22 +26,19 @@ if (NOT TARGET fmt::fmt)
|
||||
add_subdirectory(fmt)
|
||||
endif()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND MSVC)
|
||||
# If it is clang and MSVC we will add a static lib
|
||||
# CryptoPP
|
||||
add_subdirectory(cryptoppwin)
|
||||
target_include_directories(cryptoppwin INTERFACE cryptoppwin/include)
|
||||
else()
|
||||
# CryptoPP
|
||||
if (NOT TARGET cryptopp::cryptopp)
|
||||
set(CRYPTOPP_INSTALL OFF)
|
||||
set(CRYPTOPP_BUILD_TESTING OFF)
|
||||
set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp)
|
||||
add_subdirectory(cryptopp-cmake)
|
||||
file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h")
|
||||
# remove externals/cryptopp from include directories because it contains a conflicting zlib.h file
|
||||
set_target_properties(cryptopp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/cryptopp")
|
||||
endif()
|
||||
# CryptoPP
|
||||
if (NOT TARGET cryptopp::cryptopp)
|
||||
set(CRYPTOPP_INSTALL OFF)
|
||||
set(CRYPTOPP_BUILD_TESTING OFF)
|
||||
set(CRYPTOPP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/cryptopp)
|
||||
# cryptopp instruction set checks do not account for added compile options,
|
||||
# so disable extensions in the library config to match our chosen target CPU.
|
||||
set(CRYPTOPP_DISABLE_AESNI ON)
|
||||
set(CRYPTOPP_DISABLE_AVX2 ON)
|
||||
add_subdirectory(cryptopp-cmake)
|
||||
file(COPY cryptopp DESTINATION cryptopp FILES_MATCHING PATTERN "*.h")
|
||||
# remove externals/cryptopp from include directories because it contains a conflicting zlib.h file
|
||||
set_target_properties(cryptopp PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${CMAKE_CURRENT_BINARY_DIR}/cryptopp")
|
||||
endif()
|
||||
|
||||
if (NOT TARGET FFmpeg::ffmpeg)
|
||||
@@ -77,6 +72,7 @@ endif()
|
||||
|
||||
# SDL3
|
||||
if (NOT TARGET SDL3::SDL3)
|
||||
set(SDL_TEST_LIBRARY OFF)
|
||||
set(SDL_PIPEWIRE OFF)
|
||||
add_subdirectory(sdl3)
|
||||
endif()
|
||||
@@ -131,6 +127,14 @@ endif()
|
||||
# Toml11
|
||||
if (NOT TARGET toml11::toml11)
|
||||
add_subdirectory(toml11)
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
if (CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
get_target_property(_toml11_compile_options toml11 INTERFACE_COMPILE_OPTIONS)
|
||||
list(REMOVE_ITEM _toml11_compile_options "/Zc:preprocessor")
|
||||
set_target_properties(toml11 PROPERTIES INTERFACE_COMPILE_OPTIONS ${_toml11_compile_options})
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# xxHash
|
||||
@@ -213,9 +217,7 @@ endif()
|
||||
|
||||
# Discord RPC
|
||||
if (ENABLE_DISCORD_RPC)
|
||||
set(BUILD_EXAMPLES OFF)
|
||||
add_subdirectory(discord-rpc)
|
||||
target_include_directories(discord-rpc INTERFACE discord-rpc/include)
|
||||
endif()
|
||||
|
||||
# GCN Headers
|
||||
|
||||
2
externals/LibAtrac9
vendored
2
externals/LibAtrac9
vendored
Submodule externals/LibAtrac9 updated: 9640129dc6...ec8899dadf
2
externals/MoltenVK/MoltenVK
vendored
2
externals/MoltenVK/MoltenVK
vendored
Submodule externals/MoltenVK/MoltenVK updated: 5ad3ee5d2f...2048427e50
2
externals/MoltenVK/SPIRV-Cross
vendored
2
externals/MoltenVK/SPIRV-Cross
vendored
Submodule externals/MoltenVK/SPIRV-Cross updated: 6173e24b31...2c32b6bf86
2
externals/cryptopp
vendored
2
externals/cryptopp
vendored
Submodule externals/cryptopp updated: 60f81a77e0...effed0d0b8
1
externals/cryptoppwin
vendored
1
externals/cryptoppwin
vendored
Submodule externals/cryptoppwin deleted from bc3441dd2d
2
externals/discord-rpc
vendored
2
externals/discord-rpc
vendored
Submodule externals/discord-rpc updated: 4ec218155d...51b09d426a
6
externals/gcn/CMakeLists.txt
vendored
6
externals/gcn/CMakeLists.txt
vendored
@@ -3,6 +3,10 @@
|
||||
|
||||
project(gcn LANGUAGES CXX)
|
||||
|
||||
add_library(gcn dummy.cpp)
|
||||
add_library(gcn INTERFACE)
|
||||
target_sources(gcn PRIVATE
|
||||
"include/gcn/si_ci_vi_merged_offset.h"
|
||||
"include/gcn/si_ci_vi_merged_pm4_it_opcodes.h"
|
||||
)
|
||||
|
||||
target_include_directories(gcn INTERFACE include)
|
||||
|
||||
2
externals/gcn/dummy.cpp
vendored
2
externals/gcn/dummy.cpp
vendored
@@ -1,2 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
2
externals/sdl3
vendored
2
externals/sdl3
vendored
Submodule externals/sdl3 updated: 3a1d76d298...a336b62d8b
2
externals/sirit
vendored
2
externals/sirit
vendored
Submodule externals/sirit updated: 1e74f4ef8d...8b9b12c208
2
externals/vulkan-headers
vendored
2
externals/vulkan-headers
vendored
Submodule externals/vulkan-headers updated: 6a74a7d65c...a03d2f6d57
71
shell.nix
Normal file
71
shell.nix
Normal file
@@ -0,0 +1,71 @@
|
||||
# SPDX-FileCopyrightText: 2024 shadPS4 Emulator Project
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
with import (fetchTarball "https://github.com/nixos/nixpkgs/archive/cfd19cdc54680956dc1816ac577abba6b58b901c.tar.gz") { };
|
||||
|
||||
pkgs.mkShell {
|
||||
name = "shadps4-build-env";
|
||||
|
||||
nativeBuildInputs = [
|
||||
pkgs.llvmPackages_18.clang
|
||||
pkgs.cmake
|
||||
pkgs.pkg-config
|
||||
pkgs.git
|
||||
];
|
||||
|
||||
buildInputs = [
|
||||
pkgs.alsa-lib
|
||||
pkgs.libpulseaudio
|
||||
pkgs.openal
|
||||
pkgs.openssl
|
||||
pkgs.zlib
|
||||
pkgs.libedit
|
||||
pkgs.udev
|
||||
pkgs.libevdev
|
||||
pkgs.SDL2
|
||||
pkgs.jack2
|
||||
pkgs.sndio
|
||||
pkgs.qt6.qtbase
|
||||
pkgs.qt6.qttools
|
||||
pkgs.qt6.qtmultimedia
|
||||
|
||||
pkgs.vulkan-headers
|
||||
pkgs.vulkan-utility-libraries
|
||||
pkgs.vulkan-tools
|
||||
|
||||
pkgs.ffmpeg
|
||||
pkgs.fmt
|
||||
pkgs.glslang
|
||||
pkgs.libxkbcommon
|
||||
pkgs.wayland
|
||||
pkgs.xorg.libxcb
|
||||
pkgs.xorg.xcbutil
|
||||
pkgs.xorg.xcbutilkeysyms
|
||||
pkgs.xorg.xcbutilwm
|
||||
pkgs.sdl3
|
||||
pkgs.stb
|
||||
pkgs.qt6.qtwayland
|
||||
pkgs.wayland-protocols
|
||||
pkgs.libpng
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
echo "Entering shadPS4 dev shell"
|
||||
export QT_QPA_PLATFORM="wayland"
|
||||
export QT_PLUGIN_PATH="${pkgs.qt6.qtwayland}/lib/qt-6/plugins:${pkgs.qt6.qtbase}/lib/qt-6/plugins"
|
||||
export QML2_IMPORT_PATH="${pkgs.qt6.qtbase}/lib/qt-6/qml"
|
||||
export CMAKE_PREFIX_PATH="${pkgs.vulkan-headers}:$CMAKE_PREFIX_PATH"
|
||||
|
||||
# OpenGL
|
||||
export LD_LIBRARY_PATH="${
|
||||
pkgs.lib.makeLibraryPath [
|
||||
pkgs.libglvnd
|
||||
pkgs.vulkan-tools
|
||||
]
|
||||
}:$LD_LIBRARY_PATH"
|
||||
|
||||
export LDFLAGS="-L${pkgs.llvmPackages_18.libcxx}/lib -lc++"
|
||||
export LC_ALL="C.UTF-8"
|
||||
export XAUTHORITY=${builtins.getEnv "XAUTHORITY"}
|
||||
'';
|
||||
}
|
||||
27
src/common/adaptive_mutex.h
Normal file
27
src/common/adaptive_mutex.h
Normal file
@@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __linux__
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
#ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
|
||||
class AdaptiveMutex {
|
||||
public:
|
||||
void lock() {
|
||||
pthread_mutex_lock(&mutex);
|
||||
}
|
||||
void unlock() {
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_mutex_t mutex = PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP;
|
||||
};
|
||||
#endif // PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
|
||||
|
||||
} // namespace Common
|
||||
@@ -32,7 +32,6 @@ std::filesystem::path find_fs_path_or(const basic_value<TC>& v, const K& ky,
|
||||
namespace Config {
|
||||
|
||||
static bool isNeo = false;
|
||||
static bool isFullscreen = false;
|
||||
static bool playBGM = false;
|
||||
static bool isTrophyPopupDisabled = false;
|
||||
static int BGMvolume = 50;
|
||||
@@ -44,13 +43,17 @@ static std::string logFilter;
|
||||
static std::string logType = "async";
|
||||
static std::string userName = "shadPS4";
|
||||
static std::string updateChannel;
|
||||
static std::string chooseHomeTab;
|
||||
static std::string backButtonBehavior = "left";
|
||||
static bool useSpecialPad = false;
|
||||
static int specialPadClass = 1;
|
||||
static bool isMotionControlsEnabled = true;
|
||||
static bool isDebugDump = false;
|
||||
static bool isShaderDebug = false;
|
||||
static bool isShowSplash = false;
|
||||
static bool isAutoUpdate = false;
|
||||
static bool isAlwaysShowChangelog = false;
|
||||
static std::string isSideTrophy = "right";
|
||||
static bool isNullGpu = false;
|
||||
static bool shouldCopyGPUBuffers = false;
|
||||
static bool shouldDumpShaders = false;
|
||||
@@ -59,18 +62,28 @@ static u32 vblankDivider = 1;
|
||||
static bool vkValidation = false;
|
||||
static bool vkValidationSync = false;
|
||||
static bool vkValidationGpu = false;
|
||||
static bool rdocEnable = false;
|
||||
static bool vkMarkers = false;
|
||||
static bool vkCrashDiagnostic = false;
|
||||
static bool vkHostMarkers = false;
|
||||
static bool vkGuestMarkers = false;
|
||||
static bool rdocEnable = false;
|
||||
static bool isFpsColor = true;
|
||||
static bool isSeparateLogFilesEnabled = false;
|
||||
static s16 cursorState = HideCursorState::Idle;
|
||||
static int cursorHideTimeout = 5; // 5 seconds (default)
|
||||
static double trophyNotificationDuration = 6.0;
|
||||
static bool useUnifiedInputConfig = true;
|
||||
static bool overrideControllerColor = false;
|
||||
static int controllerCustomColorRGB[3] = {0, 0, 255};
|
||||
static bool separateupdatefolder = false;
|
||||
static bool compatibilityData = false;
|
||||
static bool checkCompatibilityOnStartup = false;
|
||||
static std::string trophyKey;
|
||||
|
||||
// Gui
|
||||
static bool load_game_size = true;
|
||||
std::vector<std::filesystem::path> settings_install_dirs = {};
|
||||
std::filesystem::path settings_addon_install_dir = {};
|
||||
std::filesystem::path save_data_path = {};
|
||||
u32 main_window_geometry_x = 400;
|
||||
u32 main_window_geometry_y = 400;
|
||||
u32 main_window_geometry_w = 1280;
|
||||
@@ -86,19 +99,81 @@ u32 m_window_size_H = 720;
|
||||
std::vector<std::string> m_pkg_viewer;
|
||||
std::vector<std::string> m_elf_viewer;
|
||||
std::vector<std::string> m_recent_files;
|
||||
std::string emulator_language = "en";
|
||||
std::string emulator_language = "en_US";
|
||||
static int backgroundImageOpacity = 50;
|
||||
static bool showBackgroundImage = true;
|
||||
static bool isFullscreen = false;
|
||||
static std::string fullscreenMode = "Windowed";
|
||||
static bool isHDRAllowed = false;
|
||||
|
||||
// Language
|
||||
u32 m_language = 1; // english
|
||||
|
||||
bool isNeoMode() {
|
||||
bool allowHDR() {
|
||||
return isHDRAllowed;
|
||||
}
|
||||
|
||||
bool GetUseUnifiedInputConfig() {
|
||||
return useUnifiedInputConfig;
|
||||
}
|
||||
|
||||
void SetUseUnifiedInputConfig(bool use) {
|
||||
useUnifiedInputConfig = use;
|
||||
}
|
||||
|
||||
bool GetOverrideControllerColor() {
|
||||
return overrideControllerColor;
|
||||
}
|
||||
|
||||
void SetOverrideControllerColor(bool enable) {
|
||||
overrideControllerColor = enable;
|
||||
}
|
||||
|
||||
int* GetControllerCustomColor() {
|
||||
return controllerCustomColorRGB;
|
||||
}
|
||||
|
||||
void SetControllerCustomColor(int r, int b, int g) {
|
||||
controllerCustomColorRGB[0] = r;
|
||||
controllerCustomColorRGB[1] = b;
|
||||
controllerCustomColorRGB[2] = g;
|
||||
}
|
||||
|
||||
std::string getTrophyKey() {
|
||||
return trophyKey;
|
||||
}
|
||||
|
||||
void setTrophyKey(std::string key) {
|
||||
trophyKey = key;
|
||||
}
|
||||
|
||||
bool GetLoadGameSizeEnabled() {
|
||||
return load_game_size;
|
||||
}
|
||||
|
||||
std::filesystem::path GetSaveDataPath() {
|
||||
if (save_data_path.empty()) {
|
||||
return Common::FS::GetUserPath(Common::FS::PathType::SaveDataDir);
|
||||
}
|
||||
return save_data_path;
|
||||
}
|
||||
|
||||
void setLoadGameSizeEnabled(bool enable) {
|
||||
load_game_size = enable;
|
||||
}
|
||||
|
||||
bool isNeoModeConsole() {
|
||||
return isNeo;
|
||||
}
|
||||
|
||||
bool isFullscreenMode() {
|
||||
bool getIsFullscreen() {
|
||||
return isFullscreen;
|
||||
}
|
||||
|
||||
std::string getFullscreenMode() {
|
||||
return fullscreenMode;
|
||||
}
|
||||
|
||||
bool getisTrophyPopupDisabled() {
|
||||
return isTrophyPopupDisabled;
|
||||
}
|
||||
@@ -123,6 +198,10 @@ int getCursorHideTimeout() {
|
||||
return cursorHideTimeout;
|
||||
}
|
||||
|
||||
double getTrophyNotificationDuration() {
|
||||
return trophyNotificationDuration;
|
||||
}
|
||||
|
||||
u32 getScreenWidth() {
|
||||
return screenWidth;
|
||||
}
|
||||
@@ -151,6 +230,10 @@ std::string getUpdateChannel() {
|
||||
return updateChannel;
|
||||
}
|
||||
|
||||
std::string getChooseHomeTab() {
|
||||
return chooseHomeTab;
|
||||
}
|
||||
|
||||
std::string getBackButtonBehavior() {
|
||||
return backButtonBehavior;
|
||||
}
|
||||
@@ -163,6 +246,10 @@ int getSpecialPadClass() {
|
||||
return specialPadClass;
|
||||
}
|
||||
|
||||
bool getIsMotionControlsEnabled() {
|
||||
return isMotionControlsEnabled;
|
||||
}
|
||||
|
||||
bool debugDump() {
|
||||
return isDebugDump;
|
||||
}
|
||||
@@ -179,6 +266,14 @@ bool autoUpdate() {
|
||||
return isAutoUpdate;
|
||||
}
|
||||
|
||||
bool alwaysShowChangelog() {
|
||||
return isAlwaysShowChangelog;
|
||||
}
|
||||
|
||||
std::string sideTrophy() {
|
||||
return isSideTrophy;
|
||||
}
|
||||
|
||||
bool nullGpu() {
|
||||
return isNullGpu;
|
||||
}
|
||||
@@ -199,8 +294,8 @@ bool isRdocEnabled() {
|
||||
return rdocEnable;
|
||||
}
|
||||
|
||||
bool isMarkersEnabled() {
|
||||
return vkMarkers;
|
||||
bool fpsColor() {
|
||||
return isFpsColor;
|
||||
}
|
||||
|
||||
u32 vblankDiv() {
|
||||
@@ -219,12 +314,28 @@ bool vkValidationGpuEnabled() {
|
||||
return vkValidationGpu;
|
||||
}
|
||||
|
||||
bool vkMarkersEnabled() {
|
||||
return vkMarkers || vkCrashDiagnostic; // Crash diagnostic forces markers on
|
||||
bool getVkCrashDiagnosticEnabled() {
|
||||
return vkCrashDiagnostic;
|
||||
}
|
||||
|
||||
bool vkCrashDiagnosticEnabled() {
|
||||
return vkCrashDiagnostic;
|
||||
bool getVkHostMarkersEnabled() {
|
||||
return vkHostMarkers;
|
||||
}
|
||||
|
||||
bool getVkGuestMarkersEnabled() {
|
||||
return vkGuestMarkers;
|
||||
}
|
||||
|
||||
void setVkCrashDiagnosticEnabled(bool enable) {
|
||||
vkCrashDiagnostic = enable;
|
||||
}
|
||||
|
||||
void setVkHostMarkersEnabled(bool enable) {
|
||||
vkHostMarkers = enable;
|
||||
}
|
||||
|
||||
void setVkGuestMarkersEnabled(bool enable) {
|
||||
vkGuestMarkers = enable;
|
||||
}
|
||||
|
||||
bool getSeparateUpdateEnabled() {
|
||||
@@ -267,10 +378,22 @@ void setAutoUpdate(bool enable) {
|
||||
isAutoUpdate = enable;
|
||||
}
|
||||
|
||||
void setAlwaysShowChangelog(bool enable) {
|
||||
isAlwaysShowChangelog = enable;
|
||||
}
|
||||
|
||||
void setSideTrophy(std::string side) {
|
||||
isSideTrophy = side;
|
||||
}
|
||||
|
||||
void setNullGpu(bool enable) {
|
||||
isNullGpu = enable;
|
||||
}
|
||||
|
||||
void setAllowHDR(bool enable) {
|
||||
isHDRAllowed = enable;
|
||||
}
|
||||
|
||||
void setCopyGPUCmdBuffers(bool enable) {
|
||||
shouldCopyGPUBuffers = enable;
|
||||
}
|
||||
@@ -295,10 +418,14 @@ void setVblankDiv(u32 value) {
|
||||
vblankDivider = value;
|
||||
}
|
||||
|
||||
void setFullscreenMode(bool enable) {
|
||||
void setIsFullscreen(bool enable) {
|
||||
isFullscreen = enable;
|
||||
}
|
||||
|
||||
void setFullscreenMode(std::string mode) {
|
||||
fullscreenMode = mode;
|
||||
}
|
||||
|
||||
void setisTrophyPopupDisabled(bool disable) {
|
||||
isTrophyPopupDisabled = disable;
|
||||
}
|
||||
@@ -322,6 +449,9 @@ void setCursorState(s16 newCursorState) {
|
||||
void setCursorHideTimeout(int newcursorHideTimeout) {
|
||||
cursorHideTimeout = newcursorHideTimeout;
|
||||
}
|
||||
void setTrophyNotificationDuration(double newTrophyNotificationDuration) {
|
||||
trophyNotificationDuration = newTrophyNotificationDuration;
|
||||
}
|
||||
|
||||
void setLanguage(u32 language) {
|
||||
m_language = language;
|
||||
@@ -339,6 +469,10 @@ void setLogFilter(const std::string& type) {
|
||||
logFilter = type;
|
||||
}
|
||||
|
||||
void setSeparateLogFilesEnabled(bool enabled) {
|
||||
isSeparateLogFilesEnabled = enabled;
|
||||
}
|
||||
|
||||
void setUserName(const std::string& type) {
|
||||
userName = type;
|
||||
}
|
||||
@@ -346,6 +480,9 @@ void setUserName(const std::string& type) {
|
||||
void setUpdateChannel(const std::string& type) {
|
||||
updateChannel = type;
|
||||
}
|
||||
void setChooseHomeTab(const std::string& type) {
|
||||
chooseHomeTab = type;
|
||||
}
|
||||
|
||||
void setBackButtonBehavior(const std::string& type) {
|
||||
backButtonBehavior = type;
|
||||
@@ -359,6 +496,10 @@ void setSpecialPadClass(int type) {
|
||||
specialPadClass = type;
|
||||
}
|
||||
|
||||
void setIsMotionControlsEnabled(bool use) {
|
||||
isMotionControlsEnabled = use;
|
||||
}
|
||||
|
||||
void setSeparateUpdateEnabled(bool use) {
|
||||
separateupdatefolder = use;
|
||||
}
|
||||
@@ -453,6 +594,10 @@ void setGameInstallDirs(const std::vector<std::filesystem::path>& settings_insta
|
||||
settings_install_dirs = settings_install_dirs_config;
|
||||
}
|
||||
|
||||
void setSaveDataPath(const std::filesystem::path& path) {
|
||||
save_data_path = path;
|
||||
}
|
||||
|
||||
u32 getMainWindowGeometryX() {
|
||||
return main_window_geometry_x;
|
||||
}
|
||||
@@ -533,6 +678,26 @@ u32 GetLanguage() {
|
||||
return m_language;
|
||||
}
|
||||
|
||||
bool getSeparateLogFilesEnabled() {
|
||||
return isSeparateLogFilesEnabled;
|
||||
}
|
||||
|
||||
int getBackgroundImageOpacity() {
|
||||
return backgroundImageOpacity;
|
||||
}
|
||||
|
||||
void setBackgroundImageOpacity(int opacity) {
|
||||
backgroundImageOpacity = std::clamp(opacity, 0, 100);
|
||||
}
|
||||
|
||||
bool getShowBackgroundImage() {
|
||||
return showBackgroundImage;
|
||||
}
|
||||
|
||||
void setShowBackgroundImage(bool show) {
|
||||
showBackgroundImage = show;
|
||||
}
|
||||
|
||||
void load(const std::filesystem::path& path) {
|
||||
// If the configuration file does not exist, create it and return
|
||||
std::error_code error;
|
||||
@@ -556,9 +721,10 @@ void load(const std::filesystem::path& path) {
|
||||
const toml::value& general = data.at("General");
|
||||
|
||||
isNeo = toml::find_or<bool>(general, "isPS4Pro", false);
|
||||
isFullscreen = toml::find_or<bool>(general, "Fullscreen", false);
|
||||
playBGM = toml::find_or<bool>(general, "playBGM", false);
|
||||
isTrophyPopupDisabled = toml::find_or<bool>(general, "isTrophyPopupDisabled", false);
|
||||
trophyNotificationDuration =
|
||||
toml::find_or<double>(general, "trophyNotificationDuration", 5.0);
|
||||
BGMvolume = toml::find_or<int>(general, "BGMvolume", 50);
|
||||
enableDiscordRPC = toml::find_or<bool>(general, "enableDiscordRPC", true);
|
||||
logFilter = toml::find_or<std::string>(general, "logFilter", "");
|
||||
@@ -571,10 +737,13 @@ void load(const std::filesystem::path& path) {
|
||||
}
|
||||
isShowSplash = toml::find_or<bool>(general, "showSplash", true);
|
||||
isAutoUpdate = toml::find_or<bool>(general, "autoUpdate", false);
|
||||
isAlwaysShowChangelog = toml::find_or<bool>(general, "alwaysShowChangelog", false);
|
||||
isSideTrophy = toml::find_or<std::string>(general, "sideTrophy", "right");
|
||||
separateupdatefolder = toml::find_or<bool>(general, "separateUpdateEnabled", false);
|
||||
compatibilityData = toml::find_or<bool>(general, "compatibilityEnabled", false);
|
||||
checkCompatibilityOnStartup =
|
||||
toml::find_or<bool>(general, "checkCompatibilityOnStartup", false);
|
||||
chooseHomeTab = toml::find_or<std::string>(general, "chooseHomeTab", "Release");
|
||||
}
|
||||
|
||||
if (data.contains("Input")) {
|
||||
@@ -585,6 +754,8 @@ void load(const std::filesystem::path& path) {
|
||||
backButtonBehavior = toml::find_or<std::string>(input, "backButtonBehavior", "left");
|
||||
useSpecialPad = toml::find_or<bool>(input, "useSpecialPad", false);
|
||||
specialPadClass = toml::find_or<int>(input, "specialPadClass", 1);
|
||||
isMotionControlsEnabled = toml::find_or<bool>(input, "isMotionControlsEnabled", true);
|
||||
useUnifiedInputConfig = toml::find_or<bool>(input, "useUnifiedInputConfig", true);
|
||||
}
|
||||
|
||||
if (data.contains("GPU")) {
|
||||
@@ -597,6 +768,9 @@ void load(const std::filesystem::path& path) {
|
||||
shouldDumpShaders = toml::find_or<bool>(gpu, "dumpShaders", false);
|
||||
shouldPatchShaders = toml::find_or<bool>(gpu, "patchShaders", true);
|
||||
vblankDivider = toml::find_or<int>(gpu, "vblankDivider", 1);
|
||||
isFullscreen = toml::find_or<bool>(gpu, "Fullscreen", false);
|
||||
fullscreenMode = toml::find_or<std::string>(gpu, "FullscreenMode", "Windowed");
|
||||
isHDRAllowed = toml::find_or<bool>(gpu, "allowHDR", false);
|
||||
}
|
||||
|
||||
if (data.contains("Vulkan")) {
|
||||
@@ -606,21 +780,25 @@ void load(const std::filesystem::path& path) {
|
||||
vkValidation = toml::find_or<bool>(vk, "validation", false);
|
||||
vkValidationSync = toml::find_or<bool>(vk, "validation_sync", false);
|
||||
vkValidationGpu = toml::find_or<bool>(vk, "validation_gpu", true);
|
||||
rdocEnable = toml::find_or<bool>(vk, "rdocEnable", false);
|
||||
vkMarkers = toml::find_or<bool>(vk, "rdocMarkersEnable", false);
|
||||
vkCrashDiagnostic = toml::find_or<bool>(vk, "crashDiagnostic", false);
|
||||
vkHostMarkers = toml::find_or<bool>(vk, "hostMarkers", false);
|
||||
vkGuestMarkers = toml::find_or<bool>(vk, "guestMarkers", false);
|
||||
rdocEnable = toml::find_or<bool>(vk, "rdocEnable", false);
|
||||
}
|
||||
|
||||
if (data.contains("Debug")) {
|
||||
const toml::value& debug = data.at("Debug");
|
||||
|
||||
isDebugDump = toml::find_or<bool>(debug, "DebugDump", false);
|
||||
isSeparateLogFilesEnabled = toml::find_or<bool>(debug, "isSeparateLogFilesEnabled", false);
|
||||
isShaderDebug = toml::find_or<bool>(debug, "CollectShader", false);
|
||||
isFpsColor = toml::find_or<bool>(debug, "FPSColor", true);
|
||||
}
|
||||
|
||||
if (data.contains("GUI")) {
|
||||
const toml::value& gui = data.at("GUI");
|
||||
|
||||
load_game_size = toml::find_or<bool>(gui, "loadGameSizeEnabled", true);
|
||||
m_icon_size = toml::find_or<int>(gui, "iconSize", 0);
|
||||
m_icon_size_grid = toml::find_or<int>(gui, "iconSizeGrid", 0);
|
||||
m_slider_pos = toml::find_or<int>(gui, "sliderPos", 0);
|
||||
@@ -635,6 +813,8 @@ void load(const std::filesystem::path& path) {
|
||||
addGameInstallDir(std::filesystem::path{dir});
|
||||
}
|
||||
|
||||
save_data_path = toml::find_fs_path_or(gui, "saveDataPath", {});
|
||||
|
||||
settings_addon_install_dir = toml::find_fs_path_or(gui, "addonInstallDir", {});
|
||||
main_window_geometry_x = toml::find_or<int>(gui, "geometry_x", 0);
|
||||
main_window_geometry_y = toml::find_or<int>(gui, "geometry_y", 0);
|
||||
@@ -644,7 +824,9 @@ void load(const std::filesystem::path& path) {
|
||||
m_elf_viewer = toml::find_or<std::vector<std::string>>(gui, "elfDirs", {});
|
||||
m_recent_files = toml::find_or<std::vector<std::string>>(gui, "recentFiles", {});
|
||||
m_table_mode = toml::find_or<int>(gui, "gameTableMode", 0);
|
||||
emulator_language = toml::find_or<std::string>(gui, "emulatorLanguage", "en");
|
||||
emulator_language = toml::find_or<std::string>(gui, "emulatorLanguage", "en_US");
|
||||
backgroundImageOpacity = toml::find_or<int>(gui, "backgroundImageOpacity", 50);
|
||||
showBackgroundImage = toml::find_or<bool>(gui, "showBackgroundImage", true);
|
||||
}
|
||||
|
||||
if (data.contains("Settings")) {
|
||||
@@ -652,10 +834,27 @@ void load(const std::filesystem::path& path) {
|
||||
|
||||
m_language = toml::find_or<int>(settings, "consoleLanguage", 1);
|
||||
}
|
||||
|
||||
if (data.contains("Keys")) {
|
||||
const toml::value& keys = data.at("Keys");
|
||||
trophyKey = toml::find_or<std::string>(keys, "TrophyKey", "");
|
||||
}
|
||||
|
||||
// Check if the loaded language is in the allowed list
|
||||
const std::vector<std::string> allowed_languages = {
|
||||
"ar_SA", "da_DK", "de_DE", "el_GR", "en_US", "es_ES", "fa_IR", "fi_FI", "fr_FR", "hu_HU",
|
||||
"id_ID", "it_IT", "ja_JP", "ko_KR", "lt_LT", "nb_NO", "nl_NL", "pl_PL", "pt_BR", "pt_PT",
|
||||
"ro_RO", "ru_RU", "sq_AL", "sv_SE", "tr_TR", "uk_UA", "vi_VN", "zh_CN", "zh_TW"};
|
||||
|
||||
if (std::find(allowed_languages.begin(), allowed_languages.end(), emulator_language) ==
|
||||
allowed_languages.end()) {
|
||||
emulator_language = "en_US"; // Default to en_US if not in the list
|
||||
save(path);
|
||||
}
|
||||
}
|
||||
|
||||
void save(const std::filesystem::path& path) {
|
||||
toml::value data;
|
||||
toml::ordered_value data;
|
||||
|
||||
std::error_code error;
|
||||
if (std::filesystem::exists(path, error)) {
|
||||
@@ -663,7 +862,8 @@ void save(const std::filesystem::path& path) {
|
||||
std::ifstream ifs;
|
||||
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
ifs.open(path, std::ios_base::binary);
|
||||
data = toml::parse(ifs, std::string{fmt::UTF(path.filename().u8string()).data});
|
||||
data = toml::parse<toml::ordered_type_config>(
|
||||
ifs, std::string{fmt::UTF(path.filename().u8string()).data});
|
||||
} catch (const std::exception& ex) {
|
||||
fmt::print("Exception trying to parse config file. Exception: {}\n", ex.what());
|
||||
return;
|
||||
@@ -676,8 +876,8 @@ void save(const std::filesystem::path& path) {
|
||||
}
|
||||
|
||||
data["General"]["isPS4Pro"] = isNeo;
|
||||
data["General"]["Fullscreen"] = isFullscreen;
|
||||
data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled;
|
||||
data["General"]["trophyNotificationDuration"] = trophyNotificationDuration;
|
||||
data["General"]["playBGM"] = playBGM;
|
||||
data["General"]["BGMvolume"] = BGMvolume;
|
||||
data["General"]["enableDiscordRPC"] = enableDiscordRPC;
|
||||
@@ -685,8 +885,11 @@ void save(const std::filesystem::path& path) {
|
||||
data["General"]["logType"] = logType;
|
||||
data["General"]["userName"] = userName;
|
||||
data["General"]["updateChannel"] = updateChannel;
|
||||
data["General"]["chooseHomeTab"] = chooseHomeTab;
|
||||
data["General"]["showSplash"] = isShowSplash;
|
||||
data["General"]["autoUpdate"] = isAutoUpdate;
|
||||
data["General"]["alwaysShowChangelog"] = isAlwaysShowChangelog;
|
||||
data["General"]["sideTrophy"] = isSideTrophy;
|
||||
data["General"]["separateUpdateEnabled"] = separateupdatefolder;
|
||||
data["General"]["compatibilityEnabled"] = compatibilityData;
|
||||
data["General"]["checkCompatibilityOnStartup"] = checkCompatibilityOnStartup;
|
||||
@@ -695,6 +898,8 @@ void save(const std::filesystem::path& path) {
|
||||
data["Input"]["backButtonBehavior"] = backButtonBehavior;
|
||||
data["Input"]["useSpecialPad"] = useSpecialPad;
|
||||
data["Input"]["specialPadClass"] = specialPadClass;
|
||||
data["Input"]["isMotionControlsEnabled"] = isMotionControlsEnabled;
|
||||
data["Input"]["useUnifiedInputConfig"] = useUnifiedInputConfig;
|
||||
data["GPU"]["screenWidth"] = screenWidth;
|
||||
data["GPU"]["screenHeight"] = screenHeight;
|
||||
data["GPU"]["nullGpu"] = isNullGpu;
|
||||
@@ -702,25 +907,37 @@ void save(const std::filesystem::path& path) {
|
||||
data["GPU"]["dumpShaders"] = shouldDumpShaders;
|
||||
data["GPU"]["patchShaders"] = shouldPatchShaders;
|
||||
data["GPU"]["vblankDivider"] = vblankDivider;
|
||||
data["GPU"]["Fullscreen"] = isFullscreen;
|
||||
data["GPU"]["FullscreenMode"] = fullscreenMode;
|
||||
data["GPU"]["allowHDR"] = isHDRAllowed;
|
||||
data["Vulkan"]["gpuId"] = gpuId;
|
||||
data["Vulkan"]["validation"] = vkValidation;
|
||||
data["Vulkan"]["validation_sync"] = vkValidationSync;
|
||||
data["Vulkan"]["validation_gpu"] = vkValidationGpu;
|
||||
data["Vulkan"]["rdocEnable"] = rdocEnable;
|
||||
data["Vulkan"]["rdocMarkersEnable"] = vkMarkers;
|
||||
data["Vulkan"]["crashDiagnostic"] = vkCrashDiagnostic;
|
||||
data["Vulkan"]["hostMarkers"] = vkHostMarkers;
|
||||
data["Vulkan"]["guestMarkers"] = vkGuestMarkers;
|
||||
data["Vulkan"]["rdocEnable"] = rdocEnable;
|
||||
data["Debug"]["DebugDump"] = isDebugDump;
|
||||
data["Debug"]["CollectShader"] = isShaderDebug;
|
||||
data["Debug"]["isSeparateLogFilesEnabled"] = isSeparateLogFilesEnabled;
|
||||
data["Debug"]["FPSColor"] = isFpsColor;
|
||||
|
||||
data["Keys"]["TrophyKey"] = trophyKey;
|
||||
|
||||
std::vector<std::string> install_dirs;
|
||||
for (const auto& dirString : settings_install_dirs) {
|
||||
install_dirs.emplace_back(std::string{fmt::UTF(dirString.u8string()).data});
|
||||
}
|
||||
data["GUI"]["installDirs"] = install_dirs;
|
||||
data["GUI"]["saveDataPath"] = std::string{fmt::UTF(save_data_path.u8string()).data};
|
||||
data["GUI"]["loadGameSizeEnabled"] = load_game_size;
|
||||
|
||||
data["GUI"]["addonInstallDir"] =
|
||||
std::string{fmt::UTF(settings_addon_install_dir.u8string()).data};
|
||||
data["GUI"]["emulatorLanguage"] = emulator_language;
|
||||
data["GUI"]["backgroundImageOpacity"] = backgroundImageOpacity;
|
||||
data["GUI"]["showBackgroundImage"] = showBackgroundImage;
|
||||
data["Settings"]["consoleLanguage"] = m_language;
|
||||
|
||||
std::ofstream file(path, std::ios::binary);
|
||||
@@ -730,7 +947,7 @@ void save(const std::filesystem::path& path) {
|
||||
}
|
||||
|
||||
void saveMainWindow(const std::filesystem::path& path) {
|
||||
toml::value data;
|
||||
toml::ordered_value data;
|
||||
|
||||
std::error_code error;
|
||||
if (std::filesystem::exists(path, error)) {
|
||||
@@ -738,7 +955,8 @@ void saveMainWindow(const std::filesystem::path& path) {
|
||||
std::ifstream ifs;
|
||||
ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit);
|
||||
ifs.open(path, std::ios_base::binary);
|
||||
data = toml::parse(ifs, std::string{fmt::UTF(path.filename().u8string()).data});
|
||||
data = toml::parse<toml::ordered_type_config>(
|
||||
ifs, std::string{fmt::UTF(path.filename().u8string()).data});
|
||||
} catch (const std::exception& ex) {
|
||||
fmt::print("Exception trying to parse config file. Exception: {}\n", ex.what());
|
||||
return;
|
||||
@@ -772,6 +990,7 @@ void saveMainWindow(const std::filesystem::path& path) {
|
||||
}
|
||||
|
||||
void setDefaultValues() {
|
||||
isHDRAllowed = false;
|
||||
isNeo = false;
|
||||
isFullscreen = false;
|
||||
isTrophyPopupDisabled = false;
|
||||
@@ -788,8 +1007,10 @@ void setDefaultValues() {
|
||||
} else {
|
||||
updateChannel = "Nightly";
|
||||
}
|
||||
chooseHomeTab = "General";
|
||||
cursorState = HideCursorState::Idle;
|
||||
cursorHideTimeout = 5;
|
||||
trophyNotificationDuration = 6.0;
|
||||
backButtonBehavior = "left";
|
||||
useSpecialPad = false;
|
||||
specialPadClass = 1;
|
||||
@@ -797,21 +1018,136 @@ void setDefaultValues() {
|
||||
isShaderDebug = false;
|
||||
isShowSplash = false;
|
||||
isAutoUpdate = false;
|
||||
isAlwaysShowChangelog = false;
|
||||
isSideTrophy = "right";
|
||||
isNullGpu = false;
|
||||
shouldDumpShaders = false;
|
||||
vblankDivider = 1;
|
||||
vkValidation = false;
|
||||
vkValidationSync = false;
|
||||
vkValidationGpu = false;
|
||||
rdocEnable = false;
|
||||
vkMarkers = false;
|
||||
vkCrashDiagnostic = false;
|
||||
emulator_language = "en";
|
||||
vkHostMarkers = false;
|
||||
vkGuestMarkers = false;
|
||||
rdocEnable = false;
|
||||
emulator_language = "en_US";
|
||||
m_language = 1;
|
||||
gpuId = -1;
|
||||
separateupdatefolder = false;
|
||||
compatibilityData = false;
|
||||
checkCompatibilityOnStartup = false;
|
||||
backgroundImageOpacity = 50;
|
||||
showBackgroundImage = true;
|
||||
}
|
||||
|
||||
constexpr std::string_view GetDefaultKeyboardConfig() {
|
||||
return R"(#Feeling lost? Check out the Help section!
|
||||
|
||||
# Keyboard bindings
|
||||
|
||||
triangle = kp8
|
||||
circle = kp6
|
||||
cross = kp2
|
||||
square = kp4
|
||||
# Alternatives for users without a keypad
|
||||
triangle = c
|
||||
circle = b
|
||||
cross = n
|
||||
square = v
|
||||
|
||||
l1 = q
|
||||
r1 = u
|
||||
l2 = e
|
||||
r2 = o
|
||||
l3 = x
|
||||
r3 = m
|
||||
|
||||
options = enter
|
||||
touchpad = space
|
||||
|
||||
pad_up = up
|
||||
pad_down = down
|
||||
pad_left = left
|
||||
pad_right = right
|
||||
|
||||
axis_left_x_minus = a
|
||||
axis_left_x_plus = d
|
||||
axis_left_y_minus = w
|
||||
axis_left_y_plus = s
|
||||
|
||||
axis_right_x_minus = j
|
||||
axis_right_x_plus = l
|
||||
axis_right_y_minus = i
|
||||
axis_right_y_plus = k
|
||||
|
||||
# Controller bindings
|
||||
|
||||
triangle = triangle
|
||||
cross = cross
|
||||
square = square
|
||||
circle = circle
|
||||
|
||||
l1 = l1
|
||||
l2 = l2
|
||||
l3 = l3
|
||||
r1 = r1
|
||||
r2 = r2
|
||||
r3 = r3
|
||||
|
||||
options = options
|
||||
touchpad = back
|
||||
|
||||
pad_up = pad_up
|
||||
pad_down = pad_down
|
||||
pad_left = pad_left
|
||||
pad_right = pad_right
|
||||
|
||||
axis_left_x = axis_left_x
|
||||
axis_left_y = axis_left_y
|
||||
axis_right_x = axis_right_x
|
||||
axis_right_y = axis_right_y
|
||||
|
||||
# Range of deadzones: 1 (almost none) to 127 (max)
|
||||
analog_deadzone = leftjoystick, 2, 127
|
||||
analog_deadzone = rightjoystick, 2, 127
|
||||
|
||||
override_controller_color = false, 0, 0, 255
|
||||
)";
|
||||
}
|
||||
std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id) {
|
||||
// Read configuration file of the game, and if it doesn't exist, generate it from default
|
||||
// If that doesn't exist either, generate that from getDefaultConfig() and try again
|
||||
// If even the folder is missing, we start with that.
|
||||
|
||||
const auto config_dir = Common::FS::GetUserPath(Common::FS::PathType::UserDir) / "input_config";
|
||||
const auto config_file = config_dir / (game_id + ".ini");
|
||||
const auto default_config_file = config_dir / "default.ini";
|
||||
|
||||
// Ensure the config directory exists
|
||||
if (!std::filesystem::exists(config_dir)) {
|
||||
std::filesystem::create_directories(config_dir);
|
||||
}
|
||||
|
||||
// Check if the default config exists
|
||||
if (!std::filesystem::exists(default_config_file)) {
|
||||
// If the default config is also missing, create it from getDefaultConfig()
|
||||
const auto default_config = GetDefaultKeyboardConfig();
|
||||
std::ofstream default_config_stream(default_config_file);
|
||||
if (default_config_stream) {
|
||||
default_config_stream << default_config;
|
||||
}
|
||||
}
|
||||
|
||||
// if empty, we only need to execute the function up until this point
|
||||
if (game_id.empty()) {
|
||||
return default_config_file;
|
||||
}
|
||||
|
||||
// If game-specific config doesn't exist, create it from the default config
|
||||
if (!std::filesystem::exists(config_file)) {
|
||||
std::filesystem::copy(default_config_file, config_file);
|
||||
}
|
||||
return config_file;
|
||||
}
|
||||
|
||||
} // namespace Config
|
||||
|
||||
@@ -15,8 +15,14 @@ void load(const std::filesystem::path& path);
|
||||
void save(const std::filesystem::path& path);
|
||||
void saveMainWindow(const std::filesystem::path& path);
|
||||
|
||||
bool isNeoMode();
|
||||
bool isFullscreenMode();
|
||||
std::string getTrophyKey();
|
||||
void setTrophyKey(std::string key);
|
||||
bool GetLoadGameSizeEnabled();
|
||||
std::filesystem::path GetSaveDataPath();
|
||||
void setLoadGameSizeEnabled(bool enable);
|
||||
bool getIsFullscreen();
|
||||
std::string getFullscreenMode();
|
||||
bool isNeoModeConsole();
|
||||
bool getPlayBGM();
|
||||
int getBGMvolume();
|
||||
bool getisTrophyPopupDisabled();
|
||||
@@ -24,45 +30,64 @@ bool getEnableDiscordRPC();
|
||||
bool getSeparateUpdateEnabled();
|
||||
bool getCompatibilityEnabled();
|
||||
bool getCheckCompatibilityOnStartup();
|
||||
int getBackgroundImageOpacity();
|
||||
bool getShowBackgroundImage();
|
||||
|
||||
std::string getLogFilter();
|
||||
std::string getLogType();
|
||||
std::string getUserName();
|
||||
std::string getUpdateChannel();
|
||||
std::string getChooseHomeTab();
|
||||
|
||||
s16 getCursorState();
|
||||
int getCursorHideTimeout();
|
||||
double getTrophyNotificationDuration();
|
||||
std::string getBackButtonBehavior();
|
||||
bool getUseSpecialPad();
|
||||
int getSpecialPadClass();
|
||||
bool getIsMotionControlsEnabled();
|
||||
bool GetUseUnifiedInputConfig();
|
||||
void SetUseUnifiedInputConfig(bool use);
|
||||
bool GetOverrideControllerColor();
|
||||
void SetOverrideControllerColor(bool enable);
|
||||
int* GetControllerCustomColor();
|
||||
void SetControllerCustomColor(int r, int b, int g);
|
||||
|
||||
u32 getScreenWidth();
|
||||
u32 getScreenHeight();
|
||||
s32 getGpuId();
|
||||
bool allowHDR();
|
||||
|
||||
bool debugDump();
|
||||
bool collectShadersForDebug();
|
||||
bool showSplash();
|
||||
bool autoUpdate();
|
||||
bool alwaysShowChangelog();
|
||||
std::string sideTrophy();
|
||||
bool nullGpu();
|
||||
bool copyGPUCmdBuffers();
|
||||
bool dumpShaders();
|
||||
bool patchShaders();
|
||||
bool isRdocEnabled();
|
||||
bool fpsColor();
|
||||
u32 vblankDiv();
|
||||
|
||||
void setDebugDump(bool enable);
|
||||
void setCollectShaderForDebug(bool enable);
|
||||
void setShowSplash(bool enable);
|
||||
void setAutoUpdate(bool enable);
|
||||
void setAlwaysShowChangelog(bool enable);
|
||||
void setSideTrophy(std::string side);
|
||||
void setNullGpu(bool enable);
|
||||
void setAllowHDR(bool enable);
|
||||
void setCopyGPUCmdBuffers(bool enable);
|
||||
void setDumpShaders(bool enable);
|
||||
void setVblankDiv(u32 value);
|
||||
void setGpuId(s32 selectedGpuId);
|
||||
void setScreenWidth(u32 width);
|
||||
void setScreenHeight(u32 height);
|
||||
void setFullscreenMode(bool enable);
|
||||
void setIsFullscreen(bool enable);
|
||||
void setFullscreenMode(std::string mode);
|
||||
void setisTrophyPopupDisabled(bool disable);
|
||||
void setPlayBGM(bool enable);
|
||||
void setBGMvolume(int volume);
|
||||
@@ -71,20 +96,27 @@ void setLanguage(u32 language);
|
||||
void setNeoMode(bool enable);
|
||||
void setUserName(const std::string& type);
|
||||
void setUpdateChannel(const std::string& type);
|
||||
void setChooseHomeTab(const std::string& type);
|
||||
void setSeparateUpdateEnabled(bool use);
|
||||
void setGameInstallDirs(const std::vector<std::filesystem::path>& settings_install_dirs_config);
|
||||
void setSaveDataPath(const std::filesystem::path& path);
|
||||
void setCompatibilityEnabled(bool use);
|
||||
void setCheckCompatibilityOnStartup(bool use);
|
||||
void setBackgroundImageOpacity(int opacity);
|
||||
void setShowBackgroundImage(bool show);
|
||||
|
||||
void setCursorState(s16 cursorState);
|
||||
void setCursorHideTimeout(int newcursorHideTimeout);
|
||||
void setTrophyNotificationDuration(double newTrophyNotificationDuration);
|
||||
void setBackButtonBehavior(const std::string& type);
|
||||
void setUseSpecialPad(bool use);
|
||||
void setSpecialPadClass(int type);
|
||||
void setIsMotionControlsEnabled(bool use);
|
||||
|
||||
void setLogType(const std::string& type);
|
||||
void setLogFilter(const std::string& type);
|
||||
|
||||
void setSeparateLogFilesEnabled(bool enabled);
|
||||
bool getSeparateLogFilesEnabled();
|
||||
void setVkValidation(bool enable);
|
||||
void setVkSyncValidation(bool enable);
|
||||
void setRdocEnabled(bool enable);
|
||||
@@ -92,8 +124,12 @@ void setRdocEnabled(bool enable);
|
||||
bool vkValidationEnabled();
|
||||
bool vkValidationSyncEnabled();
|
||||
bool vkValidationGpuEnabled();
|
||||
bool vkMarkersEnabled();
|
||||
bool vkCrashDiagnosticEnabled();
|
||||
bool getVkCrashDiagnosticEnabled();
|
||||
bool getVkHostMarkersEnabled();
|
||||
bool getVkGuestMarkersEnabled();
|
||||
void setVkCrashDiagnosticEnabled(bool enable);
|
||||
void setVkHostMarkersEnabled(bool enable);
|
||||
void setVkGuestMarkersEnabled(bool enable);
|
||||
|
||||
// Gui
|
||||
void setMainWindowGeometry(u32 x, u32 y, u32 w, u32 h);
|
||||
@@ -134,6 +170,9 @@ std::string getEmulatorLanguage();
|
||||
|
||||
void setDefaultValues();
|
||||
|
||||
// todo: name and function location pending
|
||||
std::filesystem::path GetFoolproofKbmConfigFile(const std::string& game_id = "");
|
||||
|
||||
// settings
|
||||
u32 GetLanguage();
|
||||
}; // namespace Config
|
||||
}; // namespace Config
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "assert.h"
|
||||
#include "bit_field.h"
|
||||
#include "singleton.h"
|
||||
#include "types.h"
|
||||
|
||||
@@ -16,6 +18,46 @@ class Emulator;
|
||||
|
||||
namespace Common {
|
||||
|
||||
union PSFAttributes {
|
||||
/// Supports initial user's logout
|
||||
BitField<0, 1, u32> support_initial_user_logout;
|
||||
/// Enter button for the common dialog is cross.
|
||||
BitField<1, 1, u32> enter_button_cross;
|
||||
/// Warning dialog for PS Move is displayed in the options menu.
|
||||
BitField<2, 1, u32> ps_move_warning;
|
||||
/// Supports stereoscopic 3D.
|
||||
BitField<3, 1, u32> support_stereoscopic_3d;
|
||||
/// Suspends when PS button is pressed.
|
||||
BitField<4, 1, u32> ps_button_suspend;
|
||||
/// Enter button for the common dialog is assigned by the system software.
|
||||
BitField<5, 1, u32> enter_button_system;
|
||||
/// Overrides share menu behavior.
|
||||
BitField<6, 1, u32> override_share_menu;
|
||||
/// Suspends when PS button is pressed and special output resolution is set.
|
||||
BitField<8, 1, u32> special_res_ps_button_suspend;
|
||||
/// Enable HDCP.
|
||||
BitField<9, 1, u32> enable_hdcp;
|
||||
/// Disable HDCP for non-game.
|
||||
BitField<10, 1, u32> disable_hdcp_non_game;
|
||||
/// Supports PS VR.
|
||||
BitField<14, 1, u32> support_ps_vr;
|
||||
/// CPU mode (6 CPU)
|
||||
BitField<15, 1, u32> six_cpu_mode;
|
||||
/// CPU mode (7 CPU)
|
||||
BitField<16, 1, u32> seven_cpu_mode;
|
||||
/// Supports PS4 Pro (Neo) mode.
|
||||
BitField<23, 1, u32> support_neo_mode;
|
||||
/// Requires PS VR.
|
||||
BitField<26, 1, u32> require_ps_vr;
|
||||
/// Supports HDR.
|
||||
BitField<29, 1, u32> support_hdr;
|
||||
/// Display location.
|
||||
BitField<31, 1, u32> display_location;
|
||||
|
||||
u32 raw{};
|
||||
};
|
||||
static_assert(sizeof(PSFAttributes) == 4);
|
||||
|
||||
class ElfInfo {
|
||||
friend class Core::Emulator;
|
||||
|
||||
@@ -26,6 +68,9 @@ class ElfInfo {
|
||||
std::string app_ver{};
|
||||
u32 firmware_ver = 0;
|
||||
u32 raw_firmware_ver = 0;
|
||||
PSFAttributes psf_attributes{};
|
||||
|
||||
std::filesystem::path splash_path{};
|
||||
|
||||
public:
|
||||
static constexpr u32 FW_15 = 0x1500000;
|
||||
@@ -34,9 +79,11 @@ public:
|
||||
static constexpr u32 FW_20 = 0x2000000;
|
||||
static constexpr u32 FW_25 = 0x2500000;
|
||||
static constexpr u32 FW_30 = 0x3000000;
|
||||
static constexpr u32 FW_35 = 0x3500000;
|
||||
static constexpr u32 FW_40 = 0x4000000;
|
||||
static constexpr u32 FW_45 = 0x4500000;
|
||||
static constexpr u32 FW_50 = 0x5000000;
|
||||
static constexpr u32 FW_55 = 0x5500000;
|
||||
static constexpr u32 FW_80 = 0x8000000;
|
||||
|
||||
static ElfInfo& Instance() {
|
||||
@@ -67,6 +114,15 @@ public:
|
||||
ASSERT(initialized);
|
||||
return raw_firmware_ver;
|
||||
}
|
||||
|
||||
[[nodiscard]] const PSFAttributes& GetPSFAttributes() const {
|
||||
ASSERT(initialized);
|
||||
return psf_attributes;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::filesystem::path& GetSplashPath() const {
|
||||
return splash_path;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -139,8 +139,9 @@ public:
|
||||
std::filesystem::create_directory(log_dir);
|
||||
Filter filter;
|
||||
filter.ParseFilterString(Config::getLogFilter());
|
||||
instance = std::unique_ptr<Impl, decltype(&Deleter)>(new Impl(log_dir / LOG_FILE, filter),
|
||||
Deleter);
|
||||
const auto& log_file_path = log_file.empty() ? LOG_FILE : log_file;
|
||||
instance = std::unique_ptr<Impl, decltype(&Deleter)>(
|
||||
new Impl(log_dir / log_file_path, filter), Deleter);
|
||||
initialization_in_progress_suppress_logging = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Kernel, Sce) \
|
||||
CLS(Lib) \
|
||||
SUB(Lib, LibC) \
|
||||
SUB(Lib, LibcInternal) \
|
||||
SUB(Lib, Kernel) \
|
||||
SUB(Lib, Pad) \
|
||||
SUB(Lib, GnmDriver) \
|
||||
@@ -95,11 +96,16 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Lib, SaveData) \
|
||||
SUB(Lib, SaveDataDialog) \
|
||||
SUB(Lib, Http) \
|
||||
SUB(Lib, Http2) \
|
||||
SUB(Lib, Ssl) \
|
||||
SUB(Lib, Ssl2) \
|
||||
SUB(Lib, SysModule) \
|
||||
SUB(Lib, Move) \
|
||||
SUB(Lib, NpCommon) \
|
||||
SUB(Lib, NpManager) \
|
||||
SUB(Lib, NpScore) \
|
||||
SUB(Lib, NpTrophy) \
|
||||
SUB(Lib, NpWebApi) \
|
||||
SUB(Lib, Screenshot) \
|
||||
SUB(Lib, LibCInternal) \
|
||||
SUB(Lib, AppContent) \
|
||||
@@ -125,6 +131,11 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
|
||||
SUB(Lib, Vdec2) \
|
||||
SUB(Lib, Videodec) \
|
||||
SUB(Lib, RazorCpu) \
|
||||
SUB(Lib, Mouse) \
|
||||
SUB(Lib, WebBrowserDialog) \
|
||||
SUB(Lib, NpParty) \
|
||||
SUB(Lib, Zlib) \
|
||||
SUB(Lib, Hmd) \
|
||||
CLS(Frontend) \
|
||||
CLS(Render) \
|
||||
SUB(Render, Vulkan) \
|
||||
|
||||
@@ -23,8 +23,8 @@ std::string FormatLogMessage(const Entry& entry) {
|
||||
const char* class_name = GetLogClassName(entry.log_class);
|
||||
const char* level_name = GetLevelName(entry.log_level);
|
||||
|
||||
return fmt::format("[{}] <{}> {}:{}:{}: {}", class_name, level_name, entry.filename,
|
||||
entry.function, entry.line_num, entry.message);
|
||||
return fmt::format("[{}] <{}> {}:{} {}: {}", class_name, level_name, entry.filename,
|
||||
entry.line_num, entry.function, entry.message);
|
||||
}
|
||||
|
||||
void PrintMessage(const Entry& entry) {
|
||||
|
||||
@@ -47,6 +47,7 @@ enum class Class : u8 {
|
||||
Lib, ///< HLE implementation of system library. Each major library
|
||||
///< should have its own subclass.
|
||||
Lib_LibC, ///< The LibC implementation.
|
||||
Lib_LibcInternal, ///< The LibcInternal implementation.
|
||||
Lib_Kernel, ///< The LibKernel implementation.
|
||||
Lib_Pad, ///< The LibScePad implementation.
|
||||
Lib_GnmDriver, ///< The LibSceGnmDriver implementation.
|
||||
@@ -57,16 +58,21 @@ enum class Class : u8 {
|
||||
Lib_MsgDlg, ///< The LibSceMsgDialog implementation.
|
||||
Lib_AudioOut, ///< The LibSceAudioOut implementation.
|
||||
Lib_AudioIn, ///< The LibSceAudioIn implementation.
|
||||
Lib_Move, ///< The LibSceMove implementation.
|
||||
Lib_Net, ///< The LibSceNet implementation.
|
||||
Lib_NetCtl, ///< The LibSecNetCtl implementation.
|
||||
Lib_NetCtl, ///< The LibSceNetCtl implementation.
|
||||
Lib_SaveData, ///< The LibSceSaveData implementation.
|
||||
Lib_SaveDataDialog, ///< The LibSceSaveDataDialog implementation.
|
||||
Lib_Ssl, ///< The LibSceSsl implementation.
|
||||
Lib_Ssl2, ///< The LibSceSsl2 implementation.
|
||||
Lib_Http, ///< The LibSceHttp implementation.
|
||||
Lib_Http2, ///< The LibSceHttp2 implementation.
|
||||
Lib_SysModule, ///< The LibSceSysModule implementation
|
||||
Lib_NpCommon, ///< The LibSceNpCommon implementation
|
||||
Lib_NpManager, ///< The LibSceNpManager implementation
|
||||
Lib_NpScore, ///< The LibSceNpScore implementation
|
||||
Lib_NpTrophy, ///< The LibSceNpTrophy implementation
|
||||
Lib_NpWebApi, ///< The LibSceWebApi implementation
|
||||
Lib_Screenshot, ///< The LibSceScreenshot implementation
|
||||
Lib_LibCInternal, ///< The LibCInternal implementation.
|
||||
Lib_AppContent, ///< The LibSceAppContent implementation.
|
||||
@@ -92,6 +98,11 @@ enum class Class : u8 {
|
||||
Lib_Vdec2, ///< The LibSceVideodec2 implementation.
|
||||
Lib_Videodec, ///< The LibSceVideodec implementation.
|
||||
Lib_RazorCpu, ///< The LibRazorCpu implementation.
|
||||
Lib_Mouse, ///< The LibSceMouse implementation
|
||||
Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation
|
||||
Lib_NpParty, ///< The LibSceNpParty implementation
|
||||
Lib_Zlib, ///< The LibSceZlib implementation.
|
||||
Lib_Hmd, ///< The LibSceHmd implementation.
|
||||
Frontend, ///< Emulator UI
|
||||
Render, ///< Video Core
|
||||
Render_Vulkan, ///< Vulkan backend
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <string>
|
||||
#include <pugixml.hpp>
|
||||
#ifdef ENABLE_QT_GUI
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
@@ -143,37 +144,38 @@ void OnGameLoaded() {
|
||||
std::string type = patchLineIt->attribute("Type").value();
|
||||
std::string address = patchLineIt->attribute("Address").value();
|
||||
std::string patchValue = patchLineIt->attribute("Value").value();
|
||||
std::string maskOffsetStr = patchLineIt->attribute("type").value();
|
||||
|
||||
patchValue = convertValueToHex(type, patchValue);
|
||||
std::string maskOffsetStr = patchLineIt->attribute("Offset").value();
|
||||
std::string targetStr = "";
|
||||
std::string sizeStr = "";
|
||||
if (type == "mask_jump32") {
|
||||
targetStr = patchLineIt->attribute("Target").value();
|
||||
sizeStr = patchLineIt->attribute("Size").value();
|
||||
} else {
|
||||
patchValue = convertValueToHex(type, patchValue);
|
||||
}
|
||||
|
||||
bool littleEndian = false;
|
||||
|
||||
if (type == "bytes16") {
|
||||
littleEndian = true;
|
||||
} else if (type == "bytes32") {
|
||||
littleEndian = true;
|
||||
} else if (type == "bytes64") {
|
||||
if (type == "bytes16" || type == "bytes32" || type == "bytes64") {
|
||||
littleEndian = true;
|
||||
}
|
||||
|
||||
MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
|
||||
int maskOffsetValue = 0;
|
||||
|
||||
if (type == "mask") {
|
||||
if (type == "mask")
|
||||
patchMask = MemoryPatcher::PatchMask::Mask;
|
||||
|
||||
// im not sure if this works, there is no games to test the mask
|
||||
// offset on yet
|
||||
if (!maskOffsetStr.empty())
|
||||
maskOffsetValue = std::stoi(maskOffsetStr, 0, 10);
|
||||
}
|
||||
|
||||
if (type == "mask_jump32")
|
||||
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
|
||||
|
||||
MemoryPatcher::PatchMemory(currentPatchName, address, patchValue, false,
|
||||
littleEndian, patchMask);
|
||||
if (type == "mask" || type == "mask_jump32" && !maskOffsetStr.empty()) {
|
||||
maskOffsetValue = std::stoi(maskOffsetStr, 0, 10);
|
||||
}
|
||||
|
||||
MemoryPatcher::PatchMemory(currentPatchName, address, patchValue,
|
||||
targetStr, sizeStr, false, littleEndian,
|
||||
patchMask, maskOffsetValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,14 +191,16 @@ void OnGameLoaded() {
|
||||
// We use the QT headers for the xml and json parsing, this define is only true on QT builds
|
||||
QString patchDir;
|
||||
Common::FS::PathToQString(patchDir, Common::FS::GetUserPath(Common::FS::PathType::PatchesDir));
|
||||
QString repositories[] = {"GoldHEN", "shadPS4"};
|
||||
QDir dir(patchDir);
|
||||
QStringList folders = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
|
||||
for (const QString& repository : repositories) {
|
||||
QString filesJsonPath = patchDir + "/" + repository + "/files.json";
|
||||
for (const QString& folder : folders) {
|
||||
QString filesJsonPath = patchDir + "/" + folder + "/files.json";
|
||||
|
||||
QFile jsonFile(filesJsonPath);
|
||||
if (!jsonFile.open(QIODevice::ReadOnly)) {
|
||||
LOG_ERROR(Loader, "Unable to open files.json for reading.");
|
||||
LOG_ERROR(Loader, "Unable to open files.json for reading in repository {}",
|
||||
folder.toStdString());
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -220,11 +224,12 @@ void OnGameLoaded() {
|
||||
}
|
||||
|
||||
if (selectedFileName.isEmpty()) {
|
||||
LOG_ERROR(Loader, "No patch file found for the current serial.");
|
||||
LOG_ERROR(Loader, "No patch file found for the current serial in repository {}",
|
||||
folder.toStdString());
|
||||
continue;
|
||||
}
|
||||
|
||||
QString filePath = patchDir + "/" + repository + "/" + selectedFileName;
|
||||
QString filePath = patchDir + "/" + folder + "/" + selectedFileName;
|
||||
QFile file(filePath);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||
LOG_ERROR(Loader, "Unable to open the file for reading.");
|
||||
@@ -274,6 +279,11 @@ void OnGameLoaded() {
|
||||
lineObject["Type"] = attributes.value("Type").toString();
|
||||
lineObject["Address"] = attributes.value("Address").toString();
|
||||
lineObject["Value"] = attributes.value("Value").toString();
|
||||
lineObject["Offset"] = attributes.value("Offset").toString();
|
||||
if (lineObject["Type"].toString() == "mask_jump32") {
|
||||
lineObject["Target"] = attributes.value("Target").toString();
|
||||
lineObject["Size"] = attributes.value("Size").toString();
|
||||
}
|
||||
linesArray.append(lineObject);
|
||||
}
|
||||
}
|
||||
@@ -287,37 +297,39 @@ void OnGameLoaded() {
|
||||
QString patchValue = lineObject["Value"].toString();
|
||||
QString maskOffsetStr = lineObject["Offset"].toString();
|
||||
|
||||
patchValue = QString::fromStdString(
|
||||
convertValueToHex(type.toStdString(), patchValue.toStdString()));
|
||||
QString targetStr;
|
||||
QString sizeStr;
|
||||
if (type == "mask_jump32") {
|
||||
targetStr = lineObject["Target"].toString();
|
||||
sizeStr = lineObject["Size"].toString();
|
||||
} else {
|
||||
patchValue = QString::fromStdString(convertValueToHex(
|
||||
type.toStdString(), patchValue.toStdString()));
|
||||
}
|
||||
|
||||
bool littleEndian = false;
|
||||
|
||||
if (type == "bytes16") {
|
||||
if (type == "bytes16" || type == "bytes32" || type == "bytes64")
|
||||
littleEndian = true;
|
||||
} else if (type == "bytes32") {
|
||||
littleEndian = true;
|
||||
} else if (type == "bytes64") {
|
||||
littleEndian = true;
|
||||
}
|
||||
|
||||
MemoryPatcher::PatchMask patchMask = MemoryPatcher::PatchMask::None;
|
||||
int maskOffsetValue = 0;
|
||||
|
||||
if (type == "mask") {
|
||||
if (type == "mask")
|
||||
patchMask = MemoryPatcher::PatchMask::Mask;
|
||||
|
||||
// im not sure if this works, there is no games to test the mask
|
||||
// offset on yet
|
||||
if (!maskOffsetStr.toStdString().empty())
|
||||
maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10);
|
||||
}
|
||||
|
||||
if (type == "mask_jump32")
|
||||
patchMask = MemoryPatcher::PatchMask::Mask_Jump32;
|
||||
|
||||
MemoryPatcher::PatchMemory(currentPatchName, address.toStdString(),
|
||||
patchValue.toStdString(), false,
|
||||
littleEndian, patchMask);
|
||||
if (type == "mask" ||
|
||||
type == "mask_jump32" && !maskOffsetStr.toStdString().empty()) {
|
||||
maskOffsetValue = std::stoi(maskOffsetStr.toStdString(), 0, 10);
|
||||
}
|
||||
|
||||
MemoryPatcher::PatchMemory(
|
||||
currentPatchName, address.toStdString(), patchValue.toStdString(),
|
||||
targetStr.toStdString(), sizeStr.toStdString(), false, littleEndian,
|
||||
patchMask, maskOffsetValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -346,7 +358,7 @@ void ApplyPendingPatches() {
|
||||
if (currentPatch.gameSerial != g_game_serial)
|
||||
continue;
|
||||
|
||||
PatchMemory(currentPatch.modNameStr, currentPatch.offsetStr, currentPatch.valueStr,
|
||||
PatchMemory(currentPatch.modNameStr, currentPatch.offsetStr, currentPatch.valueStr, "", "",
|
||||
currentPatch.isOffset, currentPatch.littleEndian, currentPatch.patchMask,
|
||||
currentPatch.maskOffset);
|
||||
}
|
||||
@@ -354,8 +366,9 @@ void ApplyPendingPatches() {
|
||||
pending_patches.clear();
|
||||
}
|
||||
|
||||
void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr, bool isOffset,
|
||||
bool littleEndian, PatchMask patchMask, int maskOffset) {
|
||||
void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr,
|
||||
std::string targetStr, std::string sizeStr, bool isOffset, bool littleEndian,
|
||||
PatchMask patchMask, int maskOffset) {
|
||||
// Send a request to modify the process memory.
|
||||
void* cheatAddress = nullptr;
|
||||
|
||||
@@ -372,7 +385,83 @@ void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valu
|
||||
cheatAddress = reinterpret_cast<void*>(PatternScan(offsetStr) + maskOffset);
|
||||
}
|
||||
|
||||
// TODO: implement mask_jump32
|
||||
if (patchMask == PatchMask::Mask_Jump32) {
|
||||
int jumpSize = std::stoi(sizeStr);
|
||||
|
||||
constexpr int MAX_PATTERN_LENGTH = 256;
|
||||
if (jumpSize < 5) {
|
||||
LOG_ERROR(Loader, "Jump size must be at least 5 bytes");
|
||||
return;
|
||||
}
|
||||
if (jumpSize > MAX_PATTERN_LENGTH) {
|
||||
LOG_ERROR(Loader, "Jump size must be no more than {} bytes.", MAX_PATTERN_LENGTH);
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the base address using "Address"
|
||||
uintptr_t baseAddress = PatternScan(offsetStr);
|
||||
if (baseAddress == 0) {
|
||||
LOG_ERROR(Loader, "PatternScan failed for mask_jump32 with pattern: {}", offsetStr);
|
||||
return;
|
||||
}
|
||||
uintptr_t patchAddress = baseAddress + maskOffset;
|
||||
|
||||
// Fills the original region (jumpSize bytes) with NOPs
|
||||
std::vector<u8> nopBytes(jumpSize, 0x90);
|
||||
std::memcpy(reinterpret_cast<void*>(patchAddress), nopBytes.data(), nopBytes.size());
|
||||
|
||||
// Use "Target" to locate the start of the code cave
|
||||
uintptr_t jump_target = PatternScan(targetStr);
|
||||
if (jump_target == 0) {
|
||||
LOG_ERROR(Loader, "PatternScan failed to Target with pattern: {}", targetStr);
|
||||
return;
|
||||
}
|
||||
|
||||
// Converts the Value attribute to a byte array (payload)
|
||||
std::vector<u8> payload;
|
||||
for (size_t i = 0; i < valueStr.length(); i += 2) {
|
||||
|
||||
std::string tempStr = valueStr.substr(i, 2);
|
||||
const char* byteStr = tempStr.c_str();
|
||||
char* endPtr;
|
||||
unsigned int byteVal = std::strtoul(byteStr, &endPtr, 16);
|
||||
|
||||
if (endPtr != byteStr + 2) {
|
||||
LOG_ERROR(Loader, "Invalid byte in Value: {}", valueStr.substr(i, 2));
|
||||
return;
|
||||
}
|
||||
payload.push_back(static_cast<u8>(byteVal));
|
||||
}
|
||||
|
||||
// Calculates the end of the code cave (where the return jump will be inserted)
|
||||
uintptr_t code_cave_end = jump_target + payload.size();
|
||||
|
||||
// Write the payload to the code cave, from jump_target
|
||||
std::memcpy(reinterpret_cast<void*>(jump_target), payload.data(), payload.size());
|
||||
|
||||
// Inserts the initial jump in the original region to divert to the code cave
|
||||
u8 jumpInstruction[5];
|
||||
jumpInstruction[0] = 0xE9;
|
||||
s32 relJump = static_cast<s32>(jump_target - patchAddress - 5);
|
||||
std::memcpy(&jumpInstruction[1], &relJump, sizeof(relJump));
|
||||
std::memcpy(reinterpret_cast<void*>(patchAddress), jumpInstruction,
|
||||
sizeof(jumpInstruction));
|
||||
|
||||
// Inserts jump back at the end of the code cave to resume execution after patching
|
||||
u8 jumpBack[5];
|
||||
jumpBack[0] = 0xE9;
|
||||
// Calculates the relative offset to return to the instruction immediately following the
|
||||
// overwritten region
|
||||
s32 target_return = static_cast<s32>((patchAddress + jumpSize) - (code_cave_end + 5));
|
||||
std::memcpy(&jumpBack[1], &target_return, sizeof(target_return));
|
||||
std::memcpy(reinterpret_cast<void*>(code_cave_end), jumpBack, sizeof(jumpBack));
|
||||
|
||||
LOG_INFO(Loader,
|
||||
"Applied Patch mask_jump32: {}, PatchAddress: {:#x}, JumpTarget: {:#x}, "
|
||||
"CodeCaveEnd: {:#x}, JumpSize: {}",
|
||||
modNameStr, patchAddress, jump_target, code_cave_end, jumpSize);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cheatAddress == nullptr) {
|
||||
LOG_ERROR(Loader, "Failed to get address for patch {}", modNameStr);
|
||||
@@ -443,4 +532,4 @@ uintptr_t PatternScan(const std::string& signature) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace MemoryPatcher
|
||||
} // namespace MemoryPatcher
|
||||
|
||||
@@ -38,8 +38,9 @@ void OnGameLoaded();
|
||||
void AddPatchToQueue(patchInfo patchToAdd);
|
||||
void ApplyPendingPatches();
|
||||
|
||||
void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr, bool isOffset,
|
||||
bool littleEndian, PatchMask patchMask = PatchMask::None, int maskOffset = 0);
|
||||
void PatchMemory(std::string modNameStr, std::string offsetStr, std::string valueStr,
|
||||
std::string targetStr, std::string sizeStr, bool isOffset, bool littleEndian,
|
||||
PatchMask patchMask = PatchMask::None, int maskOffset = 0);
|
||||
|
||||
static std::vector<int32_t> PatternToByte(const std::string& pattern);
|
||||
uintptr_t PatternScan(const std::string& signature);
|
||||
|
||||
@@ -4,11 +4,6 @@
|
||||
#include "common/native_clock.h"
|
||||
#include "common/rdtsc.h"
|
||||
#include "common/uint128.h"
|
||||
#ifdef _WIN64
|
||||
#include <pthread_time.h>
|
||||
#else
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
namespace Common {
|
||||
|
||||
@@ -34,10 +29,4 @@ u64 NativeClock::GetUptime() const {
|
||||
return FencedRDTSC();
|
||||
}
|
||||
|
||||
u64 NativeClock::GetProcessTimeUS() const {
|
||||
timespec ret;
|
||||
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ret);
|
||||
return ret.tv_nsec / 1000 + ret.tv_sec * 1000000;
|
||||
}
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -20,7 +20,6 @@ public:
|
||||
u64 GetTimeUS(u64 base_ptc = 0) const;
|
||||
u64 GetTimeMS(u64 base_ptc = 0) const;
|
||||
u64 GetUptime() const;
|
||||
u64 GetProcessTimeUS() const;
|
||||
|
||||
private:
|
||||
u64 rdtsc_frequency;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include <unordered_map>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
@@ -16,6 +17,8 @@
|
||||
#ifdef _WIN32
|
||||
// This is the maximum number of UTF-16 code units permissible in Windows file paths
|
||||
#define MAX_PATH 260
|
||||
#include <Shlobj.h>
|
||||
#include <windows.h>
|
||||
#else
|
||||
// This is the maximum number of UTF-8 code units permissible in all other OSes' file paths
|
||||
#define MAX_PATH 1024
|
||||
@@ -105,6 +108,10 @@ static auto UserPaths = [] {
|
||||
} else {
|
||||
user_dir = std::filesystem::path(getenv("HOME")) / ".local" / "share" / "shadPS4";
|
||||
}
|
||||
#elif _WIN32
|
||||
TCHAR appdata[MAX_PATH] = {0};
|
||||
SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, appdata);
|
||||
user_dir = std::filesystem::path(appdata) / "shadPS4";
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -128,6 +135,22 @@ static auto UserPaths = [] {
|
||||
create_path(PathType::CheatsDir, user_dir / CHEATS_DIR);
|
||||
create_path(PathType::PatchesDir, user_dir / PATCHES_DIR);
|
||||
create_path(PathType::MetaDataDir, user_dir / METADATA_DIR);
|
||||
create_path(PathType::CustomTrophy, user_dir / CUSTOM_TROPHY);
|
||||
|
||||
std::ofstream notice_file(user_dir / CUSTOM_TROPHY / "Notice.txt");
|
||||
if (notice_file.is_open()) {
|
||||
notice_file
|
||||
<< "++++++++++++++++++++++++++++++++\n+ Custom Trophy Images / Sound "
|
||||
"+\n++++++++++++++++++++++++++++++++\n\nYou can add custom images to the "
|
||||
"trophies.\n*We recommend a square resolution image, for example 200x200, 500x500, "
|
||||
"the same size as the height and width.\nIn this folder ('user\\custom_trophy'), "
|
||||
"add the files with the following "
|
||||
"names:\n\nbronze.png\nsilver.png\ngold.png\nplatinum.png\n\nYou can add a custom "
|
||||
"sound for trophy notifications.\n*By default, no audio is played unless it is in "
|
||||
"this folder and you are using the QT version.\nIn this folder "
|
||||
"('user\\custom_trophy'), add the files with the following names:\n\ntrophy.mp3";
|
||||
notice_file.close();
|
||||
}
|
||||
|
||||
return paths;
|
||||
}();
|
||||
@@ -176,6 +199,34 @@ void SetUserPath(PathType shad_path, const fs::path& new_path) {
|
||||
UserPaths.insert_or_assign(shad_path, new_path);
|
||||
}
|
||||
|
||||
std::optional<fs::path> FindGameByID(const fs::path& dir, const std::string& game_id,
|
||||
int max_depth) {
|
||||
if (max_depth < 0) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// Check if this is the game we're looking for
|
||||
if (dir.filename() == game_id && fs::exists(dir / "sce_sys" / "param.sfo")) {
|
||||
auto eboot_path = dir / "eboot.bin";
|
||||
if (fs::exists(eboot_path)) {
|
||||
return eboot_path;
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively search subdirectories
|
||||
std::error_code ec;
|
||||
for (const auto& entry : fs::directory_iterator(dir, ec)) {
|
||||
if (!entry.is_directory()) {
|
||||
continue;
|
||||
}
|
||||
if (auto found = FindGameByID(entry.path(), game_id, max_depth - 1)) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
void PathToQString(QString& result, const std::filesystem::path& path) {
|
||||
#ifdef _WIN32
|
||||
@@ -194,4 +245,4 @@ std::filesystem::path PathFromQString(const QString& path) {
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace Common::FS
|
||||
} // namespace Common::FS
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#ifdef ENABLE_QT_GUI
|
||||
@@ -26,6 +27,7 @@ enum class PathType {
|
||||
CheatsDir, // Where cheats are stored.
|
||||
PatchesDir, // Where patches are stored.
|
||||
MetaDataDir, // Where game metadata (e.g. trophies and menu backgrounds) is stored.
|
||||
CustomTrophy, // Where custom files for trophies are stored.
|
||||
};
|
||||
|
||||
constexpr auto PORTABLE_DIR = "user";
|
||||
@@ -43,6 +45,7 @@ constexpr auto CAPTURES_DIR = "captures";
|
||||
constexpr auto CHEATS_DIR = "cheats";
|
||||
constexpr auto PATCHES_DIR = "patches";
|
||||
constexpr auto METADATA_DIR = "game_data";
|
||||
constexpr auto CUSTOM_TROPHY = "custom_trophy";
|
||||
|
||||
// Filenames
|
||||
constexpr auto LOG_FILE = "shad_log.txt";
|
||||
@@ -115,4 +118,18 @@ void PathToQString(QString& result, const std::filesystem::path& path);
|
||||
[[nodiscard]] std::filesystem::path PathFromQString(const QString& path);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Recursively searches for a game directory by its ID.
|
||||
* Limits search depth to prevent excessive filesystem traversal.
|
||||
*
|
||||
* @param dir Base directory to start the search from
|
||||
* @param game_id The game ID to search for
|
||||
* @param max_depth Maximum directory depth to search
|
||||
*
|
||||
* @returns Path to eboot.bin if found, std::nullopt otherwise
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::filesystem::path> FindGameByID(const std::filesystem::path& dir,
|
||||
const std::string& game_id,
|
||||
int max_depth);
|
||||
|
||||
} // namespace Common::FS
|
||||
|
||||
@@ -339,7 +339,9 @@ void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token,
|
||||
}
|
||||
|
||||
std::stop_callback callback(token, [&] {
|
||||
{ std::scoped_lock lk2{*lk.mutex()}; }
|
||||
{
|
||||
std::scoped_lock lk2{*lk.mutex()};
|
||||
}
|
||||
cv.notify_all();
|
||||
});
|
||||
|
||||
|
||||
@@ -6,14 +6,18 @@
|
||||
#define GIT_REV "@GIT_REV@"
|
||||
#define GIT_BRANCH "@GIT_BRANCH@"
|
||||
#define GIT_DESC "@GIT_DESC@"
|
||||
#define GIT_REMOTE_NAME "@GIT_REMOTE_NAME@"
|
||||
#define GIT_REMOTE_URL "@GIT_REMOTE_URL@"
|
||||
#define BUILD_DATE "@BUILD_DATE@"
|
||||
|
||||
namespace Common {
|
||||
|
||||
const char g_scm_rev[] = GIT_REV;
|
||||
const char g_scm_branch[] = GIT_BRANCH;
|
||||
const char g_scm_desc[] = GIT_DESC;
|
||||
const char g_scm_date[] = BUILD_DATE;
|
||||
const char g_scm_rev[] = GIT_REV;
|
||||
const char g_scm_branch[] = GIT_BRANCH;
|
||||
const char g_scm_desc[] = GIT_DESC;
|
||||
const char g_scm_remote_name[] = GIT_REMOTE_NAME;
|
||||
const char g_scm_remote_url[] = GIT_REMOTE_URL;
|
||||
const char g_scm_date[] = BUILD_DATE;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Common {
|
||||
extern const char g_scm_rev[];
|
||||
extern const char g_scm_branch[];
|
||||
extern const char g_scm_desc[];
|
||||
extern const char g_scm_remote_name[];
|
||||
extern const char g_scm_remote_url[];
|
||||
extern const char g_scm_date[];
|
||||
|
||||
} // namespace Common
|
||||
|
||||
15
src/common/string_literal.h
Normal file
15
src/common/string_literal.h
Normal file
@@ -0,0 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
template <size_t N, typename C = char>
|
||||
struct StringLiteral {
|
||||
static constexpr size_t len = N;
|
||||
|
||||
constexpr StringLiteral(const C (&str)[N]) {
|
||||
std::copy_n(str, N, value);
|
||||
}
|
||||
|
||||
C value[N]{};
|
||||
};
|
||||
@@ -41,7 +41,7 @@ void SetCurrentThreadRealtime(const std::chrono::nanoseconds period_ns) {
|
||||
const std::chrono::nanoseconds computation_ns = period_ns / 2;
|
||||
|
||||
// Determine the timebase for converting time to ticks.
|
||||
struct mach_timebase_info timebase {};
|
||||
struct mach_timebase_info timebase{};
|
||||
mach_timebase_info(&timebase);
|
||||
const auto ticks_per_ns =
|
||||
static_cast<double>(timebase.denom) / static_cast<double>(timebase.numer);
|
||||
|
||||
@@ -37,6 +37,10 @@ public:
|
||||
void Start();
|
||||
|
||||
void End();
|
||||
|
||||
std::chrono::nanoseconds GetTotalWait() const {
|
||||
return total_wait;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
namespace Common {
|
||||
|
||||
constexpr char VERSION[] = "0.5.0";
|
||||
constexpr char VERSION[] = "0.7.0";
|
||||
constexpr bool isRelease = true;
|
||||
|
||||
} // namespace Common
|
||||
|
||||
@@ -21,12 +21,13 @@
|
||||
|
||||
#if defined(__APPLE__) && defined(ARCH_X86_64)
|
||||
// Reserve space for the system address space using a zerofill section.
|
||||
asm(".zerofill GUEST_SYSTEM,GUEST_SYSTEM,__guest_system,0xFBFC00000");
|
||||
asm(".zerofill SYSTEM_MANAGED,SYSTEM_MANAGED,__SYSTEM_MANAGED,0x7FFBFC000");
|
||||
asm(".zerofill SYSTEM_RESERVED,SYSTEM_RESERVED,__SYSTEM_RESERVED,0x7C0004000");
|
||||
#endif
|
||||
|
||||
namespace Core {
|
||||
|
||||
static constexpr size_t BackingSize = SCE_KERNEL_MAIN_DMEM_SIZE_PRO;
|
||||
static constexpr size_t BackingSize = SCE_KERNEL_TOTAL_MEM_PRO;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -67,28 +68,25 @@ struct AddressSpace::Impl {
|
||||
static constexpr size_t ReductionOnFail = 1_GB;
|
||||
static constexpr size_t MaxReductions = 10;
|
||||
|
||||
size_t reduction = 0;
|
||||
size_t virtual_size = SystemManagedSize + SystemReservedSize + UserSize;
|
||||
for (u32 i = 0; i < MaxReductions; i++) {
|
||||
virtual_base = static_cast<u8*>(VirtualAlloc2(process, NULL, virtual_size - reduction,
|
||||
virtual_base = static_cast<u8*>(VirtualAlloc2(process, NULL, virtual_size,
|
||||
MEM_RESERVE | MEM_RESERVE_PLACEHOLDER,
|
||||
PAGE_NOACCESS, ¶m, 1));
|
||||
if (virtual_base) {
|
||||
break;
|
||||
}
|
||||
reduction += ReductionOnFail;
|
||||
virtual_size -= ReductionOnFail;
|
||||
}
|
||||
ASSERT_MSG(virtual_base, "Unable to reserve virtual address space: {}",
|
||||
Common::GetLastErrorMsg());
|
||||
|
||||
// Take the reduction off of the system managed area, and leave the others unchanged.
|
||||
reduction = size_t(virtual_base - SYSTEM_MANAGED_MIN);
|
||||
system_managed_base = virtual_base;
|
||||
system_managed_size = SystemManagedSize - reduction;
|
||||
system_reserved_base = reinterpret_cast<u8*>(SYSTEM_RESERVED_MIN);
|
||||
system_reserved_size = SystemReservedSize;
|
||||
system_managed_base = virtual_base;
|
||||
system_managed_size = system_reserved_base - virtual_base;
|
||||
user_base = reinterpret_cast<u8*>(USER_MIN);
|
||||
user_size = UserSize;
|
||||
user_size = virtual_base + virtual_size - user_base;
|
||||
|
||||
LOG_INFO(Kernel_Vmm, "System managed virtual memory region: {} - {}",
|
||||
fmt::ptr(system_managed_base),
|
||||
@@ -101,10 +99,8 @@ struct AddressSpace::Impl {
|
||||
|
||||
// Initializer placeholder tracker
|
||||
const uintptr_t system_managed_addr = reinterpret_cast<uintptr_t>(system_managed_base);
|
||||
const uintptr_t system_reserved_addr = reinterpret_cast<uintptr_t>(system_reserved_base);
|
||||
const uintptr_t user_addr = reinterpret_cast<uintptr_t>(user_base);
|
||||
regions.emplace(system_managed_addr,
|
||||
MemoryRegion{system_managed_addr, virtual_size - reduction, false});
|
||||
MemoryRegion{system_managed_addr, virtual_size, false});
|
||||
|
||||
// Allocate backing file that represents the total physical memory.
|
||||
backing_handle =
|
||||
|
||||
@@ -30,16 +30,6 @@
|
||||
|
||||
using namespace Xbyak::util;
|
||||
|
||||
#define MAYBE_AVX(OPCODE, ...) \
|
||||
[&] { \
|
||||
Cpu cpu; \
|
||||
if (cpu.has(Cpu::tAVX)) { \
|
||||
c.v##OPCODE(__VA_ARGS__); \
|
||||
} else { \
|
||||
c.OPCODE(__VA_ARGS__); \
|
||||
} \
|
||||
}()
|
||||
|
||||
namespace Core {
|
||||
|
||||
static Xbyak::Reg ZydisToXbyakRegister(const ZydisRegister reg) {
|
||||
@@ -190,28 +180,44 @@ static void RestoreStack(Xbyak::CodeGenerator& c) {
|
||||
c.mov(rsp, qword[reinterpret_cast<void*>(stack_pointer_slot * sizeof(void*))]);
|
||||
}
|
||||
|
||||
/// Validates that the dst register is supported given the SaveStack/RestoreStack implementation.
|
||||
static void ValidateDst(const Xbyak::Reg& dst) {
|
||||
// No restrictions.
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// These utilities are not implemented as we can't save anything to thread local storage without
|
||||
// temporary registers.
|
||||
void InitializeThreadPatchStack() {
|
||||
// No-op
|
||||
}
|
||||
|
||||
// NOTE: Since stack pointer here is subtracted through safe zone and not saved anywhere,
|
||||
// it must not be modified during the instruction. Otherwise, we will not be able to find
|
||||
// and load registers back from where they were saved. Thus, a limitation is placed on
|
||||
// instructions, that they must not use the stack pointer register as a destination.
|
||||
|
||||
/// Saves the stack pointer to thread local storage and loads the patch stack.
|
||||
static void SaveStack(Xbyak::CodeGenerator& c) {
|
||||
UNIMPLEMENTED();
|
||||
c.lea(rsp, ptr[rsp - 128]); // red zone
|
||||
}
|
||||
|
||||
/// Restores the stack pointer from thread local storage.
|
||||
static void RestoreStack(Xbyak::CodeGenerator& c) {
|
||||
UNIMPLEMENTED();
|
||||
c.lea(rsp, ptr[rsp + 128]); // red zone
|
||||
}
|
||||
|
||||
/// Validates that the dst register is supported given the SaveStack/RestoreStack implementation.
|
||||
static void ValidateDst(const Xbyak::Reg& dst) {
|
||||
// Stack pointer is not preserved, so it can't be used as a dst.
|
||||
ASSERT_MSG(dst.getIdx() != rsp.getIdx(), "Stack pointer not supported as destination.");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// Switches to the patch stack, saves registers, and restores the original stack.
|
||||
static void SaveRegisters(Xbyak::CodeGenerator& c, const std::initializer_list<Xbyak::Reg> regs) {
|
||||
// Uses a more robust solution for saving registers on MacOS to avoid potential stack corruption
|
||||
// if games decide to not follow the ABI and use the red zone.
|
||||
SaveStack(c);
|
||||
for (const auto& reg : regs) {
|
||||
c.push(reg.cvt64());
|
||||
@@ -235,9 +241,9 @@ static void SaveContext(Xbyak::CodeGenerator& c, bool save_flags = false) {
|
||||
for (int reg = Xbyak::Operand::RAX; reg <= Xbyak::Operand::R15; reg++) {
|
||||
c.push(Xbyak::Reg64(reg));
|
||||
}
|
||||
for (int reg = 0; reg <= 7; reg++) {
|
||||
c.lea(rsp, ptr[rsp - 32]);
|
||||
c.vmovdqu(ptr[rsp], Xbyak::Ymm(reg));
|
||||
c.lea(rsp, ptr[rsp - 32 * 16]);
|
||||
for (int reg = 0; reg <= 15; reg++) {
|
||||
c.vmovdqu(ptr[rsp + 32 * reg], Xbyak::Ymm(reg));
|
||||
}
|
||||
if (save_flags) {
|
||||
c.pushfq();
|
||||
@@ -251,12 +257,12 @@ static void RestoreContext(Xbyak::CodeGenerator& c, const Xbyak::Operand& dst,
|
||||
if (restore_flags) {
|
||||
c.popfq();
|
||||
}
|
||||
for (int reg = 7; reg >= 0; reg--) {
|
||||
for (int reg = 15; reg >= 0; reg--) {
|
||||
if ((!dst.isXMM() && !dst.isYMM()) || dst.getIdx() != reg) {
|
||||
c.vmovdqu(Xbyak::Ymm(reg), ptr[rsp]);
|
||||
c.vmovdqu(Xbyak::Ymm(reg), ptr[rsp + 32 * reg]);
|
||||
}
|
||||
c.lea(rsp, ptr[rsp + 32]);
|
||||
}
|
||||
c.lea(rsp, ptr[rsp + 32 * 16]);
|
||||
for (int reg = Xbyak::Operand::R15; reg >= Xbyak::Operand::RAX; reg--) {
|
||||
if (!dst.isREG() || dst.getIdx() != reg) {
|
||||
c.pop(Xbyak::Reg64(reg));
|
||||
@@ -267,29 +273,50 @@ static void RestoreContext(Xbyak::CodeGenerator& c, const Xbyak::Operand& dst,
|
||||
RestoreStack(c);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
static void GenerateANDN(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src1 = ZydisToXbyakRegisterOperand(operands[1]);
|
||||
const auto src2 = ZydisToXbyakOperand(operands[2]);
|
||||
ValidateDst(dst);
|
||||
|
||||
const auto scratch = AllocateScratchRegister({&dst, &src1, src2.get()}, dst.getBit());
|
||||
// Check if src2 is a memory operand or a register different to dst.
|
||||
// In those cases, we don't need to use a temporary register and are free to modify dst.
|
||||
// In cases where dst and src2 are the same register, a temporary needs to be used to avoid
|
||||
// modifying src2.
|
||||
bool src2_uses_dst = false;
|
||||
if (src2->isMEM()) {
|
||||
const auto base = src2->getAddress().getRegExp().getBase().getIdx();
|
||||
const auto index = src2->getAddress().getRegExp().getIndex().getIdx();
|
||||
src2_uses_dst = base == dst.getIdx() || index == dst.getIdx();
|
||||
} else {
|
||||
ASSERT(src2->isREG());
|
||||
src2_uses_dst = src2->getReg() == dst;
|
||||
}
|
||||
|
||||
SaveRegisters(c, {scratch});
|
||||
if (!src2_uses_dst) {
|
||||
if (dst != src1)
|
||||
c.mov(dst, src1);
|
||||
c.not_(dst);
|
||||
c.and_(dst, *src2);
|
||||
} else {
|
||||
const auto scratch = AllocateScratchRegister({&dst, &src1, src2.get()}, dst.getBit());
|
||||
|
||||
c.mov(scratch, src1);
|
||||
c.not_(scratch);
|
||||
c.and_(scratch, *src2);
|
||||
c.mov(dst, scratch);
|
||||
SaveRegisters(c, {scratch});
|
||||
|
||||
RestoreRegisters(c, {scratch});
|
||||
c.mov(scratch, src1);
|
||||
c.not_(scratch);
|
||||
c.and_(scratch, *src2);
|
||||
c.mov(dst, scratch);
|
||||
|
||||
RestoreRegisters(c, {scratch});
|
||||
}
|
||||
}
|
||||
|
||||
static void GenerateBEXTR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||
const auto start_len = ZydisToXbyakRegisterOperand(operands[2]);
|
||||
ValidateDst(dst);
|
||||
|
||||
const Xbyak::Reg32e shift(Xbyak::Operand::RCX, static_cast<int>(start_len.getBit()));
|
||||
const auto scratch1 =
|
||||
@@ -327,6 +354,7 @@ static void GenerateBEXTR(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
|
||||
static void GenerateBLSI(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||
ValidateDst(dst);
|
||||
|
||||
const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
|
||||
|
||||
@@ -356,6 +384,7 @@ static void GenerateBLSI(const ZydisDecodedOperand* operands, Xbyak::CodeGenerat
|
||||
static void GenerateBLSMSK(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||
ValidateDst(dst);
|
||||
|
||||
const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
|
||||
|
||||
@@ -384,9 +413,37 @@ static void GenerateBLSMSK(const ZydisDecodedOperand* operands, Xbyak::CodeGener
|
||||
RestoreRegisters(c, {scratch});
|
||||
}
|
||||
|
||||
static void GenerateTZCNT(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||
ValidateDst(dst);
|
||||
|
||||
Xbyak::Label src_zero, end;
|
||||
|
||||
c.cmp(*src, 0);
|
||||
c.je(src_zero);
|
||||
|
||||
// If src is not zero, functions like a BSF, but also clears the CF
|
||||
c.bsf(dst, *src);
|
||||
c.clc();
|
||||
c.jmp(end);
|
||||
|
||||
c.L(src_zero);
|
||||
c.mov(dst, operands[0].size);
|
||||
// Since dst is not zero, also set ZF to zero. Testing dst with itself when we know
|
||||
// it isn't zero is a good way to do this.
|
||||
// Use cvt32 to avoid REX/Operand size prefixes.
|
||||
c.test(dst.cvt32(), dst.cvt32());
|
||||
// When source is zero, TZCNT also sets CF.
|
||||
c.stc();
|
||||
|
||||
c.L(end);
|
||||
}
|
||||
|
||||
static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
const auto dst = ZydisToXbyakRegisterOperand(operands[0]);
|
||||
const auto src = ZydisToXbyakOperand(operands[1]);
|
||||
ValidateDst(dst);
|
||||
|
||||
const auto scratch = AllocateScratchRegister({&dst, src.get()}, dst.getBit());
|
||||
|
||||
@@ -415,6 +472,8 @@ static void GenerateBLSR(const ZydisDecodedOperand* operands, Xbyak::CodeGenerat
|
||||
RestoreRegisters(c, {scratch});
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
|
||||
static __attribute__((sysv_abi)) void PerformVCVTPH2PS(float* out, const half_float::half* in,
|
||||
const u32 count) {
|
||||
for (u32 i = 0; i < count; i++) {
|
||||
@@ -605,6 +664,11 @@ static bool FilterNoSSE4a(const ZydisDecodedOperand*) {
|
||||
return !cpu.has(Cpu::tSSE4a);
|
||||
}
|
||||
|
||||
static bool FilterNoBMI1(const ZydisDecodedOperand*) {
|
||||
Cpu cpu;
|
||||
return !cpu.has(Cpu::tBMI1);
|
||||
}
|
||||
|
||||
static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenerator& c) {
|
||||
bool immediateForm = operands[1].type == ZYDIS_OPERAND_TYPE_IMMEDIATE &&
|
||||
operands[2].type == ZYDIS_OPERAND_TYPE_IMMEDIATE;
|
||||
@@ -643,7 +707,7 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
|
||||
ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64.");
|
||||
|
||||
// Get lower qword from xmm register
|
||||
MAYBE_AVX(movq, scratch1, xmm_dst);
|
||||
c.vmovq(scratch1, xmm_dst);
|
||||
|
||||
if (index != 0) {
|
||||
c.shr(scratch1, index);
|
||||
@@ -656,7 +720,7 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
|
||||
|
||||
// Writeback to xmm register, extrq instruction says top 64-bits are undefined so we don't
|
||||
// care to preserve them
|
||||
MAYBE_AVX(movq, xmm_dst, scratch1);
|
||||
c.vmovq(xmm_dst, scratch1);
|
||||
|
||||
c.pop(scratch2);
|
||||
c.pop(scratch1);
|
||||
@@ -690,7 +754,7 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
|
||||
c.push(mask);
|
||||
|
||||
// Construct the mask out of the length that resides in bottom 6 bits of source xmm
|
||||
MAYBE_AVX(movq, scratch1, xmm_src);
|
||||
c.vmovq(scratch1, xmm_src);
|
||||
c.mov(scratch2, scratch1);
|
||||
c.and_(scratch2, 0x3F);
|
||||
c.jz(length_zero);
|
||||
@@ -711,10 +775,10 @@ static void GenerateEXTRQ(const ZydisDecodedOperand* operands, Xbyak::CodeGenera
|
||||
c.and_(scratch1, 0x3F);
|
||||
c.mov(scratch2, scratch1); // cl now contains the shift amount
|
||||
|
||||
MAYBE_AVX(movq, scratch1, xmm_dst);
|
||||
c.vmovq(scratch1, xmm_dst);
|
||||
c.shr(scratch1, cl);
|
||||
c.and_(scratch1, mask);
|
||||
MAYBE_AVX(movq, xmm_dst, scratch1);
|
||||
c.vmovq(xmm_dst, scratch1);
|
||||
|
||||
c.pop(mask);
|
||||
c.pop(scratch2);
|
||||
@@ -765,8 +829,8 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
|
||||
|
||||
ASSERT_MSG(length + index <= 64, "length + index must be less than or equal to 64.");
|
||||
|
||||
MAYBE_AVX(movq, scratch1, xmm_src);
|
||||
MAYBE_AVX(movq, scratch2, xmm_dst);
|
||||
c.vmovq(scratch1, xmm_src);
|
||||
c.vmovq(scratch2, xmm_dst);
|
||||
c.mov(mask, mask_value);
|
||||
|
||||
// src &= mask
|
||||
@@ -784,12 +848,7 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
|
||||
c.or_(scratch2, scratch1);
|
||||
|
||||
// Insert scratch2 into low 64 bits of dst, upper 64 bits are unaffected
|
||||
Cpu cpu;
|
||||
if (cpu.has(Cpu::tAVX)) {
|
||||
c.vpinsrq(xmm_dst, xmm_dst, scratch2, 0);
|
||||
} else {
|
||||
c.pinsrq(xmm_dst, scratch2, 0);
|
||||
}
|
||||
c.vpinsrq(xmm_dst, xmm_dst, scratch2, 0);
|
||||
|
||||
c.pop(mask);
|
||||
c.pop(scratch2);
|
||||
@@ -816,7 +875,7 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
|
||||
c.push(mask);
|
||||
|
||||
// Get upper 64 bits of src and copy it to mask and index
|
||||
MAYBE_AVX(pextrq, index, xmm_src, 1);
|
||||
c.vpextrq(index, xmm_src, 1);
|
||||
c.mov(mask, index);
|
||||
|
||||
// When length is 0, set it to 64
|
||||
@@ -839,7 +898,7 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
|
||||
c.and_(index, 0x3F);
|
||||
|
||||
// src &= mask
|
||||
MAYBE_AVX(movq, scratch1, xmm_src);
|
||||
c.vmovq(scratch1, xmm_src);
|
||||
c.and_(scratch1, mask);
|
||||
|
||||
// mask = ~(mask << index)
|
||||
@@ -851,12 +910,12 @@ static void GenerateINSERTQ(const ZydisDecodedOperand* operands, Xbyak::CodeGene
|
||||
c.shl(scratch1, cl);
|
||||
|
||||
// dst = (dst & mask) | src
|
||||
MAYBE_AVX(movq, scratch2, xmm_dst);
|
||||
c.vmovq(scratch2, xmm_dst);
|
||||
c.and_(scratch2, mask);
|
||||
c.or_(scratch2, scratch1);
|
||||
|
||||
// Upper 64 bits are undefined in insertq
|
||||
MAYBE_AVX(movq, xmm_dst, scratch2);
|
||||
c.vmovq(xmm_dst, scratch2);
|
||||
|
||||
c.pop(mask);
|
||||
c.pop(index);
|
||||
@@ -891,14 +950,16 @@ static const std::unordered_map<ZydisMnemonic, PatchInfo> Patches = {
|
||||
{ZYDIS_MNEMONIC_EXTRQ, {FilterNoSSE4a, GenerateEXTRQ, true}},
|
||||
{ZYDIS_MNEMONIC_INSERTQ, {FilterNoSSE4a, GenerateINSERTQ, true}},
|
||||
|
||||
// BMI1
|
||||
{ZYDIS_MNEMONIC_ANDN, {FilterNoBMI1, GenerateANDN, true}},
|
||||
{ZYDIS_MNEMONIC_BEXTR, {FilterNoBMI1, GenerateBEXTR, true}},
|
||||
{ZYDIS_MNEMONIC_BLSI, {FilterNoBMI1, GenerateBLSI, true}},
|
||||
{ZYDIS_MNEMONIC_BLSMSK, {FilterNoBMI1, GenerateBLSMSK, true}},
|
||||
{ZYDIS_MNEMONIC_BLSR, {FilterNoBMI1, GenerateBLSR, true}},
|
||||
{ZYDIS_MNEMONIC_TZCNT, {FilterNoBMI1, GenerateTZCNT, true}},
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Patches for instruction sets not supported by Rosetta 2.
|
||||
// BMI1
|
||||
{ZYDIS_MNEMONIC_ANDN, {FilterRosetta2Only, GenerateANDN, true}},
|
||||
{ZYDIS_MNEMONIC_BEXTR, {FilterRosetta2Only, GenerateBEXTR, true}},
|
||||
{ZYDIS_MNEMONIC_BLSI, {FilterRosetta2Only, GenerateBLSI, true}},
|
||||
{ZYDIS_MNEMONIC_BLSMSK, {FilterRosetta2Only, GenerateBLSMSK, true}},
|
||||
{ZYDIS_MNEMONIC_BLSR, {FilterRosetta2Only, GenerateBLSR, true}},
|
||||
// F16C
|
||||
{ZYDIS_MNEMONIC_VCVTPH2PS, {FilterRosetta2Only, GenerateVCVTPH2PS, true}},
|
||||
{ZYDIS_MNEMONIC_VCVTPS2PH, {FilterRosetta2Only, GenerateVCVTPS2PH, true}},
|
||||
@@ -1056,10 +1117,10 @@ static bool TryExecuteIllegalInstruction(void* ctx, void* code_address) {
|
||||
if (length + index > 64) {
|
||||
// Undefined behavior if length + index is bigger than 64 according to the spec,
|
||||
// we'll warn and continue execution.
|
||||
LOG_WARNING(Core,
|
||||
"extrq at {} with length {} and index {} is bigger than 64, "
|
||||
"undefined behavior",
|
||||
fmt::ptr(code_address), length, index);
|
||||
LOG_TRACE(Core,
|
||||
"extrq at {} with length {} and index {} is bigger than 64, "
|
||||
"undefined behavior",
|
||||
fmt::ptr(code_address), length, index);
|
||||
}
|
||||
|
||||
lowQWordDst >>= index;
|
||||
@@ -1116,10 +1177,10 @@ static bool TryExecuteIllegalInstruction(void* ctx, void* code_address) {
|
||||
if (length + index > 64) {
|
||||
// Undefined behavior if length + index is bigger than 64 according to the spec,
|
||||
// we'll warn and continue execution.
|
||||
LOG_WARNING(Core,
|
||||
"insertq at {} with length {} and index {} is bigger than 64, "
|
||||
"undefined behavior",
|
||||
fmt::ptr(code_address), length, index);
|
||||
LOG_TRACE(Core,
|
||||
"insertq at {} with length {} and index {} is bigger than 64, "
|
||||
"undefined behavior",
|
||||
fmt::ptr(code_address), length, index);
|
||||
}
|
||||
|
||||
lowQWordSrc &= mask;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
CryptoPP::RSA::PrivateKey Crypto::key_pkg_derived_key3_keyset_init() {
|
||||
@@ -137,20 +138,20 @@ void Crypto::aesCbcCfb128DecryptEntry(std::span<const CryptoPP::byte, 32> ivkey,
|
||||
}
|
||||
}
|
||||
|
||||
void Crypto::decryptEFSM(std::span<CryptoPP::byte, 16> NPcommID,
|
||||
void Crypto::decryptEFSM(std::span<CryptoPP::byte, 16> trophyKey,
|
||||
std::span<CryptoPP::byte, 16> NPcommID,
|
||||
std::span<CryptoPP::byte, 16> efsmIv, std::span<CryptoPP::byte> ciphertext,
|
||||
std::span<CryptoPP::byte> decrypted) {
|
||||
|
||||
std::vector<CryptoPP::byte> TrophyKey = {0x21, 0xF4, 0x1A, 0x6B, 0xAD, 0x8A, 0x1D, 0x3E,
|
||||
0xCA, 0x7A, 0xD5, 0x86, 0xC1, 0x01, 0xB7, 0xA9};
|
||||
std::vector<CryptoPP::byte> TrophyIV = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
// step 1: Encrypt NPcommID
|
||||
CryptoPP::CBC_Mode<CryptoPP::AES>::Encryption encrypt;
|
||||
encrypt.SetKeyWithIV(TrophyKey.data(), TrophyKey.size(), TrophyIV.data());
|
||||
|
||||
std::vector<CryptoPP::byte> trophyIv(16, 0);
|
||||
std::vector<CryptoPP::byte> trpKey(16);
|
||||
|
||||
encrypt.SetKeyWithIV(trophyKey.data(), trophyKey.size(), trophyIv.data());
|
||||
encrypt.ProcessData(trpKey.data(), NPcommID.data(), 16);
|
||||
|
||||
// step 2: decrypt efsm.
|
||||
CryptoPP::CBC_Mode<CryptoPP::AES>::Decryption decrypt;
|
||||
decrypt.SetKeyWithIV(trpKey.data(), trpKey.size(), efsmIv.data());
|
||||
|
||||
@@ -32,7 +32,8 @@ public:
|
||||
void aesCbcCfb128DecryptEntry(std::span<const CryptoPP::byte, 32> ivkey,
|
||||
std::span<CryptoPP::byte> ciphertext,
|
||||
std::span<CryptoPP::byte> decrypted);
|
||||
void decryptEFSM(std::span<CryptoPP::byte, 16>, std::span<CryptoPP::byte, 16> efsmIv,
|
||||
void decryptEFSM(std::span<CryptoPP::byte, 16> trophyKey,
|
||||
std::span<CryptoPP::byte, 16> NPcommID, std::span<CryptoPP::byte, 16> efsmIv,
|
||||
std::span<CryptoPP::byte> ciphertext, std::span<CryptoPP::byte> decrypted);
|
||||
void PfsGenCryptoKey(std::span<const CryptoPP::byte, 32> ekpfs,
|
||||
std::span<const CryptoPP::byte, 16> seed,
|
||||
|
||||
@@ -11,11 +11,14 @@
|
||||
#include "libraries/kernel/time.h"
|
||||
#include "libraries/system/msgdialog.h"
|
||||
#include "video_core/amdgpu/pm4_cmds.h"
|
||||
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
|
||||
|
||||
using namespace DebugStateType;
|
||||
|
||||
DebugStateImpl& DebugState = *Common::Singleton<DebugStateImpl>::Instance();
|
||||
|
||||
bool DebugStateType::showing_debug_menu_bar = false;
|
||||
|
||||
static ThreadID ThisThreadID() {
|
||||
#ifdef _WIN32
|
||||
return GetCurrentThreadId();
|
||||
@@ -168,8 +171,12 @@ void DebugStateImpl::PushRegsDump(uintptr_t base_addr, uintptr_t header_addr,
|
||||
if ((*dump)->regs.stage_enable.IsStageEnabled(i)) {
|
||||
auto stage = (*dump)->regs.ProgramForStage(i);
|
||||
if (stage->address_lo != 0) {
|
||||
const auto& info = AmdGpu::Liverpool::SearchBinaryInfo(stage->Address<u32*>());
|
||||
auto code = stage->Code();
|
||||
(*dump)->stages[i] = PipelineShaderProgramDump{
|
||||
.name = Vulkan::PipelineCache::GetShaderName(Shader::StageFromIndex(i),
|
||||
info.shader_hash),
|
||||
.hash = info.shader_hash,
|
||||
.user_data = *stage,
|
||||
.code = std::vector<u32>{code.begin(), code.end()},
|
||||
};
|
||||
@@ -191,7 +198,10 @@ void DebugStateImpl::PushRegsDumpCompute(uintptr_t base_addr, uintptr_t header_a
|
||||
auto& cs = (*dump)->regs.cs_program;
|
||||
cs = cs_state;
|
||||
|
||||
const auto& info = AmdGpu::Liverpool::SearchBinaryInfo(cs.Address<u32*>());
|
||||
(*dump)->cs_data = PipelineComputerProgramDump{
|
||||
.name = Vulkan::PipelineCache::GetShaderName(Shader::Stage::Compute, info.shader_hash),
|
||||
.hash = info.shader_hash,
|
||||
.cs_program = cs,
|
||||
.code = std::vector<u32>{cs.Code().begin(), cs.Code().end()},
|
||||
};
|
||||
|
||||
@@ -35,6 +35,8 @@ class ShaderList;
|
||||
|
||||
namespace DebugStateType {
|
||||
|
||||
extern bool showing_debug_menu_bar;
|
||||
|
||||
enum class QueueType {
|
||||
dcb = 0,
|
||||
ccb = 1,
|
||||
@@ -50,11 +52,15 @@ struct QueueDump {
|
||||
};
|
||||
|
||||
struct PipelineShaderProgramDump {
|
||||
std::string name;
|
||||
u64 hash;
|
||||
Vulkan::Liverpool::ShaderProgram user_data{};
|
||||
std::vector<u32> code{};
|
||||
};
|
||||
|
||||
struct PipelineComputerProgramDump {
|
||||
std::string name;
|
||||
u64 hash;
|
||||
Vulkan::Liverpool::ComputeProgram cs_program{};
|
||||
std::vector<u32> code{};
|
||||
};
|
||||
@@ -149,6 +155,13 @@ class DebugStateImpl {
|
||||
std::vector<ShaderDump> shader_dump_list{};
|
||||
|
||||
public:
|
||||
float Framerate = 1.0f / 60.0f;
|
||||
float FrameDeltaTime;
|
||||
|
||||
std::pair<u32, u32> game_resolution{};
|
||||
std::pair<u32, u32> output_resolution{};
|
||||
bool is_using_fsr{};
|
||||
|
||||
void ShowDebugMessage(std::string message) {
|
||||
if (message.empty()) {
|
||||
return;
|
||||
@@ -156,6 +169,10 @@ public:
|
||||
debug_message_popup.push(std::move(message));
|
||||
}
|
||||
|
||||
bool& IsShowingDebugMenuBar() {
|
||||
return showing_debug_menu_bar;
|
||||
}
|
||||
|
||||
void AddCurrentThreadToGuestList();
|
||||
|
||||
void RemoveCurrentThreadFromGuestList();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "base_device.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
74
src/core/devices/console_device.cpp
Normal file
74
src/core/devices/console_device.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "console_device.h"
|
||||
|
||||
namespace Core::Devices {
|
||||
|
||||
std::shared_ptr<BaseDevice> ConsoleDevice::Create(u32 handle, const char*, int, u16) {
|
||||
return std::shared_ptr<BaseDevice>(
|
||||
reinterpret_cast<Devices::BaseDevice*>(new ConsoleDevice(handle)));
|
||||
}
|
||||
|
||||
int ConsoleDevice::ioctl(u64 cmd, Common::VaCtx* args) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 ConsoleDevice::write(const void* buf, size_t nbytes) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t ConsoleDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t ConsoleDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 ConsoleDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 ConsoleDevice::lseek(s64 offset, int whence) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 ConsoleDevice::read(void* buf, size_t nbytes) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConsoleDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 ConsoleDevice::fsync() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConsoleDevice::ftruncate(s64 length) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConsoleDevice::getdents(void* buf, u32 nbytes, s64* basep) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 ConsoleDevice::pwrite(const void* buf, size_t nbytes, u64 offset) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Core::Devices
|
||||
33
src/core/devices/console_device.h
Normal file
33
src/core/devices/console_device.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "base_device.h"
|
||||
|
||||
namespace Core::Devices {
|
||||
|
||||
class ConsoleDevice final : BaseDevice {
|
||||
u32 handle;
|
||||
|
||||
public:
|
||||
static std::shared_ptr<BaseDevice> Create(u32 handle, const char*, int, u16);
|
||||
explicit ConsoleDevice(u32 handle) : handle(handle) {}
|
||||
|
||||
~ConsoleDevice() override = default;
|
||||
|
||||
int ioctl(u64 cmd, Common::VaCtx* args) override;
|
||||
s64 write(const void* buf, size_t nbytes) override;
|
||||
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
|
||||
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
|
||||
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override;
|
||||
s64 lseek(s64 offset, int whence) override;
|
||||
s64 read(void* buf, size_t nbytes) override;
|
||||
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
|
||||
s32 fsync() override;
|
||||
int ftruncate(s64 length) override;
|
||||
int getdents(void* buf, u32 nbytes, s64* basep) override;
|
||||
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
|
||||
};
|
||||
|
||||
} // namespace Core::Devices
|
||||
74
src/core/devices/deci_tty6_device.cpp
Normal file
74
src/core/devices/deci_tty6_device.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "deci_tty6_device.h"
|
||||
|
||||
namespace Core::Devices {
|
||||
|
||||
std::shared_ptr<BaseDevice> DeciTty6Device::Create(u32 handle, const char*, int, u16) {
|
||||
return std::shared_ptr<BaseDevice>(
|
||||
reinterpret_cast<Devices::BaseDevice*>(new DeciTty6Device(handle)));
|
||||
}
|
||||
|
||||
int DeciTty6Device::ioctl(u64 cmd, Common::VaCtx* args) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 DeciTty6Device::write(const void* buf, size_t nbytes) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t DeciTty6Device::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t DeciTty6Device::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 DeciTty6Device::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 DeciTty6Device::lseek(s64 offset, int whence) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 DeciTty6Device::read(void* buf, size_t nbytes) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DeciTty6Device::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 DeciTty6Device::fsync() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DeciTty6Device::ftruncate(s64 length) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DeciTty6Device::getdents(void* buf, u32 nbytes, s64* basep) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 DeciTty6Device::pwrite(const void* buf, size_t nbytes, u64 offset) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Core::Devices
|
||||
33
src/core/devices/deci_tty6_device.h
Normal file
33
src/core/devices/deci_tty6_device.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "base_device.h"
|
||||
|
||||
namespace Core::Devices {
|
||||
|
||||
class DeciTty6Device final : BaseDevice {
|
||||
u32 handle;
|
||||
|
||||
public:
|
||||
static std::shared_ptr<BaseDevice> Create(u32 handle, const char*, int, u16);
|
||||
explicit DeciTty6Device(u32 handle) : handle(handle) {}
|
||||
|
||||
~DeciTty6Device() override = default;
|
||||
|
||||
int ioctl(u64 cmd, Common::VaCtx* args) override;
|
||||
s64 write(const void* buf, size_t nbytes) override;
|
||||
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
|
||||
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
|
||||
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override;
|
||||
s64 lseek(s64 offset, int whence) override;
|
||||
s64 read(void* buf, size_t nbytes) override;
|
||||
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
|
||||
s32 fsync() override;
|
||||
int ftruncate(s64 length) override;
|
||||
int getdents(void* buf, u32 nbytes, s64* basep) override;
|
||||
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
|
||||
};
|
||||
|
||||
} // namespace Core::Devices
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/libraries/kernel/file_system.h"
|
||||
@@ -17,10 +17,12 @@ s64 Logger::write(const void* buf, size_t nbytes) {
|
||||
}
|
||||
|
||||
size_t Logger::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
|
||||
size_t total_written = 0;
|
||||
for (int i = 0; i < iovcnt; i++) {
|
||||
log(static_cast<const char*>(iov[i].iov_base), iov[i].iov_len);
|
||||
total_written += iov[i].iov_len;
|
||||
}
|
||||
return iovcnt;
|
||||
return total_written;
|
||||
}
|
||||
|
||||
s64 Logger::pwrite(const void* buf, size_t nbytes, u64 offset) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include "base_device.h"
|
||||
@@ -17,36 +17,47 @@ public:
|
||||
int ioctl(u64 cmd, Common::VaCtx* args) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 write(const void* buf, size_t nbytes) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 lseek(s64 offset, int whence) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 read(void* buf, size_t nbytes) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 fsync() override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ftruncate(s64 length) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int getdents(void* buf, u32 nbytes, s64* basep) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
78
src/core/devices/random_device.cpp
Normal file
78
src/core/devices/random_device.cpp
Normal file
@@ -0,0 +1,78 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstdlib>
|
||||
#include "common/logging/log.h"
|
||||
#include "random_device.h"
|
||||
|
||||
namespace Core::Devices {
|
||||
|
||||
std::shared_ptr<BaseDevice> RandomDevice::Create(u32 handle, const char*, int, u16) {
|
||||
std::srand(std::time(nullptr));
|
||||
return std::shared_ptr<BaseDevice>(
|
||||
reinterpret_cast<Devices::BaseDevice*>(new RandomDevice(handle)));
|
||||
}
|
||||
|
||||
int RandomDevice::ioctl(u64 cmd, Common::VaCtx* args) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 RandomDevice::write(const void* buf, size_t nbytes) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t RandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t RandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 RandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 RandomDevice::lseek(s64 offset, int whence) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 RandomDevice::read(void* buf, size_t nbytes) {
|
||||
auto rbuf = static_cast<char*>(buf);
|
||||
for (size_t i = 0; i < nbytes; i++) {
|
||||
rbuf[i] = std::rand() & 0xFF;
|
||||
}
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
int RandomDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 RandomDevice::fsync() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RandomDevice::ftruncate(s64 length) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RandomDevice::getdents(void* buf, u32 nbytes, s64* basep) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 RandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Core::Devices
|
||||
33
src/core/devices/random_device.h
Normal file
33
src/core/devices/random_device.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "base_device.h"
|
||||
|
||||
namespace Core::Devices {
|
||||
|
||||
class RandomDevice final : BaseDevice {
|
||||
u32 handle;
|
||||
|
||||
public:
|
||||
static std::shared_ptr<BaseDevice> Create(u32 handle, const char*, int, u16);
|
||||
explicit RandomDevice(u32 handle) : handle(handle) {}
|
||||
|
||||
~RandomDevice() override = default;
|
||||
|
||||
int ioctl(u64 cmd, Common::VaCtx* args) override;
|
||||
s64 write(const void* buf, size_t nbytes) override;
|
||||
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
|
||||
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
|
||||
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override;
|
||||
s64 lseek(s64 offset, int whence) override;
|
||||
s64 read(void* buf, size_t nbytes) override;
|
||||
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
|
||||
s32 fsync() override;
|
||||
int ftruncate(s64 length) override;
|
||||
int getdents(void* buf, u32 nbytes, s64* basep) override;
|
||||
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
|
||||
};
|
||||
|
||||
} // namespace Core::Devices
|
||||
79
src/core/devices/srandom_device.cpp
Normal file
79
src/core/devices/srandom_device.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstdlib>
|
||||
#include "common/logging/log.h"
|
||||
#include "srandom_device.h"
|
||||
|
||||
namespace Core::Devices {
|
||||
|
||||
std::shared_ptr<BaseDevice> SRandomDevice::Create(u32 handle, const char*, int, u16) {
|
||||
std::srand(std::time(nullptr));
|
||||
return std::shared_ptr<BaseDevice>(
|
||||
reinterpret_cast<Devices::BaseDevice*>(new SRandomDevice(handle)));
|
||||
}
|
||||
|
||||
int SRandomDevice::ioctl(u64 cmd, Common::VaCtx* args) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 SRandomDevice::write(const void* buf, size_t nbytes) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t SRandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t SRandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 SRandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 SRandomDevice::lseek(s64 offset, int whence) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 SRandomDevice::read(void* buf, size_t nbytes) {
|
||||
auto rbuf = static_cast<char*>(buf);
|
||||
for (size_t i = 0; i < nbytes; i++) {
|
||||
rbuf[i] = std::rand();
|
||||
}
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
int SRandomDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 SRandomDevice::fsync() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return s32();
|
||||
}
|
||||
|
||||
int SRandomDevice::ftruncate(s64 length) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int SRandomDevice::getdents(void* buf, u32 nbytes, s64* basep) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 SRandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Core::Devices
|
||||
33
src/core/devices/srandom_device.h
Normal file
33
src/core/devices/srandom_device.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "base_device.h"
|
||||
|
||||
namespace Core::Devices {
|
||||
|
||||
class SRandomDevice final : BaseDevice {
|
||||
u32 handle;
|
||||
|
||||
public:
|
||||
static std::shared_ptr<BaseDevice> Create(u32 handle, const char*, int, u16);
|
||||
explicit SRandomDevice(u32 handle) : handle(handle) {}
|
||||
|
||||
~SRandomDevice() override = default;
|
||||
|
||||
int ioctl(u64 cmd, Common::VaCtx* args) override;
|
||||
s64 write(const void* buf, size_t nbytes) override;
|
||||
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
|
||||
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
|
||||
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override;
|
||||
s64 lseek(s64 offset, int whence) override;
|
||||
s64 read(void* buf, size_t nbytes) override;
|
||||
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
|
||||
s32 fsync() override;
|
||||
int ftruncate(s64 length) override;
|
||||
int getdents(void* buf, u32 nbytes, s64* basep) override;
|
||||
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
|
||||
};
|
||||
|
||||
} // namespace Core::Devices
|
||||
79
src/core/devices/urandom_device.cpp
Normal file
79
src/core/devices/urandom_device.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <cstdlib>
|
||||
#include "common/logging/log.h"
|
||||
#include "urandom_device.h"
|
||||
|
||||
namespace Core::Devices {
|
||||
|
||||
std::shared_ptr<BaseDevice> URandomDevice::Create(u32 handle, const char*, int, u16) {
|
||||
std::srand(std::time(nullptr));
|
||||
return std::shared_ptr<BaseDevice>(
|
||||
reinterpret_cast<Devices::BaseDevice*>(new URandomDevice(handle)));
|
||||
}
|
||||
|
||||
int URandomDevice::ioctl(u64 cmd, Common::VaCtx* args) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 URandomDevice::write(const void* buf, size_t nbytes) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t URandomDevice::writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t URandomDevice::readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 URandomDevice::preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 URandomDevice::lseek(s64 offset, int whence) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 URandomDevice::read(void* buf, size_t nbytes) {
|
||||
auto rbuf = static_cast<char*>(buf);
|
||||
for (size_t i = 0; i < nbytes; i++) {
|
||||
rbuf[i] = std::rand() & 0xFF;
|
||||
}
|
||||
return nbytes;
|
||||
}
|
||||
|
||||
int URandomDevice::fstat(Libraries::Kernel::OrbisKernelStat* sb) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 URandomDevice::fsync() {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int URandomDevice::ftruncate(s64 length) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int URandomDevice::getdents(void* buf, u32 nbytes, s64* basep) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
s64 URandomDevice::pwrite(const void* buf, size_t nbytes, u64 offset) {
|
||||
LOG_ERROR(Kernel_Pthread, "(STUBBED) called");
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace Core::Devices
|
||||
33
src/core/devices/urandom_device.h
Normal file
33
src/core/devices/urandom_device.h
Normal file
@@ -0,0 +1,33 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
#include <memory>
|
||||
#include "base_device.h"
|
||||
|
||||
namespace Core::Devices {
|
||||
|
||||
class URandomDevice final : BaseDevice {
|
||||
u32 handle;
|
||||
|
||||
public:
|
||||
static std::shared_ptr<BaseDevice> Create(u32 handle, const char*, int, u16);
|
||||
explicit URandomDevice(u32 handle) : handle(handle) {}
|
||||
|
||||
~URandomDevice() override = default;
|
||||
|
||||
int ioctl(u64 cmd, Common::VaCtx* args) override;
|
||||
s64 write(const void* buf, size_t nbytes) override;
|
||||
size_t readv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
|
||||
size_t writev(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt) override;
|
||||
s64 preadv(const Libraries::Kernel::SceKernelIovec* iov, int iovcnt, u64 offset) override;
|
||||
s64 lseek(s64 offset, int whence) override;
|
||||
s64 read(void* buf, size_t nbytes) override;
|
||||
int fstat(Libraries::Kernel::OrbisKernelStat* sb) override;
|
||||
s32 fsync() override;
|
||||
int ftruncate(s64 length) override;
|
||||
int getdents(void* buf, u32 nbytes, s64* basep) override;
|
||||
s64 pwrite(const void* buf, size_t nbytes, u64 offset) override;
|
||||
};
|
||||
|
||||
} // namespace Core::Devices
|
||||
@@ -21,14 +21,13 @@
|
||||
extern std::unique_ptr<Vulkan::Presenter> presenter;
|
||||
|
||||
using namespace ImGui;
|
||||
using namespace Core::Devtools;
|
||||
using L = Core::Devtools::Layer;
|
||||
using namespace ::Core::Devtools;
|
||||
using L = ::Core::Devtools::Layer;
|
||||
|
||||
static bool show_simple_fps = false;
|
||||
static bool visibility_toggled = false;
|
||||
|
||||
static float fps_scale = 1.0f;
|
||||
static bool show_advanced_debug = false;
|
||||
static int dump_frame_count = 1;
|
||||
|
||||
static Widget::FrameGraph frame_graph;
|
||||
@@ -82,8 +81,24 @@ void L::DrawMenuBar() {
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (BeginMenu("Display")) {
|
||||
auto& pp_settings = presenter->GetPPSettingsRef();
|
||||
if (BeginMenu("Brightness")) {
|
||||
SliderFloat("Gamma", &presenter->GetGammaRef(), 0.1f, 2.0f);
|
||||
SliderFloat("Gamma", &pp_settings.gamma, 0.1f, 2.0f);
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (BeginMenu("FSR")) {
|
||||
auto& fsr = presenter->GetFsrSettingsRef();
|
||||
Checkbox("FSR Enabled", &fsr.enable);
|
||||
BeginDisabled(!fsr.enable);
|
||||
{
|
||||
Checkbox("RCAS", &fsr.use_rcas);
|
||||
BeginDisabled(!fsr.use_rcas);
|
||||
{
|
||||
SliderFloat("RCAS Attenuation", &fsr.rcas_attenuation, 0.0, 3.0);
|
||||
}
|
||||
EndDisabled();
|
||||
}
|
||||
EndDisabled();
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
@@ -94,6 +109,12 @@ void L::DrawMenuBar() {
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
SameLine(ImGui::GetWindowWidth() - 30.0f);
|
||||
if (Button("X", ImVec2(25, 25))) {
|
||||
DebugState.IsShowingDebugMenuBar() = false;
|
||||
}
|
||||
|
||||
EndMainMenuBar();
|
||||
}
|
||||
|
||||
@@ -253,8 +274,20 @@ void L::DrawAdvanced() {
|
||||
}
|
||||
|
||||
void L::DrawSimple() {
|
||||
const auto io = GetIO();
|
||||
Text("%.1f FPS (%.2f ms)", io.Framerate, 1000.0f / io.Framerate);
|
||||
const float frameRate = DebugState.Framerate;
|
||||
if (Config::fpsColor()) {
|
||||
if (frameRate < 10) {
|
||||
PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); // Red
|
||||
} else if (frameRate >= 10 && frameRate < 20) {
|
||||
PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.5f, 0.0f, 1.0f)); // Orange
|
||||
} else {
|
||||
PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); // White
|
||||
}
|
||||
} else {
|
||||
PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); // White
|
||||
}
|
||||
Text("%d FPS (%.1f ms)", static_cast<int>(std::round(frameRate)), 1000.0f / frameRate);
|
||||
PopStyleColor();
|
||||
}
|
||||
|
||||
static void LoadSettings(const char* line) {
|
||||
@@ -265,7 +298,7 @@ static void LoadSettings(const char* line) {
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "show_advanced_debug=%d", &i) == 1) {
|
||||
show_advanced_debug = i != 0;
|
||||
DebugState.IsShowingDebugMenuBar() = i != 0;
|
||||
return;
|
||||
}
|
||||
if (sscanf(line, "show_frame_graph=%d", &i) == 1) {
|
||||
@@ -310,7 +343,7 @@ void L::SetupSettings() {
|
||||
handler.WriteAllFn = [](ImGuiContext*, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) {
|
||||
buf->appendf("[%s][Data]\n", handler->TypeName);
|
||||
buf->appendf("fps_scale=%f\n", fps_scale);
|
||||
buf->appendf("show_advanced_debug=%d\n", show_advanced_debug);
|
||||
buf->appendf("show_advanced_debug=%d\n", DebugState.IsShowingDebugMenuBar());
|
||||
buf->appendf("show_frame_graph=%d\n", frame_graph.is_open);
|
||||
buf->appendf("dump_frame_count=%d\n", dump_frame_count);
|
||||
buf->append("\n");
|
||||
@@ -336,12 +369,12 @@ void L::Draw() {
|
||||
|
||||
if (!DebugState.IsGuestThreadsPaused()) {
|
||||
const auto fn = DebugState.flip_frame_count.load();
|
||||
frame_graph.AddFrame(fn, io.DeltaTime);
|
||||
frame_graph.AddFrame(fn, DebugState.FrameDeltaTime);
|
||||
}
|
||||
|
||||
if (IsKeyPressed(ImGuiKey_F10, false)) {
|
||||
if (io.KeyCtrl) {
|
||||
show_advanced_debug = !show_advanced_debug;
|
||||
DebugState.IsShowingDebugMenuBar() ^= true;
|
||||
} else {
|
||||
show_simple_fps = !show_simple_fps;
|
||||
}
|
||||
@@ -376,7 +409,7 @@ void L::Draw() {
|
||||
End();
|
||||
}
|
||||
|
||||
if (show_advanced_debug) {
|
||||
if (DebugState.IsShowingDebugMenuBar()) {
|
||||
PushFont(io.Fonts->Fonts[IMGUI_FONT_MONO]);
|
||||
PushID("DevtoolsLayer");
|
||||
DrawAdvanced();
|
||||
|
||||
@@ -1174,7 +1174,7 @@ CmdListViewer::CmdListViewer(DebugStateType::FrameDump* _frame_dump,
|
||||
}
|
||||
}
|
||||
|
||||
void CmdListViewer::Draw(bool only_batches_view) {
|
||||
void CmdListViewer::Draw(bool only_batches_view, CmdListFilter& filter) {
|
||||
const auto& ctx = *GetCurrentContext();
|
||||
|
||||
if (batch_view.open) {
|
||||
@@ -1285,6 +1285,41 @@ void CmdListViewer::Draw(bool only_batches_view) {
|
||||
}
|
||||
|
||||
auto& batch = std::get<BatchInfo>(event);
|
||||
|
||||
// filtering
|
||||
{
|
||||
bool remove = false;
|
||||
|
||||
if (filter.shader_name[0] != '\0') {
|
||||
remove = true;
|
||||
std::string_view shader_name{filter.shader_name};
|
||||
const auto& data = frame_dump->regs.find(batch.command_addr);
|
||||
if (data != frame_dump->regs.end()) {
|
||||
DebugStateType::RegDump& dump = data->second;
|
||||
if (dump.is_compute) {
|
||||
if (dump.cs_data.name.contains(shader_name)) {
|
||||
remove = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < DebugStateType::RegDump::MaxShaderStages; ++i) {
|
||||
if (dump.regs.stage_enable.IsStageEnabled(i)) {
|
||||
auto& stage = dump.stages[i];
|
||||
if (stage.name.contains(shader_name)) {
|
||||
remove = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
auto const* pm4_hdr =
|
||||
reinterpret_cast<PM4Header const*>(cmdb_addr + batch.start_addr);
|
||||
|
||||
|
||||
@@ -35,6 +35,10 @@ void ParseDepthControl(u32 value, bool begin_table = true);
|
||||
void ParseEqaa(u32 value, bool begin_table = true);
|
||||
void ParseZInfo(u32 value, bool begin_table = true);
|
||||
|
||||
struct CmdListFilter {
|
||||
char shader_name[128]{};
|
||||
};
|
||||
|
||||
class CmdListViewer {
|
||||
|
||||
DebugStateType::FrameDump* frame_dump;
|
||||
@@ -70,7 +74,7 @@ public:
|
||||
explicit CmdListViewer(DebugStateType::FrameDump* frame_dump, const std::vector<u32>& cmd_list,
|
||||
uintptr_t base_addr = 0, std::string name = "");
|
||||
|
||||
void Draw(bool only_batches_view = false);
|
||||
void Draw(bool only_batches_view, CmdListFilter& filter);
|
||||
};
|
||||
|
||||
} // namespace Core::Devtools::Widget
|
||||
|
||||
@@ -117,7 +117,7 @@ static bool IsDrawCall(AmdGpu::PM4ItOpcode opcode) {
|
||||
inline std::optional<std::string> exec_cli(const char* cli) {
|
||||
std::array<char, 64> buffer{};
|
||||
std::string output;
|
||||
const auto f = popen(cli, "rt");
|
||||
const auto f = popen(cli, "r");
|
||||
if (!f) {
|
||||
pclose(f);
|
||||
return {};
|
||||
|
||||
@@ -132,6 +132,15 @@ void FrameDumpViewer::Draw() {
|
||||
}
|
||||
}
|
||||
EndDisabled();
|
||||
SameLine();
|
||||
if (BeginMenu("Filter")) {
|
||||
|
||||
TextUnformatted("Shader name");
|
||||
SameLine();
|
||||
InputText("##filter_shader", filter.shader_name, sizeof(filter.shader_name));
|
||||
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
|
||||
TextEx("Submit num");
|
||||
SameLine();
|
||||
@@ -187,7 +196,7 @@ void FrameDumpViewer::Draw() {
|
||||
EndGroup();
|
||||
}
|
||||
if (is_showing && selected_cmd != -1) {
|
||||
cmd_list_viewer[selected_cmd].Draw(is_collapsed);
|
||||
cmd_list_viewer[selected_cmd].Draw(is_collapsed, filter);
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ class FrameDumpViewer {
|
||||
s32 selected_queue_num2;
|
||||
s32 selected_cmd = -1;
|
||||
|
||||
CmdListFilter filter;
|
||||
|
||||
public:
|
||||
bool is_open = true;
|
||||
|
||||
|
||||
@@ -19,11 +19,62 @@ constexpr float BAR_HEIGHT_MULT = 1.25f;
|
||||
constexpr float FRAME_GRAPH_PADDING_Y = 3.0f;
|
||||
constexpr static float FRAME_GRAPH_HEIGHT = 50.0f;
|
||||
|
||||
void FrameGraph::DrawFrameGraph() {
|
||||
// Frame graph - inspired by
|
||||
// https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times
|
||||
const float full_width = GetContentRegionAvail().x;
|
||||
auto pos = GetCursorScreenPos();
|
||||
const ImVec2 size{full_width, FRAME_GRAPH_HEIGHT + FRAME_GRAPH_PADDING_Y * 2.0f};
|
||||
ItemSize(size);
|
||||
if (!ItemAdd({pos, pos + size}, GetID("FrameGraph"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
float target_dt = 1.0f / (TARGET_FPS * (float)Config::vblankDiv());
|
||||
float cur_pos_x = pos.x + full_width;
|
||||
pos.y += FRAME_GRAPH_PADDING_Y;
|
||||
const float final_pos_y = pos.y + FRAME_GRAPH_HEIGHT;
|
||||
|
||||
auto& draw_list = *GetWindowDrawList();
|
||||
draw_list.AddRectFilled({pos.x, pos.y - FRAME_GRAPH_PADDING_Y},
|
||||
{pos.x + full_width, final_pos_y + FRAME_GRAPH_PADDING_Y},
|
||||
IM_COL32(0x33, 0x33, 0x33, 0xFF));
|
||||
draw_list.PushClipRect({pos.x, pos.y}, {pos.x + full_width, final_pos_y}, true);
|
||||
for (u32 i = 0; i < FRAME_BUFFER_SIZE; ++i) {
|
||||
const auto& frame_info = frame_list[(DebugState.GetFrameNum() - i) % FRAME_BUFFER_SIZE];
|
||||
const float dt_factor = target_dt / frame_info.delta;
|
||||
|
||||
const float width = std::ceil(BAR_WIDTH_MULT / dt_factor);
|
||||
const float height =
|
||||
std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * FRAME_GRAPH_HEIGHT;
|
||||
|
||||
ImU32 color;
|
||||
if (dt_factor >= 0.95f) { // BLUE
|
||||
color = IM_COL32(0x33, 0x33, 0xFF, 0xFF);
|
||||
} else if (dt_factor >= 0.5f) { // GREEN <> YELLOW
|
||||
float t = 1.0f - (dt_factor - 0.5f) * 2.0f;
|
||||
int r = (int)(0xFF * t);
|
||||
color = IM_COL32(r, 0xFF, 0, 0xFF);
|
||||
} else { // YELLOW <> RED
|
||||
float t = dt_factor * 2.0f;
|
||||
int g = (int)(0xFF * t);
|
||||
color = IM_COL32(0xFF, g, 0, 0xFF);
|
||||
}
|
||||
draw_list.AddRectFilled({cur_pos_x - width, final_pos_y - height}, {cur_pos_x, final_pos_y},
|
||||
color);
|
||||
cur_pos_x -= width;
|
||||
if (cur_pos_x < width) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
draw_list.PopClipRect();
|
||||
}
|
||||
|
||||
void FrameGraph::Draw() {
|
||||
if (!is_open) {
|
||||
return;
|
||||
}
|
||||
SetNextWindowSize({340.0, 185.0f}, ImGuiCond_FirstUseEver);
|
||||
SetNextWindowSize({308.0, 270.0f}, ImGuiCond_FirstUseEver);
|
||||
if (Begin("Video debug info", &is_open)) {
|
||||
const auto& ctx = *GImGui;
|
||||
const auto& io = ctx.IO;
|
||||
@@ -32,66 +83,25 @@ void FrameGraph::Draw() {
|
||||
|
||||
auto isSystemPaused = DebugState.IsGuestThreadsPaused();
|
||||
|
||||
static float deltaTime;
|
||||
static float frameRate;
|
||||
|
||||
if (!isSystemPaused) {
|
||||
deltaTime = io.DeltaTime * 1000.0f;
|
||||
deltaTime = DebugState.FrameDeltaTime * 1000.0f;
|
||||
frameRate = 1000.0f / deltaTime;
|
||||
}
|
||||
|
||||
SeparatorText("Frame graph");
|
||||
DrawFrameGraph();
|
||||
|
||||
SeparatorText("Renderer info");
|
||||
|
||||
Text("Frame time: %.3f ms (%.1f FPS)", deltaTime, frameRate);
|
||||
Text("Presenter time: %.3f ms (%.1f FPS)", io.DeltaTime * 1000.0f, 1.0f / io.DeltaTime);
|
||||
Text("Flip frame: %d Gnm submit frame: %d", DebugState.flip_frame_count.load(),
|
||||
DebugState.gnm_frame_count.load());
|
||||
SeparatorText("Frame graph");
|
||||
|
||||
const float full_width = GetContentRegionAvail().x;
|
||||
// Frame graph - inspired by
|
||||
// https://asawicki.info/news_1758_an_idea_for_visualization_of_frame_times
|
||||
auto pos = GetCursorScreenPos();
|
||||
const ImVec2 size{full_width, FRAME_GRAPH_HEIGHT + FRAME_GRAPH_PADDING_Y * 2.0f};
|
||||
ItemSize(size);
|
||||
if (!ItemAdd({pos, pos + size}, GetID("FrameGraph"))) {
|
||||
return;
|
||||
}
|
||||
|
||||
float target_dt = 1.0f / (TARGET_FPS * (float)Config::vblankDiv());
|
||||
float cur_pos_x = pos.x + full_width;
|
||||
pos.y += FRAME_GRAPH_PADDING_Y;
|
||||
const float final_pos_y = pos.y + FRAME_GRAPH_HEIGHT;
|
||||
|
||||
draw_list.AddRectFilled({pos.x, pos.y - FRAME_GRAPH_PADDING_Y},
|
||||
{pos.x + full_width, final_pos_y + FRAME_GRAPH_PADDING_Y},
|
||||
IM_COL32(0x33, 0x33, 0x33, 0xFF));
|
||||
draw_list.PushClipRect({pos.x, pos.y}, {pos.x + full_width, final_pos_y}, true);
|
||||
for (u32 i = 0; i < FRAME_BUFFER_SIZE; ++i) {
|
||||
const auto& frame_info = frame_list[(DebugState.GetFrameNum() - i) % FRAME_BUFFER_SIZE];
|
||||
const float dt_factor = target_dt / frame_info.delta;
|
||||
|
||||
const float width = std::ceil(BAR_WIDTH_MULT / dt_factor);
|
||||
const float height =
|
||||
std::min(std::log2(BAR_HEIGHT_MULT / dt_factor) / 3.0f, 1.0f) * FRAME_GRAPH_HEIGHT;
|
||||
|
||||
ImU32 color;
|
||||
if (dt_factor >= 0.95f) { // BLUE
|
||||
color = IM_COL32(0x33, 0x33, 0xFF, 0xFF);
|
||||
} else if (dt_factor >= 0.5f) { // GREEN <> YELLOW
|
||||
float t = 1.0f - (dt_factor - 0.5f) * 2.0f;
|
||||
int r = (int)(0xFF * t);
|
||||
color = IM_COL32(r, 0xFF, 0, 0xFF);
|
||||
} else { // YELLOW <> RED
|
||||
float t = dt_factor * 2.0f;
|
||||
int g = (int)(0xFF * t);
|
||||
color = IM_COL32(0xFF, g, 0, 0xFF);
|
||||
}
|
||||
draw_list.AddRectFilled({cur_pos_x - width, final_pos_y - height},
|
||||
{cur_pos_x, final_pos_y}, color);
|
||||
cur_pos_x -= width;
|
||||
if (cur_pos_x < width) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
draw_list.PopClipRect();
|
||||
Text("Game Res: %dx%d", DebugState.game_resolution.first,
|
||||
DebugState.game_resolution.second);
|
||||
Text("Output Res: %dx%d", DebugState.output_resolution.first,
|
||||
DebugState.output_resolution.second);
|
||||
Text("FSR: %s", DebugState.is_using_fsr ? "on" : "off");
|
||||
}
|
||||
End();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,11 @@ class FrameGraph {
|
||||
|
||||
std::array<FrameInfo, FRAME_BUFFER_SIZE> frame_list{};
|
||||
|
||||
float deltaTime{};
|
||||
float frameRate{};
|
||||
|
||||
void DrawFrameGraph();
|
||||
|
||||
public:
|
||||
bool is_open = true;
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ void RegPopup::DrawColorBuffer(const AmdGpu::Liverpool::ColorBuffer& buffer) {
|
||||
"GetColorSliceSize()", buffer.GetColorSliceSize(),
|
||||
"GetTilingMode()", buffer.GetTilingMode(),
|
||||
"IsTiled()", buffer.IsTiled(),
|
||||
"NumFormat()", buffer.NumFormat()
|
||||
"NumFormat()", buffer.GetNumberFmt()
|
||||
);
|
||||
|
||||
// clang-format on
|
||||
@@ -105,7 +105,8 @@ void RegPopup::DrawDepthBuffer(const DepthBuffer& depth_data) {
|
||||
"DEPTH_SLICE.TILE_MAX", depth_buffer.depth_slice.tile_max,
|
||||
"Pitch()", depth_buffer.Pitch(),
|
||||
"Height()", depth_buffer.Height(),
|
||||
"Address()", depth_buffer.Address(),
|
||||
"DepthAddress()", depth_buffer.DepthAddress(),
|
||||
"StencilAddress()", depth_buffer.StencilAddress(),
|
||||
"NumSamples()", depth_buffer.NumSamples(),
|
||||
"NumBits()", depth_buffer.NumBits(),
|
||||
"GetDepthSliceSize()", depth_buffer.GetDepthSliceSize()
|
||||
|
||||
@@ -155,7 +155,7 @@ void RegView::DrawGraphicsRegs() {
|
||||
TableNextColumn();
|
||||
TextUnformatted("Depth buffer");
|
||||
TableNextColumn();
|
||||
if (regs.depth_buffer.Address() == 0 || !regs.depth_control.depth_enable) {
|
||||
if (regs.depth_buffer.DepthAddress() == 0 || !regs.depth_control.depth_enable) {
|
||||
TextUnformatted("N/A");
|
||||
} else {
|
||||
const char* text = last_selected_cb == depth_id && default_reg_popup.open ? "x" : "->";
|
||||
@@ -241,7 +241,7 @@ void RegView::SetData(DebugStateType::RegDump _data, const std::string& base_tit
|
||||
default_reg_popup.open = false;
|
||||
if (last_selected_cb == depth_id) {
|
||||
const auto& has_depth =
|
||||
regs.depth_buffer.Address() != 0 && regs.depth_control.depth_enable;
|
||||
regs.depth_buffer.DepthAddress() != 0 && regs.depth_control.depth_enable;
|
||||
if (has_depth) {
|
||||
default_reg_popup.SetData(title, regs.depth_buffer, regs.depth_control);
|
||||
default_reg_popup.open = true;
|
||||
@@ -292,6 +292,17 @@ void RegView::Draw() {
|
||||
EndMenuBar();
|
||||
}
|
||||
|
||||
const char* shader_name = "_";
|
||||
if (data.is_compute) {
|
||||
shader_name = data.cs_data.name.c_str();
|
||||
} else if (selected_shader >= 0) {
|
||||
shader_name = data.stages[selected_shader].name.c_str();
|
||||
}
|
||||
|
||||
TextUnformatted("Shader: ");
|
||||
SameLine();
|
||||
TextUnformatted(shader_name);
|
||||
|
||||
if (!data.is_compute &&
|
||||
BeginChild("STAGES", {},
|
||||
ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY)) {
|
||||
|
||||
@@ -24,16 +24,33 @@ using namespace ImGui;
|
||||
|
||||
namespace Core::Devtools::Widget {
|
||||
|
||||
ShaderList::Selection::Selection(int index) : index(index) {
|
||||
isa_editor.SetPalette(TextEditor::GetDarkPalette());
|
||||
isa_editor.SetReadOnly(true);
|
||||
glsl_editor.SetPalette(TextEditor::GetDarkPalette());
|
||||
glsl_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
|
||||
ShaderList::Selection::Selection(int index)
|
||||
: index(index), isa_editor(std::make_unique<TextEditor>()),
|
||||
glsl_editor(std::make_unique<TextEditor>()) {
|
||||
isa_editor->SetPalette(TextEditor::GetDarkPalette());
|
||||
isa_editor->SetReadOnly(true);
|
||||
glsl_editor->SetPalette(TextEditor::GetDarkPalette());
|
||||
glsl_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::GLSL());
|
||||
presenter->GetWindow().RequestKeyboard();
|
||||
}
|
||||
|
||||
ShaderList::Selection::~Selection() {
|
||||
presenter->GetWindow().ReleaseKeyboard();
|
||||
if (index >= 0) {
|
||||
presenter->GetWindow().ReleaseKeyboard();
|
||||
}
|
||||
}
|
||||
|
||||
ShaderList::Selection::Selection(Selection&& other) noexcept
|
||||
: index{other.index}, isa_editor{std::move(other.isa_editor)},
|
||||
glsl_editor{std::move(other.glsl_editor)}, open{other.open}, showing_bin{other.showing_bin},
|
||||
patch_path{std::move(other.patch_path)}, patch_bin_path{std::move(other.patch_bin_path)} {
|
||||
other.index = -1;
|
||||
}
|
||||
|
||||
ShaderList::Selection& ShaderList::Selection::operator=(Selection other) {
|
||||
using std::swap;
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void ShaderList::Selection::ReloadShader(DebugStateType::ShaderDump& value) {
|
||||
@@ -72,13 +89,13 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
|
||||
|
||||
value.is_patched = !value.patch_spv.empty();
|
||||
if (!value.is_patched) { // No patch
|
||||
isa_editor.SetText(value.cache_isa_disasm);
|
||||
glsl_editor.SetText(value.cache_spv_disasm);
|
||||
isa_editor->SetText(value.cache_isa_disasm);
|
||||
glsl_editor->SetText(value.cache_spv_disasm);
|
||||
} else {
|
||||
isa_editor.SetText(value.cache_patch_disasm);
|
||||
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
|
||||
glsl_editor.SetText(value.patch_source);
|
||||
glsl_editor.SetReadOnly(false);
|
||||
isa_editor->SetText(value.cache_patch_disasm);
|
||||
isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
|
||||
glsl_editor->SetText(value.patch_source);
|
||||
glsl_editor->SetReadOnly(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,21 +114,25 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
|
||||
if (value.patch_source.empty()) {
|
||||
value.patch_source = value.cache_spv_disasm;
|
||||
}
|
||||
isa_editor.SetText(value.cache_patch_disasm);
|
||||
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
|
||||
glsl_editor.SetText(value.patch_source);
|
||||
glsl_editor.SetReadOnly(false);
|
||||
isa_editor->SetText(value.cache_patch_disasm);
|
||||
isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition::SPIRV());
|
||||
glsl_editor->SetText(value.patch_source);
|
||||
glsl_editor->SetReadOnly(false);
|
||||
if (!value.patch_spv.empty()) {
|
||||
ReloadShader(value);
|
||||
}
|
||||
} else {
|
||||
isa_editor.SetText(value.cache_isa_disasm);
|
||||
isa_editor.SetLanguageDefinition(TextEditor::LanguageDefinition());
|
||||
glsl_editor.SetText(value.cache_spv_disasm);
|
||||
glsl_editor.SetReadOnly(true);
|
||||
isa_editor->SetText(value.cache_isa_disasm);
|
||||
isa_editor->SetLanguageDefinition(TextEditor::LanguageDefinition());
|
||||
glsl_editor->SetText(value.cache_spv_disasm);
|
||||
glsl_editor->SetReadOnly(true);
|
||||
ReloadShader(value);
|
||||
}
|
||||
}
|
||||
SameLine();
|
||||
if (Button("Copy name")) {
|
||||
SetClipboardText(value.name.c_str());
|
||||
}
|
||||
|
||||
if (value.is_patched) {
|
||||
if (BeginCombo("Shader type", showing_bin ? "SPIRV" : "GLSL",
|
||||
@@ -150,7 +171,7 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
|
||||
compile = true;
|
||||
}
|
||||
if (save) {
|
||||
value.patch_source = glsl_editor.GetText();
|
||||
value.patch_source = glsl_editor->GetText();
|
||||
std::ofstream file{patch_path, std::ios::binary | std::ios::trunc};
|
||||
file << value.patch_source;
|
||||
std::string msg = "Patch saved to ";
|
||||
@@ -188,7 +209,7 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
|
||||
DebugState.ShowDebugMessage("Decompilation failed (Compile was ok):\n" +
|
||||
res);
|
||||
} else {
|
||||
isa_editor.SetText(value.cache_patch_disasm);
|
||||
isa_editor->SetText(value.cache_patch_disasm);
|
||||
ReloadShader(value);
|
||||
}
|
||||
}
|
||||
@@ -197,9 +218,9 @@ bool ShaderList::Selection::DrawShader(DebugStateType::ShaderDump& value) {
|
||||
}
|
||||
|
||||
if (showing_bin) {
|
||||
isa_editor.Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail());
|
||||
isa_editor->Render(value.is_patched ? "SPIRV" : "ISA", GetContentRegionAvail());
|
||||
} else {
|
||||
glsl_editor.Render("GLSL", GetContentRegionAvail());
|
||||
glsl_editor->Render("GLSL", GetContentRegionAvail());
|
||||
}
|
||||
|
||||
End();
|
||||
@@ -229,9 +250,16 @@ void ShaderList::Draw() {
|
||||
return;
|
||||
}
|
||||
|
||||
InputTextEx("##search_shader", "Search by name", search_box, sizeof(search_box), {},
|
||||
ImGuiInputTextFlags_None);
|
||||
|
||||
auto width = GetContentRegionAvail().x;
|
||||
int i = 0;
|
||||
for (const auto& shader : DebugState.shader_dump_list) {
|
||||
if (search_box[0] != '\0' && !shader.name.contains(search_box)) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
char name[128];
|
||||
if (shader.is_patched) {
|
||||
snprintf(name, sizeof(name), "%s (PATCH ON)", shader.name.c_str());
|
||||
|
||||
@@ -14,14 +14,17 @@ class ShaderList {
|
||||
struct Selection {
|
||||
explicit Selection(int index);
|
||||
~Selection();
|
||||
Selection(const Selection& other) = delete;
|
||||
Selection(Selection&& other) noexcept;
|
||||
Selection& operator=(Selection other);
|
||||
|
||||
void ReloadShader(DebugStateType::ShaderDump& value);
|
||||
|
||||
bool DrawShader(DebugStateType::ShaderDump& value);
|
||||
|
||||
int index;
|
||||
TextEditor isa_editor{};
|
||||
TextEditor glsl_editor{};
|
||||
int index{-1};
|
||||
std::unique_ptr<TextEditor> isa_editor{};
|
||||
std::unique_ptr<TextEditor> glsl_editor{};
|
||||
bool open = true;
|
||||
bool showing_bin = false;
|
||||
|
||||
@@ -31,6 +34,8 @@ class ShaderList {
|
||||
|
||||
std::vector<Selection> open_shaders{};
|
||||
|
||||
char search_box[128]{};
|
||||
|
||||
public:
|
||||
bool open = false;
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/io_file.h"
|
||||
#include "common/stb.h"
|
||||
#include "splash.h"
|
||||
|
||||
bool Splash::Open(const std::filesystem::path& filepath) {
|
||||
ASSERT_MSG(filepath.extension().string() == ".png", "Unexpected file format passed");
|
||||
|
||||
Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Read);
|
||||
if (!file.IsOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<u8> png_file{};
|
||||
const auto png_size = file.GetSize();
|
||||
png_file.resize(png_size);
|
||||
file.Seek(0);
|
||||
file.Read(png_file);
|
||||
|
||||
auto* img_mem = stbi_load_from_memory(png_file.data(), png_file.size(),
|
||||
reinterpret_cast<int*>(&img_info.width),
|
||||
reinterpret_cast<int*>(&img_info.height),
|
||||
reinterpret_cast<int*>(&img_info.num_channels), 4);
|
||||
if (!img_mem) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto img_size = img_info.GetSizeBytes();
|
||||
img_data.resize(img_size);
|
||||
std::memcpy(img_data.data(), img_mem, img_size);
|
||||
stbi_image_free(img_mem);
|
||||
return true;
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "common/types.h"
|
||||
|
||||
class Splash {
|
||||
public:
|
||||
struct ImageInfo {
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 num_channels;
|
||||
|
||||
u32 GetSizeBytes() const {
|
||||
return width * height * 4; // we always forcing rgba8 for simplicity
|
||||
}
|
||||
};
|
||||
|
||||
Splash() = default;
|
||||
~Splash() = default;
|
||||
|
||||
bool Open(const std::filesystem::path& filepath);
|
||||
[[nodiscard]] bool IsLoaded() const {
|
||||
return img_data.size();
|
||||
}
|
||||
|
||||
const auto& GetImageData() const {
|
||||
return img_data;
|
||||
}
|
||||
|
||||
ImageInfo GetImageInfo() const {
|
||||
return img_info;
|
||||
}
|
||||
|
||||
private:
|
||||
ImageInfo img_info{};
|
||||
std::vector<u8> img_data{};
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include "common/config.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "trp.h"
|
||||
@@ -33,12 +34,29 @@ static void removePadding(std::vector<u8>& vec) {
|
||||
}
|
||||
}
|
||||
|
||||
static void hexToBytes(const char* hex, unsigned char* dst) {
|
||||
for (size_t i = 0; hex[i] != 0; i++) {
|
||||
const unsigned char value = (hex[i] < 0x3A) ? (hex[i] - 0x30) : (hex[i] - 0x37);
|
||||
dst[i / 2] |= ((i % 2) == 0) ? (value << 4) : (value);
|
||||
}
|
||||
}
|
||||
|
||||
bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string titleId) {
|
||||
std::filesystem::path gameSysDir = trophyPath / "sce_sys/trophy/";
|
||||
if (!std::filesystem::exists(gameSysDir)) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Game sce_sys directory doesn't exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto user_key_str = Config::getTrophyKey();
|
||||
if (user_key_str.size() != 32) {
|
||||
LOG_CRITICAL(Common_Filesystem, "Trophy decryption key is not specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::array<CryptoPP::byte, 16> user_key{};
|
||||
hexToBytes(user_key_str.c_str(), user_key.data());
|
||||
|
||||
for (int index = 0; const auto& it : std::filesystem::directory_iterator(gameSysDir)) {
|
||||
if (it.is_regular_file()) {
|
||||
GetNPcommID(trophyPath, index);
|
||||
@@ -97,7 +115,7 @@ bool TRP::Extract(const std::filesystem::path& trophyPath, const std::string tit
|
||||
return false;
|
||||
}
|
||||
file.Read(ESFM);
|
||||
crypto.decryptEFSM(np_comm_id, esfmIv, ESFM, XML); // decrypt
|
||||
crypto.decryptEFSM(user_key, np_comm_id, esfmIv, ESFM, XML); // decrypt
|
||||
removePadding(XML);
|
||||
std::string xml_name = entry.entry_name;
|
||||
size_t pos = xml_name.find("ESFM");
|
||||
|
||||
@@ -40,7 +40,8 @@ void MntPoints::UnmountAll() {
|
||||
m_mnt_pairs.clear();
|
||||
}
|
||||
|
||||
std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only) {
|
||||
std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_read_only,
|
||||
bool force_base_path) {
|
||||
// Evil games like Turok2 pass double slashes e.g /app0//game.kpf
|
||||
std::string corrected_path(path);
|
||||
size_t pos = corrected_path.find("//");
|
||||
@@ -72,7 +73,7 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
|
||||
patch_path /= rel_path;
|
||||
|
||||
if ((corrected_path.starts_with("/app0") || corrected_path.starts_with("/hostapp")) &&
|
||||
std::filesystem::exists(patch_path)) {
|
||||
!force_base_path && std::filesystem::exists(patch_path)) {
|
||||
return patch_path;
|
||||
}
|
||||
|
||||
@@ -132,8 +133,10 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
|
||||
return std::optional<std::filesystem::path>(current_path);
|
||||
};
|
||||
|
||||
if (const auto path = search(patch_path)) {
|
||||
return *path;
|
||||
if (!force_base_path) {
|
||||
if (const auto path = search(patch_path)) {
|
||||
return *path;
|
||||
}
|
||||
}
|
||||
if (const auto path = search(host_path)) {
|
||||
return *path;
|
||||
@@ -144,6 +147,39 @@ std::filesystem::path MntPoints::GetHostPath(std::string_view path, bool* is_rea
|
||||
return host_path;
|
||||
}
|
||||
|
||||
// TODO: Does not handle mount points inside mount points.
|
||||
void MntPoints::IterateDirectory(std::string_view guest_directory,
|
||||
const IterateDirectoryCallback& callback) {
|
||||
const auto base_path = GetHostPath(guest_directory, nullptr, true);
|
||||
const auto patch_path = GetHostPath(guest_directory, nullptr, false);
|
||||
// Only need to consider patch path if it exists and does not resolve to the same as base.
|
||||
const auto apply_patch = base_path != patch_path && std::filesystem::exists(patch_path);
|
||||
|
||||
// Pass 1: Any files that existed in the base directory, using patch directory if needed.
|
||||
if (std::filesystem::exists(base_path)) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(base_path)) {
|
||||
if (apply_patch) {
|
||||
const auto patch_entry_path = patch_path / entry.path().filename();
|
||||
if (std::filesystem::exists(patch_entry_path)) {
|
||||
callback(patch_entry_path, !std::filesystem::is_directory(patch_entry_path));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
callback(entry.path(), !entry.is_directory());
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2: Any files that exist only in the patch directory.
|
||||
if (apply_patch) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(patch_path)) {
|
||||
const auto base_entry_path = base_path / entry.path().filename();
|
||||
if (!std::filesystem::exists(base_entry_path)) {
|
||||
callback(entry.path(), !entry.is_directory());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int HandleTable::CreateHandle() {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
@@ -197,7 +233,7 @@ void HandleTable::CreateStdHandles() {
|
||||
std::shared_ptr<Devices::BaseDevice>{reinterpret_cast<Devices::BaseDevice*>(device)};
|
||||
};
|
||||
// order matters
|
||||
setup("/dev/stdin", new Devices::NopDevice(0)); // stdin
|
||||
setup("/dev/stdin", new Devices::Logger("stdin", false)); // stdin
|
||||
setup("/dev/stdout", new Devices::Logger("stdout", false)); // stdout
|
||||
setup("/dev/stderr", new Devices::Logger("stderr", true)); // stderr
|
||||
}
|
||||
|
||||
@@ -36,7 +36,11 @@ public:
|
||||
void UnmountAll();
|
||||
|
||||
std::filesystem::path GetHostPath(std::string_view guest_directory,
|
||||
bool* is_read_only = nullptr);
|
||||
bool* is_read_only = nullptr, bool force_base_path = false);
|
||||
using IterateDirectoryCallback =
|
||||
std::function<void(const std::filesystem::path& host_path, bool is_file)>;
|
||||
void IterateDirectory(std::string_view guest_directory,
|
||||
const IterateDirectoryCallback& callback);
|
||||
|
||||
const MntPair* GetMountFromHostPath(const std::string& host_path) {
|
||||
std::scoped_lock lock{m_mutex};
|
||||
|
||||
@@ -183,13 +183,15 @@ int PS4_SYSV_ABI sceAjmInstanceSwitch() {
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAjmMemoryRegister() {
|
||||
LOG_ERROR(Lib_Ajm, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceAjmMemoryRegister(u32 context_id, void* ptr, size_t num_pages) {
|
||||
// All memory is already shared with our implementation since we do not use any hardware.
|
||||
LOG_TRACE(Lib_Ajm, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI sceAjmMemoryUnregister() {
|
||||
LOG_ERROR(Lib_Ajm, "(STUBBED) called");
|
||||
int PS4_SYSV_ABI sceAjmMemoryUnregister(u32 context_id, void* ptr) {
|
||||
// All memory is already shared with our implementation since we do not use any hardware.
|
||||
LOG_TRACE(Lib_Ajm, "(STUBBED) called");
|
||||
return ORBIS_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,26 @@ union AjmJobFlags {
|
||||
};
|
||||
};
|
||||
|
||||
enum class AjmStatisticsFlags : u64 {
|
||||
Memory = 1 << 0,
|
||||
EnginePerCodec = 1 << 15,
|
||||
Engine = 1 << 16,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(AjmStatisticsFlags)
|
||||
|
||||
union AjmStatisticsJobFlags {
|
||||
AjmStatisticsJobFlags(AjmJobFlags job_flags) : raw(job_flags.raw) {}
|
||||
|
||||
u64 raw;
|
||||
struct {
|
||||
u64 version : 3;
|
||||
u64 : 12;
|
||||
AjmStatisticsFlags statistics_flags : 17;
|
||||
u64 : 32;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(AjmStatisticsJobFlags) == 8);
|
||||
|
||||
struct AjmSidebandResult {
|
||||
s32 result;
|
||||
s32 internal_result;
|
||||
@@ -126,6 +146,31 @@ union AjmSidebandInitParameters {
|
||||
u8 reserved[8];
|
||||
};
|
||||
|
||||
struct AjmSidebandStatisticsEngine {
|
||||
float usage_batch;
|
||||
float usage_interval[3];
|
||||
};
|
||||
|
||||
struct AjmSidebandStatisticsEnginePerCodec {
|
||||
u8 codec_count;
|
||||
u8 codec_id[3];
|
||||
float codec_percentage[3];
|
||||
};
|
||||
|
||||
struct AjmSidebandStatisticsMemory {
|
||||
u32 instance_free;
|
||||
u32 buffer_free;
|
||||
u32 batch_size;
|
||||
u32 input_size;
|
||||
u32 output_size;
|
||||
u32 small_size;
|
||||
};
|
||||
|
||||
struct AjmSidebandStatisticsEngineParameters {
|
||||
u32 interval_count;
|
||||
float interval[3];
|
||||
};
|
||||
|
||||
union AjmInstanceFlags {
|
||||
u64 raw;
|
||||
struct {
|
||||
@@ -178,8 +223,8 @@ int PS4_SYSV_ABI sceAjmInstanceCreate(u32 context, AjmCodecType codec_type, AjmI
|
||||
int PS4_SYSV_ABI sceAjmInstanceDestroy(u32 context, u32 instance);
|
||||
int PS4_SYSV_ABI sceAjmInstanceExtend();
|
||||
int PS4_SYSV_ABI sceAjmInstanceSwitch();
|
||||
int PS4_SYSV_ABI sceAjmMemoryRegister();
|
||||
int PS4_SYSV_ABI sceAjmMemoryUnregister();
|
||||
int PS4_SYSV_ABI sceAjmMemoryRegister(u32 context_id, void* ptr, size_t num_pages);
|
||||
int PS4_SYSV_ABI sceAjmMemoryUnregister(u32 context_id, void* ptr);
|
||||
int PS4_SYSV_ABI sceAjmModuleRegister(u32 context, AjmCodecType codec_type, s64 reserved);
|
||||
int PS4_SYSV_ABI sceAjmModuleUnregister();
|
||||
int PS4_SYSV_ABI sceAjmStrError();
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user