diff options
44 files changed, 1365 insertions, 472 deletions
diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss index f0908a7f1..12e681648 100644 --- a/dist/qt_themes/default/style.qss +++ b/dist/qt_themes/default/style.qss @@ -58,6 +58,19 @@ QPushButton#GPUStatusBarButton:!checked { color: #109010; } +QPushButton#DockingStatusBarButton { + min-width: 0px; + color: #000000; + border: 1px solid transparent; + background-color: transparent; + padding: 0px 3px 0px 3px; + text-align: center; +} + +QPushButton#DockingStatusBarButton:hover { + border: 1px solid #76797C; +} + QPushButton#buttonRefreshDevices { min-width: 21px; min-height: 21px; diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss index dac2dba86..63a636ae6 100644 --- a/dist/qt_themes/qdarkstyle/style.qss +++ b/dist/qt_themes/qdarkstyle/style.qss @@ -1304,6 +1304,19 @@ QPushButton#GPUStatusBarButton:!checked { color: #40dd40; } +QPushButton#DockingStatusBarButton { + min-width: 0px; + color: #ffffff; + border: 1px solid transparent; + background-color: transparent; + padding: 0px 3px 0px 3px; + text-align: center; +} + +QPushButton#DockingStatusBarButton:hover { + border: 1px solid #76797C; +} + QPushButton#buttonRefreshDevices { min-width: 23px; min-height: 23px; diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss index 032d05ec6..49b05c8ba 100644 --- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss +++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss @@ -2207,6 +2207,19 @@ QPushButton#GPUStatusBarButton:!checked { color: #40dd40; } +QPushButton#DockingStatusBarButton { + min-width: 0px; + color: #ffffff; + border: 1px solid transparent; + background-color: transparent; + padding: 0px 3px 0px 3px; + text-align: center; +} + +QPushButton#DockingStatusBarButton:hover { + border: 1px solid #76797C; +} + QPushButton#buttonRefreshDevices { min-width: 19px; min-height: 19px; diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index adf70eb8b..73bf626d4 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -58,6 +58,7 @@ add_library(common STATIC div_ceil.h dynamic_library.cpp dynamic_library.h + elf.h error.cpp error.h expected.h diff --git a/src/common/elf.h b/src/common/elf.h new file mode 100644 index 000000000..14a5e9597 --- /dev/null +++ b/src/common/elf.h @@ -0,0 +1,333 @@ +// SPDX-FileCopyrightText: 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <array> +#include <cstddef> + +#include "common_types.h" + +namespace Common { +namespace ELF { + +/* Type for a 16-bit quantity. */ +using Elf32_Half = u16; +using Elf64_Half = u16; + +/* Types for signed and unsigned 32-bit quantities. */ +using Elf32_Word = u32; +using Elf32_Sword = s32; +using Elf64_Word = u32; +using Elf64_Sword = s32; + +/* Types for signed and unsigned 64-bit quantities. */ +using Elf32_Xword = u64; +using Elf32_Sxword = s64; +using Elf64_Xword = u64; +using Elf64_Sxword = s64; + +/* Type of addresses. */ +using Elf32_Addr = u32; +using Elf64_Addr = u64; + +/* Type of file offsets. */ +using Elf32_Off = u32; +using Elf64_Off = u64; + +/* Type for section indices, which are 16-bit quantities. */ +using Elf32_Section = u16; +using Elf64_Section = u16; + +/* Type for version symbol information. */ +using Elf32_Versym = Elf32_Half; +using Elf64_Versym = Elf64_Half; + +constexpr size_t ElfIdentSize = 16; + +/* The ELF file header. This appears at the start of every ELF file. */ + +struct Elf32_Ehdr { + std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +}; + +struct Elf64_Ehdr { + std::array<u8, ElfIdentSize> e_ident; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +}; + +constexpr u8 ElfClass32 = 1; /* 32-bit objects */ +constexpr u8 ElfClass64 = 2; /* 64-bit objects */ +constexpr u8 ElfData2Lsb = 1; /* 2's complement, little endian */ +constexpr u8 ElfVersionCurrent = 1; /* EV_CURRENT */ +constexpr u8 ElfOsAbiNone = 0; /* System V ABI */ + +constexpr u16 ElfTypeNone = 0; /* No file type */ +constexpr u16 ElfTypeRel = 0; /* Relocatable file */ +constexpr u16 ElfTypeExec = 0; /* Executable file */ +constexpr u16 ElfTypeDyn = 0; /* Shared object file */ + +constexpr u16 ElfMachineArm = 40; /* ARM */ +constexpr u16 ElfMachineAArch64 = 183; /* ARM AARCH64 */ + +constexpr std::array<u8, ElfIdentSize> Elf32Ident{ + 0x7f, 'E', 'L', 'F', ElfClass32, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone}; + +constexpr std::array<u8, ElfIdentSize> Elf64Ident{ + 0x7f, 'E', 'L', 'F', ElfClass64, ElfData2Lsb, ElfVersionCurrent, ElfOsAbiNone}; + +/* Section header. */ + +struct Elf32_Shdr { + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +}; + +struct Elf64_Shdr { + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +}; + +constexpr u32 ElfShnUndef = 0; /* Undefined section */ + +constexpr u32 ElfShtNull = 0; /* Section header table entry unused */ +constexpr u32 ElfShtProgBits = 1; /* Program data */ +constexpr u32 ElfShtSymtab = 2; /* Symbol table */ +constexpr u32 ElfShtStrtab = 3; /* String table */ +constexpr u32 ElfShtRela = 4; /* Relocation entries with addends */ +constexpr u32 ElfShtDynamic = 6; /* Dynamic linking information */ +constexpr u32 ElfShtNobits = 7; /* Program space with no data (bss) */ +constexpr u32 ElfShtRel = 9; /* Relocation entries, no addends */ +constexpr u32 ElfShtDynsym = 11; /* Dynamic linker symbol table */ + +/* Symbol table entry. */ + +struct Elf32_Sym { + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + u8 st_info; /* Symbol type and binding */ + u8 st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +}; + +struct Elf64_Sym { + Elf64_Word st_name; /* Symbol name (string tbl index) */ + u8 st_info; /* Symbol type and binding */ + u8 st_other; /* Symbol visibility */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +}; + +/* How to extract and insert information held in the st_info field. */ + +static inline u8 ElfStBind(u8 st_info) { + return st_info >> 4; +} +static inline u8 ElfStType(u8 st_info) { + return st_info & 0xf; +} +static inline u8 ElfStInfo(u8 st_bind, u8 st_type) { + return static_cast<u8>((st_bind << 4) + (st_type & 0xf)); +} + +constexpr u8 ElfBindLocal = 0; /* Local symbol */ +constexpr u8 ElfBindGlobal = 1; /* Global symbol */ +constexpr u8 ElfBindWeak = 2; /* Weak symbol */ + +constexpr u8 ElfTypeUnspec = 0; /* Symbol type is unspecified */ +constexpr u8 ElfTypeObject = 1; /* Symbol is a data object */ +constexpr u8 ElfTypeFunc = 2; /* Symbol is a code object */ + +static inline u8 ElfStVisibility(u8 st_other) { + return static_cast<u8>(st_other & 0x3); +} + +constexpr u8 ElfVisibilityDefault = 0; /* Default symbol visibility rules */ +constexpr u8 ElfVisibilityInternal = 1; /* Processor specific hidden class */ +constexpr u8 ElfVisibilityHidden = 2; /* Sym unavailable in other modules */ +constexpr u8 ElfVisibilityProtected = 3; /* Not preemptible, not exported */ + +/* Relocation table entry without addend (in section of type ShtRel). */ + +struct Elf32_Rel { + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +}; + +/* Relocation table entry with addend (in section of type ShtRela). */ + +struct Elf32_Rela { + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +}; + +struct Elf64_Rela { + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +}; + +/* How to extract and insert information held in the r_info field. */ + +static inline u32 Elf32RelSymIndex(Elf32_Word r_info) { + return r_info >> 8; +} +static inline u8 Elf32RelType(Elf32_Word r_info) { + return static_cast<u8>(r_info & 0xff); +} +static inline Elf32_Word Elf32RelInfo(u32 sym_index, u8 type) { + return (sym_index << 8) + type; +} +static inline u32 Elf64RelSymIndex(Elf64_Xword r_info) { + return static_cast<u32>(r_info >> 32); +} +static inline u32 Elf64RelType(Elf64_Xword r_info) { + return r_info & 0xffffffff; +} +static inline Elf64_Xword Elf64RelInfo(u32 sym_index, u32 type) { + return (static_cast<Elf64_Xword>(sym_index) << 32) + type; +} + +constexpr u32 ElfArmCopy = 20; /* Copy symbol at runtime */ +constexpr u32 ElfArmGlobDat = 21; /* Create GOT entry */ +constexpr u32 ElfArmJumpSlot = 22; /* Create PLT entry */ +constexpr u32 ElfArmRelative = 23; /* Adjust by program base */ + +constexpr u32 ElfAArch64Copy = 1024; /* Copy symbol at runtime */ +constexpr u32 ElfAArch64GlobDat = 1025; /* Create GOT entry */ +constexpr u32 ElfAArch64JumpSlot = 1026; /* Create PLT entry */ +constexpr u32 ElfAArch64Relative = 1027; /* Adjust by program base */ + +/* Program segment header. */ + +struct Elf32_Phdr { + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +}; + +struct Elf64_Phdr { + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +}; + +/* Legal values for p_type (segment type). */ + +constexpr u32 ElfPtNull = 0; /* Program header table entry unused */ +constexpr u32 ElfPtLoad = 1; /* Loadable program segment */ +constexpr u32 ElfPtDynamic = 2; /* Dynamic linking information */ +constexpr u32 ElfPtInterp = 3; /* Program interpreter */ +constexpr u32 ElfPtNote = 4; /* Auxiliary information */ +constexpr u32 ElfPtPhdr = 6; /* Entry for header table itself */ +constexpr u32 ElfPtTls = 7; /* Thread-local storage segment */ + +/* Legal values for p_flags (segment flags). */ + +constexpr u32 ElfPfExec = 0; /* Segment is executable */ +constexpr u32 ElfPfWrite = 1; /* Segment is writable */ +constexpr u32 ElfPfRead = 2; /* Segment is readable */ + +/* Dynamic section entry. */ + +struct Elf32_Dyn { + Elf32_Sword d_tag; /* Dynamic entry type */ + union { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +}; + +struct Elf64_Dyn { + Elf64_Sxword d_tag; /* Dynamic entry type */ + union { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +}; + +/* Legal values for d_tag (dynamic entry type). */ + +constexpr u32 ElfDtNull = 0; /* Marks end of dynamic section */ +constexpr u32 ElfDtNeeded = 1; /* Name of needed library */ +constexpr u32 ElfDtPltRelSz = 2; /* Size in bytes of PLT relocs */ +constexpr u32 ElfDtPltGot = 3; /* Processor defined value */ +constexpr u32 ElfDtHash = 4; /* Address of symbol hash table */ +constexpr u32 ElfDtStrtab = 5; /* Address of string table */ +constexpr u32 ElfDtSymtab = 6; /* Address of symbol table */ +constexpr u32 ElfDtRela = 7; /* Address of Rela relocs */ +constexpr u32 ElfDtRelasz = 8; /* Total size of Rela relocs */ +constexpr u32 ElfDtRelaent = 9; /* Size of one Rela reloc */ +constexpr u32 ElfDtStrsz = 10; /* Size of string table */ +constexpr u32 ElfDtSyment = 11; /* Size of one symbol table entry */ +constexpr u32 ElfDtInit = 12; /* Address of init function */ +constexpr u32 ElfDtFini = 13; /* Address of termination function */ +constexpr u32 ElfDtRel = 17; /* Address of Rel relocs */ +constexpr u32 ElfDtRelsz = 18; /* Total size of Rel relocs */ +constexpr u32 ElfDtRelent = 19; /* Size of one Rel reloc */ +constexpr u32 ElfDtPltRel = 20; /* Type of reloc in PLT */ +constexpr u32 ElfDtTextRel = 22; /* Reloc might modify .text */ +constexpr u32 ElfDtJmpRel = 23; /* Address of PLT relocs */ +constexpr u32 ElfDtBindNow = 24; /* Process relocations of object */ +constexpr u32 ElfDtInitArray = 25; /* Array with addresses of init fct */ +constexpr u32 ElfDtFiniArray = 26; /* Array with addresses of fini fct */ +constexpr u32 ElfDtInitArraySz = 27; /* Size in bytes of DT_INIT_ARRAY */ +constexpr u32 ElfDtFiniArraySz = 28; /* Size in bytes of DT_FINI_ARRAY */ +constexpr u32 ElfDtSymtabShndx = 34; /* Address of SYMTAB_SHNDX section */ + +} // namespace ELF +} // namespace Common diff --git a/src/common/settings.h b/src/common/settings.h index a7bbfb0da..a507744a2 100644 --- a/src/common/settings.h +++ b/src/common/settings.h @@ -496,7 +496,7 @@ struct Values { // Renderer RangedSetting<RendererBackend> renderer_backend{ - RendererBackend::OpenGL, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; + RendererBackend::Vulkan, RendererBackend::OpenGL, RendererBackend::Vulkan, "backend"}; BasicSetting<bool> renderer_debug{false, "debug"}; BasicSetting<bool> renderer_shader_feedback{false, "shader_feedback"}; BasicSetting<bool> enable_nsight_aftermath{false, "nsight_aftermath"}; diff --git a/src/core/arm/symbols.cpp b/src/core/arm/symbols.cpp index 4aa1a1ee1..0259c7ea2 100644 --- a/src/core/arm/symbols.cpp +++ b/src/core/arm/symbols.cpp @@ -3,73 +3,14 @@ #include "common/bit_field.h" #include "common/common_funcs.h" +#include "common/elf.h" #include "core/arm/symbols.h" #include "core/core.h" #include "core/memory.h" -namespace Core { -namespace { - -constexpr u64 ELF_DYNAMIC_TAG_NULL = 0; -constexpr u64 ELF_DYNAMIC_TAG_STRTAB = 5; -constexpr u64 ELF_DYNAMIC_TAG_SYMTAB = 6; -constexpr u64 ELF_DYNAMIC_TAG_SYMENT = 11; - -enum class ELFSymbolType : u8 { - None = 0, - Object = 1, - Function = 2, - Section = 3, - File = 4, - Common = 5, - TLS = 6, -}; - -enum class ELFSymbolBinding : u8 { - Local = 0, - Global = 1, - Weak = 2, -}; - -enum class ELFSymbolVisibility : u8 { - Default = 0, - Internal = 1, - Hidden = 2, - Protected = 3, -}; - -struct ELF64Symbol { - u32 name_index; - union { - u8 info; - - BitField<0, 4, ELFSymbolType> type; - BitField<4, 4, ELFSymbolBinding> binding; - }; - ELFSymbolVisibility visibility; - u16 sh_index; - u64 value; - u64 size; -}; -static_assert(sizeof(ELF64Symbol) == 0x18, "ELF64Symbol has incorrect size."); - -struct ELF32Symbol { - u32 name_index; - u32 value; - u32 size; - union { - u8 info; - - BitField<0, 4, ELFSymbolType> type; - BitField<4, 4, ELFSymbolBinding> binding; - }; - ELFSymbolVisibility visibility; - u16 sh_index; -}; -static_assert(sizeof(ELF32Symbol) == 0x10, "ELF32Symbol has incorrect size."); - -} // Anonymous namespace +using namespace Common::ELF; +namespace Core { namespace Symbols { template <typename Word, typename ELFSymbol, typename ByteReader> @@ -110,15 +51,15 @@ static Symbols GetSymbols(ByteReader ReadBytes) { const Word value = ReadWord(dynamic_index + sizeof(Word)); dynamic_index += 2 * sizeof(Word); - if (tag == ELF_DYNAMIC_TAG_NULL) { + if (tag == ElfDtNull) { break; } - if (tag == ELF_DYNAMIC_TAG_STRTAB) { + if (tag == ElfDtStrtab) { string_table_offset = value; - } else if (tag == ELF_DYNAMIC_TAG_SYMTAB) { + } else if (tag == ElfDtSymtab) { symbol_table_offset = value; - } else if (tag == ELF_DYNAMIC_TAG_SYMENT) { + } else if (tag == ElfDtSyment) { symbol_entry_size = value; } } @@ -134,14 +75,14 @@ static Symbols GetSymbols(ByteReader ReadBytes) { ELFSymbol symbol{}; ReadBytes(&symbol, symbol_index, sizeof(ELFSymbol)); - VAddr string_offset = string_table_offset + symbol.name_index; + VAddr string_offset = string_table_offset + symbol.st_name; std::string name; for (u8 c = Read8(string_offset); c != 0; c = Read8(++string_offset)) { name += static_cast<char>(c); } symbol_index += symbol_entry_size; - out[name] = std::make_pair(symbol.value, symbol.size); + out[name] = std::make_pair(symbol.st_value, symbol.st_size); } return out; @@ -152,9 +93,9 @@ Symbols GetSymbols(VAddr base, Core::Memory::Memory& memory, bool is_64) { [&](void* ptr, size_t offset, size_t size) { memory.ReadBlock(base + offset, ptr, size); }}; if (is_64) { - return GetSymbols<u64, ELF64Symbol>(ReadBytes); + return GetSymbols<u64, Elf64_Sym>(ReadBytes); } else { - return GetSymbols<u32, ELF32Symbol>(ReadBytes); + return GetSymbols<u32, Elf32_Sym>(ReadBytes); } } @@ -164,9 +105,9 @@ Symbols GetSymbols(std::span<const u8> data, bool is_64) { }}; if (is_64) { - return GetSymbols<u64, ELF64Symbol>(ReadBytes); + return GetSymbols<u64, Elf64_Sym>(ReadBytes); } else { - return GetSymbols<u32, ELF32Symbol>(ReadBytes); + return GetSymbols<u32, Elf32_Sym>(ReadBytes); } } diff --git a/src/core/debugger/debugger.cpp b/src/core/debugger/debugger.cpp index 68ab33e46..8d64990ed 100644 --- a/src/core/debugger/debugger.cpp +++ b/src/core/debugger/debugger.cpp @@ -20,15 +20,16 @@ template <typename Readable, typename Buffer, typename Callback> static void AsyncReceiveInto(Readable& r, Buffer& buffer, Callback&& c) { static_assert(std::is_trivial_v<Buffer>); auto boost_buffer{boost::asio::buffer(&buffer, sizeof(Buffer))}; - r.async_read_some(boost_buffer, [&](const boost::system::error_code& error, size_t bytes_read) { - if (!error.failed()) { - const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); - std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; - c(received_data); - } + r.async_read_some( + boost_buffer, [&, c](const boost::system::error_code& error, size_t bytes_read) { + if (!error.failed()) { + const u8* buffer_start = reinterpret_cast<const u8*>(&buffer); + std::span<const u8> received_data{buffer_start, buffer_start + bytes_read}; + c(received_data); + } - AsyncReceiveInto(r, buffer, c); - }); + AsyncReceiveInto(r, buffer, c); + }); } template <typename Readable, typename Buffer> @@ -65,7 +66,7 @@ public: } stopped = true; - signal_pipe.write_some(boost::asio::buffer(&thread, sizeof(thread))); + boost::asio::write(signal_pipe, boost::asio::buffer(&thread, sizeof(thread))); return true; } @@ -74,7 +75,7 @@ public: } void WriteToClient(std::span<const u8> data) override { - client_socket.write_some(boost::asio::buffer(data.data(), data.size_bytes())); + boost::asio::write(client_socket, boost::asio::buffer(data.data(), data.size_bytes())); } void SetActiveThread(Kernel::KThread* thread) override { diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp index 0c36069a6..f52d78829 100644 --- a/src/core/debugger/gdbstub.cpp +++ b/src/core/debugger/gdbstub.cpp @@ -34,6 +34,65 @@ constexpr char GDB_STUB_REPLY_ERR[] = "E01"; constexpr char GDB_STUB_REPLY_OK[] = "OK"; constexpr char GDB_STUB_REPLY_EMPTY[] = ""; +static u8 CalculateChecksum(std::string_view data) { + return std::accumulate(data.begin(), data.end(), u8{0}, + [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); }); +} + +static std::string EscapeGDB(std::string_view data) { + std::string escaped; + escaped.reserve(data.size()); + + for (char c : data) { + switch (c) { + case '#': + escaped += "}\x03"; + break; + case '$': + escaped += "}\x04"; + break; + case '*': + escaped += "}\x0a"; + break; + case '}': + escaped += "}\x5d"; + break; + default: + escaped += c; + break; + } + } + + return escaped; +} + +static std::string EscapeXML(std::string_view data) { + std::string escaped; + escaped.reserve(data.size()); + + for (char c : data) { + switch (c) { + case '&': + escaped += "&"; + break; + case '"': + escaped += """; + break; + case '<': + escaped += "<"; + break; + case '>': + escaped += ">"; + break; + default: + escaped += c; + break; + } + } + + return escaped; +} + GDBStub::GDBStub(DebuggerBackend& backend_, Core::System& system_) : DebuggerFrontend(backend_), system{system_} { if (system.CurrentProcess()->Is64BitProcess()) { @@ -255,6 +314,80 @@ void GDBStub::ExecuteCommand(std::string_view packet, std::vector<DebuggerAction } } +// Structure offsets are from Atmosphere +// See osdbg_thread_local_region.os.horizon.hpp and osdbg_thread_type.os.horizon.hpp + +static std::optional<std::string> GetNameFromThreadType32(Core::Memory::Memory& memory, + const Kernel::KThread* thread) { + // Read thread type from TLS + const VAddr tls_thread_type{memory.Read32(thread->GetTLSAddress() + 0x1fc)}; + const VAddr argument_thread_type{thread->GetArgument()}; + + if (argument_thread_type && tls_thread_type != argument_thread_type) { + // Probably not created by nnsdk, no name available. + return std::nullopt; + } + + if (!tls_thread_type) { + return std::nullopt; + } + + const u16 version{memory.Read16(tls_thread_type + 0x26)}; + VAddr name_pointer{}; + if (version == 1) { + name_pointer = memory.Read32(tls_thread_type + 0xe4); + } else { + name_pointer = memory.Read32(tls_thread_type + 0xe8); + } + + if (!name_pointer) { + // No name provided. + return std::nullopt; + } + + return memory.ReadCString(name_pointer, 256); +} + +static std::optional<std::string> GetNameFromThreadType64(Core::Memory::Memory& memory, + const Kernel::KThread* thread) { + // Read thread type from TLS + const VAddr tls_thread_type{memory.Read64(thread->GetTLSAddress() + 0x1f8)}; + const VAddr argument_thread_type{thread->GetArgument()}; + + if (argument_thread_type && tls_thread_type != argument_thread_type) { + // Probably not created by nnsdk, no name available. + return std::nullopt; + } + + if (!tls_thread_type) { + return std::nullopt; + } + + const u16 version{memory.Read16(tls_thread_type + 0x46)}; + VAddr name_pointer{}; + if (version == 1) { + name_pointer = memory.Read64(tls_thread_type + 0x1a0); + } else { + name_pointer = memory.Read64(tls_thread_type + 0x1a8); + } + + if (!name_pointer) { + // No name provided. + return std::nullopt; + } + + return memory.ReadCString(name_pointer, 256); +} + +static std::optional<std::string> GetThreadName(Core::System& system, + const Kernel::KThread* thread) { + if (system.CurrentProcess()->Is64BitProcess()) { + return GetNameFromThreadType64(system.Memory(), thread); + } else { + return GetNameFromThreadType32(system.Memory(), thread); + } +} + static std::string_view GetThreadWaitReason(const Kernel::KThread* thread) { switch (thread->GetWaitReasonForDebugging()) { case Kernel::ThreadWaitReasonForDebugging::Sleep: @@ -289,6 +422,18 @@ static std::string GetThreadState(const Kernel::KThread* thread) { } } +static std::string PaginateBuffer(std::string_view buffer, std::string_view request) { + const auto amount{request.substr(request.find(',') + 1)}; + const auto offset_val{static_cast<u64>(strtoll(request.data(), nullptr, 16))}; + const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))}; + + if (offset_val + amount_val > buffer.size()) { + return fmt::format("l{}", buffer.substr(offset_val)); + } else { + return fmt::format("m{}", buffer.substr(offset_val, amount_val)); + } +} + void GDBStub::HandleQuery(std::string_view command) { if (command.starts_with("TStatus")) { // no tracepoint support @@ -297,18 +442,8 @@ void GDBStub::HandleQuery(std::string_view command) { SendReply("PacketSize=4000;qXfer:features:read+;qXfer:threads:read+;qXfer:libraries:read+;" "vContSupported+;QStartNoAckMode+"); } else if (command.starts_with("Xfer:features:read:target.xml:")) { - const auto offset{command.substr(30)}; - const auto amount{command.substr(command.find(',') + 1)}; - - const auto offset_val{static_cast<u64>(strtoll(offset.data(), nullptr, 16))}; - const auto amount_val{static_cast<u64>(strtoll(amount.data(), nullptr, 16))}; const auto target_xml{arch->GetTargetXML()}; - - if (offset_val + amount_val > target_xml.size()) { - SendReply("l" + target_xml.substr(offset_val)); - } else { - SendReply("m" + target_xml.substr(offset_val, amount_val)); - } + SendReply(PaginateBuffer(target_xml, command.substr(30))); } else if (command.starts_with("Offsets")) { Loader::AppLoader::Modules modules; system.GetAppLoader().ReadNSOModules(modules); @@ -321,6 +456,20 @@ void GDBStub::HandleQuery(std::string_view command) { SendReply(fmt::format("TextSeg={:x}", system.CurrentProcess()->PageTable().GetCodeRegionStart())); } + } else if (command.starts_with("Xfer:libraries:read::")) { + Loader::AppLoader::Modules modules; + system.GetAppLoader().ReadNSOModules(modules); + + std::string buffer; + buffer += R"(<?xml version="1.0"?>)"; + buffer += "<library-list>"; + for (const auto& [base, name] : modules) { + buffer += fmt::format(R"(<library name="{}"><segment address="{:#x}"/></library>)", + EscapeXML(name), base); + } + buffer += "</library-list>"; + + SendReply(PaginateBuffer(buffer, command.substr(21))); } else if (command.starts_with("fThreadInfo")) { // beginning of list const auto& threads = system.GlobalSchedulerContext().GetThreadList(); @@ -332,20 +481,26 @@ void GDBStub::HandleQuery(std::string_view command) { } else if (command.starts_with("sThreadInfo")) { // end of list SendReply("l"); - } else if (command.starts_with("Xfer:threads:read")) { + } else if (command.starts_with("Xfer:threads:read::")) { std::string buffer; - buffer += R"(l<?xml version="1.0"?>)"; + buffer += R"(<?xml version="1.0"?>)"; buffer += "<threads>"; const auto& threads = system.GlobalSchedulerContext().GetThreadList(); - for (const auto& thread : threads) { - buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="Thread {:d}">{}</thread>)", + for (const auto* thread : threads) { + auto thread_name{GetThreadName(system, thread)}; + if (!thread_name) { + thread_name = fmt::format("Thread {:d}", thread->GetThreadID()); + } + + buffer += fmt::format(R"(<thread id="{:x}" core="{:d}" name="{}">{}</thread>)", thread->GetThreadID(), thread->GetActiveCore(), - thread->GetThreadID(), GetThreadState(thread)); + EscapeXML(*thread_name), GetThreadState(thread)); } buffer += "</threads>"; - SendReply(buffer); + + SendReply(PaginateBuffer(buffer, command.substr(19))); } else if (command.starts_with("Attached")) { SendReply("0"); } else if (command.starts_with("StartNoAckMode")) { @@ -438,14 +593,10 @@ std::optional<std::string> GDBStub::DetachCommand() { return data.substr(1, data.size() - 4); } -u8 GDBStub::CalculateChecksum(std::string_view data) { - return std::accumulate(data.begin(), data.end(), u8{0}, - [](u8 lhs, u8 rhs) { return static_cast<u8>(lhs + rhs); }); -} - void GDBStub::SendReply(std::string_view data) { - const auto output{ - fmt::format("{}{}{}{:02x}", GDB_STUB_START, data, GDB_STUB_END, CalculateChecksum(data))}; + const auto escaped{EscapeGDB(data)}; + const auto output{fmt::format("{}{}{}{:02x}", GDB_STUB_START, escaped, GDB_STUB_END, + CalculateChecksum(escaped))}; LOG_TRACE(Debug_GDBStub, "Writing reply: {}", output); // C++ string support is complete rubbish diff --git a/src/core/debugger/gdbstub.h b/src/core/debugger/gdbstub.h index aa1f7de6c..1bb638187 100644 --- a/src/core/debugger/gdbstub.h +++ b/src/core/debugger/gdbstub.h @@ -34,7 +34,6 @@ private: std::optional<std::string> DetachCommand(); Kernel::KThread* GetThreadByID(u64 thread_id); - static u8 CalculateChecksum(std::string_view data); void SendReply(std::string_view data); void SendStatus(char status); diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index ab9ce6a86..940334f59 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp @@ -198,6 +198,10 @@ ResultCode KThread::Initialize(KThreadFunction func, uintptr_t arg, VAddr user_s resource_limit_release_hint = false; cpu_time = 0; + // Set debug context. + stack_top = user_stack_top; + argument = arg; + // Clear our stack parameters. std::memset(static_cast<void*>(std::addressof(GetStackParameters())), 0, sizeof(StackParameters)); diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 60ae0da78..f4d83f99a 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h @@ -660,6 +660,14 @@ public: void IfDummyThreadTryWait(); void IfDummyThreadEndWait(); + [[nodiscard]] uintptr_t GetArgument() const { + return argument; + } + + [[nodiscard]] VAddr GetUserStackTop() const { + return stack_top; + } + private: static constexpr size_t PriorityInheritanceCountMax = 10; union SyncObjectBuffer { @@ -791,6 +799,8 @@ private: std::vector<KSynchronizationObject*> wait_objects_for_debugging; VAddr mutex_wait_address_for_debugging{}; ThreadWaitReasonForDebugging wait_reason_for_debugging{}; + uintptr_t argument; + VAddr stack_top; public: using ConditionVariableThreadTreeType = ConditionVariableThreadTree; diff --git a/src/core/hle/service/hid/controllers/gesture.cpp b/src/core/hle/service/hid/controllers/gesture.cpp index 3eae1ae35..32e0708ba 100644 --- a/src/core/hle/service/hid/controllers/gesture.cpp +++ b/src/core/hle/service/hid/controllers/gesture.cpp @@ -61,6 +61,7 @@ void Controller_Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { } last_update_timestamp = shared_memory->gesture_lifo.timestamp; + UpdateGestureSharedMemory(gesture, time_difference); } void Controller_Gesture::ReadTouchInput() { @@ -94,8 +95,7 @@ bool Controller_Gesture::ShouldUpdateGesture(const GestureProperties& gesture, return false; } -void Controller_Gesture::UpdateGestureSharedMemory(u8* data, std::size_t size, - GestureProperties& gesture, +void Controller_Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { GestureType type = GestureType::Idle; GestureAttribute attributes{}; diff --git a/src/core/hle/service/hid/controllers/gesture.h b/src/core/hle/service/hid/controllers/gesture.h index c62a341bf..0d6099ea0 100644 --- a/src/core/hle/service/hid/controllers/gesture.h +++ b/src/core/hle/service/hid/controllers/gesture.h @@ -107,8 +107,7 @@ private: bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); // Updates the shared memory to the next state - void UpdateGestureSharedMemory(u8* data, std::size_t size, GestureProperties& gesture, - f32 time_difference); + void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference); // Initializes new gesture void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 9e32f3e60..d2a91d913 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -5,7 +5,9 @@ #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/service/hid/errors.h" #include "core/hle/service/hid/irs.h" namespace Service::HID { @@ -38,21 +40,32 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { } void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}", + applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { - LOG_DEBUG(Service_IRS, "called"); + IPC::RequestParser rp{ctx}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2, 1}; rb.Push(ResultSuccess); @@ -60,35 +73,109 @@ void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) { } void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + PackedMomentProcessorConfig processor_config; + }; + static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + PackedClusteringProcessorConfig processor_config; + }; + static_assert(sizeof(Parameters) == 0x40, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + PackedImageTransferProcessorConfig processor_config; + u32 transfer_memory_size; + }; + static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, " + "applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.transfer_memory_size, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 5}; rb.Push(ResultSuccess); @@ -97,71 +184,195 @@ void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) { } void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; + const auto processor_config{rp.PopRaw<PackedTeraPluginProcessorConfig>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " + "applet_resource_user_id={}", + camera_handle.npad_type, camera_handle.npad_id, processor_config.mode, + processor_config.required_mcu_version.major, + processor_config.required_mcu_version.minor, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; + + if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && + npad_id != Core::HID::NpadIdType::Handheld) { + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(InvalidNpadId); + return; + } + + IrCameraHandle camera_handle{ + .npad_id = static_cast<u8>(NpadIdTypeToIndex(npad_id)), + .npad_type = Core::HID::NpadStyleIndex::None, + }; + + LOG_WARNING(Service_IRS, "(STUBBED) called, npad_id={}, camera_npad_id={}, camera_npad_type={}", + npad_id, camera_handle.npad_id, camera_handle.npad_type); IPC::ResponseBuilder rb{ctx, 3}; rb.Push(ResultSuccess); - rb.PushRaw<u32>(device_handle); + rb.PushRaw(camera_handle); } void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; + const auto processor_config{rp.PopRaw<PackedPointingProcessorConfig>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}", + camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, + processor_config.required_mcu_version.minor, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; + const auto mcu_version{rp.PopRaw<PackedMcuVersion>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING( + Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}", + camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, + mcu_version.minor); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + IrCameraHandle camera_handle; + PackedFunctionLevel function_level; + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + PackedImageTransferProcessorExConfig processor_config; + u64 transfer_memory_size; + }; + static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + const auto t_mem_handle{ctx.GetCopyHandle(0)}; + + auto t_mem = + system.CurrentProcess()->GetHandleTable().GetObject<Kernel::KTransferMemory>(t_mem_handle); + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, transfer_memory_size={}, " + "applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.transfer_memory_size, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + const auto camera_handle{rp.PopRaw<IrCameraHandle>()}; + const auto processor_config{rp.PopRaw<PackedIrLedProcessorConfig>()}; + const auto applet_resource_user_id{rp.Pop<u64>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} " + "applet_resource_user_id={}", + camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target, + processor_config.required_mcu_version.major, + processor_config.required_mcu_version.minor, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + IrCameraHandle camera_handle; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", + parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, + parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) { - LOG_WARNING(Service_IRS, "(STUBBED) called"); + IPC::RequestParser rp{ctx}; + struct Parameters { + PackedFunctionLevel function_level; + INSERT_PADDING_WORDS_NOINIT(1); + u64 applet_resource_user_id; + }; + static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + + const auto parameters{rp.PopRaw<Parameters>()}; + + LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}", + parameters.function_level.function_level, parameters.applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index efb29d3fd..361dc2213 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h @@ -3,6 +3,7 @@ #pragma once +#include "core/hid/hid_types.h" #include "core/hle/service/service.h" namespace Core { @@ -17,6 +18,235 @@ public: ~IRS() override; private: + // This is nn::irsensor::IrCameraStatus + enum IrCameraStatus : u32 { + Available, + Unsupported, + Unconnected, + }; + + // This is nn::irsensor::IrCameraInternalStatus + enum IrCameraInternalStatus : u32 { + Stopped, + FirmwareUpdateNeeded, + Unkown2, + Unkown3, + Unkown4, + FirmwareVersionRequested, + FirmwareVersionIsInvalid, + Ready, + Setting, + }; + + // This is nn::irsensor::detail::StatusManager::IrSensorMode + enum IrSensorMode : u64 { + None, + MomentProcessor, + ClusteringProcessor, + ImageTransferProcessor, + PointingProcessorMarker, + TeraPluginProcessor, + IrLedProcessor, + }; + + // This is nn::irsensor::ImageProcessorStatus + enum ImageProcessorStatus : u8 { + stopped, + running, + }; + + // This is nn::irsensor::ImageTransferProcessorFormat + enum ImageTransferProcessorFormat : u8 { + Size320x240, + Size160x120, + Size80x60, + Size40x30, + Size20x15, + }; + + // This is nn::irsensor::AdaptiveClusteringMode + enum AdaptiveClusteringMode : u8 { + StaticFov, + DynamicFov, + }; + + // This is nn::irsensor::AdaptiveClusteringTargetDistance + enum AdaptiveClusteringTargetDistance : u8 { + Near, + Middle, + Far, + }; + + // This is nn::irsensor::IrsHandAnalysisMode + enum IrsHandAnalysisMode : u8 { + Silhouette, + Image, + SilhoueteAndImage, + SilhuetteOnly, + }; + + // This is nn::irsensor::IrSensorFunctionLevel + enum IrSensorFunctionLevel : u8 { + unknown0, + unknown1, + unknown2, + unknown3, + unknown4, + }; + + // This is nn::irsensor::IrCameraHandle + struct IrCameraHandle { + u8 npad_id{}; + Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None}; + INSERT_PADDING_BYTES(2); + }; + static_assert(sizeof(IrCameraHandle) == 4, "IrCameraHandle is an invalid size"); + + struct IrsRect { + s16 x; + s16 y; + s16 width; + s16 height; + }; + + // This is nn::irsensor::PackedMcuVersion + struct PackedMcuVersion { + u16 major; + u16 minor; + }; + static_assert(sizeof(PackedMcuVersion) == 4, "PackedMcuVersion is an invalid size"); + + // This is nn::irsensor::MomentProcessorConfig + struct MomentProcessorConfig { + u64 exposire_time; + u8 light_target; + u8 gain; + u8 is_negative_used; + INSERT_PADDING_BYTES(7); + IrsRect window_of_interest; + u8 preprocess; + u8 preprocess_intensity_threshold; + INSERT_PADDING_BYTES(5); + }; + static_assert(sizeof(MomentProcessorConfig) == 0x28, + "MomentProcessorConfig is an invalid size"); + + // This is nn::irsensor::PackedMomentProcessorConfig + struct PackedMomentProcessorConfig { + u64 exposire_time; + u8 light_target; + u8 gain; + u8 is_negative_used; + INSERT_PADDING_BYTES(5); + IrsRect window_of_interest; + PackedMcuVersion required_mcu_version; + u8 preprocess; + u8 preprocess_intensity_threshold; + INSERT_PADDING_BYTES(2); + }; + static_assert(sizeof(PackedMomentProcessorConfig) == 0x20, + "PackedMomentProcessorConfig is an invalid size"); + + // This is nn::irsensor::ClusteringProcessorConfig + struct ClusteringProcessorConfig { + u64 exposire_time; + u32 light_target; + u32 gain; + u8 is_negative_used; + INSERT_PADDING_BYTES(7); + IrsRect window_of_interest; + u32 pixel_count_min; + u32 pixel_count_max; + u32 object_intensity_min; + u8 is_external_light_filter_enabled; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(ClusteringProcessorConfig) == 0x30, + "ClusteringProcessorConfig is an invalid size"); + + // This is nn::irsensor::PackedClusteringProcessorConfig + struct PackedClusteringProcessorConfig { + u64 exposire_time; + u8 light_target; + u8 gain; + u8 is_negative_used; + INSERT_PADDING_BYTES(5); + IrsRect window_of_interest; + PackedMcuVersion required_mcu_version; + u32 pixel_count_min; + u32 pixel_count_max; + u32 object_intensity_min; + u8 is_external_light_filter_enabled; + INSERT_PADDING_BYTES(2); + }; + static_assert(sizeof(PackedClusteringProcessorConfig) == 0x30, + "PackedClusteringProcessorConfig is an invalid size"); + + // This is nn::irsensor::PackedImageTransferProcessorConfig + struct PackedImageTransferProcessorConfig { + u64 exposire_time; + u8 light_target; + u8 gain; + u8 is_negative_used; + INSERT_PADDING_BYTES(5); + PackedMcuVersion required_mcu_version; + u8 format; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(PackedImageTransferProcessorConfig) == 0x18, + "PackedImageTransferProcessorConfig is an invalid size"); + + // This is nn::irsensor::PackedTeraPluginProcessorConfig + struct PackedTeraPluginProcessorConfig { + PackedMcuVersion required_mcu_version; + u8 mode; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(PackedTeraPluginProcessorConfig) == 0x8, + "PackedTeraPluginProcessorConfig is an invalid size"); + + // This is nn::irsensor::PackedPointingProcessorConfig + struct PackedPointingProcessorConfig { + IrsRect window_of_interest; + PackedMcuVersion required_mcu_version; + }; + static_assert(sizeof(PackedPointingProcessorConfig) == 0xC, + "PackedPointingProcessorConfig is an invalid size"); + + // This is nn::irsensor::PackedFunctionLevel + struct PackedFunctionLevel { + IrSensorFunctionLevel function_level; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(PackedFunctionLevel) == 0x4, "PackedFunctionLevel is an invalid size"); + + // This is nn::irsensor::PackedImageTransferProcessorExConfig + struct PackedImageTransferProcessorExConfig { + u64 exposire_time; + u8 light_target; + u8 gain; + u8 is_negative_used; + INSERT_PADDING_BYTES(5); + PackedMcuVersion required_mcu_version; + ImageTransferProcessorFormat origin_format; + ImageTransferProcessorFormat trimming_format; + u16 trimming_start_x; + u16 trimming_start_y; + u8 is_external_light_filter_enabled; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(PackedImageTransferProcessorExConfig) == 0x20, + "PackedImageTransferProcessorExConfig is an invalid size"); + + // This is nn::irsensor::PackedIrLedProcessorConfig + struct PackedIrLedProcessorConfig { + PackedMcuVersion required_mcu_version; + u8 light_target; + INSERT_PADDING_BYTES(3); + }; + static_assert(sizeof(PackedIrLedProcessorConfig) == 0x8, + "PackedIrLedProcessorConfig is an invalid size"); + void ActivateIrsensor(Kernel::HLERequestContext& ctx); void DeactivateIrsensor(Kernel::HLERequestContext& ctx); void GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx); @@ -35,8 +265,6 @@ private: void RunIrLedProcessor(Kernel::HLERequestContext& ctx); void StopImageProcessorAsync(Kernel::HLERequestContext& ctx); void ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx); - - const u32 device_handle{0xABCD}; }; class IRS_SYS final : public ServiceFramework<IRS_SYS> { diff --git a/src/core/hle/service/jit/jit_context.cpp b/src/core/hle/service/jit/jit_context.cpp index 19bd85b6c..4ed3f02e2 100644 --- a/src/core/hle/service/jit/jit_context.cpp +++ b/src/core/hle/service/jit/jit_context.cpp @@ -11,10 +11,13 @@ #include "common/alignment.h" #include "common/common_funcs.h" #include "common/div_ceil.h" +#include "common/elf.h" #include "common/logging/log.h" #include "core/hle/service/jit/jit_context.h" #include "core/memory.h" +using namespace Common::ELF; + namespace Service::JIT { constexpr std::array<u8, 8> SVC0_ARM64 = { @@ -26,25 +29,6 @@ constexpr std::array HELPER_FUNCTIONS{ "_stop", "_resolve", "_panic", "memcpy", "memmove", "memset", }; -struct Elf64_Dyn { - u64 d_tag; - u64 d_un; -}; - -struct Elf64_Rela { - u64 r_offset; - u64 r_info; - s64 r_addend; -}; - -static constexpr u32 Elf64_RelaType(const Elf64_Rela* rela) { - return static_cast<u32>(rela->r_info); -} - -constexpr int DT_RELA = 7; /* Address of Rela relocs */ -constexpr int DT_RELASZ = 8; /* Total size of Rela relocs */ -constexpr int R_AARCH64_RELATIVE = 1027; /* Adjust by program base. */ - constexpr size_t STACK_ALIGN = 16; class JITContextImpl; @@ -206,17 +190,17 @@ public: if (!dyn.d_tag) { break; } - if (dyn.d_tag == DT_RELA) { - rela_dyn = dyn.d_un; + if (dyn.d_tag == ElfDtRela) { + rela_dyn = dyn.d_un.d_ptr; } - if (dyn.d_tag == DT_RELASZ) { - num_rela = dyn.d_un / sizeof(Elf64_Rela); + if (dyn.d_tag == ElfDtRelasz) { + num_rela = dyn.d_un.d_val / sizeof(Elf64_Rela); } } for (size_t i = 0; i < num_rela; i++) { const auto rela{callbacks->ReadMemory<Elf64_Rela>(rela_dyn + i * sizeof(Elf64_Rela))}; - if (Elf64_RelaType(&rela) != R_AARCH64_RELATIVE) { + if (Elf64RelType(rela.r_info) != ElfAArch64Relative) { continue; } const VAddr contents{callbacks->MemoryRead64(rela.r_offset)}; diff --git a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp index d7db77aff..4b3d5efd6 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_consumer.cpp @@ -89,14 +89,6 @@ Status BufferQueueConsumer::AcquireBuffer(BufferItem* out_buffer, LOG_DEBUG(Service_NVFlinger, "acquiring slot={}", slot); - // If the front buffer is still being tracked, update its slot state - if (core->StillTracking(*front)) { - slots[slot].acquire_called = true; - slots[slot].needs_cleanup_on_release = false; - slots[slot].buffer_state = BufferState::Acquired; - slots[slot].fence = Fence::NoFence(); - } - // If the buffer has previously been acquired by the consumer, set graphic_buffer to nullptr to // avoid unnecessarily remapping this buffer on the consumer side. if (out_buffer->acquire_called) { @@ -139,26 +131,11 @@ Status BufferQueueConsumer::ReleaseBuffer(s32 slot, u64 frame_number, const Fenc ++current; } - if (slots[slot].buffer_state == BufferState::Acquired) { - slots[slot].fence = release_fence; - slots[slot].buffer_state = BufferState::Free; - - listener = core->connected_producer_listener; - - LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); - } else if (slots[slot].needs_cleanup_on_release) { - LOG_DEBUG(Service_NVFlinger, "releasing a stale buffer slot {} (state = {})", slot, - slots[slot].buffer_state); + slots[slot].buffer_state = BufferState::Free; - slots[slot].needs_cleanup_on_release = false; + listener = core->connected_producer_listener; - return Status::StaleBufferSlot; - } else { - LOG_ERROR(Service_NVFlinger, "attempted to release buffer slot {} but its state was {}", - slot, slots[slot].buffer_state); - - return Status::BadValue; - } + LOG_DEBUG(Service_NVFlinger, "releasing slot {}", slot); core->SignalDequeueCondition(); } diff --git a/src/core/hle/service/nvflinger/buffer_queue_core.cpp b/src/core/hle/service/nvflinger/buffer_queue_core.cpp index d4e8b44d0..ea4a14ea4 100644 --- a/src/core/hle/service/nvflinger/buffer_queue_core.cpp +++ b/src/core/hle/service/nvflinger/buffer_queue_core.cpp @@ -84,10 +84,6 @@ void BufferQueueCore::FreeBufferLocked(s32 slot) { slots[slot].graphic_buffer.reset(); - if (slots[slot].buffer_state == BufferState::Acquired) { - slots[slot].needs_cleanup_on_release = true; - } - slots[slot].buffer_state = BufferState::Free; slots[slot].frame_number = UINT32_MAX; slots[slot].acquire_called = false; diff --git a/src/core/hle/service/nvflinger/buffer_slot.h b/src/core/hle/service/nvflinger/buffer_slot.h index 6b3e87446..0cd0e9964 100644 --- a/src/core/hle/service/nvflinger/buffer_slot.h +++ b/src/core/hle/service/nvflinger/buffer_slot.h @@ -31,7 +31,6 @@ struct BufferSlot final { u64 frame_number{}; Fence fence; bool acquire_called{}; - bool needs_cleanup_on_release{}; bool attached_by_consumer{}; bool is_preallocated{}; }; diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index cf5933699..dfb10c34f 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -6,6 +6,7 @@ #include <memory> #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/elf.h" #include "common/logging/log.h" #include "core/hle/kernel/code_set.h" #include "core/hle/kernel/k_page_table.h" @@ -13,159 +14,7 @@ #include "core/loader/elf.h" #include "core/memory.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// ELF Header Constants - -// File type -enum ElfType { - ET_NONE = 0, - ET_REL = 1, - ET_EXEC = 2, - ET_DYN = 3, - ET_CORE = 4, - ET_LOPROC = 0xFF00, - ET_HIPROC = 0xFFFF, -}; - -// Machine/Architecture -enum ElfMachine { - EM_NONE = 0, - EM_M32 = 1, - EM_SPARC = 2, - EM_386 = 3, - EM_68K = 4, - EM_88K = 5, - EM_860 = 7, - EM_MIPS = 8 -}; - -// File version -#define EV_NONE 0 -#define EV_CURRENT 1 - -// Identification index -#define EI_MAG0 0 -#define EI_MAG1 1 -#define EI_MAG2 2 -#define EI_MAG3 3 -#define EI_CLASS 4 -#define EI_DATA 5 -#define EI_VERSION 6 -#define EI_PAD 7 -#define EI_NIDENT 16 - -// Sections constants - -// Section types -#define SHT_NULL 0 -#define SHT_PROGBITS 1 -#define SHT_SYMTAB 2 -#define SHT_STRTAB 3 -#define SHT_RELA 4 -#define SHT_HASH 5 -#define SHT_DYNAMIC 6 -#define SHT_NOTE 7 -#define SHT_NOBITS 8 -#define SHT_REL 9 -#define SHT_SHLIB 10 -#define SHT_DYNSYM 11 -#define SHT_LOPROC 0x70000000 -#define SHT_HIPROC 0x7FFFFFFF -#define SHT_LOUSER 0x80000000 -#define SHT_HIUSER 0xFFFFFFFF - -// Section flags -enum ElfSectionFlags { - SHF_WRITE = 0x1, - SHF_ALLOC = 0x2, - SHF_EXECINSTR = 0x4, - SHF_MASKPROC = 0xF0000000, -}; - -// Segment types -#define PT_NULL 0 -#define PT_LOAD 1 -#define PT_DYNAMIC 2 -#define PT_INTERP 3 -#define PT_NOTE 4 -#define PT_SHLIB 5 -#define PT_PHDR 6 -#define PT_LOPROC 0x70000000 -#define PT_HIPROC 0x7FFFFFFF - -// Segment flags -#define PF_X 0x1 -#define PF_W 0x2 -#define PF_R 0x4 -#define PF_MASKPROC 0xF0000000 - -typedef unsigned int Elf32_Addr; -typedef unsigned short Elf32_Half; -typedef unsigned int Elf32_Off; -typedef signed int Elf32_Sword; -typedef unsigned int Elf32_Word; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// ELF file header - -struct Elf32_Ehdr { - unsigned char e_ident[EI_NIDENT]; - Elf32_Half e_type; - Elf32_Half e_machine; - Elf32_Word e_version; - Elf32_Addr e_entry; - Elf32_Off e_phoff; - Elf32_Off e_shoff; - Elf32_Word e_flags; - Elf32_Half e_ehsize; - Elf32_Half e_phentsize; - Elf32_Half e_phnum; - Elf32_Half e_shentsize; - Elf32_Half e_shnum; - Elf32_Half e_shstrndx; -}; - -// Section header -struct Elf32_Shdr { - Elf32_Word sh_name; - Elf32_Word sh_type; - Elf32_Word sh_flags; - Elf32_Addr sh_addr; - Elf32_Off sh_offset; - Elf32_Word sh_size; - Elf32_Word sh_link; - Elf32_Word sh_info; - Elf32_Word sh_addralign; - Elf32_Word sh_entsize; -}; - -// Segment header -struct Elf32_Phdr { - Elf32_Word p_type; - Elf32_Off p_offset; - Elf32_Addr p_vaddr; - Elf32_Addr p_paddr; - Elf32_Word p_filesz; - Elf32_Word p_memsz; - Elf32_Word p_flags; - Elf32_Word p_align; -}; - -// Symbol table entry -struct Elf32_Sym { - Elf32_Word st_name; - Elf32_Addr st_value; - Elf32_Word st_size; - unsigned char st_info; - unsigned char st_other; - Elf32_Half st_shndx; -}; - -// Relocation entries -struct Elf32_Rel { - Elf32_Addr r_offset; - Elf32_Word r_info; -}; +using namespace Common::ELF; //////////////////////////////////////////////////////////////////////////////////////////////////// // ElfReader class @@ -193,11 +42,11 @@ public: } // Quick accessors - ElfType GetType() const { - return (ElfType)(header->e_type); + u16 GetType() const { + return header->e_type; } - ElfMachine GetMachine() const { - return (ElfMachine)(header->e_machine); + u16 GetMachine() const { + return header->e_machine; } VAddr GetEntryPoint() const { return entryPoint; @@ -220,13 +69,13 @@ public: const u8* GetSectionDataPtr(int section) const { if (section < 0 || section >= header->e_shnum) return nullptr; - if (sections[section].sh_type != SHT_NOBITS) + if (sections[section].sh_type != ElfShtNobits) return GetPtr(sections[section].sh_offset); else return nullptr; } bool IsCodeSection(int section) const { - return sections[section].sh_type == SHT_PROGBITS; + return sections[section].sh_type == ElfShtProgBits; } const u8* GetSegmentPtr(int segment) { return GetPtr(segments[segment].p_offset); @@ -256,7 +105,7 @@ ElfReader::ElfReader(void* ptr) { } const char* ElfReader::GetSectionName(int section) const { - if (sections[section].sh_type == SHT_NULL) + if (sections[section].sh_type == ElfShtNull) return nullptr; int name_offset = sections[section].sh_name; @@ -272,7 +121,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) { LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); // Should we relocate? - relocate = (header->e_type != ET_EXEC); + relocate = (header->e_type != ElfTypeExec); if (relocate) { LOG_DEBUG(Loader, "Relocatable module"); @@ -288,7 +137,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) { u64 total_image_size = 0; for (unsigned int i = 0; i < header->e_phnum; ++i) { const Elf32_Phdr* p = &segments[i]; - if (p->p_type == PT_LOAD) { + if (p->p_type == ElfPtLoad) { total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; } } @@ -303,14 +152,14 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) { LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); - if (p->p_type == PT_LOAD) { + if (p->p_type == ElfPtLoad) { Kernel::CodeSet::Segment* codeset_segment; - u32 permission_flags = p->p_flags & (PF_R | PF_W | PF_X); - if (permission_flags == (PF_R | PF_X)) { + u32 permission_flags = p->p_flags & (ElfPfRead | ElfPfWrite | ElfPfExec); + if (permission_flags == (ElfPfRead | ElfPfExec)) { codeset_segment = &codeset.CodeSegment(); - } else if (permission_flags == (PF_R)) { + } else if (permission_flags == (ElfPfRead)) { codeset_segment = &codeset.RODataSegment(); - } else if (permission_flags == (PF_R | PF_W)) { + } else if (permission_flags == (ElfPfRead | ElfPfWrite)) { codeset_segment = &codeset.DataSegment(); } else { LOG_ERROR(Loader, "Unexpected ELF PT_LOAD segment id {} with flags {:X}", i, diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index 7d0cb8fce..3a4646289 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -595,8 +595,8 @@ void Maxwell3D::DrawArrays() { std::optional<u64> Maxwell3D::GetQueryResult() { switch (regs.query.query_get.select) { - case Regs::QuerySelect::Zero: - return 0; + case Regs::QuerySelect::Payload: + return regs.query.query_sequence; case Regs::QuerySelect::SamplesPassed: // Deferred. rasterizer->Query(regs.query.QueryAddress(), QueryType::SamplesPassed, diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index c0c2c7d96..434ba0877 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -93,7 +93,7 @@ public: }; enum class QuerySelect : u32 { - Zero = 0, + Payload = 0, TimeElapsed = 2, TransformFeedbackPrimitivesGenerated = 11, PrimitivesGenerated = 18, diff --git a/src/video_core/vulkan_common/vulkan_library.cpp b/src/video_core/vulkan_common/vulkan_library.cpp index a5dd33fb2..4eb3913ee 100644 --- a/src/video_core/vulkan_common/vulkan_library.cpp +++ b/src/video_core/vulkan_common/vulkan_library.cpp @@ -5,11 +5,13 @@ #include "common/dynamic_library.h" #include "common/fs/path_util.h" +#include "common/logging/log.h" #include "video_core/vulkan_common/vulkan_library.h" namespace Vulkan { Common::DynamicLibrary OpenLibrary() { + LOG_DEBUG(Render_Vulkan, "Looking for a Vulkan library"); Common::DynamicLibrary library; #ifdef __APPLE__ // Check if a path to a specific Vulkan library has been specified. @@ -22,9 +24,11 @@ Common::DynamicLibrary OpenLibrary() { } #else std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); + LOG_DEBUG(Render_Vulkan, "Trying Vulkan library: {}", filename); if (!library.Open(filename.c_str())) { // Android devices may not have libvulkan.so.1, only libvulkan.so. filename = Common::DynamicLibrary::GetVersionedFilename("vulkan"); + LOG_DEBUG(Render_Vulkan, "Trying Vulkan library (second attempt): {}", filename); void(library.Open(filename.c_str())); } #endif diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 07df9675d..9259ca15e 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -30,6 +30,8 @@ add_executable(yuzu applets/qt_web_browser_scripts.h bootmanager.cpp bootmanager.h + check_vulkan.cpp + check_vulkan.h compatdb.ui compatibility_list.cpp compatibility_list.h @@ -187,7 +189,7 @@ if (ENABLE_QT_TRANSLATION) # Update source TS file if enabled if (GENERATE_QT_TRANSLATION) get_target_property(SRCS yuzu SOURCES) - qt5_create_translation(QM_FILES + qt_create_translation(QM_FILES ${SRCS} ${UIS} ${YUZU_QT_LANGUAGES}/en.ts @@ -203,7 +205,7 @@ if (ENABLE_QT_TRANSLATION) list(REMOVE_ITEM LANGUAGES_TS ${YUZU_QT_LANGUAGES}/en.ts) # Compile TS files to QM files - qt5_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) + qt_add_translation(LANGUAGES_QM ${LANGUAGES_TS}) # Build a QRC file from the QM file list set(LANGUAGES_QRC ${CMAKE_CURRENT_BINARY_DIR}/languages.qrc) @@ -215,7 +217,7 @@ if (ENABLE_QT_TRANSLATION) file(APPEND ${LANGUAGES_QRC} "</qresource></RCC>") # Add the QRC file to package in all QM files - qt5_add_resources(LANGUAGES ${LANGUAGES_QRC}) + qt_add_resources(LANGUAGES ${LANGUAGES_QRC}) else() set(LANGUAGES) endif() @@ -236,7 +238,11 @@ if (APPLE) set_target_properties(yuzu PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) elseif(WIN32) # compile as a win32 gui application instead of a console application - target_link_libraries(yuzu PRIVATE Qt5::WinMain) + if (QT_VERSION VERSION_GREATER 6) + target_link_libraries(yuzu PRIVATE Qt6::EntryPointPrivate) + else() + target_link_libraries(yuzu PRIVATE Qt5::WinMain) + endif() if(MSVC) set_target_properties(yuzu PROPERTIES LINK_FLAGS_RELEASE "/SUBSYSTEM:WINDOWS") elseif(MINGW) @@ -247,7 +253,7 @@ endif() create_target_directory_groups(yuzu) target_link_libraries(yuzu PRIVATE common core input_common video_core) -target_link_libraries(yuzu PRIVATE Boost::boost glad Qt5::Widgets) +target_link_libraries(yuzu PRIVATE Boost::boost glad Qt::Widgets) target_link_libraries(yuzu PRIVATE ${PLATFORM_LIBRARIES} Threads::Threads) target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include) @@ -255,7 +261,7 @@ if (NOT WIN32) target_include_directories(yuzu PRIVATE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) endif() if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") - target_link_libraries(yuzu PRIVATE Qt5::DBus) + target_link_libraries(yuzu PRIVATE Qt::DBus) endif() target_compile_definitions(yuzu PRIVATE @@ -291,7 +297,7 @@ if (USE_DISCORD_PRESENCE) endif() if (YUZU_USE_QT_WEB_ENGINE) - target_link_libraries(yuzu PRIVATE Qt5::WebEngineCore Qt5::WebEngineWidgets) + target_link_libraries(yuzu PRIVATE Qt::WebEngineCore Qt::WebEngineWidgets) target_compile_definitions(yuzu PRIVATE -DYUZU_USE_QT_WEB_ENGINE) endif () diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index aae2de2f8..bde465485 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp @@ -752,7 +752,7 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) { input_subsystem->GetMouse()->MouseMove(x, y, touch_x, touch_y, center_x, center_y); if (Settings::values.mouse_panning && !Settings::values.mouse_enabled) { - QCursor::setPos(mapToGlobal({center_x, center_y})); + QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); } emit MouseActivity(); diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h index 87c559e7a..d01538039 100644 --- a/src/yuzu/bootmanager.h +++ b/src/yuzu/bootmanager.h @@ -10,6 +10,7 @@ #include <mutex> #include <QImage> +#include <QStringList> #include <QThread> #include <QTouchEvent> #include <QWidget> @@ -20,7 +21,6 @@ class GRenderWindow; class GMainWindow; class QKeyEvent; -class QStringList; namespace Core { enum class SystemResultStatus : u32; diff --git a/src/yuzu/check_vulkan.cpp b/src/yuzu/check_vulkan.cpp new file mode 100644 index 000000000..e6d66ab34 --- /dev/null +++ b/src/yuzu/check_vulkan.cpp @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/vulkan_common/vulkan_wrapper.h" + +#include <filesystem> +#include <fstream> +#include "common/fs/fs.h" +#include "common/fs/path_util.h" +#include "common/logging/log.h" +#include "video_core/vulkan_common/vulkan_instance.h" +#include "video_core/vulkan_common/vulkan_library.h" +#include "yuzu/check_vulkan.h" +#include "yuzu/uisettings.h" + +constexpr char TEMP_FILE_NAME[] = "vulkan_check"; + +bool CheckVulkan() { + if (UISettings::values.has_broken_vulkan) { + return true; + } + + LOG_DEBUG(Frontend, "Checking presence of Vulkan"); + + const auto fs_config_loc = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir); + const auto temp_file_loc = fs_config_loc / TEMP_FILE_NAME; + + if (std::filesystem::exists(temp_file_loc)) { + LOG_WARNING(Frontend, "Detected recovery from previous failed Vulkan initialization"); + + UISettings::values.has_broken_vulkan = true; + std::filesystem::remove(temp_file_loc); + return false; + } + + std::ofstream temp_file_handle(temp_file_loc); + temp_file_handle.close(); + + try { + Vulkan::vk::InstanceDispatch dld; + const Common::DynamicLibrary library = Vulkan::OpenLibrary(); + const Vulkan::vk::Instance instance = + Vulkan::CreateInstance(library, dld, VK_API_VERSION_1_0); + + } catch (const Vulkan::vk::Exception& exception) { + LOG_ERROR(Frontend, "Failed to initialize Vulkan: {}", exception.what()); + // Don't set has_broken_vulkan to true here: we care when loading Vulkan crashes the + // application, not when we can handle it. + } + + std::filesystem::remove(temp_file_loc); + return true; +} diff --git a/src/yuzu/check_vulkan.h b/src/yuzu/check_vulkan.h new file mode 100644 index 000000000..e4ea93582 --- /dev/null +++ b/src/yuzu/check_vulkan.h @@ -0,0 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +bool CheckVulkan(); diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 583e9df24..9df4752be 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp @@ -71,28 +71,28 @@ const std::array<int, 2> Config::default_ringcon_analogs{{ // UISetting::values.shortcuts, which is alphabetically ordered. // clang-format off const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{ - {QStringLiteral("Audio Mute/Unmute"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, - {QStringLiteral("Audio Volume Down"), QStringLiteral("Main Window"), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, - {QStringLiteral("Audio Volume Up"), QStringLiteral("Main Window"), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, - {QStringLiteral("Capture Screenshot"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, - {QStringLiteral("Change Adapting Filter"), QStringLiteral("Main Window"), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, - {QStringLiteral("Change Docked Mode"), QStringLiteral("Main Window"), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, - {QStringLiteral("Change GPU Accuracy"), QStringLiteral("Main Window"), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, - {QStringLiteral("Continue/Pause Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, - {QStringLiteral("Exit Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, - {QStringLiteral("Exit yuzu"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, - {QStringLiteral("Fullscreen"), QStringLiteral("Main Window"), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, - {QStringLiteral("Load File"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, - {QStringLiteral("Load/Remove Amiibo"), QStringLiteral("Main Window"), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, - {QStringLiteral("Restart Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, - {QStringLiteral("Stop Emulation"), QStringLiteral("Main Window"), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, - {QStringLiteral("TAS Record"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, - {QStringLiteral("TAS Reset"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, - {QStringLiteral("TAS Start/Stop"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, - {QStringLiteral("Toggle Filter Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, - {QStringLiteral("Toggle Framerate Limit"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, - {QStringLiteral("Toggle Mouse Panning"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, - {QStringLiteral("Toggle Status Bar"), QStringLiteral("Main Window"), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+M"), QStringLiteral("Home+Dpad_Right"), Qt::WindowShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("-"), QStringLiteral("Home+Dpad_Down"), Qt::ApplicationShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("+"), QStringLiteral("Home+Dpad_Up"), Qt::ApplicationShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Capture Screenshot")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+P"), QStringLiteral("Screenshot"), Qt::WidgetWithChildrenShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Adapting Filter")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F8"), QStringLiteral("Home+L"), Qt::ApplicationShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change Docked Mode")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F10"), QStringLiteral("Home+X"), Qt::ApplicationShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Change GPU Accuracy")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F9"), QStringLiteral("Home+R"), Qt::ApplicationShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Continue/Pause Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F4"), QStringLiteral("Home+Plus"), Qt::WindowShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Esc"), QStringLiteral(""), Qt::WindowShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Exit yuzu")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+Q"), QStringLiteral("Home+Minus"), Qt::WindowShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Filter Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F"), QStringLiteral(""), Qt::WindowShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Framerate Limit")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+U"), QStringLiteral("Home+Y"), Qt::ApplicationShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Mouse Panning")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F9"), QStringLiteral(""), Qt::ApplicationShortcut}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Toggle Status Bar")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+S"), QStringLiteral(""), Qt::WindowShortcut}}, }}; // clang-format on @@ -682,6 +682,12 @@ void Config::ReadRendererValues() { ReadGlobalSetting(Settings::values.bg_green); ReadGlobalSetting(Settings::values.bg_blue); + if (!global && UISettings::values.has_broken_vulkan && + Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan && + !Settings::values.renderer_backend.UsingGlobal()) { + Settings::values.renderer_backend.SetGlobal(true); + } + if (global) { ReadBasicSetting(Settings::values.renderer_debug); ReadBasicSetting(Settings::values.renderer_shader_feedback); @@ -801,6 +807,7 @@ void Config::ReadUIValues() { ReadBasicSetting(UISettings::values.pause_when_in_background); ReadBasicSetting(UISettings::values.mute_when_in_background); ReadBasicSetting(UISettings::values.hide_mouse); + ReadBasicSetting(UISettings::values.has_broken_vulkan); ReadBasicSetting(UISettings::values.disable_web_applet); qt_config->endGroup(); @@ -1348,6 +1355,7 @@ void Config::SaveUIValues() { WriteBasicSetting(UISettings::values.pause_when_in_background); WriteBasicSetting(UISettings::values.mute_when_in_background); WriteBasicSetting(UISettings::values.hide_mouse); + WriteBasicSetting(UISettings::values.has_broken_vulkan); WriteBasicSetting(UISettings::values.disable_web_applet); qt_config->endGroup(); diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp index 2f1435b10..85f34dc35 100644 --- a/src/yuzu/configuration/configure_graphics.cpp +++ b/src/yuzu/configuration/configure_graphics.cpp @@ -17,6 +17,7 @@ #include "video_core/vulkan_common/vulkan_library.h" #include "yuzu/configuration/configuration_shared.h" #include "yuzu/configuration/configure_graphics.h" +#include "yuzu/uisettings.h" ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* parent) : QWidget(parent), ui{std::make_unique<Ui::ConfigureGraphics>()}, system{system_} { @@ -57,6 +58,24 @@ ConfigureGraphics::ConfigureGraphics(const Core::System& system_, QWidget* paren UpdateBackgroundColorButton(new_bg_color); }); + connect(ui->button_check_vulkan, &QAbstractButton::clicked, this, [this] { + UISettings::values.has_broken_vulkan = false; + + if (RetrieveVulkanDevices()) { + ui->api->setEnabled(true); + ui->button_check_vulkan->hide(); + + for (const auto& device : vulkan_devices) { + ui->device->addItem(device); + } + } else { + UISettings::values.has_broken_vulkan = true; + } + }); + + ui->api->setEnabled(!UISettings::values.has_broken_vulkan.GetValue()); + ui->button_check_vulkan->setVisible(UISettings::values.has_broken_vulkan.GetValue()); + ui->bg_label->setVisible(Settings::IsConfiguringGlobal()); ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal()); } @@ -296,7 +315,7 @@ void ConfigureGraphics::UpdateAPILayout() { vulkan_device = Settings::values.vulkan_device.GetValue(true); shader_backend = Settings::values.shader_backend.GetValue(true); ui->device_widget->setEnabled(false); - ui->backend_widget->setEnabled(false); + ui->backend_widget->setEnabled(UISettings::values.has_broken_vulkan.GetValue()); } else { vulkan_device = Settings::values.vulkan_device.GetValue(); shader_backend = Settings::values.shader_backend.GetValue(); @@ -318,7 +337,11 @@ void ConfigureGraphics::UpdateAPILayout() { } } -void ConfigureGraphics::RetrieveVulkanDevices() try { +bool ConfigureGraphics::RetrieveVulkanDevices() try { + if (UISettings::values.has_broken_vulkan) { + return false; + } + using namespace Vulkan; vk::InstanceDispatch dld; @@ -333,8 +356,10 @@ void ConfigureGraphics::RetrieveVulkanDevices() try { vulkan_devices.push_back(QString::fromStdString(name)); } + return true; } catch (const Vulkan::vk::Exception& exception) { LOG_ERROR(Frontend, "Failed to enumerate devices with error: {}", exception.what()); + return false; } Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const { @@ -415,4 +440,11 @@ void ConfigureGraphics::SetupPerGameUI() { ui->api, static_cast<int>(Settings::values.renderer_backend.GetValue(true))); ConfigurationShared::InsertGlobalItem( ui->nvdec_emulation, static_cast<int>(Settings::values.nvdec_emulation.GetValue(true))); + + if (UISettings::values.has_broken_vulkan) { + ui->backend_widget->setEnabled(true); + ConfigurationShared::SetColoredComboBox( + ui->backend, ui->backend_widget, + static_cast<int>(Settings::values.shader_backend.GetValue(true))); + } } diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h index 1b101c940..8438f0187 100644 --- a/src/yuzu/configuration/configure_graphics.h +++ b/src/yuzu/configuration/configure_graphics.h @@ -41,7 +41,7 @@ private: void UpdateDeviceSelection(int device); void UpdateShaderBackendSelection(int backend); - void RetrieveVulkanDevices(); + bool RetrieveVulkanDevices(); void SetupPerGameUI(); diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui index 74f0e0b79..2f94c94bc 100644 --- a/src/yuzu/configuration/configure_graphics.ui +++ b/src/yuzu/configuration/configure_graphics.ui @@ -6,8 +6,8 @@ <rect> <x>0</x> <y>0</y> - <width>437</width> - <height>482</height> + <width>471</width> + <height>759</height> </rect> </property> <property name="windowTitle"> @@ -171,11 +171,11 @@ </widget> </item> <item> - <widget class="QCheckBox" name="accelerate_astc"> - <property name="text"> - <string>Accelerate ASTC texture decoding</string> - </property> - </widget> + <widget class="QCheckBox" name="accelerate_astc"> + <property name="text"> + <string>Accelerate ASTC texture decoding</string> + </property> + </widget> </item> <item> <widget class="QWidget" name="nvdec_emulation_widget" native="true"> @@ -438,43 +438,43 @@ </widget> </item> <item> - <widget class="QWidget" name="anti_aliasing_layout" native="true"> - <layout class="QHBoxLayout" name="horizontalLayout_7"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> + <widget class="QWidget" name="anti_aliasing_layout" native="true"> + <layout class="QHBoxLayout" name="horizontalLayout_7"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="anti_aliasing_label"> + <property name="text"> + <string>Anti-Aliasing Method:</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="anti_aliasing_combobox"> + <item> + <property name="text"> + <string>None</string> </property> - <property name="bottomMargin"> - <number>0</number> + </item> + <item> + <property name="text"> + <string>FXAA</string> </property> - <item> - <widget class="QLabel" name="anti_aliasing_label"> - <property name="text"> - <string>Anti-Aliasing Method:</string> - </property> - </widget> - </item> - <item> - <widget class="QComboBox" name="anti_aliasing_combobox"> - <item> - <property name="text"> - <string>None</string> - </property> - </item> - <item> - <property name="text"> - <string>FXAA</string> - </property> - </item> - </widget> - </item> - </layout> - </widget> + </item> + </widget> + </item> + </layout> + </widget> </item> <item> <widget class="QWidget" name="bg_layout" native="true"> @@ -574,6 +574,13 @@ </property> </spacer> </item> + <item> + <widget class="QPushButton" name="button_check_vulkan"> + <property name="text"> + <string>Check for Working Vulkan</string> + </property> + </widget> + </item> </layout> </widget> <resources/> diff --git a/src/yuzu/configuration/configure_hotkeys.cpp b/src/yuzu/configuration/configure_hotkeys.cpp index 6679e9c53..edf0893c4 100644 --- a/src/yuzu/configuration/configure_hotkeys.cpp +++ b/src/yuzu/configuration/configure_hotkeys.cpp @@ -61,14 +61,18 @@ ConfigureHotkeys::~ConfigureHotkeys() = default; void ConfigureHotkeys::Populate(const HotkeyRegistry& registry) { for (const auto& group : registry.hotkey_groups) { - auto* parent_item = new QStandardItem(group.first); + auto* parent_item = + new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(group.first))); parent_item->setEditable(false); + parent_item->setData(group.first); for (const auto& hotkey : group.second) { - auto* action = new QStandardItem(hotkey.first); + auto* action = + new QStandardItem(QCoreApplication::translate("Hotkeys", qPrintable(hotkey.first))); auto* keyseq = new QStandardItem(hotkey.second.keyseq.toString(QKeySequence::NativeText)); auto* controller_keyseq = new QStandardItem(hotkey.second.controller_keyseq); action->setEditable(false); + action->setData(hotkey.first); keyseq->setEditable(false); controller_keyseq->setEditable(false); parent_item->appendRow({action, keyseq, controller_keyseq}); @@ -93,6 +97,16 @@ void ConfigureHotkeys::RetranslateUI() { ui->retranslateUi(this); model->setHorizontalHeaderLabels({tr("Action"), tr("Hotkey"), tr("Controller Hotkey")}); + for (int key_id = 0; key_id < model->rowCount(); key_id++) { + QStandardItem* parent = model->item(key_id, 0); + parent->setText( + QCoreApplication::translate("Hotkeys", qPrintable(parent->data().toString()))); + for (int key_column_id = 0; key_column_id < parent->rowCount(); key_column_id++) { + QStandardItem* action = parent->child(key_column_id, name_column); + action->setText( + QCoreApplication::translate("Hotkeys", qPrintable(action->data().toString()))); + } + } } void ConfigureHotkeys::Configure(QModelIndex index) { @@ -273,10 +287,10 @@ void ConfigureHotkeys::ApplyConfiguration(HotkeyRegistry& registry) { const QStandardItem* controller_keyseq = parent->child(key_column_id, controller_column); for (auto& [group, sub_actions] : registry.hotkey_groups) { - if (group != parent->text()) + if (group != parent->data()) continue; for (auto& [action_name, hotkey] : sub_actions) { - if (action_name != action->text()) + if (action_name != action->data()) continue; hotkey.keyseq = QKeySequence(keyseq->text()); hotkey.controller_keyseq = controller_keyseq->text(); diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index 27559c37b..c313b0919 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -151,6 +151,8 @@ void ConfigureMotionTouch::ConnectEvents() { &ConfigureMotionTouch::OnConfigureTouchCalibration); connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this, &ConfigureMotionTouch::OnConfigureTouchFromButton); + connect(ui->buttonBox, &QDialogButtonBox::accepted, this, + &ConfigureMotionTouch::ApplyConfiguration); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] { if (CanCloseDialog()) { reject(); diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui index c75a84ae4..0237fae54 100644 --- a/src/yuzu/configuration/configure_motion_touch.ui +++ b/src/yuzu/configuration/configure_motion_touch.ui @@ -293,22 +293,5 @@ </layout> </widget> <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>ConfigureMotionTouch</receiver> - <slot>ApplyConfiguration()</slot> - <hints> - <hint type="sourcelabel"> - <x>20</x> - <y>20</y> - </hint> - <hint type="destinationlabel"> - <x>20</x> - <y>20</y> - </hint> - </hints> - </connection> - </connections> + <connections/> </ui> diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 19aa589f9..ecebb0fb7 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -130,8 +130,7 @@ void ConfigureSystem::ApplyConfiguration() { // Guard if during game and set to game-specific value if (Settings::values.rng_seed.UsingGlobal()) { if (ui->rng_seed_checkbox->isChecked()) { - Settings::values.rng_seed.SetValue( - ui->rng_seed_edit->text().toULongLong(nullptr, 16)); + Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16)); } else { Settings::values.rng_seed.SetValue(std::nullopt); } @@ -142,8 +141,7 @@ void ConfigureSystem::ApplyConfiguration() { case ConfigurationShared::CheckState::Off: Settings::values.rng_seed.SetGlobal(false); if (ui->rng_seed_checkbox->isChecked()) { - Settings::values.rng_seed.SetValue( - ui->rng_seed_edit->text().toULongLong(nullptr, 16)); + Settings::values.rng_seed.SetValue(ui->rng_seed_edit->text().toUInt(nullptr, 16)); } else { Settings::values.rng_seed.SetValue(std::nullopt); } diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 4a6d74a7e..d13530a5b 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -483,7 +483,7 @@ void GameList::DonePopulating(const QStringList& watch_list) { // Also artificially caps the watcher to a certain number of directories constexpr int LIMIT_WATCH_DIRECTORIES = 5000; constexpr int SLICE_SIZE = 25; - int len = std::min(watch_list.length(), LIMIT_WATCH_DIRECTORIES); + int len = std::min(static_cast<int>(watch_list.size()), LIMIT_WATCH_DIRECTORIES); for (int i = 0; i < len; i += SLICE_SIZE) { watcher->addPaths(watch_list.mid(i, i + SLICE_SIZE)); QCoreApplication::processEvents(); diff --git a/src/yuzu/loading_screen.cpp b/src/yuzu/loading_screen.cpp index edfb946a8..e273744fd 100644 --- a/src/yuzu/loading_screen.cpp +++ b/src/yuzu/loading_screen.cpp @@ -183,7 +183,7 @@ void LoadingScreen::OnLoadProgress(VideoCore::LoadCallbackStage stage, std::size void LoadingScreen::paintEvent(QPaintEvent* event) { QStyleOption opt; - opt.init(this); + opt.initFrom(this); QPainter p(this); style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); QWidget::paintEvent(event); diff --git a/src/yuzu/loading_screen.h b/src/yuzu/loading_screen.h index 7c960ee72..17045595d 100644 --- a/src/yuzu/loading_screen.h +++ b/src/yuzu/loading_screen.h @@ -7,6 +7,7 @@ #include <memory> #include <QString> #include <QWidget> +#include <QtGlobal> #if !QT_CONFIG(movie) #define YUZU_QT_MOVIE_MISSING 1 @@ -88,4 +89,6 @@ private: std::size_t slow_shader_first_value = 0; }; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) Q_DECLARE_METATYPE(VideoCore::LoadCallbackStage); +#endif diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 8e7f91a0f..4d7634184 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -115,6 +115,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual #include "video_core/shader_notify.h" #include "yuzu/about_dialog.h" #include "yuzu/bootmanager.h" +#include "yuzu/check_vulkan.h" #include "yuzu/compatdb.h" #include "yuzu/compatibility_list.h" #include "yuzu/configuration/config.h" @@ -198,6 +199,31 @@ static void RemoveCachedContents() { Common::FS::RemoveDirRecursively(offline_system_data); } +static QString PrettyProductName() { +#ifdef _WIN32 + // After Windows 10 Version 2004, Microsoft decided to switch to a different notation: 20H2 + // With that notation change they changed the registry key used to denote the current version + QSettings windows_registry( + QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"), + QSettings::NativeFormat); + const QString release_id = windows_registry.value(QStringLiteral("ReleaseId")).toString(); + if (release_id == QStringLiteral("2009")) { + const u32 current_build = windows_registry.value(QStringLiteral("CurrentBuild")).toUInt(); + const QString display_version = + windows_registry.value(QStringLiteral("DisplayVersion")).toString(); + const u32 ubr = windows_registry.value(QStringLiteral("UBR")).toUInt(); + u32 version = 10; + if (current_build >= 22000) { + version = 11; + } + return QStringLiteral("Windows %1 Version %2 (Build %3.%4)") + .arg(QString::number(version), display_version, QString::number(current_build), + QString::number(ubr)); + } +#endif + return QSysInfo::prettyProductName(); +} + GMainWindow::GMainWindow() : ui{std::make_unique<Ui::MainWindow>()}, system{std::make_unique<Core::System>()}, input_subsystem{std::make_shared<InputCommon::InputSubsystem>()}, @@ -259,7 +285,7 @@ GMainWindow::GMainWindow() } LOG_INFO(Frontend, "Host CPU: {}", cpu_string); #endif - LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString()); + LOG_INFO(Frontend, "Host OS: {}", PrettyProductName().toStdString()); LOG_INFO(Frontend, "Host RAM: {:.2f} GiB", Common::GetMemInfo().TotalPhysicalMemory / f64{1_GiB}); LOG_INFO(Frontend, "Host Swap: {:.2f} GiB", Common::GetMemInfo().TotalSwapMemory / f64{1_GiB}); @@ -297,6 +323,23 @@ GMainWindow::GMainWindow() MigrateConfigFiles(); + if (!CheckVulkan()) { + config->Save(); + + QMessageBox::warning( + this, tr("Broken Vulkan Installation Detected"), + tr("Vulkan initialization failed on the previous boot.<br><br>Click <a " + "href='https://yuzu-emu.org/wiki/faq/" + "#yuzu-starts-with-the-error-broken-vulkan-installation-detected'>here for " + "instructions to fix the issue</a>.")); + } + if (UISettings::values.has_broken_vulkan) { + Settings::values.renderer_backend = Settings::RendererBackend::OpenGL; + + renderer_status_button->setDisabled(true); + renderer_status_button->setChecked(false); + } + #if defined(HAVE_SDL2) && !defined(_WIN32) SDL_InitSubSystem(SDL_INIT_VIDEO); // SDL disables the screen saver by default, and setting the hint @@ -827,12 +870,11 @@ void GMainWindow::InitializeWidgets() { // Setup Dock button dock_status_button = new QPushButton(); - dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton")); + dock_status_button->setObjectName(QStringLiteral("DockingStatusBarButton")); dock_status_button->setFocusPolicy(Qt::NoFocus); connect(dock_status_button, &QPushButton::clicked, this, &GMainWindow::OnToggleDockedMode); - dock_status_button->setText(tr("DOCK")); dock_status_button->setCheckable(true); - dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); + UpdateDockedButton(); statusBar()->insertPermanentWidget(0, dock_status_button); gpu_accuracy_button = new QPushButton(); @@ -1567,7 +1609,7 @@ void GMainWindow::ShutdownGame() { emu_speed_label->setVisible(false); game_fps_label->setVisible(false); emu_frametime_label->setVisible(false); - renderer_status_button->setEnabled(true); + renderer_status_button->setEnabled(!UISettings::values.has_broken_vulkan); game_path.clear(); @@ -1587,7 +1629,7 @@ void GMainWindow::StoreRecentFile(const QString& filename) { void GMainWindow::UpdateRecentFiles() { const int num_recent_files = - std::min(UISettings::values.recent_files.size(), max_recent_files_item); + std::min(static_cast<int>(UISettings::values.recent_files.size()), max_recent_files_item); for (int i = 0; i < num_recent_files; i++) { const QString text = QStringLiteral("&%1. %2").arg(i + 1).arg( @@ -2777,6 +2819,10 @@ void GMainWindow::OnConfigure() { mouse_hide_timer.start(); } + if (!UISettings::values.has_broken_vulkan) { + renderer_status_button->setEnabled(!emulation_running); + } + UpdateStatusButtons(); controller_dialog->refreshConfiguration(); } @@ -2862,7 +2908,7 @@ void GMainWindow::OnToggleDockedMode() { } Settings::values.use_docked_mode.SetValue(!is_docked); - dock_status_button->setChecked(!is_docked); + UpdateDockedButton(); OnDockedModeChanged(is_docked, !is_docked, *system); } @@ -3228,6 +3274,12 @@ void GMainWindow::UpdateGPUAccuracyButton() { } } +void GMainWindow::UpdateDockedButton() { + const bool is_docked = Settings::values.use_docked_mode.GetValue(); + dock_status_button->setChecked(is_docked); + dock_status_button->setText(is_docked ? tr("DOCKED") : tr("HANDHELD")); +} + void GMainWindow::UpdateFilterText() { const auto filter = Settings::values.scaling_filter.GetValue(); switch (filter) { @@ -3271,10 +3323,10 @@ void GMainWindow::UpdateAAText() { } void GMainWindow::UpdateStatusButtons() { - dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue()); renderer_status_button->setChecked(Settings::values.renderer_backend.GetValue() == Settings::RendererBackend::Vulkan); UpdateGPUAccuracyButton(); + UpdateDockedButton(); UpdateFilterText(); UpdateAAText(); } @@ -3325,7 +3377,7 @@ void GMainWindow::CenterMouseCursor() { const int center_x = render_window->width() / 2; const int center_y = render_window->height() / 2; - QCursor::setPos(mapToGlobal({center_x, center_y})); + QCursor::setPos(mapToGlobal(QPoint{center_x, center_y})); } void GMainWindow::OnMouseActivity() { diff --git a/src/yuzu/main.h b/src/yuzu/main.h index b399e9b01..600647015 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h @@ -320,6 +320,7 @@ private: void MigrateConfigFiles(); void UpdateWindowTitle(std::string_view title_name = {}, std::string_view title_version = {}, std::string_view gpu_vendor = {}); + void UpdateDockedButton(); void UpdateFilterText(); void UpdateAAText(); void UpdateStatusBar(); diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index 15ba9ea17..c64d87ace 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -77,6 +77,8 @@ struct Values { Settings::BasicSetting<bool> pause_when_in_background{false, "pauseWhenInBackground"}; Settings::BasicSetting<bool> mute_when_in_background{false, "muteWhenInBackground"}; Settings::BasicSetting<bool> hide_mouse{true, "hideInactiveMouse"}; + // Set when Vulkan is known to crash the application + Settings::BasicSetting<bool> has_broken_vulkan{false, "has_broken_vulkan"}; Settings::BasicSetting<bool> select_user_on_boot{false, "select_user_on_boot"}; diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index f34d6b728..39063e32b 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -218,7 +218,7 @@ cpuopt_unsafe_ignore_global_monitor = [Renderer] # Which backend API to use. -# 0 (default): OpenGL, 1: Vulkan +# 0: OpenGL, 1 (default): Vulkan backend = # Enable graphics API debugging mode. |