diff options
Diffstat (limited to 'src')
49 files changed, 1120 insertions, 607 deletions
diff --git a/src/common/file_util.h b/src/common/file_util.h index 3d8fe6264..571503d2a 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -285,7 +285,7 @@ private: | |||
| 285 | template <typename T> | 285 | template <typename T> |
| 286 | void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) { | 286 | void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) { |
| 287 | #ifdef _MSC_VER | 287 | #ifdef _MSC_VER |
| 288 | fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode); | 288 | fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode); |
| 289 | #else | 289 | #else |
| 290 | fstream.open(filename.c_str(), openmode); | 290 | fstream.open(filename.c_str(), openmode); |
| 291 | #endif | 291 | #endif |
diff --git a/src/common/logging/text_formatter.cpp b/src/common/logging/text_formatter.cpp index 05437c137..6a0605c63 100644 --- a/src/common/logging/text_formatter.cpp +++ b/src/common/logging/text_formatter.cpp | |||
| @@ -31,7 +31,7 @@ std::string FormatLogMessage(const Entry& entry) { | |||
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | void PrintMessage(const Entry& entry) { | 33 | void PrintMessage(const Entry& entry) { |
| 34 | auto str = FormatLogMessage(entry) + '\n'; | 34 | const auto str = FormatLogMessage(entry).append(1, '\n'); |
| 35 | fputs(str.c_str(), stderr); | 35 | fputs(str.c_str(), stderr); |
| 36 | } | 36 | } |
| 37 | 37 | ||
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index c9a5425a7..731d1db34 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <cctype> | 6 | #include <cctype> |
| 7 | #include <cerrno> | 7 | #include <cerrno> |
| 8 | #include <codecvt> | ||
| 8 | #include <cstdio> | 9 | #include <cstdio> |
| 9 | #include <cstdlib> | 10 | #include <cstdlib> |
| 10 | #include <cstring> | 11 | #include <cstring> |
| @@ -13,11 +14,7 @@ | |||
| 13 | #include "common/string_util.h" | 14 | #include "common/string_util.h" |
| 14 | 15 | ||
| 15 | #ifdef _WIN32 | 16 | #ifdef _WIN32 |
| 16 | #include <codecvt> | ||
| 17 | #include <windows.h> | 17 | #include <windows.h> |
| 18 | #include "common/common_funcs.h" | ||
| 19 | #else | ||
| 20 | #include <iconv.h> | ||
| 21 | #endif | 18 | #endif |
| 22 | 19 | ||
| 23 | namespace Common { | 20 | namespace Common { |
| @@ -195,11 +192,9 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st | |||
| 195 | return result; | 192 | return result; |
| 196 | } | 193 | } |
| 197 | 194 | ||
| 198 | #ifdef _WIN32 | ||
| 199 | |||
| 200 | std::string UTF16ToUTF8(const std::u16string& input) { | 195 | std::string UTF16ToUTF8(const std::u16string& input) { |
| 201 | #if _MSC_VER >= 1900 | 196 | #ifdef _MSC_VER |
| 202 | // Workaround for missing char16_t/char32_t instantiations in MSVC2015 | 197 | // Workaround for missing char16_t/char32_t instantiations in MSVC2017 |
| 203 | std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert; | 198 | std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert; |
| 204 | std::basic_string<__int16> tmp_buffer(input.cbegin(), input.cend()); | 199 | std::basic_string<__int16> tmp_buffer(input.cbegin(), input.cend()); |
| 205 | return convert.to_bytes(tmp_buffer); | 200 | return convert.to_bytes(tmp_buffer); |
| @@ -210,8 +205,8 @@ std::string UTF16ToUTF8(const std::u16string& input) { | |||
| 210 | } | 205 | } |
| 211 | 206 | ||
| 212 | std::u16string UTF8ToUTF16(const std::string& input) { | 207 | std::u16string UTF8ToUTF16(const std::string& input) { |
| 213 | #if _MSC_VER >= 1900 | 208 | #ifdef _MSC_VER |
| 214 | // Workaround for missing char16_t/char32_t instantiations in MSVC2015 | 209 | // Workaround for missing char16_t/char32_t instantiations in MSVC2017 |
| 215 | std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert; | 210 | std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert; |
| 216 | auto tmp_buffer = convert.from_bytes(input); | 211 | auto tmp_buffer = convert.from_bytes(input); |
| 217 | return std::u16string(tmp_buffer.cbegin(), tmp_buffer.cend()); | 212 | return std::u16string(tmp_buffer.cbegin(), tmp_buffer.cend()); |
| @@ -221,6 +216,7 @@ std::u16string UTF8ToUTF16(const std::string& input) { | |||
| 221 | #endif | 216 | #endif |
| 222 | } | 217 | } |
| 223 | 218 | ||
| 219 | #ifdef _WIN32 | ||
| 224 | static std::wstring CPToUTF16(u32 code_page, const std::string& input) { | 220 | static std::wstring CPToUTF16(u32 code_page, const std::string& input) { |
| 225 | const auto size = | 221 | const auto size = |
| 226 | MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); | 222 | MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); |
| @@ -261,124 +257,6 @@ std::wstring UTF8ToUTF16W(const std::string& input) { | |||
| 261 | return CPToUTF16(CP_UTF8, input); | 257 | return CPToUTF16(CP_UTF8, input); |
| 262 | } | 258 | } |
| 263 | 259 | ||
| 264 | std::string SHIFTJISToUTF8(const std::string& input) { | ||
| 265 | return UTF16ToUTF8(CPToUTF16(932, input)); | ||
| 266 | } | ||
| 267 | |||
| 268 | std::string CP1252ToUTF8(const std::string& input) { | ||
| 269 | return UTF16ToUTF8(CPToUTF16(1252, input)); | ||
| 270 | } | ||
| 271 | |||
| 272 | #else | ||
| 273 | |||
| 274 | template <typename T> | ||
| 275 | static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input) { | ||
| 276 | iconv_t const conv_desc = iconv_open("UTF-8", fromcode); | ||
| 277 | if ((iconv_t)(-1) == conv_desc) { | ||
| 278 | LOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno)); | ||
| 279 | iconv_close(conv_desc); | ||
| 280 | return {}; | ||
| 281 | } | ||
| 282 | |||
| 283 | const std::size_t in_bytes = sizeof(T) * input.size(); | ||
| 284 | // Multiply by 4, which is the max number of bytes to encode a codepoint | ||
| 285 | const std::size_t out_buffer_size = 4 * in_bytes; | ||
| 286 | |||
| 287 | std::string out_buffer(out_buffer_size, '\0'); | ||
| 288 | |||
| 289 | auto src_buffer = &input[0]; | ||
| 290 | std::size_t src_bytes = in_bytes; | ||
| 291 | auto dst_buffer = &out_buffer[0]; | ||
| 292 | std::size_t dst_bytes = out_buffer.size(); | ||
| 293 | |||
| 294 | while (0 != src_bytes) { | ||
| 295 | std::size_t const iconv_result = | ||
| 296 | iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes); | ||
| 297 | |||
| 298 | if (static_cast<std::size_t>(-1) == iconv_result) { | ||
| 299 | if (EILSEQ == errno || EINVAL == errno) { | ||
| 300 | // Try to skip the bad character | ||
| 301 | if (0 != src_bytes) { | ||
| 302 | --src_bytes; | ||
| 303 | ++src_buffer; | ||
| 304 | } | ||
| 305 | } else { | ||
| 306 | LOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno)); | ||
| 307 | break; | ||
| 308 | } | ||
| 309 | } | ||
| 310 | } | ||
| 311 | |||
| 312 | std::string result; | ||
| 313 | out_buffer.resize(out_buffer_size - dst_bytes); | ||
| 314 | out_buffer.swap(result); | ||
| 315 | |||
| 316 | iconv_close(conv_desc); | ||
| 317 | |||
| 318 | return result; | ||
| 319 | } | ||
| 320 | |||
| 321 | std::u16string UTF8ToUTF16(const std::string& input) { | ||
| 322 | iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8"); | ||
| 323 | if ((iconv_t)(-1) == conv_desc) { | ||
| 324 | LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno)); | ||
| 325 | iconv_close(conv_desc); | ||
| 326 | return {}; | ||
| 327 | } | ||
| 328 | |||
| 329 | const std::size_t in_bytes = sizeof(char) * input.size(); | ||
| 330 | // Multiply by 4, which is the max number of bytes to encode a codepoint | ||
| 331 | const std::size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes; | ||
| 332 | |||
| 333 | std::u16string out_buffer(out_buffer_size, char16_t{}); | ||
| 334 | |||
| 335 | char* src_buffer = const_cast<char*>(&input[0]); | ||
| 336 | std::size_t src_bytes = in_bytes; | ||
| 337 | char* dst_buffer = (char*)(&out_buffer[0]); | ||
| 338 | std::size_t dst_bytes = out_buffer.size(); | ||
| 339 | |||
| 340 | while (0 != src_bytes) { | ||
| 341 | std::size_t const iconv_result = | ||
| 342 | iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes); | ||
| 343 | |||
| 344 | if (static_cast<std::size_t>(-1) == iconv_result) { | ||
| 345 | if (EILSEQ == errno || EINVAL == errno) { | ||
| 346 | // Try to skip the bad character | ||
| 347 | if (0 != src_bytes) { | ||
| 348 | --src_bytes; | ||
| 349 | ++src_buffer; | ||
| 350 | } | ||
| 351 | } else { | ||
| 352 | LOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno)); | ||
| 353 | break; | ||
| 354 | } | ||
| 355 | } | ||
| 356 | } | ||
| 357 | |||
| 358 | std::u16string result; | ||
| 359 | out_buffer.resize(out_buffer_size - dst_bytes); | ||
| 360 | out_buffer.swap(result); | ||
| 361 | |||
| 362 | iconv_close(conv_desc); | ||
| 363 | |||
| 364 | return result; | ||
| 365 | } | ||
| 366 | |||
| 367 | std::string UTF16ToUTF8(const std::u16string& input) { | ||
| 368 | return CodeToUTF8("UTF-16LE", input); | ||
| 369 | } | ||
| 370 | |||
| 371 | std::string CP1252ToUTF8(const std::string& input) { | ||
| 372 | // return CodeToUTF8("CP1252//TRANSLIT", input); | ||
| 373 | // return CodeToUTF8("CP1252//IGNORE", input); | ||
| 374 | return CodeToUTF8("CP1252", input); | ||
| 375 | } | ||
| 376 | |||
| 377 | std::string SHIFTJISToUTF8(const std::string& input) { | ||
| 378 | // return CodeToUTF8("CP932", input); | ||
| 379 | return CodeToUTF8("SJIS", input); | ||
| 380 | } | ||
| 381 | |||
| 382 | #endif | 260 | #endif |
| 383 | 261 | ||
| 384 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len) { | 262 | std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, std::size_t max_len) { |
diff --git a/src/common/string_util.h b/src/common/string_util.h index dcca6bc38..32bf6a19c 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -72,31 +72,10 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st | |||
| 72 | std::string UTF16ToUTF8(const std::u16string& input); | 72 | std::string UTF16ToUTF8(const std::u16string& input); |
| 73 | std::u16string UTF8ToUTF16(const std::string& input); | 73 | std::u16string UTF8ToUTF16(const std::string& input); |
| 74 | 74 | ||
| 75 | std::string CP1252ToUTF8(const std::string& str); | ||
| 76 | std::string SHIFTJISToUTF8(const std::string& str); | ||
| 77 | |||
| 78 | #ifdef _WIN32 | 75 | #ifdef _WIN32 |
| 79 | std::string UTF16ToUTF8(const std::wstring& input); | 76 | std::string UTF16ToUTF8(const std::wstring& input); |
| 80 | std::wstring UTF8ToUTF16W(const std::string& str); | 77 | std::wstring UTF8ToUTF16W(const std::string& str); |
| 81 | 78 | ||
| 82 | #ifdef _UNICODE | ||
| 83 | inline std::string TStrToUTF8(const std::wstring& str) { | ||
| 84 | return UTF16ToUTF8(str); | ||
| 85 | } | ||
| 86 | |||
| 87 | inline std::wstring UTF8ToTStr(const std::string& str) { | ||
| 88 | return UTF8ToUTF16W(str); | ||
| 89 | } | ||
| 90 | #else | ||
| 91 | inline std::string TStrToUTF8(const std::string& str) { | ||
| 92 | return str; | ||
| 93 | } | ||
| 94 | |||
| 95 | inline std::string UTF8ToTStr(const std::string& str) { | ||
| 96 | return str; | ||
| 97 | } | ||
| 98 | #endif | ||
| 99 | |||
| 100 | #endif | 79 | #endif |
| 101 | 80 | ||
| 102 | /** | 81 | /** |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 378d8128d..e4a676e91 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -34,6 +34,8 @@ add_library(core STATIC | |||
| 34 | file_sys/errors.h | 34 | file_sys/errors.h |
| 35 | file_sys/fsmitm_romfsbuild.cpp | 35 | file_sys/fsmitm_romfsbuild.cpp |
| 36 | file_sys/fsmitm_romfsbuild.h | 36 | file_sys/fsmitm_romfsbuild.h |
| 37 | file_sys/ips_layer.cpp | ||
| 38 | file_sys/ips_layer.h | ||
| 37 | file_sys/mode.h | 39 | file_sys/mode.h |
| 38 | file_sys/nca_metadata.cpp | 40 | file_sys/nca_metadata.cpp |
| 39 | file_sys/nca_metadata.h | 41 | file_sys/nca_metadata.h |
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 05cc84458..7e978cf7a 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp | |||
| @@ -86,7 +86,7 @@ public: | |||
| 86 | parent.jit->HaltExecution(); | 86 | parent.jit->HaltExecution(); |
| 87 | parent.SetPC(pc); | 87 | parent.SetPC(pc); |
| 88 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | 88 | Kernel::Thread* thread = Kernel::GetCurrentThread(); |
| 89 | parent.SaveContext(thread->context); | 89 | parent.SaveContext(thread->GetContext()); |
| 90 | GDBStub::Break(); | 90 | GDBStub::Break(); |
| 91 | GDBStub::SendTrap(thread, 5); | 91 | GDBStub::SendTrap(thread, 5); |
| 92 | return; | 92 | return; |
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp index e218a0b15..ded4dd359 100644 --- a/src/core/arm/unicorn/arm_unicorn.cpp +++ b/src/core/arm/unicorn/arm_unicorn.cpp | |||
| @@ -195,7 +195,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) { | |||
| 195 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); | 195 | uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); |
| 196 | } | 196 | } |
| 197 | Kernel::Thread* thread = Kernel::GetCurrentThread(); | 197 | Kernel::Thread* thread = Kernel::GetCurrentThread(); |
| 198 | SaveContext(thread->context); | 198 | SaveContext(thread->GetContext()); |
| 199 | if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) { | 199 | if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) { |
| 200 | last_bkpt_hit = false; | 200 | last_bkpt_hit = false; |
| 201 | GDBStub::Break(); | 201 | GDBStub::Break(); |
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index edfc1bbd4..8f5142a07 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp | |||
| @@ -20,7 +20,9 @@ namespace FileSys { | |||
| 20 | 20 | ||
| 21 | constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"}; | 21 | constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"}; |
| 22 | 22 | ||
| 23 | XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { | 23 | XCI::XCI(VirtualFile file_) |
| 24 | : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, | ||
| 25 | partitions(0x4) { | ||
| 24 | if (file->ReadObject(&header) != sizeof(GamecardHeader)) { | 26 | if (file->ReadObject(&header) != sizeof(GamecardHeader)) { |
| 25 | status = Loader::ResultStatus::ErrorBadXCIHeader; | 27 | status = Loader::ResultStatus::ErrorBadXCIHeader; |
| 26 | return; | 28 | return; |
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp new file mode 100644 index 000000000..df933ee36 --- /dev/null +++ b/src/core/file_sys/ips_layer.cpp | |||
| @@ -0,0 +1,88 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/assert.h" | ||
| 6 | #include "common/swap.h" | ||
| 7 | #include "core/file_sys/ips_layer.h" | ||
| 8 | #include "core/file_sys/vfs_vector.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | |||
| 12 | enum class IPSFileType { | ||
| 13 | IPS, | ||
| 14 | IPS32, | ||
| 15 | Error, | ||
| 16 | }; | ||
| 17 | |||
| 18 | static IPSFileType IdentifyMagic(const std::vector<u8>& magic) { | ||
| 19 | if (magic.size() != 5) | ||
| 20 | return IPSFileType::Error; | ||
| 21 | if (magic == std::vector<u8>{'P', 'A', 'T', 'C', 'H'}) | ||
| 22 | return IPSFileType::IPS; | ||
| 23 | if (magic == std::vector<u8>{'I', 'P', 'S', '3', '2'}) | ||
| 24 | return IPSFileType::IPS32; | ||
| 25 | return IPSFileType::Error; | ||
| 26 | } | ||
| 27 | |||
| 28 | VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { | ||
| 29 | if (in == nullptr || ips == nullptr) | ||
| 30 | return nullptr; | ||
| 31 | |||
| 32 | const auto type = IdentifyMagic(ips->ReadBytes(0x5)); | ||
| 33 | if (type == IPSFileType::Error) | ||
| 34 | return nullptr; | ||
| 35 | |||
| 36 | auto in_data = in->ReadAllBytes(); | ||
| 37 | |||
| 38 | std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4); | ||
| 39 | u64 offset = 5; // After header | ||
| 40 | while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { | ||
| 41 | offset += temp.size(); | ||
| 42 | if (type == IPSFileType::IPS32 && temp == std::vector<u8>{'E', 'E', 'O', 'F'} || | ||
| 43 | type == IPSFileType::IPS && temp == std::vector<u8>{'E', 'O', 'F'}) { | ||
| 44 | break; | ||
| 45 | } | ||
| 46 | |||
| 47 | u32 real_offset{}; | ||
| 48 | if (type == IPSFileType::IPS32) | ||
| 49 | real_offset = (temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3]; | ||
| 50 | else | ||
| 51 | real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2]; | ||
| 52 | |||
| 53 | u16 data_size{}; | ||
| 54 | if (ips->ReadObject(&data_size, offset) != sizeof(u16)) | ||
| 55 | return nullptr; | ||
| 56 | data_size = Common::swap16(data_size); | ||
| 57 | offset += sizeof(u16); | ||
| 58 | |||
| 59 | if (data_size == 0) { // RLE | ||
| 60 | u16 rle_size{}; | ||
| 61 | if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) | ||
| 62 | return nullptr; | ||
| 63 | rle_size = Common::swap16(data_size); | ||
| 64 | offset += sizeof(u16); | ||
| 65 | |||
| 66 | const auto data = ips->ReadByte(offset++); | ||
| 67 | if (data == boost::none) | ||
| 68 | return nullptr; | ||
| 69 | |||
| 70 | if (real_offset + rle_size > in_data.size()) | ||
| 71 | rle_size = in_data.size() - real_offset; | ||
| 72 | std::memset(in_data.data() + real_offset, data.get(), rle_size); | ||
| 73 | } else { // Standard Patch | ||
| 74 | auto read = data_size; | ||
| 75 | if (real_offset + read > in_data.size()) | ||
| 76 | read = in_data.size() - real_offset; | ||
| 77 | if (ips->Read(in_data.data() + real_offset, read, offset) != data_size) | ||
| 78 | return nullptr; | ||
| 79 | offset += data_size; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | if (temp != std::vector<u8>{'E', 'E', 'O', 'F'} && temp != std::vector<u8>{'E', 'O', 'F'}) | ||
| 84 | return nullptr; | ||
| 85 | return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); | ||
| 86 | } | ||
| 87 | |||
| 88 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/ips_layer.h b/src/core/file_sys/ips_layer.h new file mode 100644 index 000000000..81c163494 --- /dev/null +++ b/src/core/file_sys/ips_layer.h | |||
| @@ -0,0 +1,15 @@ | |||
| 1 | // Copyright 2018 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 <memory> | ||
| 8 | |||
| 9 | #include "core/file_sys/vfs.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips); | ||
| 14 | |||
| 15 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 57b7741f8..539698f6e 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -5,14 +5,18 @@ | |||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <cstring> | ||
| 8 | 9 | ||
| 10 | #include "common/hex_util.h" | ||
| 9 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 10 | #include "core/file_sys/content_archive.h" | 12 | #include "core/file_sys/content_archive.h" |
| 11 | #include "core/file_sys/control_metadata.h" | 13 | #include "core/file_sys/control_metadata.h" |
| 14 | #include "core/file_sys/ips_layer.h" | ||
| 12 | #include "core/file_sys/patch_manager.h" | 15 | #include "core/file_sys/patch_manager.h" |
| 13 | #include "core/file_sys/registered_cache.h" | 16 | #include "core/file_sys/registered_cache.h" |
| 14 | #include "core/file_sys/romfs.h" | 17 | #include "core/file_sys/romfs.h" |
| 15 | #include "core/file_sys/vfs_layered.h" | 18 | #include "core/file_sys/vfs_layered.h" |
| 19 | #include "core/file_sys/vfs_vector.h" | ||
| 16 | #include "core/hle/service/filesystem/filesystem.h" | 20 | #include "core/hle/service/filesystem/filesystem.h" |
| 17 | #include "core/loader/loader.h" | 21 | #include "core/loader/loader.h" |
| 18 | 22 | ||
| @@ -21,6 +25,14 @@ namespace FileSys { | |||
| 21 | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; | 25 | constexpr u64 SINGLE_BYTE_MODULUS = 0x100; |
| 22 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; | 26 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; |
| 23 | 27 | ||
| 28 | struct NSOBuildHeader { | ||
| 29 | u32_le magic; | ||
| 30 | INSERT_PADDING_BYTES(0x3C); | ||
| 31 | std::array<u8, 0x20> build_id; | ||
| 32 | INSERT_PADDING_BYTES(0xA0); | ||
| 33 | }; | ||
| 34 | static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size."); | ||
| 35 | |||
| 24 | std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { | 36 | std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { |
| 25 | std::array<u8, sizeof(u32)> bytes{}; | 37 | std::array<u8, sizeof(u32)> bytes{}; |
| 26 | bytes[0] = version % SINGLE_BYTE_MODULUS; | 38 | bytes[0] = version % SINGLE_BYTE_MODULUS; |
| @@ -34,16 +46,6 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { | |||
| 34 | return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); | 46 | return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); |
| 35 | } | 47 | } |
| 36 | 48 | ||
| 37 | constexpr std::array<const char*, 3> PATCH_TYPE_NAMES{ | ||
| 38 | "Update", | ||
| 39 | "LayeredFS", | ||
| 40 | "DLC", | ||
| 41 | }; | ||
| 42 | |||
| 43 | std::string FormatPatchTypeName(PatchType type) { | ||
| 44 | return PATCH_TYPE_NAMES.at(static_cast<std::size_t>(type)); | ||
| 45 | } | ||
| 46 | |||
| 47 | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} | 49 | PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} |
| 48 | 50 | ||
| 49 | PatchManager::~PatchManager() = default; | 51 | PatchManager::~PatchManager() = default; |
| @@ -71,6 +73,79 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 71 | return exefs; | 73 | return exefs; |
| 72 | } | 74 | } |
| 73 | 75 | ||
| 76 | static std::vector<VirtualFile> CollectIPSPatches(const std::vector<VirtualDir>& patch_dirs, | ||
| 77 | const std::string& build_id) { | ||
| 78 | std::vector<VirtualFile> ips; | ||
| 79 | ips.reserve(patch_dirs.size()); | ||
| 80 | for (const auto& subdir : patch_dirs) { | ||
| 81 | auto exefs_dir = subdir->GetSubdirectory("exefs"); | ||
| 82 | if (exefs_dir != nullptr) { | ||
| 83 | for (const auto& file : exefs_dir->GetFiles()) { | ||
| 84 | if (file->GetExtension() != "ips") | ||
| 85 | continue; | ||
| 86 | auto name = file->GetName(); | ||
| 87 | const auto p1 = name.substr(0, name.find('.')); | ||
| 88 | const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); | ||
| 89 | |||
| 90 | if (build_id == this_build_id) | ||
| 91 | ips.push_back(file); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | return ips; | ||
| 97 | } | ||
| 98 | |||
| 99 | std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { | ||
| 100 | if (nso.size() < 0x100) | ||
| 101 | return nso; | ||
| 102 | |||
| 103 | NSOBuildHeader header; | ||
| 104 | std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader)); | ||
| 105 | |||
| 106 | if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) | ||
| 107 | return nso; | ||
| 108 | |||
| 109 | const auto build_id_raw = Common::HexArrayToString(header.build_id); | ||
| 110 | const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); | ||
| 111 | |||
| 112 | LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); | ||
| 113 | |||
| 114 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||
| 115 | auto patch_dirs = load_dir->GetSubdirectories(); | ||
| 116 | std::sort(patch_dirs.begin(), patch_dirs.end(), | ||
| 117 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||
| 118 | const auto ips = CollectIPSPatches(patch_dirs, build_id); | ||
| 119 | |||
| 120 | auto out = nso; | ||
| 121 | for (const auto& ips_file : ips) { | ||
| 122 | LOG_INFO(Loader, " - Appling IPS patch from mod \"{}\"", | ||
| 123 | ips_file->GetContainingDirectory()->GetParentDirectory()->GetName()); | ||
| 124 | const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), ips_file); | ||
| 125 | if (patched != nullptr) | ||
| 126 | out = patched->ReadAllBytes(); | ||
| 127 | } | ||
| 128 | |||
| 129 | if (out.size() < 0x100) | ||
| 130 | return nso; | ||
| 131 | std::memcpy(out.data(), &header, sizeof(NSOBuildHeader)); | ||
| 132 | return out; | ||
| 133 | } | ||
| 134 | |||
| 135 | bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | ||
| 136 | const auto build_id_raw = Common::HexArrayToString(build_id_); | ||
| 137 | const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); | ||
| 138 | |||
| 139 | LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); | ||
| 140 | |||
| 141 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | ||
| 142 | auto patch_dirs = load_dir->GetSubdirectories(); | ||
| 143 | std::sort(patch_dirs.begin(), patch_dirs.end(), | ||
| 144 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | ||
| 145 | |||
| 146 | return !CollectIPSPatches(patch_dirs, build_id).empty(); | ||
| 147 | } | ||
| 148 | |||
| 74 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { | 149 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { |
| 75 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 150 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); |
| 76 | if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { | 151 | if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { |
| @@ -138,8 +213,19 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, | |||
| 138 | return romfs; | 213 | return romfs; |
| 139 | } | 214 | } |
| 140 | 215 | ||
| 141 | std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const { | 216 | static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { |
| 142 | std::map<PatchType, std::string> out; | 217 | if (to.empty()) |
| 218 | to += with; | ||
| 219 | else | ||
| 220 | to += ", " + with; | ||
| 221 | } | ||
| 222 | |||
| 223 | static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | ||
| 224 | return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); | ||
| 225 | } | ||
| 226 | |||
| 227 | std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames() const { | ||
| 228 | std::map<std::string, std::string, std::less<>> out; | ||
| 143 | const auto installed = Service::FileSystem::GetUnionContents(); | 229 | const auto installed = Service::FileSystem::GetUnionContents(); |
| 144 | 230 | ||
| 145 | // Game Updates | 231 | // Game Updates |
| @@ -148,23 +234,36 @@ std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const { | |||
| 148 | auto [nacp, discard_icon_file] = update.GetControlMetadata(); | 234 | auto [nacp, discard_icon_file] = update.GetControlMetadata(); |
| 149 | 235 | ||
| 150 | if (nacp != nullptr) { | 236 | if (nacp != nullptr) { |
| 151 | out[PatchType::Update] = nacp->GetVersionString(); | 237 | out.insert_or_assign("Update", nacp->GetVersionString()); |
| 152 | } else { | 238 | } else { |
| 153 | if (installed->HasEntry(update_tid, ContentRecordType::Program)) { | 239 | if (installed->HasEntry(update_tid, ContentRecordType::Program)) { |
| 154 | const auto meta_ver = installed->GetEntryVersion(update_tid); | 240 | const auto meta_ver = installed->GetEntryVersion(update_tid); |
| 155 | if (meta_ver == boost::none || meta_ver.get() == 0) { | 241 | if (meta_ver == boost::none || meta_ver.get() == 0) { |
| 156 | out[PatchType::Update] = ""; | 242 | out.insert_or_assign("Update", ""); |
| 157 | } else { | 243 | } else { |
| 158 | out[PatchType::Update] = | 244 | out.insert_or_assign( |
| 159 | FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements); | 245 | "Update", |
| 246 | FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements)); | ||
| 160 | } | 247 | } |
| 161 | } | 248 | } |
| 162 | } | 249 | } |
| 163 | 250 | ||
| 164 | // LayeredFS | 251 | // General Mods (LayeredFS and IPS) |
| 165 | const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 252 | const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id); |
| 166 | if (lfs_dir != nullptr && lfs_dir->GetSize() > 0) | 253 | if (mod_dir != nullptr && mod_dir->GetSize() > 0) { |
| 167 | out.insert_or_assign(PatchType::LayeredFS, ""); | 254 | for (const auto& mod : mod_dir->GetSubdirectories()) { |
| 255 | std::string types; | ||
| 256 | if (IsDirValidAndNonEmpty(mod->GetSubdirectory("exefs"))) | ||
| 257 | AppendCommaIfNotEmpty(types, "IPS"); | ||
| 258 | if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) | ||
| 259 | AppendCommaIfNotEmpty(types, "LayeredFS"); | ||
| 260 | |||
| 261 | if (types.empty()) | ||
| 262 | continue; | ||
| 263 | |||
| 264 | out.insert_or_assign(mod->GetName(), types); | ||
| 265 | } | ||
| 266 | } | ||
| 168 | 267 | ||
| 169 | // DLC | 268 | // DLC |
| 170 | const auto dlc_entries = installed->ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); | 269 | const auto dlc_entries = installed->ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); |
| @@ -186,7 +285,7 @@ std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const { | |||
| 186 | 285 | ||
| 187 | list += fmt::format("{}", dlc_match.back().title_id & 0x7FF); | 286 | list += fmt::format("{}", dlc_match.back().title_id & 0x7FF); |
| 188 | 287 | ||
| 189 | out.insert_or_assign(PatchType::DLC, std::move(list)); | 288 | out.insert_or_assign("DLC", std::move(list)); |
| 190 | } | 289 | } |
| 191 | 290 | ||
| 192 | return out; | 291 | return out; |
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 3a2a9d212..6a864ec43 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h | |||
| @@ -24,14 +24,6 @@ enum class TitleVersionFormat : u8 { | |||
| 24 | std::string FormatTitleVersion(u32 version, | 24 | std::string FormatTitleVersion(u32 version, |
| 25 | TitleVersionFormat format = TitleVersionFormat::ThreeElements); | 25 | TitleVersionFormat format = TitleVersionFormat::ThreeElements); |
| 26 | 26 | ||
| 27 | enum class PatchType { | ||
| 28 | Update, | ||
| 29 | LayeredFS, | ||
| 30 | DLC, | ||
| 31 | }; | ||
| 32 | |||
| 33 | std::string FormatPatchTypeName(PatchType type); | ||
| 34 | |||
| 35 | // A centralized class to manage patches to games. | 27 | // A centralized class to manage patches to games. |
| 36 | class PatchManager { | 28 | class PatchManager { |
| 37 | public: | 29 | public: |
| @@ -42,6 +34,14 @@ public: | |||
| 42 | // - Game Updates | 34 | // - Game Updates |
| 43 | VirtualDir PatchExeFS(VirtualDir exefs) const; | 35 | VirtualDir PatchExeFS(VirtualDir exefs) const; |
| 44 | 36 | ||
| 37 | // Currently tracked NSO patches: | ||
| 38 | // - IPS | ||
| 39 | std::vector<u8> PatchNSO(const std::vector<u8>& nso) const; | ||
| 40 | |||
| 41 | // Checks to see if PatchNSO() will have any effect given the NSO's build ID. | ||
| 42 | // Used to prevent expensive copies in NSO loader. | ||
| 43 | bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; | ||
| 44 | |||
| 45 | // Currently tracked RomFS patches: | 45 | // Currently tracked RomFS patches: |
| 46 | // - Game Updates | 46 | // - Game Updates |
| 47 | // - LayeredFS | 47 | // - LayeredFS |
| @@ -49,8 +49,8 @@ public: | |||
| 49 | ContentRecordType type = ContentRecordType::Program) const; | 49 | ContentRecordType type = ContentRecordType::Program) const; |
| 50 | 50 | ||
| 51 | // Returns a vector of pairs between patch names and patch versions. | 51 | // Returns a vector of pairs between patch names and patch versions. |
| 52 | // i.e. Update v80 will return {Update, 80} | 52 | // i.e. Update 3.2.2 will return {"Update", "3.2.2"} |
| 53 | std::map<PatchType, std::string> GetPatchVersionNames() const; | 53 | std::map<std::string, std::string, std::less<>> GetPatchVersionNames() const; |
| 54 | 54 | ||
| 55 | // Given title_id of the program, attempts to get the control data of the update and parse it, | 55 | // Given title_id of the program, attempts to get the control data of the update and parse it, |
| 56 | // falling back to the base control data. | 56 | // falling back to the base control data. |
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 11264878d..09bf077cd 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp | |||
| @@ -18,6 +18,39 @@ | |||
| 18 | #include "core/loader/loader.h" | 18 | #include "core/loader/loader.h" |
| 19 | 19 | ||
| 20 | namespace FileSys { | 20 | namespace FileSys { |
| 21 | namespace { | ||
| 22 | void SetTicketKeys(const std::vector<VirtualFile>& files) { | ||
| 23 | Core::Crypto::KeyManager keys; | ||
| 24 | |||
| 25 | for (const auto& ticket_file : files) { | ||
| 26 | if (ticket_file == nullptr) { | ||
| 27 | continue; | ||
| 28 | } | ||
| 29 | |||
| 30 | if (ticket_file->GetExtension() != "tik") { | ||
| 31 | continue; | ||
| 32 | } | ||
| 33 | |||
| 34 | if (ticket_file->GetSize() < | ||
| 35 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | ||
| 36 | continue; | ||
| 37 | } | ||
| 38 | |||
| 39 | Core::Crypto::Key128 key{}; | ||
| 40 | ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | ||
| 41 | |||
| 42 | // We get the name without the extension in order to create the rights ID. | ||
| 43 | std::string name_only(ticket_file->GetName()); | ||
| 44 | name_only.erase(name_only.size() - 4); | ||
| 45 | |||
| 46 | const auto rights_id_raw = Common::HexStringToArray<16>(name_only); | ||
| 47 | u128 rights_id; | ||
| 48 | std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); | ||
| 49 | keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } // Anonymous namespace | ||
| 53 | |||
| 21 | NSP::NSP(VirtualFile file_) | 54 | NSP::NSP(VirtualFile file_) |
| 22 | : file(std::move(file_)), status{Loader::ResultStatus::Success}, | 55 | : file(std::move(file_)), status{Loader::ResultStatus::Success}, |
| 23 | pfs(std::make_shared<PartitionFilesystem>(file)) { | 56 | pfs(std::make_shared<PartitionFilesystem>(file)) { |
| @@ -26,83 +59,16 @@ NSP::NSP(VirtualFile file_) | |||
| 26 | return; | 59 | return; |
| 27 | } | 60 | } |
| 28 | 61 | ||
| 62 | const auto files = pfs->GetFiles(); | ||
| 63 | |||
| 29 | if (IsDirectoryExeFS(pfs)) { | 64 | if (IsDirectoryExeFS(pfs)) { |
| 30 | extracted = true; | 65 | extracted = true; |
| 31 | exefs = pfs; | 66 | InitializeExeFSAndRomFS(files); |
| 32 | |||
| 33 | const auto& files = pfs->GetFiles(); | ||
| 34 | const auto romfs_iter = | ||
| 35 | std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { | ||
| 36 | return file->GetName().find(".romfs") != std::string::npos; | ||
| 37 | }); | ||
| 38 | if (romfs_iter != files.end()) | ||
| 39 | romfs = *romfs_iter; | ||
| 40 | return; | 67 | return; |
| 41 | } | 68 | } |
| 42 | 69 | ||
| 43 | extracted = false; | 70 | SetTicketKeys(files); |
| 44 | const auto files = pfs->GetFiles(); | 71 | ReadNCAs(files); |
| 45 | |||
| 46 | Core::Crypto::KeyManager keys; | ||
| 47 | for (const auto& ticket_file : files) { | ||
| 48 | if (ticket_file->GetExtension() == "tik") { | ||
| 49 | if (ticket_file == nullptr || | ||
| 50 | ticket_file->GetSize() < | ||
| 51 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | ||
| 52 | continue; | ||
| 53 | } | ||
| 54 | |||
| 55 | Core::Crypto::Key128 key{}; | ||
| 56 | ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | ||
| 57 | std::string_view name_only(ticket_file->GetName()); | ||
| 58 | name_only.remove_suffix(4); | ||
| 59 | const auto rights_id_raw = Common::HexStringToArray<16>(name_only); | ||
| 60 | u128 rights_id; | ||
| 61 | std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); | ||
| 62 | keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | for (const auto& outer_file : files) { | ||
| 67 | if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") { | ||
| 68 | const auto nca = std::make_shared<NCA>(outer_file); | ||
| 69 | if (nca->GetStatus() != Loader::ResultStatus::Success) { | ||
| 70 | program_status[nca->GetTitleId()] = nca->GetStatus(); | ||
| 71 | continue; | ||
| 72 | } | ||
| 73 | |||
| 74 | const auto section0 = nca->GetSubdirectories()[0]; | ||
| 75 | |||
| 76 | for (const auto& inner_file : section0->GetFiles()) { | ||
| 77 | if (inner_file->GetExtension() != "cnmt") | ||
| 78 | continue; | ||
| 79 | |||
| 80 | const CNMT cnmt(inner_file); | ||
| 81 | auto& ncas_title = ncas[cnmt.GetTitleID()]; | ||
| 82 | |||
| 83 | ncas_title[ContentRecordType::Meta] = nca; | ||
| 84 | for (const auto& rec : cnmt.GetContentRecords()) { | ||
| 85 | const auto id_string = Common::HexArrayToString(rec.nca_id, false); | ||
| 86 | const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); | ||
| 87 | if (next_file == nullptr) { | ||
| 88 | LOG_WARNING(Service_FS, | ||
| 89 | "NCA with ID {}.nca is listed in content metadata, but cannot " | ||
| 90 | "be found in PFS. NSP appears to be corrupted.", | ||
| 91 | id_string); | ||
| 92 | continue; | ||
| 93 | } | ||
| 94 | |||
| 95 | auto next_nca = std::make_shared<NCA>(next_file); | ||
| 96 | if (next_nca->GetType() == NCAContentType::Program) | ||
| 97 | program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); | ||
| 98 | if (next_nca->GetStatus() == Loader::ResultStatus::Success) | ||
| 99 | ncas_title[rec.type] = std::move(next_nca); | ||
| 100 | } | ||
| 101 | |||
| 102 | break; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | 72 | } |
| 107 | 73 | ||
| 108 | NSP::~NSP() = default; | 74 | NSP::~NSP() = default; |
| @@ -242,4 +208,63 @@ VirtualDir NSP::GetParentDirectory() const { | |||
| 242 | bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | 208 | bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { |
| 243 | return false; | 209 | return false; |
| 244 | } | 210 | } |
| 211 | |||
| 212 | void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { | ||
| 213 | exefs = pfs; | ||
| 214 | |||
| 215 | const auto romfs_iter = std::find_if(files.begin(), files.end(), [](const VirtualFile& file) { | ||
| 216 | return file->GetName().rfind(".romfs") != std::string::npos; | ||
| 217 | }); | ||
| 218 | |||
| 219 | if (romfs_iter == files.end()) { | ||
| 220 | return; | ||
| 221 | } | ||
| 222 | |||
| 223 | romfs = *romfs_iter; | ||
| 224 | } | ||
| 225 | |||
| 226 | void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | ||
| 227 | for (const auto& outer_file : files) { | ||
| 228 | if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { | ||
| 229 | continue; | ||
| 230 | } | ||
| 231 | |||
| 232 | const auto nca = std::make_shared<NCA>(outer_file); | ||
| 233 | if (nca->GetStatus() != Loader::ResultStatus::Success) { | ||
| 234 | program_status[nca->GetTitleId()] = nca->GetStatus(); | ||
| 235 | continue; | ||
| 236 | } | ||
| 237 | |||
| 238 | const auto section0 = nca->GetSubdirectories()[0]; | ||
| 239 | |||
| 240 | for (const auto& inner_file : section0->GetFiles()) { | ||
| 241 | if (inner_file->GetExtension() != "cnmt") | ||
| 242 | continue; | ||
| 243 | |||
| 244 | const CNMT cnmt(inner_file); | ||
| 245 | auto& ncas_title = ncas[cnmt.GetTitleID()]; | ||
| 246 | |||
| 247 | ncas_title[ContentRecordType::Meta] = nca; | ||
| 248 | for (const auto& rec : cnmt.GetContentRecords()) { | ||
| 249 | const auto id_string = Common::HexArrayToString(rec.nca_id, false); | ||
| 250 | const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); | ||
| 251 | if (next_file == nullptr) { | ||
| 252 | LOG_WARNING(Service_FS, | ||
| 253 | "NCA with ID {}.nca is listed in content metadata, but cannot " | ||
| 254 | "be found in PFS. NSP appears to be corrupted.", | ||
| 255 | id_string); | ||
| 256 | continue; | ||
| 257 | } | ||
| 258 | |||
| 259 | auto next_nca = std::make_shared<NCA>(next_file); | ||
| 260 | if (next_nca->GetType() == NCAContentType::Program) | ||
| 261 | program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); | ||
| 262 | if (next_nca->GetStatus() == Loader::ResultStatus::Success) | ||
| 263 | ncas_title[rec.type] = std::move(next_nca); | ||
| 264 | } | ||
| 265 | |||
| 266 | break; | ||
| 267 | } | ||
| 268 | } | ||
| 269 | } | ||
| 245 | } // namespace FileSys | 270 | } // namespace FileSys |
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index e85a2b76e..da3dc5e9f 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h | |||
| @@ -59,9 +59,12 @@ protected: | |||
| 59 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | 59 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; |
| 60 | 60 | ||
| 61 | private: | 61 | private: |
| 62 | void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files); | ||
| 63 | void ReadNCAs(const std::vector<VirtualFile>& files); | ||
| 64 | |||
| 62 | VirtualFile file; | 65 | VirtualFile file; |
| 63 | 66 | ||
| 64 | bool extracted; | 67 | bool extracted = false; |
| 65 | Loader::ResultStatus status; | 68 | Loader::ResultStatus status; |
| 66 | std::map<u64, Loader::ResultStatus> program_status; | 69 | std::map<u64, Loader::ResultStatus> program_status; |
| 67 | 70 | ||
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 5bc947010..e961ef121 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp | |||
| @@ -209,7 +209,7 @@ static Kernel::Thread* FindThreadById(int id) { | |||
| 209 | for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { | 209 | for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { |
| 210 | const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); | 210 | const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); |
| 211 | for (auto& thread : threads) { | 211 | for (auto& thread : threads) { |
| 212 | if (thread->GetThreadId() == static_cast<u32>(id)) { | 212 | if (thread->GetThreadID() == static_cast<u32>(id)) { |
| 213 | current_core = core; | 213 | current_core = core; |
| 214 | return thread.get(); | 214 | return thread.get(); |
| 215 | } | 215 | } |
| @@ -223,16 +223,18 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) { | |||
| 223 | return 0; | 223 | return 0; |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | const auto& thread_context = thread->GetContext(); | ||
| 227 | |||
| 226 | if (id < SP_REGISTER) { | 228 | if (id < SP_REGISTER) { |
| 227 | return thread->context.cpu_registers[id]; | 229 | return thread_context.cpu_registers[id]; |
| 228 | } else if (id == SP_REGISTER) { | 230 | } else if (id == SP_REGISTER) { |
| 229 | return thread->context.sp; | 231 | return thread_context.sp; |
| 230 | } else if (id == PC_REGISTER) { | 232 | } else if (id == PC_REGISTER) { |
| 231 | return thread->context.pc; | 233 | return thread_context.pc; |
| 232 | } else if (id == PSTATE_REGISTER) { | 234 | } else if (id == PSTATE_REGISTER) { |
| 233 | return thread->context.pstate; | 235 | return thread_context.pstate; |
| 234 | } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { | 236 | } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { |
| 235 | return thread->context.vector_registers[id - UC_ARM64_REG_Q0][0]; | 237 | return thread_context.vector_registers[id - UC_ARM64_REG_Q0][0]; |
| 236 | } else { | 238 | } else { |
| 237 | return 0; | 239 | return 0; |
| 238 | } | 240 | } |
| @@ -243,16 +245,18 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) | |||
| 243 | return; | 245 | return; |
| 244 | } | 246 | } |
| 245 | 247 | ||
| 248 | auto& thread_context = thread->GetContext(); | ||
| 249 | |||
| 246 | if (id < SP_REGISTER) { | 250 | if (id < SP_REGISTER) { |
| 247 | thread->context.cpu_registers[id] = val; | 251 | thread_context.cpu_registers[id] = val; |
| 248 | } else if (id == SP_REGISTER) { | 252 | } else if (id == SP_REGISTER) { |
| 249 | thread->context.sp = val; | 253 | thread_context.sp = val; |
| 250 | } else if (id == PC_REGISTER) { | 254 | } else if (id == PC_REGISTER) { |
| 251 | thread->context.pc = val; | 255 | thread_context.pc = val; |
| 252 | } else if (id == PSTATE_REGISTER) { | 256 | } else if (id == PSTATE_REGISTER) { |
| 253 | thread->context.pstate = static_cast<u32>(val); | 257 | thread_context.pstate = static_cast<u32>(val); |
| 254 | } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { | 258 | } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { |
| 255 | thread->context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val; | 259 | thread_context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val; |
| 256 | } | 260 | } |
| 257 | } | 261 | } |
| 258 | 262 | ||
| @@ -595,7 +599,7 @@ static void HandleQuery() { | |||
| 595 | for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { | 599 | for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { |
| 596 | const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); | 600 | const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); |
| 597 | for (const auto& thread : threads) { | 601 | for (const auto& thread : threads) { |
| 598 | val += fmt::format("{:x}", thread->GetThreadId()); | 602 | val += fmt::format("{:x}", thread->GetThreadID()); |
| 599 | val += ","; | 603 | val += ","; |
| 600 | } | 604 | } |
| 601 | } | 605 | } |
| @@ -612,7 +616,7 @@ static void HandleQuery() { | |||
| 612 | for (const auto& thread : threads) { | 616 | for (const auto& thread : threads) { |
| 613 | buffer += | 617 | buffer += |
| 614 | fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*", | 618 | fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*", |
| 615 | thread->GetThreadId(), core, thread->GetThreadId()); | 619 | thread->GetThreadID(), core, thread->GetThreadID()); |
| 616 | } | 620 | } |
| 617 | } | 621 | } |
| 618 | buffer += "</threads>"; | 622 | buffer += "</threads>"; |
| @@ -693,7 +697,7 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) { | |||
| 693 | } | 697 | } |
| 694 | 698 | ||
| 695 | if (thread) { | 699 | if (thread) { |
| 696 | buffer += fmt::format(";thread:{:x};", thread->GetThreadId()); | 700 | buffer += fmt::format(";thread:{:x};", thread->GetThreadID()); |
| 697 | } | 701 | } |
| 698 | 702 | ||
| 699 | SendReply(buffer.c_str()); | 703 | SendReply(buffer.c_str()); |
| @@ -857,7 +861,9 @@ static void WriteRegister() { | |||
| 857 | } | 861 | } |
| 858 | 862 | ||
| 859 | // Update Unicorn context skipping scheduler, no running threads at this point | 863 | // Update Unicorn context skipping scheduler, no running threads at this point |
| 860 | Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context); | 864 | Core::System::GetInstance() |
| 865 | .ArmInterface(current_core) | ||
| 866 | .LoadContext(current_thread->GetContext()); | ||
| 861 | 867 | ||
| 862 | SendReply("OK"); | 868 | SendReply("OK"); |
| 863 | } | 869 | } |
| @@ -886,7 +892,9 @@ static void WriteRegisters() { | |||
| 886 | } | 892 | } |
| 887 | 893 | ||
| 888 | // Update Unicorn context skipping scheduler, no running threads at this point | 894 | // Update Unicorn context skipping scheduler, no running threads at this point |
| 889 | Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context); | 895 | Core::System::GetInstance() |
| 896 | .ArmInterface(current_core) | ||
| 897 | .LoadContext(current_thread->GetContext()); | ||
| 890 | 898 | ||
| 891 | SendReply("OK"); | 899 | SendReply("OK"); |
| 892 | } | 900 | } |
| @@ -960,7 +968,9 @@ static void Step() { | |||
| 960 | if (command_length > 1) { | 968 | if (command_length > 1) { |
| 961 | RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread); | 969 | RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread); |
| 962 | // Update Unicorn context skipping scheduler, no running threads at this point | 970 | // Update Unicorn context skipping scheduler, no running threads at this point |
| 963 | Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context); | 971 | Core::System::GetInstance() |
| 972 | .ArmInterface(current_core) | ||
| 973 | .LoadContext(current_thread->GetContext()); | ||
| 964 | } | 974 | } |
| 965 | step_loop = true; | 975 | step_loop = true; |
| 966 | halt_loop = true; | 976 | halt_loop = true; |
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 93577591f..ebf193930 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp | |||
| @@ -23,13 +23,13 @@ namespace AddressArbiter { | |||
| 23 | // Performs actual address waiting logic. | 23 | // Performs actual address waiting logic. |
| 24 | static ResultCode WaitForAddress(VAddr address, s64 timeout) { | 24 | static ResultCode WaitForAddress(VAddr address, s64 timeout) { |
| 25 | SharedPtr<Thread> current_thread = GetCurrentThread(); | 25 | SharedPtr<Thread> current_thread = GetCurrentThread(); |
| 26 | current_thread->arb_wait_address = address; | 26 | current_thread->SetArbiterWaitAddress(address); |
| 27 | current_thread->status = ThreadStatus::WaitArb; | 27 | current_thread->SetStatus(ThreadStatus::WaitArb); |
| 28 | current_thread->wakeup_callback = nullptr; | 28 | current_thread->InvalidateWakeupCallback(); |
| 29 | 29 | ||
| 30 | current_thread->WakeAfterDelay(timeout); | 30 | current_thread->WakeAfterDelay(timeout); |
| 31 | 31 | ||
| 32 | Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); | 32 | Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); |
| 33 | return RESULT_TIMEOUT; | 33 | return RESULT_TIMEOUT; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| @@ -39,10 +39,10 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) | |||
| 39 | std::vector<SharedPtr<Thread>>& waiting_threads, | 39 | std::vector<SharedPtr<Thread>>& waiting_threads, |
| 40 | VAddr arb_addr) { | 40 | VAddr arb_addr) { |
| 41 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); | 41 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); |
| 42 | auto& thread_list = scheduler->GetThreadList(); | 42 | const auto& thread_list = scheduler->GetThreadList(); |
| 43 | 43 | ||
| 44 | for (auto& thread : thread_list) { | 44 | for (const auto& thread : thread_list) { |
| 45 | if (thread->arb_wait_address == arb_addr) | 45 | if (thread->GetArbiterWaitAddress() == arb_addr) |
| 46 | waiting_threads.push_back(thread); | 46 | waiting_threads.push_back(thread); |
| 47 | } | 47 | } |
| 48 | }; | 48 | }; |
| @@ -57,7 +57,7 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) | |||
| 57 | // Sort them by priority, such that the highest priority ones come first. | 57 | // Sort them by priority, such that the highest priority ones come first. |
| 58 | std::sort(threads.begin(), threads.end(), | 58 | std::sort(threads.begin(), threads.end(), |
| 59 | [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { | 59 | [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { |
| 60 | return lhs->current_priority < rhs->current_priority; | 60 | return lhs->GetPriority() < rhs->GetPriority(); |
| 61 | }); | 61 | }); |
| 62 | 62 | ||
| 63 | return threads; | 63 | return threads; |
| @@ -73,9 +73,9 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num | |||
| 73 | 73 | ||
| 74 | // Signal the waiting threads. | 74 | // Signal the waiting threads. |
| 75 | for (std::size_t i = 0; i < last; i++) { | 75 | for (std::size_t i = 0; i < last; i++) { |
| 76 | ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb); | 76 | ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb); |
| 77 | waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); | 77 | waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); |
| 78 | waiting_threads[i]->arb_wait_address = 0; | 78 | waiting_threads[i]->SetArbiterWaitAddress(0); |
| 79 | waiting_threads[i]->ResumeFromWait(); | 79 | waiting_threads[i]->ResumeFromWait(); |
| 80 | } | 80 | } |
| 81 | } | 81 | } |
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 72fb9d250..edad5f1b1 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp | |||
| @@ -42,14 +42,14 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread, | |||
| 42 | Kernel::SharedPtr<Kernel::Event> event) { | 42 | Kernel::SharedPtr<Kernel::Event> event) { |
| 43 | 43 | ||
| 44 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. | 44 | // Put the client thread to sleep until the wait event is signaled or the timeout expires. |
| 45 | thread->wakeup_callback = [context = *this, callback]( | 45 | thread->SetWakeupCallback([context = *this, callback]( |
| 46 | ThreadWakeupReason reason, SharedPtr<Thread> thread, | 46 | ThreadWakeupReason reason, SharedPtr<Thread> thread, |
| 47 | SharedPtr<WaitObject> object, std::size_t index) mutable -> bool { | 47 | SharedPtr<WaitObject> object, std::size_t index) mutable -> bool { |
| 48 | ASSERT(thread->status == ThreadStatus::WaitHLEEvent); | 48 | ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent); |
| 49 | callback(thread, context, reason); | 49 | callback(thread, context, reason); |
| 50 | context.WriteToOutgoingCommandBuffer(*thread); | 50 | context.WriteToOutgoingCommandBuffer(*thread); |
| 51 | return true; | 51 | return true; |
| 52 | }; | 52 | }); |
| 53 | 53 | ||
| 54 | if (!event) { | 54 | if (!event) { |
| 55 | // Create event if not provided | 55 | // Create event if not provided |
| @@ -59,8 +59,8 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread, | |||
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | event->Clear(); | 61 | event->Clear(); |
| 62 | thread->status = ThreadStatus::WaitHLEEvent; | 62 | thread->SetStatus(ThreadStatus::WaitHLEEvent); |
| 63 | thread->wait_objects = {event}; | 63 | thread->SetWaitObjects({event}); |
| 64 | event->AddWaitingThread(thread); | 64 | event->AddWaitingThread(thread); |
| 65 | 65 | ||
| 66 | if (timeout > 0) { | 66 | if (timeout > 0) { |
| @@ -209,7 +209,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb | |||
| 209 | 209 | ||
| 210 | ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) { | 210 | ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) { |
| 211 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; | 211 | std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; |
| 212 | Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), | 212 | Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), |
| 213 | dst_cmdbuf.size() * sizeof(u32)); | 213 | dst_cmdbuf.size() * sizeof(u32)); |
| 214 | 214 | ||
| 215 | // The header was already built in the internal command buffer. Attempt to parse it to verify | 215 | // The header was already built in the internal command buffer. Attempt to parse it to verify |
| @@ -268,7 +268,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) | |||
| 268 | } | 268 | } |
| 269 | 269 | ||
| 270 | // Copy the translated command buffer back into the thread's command buffer area. | 270 | // Copy the translated command buffer back into the thread's command buffer area. |
| 271 | Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), | 271 | Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), |
| 272 | dst_cmdbuf.size() * sizeof(u32)); | 272 | dst_cmdbuf.size() * sizeof(u32)); |
| 273 | 273 | ||
| 274 | return RESULT_SUCCESS; | 274 | return RESULT_SUCCESS; |
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 3e0800a71..98eb74298 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -46,40 +46,40 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_ | |||
| 46 | 46 | ||
| 47 | bool resume = true; | 47 | bool resume = true; |
| 48 | 48 | ||
| 49 | if (thread->status == ThreadStatus::WaitSynchAny || | 49 | if (thread->GetStatus() == ThreadStatus::WaitSynchAny || |
| 50 | thread->status == ThreadStatus::WaitSynchAll || | 50 | thread->GetStatus() == ThreadStatus::WaitSynchAll || |
| 51 | thread->status == ThreadStatus::WaitHLEEvent) { | 51 | thread->GetStatus() == ThreadStatus::WaitHLEEvent) { |
| 52 | // Remove the thread from each of its waiting objects' waitlists | 52 | // Remove the thread from each of its waiting objects' waitlists |
| 53 | for (auto& object : thread->wait_objects) { | 53 | for (const auto& object : thread->GetWaitObjects()) { |
| 54 | object->RemoveWaitingThread(thread.get()); | 54 | object->RemoveWaitingThread(thread.get()); |
| 55 | } | 55 | } |
| 56 | thread->wait_objects.clear(); | 56 | thread->ClearWaitObjects(); |
| 57 | 57 | ||
| 58 | // Invoke the wakeup callback before clearing the wait objects | 58 | // Invoke the wakeup callback before clearing the wait objects |
| 59 | if (thread->wakeup_callback) { | 59 | if (thread->HasWakeupCallback()) { |
| 60 | resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0); | 60 | resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0); |
| 61 | } | 61 | } |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 || | 64 | if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 || |
| 65 | thread->wait_handle) { | 65 | thread->GetWaitHandle() != 0) { |
| 66 | ASSERT(thread->status == ThreadStatus::WaitMutex); | 66 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); |
| 67 | thread->mutex_wait_address = 0; | 67 | thread->SetMutexWaitAddress(0); |
| 68 | thread->condvar_wait_address = 0; | 68 | thread->SetCondVarWaitAddress(0); |
| 69 | thread->wait_handle = 0; | 69 | thread->SetWaitHandle(0); |
| 70 | 70 | ||
| 71 | auto lock_owner = thread->lock_owner; | 71 | auto* const lock_owner = thread->GetLockOwner(); |
| 72 | // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance | 72 | // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance |
| 73 | // and don't have a lock owner unless SignalProcessWideKey was called first and the thread | 73 | // and don't have a lock owner unless SignalProcessWideKey was called first and the thread |
| 74 | // wasn't awakened due to the mutex already being acquired. | 74 | // wasn't awakened due to the mutex already being acquired. |
| 75 | if (lock_owner) { | 75 | if (lock_owner != nullptr) { |
| 76 | lock_owner->RemoveMutexWaiter(thread); | 76 | lock_owner->RemoveMutexWaiter(thread); |
| 77 | } | 77 | } |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | if (thread->arb_wait_address != 0) { | 80 | if (thread->GetArbiterWaitAddress() != 0) { |
| 81 | ASSERT(thread->status == ThreadStatus::WaitArb); | 81 | ASSERT(thread->GetStatus() == ThreadStatus::WaitArb); |
| 82 | thread->arb_wait_address = 0; | 82 | thread->SetArbiterWaitAddress(0); |
| 83 | } | 83 | } |
| 84 | 84 | ||
| 85 | if (resume) { | 85 | if (resume) { |
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 81675eac5..dd541ffcc 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp | |||
| @@ -28,11 +28,11 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread( | |||
| 28 | SharedPtr<Thread> highest_priority_thread; | 28 | SharedPtr<Thread> highest_priority_thread; |
| 29 | u32 num_waiters = 0; | 29 | u32 num_waiters = 0; |
| 30 | 30 | ||
| 31 | for (auto& thread : current_thread->wait_mutex_threads) { | 31 | for (const auto& thread : current_thread->GetMutexWaitingThreads()) { |
| 32 | if (thread->mutex_wait_address != mutex_addr) | 32 | if (thread->GetMutexWaitAddress() != mutex_addr) |
| 33 | continue; | 33 | continue; |
| 34 | 34 | ||
| 35 | ASSERT(thread->status == ThreadStatus::WaitMutex); | 35 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); |
| 36 | 36 | ||
| 37 | ++num_waiters; | 37 | ++num_waiters; |
| 38 | if (highest_priority_thread == nullptr || | 38 | if (highest_priority_thread == nullptr || |
| @@ -47,12 +47,12 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread( | |||
| 47 | /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. | 47 | /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. |
| 48 | static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread, | 48 | static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread, |
| 49 | SharedPtr<Thread> new_owner) { | 49 | SharedPtr<Thread> new_owner) { |
| 50 | auto threads = current_thread->wait_mutex_threads; | 50 | const auto threads = current_thread->GetMutexWaitingThreads(); |
| 51 | for (auto& thread : threads) { | 51 | for (const auto& thread : threads) { |
| 52 | if (thread->mutex_wait_address != mutex_addr) | 52 | if (thread->GetMutexWaitAddress() != mutex_addr) |
| 53 | continue; | 53 | continue; |
| 54 | 54 | ||
| 55 | ASSERT(thread->lock_owner == current_thread); | 55 | ASSERT(thread->GetLockOwner() == current_thread); |
| 56 | current_thread->RemoveMutexWaiter(thread); | 56 | current_thread->RemoveMutexWaiter(thread); |
| 57 | if (new_owner != thread) | 57 | if (new_owner != thread) |
| 58 | new_owner->AddMutexWaiter(thread); | 58 | new_owner->AddMutexWaiter(thread); |
| @@ -84,11 +84,11 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho | |||
| 84 | return ERR_INVALID_HANDLE; | 84 | return ERR_INVALID_HANDLE; |
| 85 | 85 | ||
| 86 | // Wait until the mutex is released | 86 | // Wait until the mutex is released |
| 87 | GetCurrentThread()->mutex_wait_address = address; | 87 | GetCurrentThread()->SetMutexWaitAddress(address); |
| 88 | GetCurrentThread()->wait_handle = requesting_thread_handle; | 88 | GetCurrentThread()->SetWaitHandle(requesting_thread_handle); |
| 89 | 89 | ||
| 90 | GetCurrentThread()->status = ThreadStatus::WaitMutex; | 90 | GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex); |
| 91 | GetCurrentThread()->wakeup_callback = nullptr; | 91 | GetCurrentThread()->InvalidateWakeupCallback(); |
| 92 | 92 | ||
| 93 | // Update the lock holder thread's priority to prevent priority inversion. | 93 | // Update the lock holder thread's priority to prevent priority inversion. |
| 94 | holding_thread->AddMutexWaiter(GetCurrentThread()); | 94 | holding_thread->AddMutexWaiter(GetCurrentThread()); |
| @@ -115,7 +115,7 @@ ResultCode Mutex::Release(VAddr address) { | |||
| 115 | // Transfer the ownership of the mutex from the previous owner to the new one. | 115 | // Transfer the ownership of the mutex from the previous owner to the new one. |
| 116 | TransferMutexOwnership(address, GetCurrentThread(), thread); | 116 | TransferMutexOwnership(address, GetCurrentThread(), thread); |
| 117 | 117 | ||
| 118 | u32 mutex_value = thread->wait_handle; | 118 | u32 mutex_value = thread->GetWaitHandle(); |
| 119 | 119 | ||
| 120 | if (num_waiters >= 2) { | 120 | if (num_waiters >= 2) { |
| 121 | // Notify the guest that there are still some threads waiting for the mutex | 121 | // Notify the guest that there are still some threads waiting for the mutex |
| @@ -125,13 +125,13 @@ ResultCode Mutex::Release(VAddr address) { | |||
| 125 | // Grant the mutex to the next waiting thread and resume it. | 125 | // Grant the mutex to the next waiting thread and resume it. |
| 126 | Memory::Write32(address, mutex_value); | 126 | Memory::Write32(address, mutex_value); |
| 127 | 127 | ||
| 128 | ASSERT(thread->status == ThreadStatus::WaitMutex); | 128 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); |
| 129 | thread->ResumeFromWait(); | 129 | thread->ResumeFromWait(); |
| 130 | 130 | ||
| 131 | thread->lock_owner = nullptr; | 131 | thread->SetLockOwner(nullptr); |
| 132 | thread->condvar_wait_address = 0; | 132 | thread->SetCondVarWaitAddress(0); |
| 133 | thread->mutex_wait_address = 0; | 133 | thread->SetMutexWaitAddress(0); |
| 134 | thread->wait_handle = 0; | 134 | thread->SetWaitHandle(0); |
| 135 | 135 | ||
| 136 | return RESULT_SUCCESS; | 136 | return RESULT_SUCCESS; |
| 137 | } | 137 | } |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index dc9fc8470..fb0027a71 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -144,15 +144,15 @@ void Process::PrepareForTermination() { | |||
| 144 | 144 | ||
| 145 | const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) { | 145 | const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) { |
| 146 | for (auto& thread : thread_list) { | 146 | for (auto& thread : thread_list) { |
| 147 | if (thread->owner_process != this) | 147 | if (thread->GetOwnerProcess() != this) |
| 148 | continue; | 148 | continue; |
| 149 | 149 | ||
| 150 | if (thread == GetCurrentThread()) | 150 | if (thread == GetCurrentThread()) |
| 151 | continue; | 151 | continue; |
| 152 | 152 | ||
| 153 | // TODO(Subv): When are the other running/ready threads terminated? | 153 | // TODO(Subv): When are the other running/ready threads terminated? |
| 154 | ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny || | 154 | ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny || |
| 155 | thread->status == ThreadStatus::WaitSynchAll, | 155 | thread->GetStatus() == ThreadStatus::WaitSynchAll, |
| 156 | "Exiting processes with non-waiting threads is currently unimplemented"); | 156 | "Exiting processes with non-waiting threads is currently unimplemented"); |
| 157 | 157 | ||
| 158 | thread->Stop(); | 158 | thread->Stop(); |
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 1e82cfffb..cfd6e1bad 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -38,10 +38,10 @@ Thread* Scheduler::PopNextReadyThread() { | |||
| 38 | Thread* next = nullptr; | 38 | Thread* next = nullptr; |
| 39 | Thread* thread = GetCurrentThread(); | 39 | Thread* thread = GetCurrentThread(); |
| 40 | 40 | ||
| 41 | if (thread && thread->status == ThreadStatus::Running) { | 41 | if (thread && thread->GetStatus() == ThreadStatus::Running) { |
| 42 | // We have to do better than the current thread. | 42 | // We have to do better than the current thread. |
| 43 | // This call returns null when that's not possible. | 43 | // This call returns null when that's not possible. |
| 44 | next = ready_queue.pop_first_better(thread->current_priority); | 44 | next = ready_queue.pop_first_better(thread->GetPriority()); |
| 45 | if (!next) { | 45 | if (!next) { |
| 46 | // Otherwise just keep going with the current thread | 46 | // Otherwise just keep going with the current thread |
| 47 | next = thread; | 47 | next = thread; |
| @@ -58,22 +58,21 @@ void Scheduler::SwitchContext(Thread* new_thread) { | |||
| 58 | 58 | ||
| 59 | // Save context for previous thread | 59 | // Save context for previous thread |
| 60 | if (previous_thread) { | 60 | if (previous_thread) { |
| 61 | previous_thread->last_running_ticks = CoreTiming::GetTicks(); | 61 | cpu_core.SaveContext(previous_thread->GetContext()); |
| 62 | cpu_core.SaveContext(previous_thread->context); | ||
| 63 | // Save the TPIDR_EL0 system register in case it was modified. | 62 | // Save the TPIDR_EL0 system register in case it was modified. |
| 64 | previous_thread->tpidr_el0 = cpu_core.GetTPIDR_EL0(); | 63 | previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); |
| 65 | 64 | ||
| 66 | if (previous_thread->status == ThreadStatus::Running) { | 65 | if (previous_thread->GetStatus() == ThreadStatus::Running) { |
| 67 | // This is only the case when a reschedule is triggered without the current thread | 66 | // This is only the case when a reschedule is triggered without the current thread |
| 68 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) | 67 | // yielding execution (i.e. an event triggered, system core time-sliced, etc) |
| 69 | ready_queue.push_front(previous_thread->current_priority, previous_thread); | 68 | ready_queue.push_front(previous_thread->GetPriority(), previous_thread); |
| 70 | previous_thread->status = ThreadStatus::Ready; | 69 | previous_thread->SetStatus(ThreadStatus::Ready); |
| 71 | } | 70 | } |
| 72 | } | 71 | } |
| 73 | 72 | ||
| 74 | // Load context of new thread | 73 | // Load context of new thread |
| 75 | if (new_thread) { | 74 | if (new_thread) { |
| 76 | ASSERT_MSG(new_thread->status == ThreadStatus::Ready, | 75 | ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, |
| 77 | "Thread must be ready to become running."); | 76 | "Thread must be ready to become running."); |
| 78 | 77 | ||
| 79 | // Cancel any outstanding wakeup events for this thread | 78 | // Cancel any outstanding wakeup events for this thread |
| @@ -83,15 +82,16 @@ void Scheduler::SwitchContext(Thread* new_thread) { | |||
| 83 | 82 | ||
| 84 | current_thread = new_thread; | 83 | current_thread = new_thread; |
| 85 | 84 | ||
| 86 | ready_queue.remove(new_thread->current_priority, new_thread); | 85 | ready_queue.remove(new_thread->GetPriority(), new_thread); |
| 87 | new_thread->status = ThreadStatus::Running; | 86 | new_thread->SetStatus(ThreadStatus::Running); |
| 88 | 87 | ||
| 89 | if (previous_process != current_thread->owner_process) { | 88 | const auto thread_owner_process = current_thread->GetOwnerProcess(); |
| 90 | Core::CurrentProcess() = current_thread->owner_process; | 89 | if (previous_process != thread_owner_process) { |
| 90 | Core::CurrentProcess() = thread_owner_process; | ||
| 91 | SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table); | 91 | SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table); |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | cpu_core.LoadContext(new_thread->context); | 94 | cpu_core.LoadContext(new_thread->GetContext()); |
| 95 | cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); | 95 | cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); |
| 96 | cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); | 96 | cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); |
| 97 | cpu_core.ClearExclusiveState(); | 97 | cpu_core.ClearExclusiveState(); |
| @@ -136,14 +136,14 @@ void Scheduler::RemoveThread(Thread* thread) { | |||
| 136 | void Scheduler::ScheduleThread(Thread* thread, u32 priority) { | 136 | void Scheduler::ScheduleThread(Thread* thread, u32 priority) { |
| 137 | std::lock_guard<std::mutex> lock(scheduler_mutex); | 137 | std::lock_guard<std::mutex> lock(scheduler_mutex); |
| 138 | 138 | ||
| 139 | ASSERT(thread->status == ThreadStatus::Ready); | 139 | ASSERT(thread->GetStatus() == ThreadStatus::Ready); |
| 140 | ready_queue.push_back(priority, thread); | 140 | ready_queue.push_back(priority, thread); |
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { | 143 | void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { |
| 144 | std::lock_guard<std::mutex> lock(scheduler_mutex); | 144 | std::lock_guard<std::mutex> lock(scheduler_mutex); |
| 145 | 145 | ||
| 146 | ASSERT(thread->status == ThreadStatus::Ready); | 146 | ASSERT(thread->GetStatus() == ThreadStatus::Ready); |
| 147 | ready_queue.remove(priority, thread); | 147 | ready_queue.remove(priority, thread); |
| 148 | } | 148 | } |
| 149 | 149 | ||
| @@ -151,8 +151,8 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { | |||
| 151 | std::lock_guard<std::mutex> lock(scheduler_mutex); | 151 | std::lock_guard<std::mutex> lock(scheduler_mutex); |
| 152 | 152 | ||
| 153 | // If thread was ready, adjust queues | 153 | // If thread was ready, adjust queues |
| 154 | if (thread->status == ThreadStatus::Ready) | 154 | if (thread->GetStatus() == ThreadStatus::Ready) |
| 155 | ready_queue.move(thread, thread->current_priority, priority); | 155 | ready_queue.move(thread, thread->GetPriority(), priority); |
| 156 | else | 156 | else |
| 157 | ready_queue.prepare(priority); | 157 | ready_queue.prepare(priority); |
| 158 | } | 158 | } |
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index aba0cab96..1ece691c7 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp | |||
| @@ -120,10 +120,10 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { | |||
| 120 | result = hle_handler->HandleSyncRequest(context); | 120 | result = hle_handler->HandleSyncRequest(context); |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | if (thread->status == ThreadStatus::Running) { | 123 | if (thread->GetStatus() == ThreadStatus::Running) { |
| 124 | // Put the thread to sleep until the server replies, it will be awoken in | 124 | // Put the thread to sleep until the server replies, it will be awoken in |
| 125 | // svcReplyAndReceive for LLE servers. | 125 | // svcReplyAndReceive for LLE servers. |
| 126 | thread->status = ThreadStatus::WaitIPC; | 126 | thread->SetStatus(ThreadStatus::WaitIPC); |
| 127 | 127 | ||
| 128 | if (hle_handler != nullptr) { | 128 | if (hle_handler != nullptr) { |
| 129 | // For HLE services, we put the request threads to sleep for a short duration to | 129 | // For HLE services, we put the request threads to sleep for a short duration to |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 1cdaa740a..6c4af7e47 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -156,7 +156,7 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) { | |||
| 156 | return ERR_INVALID_HANDLE; | 156 | return ERR_INVALID_HANDLE; |
| 157 | } | 157 | } |
| 158 | 158 | ||
| 159 | *thread_id = thread->GetThreadId(); | 159 | *thread_id = thread->GetThreadID(); |
| 160 | return RESULT_SUCCESS; | 160 | return RESULT_SUCCESS; |
| 161 | } | 161 | } |
| 162 | 162 | ||
| @@ -177,7 +177,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) { | |||
| 177 | /// Default thread wakeup callback for WaitSynchronization | 177 | /// Default thread wakeup callback for WaitSynchronization |
| 178 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, | 178 | static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, |
| 179 | SharedPtr<WaitObject> object, std::size_t index) { | 179 | SharedPtr<WaitObject> object, std::size_t index) { |
| 180 | ASSERT(thread->status == ThreadStatus::WaitSynchAny); | 180 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); |
| 181 | 181 | ||
| 182 | if (reason == ThreadWakeupReason::Timeout) { | 182 | if (reason == ThreadWakeupReason::Timeout) { |
| 183 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); | 183 | thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); |
| @@ -204,10 +204,10 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 | |||
| 204 | if (handle_count > MaxHandles) | 204 | if (handle_count > MaxHandles) |
| 205 | return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge); | 205 | return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge); |
| 206 | 206 | ||
| 207 | auto thread = GetCurrentThread(); | 207 | auto* const thread = GetCurrentThread(); |
| 208 | 208 | ||
| 209 | using ObjectPtr = SharedPtr<WaitObject>; | 209 | using ObjectPtr = Thread::ThreadWaitObjects::value_type; |
| 210 | std::vector<ObjectPtr> objects(handle_count); | 210 | Thread::ThreadWaitObjects objects(handle_count); |
| 211 | auto& kernel = Core::System::GetInstance().Kernel(); | 211 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 212 | 212 | ||
| 213 | for (u64 i = 0; i < handle_count; ++i) { | 213 | for (u64 i = 0; i < handle_count; ++i) { |
| @@ -244,14 +244,14 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 | |||
| 244 | for (auto& object : objects) | 244 | for (auto& object : objects) |
| 245 | object->AddWaitingThread(thread); | 245 | object->AddWaitingThread(thread); |
| 246 | 246 | ||
| 247 | thread->wait_objects = std::move(objects); | 247 | thread->SetWaitObjects(std::move(objects)); |
| 248 | thread->status = ThreadStatus::WaitSynchAny; | 248 | thread->SetStatus(ThreadStatus::WaitSynchAny); |
| 249 | 249 | ||
| 250 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 250 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 251 | thread->WakeAfterDelay(nano_seconds); | 251 | thread->WakeAfterDelay(nano_seconds); |
| 252 | thread->wakeup_callback = DefaultThreadWakeupCallback; | 252 | thread->SetWakeupCallback(DefaultThreadWakeupCallback); |
| 253 | 253 | ||
| 254 | Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); | 254 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 255 | 255 | ||
| 256 | return RESULT_TIMEOUT; | 256 | return RESULT_TIMEOUT; |
| 257 | } | 257 | } |
| @@ -266,7 +266,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) { | |||
| 266 | return ERR_INVALID_HANDLE; | 266 | return ERR_INVALID_HANDLE; |
| 267 | } | 267 | } |
| 268 | 268 | ||
| 269 | ASSERT(thread->status == ThreadStatus::WaitSynchAny); | 269 | ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); |
| 270 | thread->SetWaitSynchronizationResult( | 270 | thread->SetWaitSynchronizationResult( |
| 271 | ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled)); | 271 | ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled)); |
| 272 | thread->ResumeFromWait(); | 272 | thread->ResumeFromWait(); |
| @@ -425,7 +425,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { | |||
| 425 | } | 425 | } |
| 426 | 426 | ||
| 427 | const auto current_process = Core::CurrentProcess(); | 427 | const auto current_process = Core::CurrentProcess(); |
| 428 | if (thread->owner_process != current_process) { | 428 | if (thread->GetOwnerProcess() != current_process) { |
| 429 | return ERR_INVALID_HANDLE; | 429 | return ERR_INVALID_HANDLE; |
| 430 | } | 430 | } |
| 431 | 431 | ||
| @@ -433,7 +433,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { | |||
| 433 | return ERR_ALREADY_REGISTERED; | 433 | return ERR_ALREADY_REGISTERED; |
| 434 | } | 434 | } |
| 435 | 435 | ||
| 436 | Core::ARM_Interface::ThreadContext ctx = thread->context; | 436 | Core::ARM_Interface::ThreadContext ctx = thread->GetContext(); |
| 437 | // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. | 437 | // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. |
| 438 | ctx.pstate &= 0xFF0FFE20; | 438 | ctx.pstate &= 0xFF0FFE20; |
| 439 | 439 | ||
| @@ -479,14 +479,14 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { | |||
| 479 | 479 | ||
| 480 | thread->SetPriority(priority); | 480 | thread->SetPriority(priority); |
| 481 | 481 | ||
| 482 | Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); | 482 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 483 | return RESULT_SUCCESS; | 483 | return RESULT_SUCCESS; |
| 484 | } | 484 | } |
| 485 | 485 | ||
| 486 | /// Get which CPU core is executing the current thread | 486 | /// Get which CPU core is executing the current thread |
| 487 | static u32 GetCurrentProcessorNumber() { | 487 | static u32 GetCurrentProcessorNumber() { |
| 488 | LOG_TRACE(Kernel_SVC, "called"); | 488 | LOG_TRACE(Kernel_SVC, "called"); |
| 489 | return GetCurrentThread()->processor_id; | 489 | return GetCurrentThread()->GetProcessorID(); |
| 490 | } | 490 | } |
| 491 | 491 | ||
| 492 | static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size, | 492 | static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size, |
| @@ -622,10 +622,14 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 622 | CASCADE_RESULT(SharedPtr<Thread> thread, | 622 | CASCADE_RESULT(SharedPtr<Thread> thread, |
| 623 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, | 623 | Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, |
| 624 | Core::CurrentProcess())); | 624 | Core::CurrentProcess())); |
| 625 | CASCADE_RESULT(thread->guest_handle, kernel.HandleTable().Create(thread)); | 625 | const auto new_guest_handle = kernel.HandleTable().Create(thread); |
| 626 | *out_handle = thread->guest_handle; | 626 | if (new_guest_handle.Failed()) { |
| 627 | return new_guest_handle.Code(); | ||
| 628 | } | ||
| 629 | thread->SetGuestHandle(*new_guest_handle); | ||
| 630 | *out_handle = *new_guest_handle; | ||
| 627 | 631 | ||
| 628 | Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); | 632 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 629 | 633 | ||
| 630 | LOG_TRACE(Kernel_SVC, | 634 | LOG_TRACE(Kernel_SVC, |
| 631 | "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, " | 635 | "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, " |
| @@ -645,10 +649,10 @@ static ResultCode StartThread(Handle thread_handle) { | |||
| 645 | return ERR_INVALID_HANDLE; | 649 | return ERR_INVALID_HANDLE; |
| 646 | } | 650 | } |
| 647 | 651 | ||
| 648 | ASSERT(thread->status == ThreadStatus::Dormant); | 652 | ASSERT(thread->GetStatus() == ThreadStatus::Dormant); |
| 649 | 653 | ||
| 650 | thread->ResumeFromWait(); | 654 | thread->ResumeFromWait(); |
| 651 | Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); | 655 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 652 | 656 | ||
| 653 | return RESULT_SUCCESS; | 657 | return RESULT_SUCCESS; |
| 654 | } | 658 | } |
| @@ -694,17 +698,17 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var | |||
| 694 | CASCADE_CODE(Mutex::Release(mutex_addr)); | 698 | CASCADE_CODE(Mutex::Release(mutex_addr)); |
| 695 | 699 | ||
| 696 | SharedPtr<Thread> current_thread = GetCurrentThread(); | 700 | SharedPtr<Thread> current_thread = GetCurrentThread(); |
| 697 | current_thread->condvar_wait_address = condition_variable_addr; | 701 | current_thread->SetCondVarWaitAddress(condition_variable_addr); |
| 698 | current_thread->mutex_wait_address = mutex_addr; | 702 | current_thread->SetMutexWaitAddress(mutex_addr); |
| 699 | current_thread->wait_handle = thread_handle; | 703 | current_thread->SetWaitHandle(thread_handle); |
| 700 | current_thread->status = ThreadStatus::WaitMutex; | 704 | current_thread->SetStatus(ThreadStatus::WaitMutex); |
| 701 | current_thread->wakeup_callback = nullptr; | 705 | current_thread->InvalidateWakeupCallback(); |
| 702 | 706 | ||
| 703 | current_thread->WakeAfterDelay(nano_seconds); | 707 | current_thread->WakeAfterDelay(nano_seconds); |
| 704 | 708 | ||
| 705 | // Note: Deliberately don't attempt to inherit the lock owner's priority. | 709 | // Note: Deliberately don't attempt to inherit the lock owner's priority. |
| 706 | 710 | ||
| 707 | Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); | 711 | Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); |
| 708 | return RESULT_SUCCESS; | 712 | return RESULT_SUCCESS; |
| 709 | } | 713 | } |
| 710 | 714 | ||
| @@ -713,14 +717,14 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 713 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", | 717 | LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", |
| 714 | condition_variable_addr, target); | 718 | condition_variable_addr, target); |
| 715 | 719 | ||
| 716 | auto RetrieveWaitingThreads = [](std::size_t core_index, | 720 | const auto RetrieveWaitingThreads = [](std::size_t core_index, |
| 717 | std::vector<SharedPtr<Thread>>& waiting_threads, | 721 | std::vector<SharedPtr<Thread>>& waiting_threads, |
| 718 | VAddr condvar_addr) { | 722 | VAddr condvar_addr) { |
| 719 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); | 723 | const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); |
| 720 | auto& thread_list = scheduler->GetThreadList(); | 724 | const auto& thread_list = scheduler->GetThreadList(); |
| 721 | 725 | ||
| 722 | for (auto& thread : thread_list) { | 726 | for (const auto& thread : thread_list) { |
| 723 | if (thread->condvar_wait_address == condvar_addr) | 727 | if (thread->GetCondVarWaitAddress() == condvar_addr) |
| 724 | waiting_threads.push_back(thread); | 728 | waiting_threads.push_back(thread); |
| 725 | } | 729 | } |
| 726 | }; | 730 | }; |
| @@ -734,7 +738,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 734 | // Sort them by priority, such that the highest priority ones come first. | 738 | // Sort them by priority, such that the highest priority ones come first. |
| 735 | std::sort(waiting_threads.begin(), waiting_threads.end(), | 739 | std::sort(waiting_threads.begin(), waiting_threads.end(), |
| 736 | [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { | 740 | [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { |
| 737 | return lhs->current_priority < rhs->current_priority; | 741 | return lhs->GetPriority() < rhs->GetPriority(); |
| 738 | }); | 742 | }); |
| 739 | 743 | ||
| 740 | // Only process up to 'target' threads, unless 'target' is -1, in which case process | 744 | // Only process up to 'target' threads, unless 'target' is -1, in which case process |
| @@ -750,7 +754,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 750 | for (std::size_t index = 0; index < last; ++index) { | 754 | for (std::size_t index = 0; index < last; ++index) { |
| 751 | auto& thread = waiting_threads[index]; | 755 | auto& thread = waiting_threads[index]; |
| 752 | 756 | ||
| 753 | ASSERT(thread->condvar_wait_address == condition_variable_addr); | 757 | ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); |
| 754 | 758 | ||
| 755 | std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); | 759 | std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); |
| 756 | 760 | ||
| @@ -759,42 +763,43 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 759 | // Atomically read the value of the mutex. | 763 | // Atomically read the value of the mutex. |
| 760 | u32 mutex_val = 0; | 764 | u32 mutex_val = 0; |
| 761 | do { | 765 | do { |
| 762 | monitor.SetExclusive(current_core, thread->mutex_wait_address); | 766 | monitor.SetExclusive(current_core, thread->GetMutexWaitAddress()); |
| 763 | 767 | ||
| 764 | // If the mutex is not yet acquired, acquire it. | 768 | // If the mutex is not yet acquired, acquire it. |
| 765 | mutex_val = Memory::Read32(thread->mutex_wait_address); | 769 | mutex_val = Memory::Read32(thread->GetMutexWaitAddress()); |
| 766 | 770 | ||
| 767 | if (mutex_val != 0) { | 771 | if (mutex_val != 0) { |
| 768 | monitor.ClearExclusive(); | 772 | monitor.ClearExclusive(); |
| 769 | break; | 773 | break; |
| 770 | } | 774 | } |
| 771 | } while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address, | 775 | } while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(), |
| 772 | thread->wait_handle)); | 776 | thread->GetWaitHandle())); |
| 773 | 777 | ||
| 774 | if (mutex_val == 0) { | 778 | if (mutex_val == 0) { |
| 775 | // We were able to acquire the mutex, resume this thread. | 779 | // We were able to acquire the mutex, resume this thread. |
| 776 | ASSERT(thread->status == ThreadStatus::WaitMutex); | 780 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); |
| 777 | thread->ResumeFromWait(); | 781 | thread->ResumeFromWait(); |
| 778 | 782 | ||
| 779 | auto lock_owner = thread->lock_owner; | 783 | auto* const lock_owner = thread->GetLockOwner(); |
| 780 | if (lock_owner) | 784 | if (lock_owner != nullptr) { |
| 781 | lock_owner->RemoveMutexWaiter(thread); | 785 | lock_owner->RemoveMutexWaiter(thread); |
| 786 | } | ||
| 782 | 787 | ||
| 783 | thread->lock_owner = nullptr; | 788 | thread->SetLockOwner(nullptr); |
| 784 | thread->mutex_wait_address = 0; | 789 | thread->SetMutexWaitAddress(0); |
| 785 | thread->condvar_wait_address = 0; | 790 | thread->SetCondVarWaitAddress(0); |
| 786 | thread->wait_handle = 0; | 791 | thread->SetWaitHandle(0); |
| 787 | } else { | 792 | } else { |
| 788 | // Atomically signal that the mutex now has a waiting thread. | 793 | // Atomically signal that the mutex now has a waiting thread. |
| 789 | do { | 794 | do { |
| 790 | monitor.SetExclusive(current_core, thread->mutex_wait_address); | 795 | monitor.SetExclusive(current_core, thread->GetMutexWaitAddress()); |
| 791 | 796 | ||
| 792 | // Ensure that the mutex value is still what we expect. | 797 | // Ensure that the mutex value is still what we expect. |
| 793 | u32 value = Memory::Read32(thread->mutex_wait_address); | 798 | u32 value = Memory::Read32(thread->GetMutexWaitAddress()); |
| 794 | // TODO(Subv): When this happens, the kernel just clears the exclusive state and | 799 | // TODO(Subv): When this happens, the kernel just clears the exclusive state and |
| 795 | // retries the initial read for this thread. | 800 | // retries the initial read for this thread. |
| 796 | ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case"); | 801 | ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case"); |
| 797 | } while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address, | 802 | } while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(), |
| 798 | mutex_val | Mutex::MutexHasWaitersFlag)); | 803 | mutex_val | Mutex::MutexHasWaitersFlag)); |
| 799 | 804 | ||
| 800 | // The mutex is already owned by some other thread, make this thread wait on it. | 805 | // The mutex is already owned by some other thread, make this thread wait on it. |
| @@ -802,12 +807,12 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target | |||
| 802 | Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); | 807 | Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); |
| 803 | auto owner = kernel.HandleTable().Get<Thread>(owner_handle); | 808 | auto owner = kernel.HandleTable().Get<Thread>(owner_handle); |
| 804 | ASSERT(owner); | 809 | ASSERT(owner); |
| 805 | ASSERT(thread->status == ThreadStatus::WaitMutex); | 810 | ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); |
| 806 | thread->wakeup_callback = nullptr; | 811 | thread->InvalidateWakeupCallback(); |
| 807 | 812 | ||
| 808 | owner->AddMutexWaiter(thread); | 813 | owner->AddMutexWaiter(thread); |
| 809 | 814 | ||
| 810 | Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); | 815 | Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); |
| 811 | } | 816 | } |
| 812 | } | 817 | } |
| 813 | 818 | ||
| @@ -913,8 +918,8 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) | |||
| 913 | return ERR_INVALID_HANDLE; | 918 | return ERR_INVALID_HANDLE; |
| 914 | } | 919 | } |
| 915 | 920 | ||
| 916 | *core = thread->ideal_core; | 921 | *core = thread->GetIdealCore(); |
| 917 | *mask = thread->affinity_mask; | 922 | *mask = thread->GetAffinityMask(); |
| 918 | 923 | ||
| 919 | return RESULT_SUCCESS; | 924 | return RESULT_SUCCESS; |
| 920 | } | 925 | } |
| @@ -930,11 +935,13 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { | |||
| 930 | } | 935 | } |
| 931 | 936 | ||
| 932 | if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) { | 937 | if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) { |
| 933 | ASSERT(thread->owner_process->GetDefaultProcessorID() != | 938 | const u8 default_processor_id = thread->GetOwnerProcess()->GetDefaultProcessorID(); |
| 934 | static_cast<u8>(THREADPROCESSORID_DEFAULT)); | 939 | |
| 940 | ASSERT(default_processor_id != static_cast<u8>(THREADPROCESSORID_DEFAULT)); | ||
| 941 | |||
| 935 | // Set the target CPU to the one specified in the process' exheader. | 942 | // Set the target CPU to the one specified in the process' exheader. |
| 936 | core = thread->owner_process->GetDefaultProcessorID(); | 943 | core = default_processor_id; |
| 937 | mask = 1ull << core; | 944 | mask = 1ULL << core; |
| 938 | } | 945 | } |
| 939 | 946 | ||
| 940 | if (mask == 0) { | 947 | if (mask == 0) { |
| @@ -945,7 +952,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { | |||
| 945 | static constexpr u32 OnlyChangeMask = static_cast<u32>(-3); | 952 | static constexpr u32 OnlyChangeMask = static_cast<u32>(-3); |
| 946 | 953 | ||
| 947 | if (core == OnlyChangeMask) { | 954 | if (core == OnlyChangeMask) { |
| 948 | core = thread->ideal_core; | 955 | core = thread->GetIdealCore(); |
| 949 | } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) { | 956 | } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) { |
| 950 | return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); | 957 | return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); |
| 951 | } | 958 | } |
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b5c16cfbb..8e514cf9a 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -70,7 +70,7 @@ void Thread::Stop() { | |||
| 70 | 70 | ||
| 71 | void WaitCurrentThread_Sleep() { | 71 | void WaitCurrentThread_Sleep() { |
| 72 | Thread* thread = GetCurrentThread(); | 72 | Thread* thread = GetCurrentThread(); |
| 73 | thread->status = ThreadStatus::WaitSleep; | 73 | thread->SetStatus(ThreadStatus::WaitSleep); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | void ExitCurrentThread() { | 76 | void ExitCurrentThread() { |
| @@ -169,7 +169,7 @@ void Thread::ResumeFromWait() { | |||
| 169 | next_scheduler->ScheduleThread(this, current_priority); | 169 | next_scheduler->ScheduleThread(this, current_priority); |
| 170 | 170 | ||
| 171 | // Change thread's scheduler | 171 | // Change thread's scheduler |
| 172 | scheduler = next_scheduler; | 172 | scheduler = next_scheduler.get(); |
| 173 | 173 | ||
| 174 | Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); | 174 | Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); |
| 175 | } | 175 | } |
| @@ -233,7 +233,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name | |||
| 233 | thread->name = std::move(name); | 233 | thread->name = std::move(name); |
| 234 | thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); | 234 | thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); |
| 235 | thread->owner_process = owner_process; | 235 | thread->owner_process = owner_process; |
| 236 | thread->scheduler = Core::System::GetInstance().Scheduler(processor_id); | 236 | thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get(); |
| 237 | thread->scheduler->AddThread(thread, priority); | 237 | thread->scheduler->AddThread(thread, priority); |
| 238 | thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); | 238 | thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); |
| 239 | 239 | ||
| @@ -269,9 +269,9 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri | |||
| 269 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); | 269 | SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); |
| 270 | 270 | ||
| 271 | // Register 1 must be a handle to the main thread | 271 | // Register 1 must be a handle to the main thread |
| 272 | thread->guest_handle = kernel.HandleTable().Create(thread).Unwrap(); | 272 | const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap(); |
| 273 | 273 | thread->SetGuestHandle(guest_handle); | |
| 274 | thread->context.cpu_registers[1] = thread->guest_handle; | 274 | thread->GetContext().cpu_registers[1] = guest_handle; |
| 275 | 275 | ||
| 276 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires | 276 | // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires |
| 277 | thread->ResumeFromWait(); | 277 | thread->ResumeFromWait(); |
| @@ -299,6 +299,18 @@ VAddr Thread::GetCommandBufferAddress() const { | |||
| 299 | return GetTLSAddress() + CommandHeaderOffset; | 299 | return GetTLSAddress() + CommandHeaderOffset; |
| 300 | } | 300 | } |
| 301 | 301 | ||
| 302 | void Thread::SetStatus(ThreadStatus new_status) { | ||
| 303 | if (new_status == status) { | ||
| 304 | return; | ||
| 305 | } | ||
| 306 | |||
| 307 | if (status == ThreadStatus::Running) { | ||
| 308 | last_running_ticks = CoreTiming::GetTicks(); | ||
| 309 | } | ||
| 310 | |||
| 311 | status = new_status; | ||
| 312 | } | ||
| 313 | |||
| 302 | void Thread::AddMutexWaiter(SharedPtr<Thread> thread) { | 314 | void Thread::AddMutexWaiter(SharedPtr<Thread> thread) { |
| 303 | if (thread->lock_owner == this) { | 315 | if (thread->lock_owner == this) { |
| 304 | // If the thread is already waiting for this thread to release the mutex, ensure that the | 316 | // If the thread is already waiting for this thread to release the mutex, ensure that the |
| @@ -388,11 +400,23 @@ void Thread::ChangeCore(u32 core, u64 mask) { | |||
| 388 | next_scheduler->ScheduleThread(this, current_priority); | 400 | next_scheduler->ScheduleThread(this, current_priority); |
| 389 | 401 | ||
| 390 | // Change thread's scheduler | 402 | // Change thread's scheduler |
| 391 | scheduler = next_scheduler; | 403 | scheduler = next_scheduler.get(); |
| 392 | 404 | ||
| 393 | Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); | 405 | Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); |
| 394 | } | 406 | } |
| 395 | 407 | ||
| 408 | bool Thread::AllWaitObjectsReady() { | ||
| 409 | return std::none_of( | ||
| 410 | wait_objects.begin(), wait_objects.end(), | ||
| 411 | [this](const SharedPtr<WaitObject>& object) { return object->ShouldWait(this); }); | ||
| 412 | } | ||
| 413 | |||
| 414 | bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, | ||
| 415 | SharedPtr<WaitObject> object, std::size_t index) { | ||
| 416 | ASSERT(wakeup_callback); | ||
| 417 | return wakeup_callback(reason, std::move(thread), std::move(object), index); | ||
| 418 | } | ||
| 419 | |||
| 396 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 420 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 397 | 421 | ||
| 398 | /** | 422 | /** |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 4250144c3..c6ffbd28c 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -65,6 +65,15 @@ public: | |||
| 65 | using TLSMemory = std::vector<u8>; | 65 | using TLSMemory = std::vector<u8>; |
| 66 | using TLSMemoryPtr = std::shared_ptr<TLSMemory>; | 66 | using TLSMemoryPtr = std::shared_ptr<TLSMemory>; |
| 67 | 67 | ||
| 68 | using MutexWaitingThreads = std::vector<SharedPtr<Thread>>; | ||
| 69 | |||
| 70 | using ThreadContext = Core::ARM_Interface::ThreadContext; | ||
| 71 | |||
| 72 | using ThreadWaitObjects = std::vector<SharedPtr<WaitObject>>; | ||
| 73 | |||
| 74 | using WakeupCallback = std::function<bool(ThreadWakeupReason reason, SharedPtr<Thread> thread, | ||
| 75 | SharedPtr<WaitObject> object, std::size_t index)>; | ||
| 76 | |||
| 68 | /** | 77 | /** |
| 69 | * Creates and returns a new thread. The new thread is immediately scheduled | 78 | * Creates and returns a new thread. The new thread is immediately scheduled |
| 70 | * @param kernel The kernel instance this thread will be created under. | 79 | * @param kernel The kernel instance this thread will be created under. |
| @@ -106,6 +115,14 @@ public: | |||
| 106 | } | 115 | } |
| 107 | 116 | ||
| 108 | /** | 117 | /** |
| 118 | * Gets the thread's nominal priority. | ||
| 119 | * @return The current thread's nominal priority. | ||
| 120 | */ | ||
| 121 | u32 GetNominalPriority() const { | ||
| 122 | return nominal_priority; | ||
| 123 | } | ||
| 124 | |||
| 125 | /** | ||
| 109 | * Sets the thread's current priority | 126 | * Sets the thread's current priority |
| 110 | * @param priority The new priority | 127 | * @param priority The new priority |
| 111 | */ | 128 | */ |
| @@ -133,7 +150,7 @@ public: | |||
| 133 | * Gets the thread's thread ID | 150 | * Gets the thread's thread ID |
| 134 | * @return The thread's ID | 151 | * @return The thread's ID |
| 135 | */ | 152 | */ |
| 136 | u32 GetThreadId() const { | 153 | u32 GetThreadID() const { |
| 137 | return thread_id; | 154 | return thread_id; |
| 138 | } | 155 | } |
| 139 | 156 | ||
| @@ -203,6 +220,11 @@ public: | |||
| 203 | return tpidr_el0; | 220 | return tpidr_el0; |
| 204 | } | 221 | } |
| 205 | 222 | ||
| 223 | /// Sets the value of the TPIDR_EL0 Read/Write system register for this thread. | ||
| 224 | void SetTPIDR_EL0(u64 value) { | ||
| 225 | tpidr_el0 = value; | ||
| 226 | } | ||
| 227 | |||
| 206 | /* | 228 | /* |
| 207 | * Returns the address of the current thread's command buffer, located in the TLS. | 229 | * Returns the address of the current thread's command buffer, located in the TLS. |
| 208 | * @returns VAddr of the thread's command buffer. | 230 | * @returns VAddr of the thread's command buffer. |
| @@ -218,69 +240,193 @@ public: | |||
| 218 | return status == ThreadStatus::WaitSynchAll; | 240 | return status == ThreadStatus::WaitSynchAll; |
| 219 | } | 241 | } |
| 220 | 242 | ||
| 221 | Core::ARM_Interface::ThreadContext context; | 243 | ThreadContext& GetContext() { |
| 244 | return context; | ||
| 245 | } | ||
| 246 | |||
| 247 | const ThreadContext& GetContext() const { | ||
| 248 | return context; | ||
| 249 | } | ||
| 250 | |||
| 251 | ThreadStatus GetStatus() const { | ||
| 252 | return status; | ||
| 253 | } | ||
| 254 | |||
| 255 | void SetStatus(ThreadStatus new_status); | ||
| 256 | |||
| 257 | u64 GetLastRunningTicks() const { | ||
| 258 | return last_running_ticks; | ||
| 259 | } | ||
| 260 | |||
| 261 | s32 GetProcessorID() const { | ||
| 262 | return processor_id; | ||
| 263 | } | ||
| 264 | |||
| 265 | SharedPtr<Process>& GetOwnerProcess() { | ||
| 266 | return owner_process; | ||
| 267 | } | ||
| 268 | |||
| 269 | const SharedPtr<Process>& GetOwnerProcess() const { | ||
| 270 | return owner_process; | ||
| 271 | } | ||
| 272 | |||
| 273 | const ThreadWaitObjects& GetWaitObjects() const { | ||
| 274 | return wait_objects; | ||
| 275 | } | ||
| 276 | |||
| 277 | void SetWaitObjects(ThreadWaitObjects objects) { | ||
| 278 | wait_objects = std::move(objects); | ||
| 279 | } | ||
| 280 | |||
| 281 | void ClearWaitObjects() { | ||
| 282 | wait_objects.clear(); | ||
| 283 | } | ||
| 284 | |||
| 285 | /// Determines whether all the objects this thread is waiting on are ready. | ||
| 286 | bool AllWaitObjectsReady(); | ||
| 287 | |||
| 288 | const MutexWaitingThreads& GetMutexWaitingThreads() const { | ||
| 289 | return wait_mutex_threads; | ||
| 290 | } | ||
| 291 | |||
| 292 | Thread* GetLockOwner() const { | ||
| 293 | return lock_owner.get(); | ||
| 294 | } | ||
| 295 | |||
| 296 | void SetLockOwner(SharedPtr<Thread> owner) { | ||
| 297 | lock_owner = std::move(owner); | ||
| 298 | } | ||
| 299 | |||
| 300 | VAddr GetCondVarWaitAddress() const { | ||
| 301 | return condvar_wait_address; | ||
| 302 | } | ||
| 303 | |||
| 304 | void SetCondVarWaitAddress(VAddr address) { | ||
| 305 | condvar_wait_address = address; | ||
| 306 | } | ||
| 307 | |||
| 308 | VAddr GetMutexWaitAddress() const { | ||
| 309 | return mutex_wait_address; | ||
| 310 | } | ||
| 222 | 311 | ||
| 223 | u32 thread_id; | 312 | void SetMutexWaitAddress(VAddr address) { |
| 313 | mutex_wait_address = address; | ||
| 314 | } | ||
| 224 | 315 | ||
| 225 | ThreadStatus status; | 316 | Handle GetWaitHandle() const { |
| 226 | VAddr entry_point; | 317 | return wait_handle; |
| 227 | VAddr stack_top; | 318 | } |
| 228 | 319 | ||
| 229 | u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application | 320 | void SetWaitHandle(Handle handle) { |
| 230 | u32 current_priority; ///< Current thread priority, can be temporarily changed | 321 | wait_handle = handle; |
| 322 | } | ||
| 231 | 323 | ||
| 232 | u64 last_running_ticks; ///< CPU tick when thread was last running | 324 | VAddr GetArbiterWaitAddress() const { |
| 325 | return arb_wait_address; | ||
| 326 | } | ||
| 233 | 327 | ||
| 234 | s32 processor_id; | 328 | void SetArbiterWaitAddress(VAddr address) { |
| 329 | arb_wait_address = address; | ||
| 330 | } | ||
| 235 | 331 | ||
| 236 | VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread | 332 | void SetGuestHandle(Handle handle) { |
| 237 | u64 tpidr_el0; ///< TPIDR_EL0 read/write system register. | 333 | guest_handle = handle; |
| 334 | } | ||
| 238 | 335 | ||
| 239 | SharedPtr<Process> owner_process; ///< Process that owns this thread | 336 | bool HasWakeupCallback() const { |
| 337 | return wakeup_callback != nullptr; | ||
| 338 | } | ||
| 339 | |||
| 340 | void SetWakeupCallback(WakeupCallback callback) { | ||
| 341 | wakeup_callback = std::move(callback); | ||
| 342 | } | ||
| 343 | |||
| 344 | void InvalidateWakeupCallback() { | ||
| 345 | SetWakeupCallback(nullptr); | ||
| 346 | } | ||
| 347 | |||
| 348 | /** | ||
| 349 | * Invokes the thread's wakeup callback. | ||
| 350 | * | ||
| 351 | * @pre A valid wakeup callback has been set. Violating this precondition | ||
| 352 | * will cause an assertion to trigger. | ||
| 353 | */ | ||
| 354 | bool InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, | ||
| 355 | SharedPtr<WaitObject> object, std::size_t index); | ||
| 356 | |||
| 357 | u32 GetIdealCore() const { | ||
| 358 | return ideal_core; | ||
| 359 | } | ||
| 360 | |||
| 361 | u64 GetAffinityMask() const { | ||
| 362 | return affinity_mask; | ||
| 363 | } | ||
| 364 | |||
| 365 | private: | ||
| 366 | explicit Thread(KernelCore& kernel); | ||
| 367 | ~Thread() override; | ||
| 368 | |||
| 369 | Core::ARM_Interface::ThreadContext context{}; | ||
| 370 | |||
| 371 | u32 thread_id = 0; | ||
| 372 | |||
| 373 | ThreadStatus status = ThreadStatus::Dormant; | ||
| 374 | |||
| 375 | VAddr entry_point = 0; | ||
| 376 | VAddr stack_top = 0; | ||
| 377 | |||
| 378 | u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application | ||
| 379 | u32 current_priority = 0; ///< Current thread priority, can be temporarily changed | ||
| 380 | |||
| 381 | u64 last_running_ticks = 0; ///< CPU tick when thread was last running | ||
| 382 | |||
| 383 | s32 processor_id = 0; | ||
| 384 | |||
| 385 | VAddr tls_address = 0; ///< Virtual address of the Thread Local Storage of the thread | ||
| 386 | u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register. | ||
| 387 | |||
| 388 | /// Process that owns this thread | ||
| 389 | SharedPtr<Process> owner_process; | ||
| 240 | 390 | ||
| 241 | /// Objects that the thread is waiting on, in the same order as they were | 391 | /// Objects that the thread is waiting on, in the same order as they were |
| 242 | // passed to WaitSynchronization1/N. | 392 | /// passed to WaitSynchronization1/N. |
| 243 | std::vector<SharedPtr<WaitObject>> wait_objects; | 393 | ThreadWaitObjects wait_objects; |
| 244 | 394 | ||
| 245 | /// List of threads that are waiting for a mutex that is held by this thread. | 395 | /// List of threads that are waiting for a mutex that is held by this thread. |
| 246 | std::vector<SharedPtr<Thread>> wait_mutex_threads; | 396 | MutexWaitingThreads wait_mutex_threads; |
| 247 | 397 | ||
| 248 | /// Thread that owns the lock that this thread is waiting for. | 398 | /// Thread that owns the lock that this thread is waiting for. |
| 249 | SharedPtr<Thread> lock_owner; | 399 | SharedPtr<Thread> lock_owner; |
| 250 | 400 | ||
| 251 | // If waiting on a ConditionVariable, this is the ConditionVariable address | 401 | /// If waiting on a ConditionVariable, this is the ConditionVariable address |
| 252 | VAddr condvar_wait_address; | 402 | VAddr condvar_wait_address = 0; |
| 253 | VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address | 403 | /// If waiting on a Mutex, this is the mutex address |
| 254 | Handle wait_handle; ///< The handle used to wait for the mutex. | 404 | VAddr mutex_wait_address = 0; |
| 405 | /// The handle used to wait for the mutex. | ||
| 406 | Handle wait_handle = 0; | ||
| 255 | 407 | ||
| 256 | // If waiting for an AddressArbiter, this is the address being waited on. | 408 | /// If waiting for an AddressArbiter, this is the address being waited on. |
| 257 | VAddr arb_wait_address{0}; | 409 | VAddr arb_wait_address{0}; |
| 258 | 410 | ||
| 259 | std::string name; | ||
| 260 | |||
| 261 | /// Handle used by guest emulated application to access this thread | 411 | /// Handle used by guest emulated application to access this thread |
| 262 | Handle guest_handle; | 412 | Handle guest_handle = 0; |
| 263 | 413 | ||
| 264 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. | 414 | /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. |
| 265 | Handle callback_handle; | 415 | Handle callback_handle = 0; |
| 266 | 416 | ||
| 267 | using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread, | 417 | /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread |
| 268 | SharedPtr<WaitObject> object, std::size_t index); | 418 | /// was waiting via WaitSynchronizationN then the object will be the last object that became |
| 269 | // Callback that will be invoked when the thread is resumed from a waiting state. If the thread | 419 | /// available. In case of a timeout, the object will be nullptr. |
| 270 | // was waiting via WaitSynchronizationN then the object will be the last object that became | 420 | WakeupCallback wakeup_callback; |
| 271 | // available. In case of a timeout, the object will be nullptr. | ||
| 272 | std::function<WakeupCallback> wakeup_callback; | ||
| 273 | 421 | ||
| 274 | std::shared_ptr<Scheduler> scheduler; | 422 | Scheduler* scheduler = nullptr; |
| 275 | 423 | ||
| 276 | u32 ideal_core{0xFFFFFFFF}; | 424 | u32 ideal_core{0xFFFFFFFF}; |
| 277 | u64 affinity_mask{0x1}; | 425 | u64 affinity_mask{0x1}; |
| 278 | 426 | ||
| 279 | private: | ||
| 280 | explicit Thread(KernelCore& kernel); | ||
| 281 | ~Thread() override; | ||
| 282 | |||
| 283 | TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>(); | 427 | TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>(); |
| 428 | |||
| 429 | std::string name; | ||
| 284 | }; | 430 | }; |
| 285 | 431 | ||
| 286 | /** | 432 | /** |
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index b190ceb98..530ee6af7 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp | |||
| @@ -35,13 +35,15 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | |||
| 35 | u32 candidate_priority = THREADPRIO_LOWEST + 1; | 35 | u32 candidate_priority = THREADPRIO_LOWEST + 1; |
| 36 | 36 | ||
| 37 | for (const auto& thread : waiting_threads) { | 37 | for (const auto& thread : waiting_threads) { |
| 38 | const ThreadStatus thread_status = thread->GetStatus(); | ||
| 39 | |||
| 38 | // The list of waiting threads must not contain threads that are not waiting to be awakened. | 40 | // The list of waiting threads must not contain threads that are not waiting to be awakened. |
| 39 | ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny || | 41 | ASSERT_MSG(thread_status == ThreadStatus::WaitSynchAny || |
| 40 | thread->status == ThreadStatus::WaitSynchAll || | 42 | thread_status == ThreadStatus::WaitSynchAll || |
| 41 | thread->status == ThreadStatus::WaitHLEEvent, | 43 | thread_status == ThreadStatus::WaitHLEEvent, |
| 42 | "Inconsistent thread statuses in waiting_threads"); | 44 | "Inconsistent thread statuses in waiting_threads"); |
| 43 | 45 | ||
| 44 | if (thread->current_priority >= candidate_priority) | 46 | if (thread->GetPriority() >= candidate_priority) |
| 45 | continue; | 47 | continue; |
| 46 | 48 | ||
| 47 | if (ShouldWait(thread.get())) | 49 | if (ShouldWait(thread.get())) |
| @@ -50,16 +52,13 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { | |||
| 50 | // A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or | 52 | // A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or |
| 51 | // in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready. | 53 | // in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready. |
| 52 | bool ready_to_run = true; | 54 | bool ready_to_run = true; |
| 53 | if (thread->status == ThreadStatus::WaitSynchAll) { | 55 | if (thread_status == ThreadStatus::WaitSynchAll) { |
| 54 | ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), | 56 | ready_to_run = thread->AllWaitObjectsReady(); |
| 55 | [&thread](const SharedPtr<WaitObject>& object) { | ||
| 56 | return object->ShouldWait(thread.get()); | ||
| 57 | }); | ||
| 58 | } | 57 | } |
| 59 | 58 | ||
| 60 | if (ready_to_run) { | 59 | if (ready_to_run) { |
| 61 | candidate = thread.get(); | 60 | candidate = thread.get(); |
| 62 | candidate_priority = thread->current_priority; | 61 | candidate_priority = thread->GetPriority(); |
| 63 | } | 62 | } |
| 64 | } | 63 | } |
| 65 | 64 | ||
| @@ -75,24 +74,24 @@ void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { | |||
| 75 | if (!thread->IsSleepingOnWaitAll()) { | 74 | if (!thread->IsSleepingOnWaitAll()) { |
| 76 | Acquire(thread.get()); | 75 | Acquire(thread.get()); |
| 77 | } else { | 76 | } else { |
| 78 | for (auto& object : thread->wait_objects) { | 77 | for (const auto& object : thread->GetWaitObjects()) { |
| 79 | ASSERT(!object->ShouldWait(thread.get())); | 78 | ASSERT(!object->ShouldWait(thread.get())); |
| 80 | object->Acquire(thread.get()); | 79 | object->Acquire(thread.get()); |
| 81 | } | 80 | } |
| 82 | } | 81 | } |
| 83 | 82 | ||
| 84 | std::size_t index = thread->GetWaitObjectIndex(this); | 83 | const std::size_t index = thread->GetWaitObjectIndex(this); |
| 85 | 84 | ||
| 86 | for (auto& object : thread->wait_objects) | 85 | for (const auto& object : thread->GetWaitObjects()) |
| 87 | object->RemoveWaitingThread(thread.get()); | 86 | object->RemoveWaitingThread(thread.get()); |
| 88 | thread->wait_objects.clear(); | 87 | thread->ClearWaitObjects(); |
| 89 | 88 | ||
| 90 | thread->CancelWakeupTimer(); | 89 | thread->CancelWakeupTimer(); |
| 91 | 90 | ||
| 92 | bool resume = true; | 91 | bool resume = true; |
| 93 | 92 | ||
| 94 | if (thread->wakeup_callback) | 93 | if (thread->HasWakeupCallback()) |
| 95 | resume = thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this, index); | 94 | resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index); |
| 96 | 95 | ||
| 97 | if (resume) | 96 | if (resume) |
| 98 | thread->ResumeFromWait(); | 97 | thread->ResumeFromWait(); |
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index cfc28fa0c..79580bcd9 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -84,7 +84,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 84 | out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); | 84 | out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); |
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | if (out.size() <= offset) { | 87 | if (out.size() < offset) { |
| 88 | IPC::ResponseBuilder rb{ctx, 2}; | 88 | IPC::ResponseBuilder rb{ctx, 2}; |
| 89 | // TODO(DarkLordZach): Find the correct error code. | 89 | // TODO(DarkLordZach): Find the correct error code. |
| 90 | rb.Push(ResultCode(-1)); | 90 | rb.Push(ResultCode(-1)); |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index cabaf5a55..d5dced429 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -468,6 +468,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | |||
| 468 | {80, nullptr, "OpenSaveDataMetaFile"}, | 468 | {80, nullptr, "OpenSaveDataMetaFile"}, |
| 469 | {81, nullptr, "OpenSaveDataTransferManager"}, | 469 | {81, nullptr, "OpenSaveDataTransferManager"}, |
| 470 | {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, | 470 | {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, |
| 471 | {83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"}, | ||
| 471 | {100, nullptr, "OpenImageDirectoryFileSystem"}, | 472 | {100, nullptr, "OpenImageDirectoryFileSystem"}, |
| 472 | {110, nullptr, "OpenContentStorageFileSystem"}, | 473 | {110, nullptr, "OpenContentStorageFileSystem"}, |
| 473 | {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, | 474 | {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, |
| @@ -495,6 +496,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | |||
| 495 | {613, nullptr, "VerifySaveDataFileSystemBySaveDataSpaceId"}, | 496 | {613, nullptr, "VerifySaveDataFileSystemBySaveDataSpaceId"}, |
| 496 | {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"}, | 497 | {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"}, |
| 497 | {615, nullptr, "QuerySaveDataInternalStorageTotalSize"}, | 498 | {615, nullptr, "QuerySaveDataInternalStorageTotalSize"}, |
| 499 | {616, nullptr, "GetSaveDataCommitId"}, | ||
| 498 | {620, nullptr, "SetSdCardEncryptionSeed"}, | 500 | {620, nullptr, "SetSdCardEncryptionSeed"}, |
| 499 | {630, nullptr, "SetSdCardAccessibility"}, | 501 | {630, nullptr, "SetSdCardAccessibility"}, |
| 500 | {631, nullptr, "IsSdCardAccessible"}, | 502 | {631, nullptr, "IsSdCardAccessible"}, |
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp index 8fc8b1057..7321584e1 100644 --- a/src/core/hle/service/lbl/lbl.cpp +++ b/src/core/hle/service/lbl/lbl.cpp | |||
| @@ -21,29 +21,29 @@ public: | |||
| 21 | {0, nullptr, "Unknown1"}, | 21 | {0, nullptr, "Unknown1"}, |
| 22 | {1, nullptr, "Unknown2"}, | 22 | {1, nullptr, "Unknown2"}, |
| 23 | {2, nullptr, "Unknown3"}, | 23 | {2, nullptr, "Unknown3"}, |
| 24 | {3, nullptr, "Unknown4"}, | 24 | {3, nullptr, "GetCurrentBacklightLevel"}, |
| 25 | {4, nullptr, "Unknown5"}, | 25 | {4, nullptr, "Unknown4"}, |
| 26 | {5, nullptr, "Unknown6"}, | 26 | {5, nullptr, "GetAlsComputedBacklightLevel"}, |
| 27 | {6, nullptr, "TurnOffBacklight"}, | 27 | {6, nullptr, "TurnOffBacklight"}, |
| 28 | {7, nullptr, "TurnOnBacklight"}, | 28 | {7, nullptr, "TurnOnBacklight"}, |
| 29 | {8, nullptr, "GetBacklightStatus"}, | 29 | {8, nullptr, "GetBacklightStatus"}, |
| 30 | {9, nullptr, "Unknown7"}, | 30 | {9, nullptr, "Unknown5"}, |
| 31 | {10, nullptr, "Unknown8"}, | 31 | {10, nullptr, "Unknown6"}, |
| 32 | {11, nullptr, "Unknown9"}, | 32 | {11, nullptr, "Unknown7"}, |
| 33 | {12, nullptr, "Unknown10"}, | 33 | {12, nullptr, "Unknown8"}, |
| 34 | {13, nullptr, "Unknown11"}, | 34 | {13, nullptr, "Unknown9"}, |
| 35 | {14, nullptr, "Unknown12"}, | 35 | {14, nullptr, "Unknown10"}, |
| 36 | {15, nullptr, "Unknown13"}, | 36 | {15, nullptr, "GetAutoBrightnessSetting"}, |
| 37 | {16, nullptr, "ReadRawLightSensor"}, | 37 | {16, nullptr, "ReadRawLightSensor"}, |
| 38 | {17, nullptr, "Unknown14"}, | 38 | {17, nullptr, "Unknown11"}, |
| 39 | {18, nullptr, "Unknown15"}, | 39 | {18, nullptr, "Unknown12"}, |
| 40 | {19, nullptr, "Unknown16"}, | 40 | {19, nullptr, "Unknown13"}, |
| 41 | {20, nullptr, "Unknown17"}, | 41 | {20, nullptr, "Unknown14"}, |
| 42 | {21, nullptr, "Unknown18"}, | 42 | {21, nullptr, "Unknown15"}, |
| 43 | {22, nullptr, "Unknown19"}, | 43 | {22, nullptr, "Unknown16"}, |
| 44 | {23, nullptr, "Unknown20"}, | 44 | {23, nullptr, "Unknown17"}, |
| 45 | {24, nullptr, "Unknown21"}, | 45 | {24, nullptr, "Unknown18"}, |
| 46 | {25, nullptr, "Unknown22"}, | 46 | {25, nullptr, "Unknown19"}, |
| 47 | {26, &LBL::EnableVrMode, "EnableVrMode"}, | 47 | {26, &LBL::EnableVrMode, "EnableVrMode"}, |
| 48 | {27, &LBL::DisableVrMode, "DisableVrMode"}, | 48 | {27, &LBL::DisableVrMode, "DisableVrMode"}, |
| 49 | {28, &LBL::GetVrMode, "GetVrMode"}, | 49 | {28, &LBL::GetVrMode, "GetVrMode"}, |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index c1824b9c3..9a86e5824 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -130,6 +130,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) | |||
| 130 | } | 130 | } |
| 131 | 131 | ||
| 132 | process.LoadFromMetadata(metadata); | 132 | process.LoadFromMetadata(metadata); |
| 133 | const FileSys::PatchManager pm(metadata.GetTitleID()); | ||
| 133 | 134 | ||
| 134 | // Load NSO modules | 135 | // Load NSO modules |
| 135 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); | 136 | const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); |
| @@ -139,7 +140,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) | |||
| 139 | const FileSys::VirtualFile module_file = dir->GetFile(module); | 140 | const FileSys::VirtualFile module_file = dir->GetFile(module); |
| 140 | if (module_file != nullptr) { | 141 | if (module_file != nullptr) { |
| 141 | const VAddr load_addr = next_load_addr; | 142 | const VAddr load_addr = next_load_addr; |
| 142 | next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr); | 143 | next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr, pm); |
| 143 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | 144 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); |
| 144 | // Register module with GDBStub | 145 | // Register module with GDBStub |
| 145 | GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); | 146 | GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index cbe2a3e53..2186b02af 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 12 | #include "core/core.h" | 12 | #include "core/core.h" |
| 13 | #include "core/file_sys/patch_manager.h" | ||
| 13 | #include "core/gdbstub/gdbstub.h" | 14 | #include "core/gdbstub/gdbstub.h" |
| 14 | #include "core/hle/kernel/kernel.h" | 15 | #include "core/hle/kernel/kernel.h" |
| 15 | #include "core/hle/kernel/process.h" | 16 | #include "core/hle/kernel/process.h" |
| @@ -36,8 +37,7 @@ struct NsoHeader { | |||
| 36 | INSERT_PADDING_WORDS(1); | 37 | INSERT_PADDING_WORDS(1); |
| 37 | u8 flags; | 38 | u8 flags; |
| 38 | std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order) | 39 | std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order) |
| 39 | u32_le bss_size; | 40 | std::array<u8, 0x20> build_id; |
| 40 | INSERT_PADDING_BYTES(0x1c); | ||
| 41 | std::array<u32_le, 3> segments_compressed_size; | 41 | std::array<u32_le, 3> segments_compressed_size; |
| 42 | 42 | ||
| 43 | bool IsSegmentCompressed(size_t segment_num) const { | 43 | bool IsSegmentCompressed(size_t segment_num) const { |
| @@ -93,7 +93,8 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 93 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | 93 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { | 96 | VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, |
| 97 | boost::optional<FileSys::PatchManager> pm) { | ||
| 97 | if (file == nullptr) | 98 | if (file == nullptr) |
| 98 | return {}; | 99 | return {}; |
| 99 | 100 | ||
| @@ -142,6 +143,17 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { | |||
| 142 | const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; | 143 | const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; |
| 143 | program_image.resize(image_size); | 144 | program_image.resize(image_size); |
| 144 | 145 | ||
| 146 | // Apply patches if necessary | ||
| 147 | if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) { | ||
| 148 | std::vector<u8> pi_header(program_image.size() + 0x100); | ||
| 149 | std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); | ||
| 150 | std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); | ||
| 151 | |||
| 152 | pi_header = pm->PatchNSO(pi_header); | ||
| 153 | |||
| 154 | std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size()); | ||
| 155 | } | ||
| 156 | |||
| 145 | // Load codeset for current process | 157 | // Load codeset for current process |
| 146 | codeset->name = file->GetName(); | 158 | codeset->name = file->GetName(); |
| 147 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | 159 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); |
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 7f142405b..05353d4d9 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/file_sys/patch_manager.h" | ||
| 8 | #include "core/loader/linker.h" | 9 | #include "core/loader/linker.h" |
| 9 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 10 | 11 | ||
| @@ -26,7 +27,8 @@ public: | |||
| 26 | return IdentifyType(file); | 27 | return IdentifyType(file); |
| 27 | } | 28 | } |
| 28 | 29 | ||
| 29 | static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base); | 30 | static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, |
| 31 | boost::optional<FileSys::PatchManager> pm = boost::none); | ||
| 30 | 32 | ||
| 31 | ResultStatus Load(Kernel::Process& process) override; | 33 | ResultStatus Load(Kernel::Process& process) override; |
| 32 | }; | 34 | }; |
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index f5ae57039..09ecc5bad 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt | |||
| @@ -27,6 +27,8 @@ add_library(video_core STATIC | |||
| 27 | renderer_base.h | 27 | renderer_base.h |
| 28 | renderer_opengl/gl_buffer_cache.cpp | 28 | renderer_opengl/gl_buffer_cache.cpp |
| 29 | renderer_opengl/gl_buffer_cache.h | 29 | renderer_opengl/gl_buffer_cache.h |
| 30 | renderer_opengl/gl_primitive_assembler.cpp | ||
| 31 | renderer_opengl/gl_primitive_assembler.h | ||
| 30 | renderer_opengl/gl_rasterizer.cpp | 32 | renderer_opengl/gl_rasterizer.cpp |
| 31 | renderer_opengl/gl_rasterizer.h | 33 | renderer_opengl/gl_rasterizer.h |
| 32 | renderer_opengl/gl_rasterizer_cache.cpp | 34 | renderer_opengl/gl_rasterizer_cache.cpp |
diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 9f5581045..4290da33f 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h | |||
| @@ -744,6 +744,12 @@ public: | |||
| 744 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(end_addr_high) << 32) | | 744 | return static_cast<GPUVAddr>((static_cast<GPUVAddr>(end_addr_high) << 32) | |
| 745 | end_addr_low); | 745 | end_addr_low); |
| 746 | } | 746 | } |
| 747 | |||
| 748 | /// Adjust the index buffer offset so it points to the first desired index. | ||
| 749 | GPUVAddr IndexStart() const { | ||
| 750 | return StartAddress() + static_cast<size_t>(first) * | ||
| 751 | static_cast<size_t>(FormatSizeInBytes()); | ||
| 752 | } | ||
| 747 | } index_array; | 753 | } index_array; |
| 748 | 754 | ||
| 749 | INSERT_PADDING_WORDS(0x7); | 755 | INSERT_PADDING_WORDS(0x7); |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp index 578aca789..c142095c5 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp | |||
| @@ -34,7 +34,7 @@ GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size | |||
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | AlignBuffer(alignment); | 36 | AlignBuffer(alignment); |
| 37 | GLintptr uploaded_offset = buffer_offset; | 37 | const GLintptr uploaded_offset = buffer_offset; |
| 38 | 38 | ||
| 39 | Memory::ReadBlock(*cpu_addr, buffer_ptr, size); | 39 | Memory::ReadBlock(*cpu_addr, buffer_ptr, size); |
| 40 | 40 | ||
| @@ -57,13 +57,23 @@ GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t s | |||
| 57 | std::size_t alignment) { | 57 | std::size_t alignment) { |
| 58 | AlignBuffer(alignment); | 58 | AlignBuffer(alignment); |
| 59 | std::memcpy(buffer_ptr, raw_pointer, size); | 59 | std::memcpy(buffer_ptr, raw_pointer, size); |
| 60 | GLintptr uploaded_offset = buffer_offset; | 60 | const GLintptr uploaded_offset = buffer_offset; |
| 61 | 61 | ||
| 62 | buffer_ptr += size; | 62 | buffer_ptr += size; |
| 63 | buffer_offset += size; | 63 | buffer_offset += size; |
| 64 | return uploaded_offset; | 64 | return uploaded_offset; |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | std::tuple<u8*, GLintptr> OGLBufferCache::ReserveMemory(std::size_t size, std::size_t alignment) { | ||
| 68 | AlignBuffer(alignment); | ||
| 69 | u8* const uploaded_ptr = buffer_ptr; | ||
| 70 | const GLintptr uploaded_offset = buffer_offset; | ||
| 71 | |||
| 72 | buffer_ptr += size; | ||
| 73 | buffer_offset += size; | ||
| 74 | return std::make_tuple(uploaded_ptr, uploaded_offset); | ||
| 75 | } | ||
| 76 | |||
| 67 | void OGLBufferCache::Map(std::size_t max_size) { | 77 | void OGLBufferCache::Map(std::size_t max_size) { |
| 68 | bool invalidate; | 78 | bool invalidate; |
| 69 | std::tie(buffer_ptr, buffer_offset_base, invalidate) = | 79 | std::tie(buffer_ptr, buffer_offset_base, invalidate) = |
| @@ -74,6 +84,7 @@ void OGLBufferCache::Map(std::size_t max_size) { | |||
| 74 | InvalidateAll(); | 84 | InvalidateAll(); |
| 75 | } | 85 | } |
| 76 | } | 86 | } |
| 87 | |||
| 77 | void OGLBufferCache::Unmap() { | 88 | void OGLBufferCache::Unmap() { |
| 78 | stream_buffer.Unmap(buffer_offset - buffer_offset_base); | 89 | stream_buffer.Unmap(buffer_offset - buffer_offset_base); |
| 79 | } | 90 | } |
| @@ -84,7 +95,7 @@ GLuint OGLBufferCache::GetHandle() const { | |||
| 84 | 95 | ||
| 85 | void OGLBufferCache::AlignBuffer(std::size_t alignment) { | 96 | void OGLBufferCache::AlignBuffer(std::size_t alignment) { |
| 86 | // Align the offset, not the mapped pointer | 97 | // Align the offset, not the mapped pointer |
| 87 | GLintptr offset_aligned = | 98 | const GLintptr offset_aligned = |
| 88 | static_cast<GLintptr>(Common::AlignUp(static_cast<std::size_t>(buffer_offset), alignment)); | 99 | static_cast<GLintptr>(Common::AlignUp(static_cast<std::size_t>(buffer_offset), alignment)); |
| 89 | buffer_ptr += offset_aligned - buffer_offset; | 100 | buffer_ptr += offset_aligned - buffer_offset; |
| 90 | buffer_offset = offset_aligned; | 101 | buffer_offset = offset_aligned; |
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h index 6c18461f4..965976334 100644 --- a/src/video_core/renderer_opengl/gl_buffer_cache.h +++ b/src/video_core/renderer_opengl/gl_buffer_cache.h | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <memory> | 8 | #include <memory> |
| 9 | #include <tuple> | ||
| 9 | 10 | ||
| 10 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 11 | #include "video_core/rasterizer_cache.h" | 12 | #include "video_core/rasterizer_cache.h" |
| @@ -33,11 +34,17 @@ class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBuffer | |||
| 33 | public: | 34 | public: |
| 34 | explicit OGLBufferCache(std::size_t size); | 35 | explicit OGLBufferCache(std::size_t size); |
| 35 | 36 | ||
| 37 | /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been | ||
| 38 | /// allocated. | ||
| 36 | GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, | 39 | GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, |
| 37 | bool cache = true); | 40 | bool cache = true); |
| 38 | 41 | ||
| 42 | /// Uploads from a host memory. Returns host's buffer offset where it's been allocated. | ||
| 39 | GLintptr UploadHostMemory(const void* raw_pointer, std::size_t size, std::size_t alignment = 4); | 43 | GLintptr UploadHostMemory(const void* raw_pointer, std::size_t size, std::size_t alignment = 4); |
| 40 | 44 | ||
| 45 | /// Reserves memory to be used by host's CPU. Returns mapped address and offset. | ||
| 46 | std::tuple<u8*, GLintptr> ReserveMemory(std::size_t size, std::size_t alignment = 4); | ||
| 47 | |||
| 41 | void Map(std::size_t max_size); | 48 | void Map(std::size_t max_size); |
| 42 | void Unmap(); | 49 | void Unmap(); |
| 43 | 50 | ||
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.cpp b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp new file mode 100644 index 000000000..ee1d9601b --- /dev/null +++ b/src/video_core/renderer_opengl/gl_primitive_assembler.cpp | |||
| @@ -0,0 +1,64 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include "common/assert.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "core/memory.h" | ||
| 10 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | ||
| 11 | #include "video_core/renderer_opengl/gl_primitive_assembler.h" | ||
| 12 | |||
| 13 | namespace OpenGL { | ||
| 14 | |||
| 15 | constexpr u32 TRIANGLES_PER_QUAD = 6; | ||
| 16 | constexpr std::array<u32, TRIANGLES_PER_QUAD> QUAD_MAP = {0, 1, 2, 0, 2, 3}; | ||
| 17 | |||
| 18 | PrimitiveAssembler::PrimitiveAssembler(OGLBufferCache& buffer_cache) : buffer_cache(buffer_cache) {} | ||
| 19 | |||
| 20 | PrimitiveAssembler::~PrimitiveAssembler() = default; | ||
| 21 | |||
| 22 | std::size_t PrimitiveAssembler::CalculateQuadSize(u32 count) const { | ||
| 23 | ASSERT_MSG(count % 4 == 0, "Quad count is expected to be a multiple of 4"); | ||
| 24 | return (count / 4) * TRIANGLES_PER_QUAD * sizeof(GLuint); | ||
| 25 | } | ||
| 26 | |||
| 27 | GLintptr PrimitiveAssembler::MakeQuadArray(u32 first, u32 count) { | ||
| 28 | const std::size_t size{CalculateQuadSize(count)}; | ||
| 29 | auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(size); | ||
| 30 | |||
| 31 | for (u32 primitive = 0; primitive < count / 4; ++primitive) { | ||
| 32 | for (u32 i = 0; i < TRIANGLES_PER_QUAD; ++i) { | ||
| 33 | const u32 index = first + primitive * 4 + QUAD_MAP[i]; | ||
| 34 | std::memcpy(dst_pointer, &index, sizeof(index)); | ||
| 35 | dst_pointer += sizeof(index); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | return index_offset; | ||
| 40 | } | ||
| 41 | |||
| 42 | GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size, | ||
| 43 | u32 count) { | ||
| 44 | const std::size_t map_size{CalculateQuadSize(count)}; | ||
| 45 | auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size); | ||
| 46 | |||
| 47 | auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); | ||
| 48 | const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; | ||
| 49 | const u8* source{Memory::GetPointer(*cpu_addr)}; | ||
| 50 | |||
| 51 | for (u32 primitive = 0; primitive < count / 4; ++primitive) { | ||
| 52 | for (std::size_t i = 0; i < TRIANGLES_PER_QUAD; ++i) { | ||
| 53 | const u32 index = primitive * 4 + QUAD_MAP[i]; | ||
| 54 | const u8* src_offset = source + (index * index_size); | ||
| 55 | |||
| 56 | std::memcpy(dst_pointer, src_offset, index_size); | ||
| 57 | dst_pointer += index_size; | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | return index_offset; | ||
| 62 | } | ||
| 63 | |||
| 64 | } // namespace OpenGL \ No newline at end of file | ||
diff --git a/src/video_core/renderer_opengl/gl_primitive_assembler.h b/src/video_core/renderer_opengl/gl_primitive_assembler.h new file mode 100644 index 000000000..a8cb88eb5 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_primitive_assembler.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2018 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <vector> | ||
| 8 | #include <glad/glad.h> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "video_core/memory_manager.h" | ||
| 12 | |||
| 13 | namespace OpenGL { | ||
| 14 | |||
| 15 | class OGLBufferCache; | ||
| 16 | |||
| 17 | class PrimitiveAssembler { | ||
| 18 | public: | ||
| 19 | explicit PrimitiveAssembler(OGLBufferCache& buffer_cache); | ||
| 20 | ~PrimitiveAssembler(); | ||
| 21 | |||
| 22 | /// Calculates the size required by MakeQuadArray and MakeQuadIndexed. | ||
| 23 | std::size_t CalculateQuadSize(u32 count) const; | ||
| 24 | |||
| 25 | GLintptr MakeQuadArray(u32 first, u32 count); | ||
| 26 | |||
| 27 | GLintptr MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size, u32 count); | ||
| 28 | |||
| 29 | private: | ||
| 30 | OGLBufferCache& buffer_cache; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace OpenGL \ No newline at end of file | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 587d9dffb..60dcdc184 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -42,6 +42,41 @@ MICROPROFILE_DEFINE(OpenGL_Framebuffer, "OpenGL", "Framebuffer Setup", MP_RGB(12 | |||
| 42 | MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); | 42 | MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); |
| 43 | MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192)); | 43 | MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192)); |
| 44 | MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); | 44 | MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); |
| 45 | MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100)); | ||
| 46 | |||
| 47 | struct DrawParameters { | ||
| 48 | GLenum primitive_mode; | ||
| 49 | GLsizei count; | ||
| 50 | GLint current_instance; | ||
| 51 | bool use_indexed; | ||
| 52 | |||
| 53 | GLint vertex_first; | ||
| 54 | |||
| 55 | GLenum index_format; | ||
| 56 | GLint base_vertex; | ||
| 57 | GLintptr index_buffer_offset; | ||
| 58 | |||
| 59 | void DispatchDraw() const { | ||
| 60 | if (use_indexed) { | ||
| 61 | const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset); | ||
| 62 | if (current_instance > 0) { | ||
| 63 | glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format, | ||
| 64 | index_buffer_ptr, 1, base_vertex, | ||
| 65 | current_instance); | ||
| 66 | } else { | ||
| 67 | glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr, | ||
| 68 | base_vertex); | ||
| 69 | } | ||
| 70 | } else { | ||
| 71 | if (current_instance > 0) { | ||
| 72 | glDrawArraysInstancedBaseInstance(primitive_mode, vertex_first, count, 1, | ||
| 73 | current_instance); | ||
| 74 | } else { | ||
| 75 | glDrawArrays(primitive_mode, vertex_first, count); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | } | ||
| 79 | }; | ||
| 45 | 80 | ||
| 46 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) | 81 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) |
| 47 | : emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) { | 82 | : emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) { |
| @@ -172,6 +207,53 @@ void RasterizerOpenGL::SetupVertexArrays() { | |||
| 172 | } | 207 | } |
| 173 | } | 208 | } |
| 174 | 209 | ||
| 210 | DrawParameters RasterizerOpenGL::SetupDraw() { | ||
| 211 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); | ||
| 212 | const auto& regs = gpu.regs; | ||
| 213 | const bool is_indexed = accelerate_draw == AccelDraw::Indexed; | ||
| 214 | |||
| 215 | DrawParameters params{}; | ||
| 216 | params.current_instance = gpu.state.current_instance; | ||
| 217 | |||
| 218 | if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) { | ||
| 219 | MICROPROFILE_SCOPE(OpenGL_PrimitiveAssembly); | ||
| 220 | |||
| 221 | params.use_indexed = true; | ||
| 222 | params.primitive_mode = GL_TRIANGLES; | ||
| 223 | |||
| 224 | if (is_indexed) { | ||
| 225 | params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); | ||
| 226 | params.count = (regs.index_array.count / 4) * 6; | ||
| 227 | params.index_buffer_offset = primitive_assembler.MakeQuadIndexed( | ||
| 228 | regs.index_array.IndexStart(), regs.index_array.FormatSizeInBytes(), | ||
| 229 | regs.index_array.count); | ||
| 230 | params.base_vertex = static_cast<GLint>(regs.vb_element_base); | ||
| 231 | } else { | ||
| 232 | // MakeQuadArray always generates u32 indexes | ||
| 233 | params.index_format = GL_UNSIGNED_INT; | ||
| 234 | params.count = (regs.vertex_buffer.count / 4) * 6; | ||
| 235 | params.index_buffer_offset = | ||
| 236 | primitive_assembler.MakeQuadArray(regs.vertex_buffer.first, params.count); | ||
| 237 | } | ||
| 238 | return params; | ||
| 239 | } | ||
| 240 | |||
| 241 | params.use_indexed = is_indexed; | ||
| 242 | params.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); | ||
| 243 | |||
| 244 | if (is_indexed) { | ||
| 245 | MICROPROFILE_SCOPE(OpenGL_Index); | ||
| 246 | params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); | ||
| 247 | params.count = regs.index_array.count; | ||
| 248 | params.index_buffer_offset = | ||
| 249 | buffer_cache.UploadMemory(regs.index_array.IndexStart(), CalculateIndexBufferSize()); | ||
| 250 | params.base_vertex = static_cast<GLint>(regs.vb_element_base); | ||
| 251 | } else { | ||
| 252 | params.count = regs.vertex_buffer.count; | ||
| 253 | params.vertex_first = regs.vertex_buffer.first; | ||
| 254 | } | ||
| 255 | } | ||
| 256 | |||
| 175 | void RasterizerOpenGL::SetupShaders() { | 257 | void RasterizerOpenGL::SetupShaders() { |
| 176 | MICROPROFILE_SCOPE(OpenGL_Shader); | 258 | MICROPROFILE_SCOPE(OpenGL_Shader); |
| 177 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); | 259 | const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); |
| @@ -256,6 +338,13 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { | |||
| 256 | return size; | 338 | return size; |
| 257 | } | 339 | } |
| 258 | 340 | ||
| 341 | std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const { | ||
| 342 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; | ||
| 343 | |||
| 344 | return static_cast<std::size_t>(regs.index_array.count) * | ||
| 345 | static_cast<std::size_t>(regs.index_array.FormatSizeInBytes()); | ||
| 346 | } | ||
| 347 | |||
| 259 | bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { | 348 | bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { |
| 260 | accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; | 349 | accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; |
| 261 | DrawArrays(); | 350 | DrawArrays(); |
| @@ -459,16 +548,23 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 459 | 548 | ||
| 460 | // Draw the vertex batch | 549 | // Draw the vertex batch |
| 461 | const bool is_indexed = accelerate_draw == AccelDraw::Indexed; | 550 | const bool is_indexed = accelerate_draw == AccelDraw::Indexed; |
| 462 | const u64 index_buffer_size{static_cast<u64>(regs.index_array.count) * | ||
| 463 | static_cast<u64>(regs.index_array.FormatSizeInBytes())}; | ||
| 464 | 551 | ||
| 465 | state.draw.vertex_buffer = buffer_cache.GetHandle(); | 552 | state.draw.vertex_buffer = buffer_cache.GetHandle(); |
| 466 | state.Apply(); | 553 | state.Apply(); |
| 467 | 554 | ||
| 468 | std::size_t buffer_size = CalculateVertexArraysSize(); | 555 | std::size_t buffer_size = CalculateVertexArraysSize(); |
| 469 | 556 | ||
| 470 | if (is_indexed) { | 557 | // Add space for index buffer (keeping in mind non-core primitives) |
| 471 | buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + index_buffer_size; | 558 | switch (regs.draw.topology) { |
| 559 | case Maxwell::PrimitiveTopology::Quads: | ||
| 560 | buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + | ||
| 561 | primitive_assembler.CalculateQuadSize(regs.vertex_buffer.count); | ||
| 562 | break; | ||
| 563 | default: | ||
| 564 | if (is_indexed) { | ||
| 565 | buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + CalculateIndexBufferSize(); | ||
| 566 | } | ||
| 567 | break; | ||
| 472 | } | 568 | } |
| 473 | 569 | ||
| 474 | // Uniform space for the 5 shader stages | 570 | // Uniform space for the 5 shader stages |
| @@ -482,20 +578,7 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 482 | buffer_cache.Map(buffer_size); | 578 | buffer_cache.Map(buffer_size); |
| 483 | 579 | ||
| 484 | SetupVertexArrays(); | 580 | SetupVertexArrays(); |
| 485 | 581 | DrawParameters params = SetupDraw(); | |
| 486 | // If indexed mode, copy the index buffer | ||
| 487 | GLintptr index_buffer_offset = 0; | ||
| 488 | if (is_indexed) { | ||
| 489 | MICROPROFILE_SCOPE(OpenGL_Index); | ||
| 490 | |||
| 491 | // Adjust the index buffer offset so it points to the first desired index. | ||
| 492 | auto index_start = regs.index_array.StartAddress(); | ||
| 493 | index_start += static_cast<size_t>(regs.index_array.first) * | ||
| 494 | static_cast<size_t>(regs.index_array.FormatSizeInBytes()); | ||
| 495 | |||
| 496 | index_buffer_offset = buffer_cache.UploadMemory(index_start, index_buffer_size); | ||
| 497 | } | ||
| 498 | |||
| 499 | SetupShaders(); | 582 | SetupShaders(); |
| 500 | 583 | ||
| 501 | buffer_cache.Unmap(); | 584 | buffer_cache.Unmap(); |
| @@ -503,31 +586,8 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 503 | shader_program_manager->ApplyTo(state); | 586 | shader_program_manager->ApplyTo(state); |
| 504 | state.Apply(); | 587 | state.Apply(); |
| 505 | 588 | ||
| 506 | const GLenum primitive_mode{MaxwellToGL::PrimitiveTopology(regs.draw.topology)}; | 589 | // Execute draw call |
| 507 | if (is_indexed) { | 590 | params.DispatchDraw(); |
| 508 | const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)}; | ||
| 509 | |||
| 510 | if (gpu.state.current_instance > 0) { | ||
| 511 | glDrawElementsInstancedBaseVertexBaseInstance( | ||
| 512 | primitive_mode, regs.index_array.count, | ||
| 513 | MaxwellToGL::IndexFormat(regs.index_array.format), | ||
| 514 | reinterpret_cast<const void*>(index_buffer_offset), 1, base_vertex, | ||
| 515 | gpu.state.current_instance); | ||
| 516 | } else { | ||
| 517 | glDrawElementsBaseVertex(primitive_mode, regs.index_array.count, | ||
| 518 | MaxwellToGL::IndexFormat(regs.index_array.format), | ||
| 519 | reinterpret_cast<const void*>(index_buffer_offset), | ||
| 520 | base_vertex); | ||
| 521 | } | ||
| 522 | } else { | ||
| 523 | if (gpu.state.current_instance > 0) { | ||
| 524 | glDrawArraysInstancedBaseInstance(primitive_mode, regs.vertex_buffer.first, | ||
| 525 | regs.vertex_buffer.count, 1, | ||
| 526 | gpu.state.current_instance); | ||
| 527 | } else { | ||
| 528 | glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count); | ||
| 529 | } | ||
| 530 | } | ||
| 531 | 591 | ||
| 532 | // Disable scissor test | 592 | // Disable scissor test |
| 533 | state.scissor.enabled = false; | 593 | state.scissor.enabled = false; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 4c8ecbd1c..bf954bb5d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h | |||
| @@ -23,6 +23,7 @@ | |||
| 23 | #include "video_core/rasterizer_cache.h" | 23 | #include "video_core/rasterizer_cache.h" |
| 24 | #include "video_core/rasterizer_interface.h" | 24 | #include "video_core/rasterizer_interface.h" |
| 25 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 25 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| 26 | #include "video_core/renderer_opengl/gl_primitive_assembler.h" | ||
| 26 | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" | 27 | #include "video_core/renderer_opengl/gl_rasterizer_cache.h" |
| 27 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 28 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 28 | #include "video_core/renderer_opengl/gl_shader_cache.h" | 29 | #include "video_core/renderer_opengl/gl_shader_cache.h" |
| @@ -38,6 +39,7 @@ class EmuWindow; | |||
| 38 | namespace OpenGL { | 39 | namespace OpenGL { |
| 39 | 40 | ||
| 40 | struct ScreenInfo; | 41 | struct ScreenInfo; |
| 42 | struct DrawParameters; | ||
| 41 | 43 | ||
| 42 | class RasterizerOpenGL : public VideoCore::RasterizerInterface { | 44 | class RasterizerOpenGL : public VideoCore::RasterizerInterface { |
| 43 | public: | 45 | public: |
| @@ -192,12 +194,17 @@ private: | |||
| 192 | static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; | 194 | static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; |
| 193 | OGLBufferCache buffer_cache; | 195 | OGLBufferCache buffer_cache; |
| 194 | OGLFramebuffer framebuffer; | 196 | OGLFramebuffer framebuffer; |
| 197 | PrimitiveAssembler primitive_assembler{buffer_cache}; | ||
| 195 | GLint uniform_buffer_alignment; | 198 | GLint uniform_buffer_alignment; |
| 196 | 199 | ||
| 197 | std::size_t CalculateVertexArraysSize() const; | 200 | std::size_t CalculateVertexArraysSize() const; |
| 198 | 201 | ||
| 202 | std::size_t CalculateIndexBufferSize() const; | ||
| 203 | |||
| 199 | void SetupVertexArrays(); | 204 | void SetupVertexArrays(); |
| 200 | 205 | ||
| 206 | DrawParameters SetupDraw(); | ||
| 207 | |||
| 201 | void SetupShaders(); | 208 | void SetupShaders(); |
| 202 | 209 | ||
| 203 | enum class AccelDraw { Disabled, Arrays, Indexed }; | 210 | enum class AccelDraw { Disabled, Arrays, Indexed }; |
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index 6ea59f2a3..eb1da0f9e 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp | |||
| @@ -21,9 +21,8 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) | |||
| 21 | ui->output_sink_combo_box->addItem(sink_detail.id); | 21 | ui->output_sink_combo_box->addItem(sink_detail.id); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | connect(ui->volume_slider, &QSlider::valueChanged, [this] { | 24 | connect(ui->volume_slider, &QSlider::valueChanged, this, |
| 25 | ui->volume_indicator->setText(tr("%1 %").arg(ui->volume_slider->sliderPosition())); | 25 | &ConfigureAudio::setVolumeIndicatorText); |
| 26 | }); | ||
| 27 | 26 | ||
| 28 | this->setConfiguration(); | 27 | this->setConfiguration(); |
| 29 | connect(ui->output_sink_combo_box, | 28 | connect(ui->output_sink_combo_box, |
| @@ -37,32 +36,48 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) | |||
| 37 | ConfigureAudio::~ConfigureAudio() = default; | 36 | ConfigureAudio::~ConfigureAudio() = default; |
| 38 | 37 | ||
| 39 | void ConfigureAudio::setConfiguration() { | 38 | void ConfigureAudio::setConfiguration() { |
| 39 | setOutputSinkFromSinkID(); | ||
| 40 | |||
| 41 | // The device list cannot be pre-populated (nor listed) until the output sink is known. | ||
| 42 | updateAudioDevices(ui->output_sink_combo_box->currentIndex()); | ||
| 43 | |||
| 44 | setAudioDeviceFromDeviceID(); | ||
| 45 | |||
| 46 | ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); | ||
| 47 | ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum()); | ||
| 48 | setVolumeIndicatorText(ui->volume_slider->sliderPosition()); | ||
| 49 | } | ||
| 50 | |||
| 51 | void ConfigureAudio::setOutputSinkFromSinkID() { | ||
| 40 | int new_sink_index = 0; | 52 | int new_sink_index = 0; |
| 53 | |||
| 54 | const QString sink_id = QString::fromStdString(Settings::values.sink_id); | ||
| 41 | for (int index = 0; index < ui->output_sink_combo_box->count(); index++) { | 55 | for (int index = 0; index < ui->output_sink_combo_box->count(); index++) { |
| 42 | if (ui->output_sink_combo_box->itemText(index).toStdString() == Settings::values.sink_id) { | 56 | if (ui->output_sink_combo_box->itemText(index) == sink_id) { |
| 43 | new_sink_index = index; | 57 | new_sink_index = index; |
| 44 | break; | 58 | break; |
| 45 | } | 59 | } |
| 46 | } | 60 | } |
| 47 | ui->output_sink_combo_box->setCurrentIndex(new_sink_index); | ||
| 48 | 61 | ||
| 49 | ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); | 62 | ui->output_sink_combo_box->setCurrentIndex(new_sink_index); |
| 50 | 63 | } | |
| 51 | // The device list cannot be pre-populated (nor listed) until the output sink is known. | ||
| 52 | updateAudioDevices(new_sink_index); | ||
| 53 | 64 | ||
| 65 | void ConfigureAudio::setAudioDeviceFromDeviceID() { | ||
| 54 | int new_device_index = -1; | 66 | int new_device_index = -1; |
| 67 | |||
| 68 | const QString device_id = QString::fromStdString(Settings::values.audio_device_id); | ||
| 55 | for (int index = 0; index < ui->audio_device_combo_box->count(); index++) { | 69 | for (int index = 0; index < ui->audio_device_combo_box->count(); index++) { |
| 56 | if (ui->audio_device_combo_box->itemText(index).toStdString() == | 70 | if (ui->audio_device_combo_box->itemText(index) == device_id) { |
| 57 | Settings::values.audio_device_id) { | ||
| 58 | new_device_index = index; | 71 | new_device_index = index; |
| 59 | break; | 72 | break; |
| 60 | } | 73 | } |
| 61 | } | 74 | } |
| 75 | |||
| 62 | ui->audio_device_combo_box->setCurrentIndex(new_device_index); | 76 | ui->audio_device_combo_box->setCurrentIndex(new_device_index); |
| 77 | } | ||
| 63 | 78 | ||
| 64 | ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum()); | 79 | void ConfigureAudio::setVolumeIndicatorText(int percentage) { |
| 65 | ui->volume_indicator->setText(tr("%1 %").arg(ui->volume_slider->sliderPosition())); | 80 | ui->volume_indicator->setText(tr("%1%", "Volume percentage (e.g. 50%)").arg(percentage)); |
| 66 | } | 81 | } |
| 67 | 82 | ||
| 68 | void ConfigureAudio::applyConfiguration() { | 83 | void ConfigureAudio::applyConfiguration() { |
| @@ -81,10 +96,10 @@ void ConfigureAudio::updateAudioDevices(int sink_index) { | |||
| 81 | ui->audio_device_combo_box->clear(); | 96 | ui->audio_device_combo_box->clear(); |
| 82 | ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); | 97 | ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); |
| 83 | 98 | ||
| 84 | std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); | 99 | const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); |
| 85 | std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices(); | 100 | const std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices(); |
| 86 | for (const auto& device : device_list) { | 101 | for (const auto& device : device_list) { |
| 87 | ui->audio_device_combo_box->addItem(device.c_str()); | 102 | ui->audio_device_combo_box->addItem(QString::fromStdString(device)); |
| 88 | } | 103 | } |
| 89 | } | 104 | } |
| 90 | 105 | ||
diff --git a/src/yuzu/configuration/configure_audio.h b/src/yuzu/configuration/configure_audio.h index 4f0af4163..207f9dfb3 100644 --- a/src/yuzu/configuration/configure_audio.h +++ b/src/yuzu/configuration/configure_audio.h | |||
| @@ -26,6 +26,9 @@ public slots: | |||
| 26 | 26 | ||
| 27 | private: | 27 | private: |
| 28 | void setConfiguration(); | 28 | void setConfiguration(); |
| 29 | void setOutputSinkFromSinkID(); | ||
| 30 | void setAudioDeviceFromDeviceID(); | ||
| 31 | void setVolumeIndicatorText(int percentage); | ||
| 29 | 32 | ||
| 30 | std::unique_ptr<Ui::ConfigureAudio> ui; | 33 | std::unique_ptr<Ui::ConfigureAudio> ui; |
| 31 | }; | 34 | }; |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 9292d9a42..f5db9e55b 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -13,7 +13,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent) | |||
| 13 | 13 | ||
| 14 | ui->setupUi(this); | 14 | ui->setupUi(this); |
| 15 | 15 | ||
| 16 | for (auto theme : UISettings::themes) { | 16 | for (const auto& theme : UISettings::themes) { |
| 17 | ui->theme_combobox->addItem(theme.first, theme.second); | 17 | ui->theme_combobox->addItem(theme.first, theme.second); |
| 18 | } | 18 | } |
| 19 | 19 | ||
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 839d58f59..cd1549462 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp | |||
| @@ -8,27 +8,7 @@ | |||
| 8 | #include "ui_configure_graphics.h" | 8 | #include "ui_configure_graphics.h" |
| 9 | #include "yuzu/configuration/configure_graphics.h" | 9 | #include "yuzu/configuration/configure_graphics.h" |
| 10 | 10 | ||
| 11 | ConfigureGraphics::ConfigureGraphics(QWidget* parent) | 11 | namespace { |
| 12 | : QWidget(parent), ui(new Ui::ConfigureGraphics) { | ||
| 13 | |||
| 14 | ui->setupUi(this); | ||
| 15 | this->setConfiguration(); | ||
| 16 | |||
| 17 | ui->frame_limit->setEnabled(Settings::values.use_frame_limit); | ||
| 18 | connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, | ||
| 19 | &QSpinBox::setEnabled); | ||
| 20 | connect(ui->bg_button, &QPushButton::clicked, this, [this] { | ||
| 21 | const QColor new_bg_color = QColorDialog::getColor(bg_color); | ||
| 22 | if (!new_bg_color.isValid()) | ||
| 23 | return; | ||
| 24 | bg_color = new_bg_color; | ||
| 25 | ui->bg_button->setStyleSheet( | ||
| 26 | QString("QPushButton { background-color: %1 }").arg(bg_color.name())); | ||
| 27 | }); | ||
| 28 | } | ||
| 29 | |||
| 30 | ConfigureGraphics::~ConfigureGraphics() = default; | ||
| 31 | |||
| 32 | enum class Resolution : int { | 12 | enum class Resolution : int { |
| 33 | Auto, | 13 | Auto, |
| 34 | Scale1x, | 14 | Scale1x, |
| @@ -67,6 +47,28 @@ Resolution FromResolutionFactor(float factor) { | |||
| 67 | } | 47 | } |
| 68 | return Resolution::Auto; | 48 | return Resolution::Auto; |
| 69 | } | 49 | } |
| 50 | } // Anonymous namespace | ||
| 51 | |||
| 52 | ConfigureGraphics::ConfigureGraphics(QWidget* parent) | ||
| 53 | : QWidget(parent), ui(new Ui::ConfigureGraphics) { | ||
| 54 | |||
| 55 | ui->setupUi(this); | ||
| 56 | this->setConfiguration(); | ||
| 57 | |||
| 58 | ui->frame_limit->setEnabled(Settings::values.use_frame_limit); | ||
| 59 | connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, | ||
| 60 | &QSpinBox::setEnabled); | ||
| 61 | connect(ui->bg_button, &QPushButton::clicked, this, [this] { | ||
| 62 | const QColor new_bg_color = QColorDialog::getColor(bg_color); | ||
| 63 | if (!new_bg_color.isValid()) | ||
| 64 | return; | ||
| 65 | bg_color = new_bg_color; | ||
| 66 | ui->bg_button->setStyleSheet( | ||
| 67 | QString("QPushButton { background-color: %1 }").arg(bg_color.name())); | ||
| 68 | }); | ||
| 69 | } | ||
| 70 | |||
| 71 | ConfigureGraphics::~ConfigureGraphics() = default; | ||
| 70 | 72 | ||
| 71 | void ConfigureGraphics::setConfiguration() { | 73 | void ConfigureGraphics::setConfiguration() { |
| 72 | ui->resolution_factor_combobox->setCurrentIndex( | 74 | ui->resolution_factor_combobox->setCurrentIndex( |
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp index d29abb74b..473937ea9 100644 --- a/src/yuzu/configuration/configure_input.cpp +++ b/src/yuzu/configuration/configure_input.cpp | |||
| @@ -152,9 +152,9 @@ ConfigureInput::ConfigureInput(QWidget* parent) | |||
| 152 | } | 152 | } |
| 153 | } | 153 | } |
| 154 | connect(analog_map_stick[analog_id], &QPushButton::released, [=]() { | 154 | connect(analog_map_stick[analog_id], &QPushButton::released, [=]() { |
| 155 | QMessageBox::information( | 155 | QMessageBox::information(this, tr("Information"), |
| 156 | this, "Information", | 156 | tr("After pressing OK, first move your joystick horizontally, " |
| 157 | "After pressing OK, first move your joystick horizontally, and then vertically."); | 157 | "and then vertically.")); |
| 158 | handleClick( | 158 | handleClick( |
| 159 | analog_map_stick[analog_id], | 159 | analog_map_stick[analog_id], |
| 160 | [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; }, | 160 | [=](const Common::ParamPackage& params) { analogs_param[analog_id] = params; }, |
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index a3b1fd357..4a09da685 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -119,7 +119,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeCallstack::GetChildren() cons | |||
| 119 | std::vector<std::unique_ptr<WaitTreeItem>> list; | 119 | std::vector<std::unique_ptr<WaitTreeItem>> list; |
| 120 | 120 | ||
| 121 | constexpr std::size_t BaseRegister = 29; | 121 | constexpr std::size_t BaseRegister = 29; |
| 122 | u64 base_pointer = thread.context.cpu_registers[BaseRegister]; | 122 | u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister]; |
| 123 | 123 | ||
| 124 | while (base_pointer != 0) { | 124 | while (base_pointer != 0) { |
| 125 | u64 lr = Memory::Read64(base_pointer + sizeof(u64)); | 125 | u64 lr = Memory::Read64(base_pointer + sizeof(u64)); |
| @@ -213,7 +213,7 @@ WaitTreeThread::~WaitTreeThread() = default; | |||
| 213 | QString WaitTreeThread::GetText() const { | 213 | QString WaitTreeThread::GetText() const { |
| 214 | const auto& thread = static_cast<const Kernel::Thread&>(object); | 214 | const auto& thread = static_cast<const Kernel::Thread&>(object); |
| 215 | QString status; | 215 | QString status; |
| 216 | switch (thread.status) { | 216 | switch (thread.GetStatus()) { |
| 217 | case Kernel::ThreadStatus::Running: | 217 | case Kernel::ThreadStatus::Running: |
| 218 | status = tr("running"); | 218 | status = tr("running"); |
| 219 | break; | 219 | break; |
| @@ -246,15 +246,17 @@ QString WaitTreeThread::GetText() const { | |||
| 246 | status = tr("dead"); | 246 | status = tr("dead"); |
| 247 | break; | 247 | break; |
| 248 | } | 248 | } |
| 249 | QString pc_info = tr(" PC = 0x%1 LR = 0x%2") | 249 | |
| 250 | .arg(thread.context.pc, 8, 16, QLatin1Char('0')) | 250 | const auto& context = thread.GetContext(); |
| 251 | .arg(thread.context.cpu_registers[30], 8, 16, QLatin1Char('0')); | 251 | const QString pc_info = tr(" PC = 0x%1 LR = 0x%2") |
| 252 | .arg(context.pc, 8, 16, QLatin1Char('0')) | ||
| 253 | .arg(context.cpu_registers[30], 8, 16, QLatin1Char('0')); | ||
| 252 | return WaitTreeWaitObject::GetText() + pc_info + " (" + status + ") "; | 254 | return WaitTreeWaitObject::GetText() + pc_info + " (" + status + ") "; |
| 253 | } | 255 | } |
| 254 | 256 | ||
| 255 | QColor WaitTreeThread::GetColor() const { | 257 | QColor WaitTreeThread::GetColor() const { |
| 256 | const auto& thread = static_cast<const Kernel::Thread&>(object); | 258 | const auto& thread = static_cast<const Kernel::Thread&>(object); |
| 257 | switch (thread.status) { | 259 | switch (thread.GetStatus()) { |
| 258 | case Kernel::ThreadStatus::Running: | 260 | case Kernel::ThreadStatus::Running: |
| 259 | return QColor(Qt::GlobalColor::darkGreen); | 261 | return QColor(Qt::GlobalColor::darkGreen); |
| 260 | case Kernel::ThreadStatus::Ready: | 262 | case Kernel::ThreadStatus::Ready: |
| @@ -284,7 +286,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 284 | const auto& thread = static_cast<const Kernel::Thread&>(object); | 286 | const auto& thread = static_cast<const Kernel::Thread&>(object); |
| 285 | 287 | ||
| 286 | QString processor; | 288 | QString processor; |
| 287 | switch (thread.processor_id) { | 289 | switch (thread.GetProcessorID()) { |
| 288 | case Kernel::ThreadProcessorId::THREADPROCESSORID_DEFAULT: | 290 | case Kernel::ThreadProcessorId::THREADPROCESSORID_DEFAULT: |
| 289 | processor = tr("default"); | 291 | processor = tr("default"); |
| 290 | break; | 292 | break; |
| @@ -292,32 +294,35 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { | |||
| 292 | case Kernel::ThreadProcessorId::THREADPROCESSORID_1: | 294 | case Kernel::ThreadProcessorId::THREADPROCESSORID_1: |
| 293 | case Kernel::ThreadProcessorId::THREADPROCESSORID_2: | 295 | case Kernel::ThreadProcessorId::THREADPROCESSORID_2: |
| 294 | case Kernel::ThreadProcessorId::THREADPROCESSORID_3: | 296 | case Kernel::ThreadProcessorId::THREADPROCESSORID_3: |
| 295 | processor = tr("core %1").arg(thread.processor_id); | 297 | processor = tr("core %1").arg(thread.GetProcessorID()); |
| 296 | break; | 298 | break; |
| 297 | default: | 299 | default: |
| 298 | processor = tr("Unknown processor %1").arg(thread.processor_id); | 300 | processor = tr("Unknown processor %1").arg(thread.GetProcessorID()); |
| 299 | break; | 301 | break; |
| 300 | } | 302 | } |
| 301 | 303 | ||
| 302 | list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor))); | 304 | list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor))); |
| 303 | list.push_back(std::make_unique<WaitTreeText>(tr("ideal core = %1").arg(thread.ideal_core))); | ||
| 304 | list.push_back( | 305 | list.push_back( |
| 305 | std::make_unique<WaitTreeText>(tr("affinity mask = %1").arg(thread.affinity_mask))); | 306 | std::make_unique<WaitTreeText>(tr("ideal core = %1").arg(thread.GetIdealCore()))); |
| 306 | list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadId()))); | 307 | list.push_back( |
| 308 | std::make_unique<WaitTreeText>(tr("affinity mask = %1").arg(thread.GetAffinityMask()))); | ||
| 309 | list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID()))); | ||
| 307 | list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)") | 310 | list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)") |
| 308 | .arg(thread.current_priority) | 311 | .arg(thread.GetPriority()) |
| 309 | .arg(thread.nominal_priority))); | 312 | .arg(thread.GetNominalPriority()))); |
| 310 | list.push_back(std::make_unique<WaitTreeText>( | 313 | list.push_back(std::make_unique<WaitTreeText>( |
| 311 | tr("last running ticks = %1").arg(thread.last_running_ticks))); | 314 | tr("last running ticks = %1").arg(thread.GetLastRunningTicks()))); |
| 312 | 315 | ||
| 313 | if (thread.mutex_wait_address != 0) | 316 | const VAddr mutex_wait_address = thread.GetMutexWaitAddress(); |
| 314 | list.push_back(std::make_unique<WaitTreeMutexInfo>(thread.mutex_wait_address)); | 317 | if (mutex_wait_address != 0) { |
| 315 | else | 318 | list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address)); |
| 319 | } else { | ||
| 316 | list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); | 320 | list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); |
| 321 | } | ||
| 317 | 322 | ||
| 318 | if (thread.status == Kernel::ThreadStatus::WaitSynchAny || | 323 | if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAny || |
| 319 | thread.status == Kernel::ThreadStatus::WaitSynchAll) { | 324 | thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAll) { |
| 320 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, | 325 | list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(), |
| 321 | thread.IsSleepingOnWaitAll())); | 326 | thread.IsSleepingOnWaitAll())); |
| 322 | } | 327 | } |
| 323 | 328 | ||
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index e228d61bd..1947bdb93 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -60,14 +60,13 @@ QString FormatGameName(const std::string& physical_name) { | |||
| 60 | QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, bool updatable = true) { | 60 | QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, bool updatable = true) { |
| 61 | QString out; | 61 | QString out; |
| 62 | for (const auto& kv : patch_manager.GetPatchVersionNames()) { | 62 | for (const auto& kv : patch_manager.GetPatchVersionNames()) { |
| 63 | if (!updatable && kv.first == FileSys::PatchType::Update) | 63 | if (!updatable && kv.first == "Update") |
| 64 | continue; | 64 | continue; |
| 65 | 65 | ||
| 66 | if (kv.second.empty()) { | 66 | if (kv.second.empty()) { |
| 67 | out.append(fmt::format("{}\n", FileSys::FormatPatchTypeName(kv.first)).c_str()); | 67 | out.append(fmt::format("{}\n", kv.first).c_str()); |
| 68 | } else { | 68 | } else { |
| 69 | out.append(fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), kv.second) | 69 | out.append(fmt::format("{} ({})\n", kv.first, kv.second).c_str()); |
| 70 | .c_str()); | ||
| 71 | } | 70 | } |
| 72 | } | 71 | } |
| 73 | 72 | ||
diff --git a/src/yuzu/ui_settings.cpp b/src/yuzu/ui_settings.cpp index 120b34990..a314493fc 100644 --- a/src/yuzu/ui_settings.cpp +++ b/src/yuzu/ui_settings.cpp | |||
| @@ -6,5 +6,11 @@ | |||
| 6 | 6 | ||
| 7 | namespace UISettings { | 7 | namespace UISettings { |
| 8 | 8 | ||
| 9 | const Themes themes{{ | ||
| 10 | {"Default", "default"}, | ||
| 11 | {"Dark", "qdarkstyle"}, | ||
| 12 | }}; | ||
| 13 | |||
| 9 | Values values = {}; | 14 | Values values = {}; |
| 10 | } | 15 | |
| 16 | } // namespace UISettings | ||
diff --git a/src/yuzu/ui_settings.h b/src/yuzu/ui_settings.h index 89d9140f3..2e617d52a 100644 --- a/src/yuzu/ui_settings.h +++ b/src/yuzu/ui_settings.h | |||
| @@ -15,9 +15,8 @@ namespace UISettings { | |||
| 15 | using ContextualShortcut = std::pair<QString, int>; | 15 | using ContextualShortcut = std::pair<QString, int>; |
| 16 | using Shortcut = std::pair<QString, ContextualShortcut>; | 16 | using Shortcut = std::pair<QString, ContextualShortcut>; |
| 17 | 17 | ||
| 18 | static const std::array<std::pair<QString, QString>, 2> themes = { | 18 | using Themes = std::array<std::pair<const char*, const char*>, 2>; |
| 19 | {std::make_pair(QString("Default"), QString("default")), | 19 | extern const Themes themes; |
| 20 | std::make_pair(QString("Dark"), QString("qdarkstyle"))}}; | ||
| 21 | 20 | ||
| 22 | struct Values { | 21 | struct Values { |
| 23 | QByteArray geometry; | 22 | QByteArray geometry; |