diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/crypto/key_manager.cpp | 241 | ||||
| -rw-r--r-- | src/core/crypto/key_manager.h | 116 | ||||
| -rw-r--r-- | src/core/file_sys/system_archive/mii_model.cpp | 46 | ||||
| -rw-r--r-- | src/core/file_sys/system_archive/mii_model.h | 13 | ||||
| -rw-r--r-- | src/core/file_sys/system_archive/system_archive.cpp | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.cpp | 72 | ||||
| -rw-r--r-- | src/core/hle/kernel/vm_manager.h | 8 | ||||
| -rw-r--r-- | src/core/hle/service/es/es.cpp | 230 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 22 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_cache.cpp | 68 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_shader_cache.h | 21 | ||||
| -rw-r--r-- | src/video_core/shader/decode/half_set_predicate.cpp | 19 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_dialog.cpp | 44 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_general.cpp | 9 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_general.ui | 27 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.cpp | 6 | ||||
| -rw-r--r-- | src/yuzu/configuration/configure_graphics.ui | 27 | ||||
| -rw-r--r-- | src/yuzu/main.cpp | 11 |
19 files changed, 740 insertions, 245 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5462decee..877a9e353 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -70,6 +70,8 @@ add_library(core STATIC | |||
| 70 | file_sys/sdmc_factory.h | 70 | file_sys/sdmc_factory.h |
| 71 | file_sys/submission_package.cpp | 71 | file_sys/submission_package.cpp |
| 72 | file_sys/submission_package.h | 72 | file_sys/submission_package.h |
| 73 | file_sys/system_archive/mii_model.cpp | ||
| 74 | file_sys/system_archive/mii_model.h | ||
| 73 | file_sys/system_archive/ng_word.cpp | 75 | file_sys/system_archive/ng_word.cpp |
| 74 | file_sys/system_archive/ng_word.h | 76 | file_sys/system_archive/ng_word.h |
| 75 | file_sys/system_archive/system_archive.cpp | 77 | file_sys/system_archive/system_archive.cpp |
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index 6dd633363..46aceec3d 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -37,6 +37,7 @@ | |||
| 37 | namespace Core::Crypto { | 37 | namespace Core::Crypto { |
| 38 | 38 | ||
| 39 | constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; | 39 | constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; |
| 40 | constexpr u64 FULL_TICKET_SIZE = 0x400; | ||
| 40 | 41 | ||
| 41 | using namespace Common; | 42 | using namespace Common; |
| 42 | 43 | ||
| @@ -55,6 +56,99 @@ const std::map<std::pair<S128KeyType, u64>, std::string> KEYS_VARIABLE_LENGTH{ | |||
| 55 | {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, | 56 | {{S128KeyType::KeyblobMAC, 0}, "keyblob_mac_key_"}, |
| 56 | }; | 57 | }; |
| 57 | 58 | ||
| 59 | namespace { | ||
| 60 | template <std::size_t Size> | ||
| 61 | bool IsAllZeroArray(const std::array<u8, Size>& array) { | ||
| 62 | return std::all_of(array.begin(), array.end(), [](const auto& elem) { return elem == 0; }); | ||
| 63 | } | ||
| 64 | } // namespace | ||
| 65 | |||
| 66 | u64 GetSignatureTypeDataSize(SignatureType type) { | ||
| 67 | switch (type) { | ||
| 68 | case SignatureType::RSA_4096_SHA1: | ||
| 69 | case SignatureType::RSA_4096_SHA256: | ||
| 70 | return 0x200; | ||
| 71 | case SignatureType::RSA_2048_SHA1: | ||
| 72 | case SignatureType::RSA_2048_SHA256: | ||
| 73 | return 0x100; | ||
| 74 | case SignatureType::ECDSA_SHA1: | ||
| 75 | case SignatureType::ECDSA_SHA256: | ||
| 76 | return 0x3C; | ||
| 77 | } | ||
| 78 | UNREACHABLE(); | ||
| 79 | } | ||
| 80 | |||
| 81 | u64 GetSignatureTypePaddingSize(SignatureType type) { | ||
| 82 | switch (type) { | ||
| 83 | case SignatureType::RSA_4096_SHA1: | ||
| 84 | case SignatureType::RSA_4096_SHA256: | ||
| 85 | case SignatureType::RSA_2048_SHA1: | ||
| 86 | case SignatureType::RSA_2048_SHA256: | ||
| 87 | return 0x3C; | ||
| 88 | case SignatureType::ECDSA_SHA1: | ||
| 89 | case SignatureType::ECDSA_SHA256: | ||
| 90 | return 0x40; | ||
| 91 | } | ||
| 92 | UNREACHABLE(); | ||
| 93 | } | ||
| 94 | |||
| 95 | SignatureType Ticket::GetSignatureType() const { | ||
| 96 | if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { | ||
| 97 | return ticket->sig_type; | ||
| 98 | } | ||
| 99 | if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { | ||
| 100 | return ticket->sig_type; | ||
| 101 | } | ||
| 102 | if (auto ticket = std::get_if<ECDSATicket>(&data)) { | ||
| 103 | return ticket->sig_type; | ||
| 104 | } | ||
| 105 | |||
| 106 | UNREACHABLE(); | ||
| 107 | } | ||
| 108 | |||
| 109 | TicketData& Ticket::GetData() { | ||
| 110 | if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { | ||
| 111 | return ticket->data; | ||
| 112 | } | ||
| 113 | if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { | ||
| 114 | return ticket->data; | ||
| 115 | } | ||
| 116 | if (auto ticket = std::get_if<ECDSATicket>(&data)) { | ||
| 117 | return ticket->data; | ||
| 118 | } | ||
| 119 | |||
| 120 | UNREACHABLE(); | ||
| 121 | } | ||
| 122 | |||
| 123 | const TicketData& Ticket::GetData() const { | ||
| 124 | if (auto ticket = std::get_if<RSA4096Ticket>(&data)) { | ||
| 125 | return ticket->data; | ||
| 126 | } | ||
| 127 | if (auto ticket = std::get_if<RSA2048Ticket>(&data)) { | ||
| 128 | return ticket->data; | ||
| 129 | } | ||
| 130 | if (auto ticket = std::get_if<ECDSATicket>(&data)) { | ||
| 131 | return ticket->data; | ||
| 132 | } | ||
| 133 | |||
| 134 | UNREACHABLE(); | ||
| 135 | } | ||
| 136 | |||
| 137 | u64 Ticket::GetSize() const { | ||
| 138 | const auto sig_type = GetSignatureType(); | ||
| 139 | |||
| 140 | return sizeof(SignatureType) + GetSignatureTypeDataSize(sig_type) + | ||
| 141 | GetSignatureTypePaddingSize(sig_type) + sizeof(TicketData); | ||
| 142 | } | ||
| 143 | |||
| 144 | Ticket Ticket::SynthesizeCommon(Key128 title_key, const std::array<u8, 16>& rights_id) { | ||
| 145 | RSA2048Ticket out{}; | ||
| 146 | out.sig_type = SignatureType::RSA_2048_SHA256; | ||
| 147 | out.data.rights_id = rights_id; | ||
| 148 | out.data.title_key_common = title_key; | ||
| 149 | return Ticket{out}; | ||
| 150 | } | ||
| 151 | |||
| 58 | Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { | 152 | Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { |
| 59 | Key128 out{}; | 153 | Key128 out{}; |
| 60 | 154 | ||
| @@ -135,6 +229,27 @@ void KeyManager::DeriveGeneralPurposeKeys(std::size_t crypto_revision) { | |||
| 135 | } | 229 | } |
| 136 | } | 230 | } |
| 137 | 231 | ||
| 232 | RSAKeyPair<2048> KeyManager::GetETicketRSAKey() const { | ||
| 233 | if (IsAllZeroArray(eticket_extended_kek) || !HasKey(S128KeyType::ETicketRSAKek)) | ||
| 234 | return {}; | ||
| 235 | |||
| 236 | const auto eticket_final = GetKey(S128KeyType::ETicketRSAKek); | ||
| 237 | |||
| 238 | std::vector<u8> extended_iv(eticket_extended_kek.begin(), eticket_extended_kek.begin() + 0x10); | ||
| 239 | std::array<u8, 0x230> extended_dec{}; | ||
| 240 | AESCipher<Key128> rsa_1(eticket_final, Mode::CTR); | ||
| 241 | rsa_1.SetIV(extended_iv); | ||
| 242 | rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10, | ||
| 243 | extended_dec.data(), Op::Decrypt); | ||
| 244 | |||
| 245 | RSAKeyPair<2048> rsa_key{}; | ||
| 246 | std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size()); | ||
| 247 | std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size()); | ||
| 248 | std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size()); | ||
| 249 | |||
| 250 | return rsa_key; | ||
| 251 | } | ||
| 252 | |||
| 138 | Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) { | 253 | Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) { |
| 139 | AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB); | 254 | AESCipher<Key128> mac_cipher(keyblob_key, Mode::ECB); |
| 140 | Key128 mac_key{}; | 255 | Key128 mac_key{}; |
| @@ -237,7 +352,7 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke | |||
| 237 | return Loader::ResultStatus::Success; | 352 | return Loader::ResultStatus::Success; |
| 238 | } | 353 | } |
| 239 | 354 | ||
| 240 | std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) { | 355 | std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save) { |
| 241 | if (!ticket_save.IsOpen()) | 356 | if (!ticket_save.IsOpen()) |
| 242 | return {}; | 357 | return {}; |
| 243 | 358 | ||
| @@ -246,14 +361,14 @@ std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) { | |||
| 246 | return {}; | 361 | return {}; |
| 247 | } | 362 | } |
| 248 | 363 | ||
| 249 | std::vector<TicketRaw> out; | 364 | std::vector<Ticket> out; |
| 250 | for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) { | 365 | for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) { |
| 251 | if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 && | 366 | if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 && |
| 252 | buffer[offset + 3] == 0x0) { | 367 | buffer[offset + 3] == 0x0) { |
| 253 | out.emplace_back(); | 368 | out.emplace_back(); |
| 254 | auto& next = out.back(); | 369 | auto& next = out.back(); |
| 255 | std::memcpy(&next, buffer.data() + offset, sizeof(TicketRaw)); | 370 | std::memcpy(&next, buffer.data() + offset, sizeof(Ticket)); |
| 256 | offset += next.size(); | 371 | offset += FULL_TICKET_SIZE; |
| 257 | } | 372 | } |
| 258 | } | 373 | } |
| 259 | 374 | ||
| @@ -305,29 +420,23 @@ static std::optional<u64> FindTicketOffset(const std::array<u8, size>& data) { | |||
| 305 | return offset; | 420 | return offset; |
| 306 | } | 421 | } |
| 307 | 422 | ||
| 308 | std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, | 423 | std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, |
| 309 | const RSAKeyPair<2048>& key) { | 424 | const RSAKeyPair<2048>& key) { |
| 310 | u32 cert_authority; | 425 | const auto issuer = ticket.GetData().issuer; |
| 311 | std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority)); | 426 | if (issuer == std::array<u8, 0x40>{}) |
| 312 | if (cert_authority == 0) | ||
| 313 | return {}; | 427 | return {}; |
| 314 | if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't')) { | 428 | if (issuer[0] != 'R' || issuer[1] != 'o' || issuer[2] != 'o' || issuer[3] != 't') { |
| 315 | LOG_INFO(Crypto, | 429 | LOG_INFO(Crypto, "Attempting to parse ticket with non-standard certificate authority."); |
| 316 | "Attempting to parse ticket with non-standard certificate authority {:08X}.", | ||
| 317 | cert_authority); | ||
| 318 | } | 430 | } |
| 319 | 431 | ||
| 320 | Key128 rights_id; | 432 | Key128 rights_id = ticket.GetData().rights_id; |
| 321 | std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128)); | ||
| 322 | 433 | ||
| 323 | if (rights_id == Key128{}) | 434 | if (rights_id == Key128{}) |
| 324 | return {}; | 435 | return {}; |
| 325 | 436 | ||
| 326 | Key128 key_temp{}; | 437 | if (!std::any_of(ticket.GetData().title_key_common_pad.begin(), |
| 327 | 438 | ticket.GetData().title_key_common_pad.end(), [](u8 b) { return b != 0; })) { | |
| 328 | if (!std::any_of(ticket.begin() + 0x190, ticket.begin() + 0x280, [](u8 b) { return b != 0; })) { | 439 | return std::make_pair(rights_id, ticket.GetData().title_key_common); |
| 329 | std::memcpy(key_temp.data(), ticket.data() + 0x180, key_temp.size()); | ||
| 330 | return std::make_pair(rights_id, key_temp); | ||
| 331 | } | 440 | } |
| 332 | 441 | ||
| 333 | mbedtls_mpi D; // RSA Private Exponent | 442 | mbedtls_mpi D; // RSA Private Exponent |
| @@ -342,7 +451,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, | |||
| 342 | 451 | ||
| 343 | mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size()); | 452 | mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size()); |
| 344 | mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size()); | 453 | mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size()); |
| 345 | mbedtls_mpi_read_binary(&S, ticket.data() + 0x180, 0x100); | 454 | mbedtls_mpi_read_binary(&S, ticket.GetData().title_key_block.data(), 0x100); |
| 346 | 455 | ||
| 347 | mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr); | 456 | mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr); |
| 348 | 457 | ||
| @@ -366,6 +475,7 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, | |||
| 366 | return {}; | 475 | return {}; |
| 367 | ASSERT(*offset > 0); | 476 | ASSERT(*offset > 0); |
| 368 | 477 | ||
| 478 | Key128 key_temp{}; | ||
| 369 | std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size()); | 479 | std::memcpy(key_temp.data(), m_2.data() + *offset, key_temp.size()); |
| 370 | 480 | ||
| 371 | return std::make_pair(rights_id, key_temp); | 481 | return std::make_pair(rights_id, key_temp); |
| @@ -450,6 +560,8 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { | |||
| 450 | 560 | ||
| 451 | const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16); | 561 | const auto index = std::stoul(out[0].substr(18, 2), nullptr, 16); |
| 452 | encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); | 562 | encrypted_keyblobs[index] = Common::HexStringToArray<0xB0>(out[1]); |
| 563 | } else if (out[0].compare(0, 20, "eticket_extended_kek") == 0) { | ||
| 564 | eticket_extended_kek = Common::HexStringToArray<576>(out[1]); | ||
| 453 | } else { | 565 | } else { |
| 454 | for (const auto& kv : KEYS_VARIABLE_LENGTH) { | 566 | for (const auto& kv : KEYS_VARIABLE_LENGTH) { |
| 455 | if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) | 567 | if (!ValidCryptoRevisionString(out[0], kv.second.size(), 2)) |
| @@ -862,20 +974,19 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) { | |||
| 862 | // Titlekeys | 974 | // Titlekeys |
| 863 | data.DecryptProdInfo(GetBISKey(0)); | 975 | data.DecryptProdInfo(GetBISKey(0)); |
| 864 | 976 | ||
| 865 | const auto eticket_extended_kek = data.GetETicketExtendedKek(); | 977 | eticket_extended_kek = data.GetETicketExtendedKek(); |
| 978 | WriteKeyToFile(KeyCategory::Console, "eticket_extended_kek", eticket_extended_kek); | ||
| 979 | PopulateTickets(); | ||
| 980 | } | ||
| 866 | 981 | ||
| 867 | std::vector<u8> extended_iv(0x10); | 982 | void KeyManager::PopulateTickets() { |
| 868 | std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size()); | 983 | const auto rsa_key = GetETicketRSAKey(); |
| 869 | std::array<u8, 0x230> extended_dec{}; | ||
| 870 | AESCipher<Key128> rsa_1(eticket_final, Mode::CTR); | ||
| 871 | rsa_1.SetIV(extended_iv); | ||
| 872 | rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10, | ||
| 873 | extended_dec.data(), Op::Decrypt); | ||
| 874 | 984 | ||
| 875 | RSAKeyPair<2048> rsa_key{}; | 985 | if (rsa_key == RSAKeyPair<2048>{}) |
| 876 | std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size()); | 986 | return; |
| 877 | std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size()); | 987 | |
| 878 | std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size()); | 988 | if (!common_tickets.empty() && !personal_tickets.empty()) |
| 989 | return; | ||
| 879 | 990 | ||
| 880 | const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | 991 | const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + |
| 881 | "/system/save/80000000000000e1", | 992 | "/system/save/80000000000000e1", |
| @@ -886,19 +997,41 @@ void KeyManager::DeriveETicket(PartitionDataManager& data) { | |||
| 886 | 997 | ||
| 887 | const auto blob2 = GetTicketblob(save2); | 998 | const auto blob2 = GetTicketblob(save2); |
| 888 | auto res = GetTicketblob(save1); | 999 | auto res = GetTicketblob(save1); |
| 1000 | const auto idx = res.size(); | ||
| 889 | res.insert(res.end(), blob2.begin(), blob2.end()); | 1001 | res.insert(res.end(), blob2.begin(), blob2.end()); |
| 890 | 1002 | ||
| 891 | for (const auto& raw : res) { | 1003 | for (std::size_t i = 0; i < res.size(); ++i) { |
| 892 | const auto pair = ParseTicket(raw, rsa_key); | 1004 | const auto common = i < idx; |
| 1005 | const auto pair = ParseTicket(res[i], rsa_key); | ||
| 893 | if (!pair) | 1006 | if (!pair) |
| 894 | continue; | 1007 | continue; |
| 895 | const auto& [rid, key] = *pair; | 1008 | const auto& [rid, key] = *pair; |
| 896 | u128 rights_id; | 1009 | u128 rights_id; |
| 897 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | 1010 | std::memcpy(rights_id.data(), rid.data(), rid.size()); |
| 1011 | |||
| 1012 | if (common) { | ||
| 1013 | common_tickets[rights_id] = res[i]; | ||
| 1014 | } else { | ||
| 1015 | personal_tickets[rights_id] = res[i]; | ||
| 1016 | } | ||
| 1017 | |||
| 898 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | 1018 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); |
| 899 | } | 1019 | } |
| 900 | } | 1020 | } |
| 901 | 1021 | ||
| 1022 | void KeyManager::SynthesizeTickets() { | ||
| 1023 | for (const auto& key : s128_keys) { | ||
| 1024 | if (key.first.type != S128KeyType::Titlekey) { | ||
| 1025 | continue; | ||
| 1026 | } | ||
| 1027 | u128 rights_id{key.first.field1, key.first.field2}; | ||
| 1028 | Key128 rights_id_2; | ||
| 1029 | std::memcpy(rights_id_2.data(), rights_id.data(), rights_id_2.size()); | ||
| 1030 | const auto ticket = Ticket::SynthesizeCommon(key.second, rights_id_2); | ||
| 1031 | common_tickets.insert_or_assign(rights_id, ticket); | ||
| 1032 | } | ||
| 1033 | } | ||
| 1034 | |||
| 902 | void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) { | 1035 | void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) { |
| 903 | if (key == Key128{}) | 1036 | if (key == Key128{}) |
| 904 | return; | 1037 | return; |
| @@ -997,6 +1130,46 @@ void KeyManager::PopulateFromPartitionData(PartitionDataManager& data) { | |||
| 997 | DeriveBase(); | 1130 | DeriveBase(); |
| 998 | } | 1131 | } |
| 999 | 1132 | ||
| 1133 | const std::map<u128, Ticket>& KeyManager::GetCommonTickets() const { | ||
| 1134 | return common_tickets; | ||
| 1135 | } | ||
| 1136 | |||
| 1137 | const std::map<u128, Ticket>& KeyManager::GetPersonalizedTickets() const { | ||
| 1138 | return personal_tickets; | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | bool KeyManager::AddTicketCommon(Ticket raw) { | ||
| 1142 | const auto rsa_key = GetETicketRSAKey(); | ||
| 1143 | if (rsa_key == RSAKeyPair<2048>{}) | ||
| 1144 | return false; | ||
| 1145 | |||
| 1146 | const auto pair = ParseTicket(raw, rsa_key); | ||
| 1147 | if (!pair) | ||
| 1148 | return false; | ||
| 1149 | const auto& [rid, key] = *pair; | ||
| 1150 | u128 rights_id; | ||
| 1151 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | ||
| 1152 | common_tickets[rights_id] = raw; | ||
| 1153 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 1154 | return true; | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | bool KeyManager::AddTicketPersonalized(Ticket raw) { | ||
| 1158 | const auto rsa_key = GetETicketRSAKey(); | ||
| 1159 | if (rsa_key == RSAKeyPair<2048>{}) | ||
| 1160 | return false; | ||
| 1161 | |||
| 1162 | const auto pair = ParseTicket(raw, rsa_key); | ||
| 1163 | if (!pair) | ||
| 1164 | return false; | ||
| 1165 | const auto& [rid, key] = *pair; | ||
| 1166 | u128 rights_id; | ||
| 1167 | std::memcpy(rights_id.data(), rid.data(), rid.size()); | ||
| 1168 | common_tickets[rights_id] = raw; | ||
| 1169 | SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 1170 | return true; | ||
| 1171 | } | ||
| 1172 | |||
| 1000 | const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { | 1173 | const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { |
| 1001 | {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, | 1174 | {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, |
| 1002 | {"eticket_rsa_kek_source", | 1175 | {"eticket_rsa_kek_source", |
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 22f268c65..7265c4171 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h | |||
| @@ -9,8 +9,10 @@ | |||
| 9 | #include <optional> | 9 | #include <optional> |
| 10 | #include <string> | 10 | #include <string> |
| 11 | 11 | ||
| 12 | #include <variant> | ||
| 12 | #include <boost/container/flat_map.hpp> | 13 | #include <boost/container/flat_map.hpp> |
| 13 | #include <fmt/format.h> | 14 | #include <fmt/format.h> |
| 15 | #include "common/common_funcs.h" | ||
| 14 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 15 | #include "core/crypto/partition_data_manager.h" | 17 | #include "core/crypto/partition_data_manager.h" |
| 16 | #include "core/file_sys/vfs_types.h" | 18 | #include "core/file_sys/vfs_types.h" |
| @@ -30,7 +32,79 @@ constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180; | |||
| 30 | using Key128 = std::array<u8, 0x10>; | 32 | using Key128 = std::array<u8, 0x10>; |
| 31 | using Key256 = std::array<u8, 0x20>; | 33 | using Key256 = std::array<u8, 0x20>; |
| 32 | using SHA256Hash = std::array<u8, 0x20>; | 34 | using SHA256Hash = std::array<u8, 0x20>; |
| 33 | using TicketRaw = std::array<u8, 0x400>; | 35 | |
| 36 | enum class SignatureType { | ||
| 37 | RSA_4096_SHA1 = 0x10000, | ||
| 38 | RSA_2048_SHA1 = 0x10001, | ||
| 39 | ECDSA_SHA1 = 0x10002, | ||
| 40 | RSA_4096_SHA256 = 0x10003, | ||
| 41 | RSA_2048_SHA256 = 0x10004, | ||
| 42 | ECDSA_SHA256 = 0x10005, | ||
| 43 | }; | ||
| 44 | |||
| 45 | u64 GetSignatureTypeDataSize(SignatureType type); | ||
| 46 | u64 GetSignatureTypePaddingSize(SignatureType type); | ||
| 47 | |||
| 48 | enum class TitleKeyType : u8 { | ||
| 49 | Common = 0, | ||
| 50 | Personalized = 1, | ||
| 51 | }; | ||
| 52 | |||
| 53 | struct TicketData { | ||
| 54 | std::array<u8, 0x40> issuer; | ||
| 55 | union { | ||
| 56 | std::array<u8, 0x100> title_key_block; | ||
| 57 | |||
| 58 | struct { | ||
| 59 | Key128 title_key_common; | ||
| 60 | std::array<u8, 0xF0> title_key_common_pad; | ||
| 61 | }; | ||
| 62 | }; | ||
| 63 | |||
| 64 | INSERT_PADDING_BYTES(0x1); | ||
| 65 | TitleKeyType type; | ||
| 66 | INSERT_PADDING_BYTES(0x3); | ||
| 67 | u8 revision; | ||
| 68 | INSERT_PADDING_BYTES(0xA); | ||
| 69 | u64 ticket_id; | ||
| 70 | u64 device_id; | ||
| 71 | std::array<u8, 0x10> rights_id; | ||
| 72 | u32 account_id; | ||
| 73 | INSERT_PADDING_BYTES(0x14C); | ||
| 74 | }; | ||
| 75 | static_assert(sizeof(TicketData) == 0x2C0, "TicketData has incorrect size."); | ||
| 76 | |||
| 77 | struct RSA4096Ticket { | ||
| 78 | SignatureType sig_type; | ||
| 79 | std::array<u8, 0x200> sig_data; | ||
| 80 | INSERT_PADDING_BYTES(0x3C); | ||
| 81 | TicketData data; | ||
| 82 | }; | ||
| 83 | |||
| 84 | struct RSA2048Ticket { | ||
| 85 | SignatureType sig_type; | ||
| 86 | std::array<u8, 0x100> sig_data; | ||
| 87 | INSERT_PADDING_BYTES(0x3C); | ||
| 88 | TicketData data; | ||
| 89 | }; | ||
| 90 | |||
| 91 | struct ECDSATicket { | ||
| 92 | SignatureType sig_type; | ||
| 93 | std::array<u8, 0x3C> sig_data; | ||
| 94 | INSERT_PADDING_BYTES(0x40); | ||
| 95 | TicketData data; | ||
| 96 | }; | ||
| 97 | |||
| 98 | struct Ticket { | ||
| 99 | std::variant<RSA4096Ticket, RSA2048Ticket, ECDSATicket> data; | ||
| 100 | |||
| 101 | SignatureType GetSignatureType() const; | ||
| 102 | TicketData& GetData(); | ||
| 103 | const TicketData& GetData() const; | ||
| 104 | u64 GetSize() const; | ||
| 105 | |||
| 106 | static Ticket SynthesizeCommon(Key128 title_key, const std::array<u8, 0x10>& rights_id); | ||
| 107 | }; | ||
| 34 | 108 | ||
| 35 | static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); | 109 | static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); |
| 36 | static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big."); | 110 | static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big."); |
| @@ -43,6 +117,19 @@ struct RSAKeyPair { | |||
| 43 | std::array<u8, 4> exponent; | 117 | std::array<u8, 4> exponent; |
| 44 | }; | 118 | }; |
| 45 | 119 | ||
| 120 | template <size_t bit_size, size_t byte_size> | ||
| 121 | bool operator==(const RSAKeyPair<bit_size, byte_size>& lhs, | ||
| 122 | const RSAKeyPair<bit_size, byte_size>& rhs) { | ||
| 123 | return std::tie(lhs.encryption_key, lhs.decryption_key, lhs.modulus, lhs.exponent) == | ||
| 124 | std::tie(rhs.encryption_key, rhs.decryption_key, rhs.modulus, rhs.exponent); | ||
| 125 | } | ||
| 126 | |||
| 127 | template <size_t bit_size, size_t byte_size> | ||
| 128 | bool operator!=(const RSAKeyPair<bit_size, byte_size>& lhs, | ||
| 129 | const RSAKeyPair<bit_size, byte_size>& rhs) { | ||
| 130 | return !(lhs == rhs); | ||
| 131 | } | ||
| 132 | |||
| 46 | enum class KeyCategory : u8 { | 133 | enum class KeyCategory : u8 { |
| 47 | Standard, | 134 | Standard, |
| 48 | Title, | 135 | Title, |
| @@ -151,22 +238,35 @@ public: | |||
| 151 | 238 | ||
| 152 | static bool KeyFileExists(bool title); | 239 | static bool KeyFileExists(bool title); |
| 153 | 240 | ||
| 154 | // Call before using the sd seed to attempt to derive it if it dosen't exist. Needs system save | 241 | // Call before using the sd seed to attempt to derive it if it dosen't exist. Needs system |
| 155 | // 8*43 and the private file to exist. | 242 | // save 8*43 and the private file to exist. |
| 156 | void DeriveSDSeedLazy(); | 243 | void DeriveSDSeedLazy(); |
| 157 | 244 | ||
| 158 | bool BaseDeriveNecessary() const; | 245 | bool BaseDeriveNecessary() const; |
| 159 | void DeriveBase(); | 246 | void DeriveBase(); |
| 160 | void DeriveETicket(PartitionDataManager& data); | 247 | void DeriveETicket(PartitionDataManager& data); |
| 248 | void PopulateTickets(); | ||
| 249 | void SynthesizeTickets(); | ||
| 161 | 250 | ||
| 162 | void PopulateFromPartitionData(PartitionDataManager& data); | 251 | void PopulateFromPartitionData(PartitionDataManager& data); |
| 163 | 252 | ||
| 253 | const std::map<u128, Ticket>& GetCommonTickets() const; | ||
| 254 | const std::map<u128, Ticket>& GetPersonalizedTickets() const; | ||
| 255 | |||
| 256 | bool AddTicketCommon(Ticket raw); | ||
| 257 | bool AddTicketPersonalized(Ticket raw); | ||
| 258 | |||
| 164 | private: | 259 | private: |
| 165 | std::map<KeyIndex<S128KeyType>, Key128> s128_keys; | 260 | std::map<KeyIndex<S128KeyType>, Key128> s128_keys; |
| 166 | std::map<KeyIndex<S256KeyType>, Key256> s256_keys; | 261 | std::map<KeyIndex<S256KeyType>, Key256> s256_keys; |
| 167 | 262 | ||
| 263 | // Map from rights ID to ticket | ||
| 264 | std::map<u128, Ticket> common_tickets; | ||
| 265 | std::map<u128, Ticket> personal_tickets; | ||
| 266 | |||
| 168 | std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{}; | 267 | std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{}; |
| 169 | std::array<std::array<u8, 0x90>, 0x20> keyblobs{}; | 268 | std::array<std::array<u8, 0x90>, 0x20> keyblobs{}; |
| 269 | std::array<u8, 576> eticket_extended_kek{}; | ||
| 170 | 270 | ||
| 171 | bool dev_mode; | 271 | bool dev_mode; |
| 172 | void LoadFromFile(const std::string& filename, bool is_title_keys); | 272 | void LoadFromFile(const std::string& filename, bool is_title_keys); |
| @@ -178,6 +278,8 @@ private: | |||
| 178 | 278 | ||
| 179 | void DeriveGeneralPurposeKeys(std::size_t crypto_revision); | 279 | void DeriveGeneralPurposeKeys(std::size_t crypto_revision); |
| 180 | 280 | ||
| 281 | RSAKeyPair<2048> GetETicketRSAKey() const; | ||
| 282 | |||
| 181 | void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); | 283 | void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); |
| 182 | void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); | 284 | void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); |
| 183 | 285 | ||
| @@ -195,11 +297,11 @@ std::array<u8, 0x90> DecryptKeyblob(const std::array<u8, 0xB0>& encrypted_keyblo | |||
| 195 | std::optional<Key128> DeriveSDSeed(); | 297 | std::optional<Key128> DeriveSDSeed(); |
| 196 | Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); | 298 | Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys); |
| 197 | 299 | ||
| 198 | std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save); | 300 | std::vector<Ticket> GetTicketblob(const FileUtil::IOFile& ticket_save); |
| 199 | 301 | ||
| 200 | // Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset | 302 | // Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority |
| 201 | // 0x140-0x144 is zero) | 303 | // (offset 0x140-0x144 is zero) |
| 202 | std::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket, | 304 | std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, |
| 203 | const RSAKeyPair<2048>& eticket_extended_key); | 305 | const RSAKeyPair<2048>& eticket_extended_key); |
| 204 | 306 | ||
| 205 | } // namespace Core::Crypto | 307 | } // namespace Core::Crypto |
diff --git a/src/core/file_sys/system_archive/mii_model.cpp b/src/core/file_sys/system_archive/mii_model.cpp new file mode 100644 index 000000000..6a9add87c --- /dev/null +++ b/src/core/file_sys/system_archive/mii_model.cpp | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/file_sys/system_archive/mii_model.h" | ||
| 6 | #include "core/file_sys/vfs_vector.h" | ||
| 7 | |||
| 8 | namespace FileSys::SystemArchive { | ||
| 9 | |||
| 10 | namespace MiiModelData { | ||
| 11 | |||
| 12 | constexpr std::array<u8, 0x10> NFTR_STANDARD{'N', 'F', 'T', 'R', 0x01, 0x00, 0x00, 0x00, | ||
| 13 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
| 14 | constexpr std::array<u8, 0x10> NFSR_STANDARD{'N', 'F', 'S', 'R', 0x01, 0x00, 0x00, 0x00, | ||
| 15 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | ||
| 16 | |||
| 17 | constexpr auto TEXTURE_LOW_LINEAR = NFTR_STANDARD; | ||
| 18 | constexpr auto TEXTURE_LOW_SRGB = NFTR_STANDARD; | ||
| 19 | constexpr auto TEXTURE_MID_LINEAR = NFTR_STANDARD; | ||
| 20 | constexpr auto TEXTURE_MID_SRGB = NFTR_STANDARD; | ||
| 21 | constexpr auto SHAPE_HIGH = NFSR_STANDARD; | ||
| 22 | constexpr auto SHAPE_MID = NFSR_STANDARD; | ||
| 23 | |||
| 24 | } // namespace MiiModelData | ||
| 25 | |||
| 26 | VirtualDir MiiModel() { | ||
| 27 | auto out = std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, | ||
| 28 | std::vector<VirtualDir>{}, "data"); | ||
| 29 | |||
| 30 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_LINEAR.size()>>( | ||
| 31 | MiiModelData::TEXTURE_LOW_LINEAR, "NXTextureLowLinear.dat")); | ||
| 32 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_LOW_SRGB.size()>>( | ||
| 33 | MiiModelData::TEXTURE_LOW_SRGB, "NXTextureLowSRGB.dat")); | ||
| 34 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_LINEAR.size()>>( | ||
| 35 | MiiModelData::TEXTURE_MID_LINEAR, "NXTextureMidLinear.dat")); | ||
| 36 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::TEXTURE_MID_SRGB.size()>>( | ||
| 37 | MiiModelData::TEXTURE_MID_SRGB, "NXTextureMidSRGB.dat")); | ||
| 38 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_HIGH.size()>>( | ||
| 39 | MiiModelData::SHAPE_HIGH, "ShapeHigh.dat")); | ||
| 40 | out->AddFile(std::make_shared<ArrayVfsFile<MiiModelData::SHAPE_MID.size()>>( | ||
| 41 | MiiModelData::SHAPE_MID, "ShapeMid.dat")); | ||
| 42 | |||
| 43 | return std::move(out); | ||
| 44 | } | ||
| 45 | |||
| 46 | } // namespace FileSys::SystemArchive | ||
diff --git a/src/core/file_sys/system_archive/mii_model.h b/src/core/file_sys/system_archive/mii_model.h new file mode 100644 index 000000000..6c2d9398b --- /dev/null +++ b/src/core/file_sys/system_archive/mii_model.h | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | // Copyright 2019 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/file_sys/vfs_types.h" | ||
| 8 | |||
| 9 | namespace FileSys::SystemArchive { | ||
| 10 | |||
| 11 | VirtualDir MiiModel(); | ||
| 12 | |||
| 13 | } // namespace FileSys::SystemArchive | ||
diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp index c9722ed77..6d8445383 100644 --- a/src/core/file_sys/system_archive/system_archive.cpp +++ b/src/core/file_sys/system_archive/system_archive.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "core/file_sys/romfs.h" | 6 | #include "core/file_sys/romfs.h" |
| 7 | #include "core/file_sys/system_archive/mii_model.h" | ||
| 7 | #include "core/file_sys/system_archive/ng_word.h" | 8 | #include "core/file_sys/system_archive/ng_word.h" |
| 8 | #include "core/file_sys/system_archive/system_archive.h" | 9 | #include "core/file_sys/system_archive/system_archive.h" |
| 9 | #include "core/file_sys/system_archive/system_version.h" | 10 | #include "core/file_sys/system_archive/system_version.h" |
| @@ -24,7 +25,7 @@ struct SystemArchiveDescriptor { | |||
| 24 | constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES{{ | 25 | constexpr std::array<SystemArchiveDescriptor, SYSTEM_ARCHIVE_COUNT> SYSTEM_ARCHIVES{{ |
| 25 | {0x0100000000000800, "CertStore", nullptr}, | 26 | {0x0100000000000800, "CertStore", nullptr}, |
| 26 | {0x0100000000000801, "ErrorMessage", nullptr}, | 27 | {0x0100000000000801, "ErrorMessage", nullptr}, |
| 27 | {0x0100000000000802, "MiiModel", nullptr}, | 28 | {0x0100000000000802, "MiiModel", &MiiModel}, |
| 28 | {0x0100000000000803, "BrowserDll", nullptr}, | 29 | {0x0100000000000803, "BrowserDll", nullptr}, |
| 29 | {0x0100000000000804, "Help", nullptr}, | 30 | {0x0100000000000804, "Help", nullptr}, |
| 30 | {0x0100000000000805, "SharedFont", nullptr}, | 31 | {0x0100000000000805, "SharedFont", nullptr}, |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 40cea1e7c..c7af87073 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -296,12 +296,6 @@ ResultVal<VAddr> VMManager::SetHeapSize(u64 size) { | |||
| 296 | } | 296 | } |
| 297 | 297 | ||
| 298 | ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { | 298 | ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { |
| 299 | const auto end_addr = target + size; | ||
| 300 | const auto last_addr = end_addr - 1; | ||
| 301 | VAddr cur_addr = target; | ||
| 302 | |||
| 303 | ResultCode result = RESULT_SUCCESS; | ||
| 304 | |||
| 305 | // Check how much memory we've already mapped. | 299 | // Check how much memory we've already mapped. |
| 306 | const auto mapped_size_result = SizeOfAllocatedVMAsInRange(target, size); | 300 | const auto mapped_size_result = SizeOfAllocatedVMAsInRange(target, size); |
| 307 | if (mapped_size_result.Failed()) { | 301 | if (mapped_size_result.Failed()) { |
| @@ -324,13 +318,16 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { | |||
| 324 | 318 | ||
| 325 | // Keep track of the memory regions we unmap. | 319 | // Keep track of the memory regions we unmap. |
| 326 | std::vector<std::pair<u64, u64>> mapped_regions; | 320 | std::vector<std::pair<u64, u64>> mapped_regions; |
| 321 | ResultCode result = RESULT_SUCCESS; | ||
| 327 | 322 | ||
| 328 | // Iterate, trying to map memory. | 323 | // Iterate, trying to map memory. |
| 329 | { | 324 | { |
| 330 | cur_addr = target; | 325 | const auto end_addr = target + size; |
| 326 | const auto last_addr = end_addr - 1; | ||
| 327 | VAddr cur_addr = target; | ||
| 331 | 328 | ||
| 332 | auto iter = FindVMA(target); | 329 | auto iter = FindVMA(target); |
| 333 | ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end"); | 330 | ASSERT(iter != vma_map.end()); |
| 334 | 331 | ||
| 335 | while (true) { | 332 | while (true) { |
| 336 | const auto& vma = iter->second; | 333 | const auto& vma = iter->second; |
| @@ -342,7 +339,7 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { | |||
| 342 | const auto map_size = std::min(end_addr - cur_addr, vma_end - cur_addr); | 339 | const auto map_size = std::min(end_addr - cur_addr, vma_end - cur_addr); |
| 343 | if (vma.state == MemoryState::Unmapped) { | 340 | if (vma.state == MemoryState::Unmapped) { |
| 344 | const auto map_res = | 341 | const auto map_res = |
| 345 | MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size, 0), 0, | 342 | MapMemoryBlock(cur_addr, std::make_shared<PhysicalMemory>(map_size), 0, |
| 346 | map_size, MemoryState::Heap, VMAPermission::ReadWrite); | 343 | map_size, MemoryState::Heap, VMAPermission::ReadWrite); |
| 347 | result = map_res.Code(); | 344 | result = map_res.Code(); |
| 348 | if (result.IsError()) { | 345 | if (result.IsError()) { |
| @@ -360,7 +357,7 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { | |||
| 360 | // Advance to the next block. | 357 | // Advance to the next block. |
| 361 | cur_addr = vma_end; | 358 | cur_addr = vma_end; |
| 362 | iter = FindVMA(cur_addr); | 359 | iter = FindVMA(cur_addr); |
| 363 | ASSERT_MSG(iter != vma_map.end(), "MapPhysicalMemory iter != end"); | 360 | ASSERT(iter != vma_map.end()); |
| 364 | } | 361 | } |
| 365 | } | 362 | } |
| 366 | 363 | ||
| @@ -368,7 +365,7 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { | |||
| 368 | if (result.IsError()) { | 365 | if (result.IsError()) { |
| 369 | for (const auto [unmap_address, unmap_size] : mapped_regions) { | 366 | for (const auto [unmap_address, unmap_size] : mapped_regions) { |
| 370 | ASSERT_MSG(UnmapRange(unmap_address, unmap_size).IsSuccess(), | 367 | ASSERT_MSG(UnmapRange(unmap_address, unmap_size).IsSuccess(), |
| 371 | "MapPhysicalMemory un-map on error"); | 368 | "Failed to unmap memory range."); |
| 372 | } | 369 | } |
| 373 | 370 | ||
| 374 | return result; | 371 | return result; |
| @@ -381,12 +378,6 @@ ResultCode VMManager::MapPhysicalMemory(VAddr target, u64 size) { | |||
| 381 | } | 378 | } |
| 382 | 379 | ||
| 383 | ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) { | 380 | ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) { |
| 384 | const auto end_addr = target + size; | ||
| 385 | const auto last_addr = end_addr - 1; | ||
| 386 | VAddr cur_addr = target; | ||
| 387 | |||
| 388 | ResultCode result = RESULT_SUCCESS; | ||
| 389 | |||
| 390 | // Check how much memory is currently mapped. | 381 | // Check how much memory is currently mapped. |
| 391 | const auto mapped_size_result = SizeOfUnmappablePhysicalMemoryInRange(target, size); | 382 | const auto mapped_size_result = SizeOfUnmappablePhysicalMemoryInRange(target, size); |
| 392 | if (mapped_size_result.Failed()) { | 383 | if (mapped_size_result.Failed()) { |
| @@ -401,13 +392,16 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) { | |||
| 401 | 392 | ||
| 402 | // Keep track of the memory regions we unmap. | 393 | // Keep track of the memory regions we unmap. |
| 403 | std::vector<std::pair<u64, u64>> unmapped_regions; | 394 | std::vector<std::pair<u64, u64>> unmapped_regions; |
| 395 | ResultCode result = RESULT_SUCCESS; | ||
| 404 | 396 | ||
| 405 | // Try to unmap regions. | 397 | // Try to unmap regions. |
| 406 | { | 398 | { |
| 407 | cur_addr = target; | 399 | const auto end_addr = target + size; |
| 400 | const auto last_addr = end_addr - 1; | ||
| 401 | VAddr cur_addr = target; | ||
| 408 | 402 | ||
| 409 | auto iter = FindVMA(target); | 403 | auto iter = FindVMA(target); |
| 410 | ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end"); | 404 | ASSERT(iter != vma_map.end()); |
| 411 | 405 | ||
| 412 | while (true) { | 406 | while (true) { |
| 413 | const auto& vma = iter->second; | 407 | const auto& vma = iter->second; |
| @@ -434,7 +428,7 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) { | |||
| 434 | // Advance to the next block. | 428 | // Advance to the next block. |
| 435 | cur_addr = vma_end; | 429 | cur_addr = vma_end; |
| 436 | iter = FindVMA(cur_addr); | 430 | iter = FindVMA(cur_addr); |
| 437 | ASSERT_MSG(iter != vma_map.end(), "UnmapPhysicalMemory iter != end"); | 431 | ASSERT(iter != vma_map.end()); |
| 438 | } | 432 | } |
| 439 | } | 433 | } |
| 440 | 434 | ||
| @@ -443,10 +437,12 @@ ResultCode VMManager::UnmapPhysicalMemory(VAddr target, u64 size) { | |||
| 443 | if (result.IsError()) { | 437 | if (result.IsError()) { |
| 444 | for (const auto [map_address, map_size] : unmapped_regions) { | 438 | for (const auto [map_address, map_size] : unmapped_regions) { |
| 445 | const auto remap_res = | 439 | const auto remap_res = |
| 446 | MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size, 0), 0, | 440 | MapMemoryBlock(map_address, std::make_shared<PhysicalMemory>(map_size), 0, map_size, |
| 447 | map_size, MemoryState::Heap, VMAPermission::None); | 441 | MemoryState::Heap, VMAPermission::None); |
| 448 | ASSERT_MSG(remap_res.Succeeded(), "UnmapPhysicalMemory re-map on error"); | 442 | ASSERT_MSG(remap_res.Succeeded(), "Failed to remap a memory block."); |
| 449 | } | 443 | } |
| 444 | |||
| 445 | return result; | ||
| 450 | } | 446 | } |
| 451 | 447 | ||
| 452 | // Update mapped amount | 448 | // Update mapped amount |
| @@ -757,20 +753,26 @@ void VMManager::MergeAdjacentVMA(VirtualMemoryArea& left, const VirtualMemoryAre | |||
| 757 | // Always merge allocated memory blocks, even when they don't share the same backing block. | 753 | // Always merge allocated memory blocks, even when they don't share the same backing block. |
| 758 | if (left.type == VMAType::AllocatedMemoryBlock && | 754 | if (left.type == VMAType::AllocatedMemoryBlock && |
| 759 | (left.backing_block != right.backing_block || left.offset + left.size != right.offset)) { | 755 | (left.backing_block != right.backing_block || left.offset + left.size != right.offset)) { |
| 756 | const auto right_begin = right.backing_block->begin() + right.offset; | ||
| 757 | const auto right_end = right_begin + right.size; | ||
| 758 | |||
| 760 | // Check if we can save work. | 759 | // Check if we can save work. |
| 761 | if (left.offset == 0 && left.size == left.backing_block->size()) { | 760 | if (left.offset == 0 && left.size == left.backing_block->size()) { |
| 762 | // Fast case: left is an entire backing block. | 761 | // Fast case: left is an entire backing block. |
| 763 | left.backing_block->insert(left.backing_block->end(), | 762 | left.backing_block->insert(left.backing_block->end(), right_begin, right_end); |
| 764 | right.backing_block->begin() + right.offset, | ||
| 765 | right.backing_block->begin() + right.offset + right.size); | ||
| 766 | } else { | 763 | } else { |
| 767 | // Slow case: make a new memory block for left and right. | 764 | // Slow case: make a new memory block for left and right. |
| 765 | const auto left_begin = left.backing_block->begin() + left.offset; | ||
| 766 | const auto left_end = left_begin + left.size; | ||
| 767 | const auto left_size = static_cast<std::size_t>(std::distance(left_begin, left_end)); | ||
| 768 | const auto right_size = static_cast<std::size_t>(std::distance(right_begin, right_end)); | ||
| 769 | |||
| 768 | auto new_memory = std::make_shared<PhysicalMemory>(); | 770 | auto new_memory = std::make_shared<PhysicalMemory>(); |
| 769 | new_memory->insert(new_memory->end(), left.backing_block->begin() + left.offset, | 771 | new_memory->reserve(left_size + right_size); |
| 770 | left.backing_block->begin() + left.offset + left.size); | 772 | new_memory->insert(new_memory->end(), left_begin, left_end); |
| 771 | new_memory->insert(new_memory->end(), right.backing_block->begin() + right.offset, | 773 | new_memory->insert(new_memory->end(), right_begin, right_end); |
| 772 | right.backing_block->begin() + right.offset + right.size); | 774 | |
| 773 | left.backing_block = new_memory; | 775 | left.backing_block = std::move(new_memory); |
| 774 | left.offset = 0; | 776 | left.offset = 0; |
| 775 | } | 777 | } |
| 776 | 778 | ||
| @@ -965,7 +967,7 @@ ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address, | |||
| 965 | 967 | ||
| 966 | VAddr cur_addr = address; | 968 | VAddr cur_addr = address; |
| 967 | auto iter = FindVMA(cur_addr); | 969 | auto iter = FindVMA(cur_addr); |
| 968 | ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end"); | 970 | ASSERT(iter != vma_map.end()); |
| 969 | 971 | ||
| 970 | while (true) { | 972 | while (true) { |
| 971 | const auto& vma = iter->second; | 973 | const auto& vma = iter->second; |
| @@ -986,7 +988,7 @@ ResultVal<std::size_t> VMManager::SizeOfAllocatedVMAsInRange(VAddr address, | |||
| 986 | // Advance to the next block. | 988 | // Advance to the next block. |
| 987 | cur_addr = vma_end; | 989 | cur_addr = vma_end; |
| 988 | iter = std::next(iter); | 990 | iter = std::next(iter); |
| 989 | ASSERT_MSG(iter != vma_map.end(), "SizeOfAllocatedVMAsInRange iter != end"); | 991 | ASSERT(iter != vma_map.end()); |
| 990 | } | 992 | } |
| 991 | 993 | ||
| 992 | return MakeResult(mapped_size); | 994 | return MakeResult(mapped_size); |
| @@ -1000,7 +1002,7 @@ ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr ad | |||
| 1000 | 1002 | ||
| 1001 | VAddr cur_addr = address; | 1003 | VAddr cur_addr = address; |
| 1002 | auto iter = FindVMA(cur_addr); | 1004 | auto iter = FindVMA(cur_addr); |
| 1003 | ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end"); | 1005 | ASSERT(iter != vma_map.end()); |
| 1004 | 1006 | ||
| 1005 | while (true) { | 1007 | while (true) { |
| 1006 | const auto& vma = iter->second; | 1008 | const auto& vma = iter->second; |
| @@ -1029,7 +1031,7 @@ ResultVal<std::size_t> VMManager::SizeOfUnmappablePhysicalMemoryInRange(VAddr ad | |||
| 1029 | // Advance to the next block. | 1031 | // Advance to the next block. |
| 1030 | cur_addr = vma_end; | 1032 | cur_addr = vma_end; |
| 1031 | iter = std::next(iter); | 1033 | iter = std::next(iter); |
| 1032 | ASSERT_MSG(iter != vma_map.end(), "SizeOfUnmappablePhysicalMemoryInRange iter != end"); | 1034 | ASSERT(iter != vma_map.end()); |
| 1033 | } | 1035 | } |
| 1034 | 1036 | ||
| 1035 | return MakeResult(mapped_size); | 1037 | return MakeResult(mapped_size); |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index b18cde619..850a7ebc3 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -454,8 +454,8 @@ public: | |||
| 454 | 454 | ||
| 455 | /// Maps memory at a given address. | 455 | /// Maps memory at a given address. |
| 456 | /// | 456 | /// |
| 457 | /// @param addr The virtual address to map memory at. | 457 | /// @param target The virtual address to map memory at. |
| 458 | /// @param size The amount of memory to map. | 458 | /// @param size The amount of memory to map. |
| 459 | /// | 459 | /// |
| 460 | /// @note The destination address must lie within the Map region. | 460 | /// @note The destination address must lie within the Map region. |
| 461 | /// | 461 | /// |
| @@ -468,8 +468,8 @@ public: | |||
| 468 | 468 | ||
| 469 | /// Unmaps memory at a given address. | 469 | /// Unmaps memory at a given address. |
| 470 | /// | 470 | /// |
| 471 | /// @param addr The virtual address to unmap memory at. | 471 | /// @param target The virtual address to unmap memory at. |
| 472 | /// @param size The amount of memory to unmap. | 472 | /// @param size The amount of memory to unmap. |
| 473 | /// | 473 | /// |
| 474 | /// @note The destination address must lie within the Map region. | 474 | /// @note The destination address must lie within the Map region. |
| 475 | /// | 475 | /// |
diff --git a/src/core/hle/service/es/es.cpp b/src/core/hle/service/es/es.cpp index 6701cb913..af70d174d 100644 --- a/src/core/hle/service/es/es.cpp +++ b/src/core/hle/service/es/es.cpp | |||
| @@ -2,32 +2,37 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "core/crypto/key_manager.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | ||
| 5 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 6 | 8 | ||
| 7 | namespace Service::ES { | 9 | namespace Service::ES { |
| 8 | 10 | ||
| 11 | constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::ETicket, 2}; | ||
| 12 | constexpr ResultCode ERROR_INVALID_RIGHTS_ID{ErrorModule::ETicket, 3}; | ||
| 13 | |||
| 9 | class ETicket final : public ServiceFramework<ETicket> { | 14 | class ETicket final : public ServiceFramework<ETicket> { |
| 10 | public: | 15 | public: |
| 11 | explicit ETicket() : ServiceFramework{"es"} { | 16 | explicit ETicket() : ServiceFramework{"es"} { |
| 12 | // clang-format off | 17 | // clang-format off |
| 13 | static const FunctionInfo functions[] = { | 18 | static const FunctionInfo functions[] = { |
| 14 | {1, nullptr, "ImportTicket"}, | 19 | {1, &ETicket::ImportTicket, "ImportTicket"}, |
| 15 | {2, nullptr, "ImportTicketCertificateSet"}, | 20 | {2, nullptr, "ImportTicketCertificateSet"}, |
| 16 | {3, nullptr, "DeleteTicket"}, | 21 | {3, nullptr, "DeleteTicket"}, |
| 17 | {4, nullptr, "DeletePersonalizedTicket"}, | 22 | {4, nullptr, "DeletePersonalizedTicket"}, |
| 18 | {5, nullptr, "DeleteAllCommonTicket"}, | 23 | {5, nullptr, "DeleteAllCommonTicket"}, |
| 19 | {6, nullptr, "DeleteAllPersonalizedTicket"}, | 24 | {6, nullptr, "DeleteAllPersonalizedTicket"}, |
| 20 | {7, nullptr, "DeleteAllPersonalizedTicketEx"}, | 25 | {7, nullptr, "DeleteAllPersonalizedTicketEx"}, |
| 21 | {8, nullptr, "GetTitleKey"}, | 26 | {8, &ETicket::GetTitleKey, "GetTitleKey"}, |
| 22 | {9, nullptr, "CountCommonTicket"}, | 27 | {9, &ETicket::CountCommonTicket, "CountCommonTicket"}, |
| 23 | {10, nullptr, "CountPersonalizedTicket"}, | 28 | {10, &ETicket::CountPersonalizedTicket, "CountPersonalizedTicket"}, |
| 24 | {11, nullptr, "ListCommonTicket"}, | 29 | {11, &ETicket::ListCommonTicket, "ListCommonTicket"}, |
| 25 | {12, nullptr, "ListPersonalizedTicket"}, | 30 | {12, &ETicket::ListPersonalizedTicket, "ListPersonalizedTicket"}, |
| 26 | {13, nullptr, "ListMissingPersonalizedTicket"}, | 31 | {13, nullptr, "ListMissingPersonalizedTicket"}, |
| 27 | {14, nullptr, "GetCommonTicketSize"}, | 32 | {14, &ETicket::GetCommonTicketSize, "GetCommonTicketSize"}, |
| 28 | {15, nullptr, "GetPersonalizedTicketSize"}, | 33 | {15, &ETicket::GetPersonalizedTicketSize, "GetPersonalizedTicketSize"}, |
| 29 | {16, nullptr, "GetCommonTicketData"}, | 34 | {16, &ETicket::GetCommonTicketData, "GetCommonTicketData"}, |
| 30 | {17, nullptr, "GetPersonalizedTicketData"}, | 35 | {17, &ETicket::GetPersonalizedTicketData, "GetPersonalizedTicketData"}, |
| 31 | {18, nullptr, "OwnTicket"}, | 36 | {18, nullptr, "OwnTicket"}, |
| 32 | {19, nullptr, "GetTicketInfo"}, | 37 | {19, nullptr, "GetTicketInfo"}, |
| 33 | {20, nullptr, "ListLightTicketInfo"}, | 38 | {20, nullptr, "ListLightTicketInfo"}, |
| @@ -51,7 +56,212 @@ public: | |||
| 51 | }; | 56 | }; |
| 52 | // clang-format on | 57 | // clang-format on |
| 53 | RegisterHandlers(functions); | 58 | RegisterHandlers(functions); |
| 59 | |||
| 60 | keys.PopulateTickets(); | ||
| 61 | keys.SynthesizeTickets(); | ||
| 62 | } | ||
| 63 | |||
| 64 | private: | ||
| 65 | bool CheckRightsId(Kernel::HLERequestContext& ctx, const u128& rights_id) { | ||
| 66 | if (rights_id == u128{}) { | ||
| 67 | LOG_ERROR(Service_ETicket, "The rights ID was invalid!"); | ||
| 68 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 69 | rb.Push(ERROR_INVALID_RIGHTS_ID); | ||
| 70 | return false; | ||
| 71 | } | ||
| 72 | |||
| 73 | return true; | ||
| 74 | } | ||
| 75 | |||
| 76 | void ImportTicket(Kernel::HLERequestContext& ctx) { | ||
| 77 | IPC::RequestParser rp{ctx}; | ||
| 78 | const auto ticket = ctx.ReadBuffer(); | ||
| 79 | const auto cert = ctx.ReadBuffer(1); | ||
| 80 | |||
| 81 | if (ticket.size() < sizeof(Core::Crypto::Ticket)) { | ||
| 82 | LOG_ERROR(Service_ETicket, "The input buffer is not large enough!"); | ||
| 83 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 84 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 85 | return; | ||
| 86 | } | ||
| 87 | |||
| 88 | Core::Crypto::Ticket raw{}; | ||
| 89 | std::memcpy(&raw, ticket.data(), sizeof(Core::Crypto::Ticket)); | ||
| 90 | |||
| 91 | if (!keys.AddTicketPersonalized(raw)) { | ||
| 92 | LOG_ERROR(Service_ETicket, "The ticket could not be imported!"); | ||
| 93 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 94 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 95 | return; | ||
| 96 | } | ||
| 97 | |||
| 98 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 99 | rb.Push(RESULT_SUCCESS); | ||
| 100 | } | ||
| 101 | |||
| 102 | void GetTitleKey(Kernel::HLERequestContext& ctx) { | ||
| 103 | IPC::RequestParser rp{ctx}; | ||
| 104 | const auto rights_id = rp.PopRaw<u128>(); | ||
| 105 | |||
| 106 | LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); | ||
| 107 | |||
| 108 | if (!CheckRightsId(ctx, rights_id)) | ||
| 109 | return; | ||
| 110 | |||
| 111 | const auto key = | ||
| 112 | keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); | ||
| 113 | |||
| 114 | if (key == Core::Crypto::Key128{}) { | ||
| 115 | LOG_ERROR(Service_ETicket, | ||
| 116 | "The titlekey doesn't exist in the KeyManager or the rights ID was invalid!"); | ||
| 117 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 118 | rb.Push(ERROR_INVALID_RIGHTS_ID); | ||
| 119 | return; | ||
| 120 | } | ||
| 121 | |||
| 122 | ctx.WriteBuffer(key.data(), key.size()); | ||
| 123 | |||
| 124 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 125 | rb.Push(RESULT_SUCCESS); | ||
| 126 | } | ||
| 127 | |||
| 128 | void CountCommonTicket(Kernel::HLERequestContext& ctx) { | ||
| 129 | LOG_DEBUG(Service_ETicket, "called"); | ||
| 130 | |||
| 131 | const auto count = keys.GetCommonTickets().size(); | ||
| 132 | |||
| 133 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 134 | rb.Push(RESULT_SUCCESS); | ||
| 135 | rb.Push<u32>(count); | ||
| 136 | } | ||
| 137 | |||
| 138 | void CountPersonalizedTicket(Kernel::HLERequestContext& ctx) { | ||
| 139 | LOG_DEBUG(Service_ETicket, "called"); | ||
| 140 | |||
| 141 | const auto count = keys.GetPersonalizedTickets().size(); | ||
| 142 | |||
| 143 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 144 | rb.Push(RESULT_SUCCESS); | ||
| 145 | rb.Push<u32>(count); | ||
| 146 | } | ||
| 147 | |||
| 148 | void ListCommonTicket(Kernel::HLERequestContext& ctx) { | ||
| 149 | u32 out_entries; | ||
| 150 | if (keys.GetCommonTickets().empty()) | ||
| 151 | out_entries = 0; | ||
| 152 | else | ||
| 153 | out_entries = ctx.GetWriteBufferSize() / sizeof(u128); | ||
| 154 | |||
| 155 | LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); | ||
| 156 | |||
| 157 | keys.PopulateTickets(); | ||
| 158 | const auto tickets = keys.GetCommonTickets(); | ||
| 159 | std::vector<u128> ids; | ||
| 160 | std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), | ||
| 161 | [](const auto& pair) { return pair.first; }); | ||
| 162 | |||
| 163 | out_entries = std::min<u32>(ids.size(), out_entries); | ||
| 164 | ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); | ||
| 165 | |||
| 166 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 167 | rb.Push(RESULT_SUCCESS); | ||
| 168 | rb.Push<u32>(out_entries); | ||
| 54 | } | 169 | } |
| 170 | |||
| 171 | void ListPersonalizedTicket(Kernel::HLERequestContext& ctx) { | ||
| 172 | u32 out_entries; | ||
| 173 | if (keys.GetPersonalizedTickets().empty()) | ||
| 174 | out_entries = 0; | ||
| 175 | else | ||
| 176 | out_entries = ctx.GetWriteBufferSize() / sizeof(u128); | ||
| 177 | |||
| 178 | LOG_DEBUG(Service_ETicket, "called, entries={:016X}", out_entries); | ||
| 179 | |||
| 180 | keys.PopulateTickets(); | ||
| 181 | const auto tickets = keys.GetPersonalizedTickets(); | ||
| 182 | std::vector<u128> ids; | ||
| 183 | std::transform(tickets.begin(), tickets.end(), std::back_inserter(ids), | ||
| 184 | [](const auto& pair) { return pair.first; }); | ||
| 185 | |||
| 186 | out_entries = std::min<u32>(ids.size(), out_entries); | ||
| 187 | ctx.WriteBuffer(ids.data(), out_entries * sizeof(u128)); | ||
| 188 | |||
| 189 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 190 | rb.Push(RESULT_SUCCESS); | ||
| 191 | rb.Push<u32>(out_entries); | ||
| 192 | } | ||
| 193 | |||
| 194 | void GetCommonTicketSize(Kernel::HLERequestContext& ctx) { | ||
| 195 | IPC::RequestParser rp{ctx}; | ||
| 196 | const auto rights_id = rp.PopRaw<u128>(); | ||
| 197 | |||
| 198 | LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); | ||
| 199 | |||
| 200 | if (!CheckRightsId(ctx, rights_id)) | ||
| 201 | return; | ||
| 202 | |||
| 203 | const auto ticket = keys.GetCommonTickets().at(rights_id); | ||
| 204 | |||
| 205 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 206 | rb.Push(RESULT_SUCCESS); | ||
| 207 | rb.Push<u64>(ticket.GetSize()); | ||
| 208 | } | ||
| 209 | |||
| 210 | void GetPersonalizedTicketSize(Kernel::HLERequestContext& ctx) { | ||
| 211 | IPC::RequestParser rp{ctx}; | ||
| 212 | const auto rights_id = rp.PopRaw<u128>(); | ||
| 213 | |||
| 214 | LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); | ||
| 215 | |||
| 216 | if (!CheckRightsId(ctx, rights_id)) | ||
| 217 | return; | ||
| 218 | |||
| 219 | const auto ticket = keys.GetPersonalizedTickets().at(rights_id); | ||
| 220 | |||
| 221 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 222 | rb.Push(RESULT_SUCCESS); | ||
| 223 | rb.Push<u64>(ticket.GetSize()); | ||
| 224 | } | ||
| 225 | |||
| 226 | void GetCommonTicketData(Kernel::HLERequestContext& ctx) { | ||
| 227 | IPC::RequestParser rp{ctx}; | ||
| 228 | const auto rights_id = rp.PopRaw<u128>(); | ||
| 229 | |||
| 230 | LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); | ||
| 231 | |||
| 232 | if (!CheckRightsId(ctx, rights_id)) | ||
| 233 | return; | ||
| 234 | |||
| 235 | const auto ticket = keys.GetCommonTickets().at(rights_id); | ||
| 236 | |||
| 237 | const auto write_size = std::min<u64>(ticket.GetSize(), ctx.GetWriteBufferSize()); | ||
| 238 | ctx.WriteBuffer(&ticket, write_size); | ||
| 239 | |||
| 240 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 241 | rb.Push(RESULT_SUCCESS); | ||
| 242 | rb.Push<u64>(write_size); | ||
| 243 | } | ||
| 244 | |||
| 245 | void GetPersonalizedTicketData(Kernel::HLERequestContext& ctx) { | ||
| 246 | IPC::RequestParser rp{ctx}; | ||
| 247 | const auto rights_id = rp.PopRaw<u128>(); | ||
| 248 | |||
| 249 | LOG_DEBUG(Service_ETicket, "called, rights_id={:016X}{:016X}", rights_id[1], rights_id[0]); | ||
| 250 | |||
| 251 | if (!CheckRightsId(ctx, rights_id)) | ||
| 252 | return; | ||
| 253 | |||
| 254 | const auto ticket = keys.GetPersonalizedTickets().at(rights_id); | ||
| 255 | |||
| 256 | const auto write_size = std::min<u64>(ticket.GetSize(), ctx.GetWriteBufferSize()); | ||
| 257 | ctx.WriteBuffer(&ticket, write_size); | ||
| 258 | |||
| 259 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 260 | rb.Push(RESULT_SUCCESS); | ||
| 261 | rb.Push<u64>(write_size); | ||
| 262 | } | ||
| 263 | |||
| 264 | Core::Crypto::KeyManager keys; | ||
| 55 | }; | 265 | }; |
| 56 | 266 | ||
| 57 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 267 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index bb09ecd52..01d89f47d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -537,8 +537,7 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers( | |||
| 537 | texture_cache.MarkDepthBufferInUse(); | 537 | texture_cache.MarkDepthBufferInUse(); |
| 538 | 538 | ||
| 539 | fbkey.zeta = depth_surface; | 539 | fbkey.zeta = depth_surface; |
| 540 | fbkey.stencil_enable = regs.stencil_enable && | 540 | fbkey.stencil_enable = depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil; |
| 541 | depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil; | ||
| 542 | } | 541 | } |
| 543 | 542 | ||
| 544 | texture_cache.GuardRenderTargets(false); | 543 | texture_cache.GuardRenderTargets(false); |
| @@ -577,16 +576,15 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(OpenGLState& current_state, boo | |||
| 577 | if (depth_surface) { | 576 | if (depth_surface) { |
| 578 | const auto& params = depth_surface->GetSurfaceParams(); | 577 | const auto& params = depth_surface->GetSurfaceParams(); |
| 579 | switch (params.type) { | 578 | switch (params.type) { |
| 580 | case VideoCore::Surface::SurfaceType::Depth: { | 579 | case VideoCore::Surface::SurfaceType::Depth: |
| 581 | depth_surface->Attach(GL_DEPTH_ATTACHMENT, GL_DRAW_FRAMEBUFFER); | 580 | depth_surface->Attach(GL_DEPTH_ATTACHMENT, GL_DRAW_FRAMEBUFFER); |
| 582 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); | 581 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0); |
| 583 | break; | 582 | break; |
| 584 | } | 583 | case VideoCore::Surface::SurfaceType::DepthStencil: |
| 585 | case VideoCore::Surface::SurfaceType::DepthStencil: { | 584 | depth_surface->Attach(GL_DEPTH_STENCIL_ATTACHMENT, GL_DRAW_FRAMEBUFFER); |
| 586 | depth_surface->Attach(GL_DEPTH_ATTACHMENT, GL_DRAW_FRAMEBUFFER); | ||
| 587 | break; | 585 | break; |
| 588 | } | 586 | default: |
| 589 | default: { UNIMPLEMENTED(); } | 587 | UNIMPLEMENTED(); |
| 590 | } | 588 | } |
| 591 | } else { | 589 | } else { |
| 592 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, | 590 | glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, |
| @@ -639,6 +637,7 @@ void RasterizerOpenGL::Clear() { | |||
| 639 | ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); | 637 | ASSERT_MSG(regs.zeta_enable != 0, "Tried to clear stencil but buffer is not enabled!"); |
| 640 | use_stencil = true; | 638 | use_stencil = true; |
| 641 | clear_state.stencil.test_enabled = true; | 639 | clear_state.stencil.test_enabled = true; |
| 640 | |||
| 642 | if (regs.clear_flags.stencil) { | 641 | if (regs.clear_flags.stencil) { |
| 643 | // Stencil affects the clear so fill it with the used masks | 642 | // Stencil affects the clear so fill it with the used masks |
| 644 | clear_state.stencil.front.test_func = GL_ALWAYS; | 643 | clear_state.stencil.front.test_func = GL_ALWAYS; |
| @@ -1119,9 +1118,12 @@ void RasterizerOpenGL::SyncStencilTestState() { | |||
| 1119 | if (!maxwell3d.dirty.stencil_test) { | 1118 | if (!maxwell3d.dirty.stencil_test) { |
| 1120 | return; | 1119 | return; |
| 1121 | } | 1120 | } |
| 1122 | const auto& regs = maxwell3d.regs; | 1121 | maxwell3d.dirty.stencil_test = false; |
| 1123 | 1122 | ||
| 1123 | const auto& regs = maxwell3d.regs; | ||
| 1124 | state.stencil.test_enabled = regs.stencil_enable != 0; | 1124 | state.stencil.test_enabled = regs.stencil_enable != 0; |
| 1125 | state.MarkDirtyStencilState(); | ||
| 1126 | |||
| 1125 | if (!regs.stencil_enable) { | 1127 | if (!regs.stencil_enable) { |
| 1126 | return; | 1128 | return; |
| 1127 | } | 1129 | } |
| @@ -1150,8 +1152,6 @@ void RasterizerOpenGL::SyncStencilTestState() { | |||
| 1150 | state.stencil.back.action_depth_fail = GL_KEEP; | 1152 | state.stencil.back.action_depth_fail = GL_KEEP; |
| 1151 | state.stencil.back.action_depth_pass = GL_KEEP; | 1153 | state.stencil.back.action_depth_pass = GL_KEEP; |
| 1152 | } | 1154 | } |
| 1153 | state.MarkDirtyStencilState(); | ||
| 1154 | maxwell3d.dirty.stencil_test = false; | ||
| 1155 | } | 1155 | } |
| 1156 | 1156 | ||
| 1157 | void RasterizerOpenGL::SyncColorMask() { | 1157 | void RasterizerOpenGL::SyncColorMask() { |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index cf6a5cddf..909ccb82c 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -348,23 +348,16 @@ Shader CachedShader::CreateKernelFromCache(const ShaderParameters& params, | |||
| 348 | } | 348 | } |
| 349 | 349 | ||
| 350 | std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVariant& variant) { | 350 | std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVariant& variant) { |
| 351 | GLuint handle{}; | 351 | const auto [entry, is_cache_miss] = programs.try_emplace(variant); |
| 352 | if (program_type == ProgramType::Geometry) { | 352 | auto& program = entry->second; |
| 353 | handle = GetGeometryShader(variant); | 353 | if (is_cache_miss) { |
| 354 | } else { | 354 | program = TryLoadProgram(variant); |
| 355 | const auto [entry, is_cache_miss] = programs.try_emplace(variant); | 355 | if (!program) { |
| 356 | auto& program = entry->second; | 356 | program = SpecializeShader(code, entries, program_type, variant); |
| 357 | if (is_cache_miss) { | 357 | disk_cache.SaveUsage(GetUsage(variant)); |
| 358 | program = TryLoadProgram(variant); | ||
| 359 | if (!program) { | ||
| 360 | program = SpecializeShader(code, entries, program_type, variant); | ||
| 361 | disk_cache.SaveUsage(GetUsage(variant)); | ||
| 362 | } | ||
| 363 | |||
| 364 | LabelGLObject(GL_PROGRAM, program->handle, cpu_addr); | ||
| 365 | } | 358 | } |
| 366 | 359 | ||
| 367 | handle = program->handle; | 360 | LabelGLObject(GL_PROGRAM, program->handle, cpu_addr); |
| 368 | } | 361 | } |
| 369 | 362 | ||
| 370 | auto base_bindings = variant.base_bindings; | 363 | auto base_bindings = variant.base_bindings; |
| @@ -375,52 +368,9 @@ std::tuple<GLuint, BaseBindings> CachedShader::GetProgramHandle(const ProgramVar | |||
| 375 | base_bindings.gmem += static_cast<u32>(entries.global_memory_entries.size()); | 368 | base_bindings.gmem += static_cast<u32>(entries.global_memory_entries.size()); |
| 376 | base_bindings.sampler += static_cast<u32>(entries.samplers.size()); | 369 | base_bindings.sampler += static_cast<u32>(entries.samplers.size()); |
| 377 | 370 | ||
| 378 | return {handle, base_bindings}; | 371 | return {program->handle, base_bindings}; |
| 379 | } | 372 | } |
| 380 | 373 | ||
| 381 | GLuint CachedShader::GetGeometryShader(const ProgramVariant& variant) { | ||
| 382 | const auto [entry, is_cache_miss] = geometry_programs.try_emplace(variant); | ||
| 383 | auto& programs = entry->second; | ||
| 384 | |||
| 385 | switch (variant.primitive_mode) { | ||
| 386 | case GL_POINTS: | ||
| 387 | return LazyGeometryProgram(programs.points, variant); | ||
| 388 | case GL_LINES: | ||
| 389 | case GL_LINE_STRIP: | ||
| 390 | return LazyGeometryProgram(programs.lines, variant); | ||
| 391 | case GL_LINES_ADJACENCY: | ||
| 392 | case GL_LINE_STRIP_ADJACENCY: | ||
| 393 | return LazyGeometryProgram(programs.lines_adjacency, variant); | ||
| 394 | case GL_TRIANGLES: | ||
| 395 | case GL_TRIANGLE_STRIP: | ||
| 396 | case GL_TRIANGLE_FAN: | ||
| 397 | return LazyGeometryProgram(programs.triangles, variant); | ||
| 398 | case GL_TRIANGLES_ADJACENCY: | ||
| 399 | case GL_TRIANGLE_STRIP_ADJACENCY: | ||
| 400 | return LazyGeometryProgram(programs.triangles_adjacency, variant); | ||
| 401 | default: | ||
| 402 | UNREACHABLE_MSG("Unknown primitive mode."); | ||
| 403 | return LazyGeometryProgram(programs.points, variant); | ||
| 404 | } | ||
| 405 | } | ||
| 406 | |||
| 407 | GLuint CachedShader::LazyGeometryProgram(CachedProgram& target_program, | ||
| 408 | const ProgramVariant& variant) { | ||
| 409 | if (target_program) { | ||
| 410 | return target_program->handle; | ||
| 411 | } | ||
| 412 | const auto [glsl_name, debug_name, vertices] = GetPrimitiveDescription(variant.primitive_mode); | ||
| 413 | target_program = TryLoadProgram(variant); | ||
| 414 | if (!target_program) { | ||
| 415 | target_program = SpecializeShader(code, entries, program_type, variant); | ||
| 416 | disk_cache.SaveUsage(GetUsage(variant)); | ||
| 417 | } | ||
| 418 | |||
| 419 | LabelGLObject(GL_PROGRAM, target_program->handle, cpu_addr, debug_name); | ||
| 420 | |||
| 421 | return target_program->handle; | ||
| 422 | }; | ||
| 423 | |||
| 424 | CachedProgram CachedShader::TryLoadProgram(const ProgramVariant& variant) const { | 374 | CachedProgram CachedShader::TryLoadProgram(const ProgramVariant& variant) const { |
| 425 | const auto found = precompiled_programs.find(GetUsage(variant)); | 375 | const auto found = precompiled_programs.find(GetUsage(variant)); |
| 426 | if (found == precompiled_programs.end()) { | 376 | if (found == precompiled_programs.end()) { |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 2c8faf855..de195cc5d 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -86,22 +86,6 @@ private: | |||
| 86 | explicit CachedShader(const ShaderParameters& params, ProgramType program_type, | 86 | explicit CachedShader(const ShaderParameters& params, ProgramType program_type, |
| 87 | GLShader::ProgramResult result); | 87 | GLShader::ProgramResult result); |
| 88 | 88 | ||
| 89 | // Geometry programs. These are needed because GLSL needs an input topology but it's not | ||
| 90 | // declared by the hardware. Workaround this issue by generating a different shader per input | ||
| 91 | // topology class. | ||
| 92 | struct GeometryPrograms { | ||
| 93 | CachedProgram points; | ||
| 94 | CachedProgram lines; | ||
| 95 | CachedProgram lines_adjacency; | ||
| 96 | CachedProgram triangles; | ||
| 97 | CachedProgram triangles_adjacency; | ||
| 98 | }; | ||
| 99 | |||
| 100 | GLuint GetGeometryShader(const ProgramVariant& variant); | ||
| 101 | |||
| 102 | /// Generates a geometry shader or returns one that already exists. | ||
| 103 | GLuint LazyGeometryProgram(CachedProgram& target_program, const ProgramVariant& variant); | ||
| 104 | |||
| 105 | CachedProgram TryLoadProgram(const ProgramVariant& variant) const; | 89 | CachedProgram TryLoadProgram(const ProgramVariant& variant) const; |
| 106 | 90 | ||
| 107 | ShaderDiskCacheUsage GetUsage(const ProgramVariant& variant) const; | 91 | ShaderDiskCacheUsage GetUsage(const ProgramVariant& variant) const; |
| @@ -117,11 +101,6 @@ private: | |||
| 117 | std::size_t shader_length{}; | 101 | std::size_t shader_length{}; |
| 118 | 102 | ||
| 119 | std::unordered_map<ProgramVariant, CachedProgram> programs; | 103 | std::unordered_map<ProgramVariant, CachedProgram> programs; |
| 120 | std::unordered_map<ProgramVariant, GeometryPrograms> geometry_programs; | ||
| 121 | |||
| 122 | std::unordered_map<u32, GLuint> cbuf_resource_cache; | ||
| 123 | std::unordered_map<u32, GLuint> gmem_resource_cache; | ||
| 124 | std::unordered_map<u32, GLint> uniform_cache; | ||
| 125 | }; | 104 | }; |
| 126 | 105 | ||
| 127 | class ShaderCacheOpenGL final : public RasterizerCache<Shader> { | 106 | class ShaderCacheOpenGL final : public RasterizerCache<Shader> { |
diff --git a/src/video_core/shader/decode/half_set_predicate.cpp b/src/video_core/shader/decode/half_set_predicate.cpp index afea33e5f..840694527 100644 --- a/src/video_core/shader/decode/half_set_predicate.cpp +++ b/src/video_core/shader/decode/half_set_predicate.cpp | |||
| @@ -42,9 +42,8 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) { | |||
| 42 | cond = instr.hsetp2.reg.cond; | 42 | cond = instr.hsetp2.reg.cond; |
| 43 | h_and = instr.hsetp2.reg.h_and; | 43 | h_and = instr.hsetp2.reg.h_and; |
| 44 | op_b = | 44 | op_b = |
| 45 | UnpackHalfFloat(GetOperandAbsNegHalf(GetRegister(instr.gpr20), instr.hsetp2.reg.abs_b, | 45 | GetOperandAbsNegHalf(UnpackHalfFloat(GetRegister(instr.gpr20), instr.hsetp2.reg.type_b), |
| 46 | instr.hsetp2.reg.negate_b), | 46 | instr.hsetp2.reg.abs_b, instr.hsetp2.reg.negate_b); |
| 47 | instr.hsetp2.reg.type_b); | ||
| 48 | break; | 47 | break; |
| 49 | default: | 48 | default: |
| 50 | UNREACHABLE(); | 49 | UNREACHABLE(); |
| @@ -52,22 +51,22 @@ u32 ShaderIR::DecodeHalfSetPredicate(NodeBlock& bb, u32 pc) { | |||
| 52 | } | 51 | } |
| 53 | 52 | ||
| 54 | const OperationCode combiner = GetPredicateCombiner(instr.hsetp2.op); | 53 | const OperationCode combiner = GetPredicateCombiner(instr.hsetp2.op); |
| 55 | const Node combined_pred = GetPredicate(instr.hsetp2.pred3, instr.hsetp2.neg_pred); | 54 | const Node combined_pred = GetPredicate(instr.hsetp2.pred39, instr.hsetp2.neg_pred); |
| 56 | 55 | ||
| 57 | const auto Write = [&](u64 dest, Node src) { | 56 | const auto Write = [&](u64 dest, Node src) { |
| 58 | SetPredicate(bb, dest, Operation(combiner, std::move(src), combined_pred)); | 57 | SetPredicate(bb, dest, Operation(combiner, std::move(src), combined_pred)); |
| 59 | }; | 58 | }; |
| 60 | 59 | ||
| 61 | const Node comparison = GetPredicateComparisonHalf(cond, op_a, op_b); | 60 | const Node comparison = GetPredicateComparisonHalf(cond, op_a, op_b); |
| 62 | const u64 first = instr.hsetp2.pred0; | 61 | const u64 first = instr.hsetp2.pred3; |
| 63 | const u64 second = instr.hsetp2.pred39; | 62 | const u64 second = instr.hsetp2.pred0; |
| 64 | if (h_and) { | 63 | if (h_and) { |
| 65 | const Node joined = Operation(OperationCode::LogicalAnd2, comparison); | 64 | Node joined = Operation(OperationCode::LogicalAnd2, comparison); |
| 66 | Write(first, joined); | 65 | Write(first, joined); |
| 67 | Write(second, Operation(OperationCode::LogicalNegate, joined)); | 66 | Write(second, Operation(OperationCode::LogicalNegate, std::move(joined))); |
| 68 | } else { | 67 | } else { |
| 69 | Write(first, Operation(OperationCode::LogicalPick2, comparison, Immediate(0u))); | 68 | Write(first, Operation(OperationCode::LogicalPick2, comparison, Immediate(0U))); |
| 70 | Write(second, Operation(OperationCode::LogicalPick2, comparison, Immediate(1u))); | 69 | Write(second, Operation(OperationCode::LogicalPick2, comparison, Immediate(1U))); |
| 71 | } | 70 | } |
| 72 | 71 | ||
| 73 | return pc; | 72 | return pc; |
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index e636964e3..775e3f2ea 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp | |||
| @@ -68,12 +68,14 @@ void ConfigureDialog::RetranslateUI() { | |||
| 68 | ui->tabWidget->setCurrentIndex(old_index); | 68 | ui->tabWidget->setCurrentIndex(old_index); |
| 69 | } | 69 | } |
| 70 | 70 | ||
| 71 | Q_DECLARE_METATYPE(QList<QWidget*>); | ||
| 72 | |||
| 71 | void ConfigureDialog::PopulateSelectionList() { | 73 | void ConfigureDialog::PopulateSelectionList() { |
| 72 | const std::array<std::pair<QString, QStringList>, 4> items{ | 74 | const std::array<std::pair<QString, QList<QWidget*>>, 4> items{ |
| 73 | {{tr("General"), {tr("General"), tr("Web"), tr("Debug"), tr("Game List")}}, | 75 | {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}}, |
| 74 | {tr("System"), {tr("System"), tr("Profiles"), tr("Audio")}}, | 76 | {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->audioTab}}, |
| 75 | {tr("Graphics"), {tr("Graphics")}}, | 77 | {tr("Graphics"), {ui->graphicsTab}}, |
| 76 | {tr("Controls"), {tr("Input"), tr("Hotkeys")}}}, | 78 | {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, |
| 77 | }; | 79 | }; |
| 78 | 80 | ||
| 79 | [[maybe_unused]] const QSignalBlocker blocker(ui->selectorList); | 81 | [[maybe_unused]] const QSignalBlocker blocker(ui->selectorList); |
| @@ -81,7 +83,7 @@ void ConfigureDialog::PopulateSelectionList() { | |||
| 81 | ui->selectorList->clear(); | 83 | ui->selectorList->clear(); |
| 82 | for (const auto& entry : items) { | 84 | for (const auto& entry : items) { |
| 83 | auto* const item = new QListWidgetItem(entry.first); | 85 | auto* const item = new QListWidgetItem(entry.first); |
| 84 | item->setData(Qt::UserRole, entry.second); | 86 | item->setData(Qt::UserRole, QVariant::fromValue(entry.second)); |
| 85 | 87 | ||
| 86 | ui->selectorList->addItem(item); | 88 | ui->selectorList->addItem(item); |
| 87 | } | 89 | } |
| @@ -93,24 +95,26 @@ void ConfigureDialog::UpdateVisibleTabs() { | |||
| 93 | return; | 95 | return; |
| 94 | } | 96 | } |
| 95 | 97 | ||
| 96 | const std::map<QString, QWidget*> widgets = { | 98 | const std::map<QWidget*, QString> widgets = { |
| 97 | {tr("General"), ui->generalTab}, | 99 | {ui->generalTab, tr("General")}, |
| 98 | {tr("System"), ui->systemTab}, | 100 | {ui->systemTab, tr("System")}, |
| 99 | {tr("Profiles"), ui->profileManagerTab}, | 101 | {ui->profileManagerTab, tr("Profiles")}, |
| 100 | {tr("Input"), ui->inputTab}, | 102 | {ui->inputTab, tr("Input")}, |
| 101 | {tr("Hotkeys"), ui->hotkeysTab}, | 103 | {ui->hotkeysTab, tr("Hotkeys")}, |
| 102 | {tr("Graphics"), ui->graphicsTab}, | 104 | {ui->graphicsTab, tr("Graphics")}, |
| 103 | {tr("Audio"), ui->audioTab}, | 105 | {ui->audioTab, tr("Audio")}, |
| 104 | {tr("Debug"), ui->debugTab}, | 106 | {ui->debugTab, tr("Debug")}, |
| 105 | {tr("Web"), ui->webTab}, | 107 | {ui->webTab, tr("Web")}, |
| 106 | {tr("Game List"), ui->gameListTab}, | 108 | {ui->gameListTab, tr("Game List")}, |
| 107 | }; | 109 | }; |
| 108 | 110 | ||
| 109 | [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); | 111 | [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); |
| 110 | 112 | ||
| 111 | ui->tabWidget->clear(); | 113 | ui->tabWidget->clear(); |
| 112 | const QStringList tabs = items[0]->data(Qt::UserRole).toStringList(); | 114 | |
| 113 | for (const auto& tab : tabs) { | 115 | const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole)); |
| 114 | ui->tabWidget->addTab(widgets.find(tab)->second, tab); | 116 | |
| 117 | for (const auto tab : tabs) { | ||
| 118 | ui->tabWidget->addTab(tab, widgets.at(tab)); | ||
| 115 | } | 119 | } |
| 116 | } | 120 | } |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 727836b17..10bcd650e 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -19,6 +19,8 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | |||
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | SetConfiguration(); | 21 | SetConfiguration(); |
| 22 | |||
| 23 | connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled); | ||
| 22 | } | 24 | } |
| 23 | 25 | ||
| 24 | ConfigureGeneral::~ConfigureGeneral() = default; | 26 | ConfigureGeneral::~ConfigureGeneral() = default; |
| @@ -27,6 +29,10 @@ void ConfigureGeneral::SetConfiguration() { | |||
| 27 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); | 29 | ui->toggle_check_exit->setChecked(UISettings::values.confirm_before_closing); |
| 28 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); | 30 | ui->toggle_user_on_boot->setChecked(UISettings::values.select_user_on_boot); |
| 29 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); | 31 | ui->theme_combobox->setCurrentIndex(ui->theme_combobox->findData(UISettings::values.theme)); |
| 32 | |||
| 33 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); | ||
| 34 | ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); | ||
| 35 | ui->frame_limit->setValue(Settings::values.frame_limit); | ||
| 30 | } | 36 | } |
| 31 | 37 | ||
| 32 | void ConfigureGeneral::ApplyConfiguration() { | 38 | void ConfigureGeneral::ApplyConfiguration() { |
| @@ -34,6 +40,9 @@ void ConfigureGeneral::ApplyConfiguration() { | |||
| 34 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); | 40 | UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked(); |
| 35 | UISettings::values.theme = | 41 | UISettings::values.theme = |
| 36 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); | 42 | ui->theme_combobox->itemData(ui->theme_combobox->currentIndex()).toString(); |
| 43 | |||
| 44 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); | ||
| 45 | Settings::values.frame_limit = ui->frame_limit->value(); | ||
| 37 | } | 46 | } |
| 38 | 47 | ||
| 39 | void ConfigureGeneral::changeEvent(QEvent* event) { | 48 | void ConfigureGeneral::changeEvent(QEvent* event) { |
diff --git a/src/yuzu/configuration/configure_general.ui b/src/yuzu/configuration/configure_general.ui index e747a4ce2..0bb91d64b 100644 --- a/src/yuzu/configuration/configure_general.ui +++ b/src/yuzu/configuration/configure_general.ui | |||
| @@ -25,6 +25,33 @@ | |||
| 25 | <item> | 25 | <item> |
| 26 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> | 26 | <layout class="QVBoxLayout" name="GeneralVerticalLayout"> |
| 27 | <item> | 27 | <item> |
| 28 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||
| 29 | <item> | ||
| 30 | <widget class="QCheckBox" name="toggle_frame_limit"> | ||
| 31 | <property name="text"> | ||
| 32 | <string>Limit Speed Percent</string> | ||
| 33 | </property> | ||
| 34 | </widget> | ||
| 35 | </item> | ||
| 36 | <item> | ||
| 37 | <widget class="QSpinBox" name="frame_limit"> | ||
| 38 | <property name="suffix"> | ||
| 39 | <string>%</string> | ||
| 40 | </property> | ||
| 41 | <property name="minimum"> | ||
| 42 | <number>1</number> | ||
| 43 | </property> | ||
| 44 | <property name="maximum"> | ||
| 45 | <number>9999</number> | ||
| 46 | </property> | ||
| 47 | <property name="value"> | ||
| 48 | <number>100</number> | ||
| 49 | </property> | ||
| 50 | </widget> | ||
| 51 | </item> | ||
| 52 | </layout> | ||
| 53 | </item> | ||
| 54 | <item> | ||
| 28 | <widget class="QCheckBox" name="toggle_check_exit"> | 55 | <widget class="QCheckBox" name="toggle_check_exit"> |
| 29 | <property name="text"> | 56 | <property name="text"> |
| 30 | <string>Confirm exit while emulation is running</string> | 57 | <string>Confirm exit while emulation is running</string> |
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 2b17b250c..2c9e322c9 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -55,7 +55,6 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent) | |||
| 55 | 55 | ||
| 56 | SetConfiguration(); | 56 | SetConfiguration(); |
| 57 | 57 | ||
| 58 | connect(ui->toggle_frame_limit, &QCheckBox::toggled, ui->frame_limit, &QSpinBox::setEnabled); | ||
| 59 | connect(ui->bg_button, &QPushButton::clicked, this, [this] { | 58 | connect(ui->bg_button, &QPushButton::clicked, this, [this] { |
| 60 | const QColor new_bg_color = QColorDialog::getColor(bg_color); | 59 | const QColor new_bg_color = QColorDialog::getColor(bg_color); |
| 61 | if (!new_bg_color.isValid()) { | 60 | if (!new_bg_color.isValid()) { |
| @@ -72,9 +71,6 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 72 | 71 | ||
| 73 | ui->resolution_factor_combobox->setCurrentIndex( | 72 | ui->resolution_factor_combobox->setCurrentIndex( |
| 74 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); | 73 | static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor))); |
| 75 | ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit); | ||
| 76 | ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); | ||
| 77 | ui->frame_limit->setValue(Settings::values.frame_limit); | ||
| 78 | ui->use_disk_shader_cache->setEnabled(runtime_lock); | 74 | ui->use_disk_shader_cache->setEnabled(runtime_lock); |
| 79 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); | 75 | ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache); |
| 80 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); | 76 | ui->use_accurate_gpu_emulation->setChecked(Settings::values.use_accurate_gpu_emulation); |
| @@ -89,8 +85,6 @@ void ConfigureGraphics::SetConfiguration() { | |||
| 89 | void ConfigureGraphics::ApplyConfiguration() { | 85 | void ConfigureGraphics::ApplyConfiguration() { |
| 90 | Settings::values.resolution_factor = | 86 | Settings::values.resolution_factor = |
| 91 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); | 87 | ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex())); |
| 92 | Settings::values.use_frame_limit = ui->toggle_frame_limit->isChecked(); | ||
| 93 | Settings::values.frame_limit = ui->frame_limit->value(); | ||
| 94 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); | 88 | Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked(); |
| 95 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); | 89 | Settings::values.use_accurate_gpu_emulation = ui->use_accurate_gpu_emulation->isChecked(); |
| 96 | Settings::values.use_asynchronous_gpu_emulation = | 90 | Settings::values.use_asynchronous_gpu_emulation = |
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 15ab18ecd..0309ee300 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui | |||
| @@ -23,33 +23,6 @@ | |||
| 23 | </property> | 23 | </property> |
| 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> | 24 | <layout class="QVBoxLayout" name="verticalLayout_2"> |
| 25 | <item> | 25 | <item> |
| 26 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||
| 27 | <item> | ||
| 28 | <widget class="QCheckBox" name="toggle_frame_limit"> | ||
| 29 | <property name="text"> | ||
| 30 | <string>Limit Speed Percent</string> | ||
| 31 | </property> | ||
| 32 | </widget> | ||
| 33 | </item> | ||
| 34 | <item> | ||
| 35 | <widget class="QSpinBox" name="frame_limit"> | ||
| 36 | <property name="suffix"> | ||
| 37 | <string>%</string> | ||
| 38 | </property> | ||
| 39 | <property name="minimum"> | ||
| 40 | <number>1</number> | ||
| 41 | </property> | ||
| 42 | <property name="maximum"> | ||
| 43 | <number>9999</number> | ||
| 44 | </property> | ||
| 45 | <property name="value"> | ||
| 46 | <number>100</number> | ||
| 47 | </property> | ||
| 48 | </widget> | ||
| 49 | </item> | ||
| 50 | </layout> | ||
| 51 | </item> | ||
| 52 | <item> | ||
| 53 | <widget class="QCheckBox" name="use_disk_shader_cache"> | 26 | <widget class="QCheckBox" name="use_disk_shader_cache"> |
| 54 | <property name="text"> | 27 | <property name="text"> |
| 55 | <string>Use disk shader cache</string> | 28 | <string>Use disk shader cache</string> |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 6d249cb3e..8304c6517 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -6,6 +6,9 @@ | |||
| 6 | #include <clocale> | 6 | #include <clocale> |
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | #ifdef __APPLE__ | ||
| 10 | #include <unistd.h> // for chdir | ||
| 11 | #endif | ||
| 9 | 12 | ||
| 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. | 13 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. |
| 11 | #include "applets/error.h" | 14 | #include "applets/error.h" |
| @@ -2226,6 +2229,14 @@ int main(int argc, char* argv[]) { | |||
| 2226 | QCoreApplication::setOrganizationName(QStringLiteral("yuzu team")); | 2229 | QCoreApplication::setOrganizationName(QStringLiteral("yuzu team")); |
| 2227 | QCoreApplication::setApplicationName(QStringLiteral("yuzu")); | 2230 | QCoreApplication::setApplicationName(QStringLiteral("yuzu")); |
| 2228 | 2231 | ||
| 2232 | #ifdef __APPLE__ | ||
| 2233 | // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/". | ||
| 2234 | // But since we require the working directory to be the executable path for the location of the | ||
| 2235 | // user folder in the Qt Frontend, we need to cd into that working directory | ||
| 2236 | const std::string bin_path = FileUtil::GetBundleDirectory() + DIR_SEP + ".."; | ||
| 2237 | chdir(bin_path.c_str()); | ||
| 2238 | #endif | ||
| 2239 | |||
| 2229 | // Enables the core to make the qt created contexts current on std::threads | 2240 | // Enables the core to make the qt created contexts current on std::threads |
| 2230 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); | 2241 | QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity); |
| 2231 | QApplication app(argc, argv); | 2242 | QApplication app(argc, argv); |