diff options
49 files changed, 1253 insertions, 639 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index cd990188e..92c3e855c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -344,14 +344,6 @@ ELSEIF (CMAKE_SYSTEM_NAME MATCHES "^(Linux|kFreeBSD|GNU|SunOS)$")      set(PLATFORM_LIBRARIES rt)  ENDIF (APPLE) -# MINGW: GCC does not support codecvt, so use iconv instead -if (UNIX OR MINGW) -    find_library(ICONV_LIBRARY NAMES iconv) -    if (ICONV_LIBRARY) -        list(APPEND PLATFORM_LIBRARIES ${ICONV_LIBRARY}) -    endif() -endif() -  # Setup a custom clang-format target (if clang-format can be found) that will run  # against all the src files. This should be used before making a pull request.  # ======================================================================= 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 23fd6e920..a98dbb9ab 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 4b3b5e665..539698f6e 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp @@ -2,22 +2,36 @@  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. +#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"  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{}; @@ -32,15 +46,6 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {      return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);  } -constexpr std::array<const char*, 2> PATCH_TYPE_NAMES{ -    "Update", -    "LayeredFS", -}; - -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; @@ -68,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) { @@ -135,31 +213,80 @@ 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      const auto update_tid = GetUpdateTitleID(title_id);      PatchManager update{update_tid};      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));              }          }      } -    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); +    std::vector<RegisteredCacheEntry> dlc_match; +    dlc_match.reserve(dlc_entries.size()); +    std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), +                 [this, &installed](const RegisteredCacheEntry& entry) { +                     return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id && +                            installed->GetEntry(entry)->GetStatus() == +                                Loader::ResultStatus::Success; +                 }); +    if (!dlc_match.empty()) { +        // Ensure sorted so DLC IDs show in order. +        std::sort(dlc_match.begin(), dlc_match.end()); + +        std::string list; +        for (size_t i = 0; i < dlc_match.size() - 1; ++i) +            list += fmt::format("{}, ", dlc_match[i].title_id & 0x7FF); + +        list += fmt::format("{}", dlc_match.back().title_id & 0x7FF); + +        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 464f17515..6a864ec43 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h @@ -24,13 +24,6 @@ enum class TitleVersionFormat : u8 {  std::string FormatTitleVersion(u32 version,                                 TitleVersionFormat format = TitleVersionFormat::ThreeElements); -enum class PatchType { -    Update, -    LayeredFS, -}; - -std::string FormatPatchTypeName(PatchType type); -  // A centralized class to manage patches to games.  class PatchManager {  public: @@ -41,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 @@ -48,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/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index d027a8d59..4994c2532 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp @@ -39,36 +39,35 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {  }  ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { +    std::shared_ptr<NCA> res; +      switch (storage) { -    case StorageId::NandSystem: { -        const auto res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); -        if (res == nullptr) { -            // TODO(DarkLordZach): Find the right error code to use here -            return ResultCode(-1); -        } -        const auto romfs = res->GetRomFS(); -        if (romfs == nullptr) { -            // TODO(DarkLordZach): Find the right error code to use here -            return ResultCode(-1); -        } -        return MakeResult<VirtualFile>(romfs); -    } -    case StorageId::NandUser: { -        const auto res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type); -        if (res == nullptr) { -            // TODO(DarkLordZach): Find the right error code to use here -            return ResultCode(-1); -        } -        const auto romfs = res->GetRomFS(); -        if (romfs == nullptr) { -            // TODO(DarkLordZach): Find the right error code to use here -            return ResultCode(-1); -        } -        return MakeResult<VirtualFile>(romfs); -    } +    case StorageId::None: +        res = Service::FileSystem::GetUnionContents()->GetEntry(title_id, type); +        break; +    case StorageId::NandSystem: +        res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); +        break; +    case StorageId::NandUser: +        res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type); +        break; +    case StorageId::SdCard: +        res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type); +        break;      default:          UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));      } + +    if (res == nullptr) { +        // TODO(DarkLordZach): Find the right error code to use here +        return ResultCode(-1); +    } +    const auto romfs = res->GetRomFS(); +    if (romfs == nullptr) { +        // TODO(DarkLordZach): Find the right error code to use here +        return ResultCode(-1); +    } +    return MakeResult<VirtualFile>(romfs);  }  } // namespace FileSys 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..78d8b74bb 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 d9eeac9ec..79580bcd9 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp @@ -2,22 +2,57 @@  // Licensed under GPLv2 or any later version  // Refer to the license.txt file included. +#include <algorithm> +#include <numeric> +#include <vector>  #include "common/logging/log.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/partition_filesystem.h" +#include "core/file_sys/registered_cache.h"  #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/process.h"  #include "core/hle/service/aoc/aoc_u.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/loader/loader.h"  namespace Service::AOC { -AOC_U::AOC_U() : ServiceFramework("aoc:u") { +constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; +constexpr u64 DLC_BASE_TO_AOC_ID_MASK = 0x1000; + +static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { +    return (aoc & DLC_BASE_TITLE_ID_MASK) == base; +} + +static std::vector<u64> AccumulateAOCTitleIDs() { +    std::vector<u64> add_on_content; +    const auto rcu = FileSystem::GetUnionContents(); +    const auto list = +        rcu->ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); +    std::transform(list.begin(), list.end(), std::back_inserter(add_on_content), +                   [](const FileSys::RegisteredCacheEntry& rce) { return rce.title_id; }); +    add_on_content.erase( +        std::remove_if( +            add_on_content.begin(), add_on_content.end(), +            [&rcu](u64 tid) { +                return rcu->GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() != +                       Loader::ResultStatus::Success; +            }), +        add_on_content.end()); +    return add_on_content; +} + +AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs()) {      static const FunctionInfo functions[] = {          {0, nullptr, "CountAddOnContentByApplicationId"},          {1, nullptr, "ListAddOnContentByApplicationId"},          {2, &AOC_U::CountAddOnContent, "CountAddOnContent"},          {3, &AOC_U::ListAddOnContent, "ListAddOnContent"},          {4, nullptr, "GetAddOnContentBaseIdByApplicationId"}, -        {5, nullptr, "GetAddOnContentBaseId"}, +        {5, &AOC_U::GetAddOnContentBaseId, "GetAddOnContentBaseId"},          {6, nullptr, "PrepareAddOnContentByApplicationId"}, -        {7, nullptr, "PrepareAddOnContent"}, +        {7, &AOC_U::PrepareAddOnContent, "PrepareAddOnContent"},          {8, nullptr, "GetAddOnContentListChangedEvent"},      };      RegisterHandlers(functions); @@ -28,15 +63,59 @@ AOC_U::~AOC_U() = default;  void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {      IPC::ResponseBuilder rb{ctx, 4};      rb.Push(RESULT_SUCCESS); -    rb.Push<u64>(0); -    LOG_WARNING(Service_AOC, "(STUBBED) called"); + +    const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); +    rb.Push<u32>(std::count_if(add_on_content.begin(), add_on_content.end(), [¤t](u64 tid) { +        return (tid & DLC_BASE_TITLE_ID_MASK) == current; +    }));  }  void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; + +    const auto offset = rp.PopRaw<u32>(); +    auto count = rp.PopRaw<u32>(); + +    const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); + +    std::vector<u32> out; +    for (size_t i = 0; i < add_on_content.size(); ++i) { +        if ((add_on_content[i] & DLC_BASE_TITLE_ID_MASK) == current) +            out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); +    } + +    if (out.size() < offset) { +        IPC::ResponseBuilder rb{ctx, 2}; +        // TODO(DarkLordZach): Find the correct error code. +        rb.Push(ResultCode(-1)); +        return; +    } + +    count = std::min<size_t>(out.size() - offset, count); +    std::rotate(out.begin(), out.begin() + offset, out.end()); +    out.resize(count); + +    ctx.WriteBuffer(out); + +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(RESULT_SUCCESS); +} + +void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {      IPC::ResponseBuilder rb{ctx, 4};      rb.Push(RESULT_SUCCESS); -    rb.Push<u64>(0); -    LOG_WARNING(Service_AOC, "(STUBBED) called"); +    rb.Push(Core::System::GetInstance().CurrentProcess()->GetTitleID() | DLC_BASE_TO_AOC_ID_MASK); +} + +void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { +    IPC::RequestParser rp{ctx}; + +    const auto aoc_id = rp.PopRaw<u32>(); + +    LOG_WARNING(Service_AOC, "(STUBBED) called with aoc_id={:08X}", aoc_id); + +    IPC::ResponseBuilder rb{ctx, 2}; +    rb.Push(RESULT_SUCCESS);  }  void InstallInterfaces(SM::ServiceManager& service_manager) { diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h index 29ce8f488..b3c7cab7a 100644 --- a/src/core/hle/service/aoc/aoc_u.h +++ b/src/core/hle/service/aoc/aoc_u.h @@ -16,6 +16,10 @@ public:  private:      void CountAddOnContent(Kernel::HLERequestContext& ctx);      void ListAddOnContent(Kernel::HLERequestContext& ctx); +    void GetAddOnContentBaseId(Kernel::HLERequestContext& ctx); +    void PrepareAddOnContent(Kernel::HLERequestContext& ctx); + +    std::vector<u64> add_on_content;  };  /// Registers all AOC services with the specified service manager. 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 14d82a7bc..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; @@ -909,7 +969,10 @@ void RasterizerOpenGL::SyncTransformFeedback() {  void RasterizerOpenGL::SyncPointState() {      const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; -    state.point.size = regs.point_size; +    // TODO(Rodrigo): Most games do not set a point size. I think this is a case of a +    // register carrying a default value. For now, if the point size is zero, assume it's +    // OpenGL's default (1). +    state.point.size = regs.point_size == 0 ? 1 : regs.point_size;  }  } // namespace OpenGL 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_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());          }      }  | 
