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: template <typename T> void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) { #ifdef _MSC_VER - fstream.open(Common::UTF8ToTStr(filename).c_str(), openmode); + fstream.open(Common::UTF8ToUTF16W(filename).c_str(), openmode); #else fstream.open(filename.c_str(), openmode); #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) { } void PrintMessage(const Entry& entry) { - auto str = FormatLogMessage(entry) + '\n'; + const auto str = FormatLogMessage(entry).append(1, '\n'); fputs(str.c_str(), stderr); } 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 @@ #include <algorithm> #include <cctype> #include <cerrno> +#include <codecvt> #include <cstdio> #include <cstdlib> #include <cstring> @@ -13,11 +14,7 @@ #include "common/string_util.h" #ifdef _WIN32 -#include <codecvt> #include <windows.h> -#include "common/common_funcs.h" -#else -#include <iconv.h> #endif namespace Common { @@ -195,11 +192,9 @@ std::string ReplaceAll(std::string result, const std::string& src, const std::st return result; } -#ifdef _WIN32 - std::string UTF16ToUTF8(const std::u16string& input) { -#if _MSC_VER >= 1900 - // Workaround for missing char16_t/char32_t instantiations in MSVC2015 +#ifdef _MSC_VER + // Workaround for missing char16_t/char32_t instantiations in MSVC2017 std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert; std::basic_string<__int16> tmp_buffer(input.cbegin(), input.cend()); return convert.to_bytes(tmp_buffer); @@ -210,8 +205,8 @@ std::string UTF16ToUTF8(const std::u16string& input) { } std::u16string UTF8ToUTF16(const std::string& input) { -#if _MSC_VER >= 1900 - // Workaround for missing char16_t/char32_t instantiations in MSVC2015 +#ifdef _MSC_VER + // Workaround for missing char16_t/char32_t instantiations in MSVC2017 std::wstring_convert<std::codecvt_utf8_utf16<__int16>, __int16> convert; auto tmp_buffer = convert.from_bytes(input); return std::u16string(tmp_buffer.cbegin(), tmp_buffer.cend()); @@ -221,6 +216,7 @@ std::u16string UTF8ToUTF16(const std::string& input) { #endif } +#ifdef _WIN32 static std::wstring CPToUTF16(u32 code_page, const std::string& input) { const auto size = MultiByteToWideChar(code_page, 0, input.data(), static_cast<int>(input.size()), nullptr, 0); @@ -261,124 +257,6 @@ std::wstring UTF8ToUTF16W(const std::string& input) { return CPToUTF16(CP_UTF8, input); } -std::string SHIFTJISToUTF8(const std::string& input) { - return UTF16ToUTF8(CPToUTF16(932, input)); -} - -std::string CP1252ToUTF8(const std::string& input) { - return UTF16ToUTF8(CPToUTF16(1252, input)); -} - -#else - -template <typename T> -static std::string CodeToUTF8(const char* fromcode, const std::basic_string<T>& input) { - iconv_t const conv_desc = iconv_open("UTF-8", fromcode); - if ((iconv_t)(-1) == conv_desc) { - LOG_ERROR(Common, "Iconv initialization failure [{}]: {}", fromcode, strerror(errno)); - iconv_close(conv_desc); - return {}; - } - - const std::size_t in_bytes = sizeof(T) * input.size(); - // Multiply by 4, which is the max number of bytes to encode a codepoint - const std::size_t out_buffer_size = 4 * in_bytes; - - std::string out_buffer(out_buffer_size, '\0'); - - auto src_buffer = &input[0]; - std::size_t src_bytes = in_bytes; - auto dst_buffer = &out_buffer[0]; - std::size_t dst_bytes = out_buffer.size(); - - while (0 != src_bytes) { - std::size_t const iconv_result = - iconv(conv_desc, (char**)(&src_buffer), &src_bytes, &dst_buffer, &dst_bytes); - - if (static_cast<std::size_t>(-1) == iconv_result) { - if (EILSEQ == errno || EINVAL == errno) { - // Try to skip the bad character - if (0 != src_bytes) { - --src_bytes; - ++src_buffer; - } - } else { - LOG_ERROR(Common, "iconv failure [{}]: {}", fromcode, strerror(errno)); - break; - } - } - } - - std::string result; - out_buffer.resize(out_buffer_size - dst_bytes); - out_buffer.swap(result); - - iconv_close(conv_desc); - - return result; -} - -std::u16string UTF8ToUTF16(const std::string& input) { - iconv_t const conv_desc = iconv_open("UTF-16LE", "UTF-8"); - if ((iconv_t)(-1) == conv_desc) { - LOG_ERROR(Common, "Iconv initialization failure [UTF-8]: {}", strerror(errno)); - iconv_close(conv_desc); - return {}; - } - - const std::size_t in_bytes = sizeof(char) * input.size(); - // Multiply by 4, which is the max number of bytes to encode a codepoint - const std::size_t out_buffer_size = 4 * sizeof(char16_t) * in_bytes; - - std::u16string out_buffer(out_buffer_size, char16_t{}); - - char* src_buffer = const_cast<char*>(&input[0]); - std::size_t src_bytes = in_bytes; - char* dst_buffer = (char*)(&out_buffer[0]); - std::size_t dst_bytes = out_buffer.size(); - - while (0 != src_bytes) { - std::size_t const iconv_result = - iconv(conv_desc, &src_buffer, &src_bytes, &dst_buffer, &dst_bytes); - - if (static_cast<std::size_t>(-1) == iconv_result) { - if (EILSEQ == errno || EINVAL == errno) { - // Try to skip the bad character - if (0 != src_bytes) { - --src_bytes; - ++src_buffer; - } - } else { - LOG_ERROR(Common, "iconv failure [UTF-8]: {}", strerror(errno)); - break; - } - } - } - - std::u16string result; - out_buffer.resize(out_buffer_size - dst_bytes); - out_buffer.swap(result); - - iconv_close(conv_desc); - - return result; -} - -std::string UTF16ToUTF8(const std::u16string& input) { - return CodeToUTF8("UTF-16LE", input); -} - -std::string CP1252ToUTF8(const std::string& input) { - // return CodeToUTF8("CP1252//TRANSLIT", input); - // return CodeToUTF8("CP1252//IGNORE", input); - return CodeToUTF8("CP1252", input); -} - -std::string SHIFTJISToUTF8(const std::string& input) { - // return CodeToUTF8("CP932", input); - return CodeToUTF8("SJIS", input); -} - #endif 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 std::string UTF16ToUTF8(const std::u16string& input); std::u16string UTF8ToUTF16(const std::string& input); -std::string CP1252ToUTF8(const std::string& str); -std::string SHIFTJISToUTF8(const std::string& str); - #ifdef _WIN32 std::string UTF16ToUTF8(const std::wstring& input); std::wstring UTF8ToUTF16W(const std::string& str); -#ifdef _UNICODE -inline std::string TStrToUTF8(const std::wstring& str) { - return UTF16ToUTF8(str); -} - -inline std::wstring UTF8ToTStr(const std::string& str) { - return UTF8ToUTF16W(str); -} -#else -inline std::string TStrToUTF8(const std::string& str) { - return str; -} - -inline std::string UTF8ToTStr(const std::string& str) { - return str; -} -#endif - #endif /** 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 file_sys/errors.h file_sys/fsmitm_romfsbuild.cpp file_sys/fsmitm_romfsbuild.h + file_sys/ips_layer.cpp + file_sys/ips_layer.h file_sys/mode.h file_sys/nca_metadata.cpp 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: parent.jit->HaltExecution(); parent.SetPC(pc); Kernel::Thread* thread = Kernel::GetCurrentThread(); - parent.SaveContext(thread->context); + parent.SaveContext(thread->GetContext()); GDBStub::Break(); GDBStub::SendTrap(thread, 5); 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) { uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address); } Kernel::Thread* thread = Kernel::GetCurrentThread(); - SaveContext(thread->context); + SaveContext(thread->GetContext()); if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) { last_bkpt_hit = false; 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 { constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"}; -XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { +XCI::XCI(VirtualFile file_) + : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, + partitions(0x4) { if (file->ReadObject(&header) != sizeof(GamecardHeader)) { status = Loader::ResultStatus::ErrorBadXCIHeader; 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 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/assert.h" +#include "common/swap.h" +#include "core/file_sys/ips_layer.h" +#include "core/file_sys/vfs_vector.h" + +namespace FileSys { + +enum class IPSFileType { + IPS, + IPS32, + Error, +}; + +static IPSFileType IdentifyMagic(const std::vector<u8>& magic) { + if (magic.size() != 5) + return IPSFileType::Error; + if (magic == std::vector<u8>{'P', 'A', 'T', 'C', 'H'}) + return IPSFileType::IPS; + if (magic == std::vector<u8>{'I', 'P', 'S', '3', '2'}) + return IPSFileType::IPS32; + return IPSFileType::Error; +} + +VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { + if (in == nullptr || ips == nullptr) + return nullptr; + + const auto type = IdentifyMagic(ips->ReadBytes(0x5)); + if (type == IPSFileType::Error) + return nullptr; + + auto in_data = in->ReadAllBytes(); + + std::vector<u8> temp(type == IPSFileType::IPS ? 3 : 4); + u64 offset = 5; // After header + while (ips->Read(temp.data(), temp.size(), offset) == temp.size()) { + offset += temp.size(); + if (type == IPSFileType::IPS32 && temp == std::vector<u8>{'E', 'E', 'O', 'F'} || + type == IPSFileType::IPS && temp == std::vector<u8>{'E', 'O', 'F'}) { + break; + } + + u32 real_offset{}; + if (type == IPSFileType::IPS32) + real_offset = (temp[0] << 24) | (temp[1] << 16) | (temp[2] << 8) | temp[3]; + else + real_offset = (temp[0] << 16) | (temp[1] << 8) | temp[2]; + + u16 data_size{}; + if (ips->ReadObject(&data_size, offset) != sizeof(u16)) + return nullptr; + data_size = Common::swap16(data_size); + offset += sizeof(u16); + + if (data_size == 0) { // RLE + u16 rle_size{}; + if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) + return nullptr; + rle_size = Common::swap16(data_size); + offset += sizeof(u16); + + const auto data = ips->ReadByte(offset++); + if (data == boost::none) + return nullptr; + + if (real_offset + rle_size > in_data.size()) + rle_size = in_data.size() - real_offset; + std::memset(in_data.data() + real_offset, data.get(), rle_size); + } else { // Standard Patch + auto read = data_size; + if (real_offset + read > in_data.size()) + read = in_data.size() - real_offset; + if (ips->Read(in_data.data() + real_offset, read, offset) != data_size) + return nullptr; + offset += data_size; + } + } + + if (temp != std::vector<u8>{'E', 'E', 'O', 'F'} && temp != std::vector<u8>{'E', 'O', 'F'}) + return nullptr; + return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory()); +} + +} // 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 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> + +#include "core/file_sys/vfs.h" + +namespace FileSys { + +VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips); + +} // 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 @@ #include <algorithm> #include <array> #include <cstddef> +#include <cstring> +#include "common/hex_util.h" #include "common/logging/log.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" +#include "core/file_sys/ips_layer.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/romfs.h" #include "core/file_sys/vfs_layered.h" +#include "core/file_sys/vfs_vector.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" @@ -21,6 +25,14 @@ namespace FileSys { constexpr u64 SINGLE_BYTE_MODULUS = 0x100; constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; +struct NSOBuildHeader { + u32_le magic; + INSERT_PADDING_BYTES(0x3C); + std::array<u8, 0x20> build_id; + INSERT_PADDING_BYTES(0xA0); +}; +static_assert(sizeof(NSOBuildHeader) == 0x100, "NSOBuildHeader has incorrect size."); + std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { std::array<u8, sizeof(u32)> bytes{}; bytes[0] = version % SINGLE_BYTE_MODULUS; @@ -34,16 +46,6 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) { return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]); } -constexpr std::array<const char*, 3> PATCH_TYPE_NAMES{ - "Update", - "LayeredFS", - "DLC", -}; - -std::string FormatPatchTypeName(PatchType type) { - return PATCH_TYPE_NAMES.at(static_cast<std::size_t>(type)); -} - PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} PatchManager::~PatchManager() = default; @@ -71,6 +73,79 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { return exefs; } +static std::vector<VirtualFile> CollectIPSPatches(const std::vector<VirtualDir>& patch_dirs, + const std::string& build_id) { + std::vector<VirtualFile> ips; + ips.reserve(patch_dirs.size()); + for (const auto& subdir : patch_dirs) { + auto exefs_dir = subdir->GetSubdirectory("exefs"); + if (exefs_dir != nullptr) { + for (const auto& file : exefs_dir->GetFiles()) { + if (file->GetExtension() != "ips") + continue; + auto name = file->GetName(); + const auto p1 = name.substr(0, name.find('.')); + const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1); + + if (build_id == this_build_id) + ips.push_back(file); + } + } + } + + return ips; +} + +std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const { + if (nso.size() < 0x100) + return nso; + + NSOBuildHeader header; + std::memcpy(&header, nso.data(), sizeof(NSOBuildHeader)); + + if (header.magic != Common::MakeMagic('N', 'S', 'O', '0')) + return nso; + + const auto build_id_raw = Common::HexArrayToString(header.build_id); + const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); + + LOG_INFO(Loader, "Patching NSO for build_id={}", build_id); + + const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); + auto patch_dirs = load_dir->GetSubdirectories(); + std::sort(patch_dirs.begin(), patch_dirs.end(), + [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); + const auto ips = CollectIPSPatches(patch_dirs, build_id); + + auto out = nso; + for (const auto& ips_file : ips) { + LOG_INFO(Loader, " - Appling IPS patch from mod \"{}\"", + ips_file->GetContainingDirectory()->GetParentDirectory()->GetName()); + const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), ips_file); + if (patched != nullptr) + out = patched->ReadAllBytes(); + } + + if (out.size() < 0x100) + return nso; + std::memcpy(out.data(), &header, sizeof(NSOBuildHeader)); + return out; +} + +bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { + const auto build_id_raw = Common::HexArrayToString(build_id_); + const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1); + + LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); + + const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); + auto patch_dirs = load_dir->GetSubdirectories(); + std::sort(patch_dirs.begin(), patch_dirs.end(), + [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); + + return !CollectIPSPatches(patch_dirs, build_id).empty(); +} + static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) { @@ -138,8 +213,19 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, return romfs; } -std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const { - std::map<PatchType, std::string> out; +static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) { + if (to.empty()) + to += with; + else + to += ", " + with; +} + +static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { + return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); +} + +std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames() const { + std::map<std::string, std::string, std::less<>> out; const auto installed = Service::FileSystem::GetUnionContents(); // Game Updates @@ -148,23 +234,36 @@ std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const { auto [nacp, discard_icon_file] = update.GetControlMetadata(); if (nacp != nullptr) { - out[PatchType::Update] = nacp->GetVersionString(); + out.insert_or_assign("Update", nacp->GetVersionString()); } else { if (installed->HasEntry(update_tid, ContentRecordType::Program)) { const auto meta_ver = installed->GetEntryVersion(update_tid); if (meta_ver == boost::none || meta_ver.get() == 0) { - out[PatchType::Update] = ""; + out.insert_or_assign("Update", ""); } else { - out[PatchType::Update] = - FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements); + out.insert_or_assign( + "Update", + FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements)); } } } - // LayeredFS - const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id); - if (lfs_dir != nullptr && lfs_dir->GetSize() > 0) - out.insert_or_assign(PatchType::LayeredFS, ""); + // General Mods (LayeredFS and IPS) + const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id); + if (mod_dir != nullptr && mod_dir->GetSize() > 0) { + for (const auto& mod : mod_dir->GetSubdirectories()) { + std::string types; + if (IsDirValidAndNonEmpty(mod->GetSubdirectory("exefs"))) + AppendCommaIfNotEmpty(types, "IPS"); + if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs"))) + AppendCommaIfNotEmpty(types, "LayeredFS"); + + if (types.empty()) + continue; + + out.insert_or_assign(mod->GetName(), types); + } + } // DLC const auto dlc_entries = installed->ListEntriesFilter(TitleType::AOC, ContentRecordType::Data); @@ -186,7 +285,7 @@ std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const { list += fmt::format("{}", dlc_match.back().title_id & 0x7FF); - out.insert_or_assign(PatchType::DLC, std::move(list)); + out.insert_or_assign("DLC", std::move(list)); } 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 { std::string FormatTitleVersion(u32 version, TitleVersionFormat format = TitleVersionFormat::ThreeElements); -enum class PatchType { - Update, - LayeredFS, - DLC, -}; - -std::string FormatPatchTypeName(PatchType type); - // A centralized class to manage patches to games. class PatchManager { public: @@ -42,6 +34,14 @@ public: // - Game Updates VirtualDir PatchExeFS(VirtualDir exefs) const; + // Currently tracked NSO patches: + // - IPS + std::vector<u8> PatchNSO(const std::vector<u8>& nso) const; + + // Checks to see if PatchNSO() will have any effect given the NSO's build ID. + // Used to prevent expensive copies in NSO loader. + bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const; + // Currently tracked RomFS patches: // - Game Updates // - LayeredFS @@ -49,8 +49,8 @@ public: ContentRecordType type = ContentRecordType::Program) const; // Returns a vector of pairs between patch names and patch versions. - // i.e. Update v80 will return {Update, 80} - std::map<PatchType, std::string> GetPatchVersionNames() const; + // i.e. Update 3.2.2 will return {"Update", "3.2.2"} + std::map<std::string, std::string, std::less<>> GetPatchVersionNames() const; // Given title_id of the program, attempts to get the control data of the update and parse it, // 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 @@ #include "core/loader/loader.h" namespace FileSys { +namespace { +void SetTicketKeys(const std::vector<VirtualFile>& files) { + Core::Crypto::KeyManager keys; + + for (const auto& ticket_file : files) { + if (ticket_file == nullptr) { + continue; + } + + if (ticket_file->GetExtension() != "tik") { + continue; + } + + if (ticket_file->GetSize() < + Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { + continue; + } + + Core::Crypto::Key128 key{}; + ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); + + // We get the name without the extension in order to create the rights ID. + std::string name_only(ticket_file->GetName()); + name_only.erase(name_only.size() - 4); + + const auto rights_id_raw = Common::HexStringToArray<16>(name_only); + u128 rights_id; + std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); + keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); + } +} +} // Anonymous namespace + NSP::NSP(VirtualFile file_) : file(std::move(file_)), status{Loader::ResultStatus::Success}, pfs(std::make_shared<PartitionFilesystem>(file)) { @@ -26,83 +59,16 @@ NSP::NSP(VirtualFile file_) return; } + const auto files = pfs->GetFiles(); + if (IsDirectoryExeFS(pfs)) { extracted = true; - exefs = pfs; - - const auto& files = pfs->GetFiles(); - const auto romfs_iter = - std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { - return file->GetName().find(".romfs") != std::string::npos; - }); - if (romfs_iter != files.end()) - romfs = *romfs_iter; + InitializeExeFSAndRomFS(files); return; } - extracted = false; - const auto files = pfs->GetFiles(); - - Core::Crypto::KeyManager keys; - for (const auto& ticket_file : files) { - if (ticket_file->GetExtension() == "tik") { - if (ticket_file == nullptr || - ticket_file->GetSize() < - Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { - continue; - } - - Core::Crypto::Key128 key{}; - ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); - std::string_view name_only(ticket_file->GetName()); - name_only.remove_suffix(4); - const auto rights_id_raw = Common::HexStringToArray<16>(name_only); - u128 rights_id; - std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); - keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); - } - } - - for (const auto& outer_file : files) { - if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") { - const auto nca = std::make_shared<NCA>(outer_file); - if (nca->GetStatus() != Loader::ResultStatus::Success) { - program_status[nca->GetTitleId()] = nca->GetStatus(); - continue; - } - - const auto section0 = nca->GetSubdirectories()[0]; - - for (const auto& inner_file : section0->GetFiles()) { - if (inner_file->GetExtension() != "cnmt") - continue; - - const CNMT cnmt(inner_file); - auto& ncas_title = ncas[cnmt.GetTitleID()]; - - ncas_title[ContentRecordType::Meta] = nca; - for (const auto& rec : cnmt.GetContentRecords()) { - const auto id_string = Common::HexArrayToString(rec.nca_id, false); - const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); - if (next_file == nullptr) { - LOG_WARNING(Service_FS, - "NCA with ID {}.nca is listed in content metadata, but cannot " - "be found in PFS. NSP appears to be corrupted.", - id_string); - continue; - } - - auto next_nca = std::make_shared<NCA>(next_file); - if (next_nca->GetType() == NCAContentType::Program) - program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); - if (next_nca->GetStatus() == Loader::ResultStatus::Success) - ncas_title[rec.type] = std::move(next_nca); - } - - break; - } - } - } + SetTicketKeys(files); + ReadNCAs(files); } NSP::~NSP() = default; @@ -242,4 +208,63 @@ VirtualDir NSP::GetParentDirectory() const { bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { return false; } + +void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { + exefs = pfs; + + const auto romfs_iter = std::find_if(files.begin(), files.end(), [](const VirtualFile& file) { + return file->GetName().rfind(".romfs") != std::string::npos; + }); + + if (romfs_iter == files.end()) { + return; + } + + romfs = *romfs_iter; +} + +void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { + for (const auto& outer_file : files) { + if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { + continue; + } + + const auto nca = std::make_shared<NCA>(outer_file); + if (nca->GetStatus() != Loader::ResultStatus::Success) { + program_status[nca->GetTitleId()] = nca->GetStatus(); + continue; + } + + const auto section0 = nca->GetSubdirectories()[0]; + + for (const auto& inner_file : section0->GetFiles()) { + if (inner_file->GetExtension() != "cnmt") + continue; + + const CNMT cnmt(inner_file); + auto& ncas_title = ncas[cnmt.GetTitleID()]; + + ncas_title[ContentRecordType::Meta] = nca; + for (const auto& rec : cnmt.GetContentRecords()) { + const auto id_string = Common::HexArrayToString(rec.nca_id, false); + const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); + if (next_file == nullptr) { + LOG_WARNING(Service_FS, + "NCA with ID {}.nca is listed in content metadata, but cannot " + "be found in PFS. NSP appears to be corrupted.", + id_string); + continue; + } + + auto next_nca = std::make_shared<NCA>(next_file); + if (next_nca->GetType() == NCAContentType::Program) + program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); + if (next_nca->GetStatus() == Loader::ResultStatus::Success) + ncas_title[rec.type] = std::move(next_nca); + } + + break; + } + } +} } // 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: bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; private: + void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files); + void ReadNCAs(const std::vector<VirtualFile>& files); + VirtualFile file; - bool extracted; + bool extracted = false; Loader::ResultStatus status; std::map<u64, Loader::ResultStatus> program_status; 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) { for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); for (auto& thread : threads) { - if (thread->GetThreadId() == static_cast<u32>(id)) { + if (thread->GetThreadID() == static_cast<u32>(id)) { current_core = core; return thread.get(); } @@ -223,16 +223,18 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) { return 0; } + const auto& thread_context = thread->GetContext(); + if (id < SP_REGISTER) { - return thread->context.cpu_registers[id]; + return thread_context.cpu_registers[id]; } else if (id == SP_REGISTER) { - return thread->context.sp; + return thread_context.sp; } else if (id == PC_REGISTER) { - return thread->context.pc; + return thread_context.pc; } else if (id == PSTATE_REGISTER) { - return thread->context.pstate; + return thread_context.pstate; } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { - return thread->context.vector_registers[id - UC_ARM64_REG_Q0][0]; + return thread_context.vector_registers[id - UC_ARM64_REG_Q0][0]; } else { return 0; } @@ -243,16 +245,18 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr) return; } + auto& thread_context = thread->GetContext(); + if (id < SP_REGISTER) { - thread->context.cpu_registers[id] = val; + thread_context.cpu_registers[id] = val; } else if (id == SP_REGISTER) { - thread->context.sp = val; + thread_context.sp = val; } else if (id == PC_REGISTER) { - thread->context.pc = val; + thread_context.pc = val; } else if (id == PSTATE_REGISTER) { - thread->context.pstate = static_cast<u32>(val); + thread_context.pstate = static_cast<u32>(val); } else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) { - thread->context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val; + thread_context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val; } } @@ -595,7 +599,7 @@ static void HandleQuery() { for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) { const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList(); for (const auto& thread : threads) { - val += fmt::format("{:x}", thread->GetThreadId()); + val += fmt::format("{:x}", thread->GetThreadID()); val += ","; } } @@ -612,7 +616,7 @@ static void HandleQuery() { for (const auto& thread : threads) { buffer += fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*", - thread->GetThreadId(), core, thread->GetThreadId()); + thread->GetThreadID(), core, thread->GetThreadID()); } } buffer += "</threads>"; @@ -693,7 +697,7 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) { } if (thread) { - buffer += fmt::format(";thread:{:x};", thread->GetThreadId()); + buffer += fmt::format(";thread:{:x};", thread->GetThreadID()); } SendReply(buffer.c_str()); @@ -857,7 +861,9 @@ static void WriteRegister() { } // Update Unicorn context skipping scheduler, no running threads at this point - Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context); + Core::System::GetInstance() + .ArmInterface(current_core) + .LoadContext(current_thread->GetContext()); SendReply("OK"); } @@ -886,7 +892,9 @@ static void WriteRegisters() { } // Update Unicorn context skipping scheduler, no running threads at this point - Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context); + Core::System::GetInstance() + .ArmInterface(current_core) + .LoadContext(current_thread->GetContext()); SendReply("OK"); } @@ -960,7 +968,9 @@ static void Step() { if (command_length > 1) { RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread); // Update Unicorn context skipping scheduler, no running threads at this point - Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context); + Core::System::GetInstance() + .ArmInterface(current_core) + .LoadContext(current_thread->GetContext()); } step_loop = true; 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 { // Performs actual address waiting logic. static ResultCode WaitForAddress(VAddr address, s64 timeout) { SharedPtr<Thread> current_thread = GetCurrentThread(); - current_thread->arb_wait_address = address; - current_thread->status = ThreadStatus::WaitArb; - current_thread->wakeup_callback = nullptr; + current_thread->SetArbiterWaitAddress(address); + current_thread->SetStatus(ThreadStatus::WaitArb); + current_thread->InvalidateWakeupCallback(); current_thread->WakeAfterDelay(timeout); - Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); + Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); return RESULT_TIMEOUT; } @@ -39,10 +39,10 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) std::vector<SharedPtr<Thread>>& waiting_threads, VAddr arb_addr) { const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); - auto& thread_list = scheduler->GetThreadList(); + const auto& thread_list = scheduler->GetThreadList(); - for (auto& thread : thread_list) { - if (thread->arb_wait_address == arb_addr) + for (const auto& thread : thread_list) { + if (thread->GetArbiterWaitAddress() == arb_addr) waiting_threads.push_back(thread); } }; @@ -57,7 +57,7 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address) // Sort them by priority, such that the highest priority ones come first. std::sort(threads.begin(), threads.end(), [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { - return lhs->current_priority < rhs->current_priority; + return lhs->GetPriority() < rhs->GetPriority(); }); return threads; @@ -73,9 +73,9 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num // Signal the waiting threads. for (std::size_t i = 0; i < last; i++) { - ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb); + ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb); waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS); - waiting_threads[i]->arb_wait_address = 0; + waiting_threads[i]->SetArbiterWaitAddress(0); waiting_threads[i]->ResumeFromWait(); } } 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, Kernel::SharedPtr<Kernel::Event> event) { // Put the client thread to sleep until the wait event is signaled or the timeout expires. - thread->wakeup_callback = [context = *this, callback]( + thread->SetWakeupCallback([context = *this, callback]( ThreadWakeupReason reason, SharedPtr<Thread> thread, SharedPtr<WaitObject> object, std::size_t index) mutable -> bool { - ASSERT(thread->status == ThreadStatus::WaitHLEEvent); + ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent); callback(thread, context, reason); context.WriteToOutgoingCommandBuffer(*thread); return true; - }; + }); if (!event) { // Create event if not provided @@ -59,8 +59,8 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread, } event->Clear(); - thread->status = ThreadStatus::WaitHLEEvent; - thread->wait_objects = {event}; + thread->SetStatus(ThreadStatus::WaitHLEEvent); + thread->SetWaitObjects({event}); event->AddWaitingThread(thread); if (timeout > 0) { @@ -209,7 +209,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) { std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf; - Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), + Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), dst_cmdbuf.size() * sizeof(u32)); // 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) } // Copy the translated command buffer back into the thread's command buffer area. - Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(), + Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(), dst_cmdbuf.size() * sizeof(u32)); 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_ bool resume = true; - if (thread->status == ThreadStatus::WaitSynchAny || - thread->status == ThreadStatus::WaitSynchAll || - thread->status == ThreadStatus::WaitHLEEvent) { + if (thread->GetStatus() == ThreadStatus::WaitSynchAny || + thread->GetStatus() == ThreadStatus::WaitSynchAll || + thread->GetStatus() == ThreadStatus::WaitHLEEvent) { // Remove the thread from each of its waiting objects' waitlists - for (auto& object : thread->wait_objects) { + for (const auto& object : thread->GetWaitObjects()) { object->RemoveWaitingThread(thread.get()); } - thread->wait_objects.clear(); + thread->ClearWaitObjects(); // Invoke the wakeup callback before clearing the wait objects - if (thread->wakeup_callback) { - resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0); + if (thread->HasWakeupCallback()) { + resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0); } } - if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 || - thread->wait_handle) { - ASSERT(thread->status == ThreadStatus::WaitMutex); - thread->mutex_wait_address = 0; - thread->condvar_wait_address = 0; - thread->wait_handle = 0; + if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 || + thread->GetWaitHandle() != 0) { + ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); + thread->SetMutexWaitAddress(0); + thread->SetCondVarWaitAddress(0); + thread->SetWaitHandle(0); - auto lock_owner = thread->lock_owner; + auto* const lock_owner = thread->GetLockOwner(); // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance // and don't have a lock owner unless SignalProcessWideKey was called first and the thread // wasn't awakened due to the mutex already being acquired. - if (lock_owner) { + if (lock_owner != nullptr) { lock_owner->RemoveMutexWaiter(thread); } } - if (thread->arb_wait_address != 0) { - ASSERT(thread->status == ThreadStatus::WaitArb); - thread->arb_wait_address = 0; + if (thread->GetArbiterWaitAddress() != 0) { + ASSERT(thread->GetStatus() == ThreadStatus::WaitArb); + thread->SetArbiterWaitAddress(0); } 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( SharedPtr<Thread> highest_priority_thread; u32 num_waiters = 0; - for (auto& thread : current_thread->wait_mutex_threads) { - if (thread->mutex_wait_address != mutex_addr) + for (const auto& thread : current_thread->GetMutexWaitingThreads()) { + if (thread->GetMutexWaitAddress() != mutex_addr) continue; - ASSERT(thread->status == ThreadStatus::WaitMutex); + ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); ++num_waiters; if (highest_priority_thread == nullptr || @@ -47,12 +47,12 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread( /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread, SharedPtr<Thread> new_owner) { - auto threads = current_thread->wait_mutex_threads; - for (auto& thread : threads) { - if (thread->mutex_wait_address != mutex_addr) + const auto threads = current_thread->GetMutexWaitingThreads(); + for (const auto& thread : threads) { + if (thread->GetMutexWaitAddress() != mutex_addr) continue; - ASSERT(thread->lock_owner == current_thread); + ASSERT(thread->GetLockOwner() == current_thread); current_thread->RemoveMutexWaiter(thread); if (new_owner != thread) new_owner->AddMutexWaiter(thread); @@ -84,11 +84,11 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho return ERR_INVALID_HANDLE; // Wait until the mutex is released - GetCurrentThread()->mutex_wait_address = address; - GetCurrentThread()->wait_handle = requesting_thread_handle; + GetCurrentThread()->SetMutexWaitAddress(address); + GetCurrentThread()->SetWaitHandle(requesting_thread_handle); - GetCurrentThread()->status = ThreadStatus::WaitMutex; - GetCurrentThread()->wakeup_callback = nullptr; + GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex); + GetCurrentThread()->InvalidateWakeupCallback(); // Update the lock holder thread's priority to prevent priority inversion. holding_thread->AddMutexWaiter(GetCurrentThread()); @@ -115,7 +115,7 @@ ResultCode Mutex::Release(VAddr address) { // Transfer the ownership of the mutex from the previous owner to the new one. TransferMutexOwnership(address, GetCurrentThread(), thread); - u32 mutex_value = thread->wait_handle; + u32 mutex_value = thread->GetWaitHandle(); if (num_waiters >= 2) { // Notify the guest that there are still some threads waiting for the mutex @@ -125,13 +125,13 @@ ResultCode Mutex::Release(VAddr address) { // Grant the mutex to the next waiting thread and resume it. Memory::Write32(address, mutex_value); - ASSERT(thread->status == ThreadStatus::WaitMutex); + ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); thread->ResumeFromWait(); - thread->lock_owner = nullptr; - thread->condvar_wait_address = 0; - thread->mutex_wait_address = 0; - thread->wait_handle = 0; + thread->SetLockOwner(nullptr); + thread->SetCondVarWaitAddress(0); + thread->SetMutexWaitAddress(0); + thread->SetWaitHandle(0); return RESULT_SUCCESS; } 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() { const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) { for (auto& thread : thread_list) { - if (thread->owner_process != this) + if (thread->GetOwnerProcess() != this) continue; if (thread == GetCurrentThread()) continue; // TODO(Subv): When are the other running/ready threads terminated? - ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny || - thread->status == ThreadStatus::WaitSynchAll, + ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny || + thread->GetStatus() == ThreadStatus::WaitSynchAll, "Exiting processes with non-waiting threads is currently unimplemented"); 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() { Thread* next = nullptr; Thread* thread = GetCurrentThread(); - if (thread && thread->status == ThreadStatus::Running) { + if (thread && thread->GetStatus() == ThreadStatus::Running) { // We have to do better than the current thread. // This call returns null when that's not possible. - next = ready_queue.pop_first_better(thread->current_priority); + next = ready_queue.pop_first_better(thread->GetPriority()); if (!next) { // Otherwise just keep going with the current thread next = thread; @@ -58,22 +58,21 @@ void Scheduler::SwitchContext(Thread* new_thread) { // Save context for previous thread if (previous_thread) { - previous_thread->last_running_ticks = CoreTiming::GetTicks(); - cpu_core.SaveContext(previous_thread->context); + cpu_core.SaveContext(previous_thread->GetContext()); // Save the TPIDR_EL0 system register in case it was modified. - previous_thread->tpidr_el0 = cpu_core.GetTPIDR_EL0(); + previous_thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0()); - if (previous_thread->status == ThreadStatus::Running) { + if (previous_thread->GetStatus() == ThreadStatus::Running) { // This is only the case when a reschedule is triggered without the current thread // yielding execution (i.e. an event triggered, system core time-sliced, etc) - ready_queue.push_front(previous_thread->current_priority, previous_thread); - previous_thread->status = ThreadStatus::Ready; + ready_queue.push_front(previous_thread->GetPriority(), previous_thread); + previous_thread->SetStatus(ThreadStatus::Ready); } } // Load context of new thread if (new_thread) { - ASSERT_MSG(new_thread->status == ThreadStatus::Ready, + ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready, "Thread must be ready to become running."); // Cancel any outstanding wakeup events for this thread @@ -83,15 +82,16 @@ void Scheduler::SwitchContext(Thread* new_thread) { current_thread = new_thread; - ready_queue.remove(new_thread->current_priority, new_thread); - new_thread->status = ThreadStatus::Running; + ready_queue.remove(new_thread->GetPriority(), new_thread); + new_thread->SetStatus(ThreadStatus::Running); - if (previous_process != current_thread->owner_process) { - Core::CurrentProcess() = current_thread->owner_process; + const auto thread_owner_process = current_thread->GetOwnerProcess(); + if (previous_process != thread_owner_process) { + Core::CurrentProcess() = thread_owner_process; SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table); } - cpu_core.LoadContext(new_thread->context); + cpu_core.LoadContext(new_thread->GetContext()); cpu_core.SetTlsAddress(new_thread->GetTLSAddress()); cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0()); cpu_core.ClearExclusiveState(); @@ -136,14 +136,14 @@ void Scheduler::RemoveThread(Thread* thread) { void Scheduler::ScheduleThread(Thread* thread, u32 priority) { std::lock_guard<std::mutex> lock(scheduler_mutex); - ASSERT(thread->status == ThreadStatus::Ready); + ASSERT(thread->GetStatus() == ThreadStatus::Ready); ready_queue.push_back(priority, thread); } void Scheduler::UnscheduleThread(Thread* thread, u32 priority) { std::lock_guard<std::mutex> lock(scheduler_mutex); - ASSERT(thread->status == ThreadStatus::Ready); + ASSERT(thread->GetStatus() == ThreadStatus::Ready); ready_queue.remove(priority, thread); } @@ -151,8 +151,8 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { std::lock_guard<std::mutex> lock(scheduler_mutex); // If thread was ready, adjust queues - if (thread->status == ThreadStatus::Ready) - ready_queue.move(thread, thread->current_priority, priority); + if (thread->GetStatus() == ThreadStatus::Ready) + ready_queue.move(thread, thread->GetPriority(), priority); else ready_queue.prepare(priority); } 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) { result = hle_handler->HandleSyncRequest(context); } - if (thread->status == ThreadStatus::Running) { + if (thread->GetStatus() == ThreadStatus::Running) { // Put the thread to sleep until the server replies, it will be awoken in // svcReplyAndReceive for LLE servers. - thread->status = ThreadStatus::WaitIPC; + thread->SetStatus(ThreadStatus::WaitIPC); if (hle_handler != nullptr) { // 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) { return ERR_INVALID_HANDLE; } - *thread_id = thread->GetThreadId(); + *thread_id = thread->GetThreadID(); return RESULT_SUCCESS; } @@ -177,7 +177,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) { /// Default thread wakeup callback for WaitSynchronization static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, SharedPtr<WaitObject> object, std::size_t index) { - ASSERT(thread->status == ThreadStatus::WaitSynchAny); + ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); if (reason == ThreadWakeupReason::Timeout) { thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); @@ -204,10 +204,10 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 if (handle_count > MaxHandles) return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge); - auto thread = GetCurrentThread(); + auto* const thread = GetCurrentThread(); - using ObjectPtr = SharedPtr<WaitObject>; - std::vector<ObjectPtr> objects(handle_count); + using ObjectPtr = Thread::ThreadWaitObjects::value_type; + Thread::ThreadWaitObjects objects(handle_count); auto& kernel = Core::System::GetInstance().Kernel(); for (u64 i = 0; i < handle_count; ++i) { @@ -244,14 +244,14 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64 for (auto& object : objects) object->AddWaitingThread(thread); - thread->wait_objects = std::move(objects); - thread->status = ThreadStatus::WaitSynchAny; + thread->SetWaitObjects(std::move(objects)); + thread->SetStatus(ThreadStatus::WaitSynchAny); // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); - thread->wakeup_callback = DefaultThreadWakeupCallback; + thread->SetWakeupCallback(DefaultThreadWakeupCallback); - Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); + Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); return RESULT_TIMEOUT; } @@ -266,7 +266,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) { return ERR_INVALID_HANDLE; } - ASSERT(thread->status == ThreadStatus::WaitSynchAny); + ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny); thread->SetWaitSynchronizationResult( ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled)); thread->ResumeFromWait(); @@ -425,7 +425,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { } const auto current_process = Core::CurrentProcess(); - if (thread->owner_process != current_process) { + if (thread->GetOwnerProcess() != current_process) { return ERR_INVALID_HANDLE; } @@ -433,7 +433,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) { return ERR_ALREADY_REGISTERED; } - Core::ARM_Interface::ThreadContext ctx = thread->context; + Core::ARM_Interface::ThreadContext ctx = thread->GetContext(); // Mask away mode bits, interrupt bits, IL bit, and other reserved bits. ctx.pstate &= 0xFF0FFE20; @@ -479,14 +479,14 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { thread->SetPriority(priority); - Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); + Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); return RESULT_SUCCESS; } /// Get which CPU core is executing the current thread static u32 GetCurrentProcessorNumber() { LOG_TRACE(Kernel_SVC, "called"); - return GetCurrentThread()->processor_id; + return GetCurrentThread()->GetProcessorID(); } 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 CASCADE_RESULT(SharedPtr<Thread> thread, Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top, Core::CurrentProcess())); - CASCADE_RESULT(thread->guest_handle, kernel.HandleTable().Create(thread)); - *out_handle = thread->guest_handle; + const auto new_guest_handle = kernel.HandleTable().Create(thread); + if (new_guest_handle.Failed()) { + return new_guest_handle.Code(); + } + thread->SetGuestHandle(*new_guest_handle); + *out_handle = *new_guest_handle; - Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); + Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); LOG_TRACE(Kernel_SVC, "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, " @@ -645,10 +649,10 @@ static ResultCode StartThread(Handle thread_handle) { return ERR_INVALID_HANDLE; } - ASSERT(thread->status == ThreadStatus::Dormant); + ASSERT(thread->GetStatus() == ThreadStatus::Dormant); thread->ResumeFromWait(); - Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); + Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); return RESULT_SUCCESS; } @@ -694,17 +698,17 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var CASCADE_CODE(Mutex::Release(mutex_addr)); SharedPtr<Thread> current_thread = GetCurrentThread(); - current_thread->condvar_wait_address = condition_variable_addr; - current_thread->mutex_wait_address = mutex_addr; - current_thread->wait_handle = thread_handle; - current_thread->status = ThreadStatus::WaitMutex; - current_thread->wakeup_callback = nullptr; + current_thread->SetCondVarWaitAddress(condition_variable_addr); + current_thread->SetMutexWaitAddress(mutex_addr); + current_thread->SetWaitHandle(thread_handle); + current_thread->SetStatus(ThreadStatus::WaitMutex); + current_thread->InvalidateWakeupCallback(); current_thread->WakeAfterDelay(nano_seconds); // Note: Deliberately don't attempt to inherit the lock owner's priority. - Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule(); + Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule(); return RESULT_SUCCESS; } @@ -713,14 +717,14 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}", condition_variable_addr, target); - auto RetrieveWaitingThreads = [](std::size_t core_index, - std::vector<SharedPtr<Thread>>& waiting_threads, - VAddr condvar_addr) { + const auto RetrieveWaitingThreads = [](std::size_t core_index, + std::vector<SharedPtr<Thread>>& waiting_threads, + VAddr condvar_addr) { const auto& scheduler = Core::System::GetInstance().Scheduler(core_index); - auto& thread_list = scheduler->GetThreadList(); + const auto& thread_list = scheduler->GetThreadList(); - for (auto& thread : thread_list) { - if (thread->condvar_wait_address == condvar_addr) + for (const auto& thread : thread_list) { + if (thread->GetCondVarWaitAddress() == condvar_addr) waiting_threads.push_back(thread); } }; @@ -734,7 +738,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target // Sort them by priority, such that the highest priority ones come first. std::sort(waiting_threads.begin(), waiting_threads.end(), [](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) { - return lhs->current_priority < rhs->current_priority; + return lhs->GetPriority() < rhs->GetPriority(); }); // 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 for (std::size_t index = 0; index < last; ++index) { auto& thread = waiting_threads[index]; - ASSERT(thread->condvar_wait_address == condition_variable_addr); + ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr); std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex(); @@ -759,42 +763,43 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target // Atomically read the value of the mutex. u32 mutex_val = 0; do { - monitor.SetExclusive(current_core, thread->mutex_wait_address); + monitor.SetExclusive(current_core, thread->GetMutexWaitAddress()); // If the mutex is not yet acquired, acquire it. - mutex_val = Memory::Read32(thread->mutex_wait_address); + mutex_val = Memory::Read32(thread->GetMutexWaitAddress()); if (mutex_val != 0) { monitor.ClearExclusive(); break; } - } while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address, - thread->wait_handle)); + } while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(), + thread->GetWaitHandle())); if (mutex_val == 0) { // We were able to acquire the mutex, resume this thread. - ASSERT(thread->status == ThreadStatus::WaitMutex); + ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); thread->ResumeFromWait(); - auto lock_owner = thread->lock_owner; - if (lock_owner) + auto* const lock_owner = thread->GetLockOwner(); + if (lock_owner != nullptr) { lock_owner->RemoveMutexWaiter(thread); + } - thread->lock_owner = nullptr; - thread->mutex_wait_address = 0; - thread->condvar_wait_address = 0; - thread->wait_handle = 0; + thread->SetLockOwner(nullptr); + thread->SetMutexWaitAddress(0); + thread->SetCondVarWaitAddress(0); + thread->SetWaitHandle(0); } else { // Atomically signal that the mutex now has a waiting thread. do { - monitor.SetExclusive(current_core, thread->mutex_wait_address); + monitor.SetExclusive(current_core, thread->GetMutexWaitAddress()); // Ensure that the mutex value is still what we expect. - u32 value = Memory::Read32(thread->mutex_wait_address); + u32 value = Memory::Read32(thread->GetMutexWaitAddress()); // TODO(Subv): When this happens, the kernel just clears the exclusive state and // retries the initial read for this thread. ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case"); - } while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address, + } while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(), mutex_val | Mutex::MutexHasWaitersFlag)); // 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 Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask); auto owner = kernel.HandleTable().Get<Thread>(owner_handle); ASSERT(owner); - ASSERT(thread->status == ThreadStatus::WaitMutex); - thread->wakeup_callback = nullptr; + ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex); + thread->InvalidateWakeupCallback(); owner->AddMutexWaiter(thread); - Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule(); + Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule(); } } @@ -913,8 +918,8 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask) return ERR_INVALID_HANDLE; } - *core = thread->ideal_core; - *mask = thread->affinity_mask; + *core = thread->GetIdealCore(); + *mask = thread->GetAffinityMask(); return RESULT_SUCCESS; } @@ -930,11 +935,13 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { } if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) { - ASSERT(thread->owner_process->GetDefaultProcessorID() != - static_cast<u8>(THREADPROCESSORID_DEFAULT)); + const u8 default_processor_id = thread->GetOwnerProcess()->GetDefaultProcessorID(); + + ASSERT(default_processor_id != static_cast<u8>(THREADPROCESSORID_DEFAULT)); + // Set the target CPU to the one specified in the process' exheader. - core = thread->owner_process->GetDefaultProcessorID(); - mask = 1ull << core; + core = default_processor_id; + mask = 1ULL << core; } if (mask == 0) { @@ -945,7 +952,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) { static constexpr u32 OnlyChangeMask = static_cast<u32>(-3); if (core == OnlyChangeMask) { - core = thread->ideal_core; + core = thread->GetIdealCore(); } else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) { return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId); } 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() { void WaitCurrentThread_Sleep() { Thread* thread = GetCurrentThread(); - thread->status = ThreadStatus::WaitSleep; + thread->SetStatus(ThreadStatus::WaitSleep); } void ExitCurrentThread() { @@ -169,7 +169,7 @@ void Thread::ResumeFromWait() { next_scheduler->ScheduleThread(this, current_priority); // Change thread's scheduler - scheduler = next_scheduler; + scheduler = next_scheduler.get(); Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); } @@ -233,7 +233,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name thread->name = std::move(name); thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap(); thread->owner_process = owner_process; - thread->scheduler = Core::System::GetInstance().Scheduler(processor_id); + thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get(); thread->scheduler->AddThread(thread, priority); thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread); @@ -269,9 +269,9 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); // Register 1 must be a handle to the main thread - thread->guest_handle = kernel.HandleTable().Create(thread).Unwrap(); - - thread->context.cpu_registers[1] = thread->guest_handle; + const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap(); + thread->SetGuestHandle(guest_handle); + thread->GetContext().cpu_registers[1] = guest_handle; // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires thread->ResumeFromWait(); @@ -299,6 +299,18 @@ VAddr Thread::GetCommandBufferAddress() const { return GetTLSAddress() + CommandHeaderOffset; } +void Thread::SetStatus(ThreadStatus new_status) { + if (new_status == status) { + return; + } + + if (status == ThreadStatus::Running) { + last_running_ticks = CoreTiming::GetTicks(); + } + + status = new_status; +} + void Thread::AddMutexWaiter(SharedPtr<Thread> thread) { if (thread->lock_owner == this) { // 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) { next_scheduler->ScheduleThread(this, current_priority); // Change thread's scheduler - scheduler = next_scheduler; + scheduler = next_scheduler.get(); Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule(); } +bool Thread::AllWaitObjectsReady() { + return std::none_of( + wait_objects.begin(), wait_objects.end(), + [this](const SharedPtr<WaitObject>& object) { return object->ShouldWait(this); }); +} + +bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, + SharedPtr<WaitObject> object, std::size_t index) { + ASSERT(wakeup_callback); + return wakeup_callback(reason, std::move(thread), std::move(object), index); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// /** 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: using TLSMemory = std::vector<u8>; using TLSMemoryPtr = std::shared_ptr<TLSMemory>; + using MutexWaitingThreads = std::vector<SharedPtr<Thread>>; + + using ThreadContext = Core::ARM_Interface::ThreadContext; + + using ThreadWaitObjects = std::vector<SharedPtr<WaitObject>>; + + using WakeupCallback = std::function<bool(ThreadWakeupReason reason, SharedPtr<Thread> thread, + SharedPtr<WaitObject> object, std::size_t index)>; + /** * Creates and returns a new thread. The new thread is immediately scheduled * @param kernel The kernel instance this thread will be created under. @@ -106,6 +115,14 @@ public: } /** + * Gets the thread's nominal priority. + * @return The current thread's nominal priority. + */ + u32 GetNominalPriority() const { + return nominal_priority; + } + + /** * Sets the thread's current priority * @param priority The new priority */ @@ -133,7 +150,7 @@ public: * Gets the thread's thread ID * @return The thread's ID */ - u32 GetThreadId() const { + u32 GetThreadID() const { return thread_id; } @@ -203,6 +220,11 @@ public: return tpidr_el0; } + /// Sets the value of the TPIDR_EL0 Read/Write system register for this thread. + void SetTPIDR_EL0(u64 value) { + tpidr_el0 = value; + } + /* * Returns the address of the current thread's command buffer, located in the TLS. * @returns VAddr of the thread's command buffer. @@ -218,69 +240,193 @@ public: return status == ThreadStatus::WaitSynchAll; } - Core::ARM_Interface::ThreadContext context; + ThreadContext& GetContext() { + return context; + } + + const ThreadContext& GetContext() const { + return context; + } + + ThreadStatus GetStatus() const { + return status; + } + + void SetStatus(ThreadStatus new_status); + + u64 GetLastRunningTicks() const { + return last_running_ticks; + } + + s32 GetProcessorID() const { + return processor_id; + } + + SharedPtr<Process>& GetOwnerProcess() { + return owner_process; + } + + const SharedPtr<Process>& GetOwnerProcess() const { + return owner_process; + } + + const ThreadWaitObjects& GetWaitObjects() const { + return wait_objects; + } + + void SetWaitObjects(ThreadWaitObjects objects) { + wait_objects = std::move(objects); + } + + void ClearWaitObjects() { + wait_objects.clear(); + } + + /// Determines whether all the objects this thread is waiting on are ready. + bool AllWaitObjectsReady(); + + const MutexWaitingThreads& GetMutexWaitingThreads() const { + return wait_mutex_threads; + } + + Thread* GetLockOwner() const { + return lock_owner.get(); + } + + void SetLockOwner(SharedPtr<Thread> owner) { + lock_owner = std::move(owner); + } + + VAddr GetCondVarWaitAddress() const { + return condvar_wait_address; + } + + void SetCondVarWaitAddress(VAddr address) { + condvar_wait_address = address; + } + + VAddr GetMutexWaitAddress() const { + return mutex_wait_address; + } - u32 thread_id; + void SetMutexWaitAddress(VAddr address) { + mutex_wait_address = address; + } - ThreadStatus status; - VAddr entry_point; - VAddr stack_top; + Handle GetWaitHandle() const { + return wait_handle; + } - u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application - u32 current_priority; ///< Current thread priority, can be temporarily changed + void SetWaitHandle(Handle handle) { + wait_handle = handle; + } - u64 last_running_ticks; ///< CPU tick when thread was last running + VAddr GetArbiterWaitAddress() const { + return arb_wait_address; + } - s32 processor_id; + void SetArbiterWaitAddress(VAddr address) { + arb_wait_address = address; + } - VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread - u64 tpidr_el0; ///< TPIDR_EL0 read/write system register. + void SetGuestHandle(Handle handle) { + guest_handle = handle; + } - SharedPtr<Process> owner_process; ///< Process that owns this thread + bool HasWakeupCallback() const { + return wakeup_callback != nullptr; + } + + void SetWakeupCallback(WakeupCallback callback) { + wakeup_callback = std::move(callback); + } + + void InvalidateWakeupCallback() { + SetWakeupCallback(nullptr); + } + + /** + * Invokes the thread's wakeup callback. + * + * @pre A valid wakeup callback has been set. Violating this precondition + * will cause an assertion to trigger. + */ + bool InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread, + SharedPtr<WaitObject> object, std::size_t index); + + u32 GetIdealCore() const { + return ideal_core; + } + + u64 GetAffinityMask() const { + return affinity_mask; + } + +private: + explicit Thread(KernelCore& kernel); + ~Thread() override; + + Core::ARM_Interface::ThreadContext context{}; + + u32 thread_id = 0; + + ThreadStatus status = ThreadStatus::Dormant; + + VAddr entry_point = 0; + VAddr stack_top = 0; + + u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application + u32 current_priority = 0; ///< Current thread priority, can be temporarily changed + + u64 last_running_ticks = 0; ///< CPU tick when thread was last running + + s32 processor_id = 0; + + VAddr tls_address = 0; ///< Virtual address of the Thread Local Storage of the thread + u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register. + + /// Process that owns this thread + SharedPtr<Process> owner_process; /// Objects that the thread is waiting on, in the same order as they were - // passed to WaitSynchronization1/N. - std::vector<SharedPtr<WaitObject>> wait_objects; + /// passed to WaitSynchronization1/N. + ThreadWaitObjects wait_objects; /// List of threads that are waiting for a mutex that is held by this thread. - std::vector<SharedPtr<Thread>> wait_mutex_threads; + MutexWaitingThreads wait_mutex_threads; /// Thread that owns the lock that this thread is waiting for. SharedPtr<Thread> lock_owner; - // If waiting on a ConditionVariable, this is the ConditionVariable address - VAddr condvar_wait_address; - VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address - Handle wait_handle; ///< The handle used to wait for the mutex. + /// If waiting on a ConditionVariable, this is the ConditionVariable address + VAddr condvar_wait_address = 0; + /// If waiting on a Mutex, this is the mutex address + VAddr mutex_wait_address = 0; + /// The handle used to wait for the mutex. + Handle wait_handle = 0; - // If waiting for an AddressArbiter, this is the address being waited on. + /// If waiting for an AddressArbiter, this is the address being waited on. VAddr arb_wait_address{0}; - std::string name; - /// Handle used by guest emulated application to access this thread - Handle guest_handle; + Handle guest_handle = 0; /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. - Handle callback_handle; + Handle callback_handle = 0; - using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread, - SharedPtr<WaitObject> object, std::size_t index); - // Callback that will be invoked when the thread is resumed from a waiting state. If the thread - // was waiting via WaitSynchronizationN then the object will be the last object that became - // available. In case of a timeout, the object will be nullptr. - std::function<WakeupCallback> wakeup_callback; + /// Callback that will be invoked when the thread is resumed from a waiting state. If the thread + /// was waiting via WaitSynchronizationN then the object will be the last object that became + /// available. In case of a timeout, the object will be nullptr. + WakeupCallback wakeup_callback; - std::shared_ptr<Scheduler> scheduler; + Scheduler* scheduler = nullptr; u32 ideal_core{0xFFFFFFFF}; u64 affinity_mask{0x1}; -private: - explicit Thread(KernelCore& kernel); - ~Thread() override; - TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>(); + + std::string name; }; /** 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() { u32 candidate_priority = THREADPRIO_LOWEST + 1; for (const auto& thread : waiting_threads) { + const ThreadStatus thread_status = thread->GetStatus(); + // The list of waiting threads must not contain threads that are not waiting to be awakened. - ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny || - thread->status == ThreadStatus::WaitSynchAll || - thread->status == ThreadStatus::WaitHLEEvent, + ASSERT_MSG(thread_status == ThreadStatus::WaitSynchAny || + thread_status == ThreadStatus::WaitSynchAll || + thread_status == ThreadStatus::WaitHLEEvent, "Inconsistent thread statuses in waiting_threads"); - if (thread->current_priority >= candidate_priority) + if (thread->GetPriority() >= candidate_priority) continue; if (ShouldWait(thread.get())) @@ -50,16 +52,13 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { // A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or // in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready. bool ready_to_run = true; - if (thread->status == ThreadStatus::WaitSynchAll) { - ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), - [&thread](const SharedPtr<WaitObject>& object) { - return object->ShouldWait(thread.get()); - }); + if (thread_status == ThreadStatus::WaitSynchAll) { + ready_to_run = thread->AllWaitObjectsReady(); } if (ready_to_run) { candidate = thread.get(); - candidate_priority = thread->current_priority; + candidate_priority = thread->GetPriority(); } } @@ -75,24 +74,24 @@ void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) { if (!thread->IsSleepingOnWaitAll()) { Acquire(thread.get()); } else { - for (auto& object : thread->wait_objects) { + for (const auto& object : thread->GetWaitObjects()) { ASSERT(!object->ShouldWait(thread.get())); object->Acquire(thread.get()); } } - std::size_t index = thread->GetWaitObjectIndex(this); + const std::size_t index = thread->GetWaitObjectIndex(this); - for (auto& object : thread->wait_objects) + for (const auto& object : thread->GetWaitObjects()) object->RemoveWaitingThread(thread.get()); - thread->wait_objects.clear(); + thread->ClearWaitObjects(); thread->CancelWakeupTimer(); bool resume = true; - if (thread->wakeup_callback) - resume = thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this, index); + if (thread->HasWakeupCallback()) + resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index); if (resume) 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) { out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); } - if (out.size() <= offset) { + if (out.size() < offset) { IPC::ResponseBuilder rb{ctx, 2}; // TODO(DarkLordZach): Find the correct error code. 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") { {80, nullptr, "OpenSaveDataMetaFile"}, {81, nullptr, "OpenSaveDataTransferManager"}, {82, nullptr, "OpenSaveDataTransferManagerVersion2"}, + {83, nullptr, "OpenSaveDataTransferProhibiterForCloudBackUp"}, {100, nullptr, "OpenImageDirectoryFileSystem"}, {110, nullptr, "OpenContentStorageFileSystem"}, {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, @@ -495,6 +496,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { {613, nullptr, "VerifySaveDataFileSystemBySaveDataSpaceId"}, {614, nullptr, "CorruptSaveDataFileSystemBySaveDataSpaceId"}, {615, nullptr, "QuerySaveDataInternalStorageTotalSize"}, + {616, nullptr, "GetSaveDataCommitId"}, {620, nullptr, "SetSdCardEncryptionSeed"}, {630, nullptr, "SetSdCardAccessibility"}, {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: {0, nullptr, "Unknown1"}, {1, nullptr, "Unknown2"}, {2, nullptr, "Unknown3"}, - {3, nullptr, "Unknown4"}, - {4, nullptr, "Unknown5"}, - {5, nullptr, "Unknown6"}, + {3, nullptr, "GetCurrentBacklightLevel"}, + {4, nullptr, "Unknown4"}, + {5, nullptr, "GetAlsComputedBacklightLevel"}, {6, nullptr, "TurnOffBacklight"}, {7, nullptr, "TurnOnBacklight"}, {8, nullptr, "GetBacklightStatus"}, - {9, nullptr, "Unknown7"}, - {10, nullptr, "Unknown8"}, - {11, nullptr, "Unknown9"}, - {12, nullptr, "Unknown10"}, - {13, nullptr, "Unknown11"}, - {14, nullptr, "Unknown12"}, - {15, nullptr, "Unknown13"}, + {9, nullptr, "Unknown5"}, + {10, nullptr, "Unknown6"}, + {11, nullptr, "Unknown7"}, + {12, nullptr, "Unknown8"}, + {13, nullptr, "Unknown9"}, + {14, nullptr, "Unknown10"}, + {15, nullptr, "GetAutoBrightnessSetting"}, {16, nullptr, "ReadRawLightSensor"}, - {17, nullptr, "Unknown14"}, - {18, nullptr, "Unknown15"}, - {19, nullptr, "Unknown16"}, - {20, nullptr, "Unknown17"}, - {21, nullptr, "Unknown18"}, - {22, nullptr, "Unknown19"}, - {23, nullptr, "Unknown20"}, - {24, nullptr, "Unknown21"}, - {25, nullptr, "Unknown22"}, + {17, nullptr, "Unknown11"}, + {18, nullptr, "Unknown12"}, + {19, nullptr, "Unknown13"}, + {20, nullptr, "Unknown14"}, + {21, nullptr, "Unknown15"}, + {22, nullptr, "Unknown16"}, + {23, nullptr, "Unknown17"}, + {24, nullptr, "Unknown18"}, + {25, nullptr, "Unknown19"}, {26, &LBL::EnableVrMode, "EnableVrMode"}, {27, &LBL::DisableVrMode, "DisableVrMode"}, {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) } process.LoadFromMetadata(metadata); + const FileSys::PatchManager pm(metadata.GetTitleID()); // Load NSO modules const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); @@ -139,7 +140,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) const FileSys::VirtualFile module_file = dir->GetFile(module); if (module_file != nullptr) { const VAddr load_addr = next_load_addr; - next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr); + next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr, pm); LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); // Register module with GDBStub 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 @@ #include "common/logging/log.h" #include "common/swap.h" #include "core/core.h" +#include "core/file_sys/patch_manager.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" @@ -36,8 +37,7 @@ struct NsoHeader { INSERT_PADDING_WORDS(1); u8 flags; std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order) - u32_le bss_size; - INSERT_PADDING_BYTES(0x1c); + std::array<u8, 0x20> build_id; std::array<u32_le, 3> segments_compressed_size; bool IsSegmentCompressed(size_t segment_num) const { @@ -93,7 +93,8 @@ static constexpr u32 PageAlignSize(u32 size) { return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; } -VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { +VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, + boost::optional<FileSys::PatchManager> pm) { if (file == nullptr) return {}; @@ -142,6 +143,17 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; program_image.resize(image_size); + // Apply patches if necessary + if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) { + std::vector<u8> pi_header(program_image.size() + 0x100); + std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); + std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); + + pi_header = pm->PatchNSO(pi_header); + + std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size()); + } + // Load codeset for current process codeset->name = file->GetName(); 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 @@ #pragma once #include "common/common_types.h" +#include "core/file_sys/patch_manager.h" #include "core/loader/linker.h" #include "core/loader/loader.h" @@ -26,7 +27,8 @@ public: return IdentifyType(file); } - static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base); + static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, + boost::optional<FileSys::PatchManager> pm = boost::none); ResultStatus Load(Kernel::Process& process) override; }; 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 renderer_base.h renderer_opengl/gl_buffer_cache.cpp renderer_opengl/gl_buffer_cache.h + renderer_opengl/gl_primitive_assembler.cpp + renderer_opengl/gl_primitive_assembler.h renderer_opengl/gl_rasterizer.cpp renderer_opengl/gl_rasterizer.h 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: return static_cast<GPUVAddr>((static_cast<GPUVAddr>(end_addr_high) << 32) | end_addr_low); } + + /// Adjust the index buffer offset so it points to the first desired index. + GPUVAddr IndexStart() const { + return StartAddress() + static_cast<size_t>(first) * + static_cast<size_t>(FormatSizeInBytes()); + } } index_array; 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 } AlignBuffer(alignment); - GLintptr uploaded_offset = buffer_offset; + const GLintptr uploaded_offset = buffer_offset; Memory::ReadBlock(*cpu_addr, buffer_ptr, size); @@ -57,13 +57,23 @@ GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t s std::size_t alignment) { AlignBuffer(alignment); std::memcpy(buffer_ptr, raw_pointer, size); - GLintptr uploaded_offset = buffer_offset; + const GLintptr uploaded_offset = buffer_offset; buffer_ptr += size; buffer_offset += size; return uploaded_offset; } +std::tuple<u8*, GLintptr> OGLBufferCache::ReserveMemory(std::size_t size, std::size_t alignment) { + AlignBuffer(alignment); + u8* const uploaded_ptr = buffer_ptr; + const GLintptr uploaded_offset = buffer_offset; + + buffer_ptr += size; + buffer_offset += size; + return std::make_tuple(uploaded_ptr, uploaded_offset); +} + void OGLBufferCache::Map(std::size_t max_size) { bool invalidate; std::tie(buffer_ptr, buffer_offset_base, invalidate) = @@ -74,6 +84,7 @@ void OGLBufferCache::Map(std::size_t max_size) { InvalidateAll(); } } + void OGLBufferCache::Unmap() { stream_buffer.Unmap(buffer_offset - buffer_offset_base); } @@ -84,7 +95,7 @@ GLuint OGLBufferCache::GetHandle() const { void OGLBufferCache::AlignBuffer(std::size_t alignment) { // Align the offset, not the mapped pointer - GLintptr offset_aligned = + const GLintptr offset_aligned = static_cast<GLintptr>(Common::AlignUp(static_cast<std::size_t>(buffer_offset), alignment)); buffer_ptr += offset_aligned - buffer_offset; 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 @@ #include <cstddef> #include <memory> +#include <tuple> #include "common/common_types.h" #include "video_core/rasterizer_cache.h" @@ -33,11 +34,17 @@ class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBuffer public: explicit OGLBufferCache(std::size_t size); + /// Uploads data from a guest GPU address. Returns host's buffer offset where it's been + /// allocated. GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4, bool cache = true); + /// Uploads from a host memory. Returns host's buffer offset where it's been allocated. GLintptr UploadHostMemory(const void* raw_pointer, std::size_t size, std::size_t alignment = 4); + /// Reserves memory to be used by host's CPU. Returns mapped address and offset. + std::tuple<u8*, GLintptr> ReserveMemory(std::size_t size, std::size_t alignment = 4); + void Map(std::size_t max_size); void Unmap(); 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 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <array> +#include "common/assert.h" +#include "common/common_types.h" +#include "core/memory.h" +#include "video_core/renderer_opengl/gl_buffer_cache.h" +#include "video_core/renderer_opengl/gl_primitive_assembler.h" + +namespace OpenGL { + +constexpr u32 TRIANGLES_PER_QUAD = 6; +constexpr std::array<u32, TRIANGLES_PER_QUAD> QUAD_MAP = {0, 1, 2, 0, 2, 3}; + +PrimitiveAssembler::PrimitiveAssembler(OGLBufferCache& buffer_cache) : buffer_cache(buffer_cache) {} + +PrimitiveAssembler::~PrimitiveAssembler() = default; + +std::size_t PrimitiveAssembler::CalculateQuadSize(u32 count) const { + ASSERT_MSG(count % 4 == 0, "Quad count is expected to be a multiple of 4"); + return (count / 4) * TRIANGLES_PER_QUAD * sizeof(GLuint); +} + +GLintptr PrimitiveAssembler::MakeQuadArray(u32 first, u32 count) { + const std::size_t size{CalculateQuadSize(count)}; + auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(size); + + for (u32 primitive = 0; primitive < count / 4; ++primitive) { + for (u32 i = 0; i < TRIANGLES_PER_QUAD; ++i) { + const u32 index = first + primitive * 4 + QUAD_MAP[i]; + std::memcpy(dst_pointer, &index, sizeof(index)); + dst_pointer += sizeof(index); + } + } + + return index_offset; +} + +GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size, + u32 count) { + const std::size_t map_size{CalculateQuadSize(count)}; + auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size); + + auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager(); + const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; + const u8* source{Memory::GetPointer(*cpu_addr)}; + + for (u32 primitive = 0; primitive < count / 4; ++primitive) { + for (std::size_t i = 0; i < TRIANGLES_PER_QUAD; ++i) { + const u32 index = primitive * 4 + QUAD_MAP[i]; + const u8* src_offset = source + (index * index_size); + + std::memcpy(dst_pointer, src_offset, index_size); + dst_pointer += index_size; + } + } + + return index_offset; +} + +} // 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 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include <glad/glad.h> + +#include "common/common_types.h" +#include "video_core/memory_manager.h" + +namespace OpenGL { + +class OGLBufferCache; + +class PrimitiveAssembler { +public: + explicit PrimitiveAssembler(OGLBufferCache& buffer_cache); + ~PrimitiveAssembler(); + + /// Calculates the size required by MakeQuadArray and MakeQuadIndexed. + std::size_t CalculateQuadSize(u32 count) const; + + GLintptr MakeQuadArray(u32 first, u32 count); + + GLintptr MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size, u32 count); + +private: + OGLBufferCache& buffer_cache; +}; + +} // 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 MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192)); MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); +MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100)); + +struct DrawParameters { + GLenum primitive_mode; + GLsizei count; + GLint current_instance; + bool use_indexed; + + GLint vertex_first; + + GLenum index_format; + GLint base_vertex; + GLintptr index_buffer_offset; + + void DispatchDraw() const { + if (use_indexed) { + const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset); + if (current_instance > 0) { + glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format, + index_buffer_ptr, 1, base_vertex, + current_instance); + } else { + glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr, + base_vertex); + } + } else { + if (current_instance > 0) { + glDrawArraysInstancedBaseInstance(primitive_mode, vertex_first, count, 1, + current_instance); + } else { + glDrawArrays(primitive_mode, vertex_first, count); + } + } + } +}; RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info) : emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) { @@ -172,6 +207,53 @@ void RasterizerOpenGL::SetupVertexArrays() { } } +DrawParameters RasterizerOpenGL::SetupDraw() { + const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); + const auto& regs = gpu.regs; + const bool is_indexed = accelerate_draw == AccelDraw::Indexed; + + DrawParameters params{}; + params.current_instance = gpu.state.current_instance; + + if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) { + MICROPROFILE_SCOPE(OpenGL_PrimitiveAssembly); + + params.use_indexed = true; + params.primitive_mode = GL_TRIANGLES; + + if (is_indexed) { + params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); + params.count = (regs.index_array.count / 4) * 6; + params.index_buffer_offset = primitive_assembler.MakeQuadIndexed( + regs.index_array.IndexStart(), regs.index_array.FormatSizeInBytes(), + regs.index_array.count); + params.base_vertex = static_cast<GLint>(regs.vb_element_base); + } else { + // MakeQuadArray always generates u32 indexes + params.index_format = GL_UNSIGNED_INT; + params.count = (regs.vertex_buffer.count / 4) * 6; + params.index_buffer_offset = + primitive_assembler.MakeQuadArray(regs.vertex_buffer.first, params.count); + } + return params; + } + + params.use_indexed = is_indexed; + params.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology); + + if (is_indexed) { + MICROPROFILE_SCOPE(OpenGL_Index); + params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format); + params.count = regs.index_array.count; + params.index_buffer_offset = + buffer_cache.UploadMemory(regs.index_array.IndexStart(), CalculateIndexBufferSize()); + params.base_vertex = static_cast<GLint>(regs.vb_element_base); + } else { + params.count = regs.vertex_buffer.count; + params.vertex_first = regs.vertex_buffer.first; + } +} + void RasterizerOpenGL::SetupShaders() { MICROPROFILE_SCOPE(OpenGL_Shader); const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); @@ -256,6 +338,13 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const { return size; } +std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const { + const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; + + return static_cast<std::size_t>(regs.index_array.count) * + static_cast<std::size_t>(regs.index_array.FormatSizeInBytes()); +} + bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays; DrawArrays(); @@ -459,16 +548,23 @@ void RasterizerOpenGL::DrawArrays() { // Draw the vertex batch const bool is_indexed = accelerate_draw == AccelDraw::Indexed; - const u64 index_buffer_size{static_cast<u64>(regs.index_array.count) * - static_cast<u64>(regs.index_array.FormatSizeInBytes())}; state.draw.vertex_buffer = buffer_cache.GetHandle(); state.Apply(); std::size_t buffer_size = CalculateVertexArraysSize(); - if (is_indexed) { - buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + index_buffer_size; + // Add space for index buffer (keeping in mind non-core primitives) + switch (regs.draw.topology) { + case Maxwell::PrimitiveTopology::Quads: + buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + + primitive_assembler.CalculateQuadSize(regs.vertex_buffer.count); + break; + default: + if (is_indexed) { + buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + CalculateIndexBufferSize(); + } + break; } // Uniform space for the 5 shader stages @@ -482,20 +578,7 @@ void RasterizerOpenGL::DrawArrays() { buffer_cache.Map(buffer_size); SetupVertexArrays(); - - // If indexed mode, copy the index buffer - GLintptr index_buffer_offset = 0; - if (is_indexed) { - MICROPROFILE_SCOPE(OpenGL_Index); - - // Adjust the index buffer offset so it points to the first desired index. - auto index_start = regs.index_array.StartAddress(); - index_start += static_cast<size_t>(regs.index_array.first) * - static_cast<size_t>(regs.index_array.FormatSizeInBytes()); - - index_buffer_offset = buffer_cache.UploadMemory(index_start, index_buffer_size); - } - + DrawParameters params = SetupDraw(); SetupShaders(); buffer_cache.Unmap(); @@ -503,31 +586,8 @@ void RasterizerOpenGL::DrawArrays() { shader_program_manager->ApplyTo(state); state.Apply(); - const GLenum primitive_mode{MaxwellToGL::PrimitiveTopology(regs.draw.topology)}; - if (is_indexed) { - const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)}; - - if (gpu.state.current_instance > 0) { - glDrawElementsInstancedBaseVertexBaseInstance( - primitive_mode, regs.index_array.count, - MaxwellToGL::IndexFormat(regs.index_array.format), - reinterpret_cast<const void*>(index_buffer_offset), 1, base_vertex, - gpu.state.current_instance); - } else { - glDrawElementsBaseVertex(primitive_mode, regs.index_array.count, - MaxwellToGL::IndexFormat(regs.index_array.format), - reinterpret_cast<const void*>(index_buffer_offset), - base_vertex); - } - } else { - if (gpu.state.current_instance > 0) { - glDrawArraysInstancedBaseInstance(primitive_mode, regs.vertex_buffer.first, - regs.vertex_buffer.count, 1, - gpu.state.current_instance); - } else { - glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count); - } - } + // Execute draw call + params.DispatchDraw(); // Disable scissor test 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 @@ #include "video_core/rasterizer_cache.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_opengl/gl_buffer_cache.h" +#include "video_core/renderer_opengl/gl_primitive_assembler.h" #include "video_core/renderer_opengl/gl_rasterizer_cache.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_shader_cache.h" @@ -38,6 +39,7 @@ class EmuWindow; namespace OpenGL { struct ScreenInfo; +struct DrawParameters; class RasterizerOpenGL : public VideoCore::RasterizerInterface { public: @@ -192,12 +194,17 @@ private: static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; OGLBufferCache buffer_cache; OGLFramebuffer framebuffer; + PrimitiveAssembler primitive_assembler{buffer_cache}; GLint uniform_buffer_alignment; std::size_t CalculateVertexArraysSize() const; + std::size_t CalculateIndexBufferSize() const; + void SetupVertexArrays(); + DrawParameters SetupDraw(); + void SetupShaders(); 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) ui->output_sink_combo_box->addItem(sink_detail.id); } - connect(ui->volume_slider, &QSlider::valueChanged, [this] { - ui->volume_indicator->setText(tr("%1 %").arg(ui->volume_slider->sliderPosition())); - }); + connect(ui->volume_slider, &QSlider::valueChanged, this, + &ConfigureAudio::setVolumeIndicatorText); this->setConfiguration(); connect(ui->output_sink_combo_box, @@ -37,32 +36,48 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) ConfigureAudio::~ConfigureAudio() = default; void ConfigureAudio::setConfiguration() { + setOutputSinkFromSinkID(); + + // The device list cannot be pre-populated (nor listed) until the output sink is known. + updateAudioDevices(ui->output_sink_combo_box->currentIndex()); + + setAudioDeviceFromDeviceID(); + + ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); + ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum()); + setVolumeIndicatorText(ui->volume_slider->sliderPosition()); +} + +void ConfigureAudio::setOutputSinkFromSinkID() { int new_sink_index = 0; + + const QString sink_id = QString::fromStdString(Settings::values.sink_id); for (int index = 0; index < ui->output_sink_combo_box->count(); index++) { - if (ui->output_sink_combo_box->itemText(index).toStdString() == Settings::values.sink_id) { + if (ui->output_sink_combo_box->itemText(index) == sink_id) { new_sink_index = index; break; } } - ui->output_sink_combo_box->setCurrentIndex(new_sink_index); - ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching); - - // The device list cannot be pre-populated (nor listed) until the output sink is known. - updateAudioDevices(new_sink_index); + ui->output_sink_combo_box->setCurrentIndex(new_sink_index); +} +void ConfigureAudio::setAudioDeviceFromDeviceID() { int new_device_index = -1; + + const QString device_id = QString::fromStdString(Settings::values.audio_device_id); for (int index = 0; index < ui->audio_device_combo_box->count(); index++) { - if (ui->audio_device_combo_box->itemText(index).toStdString() == - Settings::values.audio_device_id) { + if (ui->audio_device_combo_box->itemText(index) == device_id) { new_device_index = index; break; } } + ui->audio_device_combo_box->setCurrentIndex(new_device_index); +} - ui->volume_slider->setValue(Settings::values.volume * ui->volume_slider->maximum()); - ui->volume_indicator->setText(tr("%1 %").arg(ui->volume_slider->sliderPosition())); +void ConfigureAudio::setVolumeIndicatorText(int percentage) { + ui->volume_indicator->setText(tr("%1%", "Volume percentage (e.g. 50%)").arg(percentage)); } void ConfigureAudio::applyConfiguration() { @@ -81,10 +96,10 @@ void ConfigureAudio::updateAudioDevices(int sink_index) { ui->audio_device_combo_box->clear(); ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); - std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); - std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices(); + const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); + const std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices(); for (const auto& device : device_list) { - ui->audio_device_combo_box->addItem(device.c_str()); + ui->audio_device_combo_box->addItem(QString::fromStdString(device)); } } 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: private: void setConfiguration(); + void setOutputSinkFromSinkID(); + void setAudioDeviceFromDeviceID(); + void setVolumeIndicatorText(int percentage); std::unique_ptr<Ui::ConfigureAudio> ui; }; 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) ui->setupUi(this); - for (auto theme : UISettings::themes) { + for (const auto& theme : UISettings::themes) { ui->theme_combobox->addItem(theme.first, theme.second); } 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 @@ #include "ui_configure_graphics.h" #include "yuzu/configuration/configure_graphics.h" -ConfigureGraphics::ConfigureGraphics(QWidget* parent) - : QWidget(parent), ui(new Ui::ConfigureGraphics) { - - ui->setupUi(this); - this->setConfiguration(); - - ui->frame_limit->setEnabled(Settings::values.use_frame_limit); - connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, - &QSpinBox::setEnabled); - connect(ui->bg_button, &QPushButton::clicked, this, [this] { - const QColor new_bg_color = QColorDialog::getColor(bg_color); - if (!new_bg_color.isValid()) - return; - bg_color = new_bg_color; - ui->bg_button->setStyleSheet( - QString("QPushButton { background-color: %1 }").arg(bg_color.name())); - }); -} - -ConfigureGraphics::~ConfigureGraphics() = default; - +namespace { enum class Resolution : int { Auto, Scale1x, @@ -67,6 +47,28 @@ Resolution FromResolutionFactor(float factor) { } return Resolution::Auto; } +} // Anonymous namespace + +ConfigureGraphics::ConfigureGraphics(QWidget* parent) + : QWidget(parent), ui(new Ui::ConfigureGraphics) { + + ui->setupUi(this); + this->setConfiguration(); + + ui->frame_limit->setEnabled(Settings::values.use_frame_limit); + connect(ui->toggle_frame_limit, &QCheckBox::stateChanged, ui->frame_limit, + &QSpinBox::setEnabled); + connect(ui->bg_button, &QPushButton::clicked, this, [this] { + const QColor new_bg_color = QColorDialog::getColor(bg_color); + if (!new_bg_color.isValid()) + return; + bg_color = new_bg_color; + ui->bg_button->setStyleSheet( + QString("QPushButton { background-color: %1 }").arg(bg_color.name())); + }); +} + +ConfigureGraphics::~ConfigureGraphics() = default; void ConfigureGraphics::setConfiguration() { 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) } } connect(analog_map_stick[analog_id], &QPushButton::released, [=]() { - QMessageBox::information( - this, "Information", - "After pressing OK, first move your joystick horizontally, and then vertically."); + QMessageBox::information(this, tr("Information"), + tr("After pressing OK, first move your joystick horizontally, " + "and then vertically.")); handleClick( analog_map_stick[analog_id], [=](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 std::vector<std::unique_ptr<WaitTreeItem>> list; constexpr std::size_t BaseRegister = 29; - u64 base_pointer = thread.context.cpu_registers[BaseRegister]; + u64 base_pointer = thread.GetContext().cpu_registers[BaseRegister]; while (base_pointer != 0) { u64 lr = Memory::Read64(base_pointer + sizeof(u64)); @@ -213,7 +213,7 @@ WaitTreeThread::~WaitTreeThread() = default; QString WaitTreeThread::GetText() const { const auto& thread = static_cast<const Kernel::Thread&>(object); QString status; - switch (thread.status) { + switch (thread.GetStatus()) { case Kernel::ThreadStatus::Running: status = tr("running"); break; @@ -246,15 +246,17 @@ QString WaitTreeThread::GetText() const { status = tr("dead"); break; } - QString pc_info = tr(" PC = 0x%1 LR = 0x%2") - .arg(thread.context.pc, 8, 16, QLatin1Char('0')) - .arg(thread.context.cpu_registers[30], 8, 16, QLatin1Char('0')); + + const auto& context = thread.GetContext(); + const QString pc_info = tr(" PC = 0x%1 LR = 0x%2") + .arg(context.pc, 8, 16, QLatin1Char('0')) + .arg(context.cpu_registers[30], 8, 16, QLatin1Char('0')); return WaitTreeWaitObject::GetText() + pc_info + " (" + status + ") "; } QColor WaitTreeThread::GetColor() const { const auto& thread = static_cast<const Kernel::Thread&>(object); - switch (thread.status) { + switch (thread.GetStatus()) { case Kernel::ThreadStatus::Running: return QColor(Qt::GlobalColor::darkGreen); case Kernel::ThreadStatus::Ready: @@ -284,7 +286,7 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { const auto& thread = static_cast<const Kernel::Thread&>(object); QString processor; - switch (thread.processor_id) { + switch (thread.GetProcessorID()) { case Kernel::ThreadProcessorId::THREADPROCESSORID_DEFAULT: processor = tr("default"); break; @@ -292,32 +294,35 @@ std::vector<std::unique_ptr<WaitTreeItem>> WaitTreeThread::GetChildren() const { case Kernel::ThreadProcessorId::THREADPROCESSORID_1: case Kernel::ThreadProcessorId::THREADPROCESSORID_2: case Kernel::ThreadProcessorId::THREADPROCESSORID_3: - processor = tr("core %1").arg(thread.processor_id); + processor = tr("core %1").arg(thread.GetProcessorID()); break; default: - processor = tr("Unknown processor %1").arg(thread.processor_id); + processor = tr("Unknown processor %1").arg(thread.GetProcessorID()); break; } list.push_back(std::make_unique<WaitTreeText>(tr("processor = %1").arg(processor))); - list.push_back(std::make_unique<WaitTreeText>(tr("ideal core = %1").arg(thread.ideal_core))); list.push_back( - std::make_unique<WaitTreeText>(tr("affinity mask = %1").arg(thread.affinity_mask))); - list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadId()))); + std::make_unique<WaitTreeText>(tr("ideal core = %1").arg(thread.GetIdealCore()))); + list.push_back( + std::make_unique<WaitTreeText>(tr("affinity mask = %1").arg(thread.GetAffinityMask()))); + list.push_back(std::make_unique<WaitTreeText>(tr("thread id = %1").arg(thread.GetThreadID()))); list.push_back(std::make_unique<WaitTreeText>(tr("priority = %1(current) / %2(normal)") - .arg(thread.current_priority) - .arg(thread.nominal_priority))); + .arg(thread.GetPriority()) + .arg(thread.GetNominalPriority()))); list.push_back(std::make_unique<WaitTreeText>( - tr("last running ticks = %1").arg(thread.last_running_ticks))); + tr("last running ticks = %1").arg(thread.GetLastRunningTicks()))); - if (thread.mutex_wait_address != 0) - list.push_back(std::make_unique<WaitTreeMutexInfo>(thread.mutex_wait_address)); - else + const VAddr mutex_wait_address = thread.GetMutexWaitAddress(); + if (mutex_wait_address != 0) { + list.push_back(std::make_unique<WaitTreeMutexInfo>(mutex_wait_address)); + } else { list.push_back(std::make_unique<WaitTreeText>(tr("not waiting for mutex"))); + } - if (thread.status == Kernel::ThreadStatus::WaitSynchAny || - thread.status == Kernel::ThreadStatus::WaitSynchAll) { - list.push_back(std::make_unique<WaitTreeObjectList>(thread.wait_objects, + if (thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAny || + thread.GetStatus() == Kernel::ThreadStatus::WaitSynchAll) { + list.push_back(std::make_unique<WaitTreeObjectList>(thread.GetWaitObjects(), thread.IsSleepingOnWaitAll())); } 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) { QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, bool updatable = true) { QString out; for (const auto& kv : patch_manager.GetPatchVersionNames()) { - if (!updatable && kv.first == FileSys::PatchType::Update) + if (!updatable && kv.first == "Update") continue; if (kv.second.empty()) { - out.append(fmt::format("{}\n", FileSys::FormatPatchTypeName(kv.first)).c_str()); + out.append(fmt::format("{}\n", kv.first).c_str()); } else { - out.append(fmt::format("{} ({})\n", FileSys::FormatPatchTypeName(kv.first), kv.second) - .c_str()); + out.append(fmt::format("{} ({})\n", kv.first, kv.second).c_str()); } } 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 @@ namespace UISettings { +const Themes themes{{ + {"Default", "default"}, + {"Dark", "qdarkstyle"}, +}}; + Values values = {}; -} + +} // 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 { using ContextualShortcut = std::pair<QString, int>; using Shortcut = std::pair<QString, ContextualShortcut>; -static const std::array<std::pair<QString, QString>, 2> themes = { - {std::make_pair(QString("Default"), QString("default")), - std::make_pair(QString("Dark"), QString("qdarkstyle"))}}; +using Themes = std::array<std::pair<const char*, const char*>, 2>; +extern const Themes themes; struct Values { QByteArray geometry; |