diff options
47 files changed, 1155 insertions, 379 deletions
diff --git a/src/audio_core/time_stretch.cpp b/src/audio_core/time_stretch.cpp index d72d67994..cee8b12dd 100644 --- a/src/audio_core/time_stretch.cpp +++ b/src/audio_core/time_stretch.cpp | |||
| @@ -10,8 +10,7 @@ | |||
| 10 | 10 | ||
| 11 | namespace AudioCore { | 11 | namespace AudioCore { |
| 12 | 12 | ||
| 13 | TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) | 13 | TimeStretcher::TimeStretcher(u32 sample_rate, u32 channel_count) : m_sample_rate{sample_rate} { |
| 14 | : m_sample_rate(sample_rate), m_channel_count(channel_count) { | ||
| 15 | m_sound_touch.setChannels(channel_count); | 14 | m_sound_touch.setChannels(channel_count); |
| 16 | m_sound_touch.setSampleRate(sample_rate); | 15 | m_sound_touch.setSampleRate(sample_rate); |
| 17 | m_sound_touch.setPitch(1.0); | 16 | m_sound_touch.setPitch(1.0); |
diff --git a/src/audio_core/time_stretch.h b/src/audio_core/time_stretch.h index decd760f1..bb2270b96 100644 --- a/src/audio_core/time_stretch.h +++ b/src/audio_core/time_stretch.h | |||
| @@ -27,7 +27,6 @@ public: | |||
| 27 | 27 | ||
| 28 | private: | 28 | private: |
| 29 | u32 m_sample_rate; | 29 | u32 m_sample_rate; |
| 30 | u32 m_channel_count; | ||
| 31 | soundtouch::SoundTouch m_sound_touch; | 30 | soundtouch::SoundTouch m_sound_touch; |
| 32 | double m_stretch_ratio = 1.0; | 31 | double m_stretch_ratio = 1.0; |
| 33 | }; | 32 | }; |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 9f5918851..6d5218465 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -196,6 +196,7 @@ void FileBackend::Write(const Entry& entry) { | |||
| 196 | SUB(Service, NFP) \ | 196 | SUB(Service, NFP) \ |
| 197 | SUB(Service, NIFM) \ | 197 | SUB(Service, NIFM) \ |
| 198 | SUB(Service, NIM) \ | 198 | SUB(Service, NIM) \ |
| 199 | SUB(Service, NPNS) \ | ||
| 199 | SUB(Service, NS) \ | 200 | SUB(Service, NS) \ |
| 200 | SUB(Service, NVDRV) \ | 201 | SUB(Service, NVDRV) \ |
| 201 | SUB(Service, PCIE) \ | 202 | SUB(Service, PCIE) \ |
| @@ -204,10 +205,12 @@ void FileBackend::Write(const Entry& entry) { | |||
| 204 | SUB(Service, PM) \ | 205 | SUB(Service, PM) \ |
| 205 | SUB(Service, PREPO) \ | 206 | SUB(Service, PREPO) \ |
| 206 | SUB(Service, PSC) \ | 207 | SUB(Service, PSC) \ |
| 208 | SUB(Service, PSM) \ | ||
| 207 | SUB(Service, SET) \ | 209 | SUB(Service, SET) \ |
| 208 | SUB(Service, SM) \ | 210 | SUB(Service, SM) \ |
| 209 | SUB(Service, SPL) \ | 211 | SUB(Service, SPL) \ |
| 210 | SUB(Service, SSL) \ | 212 | SUB(Service, SSL) \ |
| 213 | SUB(Service, TCAP) \ | ||
| 211 | SUB(Service, Time) \ | 214 | SUB(Service, Time) \ |
| 212 | SUB(Service, USB) \ | 215 | SUB(Service, USB) \ |
| 213 | SUB(Service, VI) \ | 216 | SUB(Service, VI) \ |
diff --git a/src/common/logging/log.h b/src/common/logging/log.h index c9161155a..d4ec31ec3 100644 --- a/src/common/logging/log.h +++ b/src/common/logging/log.h | |||
| @@ -83,6 +83,7 @@ enum class Class : ClassType { | |||
| 83 | Service_NFP, ///< The NFP service | 83 | Service_NFP, ///< The NFP service |
| 84 | Service_NIFM, ///< The NIFM (Network interface) service | 84 | Service_NIFM, ///< The NIFM (Network interface) service |
| 85 | Service_NIM, ///< The NIM service | 85 | Service_NIM, ///< The NIM service |
| 86 | Service_NPNS, ///< The NPNS service | ||
| 86 | Service_NS, ///< The NS services | 87 | Service_NS, ///< The NS services |
| 87 | Service_NVDRV, ///< The NVDRV (Nvidia driver) service | 88 | Service_NVDRV, ///< The NVDRV (Nvidia driver) service |
| 88 | Service_PCIE, ///< The PCIe service | 89 | Service_PCIE, ///< The PCIe service |
| @@ -96,6 +97,7 @@ enum class Class : ClassType { | |||
| 96 | Service_SM, ///< The SM (Service manager) service | 97 | Service_SM, ///< The SM (Service manager) service |
| 97 | Service_SPL, ///< The SPL service | 98 | Service_SPL, ///< The SPL service |
| 98 | Service_SSL, ///< The SSL service | 99 | Service_SSL, ///< The SSL service |
| 100 | Service_TCAP, ///< The TCAP service. | ||
| 99 | Service_Time, ///< The time service | 101 | Service_Time, ///< The time service |
| 100 | Service_USB, ///< The USB (Universal Serial Bus) service | 102 | Service_USB, ///< The USB (Universal Serial Bus) service |
| 101 | Service_VI, ///< The VI (Video interface) service | 103 | Service_VI, ///< The VI (Video interface) service |
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index fd0786068..fefc3c747 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -713,7 +713,6 @@ void KeyManager::DeriveBase() { | |||
| 713 | 713 | ||
| 714 | const auto sbk = GetKey(S128KeyType::SecureBoot); | 714 | const auto sbk = GetKey(S128KeyType::SecureBoot); |
| 715 | const auto tsec = GetKey(S128KeyType::TSEC); | 715 | const auto tsec = GetKey(S128KeyType::TSEC); |
| 716 | const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master)); | ||
| 717 | 716 | ||
| 718 | for (size_t i = 0; i < revisions.size(); ++i) { | 717 | for (size_t i = 0; i < revisions.size(); ++i) { |
| 719 | if (!revisions[i]) | 718 | if (!revisions[i]) |
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp index 554eae9bc..999939d5a 100644 --- a/src/core/file_sys/ips_layer.cpp +++ b/src/core/file_sys/ips_layer.cpp | |||
| @@ -99,7 +99,7 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) { | |||
| 99 | u16 rle_size{}; | 99 | u16 rle_size{}; |
| 100 | if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) | 100 | if (ips->ReadObject(&rle_size, offset) != sizeof(u16)) |
| 101 | return nullptr; | 101 | return nullptr; |
| 102 | rle_size = Common::swap16(data_size); | 102 | rle_size = Common::swap16(rle_size); |
| 103 | offset += sizeof(u16); | 103 | offset += sizeof(u16); |
| 104 | 104 | ||
| 105 | const auto data = ips->ReadByte(offset++); | 105 | const auto data = ips->ReadByte(offset++); |
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index bfe50da73..3824c74e0 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp | |||
| @@ -472,10 +472,14 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t blo | |||
| 472 | std::vector<u8> temp(std::min(block_size, src->GetSize())); | 472 | std::vector<u8> temp(std::min(block_size, src->GetSize())); |
| 473 | for (std::size_t i = 0; i < src->GetSize(); i += block_size) { | 473 | for (std::size_t i = 0; i < src->GetSize(); i += block_size) { |
| 474 | const auto read = std::min(block_size, src->GetSize() - i); | 474 | const auto read = std::min(block_size, src->GetSize() - i); |
| 475 | const auto block = src->Read(temp.data(), read, i); | ||
| 476 | 475 | ||
| 477 | if (dest->Write(temp.data(), read, i) != read) | 476 | if (src->Read(temp.data(), read, i) != read) { |
| 478 | return false; | 477 | return false; |
| 478 | } | ||
| 479 | |||
| 480 | if (dest->Write(temp.data(), read, i) != read) { | ||
| 481 | return false; | ||
| 482 | } | ||
| 479 | } | 483 | } |
| 480 | 484 | ||
| 481 | return true; | 485 | return true; |
diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 419f45896..ed84197b3 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h | |||
| @@ -14,11 +14,6 @@ namespace IPC { | |||
| 14 | /// Size of the command buffer area, in 32-bit words. | 14 | /// Size of the command buffer area, in 32-bit words. |
| 15 | constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); | 15 | constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32); |
| 16 | 16 | ||
| 17 | // These errors are commonly returned by invalid IPC translations, so alias them here for | ||
| 18 | // convenience. | ||
| 19 | // TODO(yuriks): These will probably go away once translation is implemented inside the kernel. | ||
| 20 | constexpr auto ERR_INVALID_HANDLE = Kernel::ERR_INVALID_HANDLE_OS; | ||
| 21 | |||
| 22 | enum class ControlCommand : u32 { | 17 | enum class ControlCommand : u32 { |
| 23 | ConvertSessionToDomain = 0, | 18 | ConvertSessionToDomain = 0, |
| 24 | ConvertDomainToSession = 1, | 19 | ConvertDomainToSession = 1, |
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index 885259618..ee698c8a7 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h | |||
| @@ -10,11 +10,6 @@ namespace Kernel { | |||
| 10 | 10 | ||
| 11 | namespace ErrCodes { | 11 | namespace ErrCodes { |
| 12 | enum { | 12 | enum { |
| 13 | // TODO(Subv): Remove these 3DS OS error codes. | ||
| 14 | SessionClosedByRemote = 26, | ||
| 15 | NoPendingSessions = 35, | ||
| 16 | InvalidBufferDescriptor = 48, | ||
| 17 | |||
| 18 | // Confirmed Switch OS error codes | 13 | // Confirmed Switch OS error codes |
| 19 | MaxConnectionsReached = 7, | 14 | MaxConnectionsReached = 7, |
| 20 | InvalidSize = 101, | 15 | InvalidSize = 101, |
| @@ -26,6 +21,7 @@ enum { | |||
| 26 | InvalidThreadPriority = 112, | 21 | InvalidThreadPriority = 112, |
| 27 | InvalidProcessorId = 113, | 22 | InvalidProcessorId = 113, |
| 28 | InvalidHandle = 114, | 23 | InvalidHandle = 114, |
| 24 | InvalidPointer = 115, | ||
| 29 | InvalidCombination = 116, | 25 | InvalidCombination = 116, |
| 30 | Timeout = 117, | 26 | Timeout = 117, |
| 31 | SynchronizationCanceled = 118, | 27 | SynchronizationCanceled = 118, |
| @@ -33,6 +29,7 @@ enum { | |||
| 33 | InvalidEnumValue = 120, | 29 | InvalidEnumValue = 120, |
| 34 | NoSuchEntry = 121, | 30 | NoSuchEntry = 121, |
| 35 | AlreadyRegistered = 122, | 31 | AlreadyRegistered = 122, |
| 32 | SessionClosed = 123, | ||
| 36 | InvalidState = 125, | 33 | InvalidState = 125, |
| 37 | ResourceLimitExceeded = 132, | 34 | ResourceLimitExceeded = 132, |
| 38 | }; | 35 | }; |
| @@ -41,18 +38,14 @@ enum { | |||
| 41 | // WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always | 38 | // WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always |
| 42 | // double check that the code matches before re-using the constant. | 39 | // double check that the code matches before re-using the constant. |
| 43 | 40 | ||
| 44 | // TODO(bunnei): Replace -1 with correct errors for Switch OS | ||
| 45 | constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); | 41 | constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull); |
| 46 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(-1); | 42 | constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed); |
| 47 | constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); | 43 | constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge); |
| 48 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, | 44 | constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel, |
| 49 | ErrCodes::MaxConnectionsReached); | 45 | ErrCodes::MaxConnectionsReached); |
| 50 | constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); | 46 | constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue); |
| 51 | constexpr ResultCode ERR_INVALID_ENUM_VALUE_FND(-1); | ||
| 52 | constexpr ResultCode ERR_INVALID_COMBINATION(-1); | ||
| 53 | constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, | 47 | constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel, |
| 54 | ErrCodes::InvalidCombination); | 48 | ErrCodes::InvalidCombination); |
| 55 | constexpr ResultCode ERR_OUT_OF_MEMORY(-1); | ||
| 56 | constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); | 49 | constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress); |
| 57 | constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); | 50 | constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState); |
| 58 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, | 51 | constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel, |
| @@ -65,14 +58,8 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::Alrea | |||
| 65 | constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); | 58 | constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState); |
| 66 | constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, | 59 | constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel, |
| 67 | ErrCodes::InvalidThreadPriority); | 60 | ErrCodes::InvalidThreadPriority); |
| 68 | constexpr ResultCode ERR_INVALID_POINTER(-1); | 61 | constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer); |
| 69 | constexpr ResultCode ERR_INVALID_OBJECT_ADDR(-1); | ||
| 70 | constexpr ResultCode ERR_NOT_AUTHORIZED(-1); | ||
| 71 | /// Alternate code returned instead of ERR_INVALID_HANDLE in some code paths. | ||
| 72 | constexpr ResultCode ERR_INVALID_HANDLE_OS(-1); | ||
| 73 | constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); | 62 | constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry); |
| 74 | constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); | 63 | constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout); |
| 75 | /// Returned when Accept() is called on a port with no sessions to be accepted. | ||
| 76 | constexpr ResultCode ERR_NO_PENDING_SESSIONS(-1); | ||
| 77 | 64 | ||
| 78 | } // namespace Kernel | 65 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 073dd5a7d..420218d59 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -232,6 +232,12 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) { | |||
| 232 | MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); | 232 | MapSegment(module_.CodeSegment(), VMAPermission::ReadExecute, MemoryState::CodeStatic); |
| 233 | MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); | 233 | MapSegment(module_.RODataSegment(), VMAPermission::Read, MemoryState::CodeMutable); |
| 234 | MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); | 234 | MapSegment(module_.DataSegment(), VMAPermission::ReadWrite, MemoryState::CodeMutable); |
| 235 | |||
| 236 | // Clear instruction cache in CPU JIT | ||
| 237 | Core::System::GetInstance().ArmInterface(0).ClearInstructionCache(); | ||
| 238 | Core::System::GetInstance().ArmInterface(1).ClearInstructionCache(); | ||
| 239 | Core::System::GetInstance().ArmInterface(2).ClearInstructionCache(); | ||
| 240 | Core::System::GetInstance().ArmInterface(3).ClearInstructionCache(); | ||
| 235 | } | 241 | } |
| 236 | 242 | ||
| 237 | ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { | 243 | ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { |
diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 3792e3e18..d6ceeb2da 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp | |||
| @@ -18,7 +18,7 @@ ServerPort::~ServerPort() = default; | |||
| 18 | 18 | ||
| 19 | ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { | 19 | ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { |
| 20 | if (pending_sessions.empty()) { | 20 | if (pending_sessions.empty()) { |
| 21 | return ERR_NO_PENDING_SESSIONS; | 21 | return ERR_NOT_FOUND; |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | auto session = std::move(pending_sessions.back()); | 24 | auto session = std::move(pending_sessions.back()); |
| @@ -28,7 +28,7 @@ ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { | |||
| 28 | 28 | ||
| 29 | bool ServerPort::ShouldWait(Thread* thread) const { | 29 | bool ServerPort::ShouldWait(Thread* thread) const { |
| 30 | // If there are no pending sessions, we wait until a new one is added. | 30 | // If there are no pending sessions, we wait until a new one is added. |
| 31 | return pending_sessions.size() == 0; | 31 | return pending_sessions.empty(); |
| 32 | } | 32 | } |
| 33 | 33 | ||
| 34 | void ServerPort::Acquire(Thread* thread) { | 34 | void ServerPort::Acquire(Thread* thread) { |
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index d061e6155..a016a86b6 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp | |||
| @@ -80,20 +80,19 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet( | |||
| 80 | 80 | ||
| 81 | ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, | 81 | ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions, |
| 82 | MemoryPermission other_permissions) { | 82 | MemoryPermission other_permissions) { |
| 83 | 83 | const MemoryPermission own_other_permissions = | |
| 84 | MemoryPermission own_other_permissions = | ||
| 85 | target_process == owner_process ? this->permissions : this->other_permissions; | 84 | target_process == owner_process ? this->permissions : this->other_permissions; |
| 86 | 85 | ||
| 87 | // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare | 86 | // Automatically allocated memory blocks can only be mapped with other_permissions = DontCare |
| 88 | if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { | 87 | if (base_address == 0 && other_permissions != MemoryPermission::DontCare) { |
| 89 | return ERR_INVALID_COMBINATION; | 88 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 90 | } | 89 | } |
| 91 | 90 | ||
| 92 | // Error out if the requested permissions don't match what the creator process allows. | 91 | // Error out if the requested permissions don't match what the creator process allows. |
| 93 | if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { | 92 | if (static_cast<u32>(permissions) & ~static_cast<u32>(own_other_permissions)) { |
| 94 | LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", | 93 | LOG_ERROR(Kernel, "cannot map id={}, address=0x{:X} name={}, permissions don't match", |
| 95 | GetObjectId(), address, name); | 94 | GetObjectId(), address, name); |
| 96 | return ERR_INVALID_COMBINATION; | 95 | return ERR_INVALID_MEMORY_PERMISSIONS; |
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | // Error out if the provided permissions are not compatible with what the creator process needs. | 98 | // Error out if the provided permissions are not compatible with what the creator process needs. |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 9a783d524..a5302d924 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -594,16 +594,17 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) { | |||
| 594 | } | 594 | } |
| 595 | 595 | ||
| 596 | const auto* const current_process = Core::CurrentProcess(); | 596 | const auto* const current_process = Core::CurrentProcess(); |
| 597 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | ||
| 598 | if (!thread) { | ||
| 599 | return ERR_INVALID_HANDLE; | ||
| 600 | } | ||
| 601 | 597 | ||
| 602 | // Note: The kernel uses the current process's resource limit instead of | 598 | // Note: The kernel uses the current process's resource limit instead of |
| 603 | // the one from the thread owner's resource limit. | 599 | // the one from the thread owner's resource limit. |
| 604 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); | 600 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); |
| 605 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { | 601 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { |
| 606 | return ERR_NOT_AUTHORIZED; | 602 | return ERR_INVALID_THREAD_PRIORITY; |
| 603 | } | ||
| 604 | |||
| 605 | SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle); | ||
| 606 | if (!thread) { | ||
| 607 | return ERR_INVALID_HANDLE; | ||
| 607 | } | 608 | } |
| 608 | 609 | ||
| 609 | thread->SetPriority(priority); | 610 | thread->SetPriority(priority); |
| @@ -745,7 +746,7 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V | |||
| 745 | auto* const current_process = Core::CurrentProcess(); | 746 | auto* const current_process = Core::CurrentProcess(); |
| 746 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); | 747 | const ResourceLimit& resource_limit = current_process->GetResourceLimit(); |
| 747 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { | 748 | if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) { |
| 748 | return ERR_NOT_AUTHORIZED; | 749 | return ERR_INVALID_THREAD_PRIORITY; |
| 749 | } | 750 | } |
| 750 | 751 | ||
| 751 | if (processor_id == THREADPROCESSORID_DEFAULT) { | 752 | if (processor_id == THREADPROCESSORID_DEFAULT) { |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index e1a34eef1..1a92c8f70 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -143,6 +143,26 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me | |||
| 143 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); | 143 | return MakeResult<VMAHandle>(MergeAdjacent(vma_handle)); |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | ResultVal<VAddr> VMManager::FindFreeRegion(u64 size) const { | ||
| 147 | // Find the first Free VMA. | ||
| 148 | const VAddr base = GetASLRRegionBaseAddress(); | ||
| 149 | const VMAHandle vma_handle = std::find_if(vma_map.begin(), vma_map.end(), [&](const auto& vma) { | ||
| 150 | if (vma.second.type != VMAType::Free) | ||
| 151 | return false; | ||
| 152 | |||
| 153 | const VAddr vma_end = vma.second.base + vma.second.size; | ||
| 154 | return vma_end > base && vma_end >= base + size; | ||
| 155 | }); | ||
| 156 | |||
| 157 | if (vma_handle == vma_map.end()) { | ||
| 158 | // TODO(Subv): Find the correct error code here. | ||
| 159 | return ResultCode(-1); | ||
| 160 | } | ||
| 161 | |||
| 162 | const VAddr target = std::max(base, vma_handle->second.base); | ||
| 163 | return MakeResult<VAddr>(target); | ||
| 164 | } | ||
| 165 | |||
| 146 | ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, | 166 | ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u64 size, |
| 147 | MemoryState state, | 167 | MemoryState state, |
| 148 | Memory::MemoryHookPointer mmio_handler) { | 168 | Memory::MemoryHookPointer mmio_handler) { |
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 84c890224..2447cbb8f 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -158,6 +158,14 @@ public: | |||
| 158 | ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); | 158 | ResultVal<VMAHandle> MapBackingMemory(VAddr target, u8* memory, u64 size, MemoryState state); |
| 159 | 159 | ||
| 160 | /** | 160 | /** |
| 161 | * Finds the first free address that can hold a region of the desired size. | ||
| 162 | * | ||
| 163 | * @param size Size of the desired region. | ||
| 164 | * @return The found free address. | ||
| 165 | */ | ||
| 166 | ResultVal<VAddr> FindFreeRegion(u64 size) const; | ||
| 167 | |||
| 168 | /** | ||
| 161 | * Maps a memory-mapped IO region at a given address. | 169 | * Maps a memory-mapped IO region at a given address. |
| 162 | * | 170 | * |
| 163 | * @param target The guest address to start the mapping at. | 171 | * @param target The guest address to start the mapping at. |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index e61748ca3..c6437a671 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -2,9 +2,13 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 5 | #include <array> | 6 | #include <array> |
| 7 | #include "common/common_paths.h" | ||
| 6 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/file_util.h" | ||
| 7 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/string_util.h" | ||
| 8 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 9 | #include "core/core_timing.h" | 13 | #include "core/core_timing.h" |
| 10 | #include "core/hle/ipc_helpers.h" | 14 | #include "core/hle/ipc_helpers.h" |
| @@ -16,6 +20,7 @@ | |||
| 16 | #include "core/hle/service/acc/profile_manager.h" | 20 | #include "core/hle/service/acc/profile_manager.h" |
| 17 | 21 | ||
| 18 | namespace Service::Account { | 22 | namespace Service::Account { |
| 23 | |||
| 19 | // TODO: RE this structure | 24 | // TODO: RE this structure |
| 20 | struct UserData { | 25 | struct UserData { |
| 21 | INSERT_PADDING_WORDS(1); | 26 | INSERT_PADDING_WORDS(1); |
| @@ -27,6 +32,29 @@ struct UserData { | |||
| 27 | }; | 32 | }; |
| 28 | static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); | 33 | static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size"); |
| 29 | 34 | ||
| 35 | // Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg | ||
| 36 | // used as a backup should the one on disk not exist | ||
| 37 | constexpr u32 backup_jpeg_size = 107; | ||
| 38 | constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{ | ||
| 39 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, | ||
| 40 | 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, | ||
| 41 | 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, | ||
| 42 | 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, | ||
| 43 | 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, | ||
| 44 | 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, | ||
| 45 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||
| 46 | }}; | ||
| 47 | |||
| 48 | static std::string GetImagePath(UUID uuid) { | ||
| 49 | return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 50 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | ||
| 51 | } | ||
| 52 | |||
| 53 | static constexpr u32 SanitizeJPEGSize(std::size_t size) { | ||
| 54 | constexpr std::size_t max_jpeg_image_size = 0x20000; | ||
| 55 | return static_cast<u32>(std::min(size, max_jpeg_image_size)); | ||
| 56 | } | ||
| 57 | |||
| 30 | class IProfile final : public ServiceFramework<IProfile> { | 58 | class IProfile final : public ServiceFramework<IProfile> { |
| 31 | public: | 59 | public: |
| 32 | explicit IProfile(UUID user_id, ProfileManager& profile_manager) | 60 | explicit IProfile(UUID user_id, ProfileManager& profile_manager) |
| @@ -73,32 +101,42 @@ private: | |||
| 73 | } | 101 | } |
| 74 | 102 | ||
| 75 | void LoadImage(Kernel::HLERequestContext& ctx) { | 103 | void LoadImage(Kernel::HLERequestContext& ctx) { |
| 76 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 104 | LOG_DEBUG(Service_ACC, "called"); |
| 77 | // smallest jpeg https://github.com/mathiasbynens/small/blob/master/jpeg.jpg | 105 | |
| 78 | // TODO(mailwl): load actual profile image from disk, width 256px, max size 0x20000 | ||
| 79 | constexpr u32 jpeg_size = 107; | ||
| 80 | static constexpr std::array<u8, jpeg_size> jpeg{ | ||
| 81 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, | ||
| 82 | 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, | ||
| 83 | 0x08, 0x06, 0x06, 0x05, 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, | ||
| 84 | 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, | ||
| 85 | 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, | ||
| 86 | 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x11, 0x00, | ||
| 87 | 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, 0x01, 0x01, | ||
| 88 | 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||
| 89 | }; | ||
| 90 | ctx.WriteBuffer(jpeg); | ||
| 91 | IPC::ResponseBuilder rb{ctx, 3}; | 106 | IPC::ResponseBuilder rb{ctx, 3}; |
| 92 | rb.Push(RESULT_SUCCESS); | 107 | rb.Push(RESULT_SUCCESS); |
| 93 | rb.Push<u32>(jpeg_size); | 108 | |
| 109 | const FileUtil::IOFile image(GetImagePath(user_id), "rb"); | ||
| 110 | if (!image.IsOpen()) { | ||
| 111 | LOG_WARNING(Service_ACC, | ||
| 112 | "Failed to load user provided image! Falling back to built-in backup..."); | ||
| 113 | ctx.WriteBuffer(backup_jpeg); | ||
| 114 | rb.Push<u32>(backup_jpeg_size); | ||
| 115 | return; | ||
| 116 | } | ||
| 117 | |||
| 118 | const u32 size = SanitizeJPEGSize(image.GetSize()); | ||
| 119 | std::vector<u8> buffer(size); | ||
| 120 | image.ReadBytes(buffer.data(), buffer.size()); | ||
| 121 | |||
| 122 | ctx.WriteBuffer(buffer.data(), buffer.size()); | ||
| 123 | rb.Push<u32>(size); | ||
| 94 | } | 124 | } |
| 95 | 125 | ||
| 96 | void GetImageSize(Kernel::HLERequestContext& ctx) { | 126 | void GetImageSize(Kernel::HLERequestContext& ctx) { |
| 97 | LOG_WARNING(Service_ACC, "(STUBBED) called"); | 127 | LOG_DEBUG(Service_ACC, "called"); |
| 98 | constexpr u32 jpeg_size = 107; | ||
| 99 | IPC::ResponseBuilder rb{ctx, 3}; | 128 | IPC::ResponseBuilder rb{ctx, 3}; |
| 100 | rb.Push(RESULT_SUCCESS); | 129 | rb.Push(RESULT_SUCCESS); |
| 101 | rb.Push<u32>(jpeg_size); | 130 | |
| 131 | const FileUtil::IOFile image(GetImagePath(user_id), "rb"); | ||
| 132 | |||
| 133 | if (!image.IsOpen()) { | ||
| 134 | LOG_WARNING(Service_ACC, | ||
| 135 | "Failed to load user provided image! Falling back to built-in backup..."); | ||
| 136 | rb.Push<u32>(backup_jpeg_size); | ||
| 137 | } else { | ||
| 138 | rb.Push<u32>(SanitizeJPEGSize(image.GetSize())); | ||
| 139 | } | ||
| 102 | } | 140 | } |
| 103 | 141 | ||
| 104 | const ProfileManager& profile_manager; | 142 | const ProfileManager& profile_manager; |
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index bcb3475db..3cac1b4ff 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp | |||
| @@ -3,41 +3,66 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <random> | 5 | #include <random> |
| 6 | #include <boost/optional.hpp> | 6 | |
| 7 | #include "common/file_util.h" | ||
| 7 | #include "core/hle/service/acc/profile_manager.h" | 8 | #include "core/hle/service/acc/profile_manager.h" |
| 8 | #include "core/settings.h" | 9 | #include "core/settings.h" |
| 9 | 10 | ||
| 10 | namespace Service::Account { | 11 | namespace Service::Account { |
| 12 | |||
| 13 | struct UserRaw { | ||
| 14 | UUID uuid; | ||
| 15 | UUID uuid2; | ||
| 16 | u64 timestamp; | ||
| 17 | ProfileUsername username; | ||
| 18 | INSERT_PADDING_BYTES(0x80); | ||
| 19 | }; | ||
| 20 | static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size."); | ||
| 21 | |||
| 22 | struct ProfileDataRaw { | ||
| 23 | INSERT_PADDING_BYTES(0x10); | ||
| 24 | std::array<UserRaw, MAX_USERS> users; | ||
| 25 | }; | ||
| 26 | static_assert(sizeof(ProfileDataRaw) == 0x650, "ProfileDataRaw has incorrect size."); | ||
| 27 | |||
| 11 | // TODO(ogniK): Get actual error codes | 28 | // TODO(ogniK): Get actual error codes |
| 12 | constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1); | 29 | constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, -1); |
| 13 | constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2); | 30 | constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, -2); |
| 14 | constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); | 31 | constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); |
| 15 | 32 | ||
| 16 | const UUID& UUID::Generate() { | 33 | constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/"; |
| 34 | |||
| 35 | UUID UUID::Generate() { | ||
| 17 | std::random_device device; | 36 | std::random_device device; |
| 18 | std::mt19937 gen(device()); | 37 | std::mt19937 gen(device()); |
| 19 | std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); | 38 | std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); |
| 20 | uuid[0] = distribution(gen); | 39 | return UUID{distribution(gen), distribution(gen)}; |
| 21 | uuid[1] = distribution(gen); | ||
| 22 | return *this; | ||
| 23 | } | 40 | } |
| 24 | 41 | ||
| 25 | ProfileManager::ProfileManager() { | 42 | ProfileManager::ProfileManager() { |
| 26 | // TODO(ogniK): Create the default user we have for now until loading/saving users is added | 43 | ParseUserSaveFile(); |
| 27 | auto user_uuid = UUID{1, 0}; | 44 | |
| 28 | ASSERT(CreateNewUser(user_uuid, Settings::values.username).IsSuccess()); | 45 | if (user_count == 0) |
| 29 | OpenUser(user_uuid); | 46 | CreateNewUser(UUID::Generate(), "yuzu"); |
| 47 | |||
| 48 | auto current = std::clamp<int>(Settings::values.current_user, 0, MAX_USERS - 1); | ||
| 49 | if (UserExistsIndex(current)) | ||
| 50 | current = 0; | ||
| 51 | |||
| 52 | OpenUser(*GetUser(current)); | ||
| 30 | } | 53 | } |
| 31 | 54 | ||
| 32 | ProfileManager::~ProfileManager() = default; | 55 | ProfileManager::~ProfileManager() { |
| 56 | WriteUserSaveFile(); | ||
| 57 | } | ||
| 33 | 58 | ||
| 34 | /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the | 59 | /// After a users creation it needs to be "registered" to the system. AddToProfiles handles the |
| 35 | /// internal management of the users profiles | 60 | /// internal management of the users profiles |
| 36 | boost::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& user) { | 61 | std::optional<std::size_t> ProfileManager::AddToProfiles(const ProfileInfo& profile) { |
| 37 | if (user_count >= MAX_USERS) { | 62 | if (user_count >= MAX_USERS) { |
| 38 | return boost::none; | 63 | return {}; |
| 39 | } | 64 | } |
| 40 | profiles[user_count] = user; | 65 | profiles[user_count] = profile; |
| 41 | return user_count++; | 66 | return user_count++; |
| 42 | } | 67 | } |
| 43 | 68 | ||
| @@ -56,7 +81,7 @@ bool ProfileManager::RemoveProfileAtIndex(std::size_t index) { | |||
| 56 | 81 | ||
| 57 | /// Helper function to register a user to the system | 82 | /// Helper function to register a user to the system |
| 58 | ResultCode ProfileManager::AddUser(const ProfileInfo& user) { | 83 | ResultCode ProfileManager::AddUser(const ProfileInfo& user) { |
| 59 | if (AddToProfiles(user) == boost::none) { | 84 | if (!AddToProfiles(user)) { |
| 60 | return ERROR_TOO_MANY_USERS; | 85 | return ERROR_TOO_MANY_USERS; |
| 61 | } | 86 | } |
| 62 | return RESULT_SUCCESS; | 87 | return RESULT_SUCCESS; |
| @@ -101,31 +126,40 @@ ResultCode ProfileManager::CreateNewUser(UUID uuid, const std::string& username) | |||
| 101 | return CreateNewUser(uuid, username_output); | 126 | return CreateNewUser(uuid, username_output); |
| 102 | } | 127 | } |
| 103 | 128 | ||
| 129 | std::optional<UUID> ProfileManager::GetUser(std::size_t index) const { | ||
| 130 | if (index >= MAX_USERS) { | ||
| 131 | return {}; | ||
| 132 | } | ||
| 133 | |||
| 134 | return profiles[index].user_uuid; | ||
| 135 | } | ||
| 136 | |||
| 104 | /// Returns a users profile index based on their user id. | 137 | /// Returns a users profile index based on their user id. |
| 105 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { | 138 | std::optional<std::size_t> ProfileManager::GetUserIndex(const UUID& uuid) const { |
| 106 | if (!uuid) { | 139 | if (!uuid) { |
| 107 | return boost::none; | 140 | return {}; |
| 108 | } | 141 | } |
| 109 | auto iter = std::find_if(profiles.begin(), profiles.end(), | 142 | |
| 110 | [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); | 143 | const auto iter = std::find_if(profiles.begin(), profiles.end(), |
| 144 | [&uuid](const ProfileInfo& p) { return p.user_uuid == uuid; }); | ||
| 111 | if (iter == profiles.end()) { | 145 | if (iter == profiles.end()) { |
| 112 | return boost::none; | 146 | return {}; |
| 113 | } | 147 | } |
| 148 | |||
| 114 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); | 149 | return static_cast<std::size_t>(std::distance(profiles.begin(), iter)); |
| 115 | } | 150 | } |
| 116 | 151 | ||
| 117 | /// Returns a users profile index based on their profile | 152 | /// Returns a users profile index based on their profile |
| 118 | boost::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { | 153 | std::optional<std::size_t> ProfileManager::GetUserIndex(const ProfileInfo& user) const { |
| 119 | return GetUserIndex(user.user_uuid); | 154 | return GetUserIndex(user.user_uuid); |
| 120 | } | 155 | } |
| 121 | 156 | ||
| 122 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* | 157 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* |
| 123 | bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, | 158 | bool ProfileManager::GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const { |
| 124 | ProfileBase& profile) const { | 159 | if (!index || index >= MAX_USERS) { |
| 125 | if (index == boost::none || index >= MAX_USERS) { | ||
| 126 | return false; | 160 | return false; |
| 127 | } | 161 | } |
| 128 | const auto& prof_info = profiles[index.get()]; | 162 | const auto& prof_info = profiles[*index]; |
| 129 | profile.user_uuid = prof_info.user_uuid; | 163 | profile.user_uuid = prof_info.user_uuid; |
| 130 | profile.username = prof_info.username; | 164 | profile.username = prof_info.username; |
| 131 | profile.timestamp = prof_info.creation_time; | 165 | profile.timestamp = prof_info.creation_time; |
| @@ -134,7 +168,7 @@ bool ProfileManager::GetProfileBase(boost::optional<std::size_t> index, | |||
| 134 | 168 | ||
| 135 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* | 169 | /// Returns the data structure used by the switch when GetProfileBase is called on acc:* |
| 136 | bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { | 170 | bool ProfileManager::GetProfileBase(UUID uuid, ProfileBase& profile) const { |
| 137 | auto idx = GetUserIndex(uuid); | 171 | const auto idx = GetUserIndex(uuid); |
| 138 | return GetProfileBase(idx, profile); | 172 | return GetProfileBase(idx, profile); |
| 139 | } | 173 | } |
| 140 | 174 | ||
| @@ -161,26 +195,34 @@ std::size_t ProfileManager::GetOpenUserCount() const { | |||
| 161 | 195 | ||
| 162 | /// Checks if a user id exists in our profile manager | 196 | /// Checks if a user id exists in our profile manager |
| 163 | bool ProfileManager::UserExists(UUID uuid) const { | 197 | bool ProfileManager::UserExists(UUID uuid) const { |
| 164 | return (GetUserIndex(uuid) != boost::none); | 198 | return GetUserIndex(uuid) != std::nullopt; |
| 199 | } | ||
| 200 | |||
| 201 | bool ProfileManager::UserExistsIndex(std::size_t index) const { | ||
| 202 | if (index >= MAX_USERS) | ||
| 203 | return false; | ||
| 204 | return profiles[index].user_uuid.uuid != INVALID_UUID; | ||
| 165 | } | 205 | } |
| 166 | 206 | ||
| 167 | /// Opens a specific user | 207 | /// Opens a specific user |
| 168 | void ProfileManager::OpenUser(UUID uuid) { | 208 | void ProfileManager::OpenUser(UUID uuid) { |
| 169 | auto idx = GetUserIndex(uuid); | 209 | const auto idx = GetUserIndex(uuid); |
| 170 | if (idx == boost::none) { | 210 | if (!idx) { |
| 171 | return; | 211 | return; |
| 172 | } | 212 | } |
| 173 | profiles[idx.get()].is_open = true; | 213 | |
| 214 | profiles[*idx].is_open = true; | ||
| 174 | last_opened_user = uuid; | 215 | last_opened_user = uuid; |
| 175 | } | 216 | } |
| 176 | 217 | ||
| 177 | /// Closes a specific user | 218 | /// Closes a specific user |
| 178 | void ProfileManager::CloseUser(UUID uuid) { | 219 | void ProfileManager::CloseUser(UUID uuid) { |
| 179 | auto idx = GetUserIndex(uuid); | 220 | const auto idx = GetUserIndex(uuid); |
| 180 | if (idx == boost::none) { | 221 | if (!idx) { |
| 181 | return; | 222 | return; |
| 182 | } | 223 | } |
| 183 | profiles[idx.get()].is_open = false; | 224 | |
| 225 | profiles[*idx].is_open = false; | ||
| 184 | } | 226 | } |
| 185 | 227 | ||
| 186 | /// Gets all valid user ids on the system | 228 | /// Gets all valid user ids on the system |
| @@ -210,10 +252,10 @@ UUID ProfileManager::GetLastOpenedUser() const { | |||
| 210 | } | 252 | } |
| 211 | 253 | ||
| 212 | /// Return the users profile base and the unknown arbitary data. | 254 | /// Return the users profile base and the unknown arbitary data. |
| 213 | bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, | 255 | bool ProfileManager::GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, |
| 214 | ProfileData& data) const { | 256 | ProfileData& data) const { |
| 215 | if (GetProfileBase(index, profile)) { | 257 | if (GetProfileBase(index, profile)) { |
| 216 | data = profiles[index.get()].data; | 258 | data = profiles[*index].data; |
| 217 | return true; | 259 | return true; |
| 218 | } | 260 | } |
| 219 | return false; | 261 | return false; |
| @@ -222,7 +264,7 @@ bool ProfileManager::GetProfileBaseAndData(boost::optional<std::size_t> index, P | |||
| 222 | /// Return the users profile base and the unknown arbitary data. | 264 | /// Return the users profile base and the unknown arbitary data. |
| 223 | bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, | 265 | bool ProfileManager::GetProfileBaseAndData(UUID uuid, ProfileBase& profile, |
| 224 | ProfileData& data) const { | 266 | ProfileData& data) const { |
| 225 | auto idx = GetUserIndex(uuid); | 267 | const auto idx = GetUserIndex(uuid); |
| 226 | return GetProfileBaseAndData(idx, profile, data); | 268 | return GetProfileBaseAndData(idx, profile, data); |
| 227 | } | 269 | } |
| 228 | 270 | ||
| @@ -239,4 +281,96 @@ bool ProfileManager::CanSystemRegisterUser() const { | |||
| 239 | // emulate qlaunch. Update this to dynamically change. | 281 | // emulate qlaunch. Update this to dynamically change. |
| 240 | } | 282 | } |
| 241 | 283 | ||
| 284 | bool ProfileManager::RemoveUser(UUID uuid) { | ||
| 285 | const auto index = GetUserIndex(uuid); | ||
| 286 | if (!index) { | ||
| 287 | return false; | ||
| 288 | } | ||
| 289 | |||
| 290 | profiles[*index] = ProfileInfo{}; | ||
| 291 | std::stable_partition(profiles.begin(), profiles.end(), | ||
| 292 | [](const ProfileInfo& profile) { return profile.user_uuid; }); | ||
| 293 | return true; | ||
| 294 | } | ||
| 295 | |||
| 296 | bool ProfileManager::SetProfileBase(UUID uuid, const ProfileBase& profile_new) { | ||
| 297 | const auto index = GetUserIndex(uuid); | ||
| 298 | if (!index || profile_new.user_uuid == UUID(INVALID_UUID)) { | ||
| 299 | return false; | ||
| 300 | } | ||
| 301 | |||
| 302 | auto& profile = profiles[*index]; | ||
| 303 | profile.user_uuid = profile_new.user_uuid; | ||
| 304 | profile.username = profile_new.username; | ||
| 305 | profile.creation_time = profile_new.timestamp; | ||
| 306 | |||
| 307 | return true; | ||
| 308 | } | ||
| 309 | |||
| 310 | void ProfileManager::ParseUserSaveFile() { | ||
| 311 | FileUtil::IOFile save(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 312 | ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", | ||
| 313 | "rb"); | ||
| 314 | |||
| 315 | if (!save.IsOpen()) { | ||
| 316 | LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " | ||
| 317 | "user 'yuzu' with random UUID."); | ||
| 318 | return; | ||
| 319 | } | ||
| 320 | |||
| 321 | ProfileDataRaw data; | ||
| 322 | if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) { | ||
| 323 | LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user " | ||
| 324 | "'yuzu' with random UUID."); | ||
| 325 | return; | ||
| 326 | } | ||
| 327 | |||
| 328 | for (std::size_t i = 0; i < MAX_USERS; ++i) { | ||
| 329 | const auto& user = data.users[i]; | ||
| 330 | |||
| 331 | if (user.uuid != UUID(INVALID_UUID)) | ||
| 332 | AddUser({user.uuid, user.username, user.timestamp, {}, false}); | ||
| 333 | } | ||
| 334 | |||
| 335 | std::stable_partition(profiles.begin(), profiles.end(), | ||
| 336 | [](const ProfileInfo& profile) { return profile.user_uuid; }); | ||
| 337 | } | ||
| 338 | |||
| 339 | void ProfileManager::WriteUserSaveFile() { | ||
| 340 | ProfileDataRaw raw{}; | ||
| 341 | |||
| 342 | for (std::size_t i = 0; i < MAX_USERS; ++i) { | ||
| 343 | raw.users[i].username = profiles[i].username; | ||
| 344 | raw.users[i].uuid2 = profiles[i].user_uuid; | ||
| 345 | raw.users[i].uuid = profiles[i].user_uuid; | ||
| 346 | raw.users[i].timestamp = profiles[i].creation_time; | ||
| 347 | } | ||
| 348 | |||
| 349 | const auto raw_path = | ||
| 350 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; | ||
| 351 | if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) | ||
| 352 | FileUtil::Delete(raw_path); | ||
| 353 | |||
| 354 | const auto path = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 355 | ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat"; | ||
| 356 | |||
| 357 | if (!FileUtil::CreateFullPath(path)) { | ||
| 358 | LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " | ||
| 359 | "nand/system/save/8000000000000010/su/avators to mitigate this " | ||
| 360 | "issue."); | ||
| 361 | return; | ||
| 362 | } | ||
| 363 | |||
| 364 | FileUtil::IOFile save(path, "wb"); | ||
| 365 | |||
| 366 | if (!save.IsOpen()) { | ||
| 367 | LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " | ||
| 368 | "made in current session will be saved."); | ||
| 369 | return; | ||
| 370 | } | ||
| 371 | |||
| 372 | save.Resize(sizeof(ProfileDataRaw)); | ||
| 373 | save.WriteBytes(&raw, sizeof(ProfileDataRaw)); | ||
| 374 | } | ||
| 375 | |||
| 242 | }; // namespace Service::Account | 376 | }; // namespace Service::Account |
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index bffd4cf4d..1cd2e51b2 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h | |||
| @@ -5,8 +5,8 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <optional> | ||
| 8 | 9 | ||
| 9 | #include "boost/optional.hpp" | ||
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/swap.h" | 11 | #include "common/swap.h" |
| 12 | #include "core/hle/result.h" | 12 | #include "core/hle/result.h" |
| @@ -36,7 +36,7 @@ struct UUID { | |||
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | // TODO(ogniK): Properly generate uuids based on RFC-4122 | 38 | // TODO(ogniK): Properly generate uuids based on RFC-4122 |
| 39 | const UUID& Generate(); | 39 | static UUID Generate(); |
| 40 | 40 | ||
| 41 | // Set the UUID to {0,0} to be considered an invalid user | 41 | // Set the UUID to {0,0} to be considered an invalid user |
| 42 | void Invalidate() { | 42 | void Invalidate() { |
| @@ -45,6 +45,15 @@ struct UUID { | |||
| 45 | std::string Format() const { | 45 | std::string Format() const { |
| 46 | return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); | 46 | return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]); |
| 47 | } | 47 | } |
| 48 | |||
| 49 | std::string FormatSwitch() const { | ||
| 50 | std::array<u8, 16> s{}; | ||
| 51 | std::memcpy(s.data(), uuid.data(), sizeof(u128)); | ||
| 52 | return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{" | ||
| 53 | ":02x}{:02x}{:02x}{:02x}{:02x}", | ||
| 54 | s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11], | ||
| 55 | s[12], s[13], s[14], s[15]); | ||
| 56 | } | ||
| 48 | }; | 57 | }; |
| 49 | static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); | 58 | static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); |
| 50 | 59 | ||
| @@ -81,18 +90,19 @@ static_assert(sizeof(ProfileBase) == 0x38, "ProfileBase is an invalid size"); | |||
| 81 | /// objects | 90 | /// objects |
| 82 | class ProfileManager { | 91 | class ProfileManager { |
| 83 | public: | 92 | public: |
| 84 | ProfileManager(); // TODO(ogniK): Load from system save | 93 | ProfileManager(); |
| 85 | ~ProfileManager(); | 94 | ~ProfileManager(); |
| 86 | 95 | ||
| 87 | ResultCode AddUser(const ProfileInfo& user); | 96 | ResultCode AddUser(const ProfileInfo& user); |
| 88 | ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); | 97 | ResultCode CreateNewUser(UUID uuid, const ProfileUsername& username); |
| 89 | ResultCode CreateNewUser(UUID uuid, const std::string& username); | 98 | ResultCode CreateNewUser(UUID uuid, const std::string& username); |
| 90 | boost::optional<std::size_t> GetUserIndex(const UUID& uuid) const; | 99 | std::optional<UUID> GetUser(std::size_t index) const; |
| 91 | boost::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; | 100 | std::optional<std::size_t> GetUserIndex(const UUID& uuid) const; |
| 92 | bool GetProfileBase(boost::optional<std::size_t> index, ProfileBase& profile) const; | 101 | std::optional<std::size_t> GetUserIndex(const ProfileInfo& user) const; |
| 102 | bool GetProfileBase(std::optional<std::size_t> index, ProfileBase& profile) const; | ||
| 93 | bool GetProfileBase(UUID uuid, ProfileBase& profile) const; | 103 | bool GetProfileBase(UUID uuid, ProfileBase& profile) const; |
| 94 | bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; | 104 | bool GetProfileBase(const ProfileInfo& user, ProfileBase& profile) const; |
| 95 | bool GetProfileBaseAndData(boost::optional<std::size_t> index, ProfileBase& profile, | 105 | bool GetProfileBaseAndData(std::optional<std::size_t> index, ProfileBase& profile, |
| 96 | ProfileData& data) const; | 106 | ProfileData& data) const; |
| 97 | bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; | 107 | bool GetProfileBaseAndData(UUID uuid, ProfileBase& profile, ProfileData& data) const; |
| 98 | bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, | 108 | bool GetProfileBaseAndData(const ProfileInfo& user, ProfileBase& profile, |
| @@ -100,6 +110,7 @@ public: | |||
| 100 | std::size_t GetUserCount() const; | 110 | std::size_t GetUserCount() const; |
| 101 | std::size_t GetOpenUserCount() const; | 111 | std::size_t GetOpenUserCount() const; |
| 102 | bool UserExists(UUID uuid) const; | 112 | bool UserExists(UUID uuid) const; |
| 113 | bool UserExistsIndex(std::size_t index) const; | ||
| 103 | void OpenUser(UUID uuid); | 114 | void OpenUser(UUID uuid); |
| 104 | void CloseUser(UUID uuid); | 115 | void CloseUser(UUID uuid); |
| 105 | UserIDArray GetOpenUsers() const; | 116 | UserIDArray GetOpenUsers() const; |
| @@ -108,11 +119,17 @@ public: | |||
| 108 | 119 | ||
| 109 | bool CanSystemRegisterUser() const; | 120 | bool CanSystemRegisterUser() const; |
| 110 | 121 | ||
| 122 | bool RemoveUser(UUID uuid); | ||
| 123 | bool SetProfileBase(UUID uuid, const ProfileBase& profile_new); | ||
| 124 | |||
| 111 | private: | 125 | private: |
| 126 | void ParseUserSaveFile(); | ||
| 127 | void WriteUserSaveFile(); | ||
| 128 | std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); | ||
| 129 | bool RemoveProfileAtIndex(std::size_t index); | ||
| 130 | |||
| 112 | std::array<ProfileInfo, MAX_USERS> profiles{}; | 131 | std::array<ProfileInfo, MAX_USERS> profiles{}; |
| 113 | std::size_t user_count = 0; | 132 | std::size_t user_count = 0; |
| 114 | boost::optional<std::size_t> AddToProfiles(const ProfileInfo& profile); | ||
| 115 | bool RemoveProfileAtIndex(std::size_t index); | ||
| 116 | UUID last_opened_user{INVALID_UUID}; | 133 | UUID last_opened_user{INVALID_UUID}; |
| 117 | }; | 134 | }; |
| 118 | 135 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index ecf72ae24..59aafd616 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -4,11 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <cinttypes> | 6 | #include <cinttypes> |
| 7 | #include <cstring> | ||
| 7 | #include <stack> | 8 | #include <stack> |
| 8 | #include "core/core.h" | 9 | #include "core/core.h" |
| 9 | #include "core/hle/ipc_helpers.h" | 10 | #include "core/hle/ipc_helpers.h" |
| 10 | #include "core/hle/kernel/event.h" | 11 | #include "core/hle/kernel/event.h" |
| 11 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/hle/service/acc/profile_manager.h" | ||
| 12 | #include "core/hle/service/am/am.h" | 14 | #include "core/hle/service/am/am.h" |
| 13 | #include "core/hle/service/am/applet_ae.h" | 15 | #include "core/hle/service/am/applet_ae.h" |
| 14 | #include "core/hle/service/am/applet_oe.h" | 16 | #include "core/hle/service/am/applet_oe.h" |
| @@ -26,6 +28,16 @@ | |||
| 26 | 28 | ||
| 27 | namespace Service::AM { | 29 | namespace Service::AM { |
| 28 | 30 | ||
| 31 | constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA; | ||
| 32 | |||
| 33 | struct LaunchParameters { | ||
| 34 | u32_le magic; | ||
| 35 | u32_le is_account_selected; | ||
| 36 | u128 current_user; | ||
| 37 | INSERT_PADDING_BYTES(0x70); | ||
| 38 | }; | ||
| 39 | static_assert(sizeof(LaunchParameters) == 0x88); | ||
| 40 | |||
| 29 | IWindowController::IWindowController() : ServiceFramework("IWindowController") { | 41 | IWindowController::IWindowController() : ServiceFramework("IWindowController") { |
| 30 | // clang-format off | 42 | // clang-format off |
| 31 | static const FunctionInfo functions[] = { | 43 | static const FunctionInfo functions[] = { |
| @@ -724,20 +736,23 @@ void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx | |||
| 724 | } | 736 | } |
| 725 | 737 | ||
| 726 | void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { | 738 | void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) { |
| 727 | constexpr std::array<u8, 0x88> data{{ | 739 | LaunchParameters params{}; |
| 728 | 0xca, 0x97, 0x94, 0xc7, // Magic | ||
| 729 | 1, 0, 0, 0, // IsAccountSelected (bool) | ||
| 730 | 1, 0, 0, 0, // User Id (word 0) | ||
| 731 | 0, 0, 0, 0, // User Id (word 1) | ||
| 732 | 0, 0, 0, 0, // User Id (word 2) | ||
| 733 | 0, 0, 0, 0 // User Id (word 3) | ||
| 734 | }}; | ||
| 735 | 740 | ||
| 736 | std::vector<u8> buffer(data.begin(), data.end()); | 741 | params.magic = POP_LAUNCH_PARAMETER_MAGIC; |
| 742 | params.is_account_selected = 1; | ||
| 743 | |||
| 744 | Account::ProfileManager profile_manager{}; | ||
| 745 | const auto uuid = profile_manager.GetUser(Settings::values.current_user); | ||
| 746 | ASSERT(uuid != std::nullopt); | ||
| 747 | params.current_user = uuid->uuid; | ||
| 737 | 748 | ||
| 738 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 749 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 739 | 750 | ||
| 740 | rb.Push(RESULT_SUCCESS); | 751 | rb.Push(RESULT_SUCCESS); |
| 752 | |||
| 753 | std::vector<u8> buffer(sizeof(LaunchParameters)); | ||
| 754 | std::memcpy(buffer.data(), ¶ms, buffer.size()); | ||
| 755 | |||
| 741 | rb.PushIpcInterface<AM::IStorage>(buffer); | 756 | rb.PushIpcInterface<AM::IStorage>(buffer); |
| 742 | 757 | ||
| 743 | LOG_DEBUG(Service_AM, "called"); | 758 | LOG_DEBUG(Service_AM, "called"); |
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 428069df2..54305cf05 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -24,8 +24,8 @@ namespace Service::AOC { | |||
| 24 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; | 24 | constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; |
| 25 | constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; | 25 | constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000; |
| 26 | 26 | ||
| 27 | static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { | 27 | static bool CheckAOCTitleIDMatchesBase(u64 title_id, u64 base) { |
| 28 | return (aoc & DLC_BASE_TITLE_ID_MASK) == base; | 28 | return (title_id & DLC_BASE_TITLE_ID_MASK) == base; |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | static std::vector<u64> AccumulateAOCTitleIDs() { | 31 | static std::vector<u64> AccumulateAOCTitleIDs() { |
| @@ -74,7 +74,7 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 74 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 74 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 75 | rb.Push<u32>(static_cast<u32>( | 75 | rb.Push<u32>(static_cast<u32>( |
| 76 | std::count_if(add_on_content.begin(), add_on_content.end(), | 76 | std::count_if(add_on_content.begin(), add_on_content.end(), |
| 77 | [¤t](u64 tid) { return (tid & DLC_BASE_TITLE_ID_MASK) == current; }))); | 77 | [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { | 80 | void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp index b06e65a77..4b4d1324f 100644 --- a/src/core/hle/service/hid/controllers/npad.cpp +++ b/src/core/hle/service/hid/controllers/npad.cpp | |||
| @@ -108,9 +108,10 @@ void Controller_NPad::OnInit() { | |||
| 108 | styleset_changed_event = | 108 | styleset_changed_event = |
| 109 | Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); | 109 | Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged"); |
| 110 | 110 | ||
| 111 | if (!IsControllerActivated()) | 111 | if (!IsControllerActivated()) { |
| 112 | return; | 112 | return; |
| 113 | std::size_t controller{}; | 113 | } |
| 114 | |||
| 114 | if (style.raw == 0) { | 115 | if (style.raw == 0) { |
| 115 | // We want to support all controllers | 116 | // We want to support all controllers |
| 116 | style.handheld.Assign(1); | 117 | style.handheld.Assign(1); |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index ec32faf15..d607d985e 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -3,9 +3,13 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <fmt/format.h> | ||
| 6 | 7 | ||
| 8 | #include "core/hle/ipc_helpers.h" | ||
| 9 | #include "core/hle/kernel/process.h" | ||
| 7 | #include "core/hle/service/ldr/ldr.h" | 10 | #include "core/hle/service/ldr/ldr.h" |
| 8 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 12 | #include "core/loader/nro.h" | ||
| 9 | 13 | ||
| 10 | namespace Service::LDR { | 14 | namespace Service::LDR { |
| 11 | 15 | ||
| @@ -59,16 +63,58 @@ public: | |||
| 59 | explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { | 63 | explicit RelocatableObject() : ServiceFramework{"ldr:ro"} { |
| 60 | // clang-format off | 64 | // clang-format off |
| 61 | static const FunctionInfo functions[] = { | 65 | static const FunctionInfo functions[] = { |
| 62 | {0, nullptr, "LoadNro"}, | 66 | {0, &RelocatableObject::LoadNro, "LoadNro"}, |
| 63 | {1, nullptr, "UnloadNro"}, | 67 | {1, nullptr, "UnloadNro"}, |
| 64 | {2, nullptr, "LoadNrr"}, | 68 | {2, &RelocatableObject::LoadNrr, "LoadNrr"}, |
| 65 | {3, nullptr, "UnloadNrr"}, | 69 | {3, nullptr, "UnloadNrr"}, |
| 66 | {4, nullptr, "Initialize"}, | 70 | {4, &RelocatableObject::Initialize, "Initialize"}, |
| 67 | }; | 71 | }; |
| 68 | // clang-format on | 72 | // clang-format on |
| 69 | 73 | ||
| 70 | RegisterHandlers(functions); | 74 | RegisterHandlers(functions); |
| 71 | } | 75 | } |
| 76 | |||
| 77 | void LoadNrr(Kernel::HLERequestContext& ctx) { | ||
| 78 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 79 | rb.Push(RESULT_SUCCESS); | ||
| 80 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 81 | } | ||
| 82 | |||
| 83 | void LoadNro(Kernel::HLERequestContext& ctx) { | ||
| 84 | IPC::RequestParser rp{ctx}; | ||
| 85 | rp.Skip(2, false); | ||
| 86 | const VAddr nro_addr{rp.Pop<VAddr>()}; | ||
| 87 | const u64 nro_size{rp.Pop<u64>()}; | ||
| 88 | const VAddr bss_addr{rp.Pop<VAddr>()}; | ||
| 89 | const u64 bss_size{rp.Pop<u64>()}; | ||
| 90 | |||
| 91 | // Read NRO data from memory | ||
| 92 | std::vector<u8> nro_data(nro_size); | ||
| 93 | Memory::ReadBlock(nro_addr, nro_data.data(), nro_size); | ||
| 94 | |||
| 95 | // Load NRO as new executable module | ||
| 96 | const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)}; | ||
| 97 | Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr); | ||
| 98 | |||
| 99 | // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party. | ||
| 100 | // It is currently missing: | ||
| 101 | // - Signature checks with LoadNRR | ||
| 102 | // - Checking if a module has already been loaded | ||
| 103 | // - Using/validating BSS, etc. params (these are used from NRO header instead) | ||
| 104 | // - Error checking | ||
| 105 | // - ...Probably other things | ||
| 106 | |||
| 107 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 108 | rb.Push(RESULT_SUCCESS); | ||
| 109 | rb.Push(addr); | ||
| 110 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 111 | } | ||
| 112 | |||
| 113 | void Initialize(Kernel::HLERequestContext& ctx) { | ||
| 114 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 115 | rb.Push(RESULT_SUCCESS); | ||
| 116 | LOG_WARNING(Service_LDR, "(STUBBED) called"); | ||
| 117 | } | ||
| 72 | }; | 118 | }; |
| 73 | 119 | ||
| 74 | void InstallInterfaces(SM::ServiceManager& sm) { | 120 | void InstallInterfaces(SM::ServiceManager& sm) { |
diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp index e7fb5a419..c489da071 100644 --- a/src/core/hle/service/usb/usb.cpp +++ b/src/core/hle/service/usb/usb.cpp | |||
| @@ -67,15 +67,15 @@ public: | |||
| 67 | explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { | 67 | explicit IClientEpSession() : ServiceFramework{"IClientEpSession"} { |
| 68 | // clang-format off | 68 | // clang-format off |
| 69 | static const FunctionInfo functions[] = { | 69 | static const FunctionInfo functions[] = { |
| 70 | {0, nullptr, "Unknown1"}, | 70 | {0, nullptr, "Open"}, |
| 71 | {1, nullptr, "Unknown2"}, | 71 | {1, nullptr, "Close"}, |
| 72 | {2, nullptr, "Unknown3"}, | 72 | {2, nullptr, "Unknown1"}, |
| 73 | {3, nullptr, "Unknown4"}, | 73 | {3, nullptr, "Populate"}, |
| 74 | {4, nullptr, "PostBufferAsync"}, | 74 | {4, nullptr, "PostBufferAsync"}, |
| 75 | {5, nullptr, "Unknown5"}, | 75 | {5, nullptr, "GetXferReport"}, |
| 76 | {6, nullptr, "Unknown6"}, | 76 | {6, nullptr, "Unknown2"}, |
| 77 | {7, nullptr, "Unknown7"}, | 77 | {7, nullptr, "Unknown3"}, |
| 78 | {8, nullptr, "Unknown8"}, | 78 | {8, nullptr, "Unknown4"}, |
| 79 | }; | 79 | }; |
| 80 | // clang-format on | 80 | // clang-format on |
| 81 | 81 | ||
| @@ -89,15 +89,15 @@ public: | |||
| 89 | // clang-format off | 89 | // clang-format off |
| 90 | static const FunctionInfo functions[] = { | 90 | static const FunctionInfo functions[] = { |
| 91 | {0, nullptr, "Unknown1"}, | 91 | {0, nullptr, "Unknown1"}, |
| 92 | {1, nullptr, "Unknown2"}, | 92 | {1, nullptr, "SetInterface"}, |
| 93 | {2, nullptr, "Unknown3"}, | 93 | {2, nullptr, "GetInterface"}, |
| 94 | {3, nullptr, "Unknown4"}, | 94 | {3, nullptr, "GetAlternateInterface"}, |
| 95 | {4, nullptr, "Unknown5"}, | 95 | {4, nullptr, "GetCurrentFrame"}, |
| 96 | {5, nullptr, "CtrlXferAsync"}, | 96 | {5, nullptr, "CtrlXferAsync"}, |
| 97 | {6, nullptr, "Unknown6"}, | 97 | {6, nullptr, "Unknown2"}, |
| 98 | {7, nullptr, "GetCtrlXferReport"}, | 98 | {7, nullptr, "GetCtrlXferReport"}, |
| 99 | {8, nullptr, "Unknown7"}, | 99 | {8, nullptr, "ResetDevice"}, |
| 100 | {9, nullptr, "GetClientEpSession"}, | 100 | {9, nullptr, "OpenUsbEp"}, |
| 101 | }; | 101 | }; |
| 102 | // clang-format on | 102 | // clang-format on |
| 103 | 103 | ||
| @@ -111,13 +111,14 @@ public: | |||
| 111 | // clang-format off | 111 | // clang-format off |
| 112 | static const FunctionInfo functions[] = { | 112 | static const FunctionInfo functions[] = { |
| 113 | {0, nullptr, "BindClientProcess"}, | 113 | {0, nullptr, "BindClientProcess"}, |
| 114 | {1, nullptr, "Unknown1"}, | 114 | {1, nullptr, "QueryAllInterfaces"}, |
| 115 | {2, nullptr, "Unknown2"}, | 115 | {2, nullptr, "QueryAvailableInterfaces"}, |
| 116 | {3, nullptr, "Unknown3"}, | 116 | {3, nullptr, "QueryAcquiredInterfaces"}, |
| 117 | {4, nullptr, "Unknown4"}, | 117 | {4, nullptr, "CreateInterfaceAvailableEvent"}, |
| 118 | {5, nullptr, "Unknown5"}, | 118 | {5, nullptr, "DestroyInterfaceAvailableEvent"}, |
| 119 | {6, nullptr, "GetInterfaceStateChangeEvent"}, | 119 | {6, nullptr, "GetInterfaceStateChangeEvent"}, |
| 120 | {7, nullptr, "GetClientIfSession"}, | 120 | {7, nullptr, "AcquireUsbIf"}, |
| 121 | {8, nullptr, "Unknown1"}, | ||
| 121 | }; | 122 | }; |
| 122 | // clang-format on | 123 | // clang-format on |
| 123 | 124 | ||
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 243b499f2..bc8e402a8 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -127,18 +127,23 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 127 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | 127 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
| 128 | } | 128 | } |
| 129 | 129 | ||
| 130 | bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { | 130 | /*static*/ bool AppLoader_NRO::LoadNro(const std::vector<u8>& data, const std::string& name, |
| 131 | // Read NSO header | 131 | VAddr load_base) { |
| 132 | NroHeader nro_header{}; | 132 | |
| 133 | if (sizeof(NroHeader) != file.ReadObject(&nro_header)) { | 133 | if (data.size() < sizeof(NroHeader)) { |
| 134 | return {}; | 134 | return {}; |
| 135 | } | 135 | } |
| 136 | |||
| 137 | // Read NSO header | ||
| 138 | NroHeader nro_header{}; | ||
| 139 | std::memcpy(&nro_header, data.data(), sizeof(NroHeader)); | ||
| 136 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { | 140 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { |
| 137 | return {}; | 141 | return {}; |
| 138 | } | 142 | } |
| 139 | 143 | ||
| 140 | // Build program image | 144 | // Build program image |
| 141 | std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size)); | 145 | std::vector<u8> program_image(PageAlignSize(nro_header.file_size)); |
| 146 | std::memcpy(program_image.data(), data.data(), program_image.size()); | ||
| 142 | if (program_image.size() != PageAlignSize(nro_header.file_size)) { | 147 | if (program_image.size() != PageAlignSize(nro_header.file_size)) { |
| 143 | return {}; | 148 | return {}; |
| 144 | } | 149 | } |
| @@ -182,11 +187,15 @@ bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { | |||
| 182 | Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); | 187 | Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); |
| 183 | 188 | ||
| 184 | // Register module with GDBStub | 189 | // Register module with GDBStub |
| 185 | GDBStub::RegisterModule(file.GetName(), load_base, load_base); | 190 | GDBStub::RegisterModule(name, load_base, load_base); |
| 186 | 191 | ||
| 187 | return true; | 192 | return true; |
| 188 | } | 193 | } |
| 189 | 194 | ||
| 195 | bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) { | ||
| 196 | return AppLoader_NRO::LoadNro(file.ReadAllBytes(), file.GetName(), load_base); | ||
| 197 | } | ||
| 198 | |||
| 190 | ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { | 199 | ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { |
| 191 | if (is_loaded) { | 200 | if (is_loaded) { |
| 192 | return ResultStatus::ErrorAlreadyLoaded; | 201 | return ResultStatus::ErrorAlreadyLoaded; |
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 50ee5a78a..3e6959302 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include <vector> | ||
| 8 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 9 | #include "core/loader/linker.h" | 10 | #include "core/loader/linker.h" |
| 10 | #include "core/loader/loader.h" | 11 | #include "core/loader/loader.h" |
| @@ -40,6 +41,8 @@ public: | |||
| 40 | ResultStatus ReadTitle(std::string& title) override; | 41 | ResultStatus ReadTitle(std::string& title) override; |
| 41 | bool IsRomFSUpdatable() const override; | 42 | bool IsRomFSUpdatable() const override; |
| 42 | 43 | ||
| 44 | static bool LoadNro(const std::vector<u8>& data, const std::string& name, VAddr load_base); | ||
| 45 | |||
| 43 | private: | 46 | private: |
| 44 | bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); | 47 | bool LoadNro(const FileSys::VfsFile& file, VAddr load_base); |
| 45 | 48 | ||
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index 7d95816fe..c716a462b 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp | |||
| @@ -74,10 +74,6 @@ double PerfStats::GetLastFrameTimeScale() { | |||
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { | 76 | void FrameLimiter::DoFrameLimiting(microseconds current_system_time_us) { |
| 77 | // Max lag caused by slow frames. Can be adjusted to compensate for too many slow frames. Higher | ||
| 78 | // values increase the time needed to recover and limit framerate again after spikes. | ||
| 79 | constexpr microseconds MAX_LAG_TIME_US = 25000us; | ||
| 80 | |||
| 81 | if (!Settings::values.use_frame_limit) { | 77 | if (!Settings::values.use_frame_limit) { |
| 82 | return; | 78 | return; |
| 83 | } | 79 | } |
diff --git a/src/core/settings.h b/src/core/settings.h index ca80718e2..b5aeff29b 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -114,7 +114,7 @@ struct Values { | |||
| 114 | // System | 114 | // System |
| 115 | bool use_docked_mode; | 115 | bool use_docked_mode; |
| 116 | bool enable_nfc; | 116 | bool enable_nfc; |
| 117 | std::string username; | 117 | int current_user; |
| 118 | int language_index; | 118 | int language_index; |
| 119 | 119 | ||
| 120 | // Controls | 120 | // Controls |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index bca014a4a..78ba29fc1 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -155,7 +155,6 @@ void Maxwell3D::ProcessQueryGet() { | |||
| 155 | ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, | 155 | ASSERT_MSG(regs.query.query_get.unit == Regs::QueryUnit::Crop, |
| 156 | "Units other than CROP are unimplemented"); | 156 | "Units other than CROP are unimplemented"); |
| 157 | 157 | ||
| 158 | u32 value = Memory::Read32(*address); | ||
| 159 | u64 result = 0; | 158 | u64 result = 0; |
| 160 | 159 | ||
| 161 | // TODO(Subv): Support the other query variables | 160 | // TODO(Subv): Support the other query variables |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 6cd08d28b..af7756266 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -79,6 +79,7 @@ union Attribute { | |||
| 79 | constexpr explicit Attribute(u64 value) : value(value) {} | 79 | constexpr explicit Attribute(u64 value) : value(value) {} |
| 80 | 80 | ||
| 81 | enum class Index : u64 { | 81 | enum class Index : u64 { |
| 82 | PointSize = 6, | ||
| 82 | Position = 7, | 83 | Position = 7, |
| 83 | Attribute_0 = 8, | 84 | Attribute_0 = 8, |
| 84 | Attribute_31 = 39, | 85 | Attribute_31 = 39, |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 9c8925383..591ec7998 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -78,6 +78,29 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 78 | } | 78 | } |
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | std::size_t SurfaceParams::InnerMemorySize(bool layer_only) const { | ||
| 82 | const u32 compression_factor{GetCompressionFactor(pixel_format)}; | ||
| 83 | const u32 bytes_per_pixel{GetBytesPerPixel(pixel_format)}; | ||
| 84 | u32 m_depth = (layer_only ? 1U : depth); | ||
| 85 | u32 m_width = std::max(1U, width / compression_factor); | ||
| 86 | u32 m_height = std::max(1U, height / compression_factor); | ||
| 87 | std::size_t size = Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, | ||
| 88 | m_depth, block_height, block_depth); | ||
| 89 | u32 m_block_height = block_height; | ||
| 90 | u32 m_block_depth = block_depth; | ||
| 91 | std::size_t block_size_bytes = 512 * block_height * block_depth; // 512 is GOB size | ||
| 92 | for (u32 i = 1; i < max_mip_level; i++) { | ||
| 93 | m_width = std::max(1U, m_width / 2); | ||
| 94 | m_height = std::max(1U, m_height / 2); | ||
| 95 | m_depth = std::max(1U, m_depth / 2); | ||
| 96 | m_block_height = std::max(1U, m_block_height / 2); | ||
| 97 | m_block_depth = std::max(1U, m_block_depth / 2); | ||
| 98 | size += Tegra::Texture::CalculateSize(is_tiled, bytes_per_pixel, m_width, m_height, m_depth, | ||
| 99 | m_block_height, m_block_depth); | ||
| 100 | } | ||
| 101 | return is_tiled ? Common::AlignUp(size, block_size_bytes) : size; | ||
| 102 | } | ||
| 103 | |||
| 81 | /*static*/ SurfaceParams SurfaceParams::CreateForTexture( | 104 | /*static*/ SurfaceParams SurfaceParams::CreateForTexture( |
| 82 | const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { | 105 | const Tegra::Texture::FullTextureInfo& config, const GLShader::SamplerEntry& entry) { |
| 83 | SurfaceParams params{}; | 106 | SurfaceParams params{}; |
| @@ -124,6 +147,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 124 | break; | 147 | break; |
| 125 | } | 148 | } |
| 126 | 149 | ||
| 150 | params.is_layered = SurfaceTargetIsLayered(params.target); | ||
| 127 | params.max_mip_level = config.tic.max_mip_level + 1; | 151 | params.max_mip_level = config.tic.max_mip_level + 1; |
| 128 | params.rt = {}; | 152 | params.rt = {}; |
| 129 | 153 | ||
| @@ -150,6 +174,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 150 | params.target = SurfaceTarget::Texture2D; | 174 | params.target = SurfaceTarget::Texture2D; |
| 151 | params.depth = 1; | 175 | params.depth = 1; |
| 152 | params.max_mip_level = 0; | 176 | params.max_mip_level = 0; |
| 177 | params.is_layered = false; | ||
| 153 | 178 | ||
| 154 | // Render target specific parameters, not used for caching | 179 | // Render target specific parameters, not used for caching |
| 155 | params.rt.index = static_cast<u32>(index); | 180 | params.rt.index = static_cast<u32>(index); |
| @@ -182,6 +207,7 @@ void SurfaceParams::InitCacheParameters(Tegra::GPUVAddr gpu_addr_) { | |||
| 182 | params.target = SurfaceTarget::Texture2D; | 207 | params.target = SurfaceTarget::Texture2D; |
| 183 | params.depth = 1; | 208 | params.depth = 1; |
| 184 | params.max_mip_level = 0; | 209 | params.max_mip_level = 0; |
| 210 | params.is_layered = false; | ||
| 185 | params.rt = {}; | 211 | params.rt = {}; |
| 186 | 212 | ||
| 187 | params.InitCacheParameters(zeta_address); | 213 | params.InitCacheParameters(zeta_address); |
| @@ -361,10 +387,11 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u32 block_depth, u32 d | |||
| 361 | } | 387 | } |
| 362 | } | 388 | } |
| 363 | 389 | ||
| 364 | static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), | 390 | using GLConversionArray = std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), |
| 365 | SurfaceParams::MaxPixelFormat> | 391 | SurfaceParams::MaxPixelFormat>; |
| 366 | morton_to_gl_fns = { | 392 | |
| 367 | // clang-format off | 393 | static constexpr GLConversionArray morton_to_gl_fns = { |
| 394 | // clang-format off | ||
| 368 | MortonCopy<true, PixelFormat::ABGR8U>, | 395 | MortonCopy<true, PixelFormat::ABGR8U>, |
| 369 | MortonCopy<true, PixelFormat::ABGR8S>, | 396 | MortonCopy<true, PixelFormat::ABGR8S>, |
| 370 | MortonCopy<true, PixelFormat::ABGR8UI>, | 397 | MortonCopy<true, PixelFormat::ABGR8UI>, |
| @@ -418,13 +445,11 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, | |||
| 418 | MortonCopy<true, PixelFormat::Z24S8>, | 445 | MortonCopy<true, PixelFormat::Z24S8>, |
| 419 | MortonCopy<true, PixelFormat::S8Z24>, | 446 | MortonCopy<true, PixelFormat::S8Z24>, |
| 420 | MortonCopy<true, PixelFormat::Z32FS8>, | 447 | MortonCopy<true, PixelFormat::Z32FS8>, |
| 421 | // clang-format on | 448 | // clang-format on |
| 422 | }; | 449 | }; |
| 423 | 450 | ||
| 424 | static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, VAddr), | 451 | static constexpr GLConversionArray gl_to_morton_fns = { |
| 425 | SurfaceParams::MaxPixelFormat> | 452 | // clang-format off |
| 426 | gl_to_morton_fns = { | ||
| 427 | // clang-format off | ||
| 428 | MortonCopy<false, PixelFormat::ABGR8U>, | 453 | MortonCopy<false, PixelFormat::ABGR8U>, |
| 429 | MortonCopy<false, PixelFormat::ABGR8S>, | 454 | MortonCopy<false, PixelFormat::ABGR8S>, |
| 430 | MortonCopy<false, PixelFormat::ABGR8UI>, | 455 | MortonCopy<false, PixelFormat::ABGR8UI>, |
| @@ -479,9 +504,35 @@ static constexpr std::array<void (*)(u32, u32, u32, u32, u32, u8*, std::size_t, | |||
| 479 | MortonCopy<false, PixelFormat::Z24S8>, | 504 | MortonCopy<false, PixelFormat::Z24S8>, |
| 480 | MortonCopy<false, PixelFormat::S8Z24>, | 505 | MortonCopy<false, PixelFormat::S8Z24>, |
| 481 | MortonCopy<false, PixelFormat::Z32FS8>, | 506 | MortonCopy<false, PixelFormat::Z32FS8>, |
| 482 | // clang-format on | 507 | // clang-format on |
| 483 | }; | 508 | }; |
| 484 | 509 | ||
| 510 | void SwizzleFunc(const GLConversionArray& functions, const SurfaceParams& params, | ||
| 511 | std::vector<u8>& gl_buffer) { | ||
| 512 | u32 depth = params.depth; | ||
| 513 | if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { | ||
| 514 | // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. | ||
| 515 | depth = 1U; | ||
| 516 | } | ||
| 517 | if (params.is_layered) { | ||
| 518 | u64 offset = 0; | ||
| 519 | u64 offset_gl = 0; | ||
| 520 | u64 layer_size = params.LayerMemorySize(); | ||
| 521 | u64 gl_size = params.LayerSizeGL(); | ||
| 522 | for (u32 i = 0; i < depth; i++) { | ||
| 523 | functions[static_cast<std::size_t>(params.pixel_format)]( | ||
| 524 | params.width, params.block_height, params.height, params.block_depth, 1, | ||
| 525 | gl_buffer.data() + offset_gl, gl_size, params.addr + offset); | ||
| 526 | offset += layer_size; | ||
| 527 | offset_gl += gl_size; | ||
| 528 | } | ||
| 529 | } else { | ||
| 530 | functions[static_cast<std::size_t>(params.pixel_format)]( | ||
| 531 | params.width, params.block_height, params.height, params.block_depth, depth, | ||
| 532 | gl_buffer.data(), gl_buffer.size(), params.addr); | ||
| 533 | } | ||
| 534 | } | ||
| 535 | |||
| 485 | static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, | 536 | static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface, |
| 486 | GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, | 537 | GLuint read_fb_handle, GLuint draw_fb_handle, GLenum src_attachment = 0, |
| 487 | GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { | 538 | GLenum dst_attachment = 0, std::size_t cubemap_face = 0) { |
| @@ -881,21 +932,10 @@ void CachedSurface::LoadGLBuffer() { | |||
| 881 | 932 | ||
| 882 | gl_buffer.resize(params.size_in_bytes_gl); | 933 | gl_buffer.resize(params.size_in_bytes_gl); |
| 883 | if (params.is_tiled) { | 934 | if (params.is_tiled) { |
| 884 | u32 depth = params.depth; | ||
| 885 | u32 block_depth = params.block_depth; | ||
| 886 | |||
| 887 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", | 935 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", |
| 888 | params.block_width, static_cast<u32>(params.target)); | 936 | params.block_width, static_cast<u32>(params.target)); |
| 889 | 937 | ||
| 890 | if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { | 938 | SwizzleFunc(morton_to_gl_fns, params, gl_buffer); |
| 891 | // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. | ||
| 892 | depth = 1U; | ||
| 893 | block_depth = 1U; | ||
| 894 | } | ||
| 895 | |||
| 896 | morton_to_gl_fns[static_cast<std::size_t>(params.pixel_format)]( | ||
| 897 | params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), | ||
| 898 | gl_buffer.size(), params.addr); | ||
| 899 | } else { | 939 | } else { |
| 900 | const auto texture_src_data{Memory::GetPointer(params.addr)}; | 940 | const auto texture_src_data{Memory::GetPointer(params.addr)}; |
| 901 | const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; | 941 | const auto texture_src_data_end{texture_src_data + params.size_in_bytes_gl}; |
| @@ -929,19 +969,10 @@ void CachedSurface::FlushGLBuffer() { | |||
| 929 | const u8* const texture_src_data = Memory::GetPointer(params.addr); | 969 | const u8* const texture_src_data = Memory::GetPointer(params.addr); |
| 930 | ASSERT(texture_src_data); | 970 | ASSERT(texture_src_data); |
| 931 | if (params.is_tiled) { | 971 | if (params.is_tiled) { |
| 932 | u32 depth = params.depth; | ||
| 933 | u32 block_depth = params.block_depth; | ||
| 934 | |||
| 935 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", | 972 | ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture type {}", |
| 936 | params.block_width, static_cast<u32>(params.target)); | 973 | params.block_width, static_cast<u32>(params.target)); |
| 937 | 974 | ||
| 938 | if (params.target == SurfaceParams::SurfaceTarget::Texture2D) { | 975 | SwizzleFunc(gl_to_morton_fns, params, gl_buffer); |
| 939 | // TODO(Blinkhawk): Eliminate this condition once all texture types are implemented. | ||
| 940 | depth = 1U; | ||
| 941 | } | ||
| 942 | gl_to_morton_fns[static_cast<size_t>(params.pixel_format)]( | ||
| 943 | params.width, params.block_height, params.height, block_depth, depth, gl_buffer.data(), | ||
| 944 | gl_buffer.size(), GetAddr()); | ||
| 945 | } else { | 976 | } else { |
| 946 | std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); | 977 | std::memcpy(Memory::GetPointer(GetAddr()), gl_buffer.data(), GetSizeInBytes()); |
| 947 | } | 978 | } |
| @@ -1179,7 +1210,7 @@ void RasterizerCacheOpenGL::AccurateCopySurface(const Surface& src_surface, | |||
| 1179 | const Surface& dst_surface) { | 1210 | const Surface& dst_surface) { |
| 1180 | const auto& src_params{src_surface->GetSurfaceParams()}; | 1211 | const auto& src_params{src_surface->GetSurfaceParams()}; |
| 1181 | const auto& dst_params{dst_surface->GetSurfaceParams()}; | 1212 | const auto& dst_params{dst_surface->GetSurfaceParams()}; |
| 1182 | FlushRegion(src_params.addr, dst_params.size_in_bytes); | 1213 | FlushRegion(src_params.addr, dst_params.MemorySize()); |
| 1183 | LoadSurface(dst_surface); | 1214 | LoadSurface(dst_surface); |
| 1184 | } | 1215 | } |
| 1185 | 1216 | ||
| @@ -1221,44 +1252,10 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, | |||
| 1221 | CopySurface(old_surface, new_surface, copy_pbo.handle); | 1252 | CopySurface(old_surface, new_surface, copy_pbo.handle); |
| 1222 | } | 1253 | } |
| 1223 | break; | 1254 | break; |
| 1255 | case SurfaceParams::SurfaceTarget::TextureCubemap: | ||
| 1224 | case SurfaceParams::SurfaceTarget::Texture3D: | 1256 | case SurfaceParams::SurfaceTarget::Texture3D: |
| 1225 | AccurateCopySurface(old_surface, new_surface); | 1257 | AccurateCopySurface(old_surface, new_surface); |
| 1226 | break; | 1258 | break; |
| 1227 | case SurfaceParams::SurfaceTarget::TextureCubemap: { | ||
| 1228 | if (old_params.rt.array_mode != 1) { | ||
| 1229 | // TODO(bunnei): This is used by Breath of the Wild, I'm not sure how to implement this | ||
| 1230 | // yet (array rendering used as a cubemap texture). | ||
| 1231 | LOG_CRITICAL(HW_GPU, "Unhandled rendertarget array_mode {}", old_params.rt.array_mode); | ||
| 1232 | UNREACHABLE(); | ||
| 1233 | return new_surface; | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | // This seems to be used for render-to-cubemap texture | ||
| 1237 | ASSERT_MSG(old_params.target == SurfaceParams::SurfaceTarget::Texture2D, "Unexpected"); | ||
| 1238 | ASSERT_MSG(old_params.pixel_format == new_params.pixel_format, "Unexpected"); | ||
| 1239 | ASSERT_MSG(old_params.rt.base_layer == 0, "Unimplemented"); | ||
| 1240 | |||
| 1241 | // TODO(bunnei): Verify the below - this stride seems to be in 32-bit words, not pixels. | ||
| 1242 | // Tested with Splatoon 2, Super Mario Odyssey, and Breath of the Wild. | ||
| 1243 | const std::size_t byte_stride{old_params.rt.layer_stride * sizeof(u32)}; | ||
| 1244 | |||
| 1245 | for (std::size_t index = 0; index < new_params.depth; ++index) { | ||
| 1246 | Surface face_surface{TryGetReservedSurface(old_params)}; | ||
| 1247 | ASSERT_MSG(face_surface, "Unexpected"); | ||
| 1248 | |||
| 1249 | if (is_blit) { | ||
| 1250 | BlitSurface(face_surface, new_surface, read_framebuffer.handle, | ||
| 1251 | draw_framebuffer.handle, face_surface->GetSurfaceParams().rt.index, | ||
| 1252 | new_params.rt.index, index); | ||
| 1253 | } else { | ||
| 1254 | CopySurface(face_surface, new_surface, copy_pbo.handle, | ||
| 1255 | face_surface->GetSurfaceParams().rt.index, new_params.rt.index, index); | ||
| 1256 | } | ||
| 1257 | |||
| 1258 | old_params.addr += byte_stride; | ||
| 1259 | } | ||
| 1260 | break; | ||
| 1261 | } | ||
| 1262 | default: | 1259 | default: |
| 1263 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", | 1260 | LOG_CRITICAL(Render_OpenGL, "Unimplemented surface target={}", |
| 1264 | static_cast<u32>(new_params.target)); | 1261 | static_cast<u32>(new_params.target)); |
| @@ -1266,7 +1263,7 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface, | |||
| 1266 | } | 1263 | } |
| 1267 | 1264 | ||
| 1268 | return new_surface; | 1265 | return new_surface; |
| 1269 | } | 1266 | } // namespace OpenGL |
| 1270 | 1267 | ||
| 1271 | Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { | 1268 | Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr addr) const { |
| 1272 | return TryGet(addr); | 1269 | return TryGet(addr); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 0dd0d90a3..50a7ab47d 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -168,6 +168,23 @@ struct SurfaceParams { | |||
| 168 | } | 168 | } |
| 169 | } | 169 | } |
| 170 | 170 | ||
| 171 | static bool SurfaceTargetIsLayered(SurfaceTarget target) { | ||
| 172 | switch (target) { | ||
| 173 | case SurfaceTarget::Texture1D: | ||
| 174 | case SurfaceTarget::Texture2D: | ||
| 175 | case SurfaceTarget::Texture3D: | ||
| 176 | return false; | ||
| 177 | case SurfaceTarget::Texture1DArray: | ||
| 178 | case SurfaceTarget::Texture2DArray: | ||
| 179 | case SurfaceTarget::TextureCubemap: | ||
| 180 | return true; | ||
| 181 | default: | ||
| 182 | LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target)); | ||
| 183 | UNREACHABLE(); | ||
| 184 | return false; | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 171 | /** | 188 | /** |
| 172 | * Gets the compression factor for the specified PixelFormat. This applies to just the | 189 | * Gets the compression factor for the specified PixelFormat. This applies to just the |
| 173 | * "compressed width" and "compressed height", not the overall compression factor of a | 190 | * "compressed width" and "compressed height", not the overall compression factor of a |
| @@ -742,6 +759,25 @@ struct SurfaceParams { | |||
| 742 | return size_in_bytes_gl / 6; | 759 | return size_in_bytes_gl / 6; |
| 743 | } | 760 | } |
| 744 | 761 | ||
| 762 | /// Returns the exact size of memory occupied by the texture in VRAM, including mipmaps. | ||
| 763 | std::size_t MemorySize() const { | ||
| 764 | std::size_t size = InnerMemorySize(is_layered); | ||
| 765 | if (is_layered) | ||
| 766 | return size * depth; | ||
| 767 | return size; | ||
| 768 | } | ||
| 769 | |||
| 770 | /// Returns the exact size of the memory occupied by a layer in a texture in VRAM, including | ||
| 771 | /// mipmaps. | ||
| 772 | std::size_t LayerMemorySize() const { | ||
| 773 | return InnerMemorySize(true); | ||
| 774 | } | ||
| 775 | |||
| 776 | /// Returns the size of a layer of this surface in OpenGL. | ||
| 777 | std::size_t LayerSizeGL() const { | ||
| 778 | return SizeInBytesRaw(true) / depth; | ||
| 779 | } | ||
| 780 | |||
| 745 | /// Creates SurfaceParams from a texture configuration | 781 | /// Creates SurfaceParams from a texture configuration |
| 746 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, | 782 | static SurfaceParams CreateForTexture(const Tegra::Texture::FullTextureInfo& config, |
| 747 | const GLShader::SamplerEntry& entry); | 783 | const GLShader::SamplerEntry& entry); |
| @@ -782,6 +818,7 @@ struct SurfaceParams { | |||
| 782 | u32 unaligned_height; | 818 | u32 unaligned_height; |
| 783 | SurfaceTarget target; | 819 | SurfaceTarget target; |
| 784 | u32 max_mip_level; | 820 | u32 max_mip_level; |
| 821 | bool is_layered; | ||
| 785 | 822 | ||
| 786 | // Parameters used for caching | 823 | // Parameters used for caching |
| 787 | VAddr addr; | 824 | VAddr addr; |
| @@ -797,6 +834,9 @@ struct SurfaceParams { | |||
| 797 | u32 layer_stride; | 834 | u32 layer_stride; |
| 798 | u32 base_layer; | 835 | u32 base_layer; |
| 799 | } rt; | 836 | } rt; |
| 837 | |||
| 838 | private: | ||
| 839 | std::size_t InnerMemorySize(bool layer_only = false) const; | ||
| 800 | }; | 840 | }; |
| 801 | 841 | ||
| 802 | }; // namespace OpenGL | 842 | }; // namespace OpenGL |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index fe4d1bd83..81ffb24e4 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <set> | 6 | #include <set> |
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include <string_view> | 8 | #include <string_view> |
| 9 | #include <unordered_set> | ||
| 9 | 10 | ||
| 10 | #include <boost/optional.hpp> | 11 | #include <boost/optional.hpp> |
| 11 | #include <fmt/format.h> | 12 | #include <fmt/format.h> |
| @@ -276,7 +277,8 @@ public: | |||
| 276 | GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, | 277 | GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations, |
| 277 | const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix, | 278 | const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix, |
| 278 | const Tegra::Shader::Header& header) | 279 | const Tegra::Shader::Header& header) |
| 279 | : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} { | 280 | : shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header}, |
| 281 | fixed_pipeline_output_attributes_used{} { | ||
| 280 | BuildRegisterList(); | 282 | BuildRegisterList(); |
| 281 | BuildInputList(); | 283 | BuildInputList(); |
| 282 | } | 284 | } |
| @@ -480,7 +482,12 @@ public: | |||
| 480 | std::to_string(static_cast<u32>(attribute)) + ']' + | 482 | std::to_string(static_cast<u32>(attribute)) + ']' + |
| 481 | GetSwizzle(elem) + " = " + src + ';'); | 483 | GetSwizzle(elem) + " = " + src + ';'); |
| 482 | } else { | 484 | } else { |
| 483 | shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); | 485 | if (attribute == Attribute::Index::PointSize) { |
| 486 | fixed_pipeline_output_attributes_used.insert(attribute); | ||
| 487 | shader.AddLine(dest + " = " + src + ';'); | ||
| 488 | } else { | ||
| 489 | shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';'); | ||
| 490 | } | ||
| 484 | } | 491 | } |
| 485 | } | 492 | } |
| 486 | } | 493 | } |
| @@ -524,6 +531,7 @@ public: | |||
| 524 | 531 | ||
| 525 | /// Add declarations. | 532 | /// Add declarations. |
| 526 | void GenerateDeclarations(const std::string& suffix) { | 533 | void GenerateDeclarations(const std::string& suffix) { |
| 534 | GenerateVertex(); | ||
| 527 | GenerateRegisters(suffix); | 535 | GenerateRegisters(suffix); |
| 528 | GenerateInternalFlags(); | 536 | GenerateInternalFlags(); |
| 529 | GenerateInputAttrs(); | 537 | GenerateInputAttrs(); |
| @@ -683,6 +691,20 @@ private: | |||
| 683 | declarations.AddNewLine(); | 691 | declarations.AddNewLine(); |
| 684 | } | 692 | } |
| 685 | 693 | ||
| 694 | void GenerateVertex() { | ||
| 695 | if (stage != Maxwell3D::Regs::ShaderStage::Vertex) | ||
| 696 | return; | ||
| 697 | declarations.AddLine("out gl_PerVertex {"); | ||
| 698 | ++declarations.scope; | ||
| 699 | declarations.AddLine("vec4 gl_Position;"); | ||
| 700 | for (auto& o : fixed_pipeline_output_attributes_used) { | ||
| 701 | if (o == Attribute::Index::PointSize) | ||
| 702 | declarations.AddLine("float gl_PointSize;"); | ||
| 703 | } | ||
| 704 | --declarations.scope; | ||
| 705 | declarations.AddLine("};"); | ||
| 706 | } | ||
| 707 | |||
| 686 | /// Generates code representing a temporary (GPR) register. | 708 | /// Generates code representing a temporary (GPR) register. |
| 687 | std::string GetRegister(const Register& reg, unsigned elem) { | 709 | std::string GetRegister(const Register& reg, unsigned elem) { |
| 688 | if (reg == Register::ZeroIndex) { | 710 | if (reg == Register::ZeroIndex) { |
| @@ -836,6 +858,8 @@ private: | |||
| 836 | /// Generates code representing the declaration name of an output attribute register. | 858 | /// Generates code representing the declaration name of an output attribute register. |
| 837 | std::string GetOutputAttribute(Attribute::Index attribute) { | 859 | std::string GetOutputAttribute(Attribute::Index attribute) { |
| 838 | switch (attribute) { | 860 | switch (attribute) { |
| 861 | case Attribute::Index::PointSize: | ||
| 862 | return "gl_PointSize"; | ||
| 839 | case Attribute::Index::Position: | 863 | case Attribute::Index::Position: |
| 840 | return "position"; | 864 | return "position"; |
| 841 | default: | 865 | default: |
| @@ -870,6 +894,7 @@ private: | |||
| 870 | const Maxwell3D::Regs::ShaderStage& stage; | 894 | const Maxwell3D::Regs::ShaderStage& stage; |
| 871 | const std::string& suffix; | 895 | const std::string& suffix; |
| 872 | const Tegra::Shader::Header& header; | 896 | const Tegra::Shader::Header& header; |
| 897 | std::unordered_set<Attribute::Index> fixed_pipeline_output_attributes_used; | ||
| 873 | }; | 898 | }; |
| 874 | 899 | ||
| 875 | class GLSLGenerator { | 900 | class GLSLGenerator { |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index e883ffb1d..dfb562706 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.cpp | |||
| @@ -19,9 +19,6 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) { | |||
| 19 | out += Decompiler::GetCommonDeclarations(); | 19 | out += Decompiler::GetCommonDeclarations(); |
| 20 | 20 | ||
| 21 | out += R"( | 21 | out += R"( |
| 22 | out gl_PerVertex { | ||
| 23 | vec4 gl_Position; | ||
| 24 | }; | ||
| 25 | 22 | ||
| 26 | layout (location = 0) out vec4 position; | 23 | layout (location = 0) out vec4 position; |
| 27 | 24 | ||
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index f1b40e7f5..550ca856c 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -142,7 +142,6 @@ void SwizzledData(u8* swizzled_data, u8* unswizzled_data, const bool unswizzle, | |||
| 142 | const u32 blocks_on_x = div_ceil(width, block_x_elements); | 142 | const u32 blocks_on_x = div_ceil(width, block_x_elements); |
| 143 | const u32 blocks_on_y = div_ceil(height, block_y_elements); | 143 | const u32 blocks_on_y = div_ceil(height, block_y_elements); |
| 144 | const u32 blocks_on_z = div_ceil(depth, block_z_elements); | 144 | const u32 blocks_on_z = div_ceil(depth, block_z_elements); |
| 145 | const u32 blocks = blocks_on_x * blocks_on_y * blocks_on_z; | ||
| 146 | const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z; | 145 | const u32 gob_size = gob_x_bytes * gob_elements_y * gob_elements_z; |
| 147 | const u32 xy_block_size = gob_size * block_height; | 146 | const u32 xy_block_size = gob_size * block_height; |
| 148 | const u32 block_size = xy_block_size * block_depth; | 147 | const u32 block_size = xy_block_size * block_depth; |
| @@ -320,13 +319,13 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat | |||
| 320 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, | 319 | std::size_t CalculateSize(bool tiled, u32 bytes_per_pixel, u32 width, u32 height, u32 depth, |
| 321 | u32 block_height, u32 block_depth) { | 320 | u32 block_height, u32 block_depth) { |
| 322 | if (tiled) { | 321 | if (tiled) { |
| 323 | const u32 gobs_in_x = 64 / bytes_per_pixel; | 322 | const u32 gobs_in_x = 64; |
| 324 | const u32 gobs_in_y = 8; | 323 | const u32 gobs_in_y = 8; |
| 325 | const u32 gobs_in_z = 1; | 324 | const u32 gobs_in_z = 1; |
| 326 | const u32 aligned_width = Common::AlignUp(width, gobs_in_x); | 325 | const u32 aligned_width = Common::AlignUp(width * bytes_per_pixel, gobs_in_x); |
| 327 | const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); | 326 | const u32 aligned_height = Common::AlignUp(height, gobs_in_y * block_height); |
| 328 | const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); | 327 | const u32 aligned_depth = Common::AlignUp(depth, gobs_in_z * block_depth); |
| 329 | return aligned_width * aligned_height * aligned_depth * bytes_per_pixel; | 328 | return aligned_width * aligned_height * aligned_depth; |
| 330 | } else { | 329 | } else { |
| 331 | return width * height * depth * bytes_per_pixel; | 330 | return width * height * depth * bytes_per_pixel; |
| 332 | } | 331 | } |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index e8ab23326..39eef8858 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | 8 | ||
| 9 | #include "common/microprofile.h" | 9 | #include "common/microprofile.h" |
| 10 | #include "common/scm_rev.h" | 10 | #include "common/scm_rev.h" |
| 11 | #include "common/string_util.h" | ||
| 12 | #include "core/core.h" | 11 | #include "core/core.h" |
| 13 | #include "core/frontend/framebuffer_layout.h" | 12 | #include "core/frontend/framebuffer_layout.h" |
| 14 | #include "core/settings.h" | 13 | #include "core/settings.h" |
| @@ -107,9 +106,8 @@ private: | |||
| 107 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) | 106 | GRenderWindow::GRenderWindow(QWidget* parent, EmuThread* emu_thread) |
| 108 | : QWidget(parent), child(nullptr), emu_thread(emu_thread) { | 107 | : QWidget(parent), child(nullptr), emu_thread(emu_thread) { |
| 109 | 108 | ||
| 110 | std::string window_title = fmt::format("yuzu {} | {}-{}", Common::g_build_name, | 109 | setWindowTitle(QStringLiteral("yuzu %1 | %2-%3") |
| 111 | Common::g_scm_branch, Common::g_scm_desc); | 110 | .arg(Common::g_build_name, Common::g_scm_branch, Common::g_scm_desc)); |
| 112 | setWindowTitle(QString::fromStdString(window_title)); | ||
| 113 | setAttribute(Qt::WA_AcceptTouchEvents); | 111 | setAttribute(Qt::WA_AcceptTouchEvents); |
| 114 | 112 | ||
| 115 | InputCommon::Init(); | 113 | InputCommon::Init(); |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index d029590ff..d4fd60a73 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <QSettings> | 5 | #include <QSettings> |
| 6 | #include "common/file_util.h" | 6 | #include "common/file_util.h" |
| 7 | #include "core/hle/service/acc/profile_manager.h" | ||
| 7 | #include "input_common/main.h" | 8 | #include "input_common/main.h" |
| 8 | #include "yuzu/configuration/config.h" | 9 | #include "yuzu/configuration/config.h" |
| 9 | #include "yuzu/ui_settings.h" | 10 | #include "yuzu/ui_settings.h" |
| @@ -12,11 +13,16 @@ Config::Config() { | |||
| 12 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | 13 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. |
| 13 | qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; | 14 | qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini"; |
| 14 | FileUtil::CreateFullPath(qt_config_loc); | 15 | FileUtil::CreateFullPath(qt_config_loc); |
| 15 | qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); | 16 | qt_config = |
| 17 | std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat); | ||
| 16 | 18 | ||
| 17 | Reload(); | 19 | Reload(); |
| 18 | } | 20 | } |
| 19 | 21 | ||
| 22 | Config::~Config() { | ||
| 23 | Save(); | ||
| 24 | } | ||
| 25 | |||
| 20 | const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { | 26 | const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = { |
| 21 | Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, | 27 | Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q, |
| 22 | Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, | 28 | Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T, |
| @@ -123,7 +129,10 @@ void Config::ReadValues() { | |||
| 123 | qt_config->beginGroup("System"); | 129 | qt_config->beginGroup("System"); |
| 124 | Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); | 130 | Settings::values.use_docked_mode = qt_config->value("use_docked_mode", false).toBool(); |
| 125 | Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool(); | 131 | Settings::values.enable_nfc = qt_config->value("enable_nfc", true).toBool(); |
| 126 | Settings::values.username = qt_config->value("username", "yuzu").toString().toStdString(); | 132 | |
| 133 | Settings::values.current_user = std::clamp<int>(qt_config->value("current_user", 0).toInt(), 0, | ||
| 134 | Service::Account::MAX_USERS - 1); | ||
| 135 | |||
| 127 | Settings::values.language_index = qt_config->value("language_index", 1).toInt(); | 136 | Settings::values.language_index = qt_config->value("language_index", 1).toInt(); |
| 128 | qt_config->endGroup(); | 137 | qt_config->endGroup(); |
| 129 | 138 | ||
| @@ -260,7 +269,8 @@ void Config::SaveValues() { | |||
| 260 | qt_config->beginGroup("System"); | 269 | qt_config->beginGroup("System"); |
| 261 | qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); | 270 | qt_config->setValue("use_docked_mode", Settings::values.use_docked_mode); |
| 262 | qt_config->setValue("enable_nfc", Settings::values.enable_nfc); | 271 | qt_config->setValue("enable_nfc", Settings::values.enable_nfc); |
| 263 | qt_config->setValue("username", QString::fromStdString(Settings::values.username)); | 272 | qt_config->setValue("current_user", Settings::values.current_user); |
| 273 | |||
| 264 | qt_config->setValue("language_index", Settings::values.language_index); | 274 | qt_config->setValue("language_index", Settings::values.language_index); |
| 265 | qt_config->endGroup(); | 275 | qt_config->endGroup(); |
| 266 | 276 | ||
| @@ -337,9 +347,3 @@ void Config::Reload() { | |||
| 337 | void Config::Save() { | 347 | void Config::Save() { |
| 338 | SaveValues(); | 348 | SaveValues(); |
| 339 | } | 349 | } |
| 340 | |||
| 341 | Config::~Config() { | ||
| 342 | Save(); | ||
| 343 | |||
| 344 | delete qt_config; | ||
| 345 | } | ||
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h index cbf745ea2..9c99c1b75 100644 --- a/src/yuzu/configuration/config.h +++ b/src/yuzu/configuration/config.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <memory> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <QVariant> | 10 | #include <QVariant> |
| 10 | #include "core/settings.h" | 11 | #include "core/settings.h" |
| @@ -12,12 +13,6 @@ | |||
| 12 | class QSettings; | 13 | class QSettings; |
| 13 | 14 | ||
| 14 | class Config { | 15 | class Config { |
| 15 | QSettings* qt_config; | ||
| 16 | std::string qt_config_loc; | ||
| 17 | |||
| 18 | void ReadValues(); | ||
| 19 | void SaveValues(); | ||
| 20 | |||
| 21 | public: | 16 | public: |
| 22 | Config(); | 17 | Config(); |
| 23 | ~Config(); | 18 | ~Config(); |
| @@ -27,4 +22,11 @@ public: | |||
| 27 | 22 | ||
| 28 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; | 23 | static const std::array<int, Settings::NativeButton::NumButtons> default_buttons; |
| 29 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; | 24 | static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs; |
| 25 | |||
| 26 | private: | ||
| 27 | void ReadValues(); | ||
| 28 | void SaveValues(); | ||
| 29 | |||
| 30 | std::unique_ptr<QSettings> qt_config; | ||
| 31 | std::string qt_config_loc; | ||
| 30 | }; | 32 | }; |
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index e9ed9c38f..20ffb0a9a 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp | |||
| @@ -2,14 +2,27 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <QFileDialog> | ||
| 7 | #include <QGraphicsItem> | ||
| 8 | #include <QGraphicsScene> | ||
| 9 | #include <QInputDialog> | ||
| 5 | #include <QMessageBox> | 10 | #include <QMessageBox> |
| 11 | #include <QStandardItemModel> | ||
| 12 | #include <QTreeView> | ||
| 13 | #include <QVBoxLayout> | ||
| 14 | #include "common/common_paths.h" | ||
| 15 | #include "common/logging/backend.h" | ||
| 16 | #include "common/string_util.h" | ||
| 6 | #include "core/core.h" | 17 | #include "core/core.h" |
| 18 | #include "core/hle/service/acc/profile_manager.h" | ||
| 7 | #include "core/settings.h" | 19 | #include "core/settings.h" |
| 8 | #include "ui_configure_system.h" | 20 | #include "ui_configure_system.h" |
| 9 | #include "yuzu/configuration/configure_system.h" | 21 | #include "yuzu/configuration/configure_system.h" |
| 10 | #include "yuzu/main.h" | 22 | #include "yuzu/main.h" |
| 11 | 23 | ||
| 12 | static const std::array<int, 12> days_in_month = {{ | 24 | namespace { |
| 25 | constexpr std::array<int, 12> days_in_month = {{ | ||
| 13 | 31, | 26 | 31, |
| 14 | 29, | 27 | 29, |
| 15 | 31, | 28 | 31, |
| @@ -24,13 +37,82 @@ static const std::array<int, 12> days_in_month = {{ | |||
| 24 | 31, | 37 | 31, |
| 25 | }}; | 38 | }}; |
| 26 | 39 | ||
| 27 | ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::ConfigureSystem) { | 40 | // Same backup JPEG used by acc IProfile::GetImage if no jpeg found |
| 41 | constexpr std::array<u8, 107> backup_jpeg{ | ||
| 42 | 0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02, | ||
| 43 | 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05, | ||
| 44 | 0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e, | ||
| 45 | 0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13, | ||
| 46 | 0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01, | ||
| 47 | 0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08, | ||
| 48 | 0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9, | ||
| 49 | }; | ||
| 50 | |||
| 51 | std::string GetImagePath(Service::Account::UUID uuid) { | ||
| 52 | return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + | ||
| 53 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | ||
| 54 | } | ||
| 55 | |||
| 56 | std::string GetAccountUsername(const Service::Account::ProfileManager& manager, | ||
| 57 | Service::Account::UUID uuid) { | ||
| 58 | Service::Account::ProfileBase profile; | ||
| 59 | if (!manager.GetProfileBase(uuid, profile)) { | ||
| 60 | return ""; | ||
| 61 | } | ||
| 62 | |||
| 63 | return Common::StringFromFixedZeroTerminatedBuffer( | ||
| 64 | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); | ||
| 65 | } | ||
| 66 | } // Anonymous namespace | ||
| 67 | |||
| 68 | ConfigureSystem::ConfigureSystem(QWidget* parent) | ||
| 69 | : QWidget(parent), ui(new Ui::ConfigureSystem), | ||
| 70 | profile_manager(std::make_unique<Service::Account::ProfileManager>()) { | ||
| 28 | ui->setupUi(this); | 71 | ui->setupUi(this); |
| 29 | connect(ui->combo_birthmonth, | 72 | connect(ui->combo_birthmonth, |
| 30 | static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, | 73 | static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, |
| 31 | &ConfigureSystem::updateBirthdayComboBox); | 74 | &ConfigureSystem::UpdateBirthdayComboBox); |
| 32 | connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, | 75 | connect(ui->button_regenerate_console_id, &QPushButton::clicked, this, |
| 33 | &ConfigureSystem::refreshConsoleID); | 76 | &ConfigureSystem::RefreshConsoleID); |
| 77 | |||
| 78 | layout = new QVBoxLayout; | ||
| 79 | tree_view = new QTreeView; | ||
| 80 | item_model = new QStandardItemModel(tree_view); | ||
| 81 | tree_view->setModel(item_model); | ||
| 82 | |||
| 83 | tree_view->setAlternatingRowColors(true); | ||
| 84 | tree_view->setSelectionMode(QHeaderView::SingleSelection); | ||
| 85 | tree_view->setSelectionBehavior(QHeaderView::SelectRows); | ||
| 86 | tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 87 | tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 88 | tree_view->setSortingEnabled(true); | ||
| 89 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); | ||
| 90 | tree_view->setUniformRowHeights(true); | ||
| 91 | tree_view->setIconSize({64, 64}); | ||
| 92 | tree_view->setContextMenuPolicy(Qt::NoContextMenu); | ||
| 93 | |||
| 94 | item_model->insertColumns(0, 1); | ||
| 95 | item_model->setHeaderData(0, Qt::Horizontal, "Users"); | ||
| 96 | |||
| 97 | // We must register all custom types with the Qt Automoc system so that we are able to use it | ||
| 98 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | ||
| 99 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | ||
| 100 | |||
| 101 | layout->setContentsMargins(0, 0, 0, 0); | ||
| 102 | layout->setSpacing(0); | ||
| 103 | layout->addWidget(tree_view); | ||
| 104 | |||
| 105 | ui->scrollArea->setLayout(layout); | ||
| 106 | |||
| 107 | connect(tree_view, &QTreeView::clicked, this, &ConfigureSystem::SelectUser); | ||
| 108 | |||
| 109 | connect(ui->pm_add, &QPushButton::pressed, this, &ConfigureSystem::AddUser); | ||
| 110 | connect(ui->pm_rename, &QPushButton::pressed, this, &ConfigureSystem::RenameUser); | ||
| 111 | connect(ui->pm_remove, &QPushButton::pressed, this, &ConfigureSystem::DeleteUser); | ||
| 112 | connect(ui->pm_set_image, &QPushButton::pressed, this, &ConfigureSystem::SetUserImage); | ||
| 113 | |||
| 114 | scene = new QGraphicsScene; | ||
| 115 | ui->current_user_icon->setScene(scene); | ||
| 34 | 116 | ||
| 35 | this->setConfiguration(); | 117 | this->setConfiguration(); |
| 36 | } | 118 | } |
| @@ -39,8 +121,58 @@ ConfigureSystem::~ConfigureSystem() = default; | |||
| 39 | 121 | ||
| 40 | void ConfigureSystem::setConfiguration() { | 122 | void ConfigureSystem::setConfiguration() { |
| 41 | enabled = !Core::System::GetInstance().IsPoweredOn(); | 123 | enabled = !Core::System::GetInstance().IsPoweredOn(); |
| 42 | ui->edit_username->setText(QString::fromStdString(Settings::values.username)); | 124 | |
| 43 | ui->combo_language->setCurrentIndex(Settings::values.language_index); | 125 | ui->combo_language->setCurrentIndex(Settings::values.language_index); |
| 126 | |||
| 127 | item_model->removeRows(0, item_model->rowCount()); | ||
| 128 | list_items.clear(); | ||
| 129 | |||
| 130 | PopulateUserList(); | ||
| 131 | UpdateCurrentUser(); | ||
| 132 | } | ||
| 133 | |||
| 134 | static QPixmap GetIcon(Service::Account::UUID uuid) { | ||
| 135 | const auto icon_url = QString::fromStdString(GetImagePath(uuid)); | ||
| 136 | QPixmap icon{icon_url}; | ||
| 137 | |||
| 138 | if (!icon) { | ||
| 139 | icon.fill(Qt::black); | ||
| 140 | icon.loadFromData(backup_jpeg.data(), backup_jpeg.size()); | ||
| 141 | } | ||
| 142 | |||
| 143 | return icon; | ||
| 144 | } | ||
| 145 | |||
| 146 | void ConfigureSystem::PopulateUserList() { | ||
| 147 | const auto& profiles = profile_manager->GetAllUsers(); | ||
| 148 | for (const auto& user : profiles) { | ||
| 149 | Service::Account::ProfileBase profile; | ||
| 150 | if (!profile_manager->GetProfileBase(user, profile)) | ||
| 151 | continue; | ||
| 152 | |||
| 153 | const auto username = Common::StringFromFixedZeroTerminatedBuffer( | ||
| 154 | reinterpret_cast<const char*>(profile.username.data()), profile.username.size()); | ||
| 155 | |||
| 156 | list_items.push_back(QList<QStandardItem*>{new QStandardItem{ | ||
| 157 | GetIcon(user).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 158 | QString::fromStdString(username + '\n' + user.FormatSwitch())}}); | ||
| 159 | } | ||
| 160 | |||
| 161 | for (const auto& item : list_items) | ||
| 162 | item_model->appendRow(item); | ||
| 163 | } | ||
| 164 | |||
| 165 | void ConfigureSystem::UpdateCurrentUser() { | ||
| 166 | ui->pm_add->setEnabled(profile_manager->GetUserCount() < Service::Account::MAX_USERS); | ||
| 167 | |||
| 168 | const auto& current_user = profile_manager->GetUser(Settings::values.current_user); | ||
| 169 | ASSERT(current_user != std::nullopt); | ||
| 170 | const auto username = GetAccountUsername(*profile_manager, *current_user); | ||
| 171 | |||
| 172 | scene->clear(); | ||
| 173 | scene->addPixmap( | ||
| 174 | GetIcon(*current_user).scaled(48, 48, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); | ||
| 175 | ui->current_user_username->setText(QString::fromStdString(username)); | ||
| 44 | } | 176 | } |
| 45 | 177 | ||
| 46 | void ConfigureSystem::ReadSystemSettings() {} | 178 | void ConfigureSystem::ReadSystemSettings() {} |
| @@ -48,12 +180,12 @@ void ConfigureSystem::ReadSystemSettings() {} | |||
| 48 | void ConfigureSystem::applyConfiguration() { | 180 | void ConfigureSystem::applyConfiguration() { |
| 49 | if (!enabled) | 181 | if (!enabled) |
| 50 | return; | 182 | return; |
| 51 | Settings::values.username = ui->edit_username->text().toStdString(); | 183 | |
| 52 | Settings::values.language_index = ui->combo_language->currentIndex(); | 184 | Settings::values.language_index = ui->combo_language->currentIndex(); |
| 53 | Settings::Apply(); | 185 | Settings::Apply(); |
| 54 | } | 186 | } |
| 55 | 187 | ||
| 56 | void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { | 188 | void ConfigureSystem::UpdateBirthdayComboBox(int birthmonth_index) { |
| 57 | if (birthmonth_index < 0 || birthmonth_index >= 12) | 189 | if (birthmonth_index < 0 || birthmonth_index >= 12) |
| 58 | return; | 190 | return; |
| 59 | 191 | ||
| @@ -78,7 +210,7 @@ void ConfigureSystem::updateBirthdayComboBox(int birthmonth_index) { | |||
| 78 | ui->combo_birthday->setCurrentIndex(birthday_index); | 210 | ui->combo_birthday->setCurrentIndex(birthday_index); |
| 79 | } | 211 | } |
| 80 | 212 | ||
| 81 | void ConfigureSystem::refreshConsoleID() { | 213 | void ConfigureSystem::RefreshConsoleID() { |
| 82 | QMessageBox::StandardButton reply; | 214 | QMessageBox::StandardButton reply; |
| 83 | QString warning_text = tr("This will replace your current virtual Switch with a new one. " | 215 | QString warning_text = tr("This will replace your current virtual Switch with a new one. " |
| 84 | "Your current virtual Switch will not be recoverable. " | 216 | "Your current virtual Switch will not be recoverable. " |
| @@ -92,3 +224,129 @@ void ConfigureSystem::refreshConsoleID() { | |||
| 92 | ui->label_console_id->setText( | 224 | ui->label_console_id->setText( |
| 93 | tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); | 225 | tr("Console ID: 0x%1").arg(QString::number(console_id, 16).toUpper())); |
| 94 | } | 226 | } |
| 227 | |||
| 228 | void ConfigureSystem::SelectUser(const QModelIndex& index) { | ||
| 229 | Settings::values.current_user = | ||
| 230 | std::clamp<std::size_t>(index.row(), 0, profile_manager->GetUserCount() - 1); | ||
| 231 | |||
| 232 | UpdateCurrentUser(); | ||
| 233 | |||
| 234 | ui->pm_remove->setEnabled(profile_manager->GetUserCount() >= 2); | ||
| 235 | ui->pm_rename->setEnabled(true); | ||
| 236 | ui->pm_set_image->setEnabled(true); | ||
| 237 | } | ||
| 238 | |||
| 239 | void ConfigureSystem::AddUser() { | ||
| 240 | const auto uuid = Service::Account::UUID::Generate(); | ||
| 241 | |||
| 242 | bool ok = false; | ||
| 243 | const auto username = | ||
| 244 | QInputDialog::getText(this, tr("Enter Username"), tr("Enter a username for the new user:"), | ||
| 245 | QLineEdit::Normal, QString(), &ok); | ||
| 246 | if (!ok) | ||
| 247 | return; | ||
| 248 | |||
| 249 | profile_manager->CreateNewUser(uuid, username.toStdString()); | ||
| 250 | |||
| 251 | item_model->appendRow(new QStandardItem{ | ||
| 252 | GetIcon(uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 253 | QString::fromStdString(username.toStdString() + '\n' + uuid.FormatSwitch())}); | ||
| 254 | } | ||
| 255 | |||
| 256 | void ConfigureSystem::RenameUser() { | ||
| 257 | const auto user = tree_view->currentIndex().row(); | ||
| 258 | const auto uuid = profile_manager->GetUser(user); | ||
| 259 | ASSERT(uuid != std::nullopt); | ||
| 260 | const auto username = GetAccountUsername(*profile_manager, *uuid); | ||
| 261 | |||
| 262 | Service::Account::ProfileBase profile; | ||
| 263 | if (!profile_manager->GetProfileBase(*uuid, profile)) | ||
| 264 | return; | ||
| 265 | |||
| 266 | bool ok = false; | ||
| 267 | const auto new_username = | ||
| 268 | QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"), | ||
| 269 | QLineEdit::Normal, QString::fromStdString(username), &ok); | ||
| 270 | |||
| 271 | if (!ok) | ||
| 272 | return; | ||
| 273 | |||
| 274 | std::fill(profile.username.begin(), profile.username.end(), '\0'); | ||
| 275 | const auto username_std = new_username.toStdString(); | ||
| 276 | if (username_std.size() > profile.username.size()) { | ||
| 277 | std::copy_n(username_std.begin(), std::min(profile.username.size(), username_std.size()), | ||
| 278 | profile.username.begin()); | ||
| 279 | } else { | ||
| 280 | std::copy(username_std.begin(), username_std.end(), profile.username.begin()); | ||
| 281 | } | ||
| 282 | |||
| 283 | profile_manager->SetProfileBase(*uuid, profile); | ||
| 284 | |||
| 285 | item_model->setItem( | ||
| 286 | user, 0, | ||
| 287 | new QStandardItem{ | ||
| 288 | GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 289 | tr("%1\n%2", "%1 is the profile username, %2 is the formatted UUID (e.g. " | ||
| 290 | "00112233-4455-6677-8899-AABBCCDDEEFF))") | ||
| 291 | .arg(QString::fromStdString(username_std), | ||
| 292 | QString::fromStdString(uuid->FormatSwitch()))}); | ||
| 293 | UpdateCurrentUser(); | ||
| 294 | } | ||
| 295 | |||
| 296 | void ConfigureSystem::DeleteUser() { | ||
| 297 | const auto index = tree_view->currentIndex().row(); | ||
| 298 | const auto uuid = profile_manager->GetUser(index); | ||
| 299 | ASSERT(uuid != std::nullopt); | ||
| 300 | const auto username = GetAccountUsername(*profile_manager, *uuid); | ||
| 301 | |||
| 302 | const auto confirm = | ||
| 303 | QMessageBox::question(this, tr("Confirm Delete"), | ||
| 304 | tr("You are about to delete user with name %1. Are you sure?") | ||
| 305 | .arg(QString::fromStdString(username))); | ||
| 306 | |||
| 307 | if (confirm == QMessageBox::No) | ||
| 308 | return; | ||
| 309 | |||
| 310 | if (Settings::values.current_user == tree_view->currentIndex().row()) | ||
| 311 | Settings::values.current_user = 0; | ||
| 312 | UpdateCurrentUser(); | ||
| 313 | |||
| 314 | if (!profile_manager->RemoveUser(*uuid)) | ||
| 315 | return; | ||
| 316 | |||
| 317 | item_model->removeRows(tree_view->currentIndex().row(), 1); | ||
| 318 | tree_view->clearSelection(); | ||
| 319 | |||
| 320 | ui->pm_remove->setEnabled(false); | ||
| 321 | ui->pm_rename->setEnabled(false); | ||
| 322 | } | ||
| 323 | |||
| 324 | void ConfigureSystem::SetUserImage() { | ||
| 325 | const auto index = tree_view->currentIndex().row(); | ||
| 326 | const auto uuid = profile_manager->GetUser(index); | ||
| 327 | ASSERT(uuid != std::nullopt); | ||
| 328 | const auto username = GetAccountUsername(*profile_manager, *uuid); | ||
| 329 | |||
| 330 | const auto file = QFileDialog::getOpenFileName(this, tr("Select User Image"), QString(), | ||
| 331 | tr("JPEG Images (*.jpg *.jpeg)")); | ||
| 332 | |||
| 333 | if (file.isEmpty()) | ||
| 334 | return; | ||
| 335 | |||
| 336 | FileUtil::Delete(GetImagePath(*uuid)); | ||
| 337 | |||
| 338 | const auto raw_path = | ||
| 339 | FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) + "/system/save/8000000000000010"; | ||
| 340 | if (FileUtil::Exists(raw_path) && !FileUtil::IsDirectory(raw_path)) | ||
| 341 | FileUtil::Delete(raw_path); | ||
| 342 | |||
| 343 | FileUtil::CreateFullPath(GetImagePath(*uuid)); | ||
| 344 | FileUtil::Copy(file.toStdString(), GetImagePath(*uuid)); | ||
| 345 | |||
| 346 | item_model->setItem( | ||
| 347 | index, 0, | ||
| 348 | new QStandardItem{ | ||
| 349 | GetIcon(*uuid).scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation), | ||
| 350 | QString::fromStdString(username + '\n' + uuid->FormatSwitch())}); | ||
| 351 | UpdateCurrentUser(); | ||
| 352 | } | ||
diff --git a/src/yuzu/configuration/configure_system.h b/src/yuzu/configuration/configure_system.h index f13de17d4..07764e1f7 100644 --- a/src/yuzu/configuration/configure_system.h +++ b/src/yuzu/configuration/configure_system.h | |||
| @@ -5,8 +5,20 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | |||
| 9 | #include <QList> | ||
| 8 | #include <QWidget> | 10 | #include <QWidget> |
| 9 | 11 | ||
| 12 | class QGraphicsScene; | ||
| 13 | class QStandardItem; | ||
| 14 | class QStandardItemModel; | ||
| 15 | class QTreeView; | ||
| 16 | class QVBoxLayout; | ||
| 17 | |||
| 18 | namespace Service::Account { | ||
| 19 | class ProfileManager; | ||
| 20 | } | ||
| 21 | |||
| 10 | namespace Ui { | 22 | namespace Ui { |
| 11 | class ConfigureSystem; | 23 | class ConfigureSystem; |
| 12 | } | 24 | } |
| @@ -16,23 +28,39 @@ class ConfigureSystem : public QWidget { | |||
| 16 | 28 | ||
| 17 | public: | 29 | public: |
| 18 | explicit ConfigureSystem(QWidget* parent = nullptr); | 30 | explicit ConfigureSystem(QWidget* parent = nullptr); |
| 19 | ~ConfigureSystem(); | 31 | ~ConfigureSystem() override; |
| 20 | 32 | ||
| 21 | void applyConfiguration(); | 33 | void applyConfiguration(); |
| 22 | void setConfiguration(); | 34 | void setConfiguration(); |
| 23 | 35 | ||
| 24 | public slots: | ||
| 25 | void updateBirthdayComboBox(int birthmonth_index); | ||
| 26 | void refreshConsoleID(); | ||
| 27 | |||
| 28 | private: | 36 | private: |
| 29 | void ReadSystemSettings(); | 37 | void ReadSystemSettings(); |
| 30 | 38 | ||
| 39 | void UpdateBirthdayComboBox(int birthmonth_index); | ||
| 40 | void RefreshConsoleID(); | ||
| 41 | |||
| 42 | void PopulateUserList(); | ||
| 43 | void UpdateCurrentUser(); | ||
| 44 | void SelectUser(const QModelIndex& index); | ||
| 45 | void AddUser(); | ||
| 46 | void RenameUser(); | ||
| 47 | void DeleteUser(); | ||
| 48 | void SetUserImage(); | ||
| 49 | |||
| 50 | QVBoxLayout* layout; | ||
| 51 | QTreeView* tree_view; | ||
| 52 | QStandardItemModel* item_model; | ||
| 53 | QGraphicsScene* scene; | ||
| 54 | |||
| 55 | std::vector<QList<QStandardItem*>> list_items; | ||
| 56 | |||
| 31 | std::unique_ptr<Ui::ConfigureSystem> ui; | 57 | std::unique_ptr<Ui::ConfigureSystem> ui; |
| 32 | bool enabled; | 58 | bool enabled = false; |
| 59 | |||
| 60 | int birthmonth = 0; | ||
| 61 | int birthday = 0; | ||
| 62 | int language_index = 0; | ||
| 63 | int sound_index = 0; | ||
| 33 | 64 | ||
| 34 | std::u16string username; | 65 | std::unique_ptr<Service::Account::ProfileManager> profile_manager; |
| 35 | int birthmonth, birthday; | ||
| 36 | int language_index; | ||
| 37 | int sound_index; | ||
| 38 | }; | 66 | }; |
diff --git a/src/yuzu/configuration/configure_system.ui b/src/yuzu/configuration/configure_system.ui index f3f8db038..020b32a37 100644 --- a/src/yuzu/configuration/configure_system.ui +++ b/src/yuzu/configuration/configure_system.ui | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | <x>0</x> | 7 | <x>0</x> |
| 8 | <y>0</y> | 8 | <y>0</y> |
| 9 | <width>360</width> | 9 | <width>360</width> |
| 10 | <height>377</height> | 10 | <height>483</height> |
| 11 | </rect> | 11 | </rect> |
| 12 | </property> | 12 | </property> |
| 13 | <property name="windowTitle"> | 13 | <property name="windowTitle"> |
| @@ -22,34 +22,28 @@ | |||
| 22 | <string>System Settings</string> | 22 | <string>System Settings</string> |
| 23 | </property> | 23 | </property> |
| 24 | <layout class="QGridLayout" name="gridLayout"> | 24 | <layout class="QGridLayout" name="gridLayout"> |
| 25 | <item row="0" column="0"> | 25 | <item row="1" column="0"> |
| 26 | <widget class="QLabel" name="label_username"> | 26 | <widget class="QLabel" name="label_language"> |
| 27 | <property name="text"> | 27 | <property name="text"> |
| 28 | <string>Username</string> | 28 | <string>Language</string> |
| 29 | </property> | 29 | </property> |
| 30 | </widget> | 30 | </widget> |
| 31 | </item> | 31 | </item> |
| 32 | <item row="0" column="1"> | 32 | <item row="0" column="0"> |
| 33 | <widget class="QLineEdit" name="edit_username"> | 33 | <widget class="QLabel" name="label_birthday"> |
| 34 | <property name="sizePolicy"> | 34 | <property name="text"> |
| 35 | <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> | 35 | <string>Birthday</string> |
| 36 | <horstretch>0</horstretch> | ||
| 37 | <verstretch>0</verstretch> | ||
| 38 | </sizepolicy> | ||
| 39 | </property> | ||
| 40 | <property name="maxLength"> | ||
| 41 | <number>32</number> | ||
| 42 | </property> | 36 | </property> |
| 43 | </widget> | 37 | </widget> |
| 44 | </item> | 38 | </item> |
| 45 | <item row="1" column="0"> | 39 | <item row="3" column="0"> |
| 46 | <widget class="QLabel" name="label_birthday"> | 40 | <widget class="QLabel" name="label_console_id"> |
| 47 | <property name="text"> | 41 | <property name="text"> |
| 48 | <string>Birthday</string> | 42 | <string>Console ID:</string> |
| 49 | </property> | 43 | </property> |
| 50 | </widget> | 44 | </widget> |
| 51 | </item> | 45 | </item> |
| 52 | <item row="1" column="1"> | 46 | <item row="0" column="1"> |
| 53 | <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> | 47 | <layout class="QHBoxLayout" name="horizontalLayout_birthday2"> |
| 54 | <item> | 48 | <item> |
| 55 | <widget class="QComboBox" name="combo_birthmonth"> | 49 | <widget class="QComboBox" name="combo_birthmonth"> |
| @@ -120,14 +114,7 @@ | |||
| 120 | </item> | 114 | </item> |
| 121 | </layout> | 115 | </layout> |
| 122 | </item> | 116 | </item> |
| 123 | <item row="2" column="0"> | 117 | <item row="1" column="1"> |
| 124 | <widget class="QLabel" name="label_language"> | ||
| 125 | <property name="text"> | ||
| 126 | <string>Language</string> | ||
| 127 | </property> | ||
| 128 | </widget> | ||
| 129 | </item> | ||
| 130 | <item row="2" column="1"> | ||
| 131 | <widget class="QComboBox" name="combo_language"> | 118 | <widget class="QComboBox" name="combo_language"> |
| 132 | <property name="toolTip"> | 119 | <property name="toolTip"> |
| 133 | <string>Note: this can be overridden when region setting is auto-select</string> | 120 | <string>Note: this can be overridden when region setting is auto-select</string> |
| @@ -187,31 +174,31 @@ | |||
| 187 | <string>Russian (Русский)</string> | 174 | <string>Russian (Русский)</string> |
| 188 | </property> | 175 | </property> |
| 189 | </item> | 176 | </item> |
| 190 | <item> | 177 | <item> |
| 191 | <property name="text"> | 178 | <property name="text"> |
| 192 | <string>Taiwanese</string> | 179 | <string>Taiwanese</string> |
| 193 | </property> | 180 | </property> |
| 194 | </item> | 181 | </item> |
| 195 | <item> | 182 | <item> |
| 196 | <property name="text"> | 183 | <property name="text"> |
| 197 | <string>British English</string> | 184 | <string>British English</string> |
| 198 | </property> | 185 | </property> |
| 199 | </item> | 186 | </item> |
| 200 | <item> | 187 | <item> |
| 201 | <property name="text"> | 188 | <property name="text"> |
| 202 | <string>Canadian French</string> | 189 | <string>Canadian French</string> |
| 203 | </property> | 190 | </property> |
| 204 | </item> | 191 | </item> |
| 205 | <item> | 192 | <item> |
| 206 | <property name="text"> | 193 | <property name="text"> |
| 207 | <string>Latin American Spanish</string> | 194 | <string>Latin American Spanish</string> |
| 208 | </property> | 195 | </property> |
| 209 | </item> | 196 | </item> |
| 210 | <item> | 197 | <item> |
| 211 | <property name="text"> | 198 | <property name="text"> |
| 212 | <string>Simplified Chinese</string> | 199 | <string>Simplified Chinese</string> |
| 213 | </property> | 200 | </property> |
| 214 | </item> | 201 | </item> |
| 215 | <item> | 202 | <item> |
| 216 | <property name="text"> | 203 | <property name="text"> |
| 217 | <string>Traditional Chinese (正體中文)</string> | 204 | <string>Traditional Chinese (正體中文)</string> |
| @@ -219,14 +206,14 @@ | |||
| 219 | </item> | 206 | </item> |
| 220 | </widget> | 207 | </widget> |
| 221 | </item> | 208 | </item> |
| 222 | <item row="3" column="0"> | 209 | <item row="2" column="0"> |
| 223 | <widget class="QLabel" name="label_sound"> | 210 | <widget class="QLabel" name="label_sound"> |
| 224 | <property name="text"> | 211 | <property name="text"> |
| 225 | <string>Sound output mode</string> | 212 | <string>Sound output mode</string> |
| 226 | </property> | 213 | </property> |
| 227 | </widget> | 214 | </widget> |
| 228 | </item> | 215 | </item> |
| 229 | <item row="3" column="1"> | 216 | <item row="2" column="1"> |
| 230 | <widget class="QComboBox" name="combo_sound"> | 217 | <widget class="QComboBox" name="combo_sound"> |
| 231 | <item> | 218 | <item> |
| 232 | <property name="text"> | 219 | <property name="text"> |
| @@ -245,14 +232,7 @@ | |||
| 245 | </item> | 232 | </item> |
| 246 | </widget> | 233 | </widget> |
| 247 | </item> | 234 | </item> |
| 248 | <item row="4" column="0"> | 235 | <item row="3" column="1"> |
| 249 | <widget class="QLabel" name="label_console_id"> | ||
| 250 | <property name="text"> | ||
| 251 | <string>Console ID:</string> | ||
| 252 | </property> | ||
| 253 | </widget> | ||
| 254 | </item> | ||
| 255 | <item row="4" column="1"> | ||
| 256 | <widget class="QPushButton" name="button_regenerate_console_id"> | 236 | <widget class="QPushButton" name="button_regenerate_console_id"> |
| 257 | <property name="sizePolicy"> | 237 | <property name="sizePolicy"> |
| 258 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> | 238 | <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> |
| @@ -272,6 +252,143 @@ | |||
| 272 | </widget> | 252 | </widget> |
| 273 | </item> | 253 | </item> |
| 274 | <item> | 254 | <item> |
| 255 | <widget class="QGroupBox" name="gridGroupBox"> | ||
| 256 | <property name="title"> | ||
| 257 | <string>Profile Manager</string> | ||
| 258 | </property> | ||
| 259 | <layout class="QGridLayout" name="gridLayout_2"> | ||
| 260 | <property name="sizeConstraint"> | ||
| 261 | <enum>QLayout::SetNoConstraint</enum> | ||
| 262 | </property> | ||
| 263 | <item row="0" column="0"> | ||
| 264 | <layout class="QHBoxLayout" name="horizontalLayout_2"> | ||
| 265 | <item> | ||
| 266 | <widget class="QLabel" name="label"> | ||
| 267 | <property name="sizePolicy"> | ||
| 268 | <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> | ||
| 269 | <horstretch>0</horstretch> | ||
| 270 | <verstretch>0</verstretch> | ||
| 271 | </sizepolicy> | ||
| 272 | </property> | ||
| 273 | <property name="text"> | ||
| 274 | <string>Current User</string> | ||
| 275 | </property> | ||
| 276 | </widget> | ||
| 277 | </item> | ||
| 278 | <item> | ||
| 279 | <widget class="QGraphicsView" name="current_user_icon"> | ||
| 280 | <property name="minimumSize"> | ||
| 281 | <size> | ||
| 282 | <width>48</width> | ||
| 283 | <height>48</height> | ||
| 284 | </size> | ||
| 285 | </property> | ||
| 286 | <property name="maximumSize"> | ||
| 287 | <size> | ||
| 288 | <width>48</width> | ||
| 289 | <height>48</height> | ||
| 290 | </size> | ||
| 291 | </property> | ||
| 292 | <property name="verticalScrollBarPolicy"> | ||
| 293 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 294 | </property> | ||
| 295 | <property name="horizontalScrollBarPolicy"> | ||
| 296 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 297 | </property> | ||
| 298 | <property name="interactive"> | ||
| 299 | <bool>false</bool> | ||
| 300 | </property> | ||
| 301 | </widget> | ||
| 302 | </item> | ||
| 303 | <item> | ||
| 304 | <widget class="QLabel" name="current_user_username"> | ||
| 305 | <property name="sizePolicy"> | ||
| 306 | <sizepolicy hsizetype="Preferred" vsizetype="Minimum"> | ||
| 307 | <horstretch>0</horstretch> | ||
| 308 | <verstretch>0</verstretch> | ||
| 309 | </sizepolicy> | ||
| 310 | </property> | ||
| 311 | <property name="text"> | ||
| 312 | <string>Username</string> | ||
| 313 | </property> | ||
| 314 | </widget> | ||
| 315 | </item> | ||
| 316 | </layout> | ||
| 317 | </item> | ||
| 318 | <item row="1" column="0"> | ||
| 319 | <widget class="QScrollArea" name="scrollArea"> | ||
| 320 | <property name="sizePolicy"> | ||
| 321 | <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> | ||
| 322 | <horstretch>0</horstretch> | ||
| 323 | <verstretch>0</verstretch> | ||
| 324 | </sizepolicy> | ||
| 325 | </property> | ||
| 326 | <property name="frameShape"> | ||
| 327 | <enum>QFrame::StyledPanel</enum> | ||
| 328 | </property> | ||
| 329 | <property name="widgetResizable"> | ||
| 330 | <bool>false</bool> | ||
| 331 | </property> | ||
| 332 | </widget> | ||
| 333 | </item> | ||
| 334 | <item row="2" column="0"> | ||
| 335 | <layout class="QHBoxLayout" name="horizontalLayout_3"> | ||
| 336 | <item> | ||
| 337 | <widget class="QPushButton" name="pm_set_image"> | ||
| 338 | <property name="enabled"> | ||
| 339 | <bool>false</bool> | ||
| 340 | </property> | ||
| 341 | <property name="text"> | ||
| 342 | <string>Set Image</string> | ||
| 343 | </property> | ||
| 344 | </widget> | ||
| 345 | </item> | ||
| 346 | <item> | ||
| 347 | <spacer name="horizontalSpacer"> | ||
| 348 | <property name="orientation"> | ||
| 349 | <enum>Qt::Horizontal</enum> | ||
| 350 | </property> | ||
| 351 | <property name="sizeHint" stdset="0"> | ||
| 352 | <size> | ||
| 353 | <width>40</width> | ||
| 354 | <height>20</height> | ||
| 355 | </size> | ||
| 356 | </property> | ||
| 357 | </spacer> | ||
| 358 | </item> | ||
| 359 | <item> | ||
| 360 | <widget class="QPushButton" name="pm_add"> | ||
| 361 | <property name="text"> | ||
| 362 | <string>Add</string> | ||
| 363 | </property> | ||
| 364 | </widget> | ||
| 365 | </item> | ||
| 366 | <item> | ||
| 367 | <widget class="QPushButton" name="pm_rename"> | ||
| 368 | <property name="enabled"> | ||
| 369 | <bool>false</bool> | ||
| 370 | </property> | ||
| 371 | <property name="text"> | ||
| 372 | <string>Rename</string> | ||
| 373 | </property> | ||
| 374 | </widget> | ||
| 375 | </item> | ||
| 376 | <item> | ||
| 377 | <widget class="QPushButton" name="pm_remove"> | ||
| 378 | <property name="enabled"> | ||
| 379 | <bool>false</bool> | ||
| 380 | </property> | ||
| 381 | <property name="text"> | ||
| 382 | <string>Remove</string> | ||
| 383 | </property> | ||
| 384 | </widget> | ||
| 385 | </item> | ||
| 386 | </layout> | ||
| 387 | </item> | ||
| 388 | </layout> | ||
| 389 | </widget> | ||
| 390 | </item> | ||
| 391 | <item> | ||
| 275 | <widget class="QLabel" name="label_disable_info"> | 392 | <widget class="QLabel" name="label_disable_info"> |
| 276 | <property name="text"> | 393 | <property name="text"> |
| 277 | <string>System settings are available only when game is not running.</string> | 394 | <string>System settings are available only when game is not running.</string> |
| @@ -281,19 +398,6 @@ | |||
| 281 | </property> | 398 | </property> |
| 282 | </widget> | 399 | </widget> |
| 283 | </item> | 400 | </item> |
| 284 | <item> | ||
| 285 | <spacer name="verticalSpacer"> | ||
| 286 | <property name="orientation"> | ||
| 287 | <enum>Qt::Vertical</enum> | ||
| 288 | </property> | ||
| 289 | <property name="sizeHint" stdset="0"> | ||
| 290 | <size> | ||
| 291 | <width>20</width> | ||
| 292 | <height>40</height> | ||
| 293 | </size> | ||
| 294 | </property> | ||
| 295 | </spacer> | ||
| 296 | </item> | ||
| 297 | </layout> | 401 | </layout> |
| 298 | </item> | 402 | </item> |
| 299 | </layout> | 403 | </layout> |
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp index b5c88f944..67ed0ba6d 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints.cpp +++ b/src/yuzu/debugger/graphics/graphics_breakpoints.cpp | |||
| @@ -2,7 +2,6 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <map> | ||
| 6 | #include <QLabel> | 5 | #include <QLabel> |
| 7 | #include <QMetaType> | 6 | #include <QMetaType> |
| 8 | #include <QPushButton> | 7 | #include <QPushButton> |
| @@ -32,21 +31,8 @@ QVariant BreakPointModel::data(const QModelIndex& index, int role) const { | |||
| 32 | switch (role) { | 31 | switch (role) { |
| 33 | case Qt::DisplayRole: { | 32 | case Qt::DisplayRole: { |
| 34 | if (index.column() == 0) { | 33 | if (index.column() == 0) { |
| 35 | static const std::map<Tegra::DebugContext::Event, QString> map = { | 34 | return DebugContextEventToString(event); |
| 36 | {Tegra::DebugContext::Event::MaxwellCommandLoaded, tr("Maxwell command loaded")}, | ||
| 37 | {Tegra::DebugContext::Event::MaxwellCommandProcessed, | ||
| 38 | tr("Maxwell command processed")}, | ||
| 39 | {Tegra::DebugContext::Event::IncomingPrimitiveBatch, | ||
| 40 | tr("Incoming primitive batch")}, | ||
| 41 | {Tegra::DebugContext::Event::FinishedPrimitiveBatch, | ||
| 42 | tr("Finished primitive batch")}, | ||
| 43 | }; | ||
| 44 | |||
| 45 | DEBUG_ASSERT(map.size() == | ||
| 46 | static_cast<std::size_t>(Tegra::DebugContext::Event::NumEvents)); | ||
| 47 | return (map.find(event) != map.end()) ? map.at(event) : QString(); | ||
| 48 | } | 35 | } |
| 49 | |||
| 50 | break; | 36 | break; |
| 51 | } | 37 | } |
| 52 | 38 | ||
| @@ -128,6 +114,23 @@ void BreakPointModel::OnResumed() { | |||
| 128 | active_breakpoint = context->active_breakpoint; | 114 | active_breakpoint = context->active_breakpoint; |
| 129 | } | 115 | } |
| 130 | 116 | ||
| 117 | QString BreakPointModel::DebugContextEventToString(Tegra::DebugContext::Event event) { | ||
| 118 | switch (event) { | ||
| 119 | case Tegra::DebugContext::Event::MaxwellCommandLoaded: | ||
| 120 | return tr("Maxwell command loaded"); | ||
| 121 | case Tegra::DebugContext::Event::MaxwellCommandProcessed: | ||
| 122 | return tr("Maxwell command processed"); | ||
| 123 | case Tegra::DebugContext::Event::IncomingPrimitiveBatch: | ||
| 124 | return tr("Incoming primitive batch"); | ||
| 125 | case Tegra::DebugContext::Event::FinishedPrimitiveBatch: | ||
| 126 | return tr("Finished primitive batch"); | ||
| 127 | case Tegra::DebugContext::Event::NumEvents: | ||
| 128 | break; | ||
| 129 | } | ||
| 130 | |||
| 131 | return tr("Unknown debug context event"); | ||
| 132 | } | ||
| 133 | |||
| 131 | GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( | 134 | GraphicsBreakPointsWidget::GraphicsBreakPointsWidget( |
| 132 | std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) | 135 | std::shared_ptr<Tegra::DebugContext> debug_context, QWidget* parent) |
| 133 | : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( | 136 | : QDockWidget(tr("Maxwell Breakpoints"), parent), Tegra::DebugContext::BreakPointObserver( |
diff --git a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h index 7112b87e6..fb488e38f 100644 --- a/src/yuzu/debugger/graphics/graphics_breakpoints_p.h +++ b/src/yuzu/debugger/graphics/graphics_breakpoints_p.h | |||
| @@ -29,6 +29,8 @@ public: | |||
| 29 | void OnResumed(); | 29 | void OnResumed(); |
| 30 | 30 | ||
| 31 | private: | 31 | private: |
| 32 | static QString DebugContextEventToString(Tegra::DebugContext::Event event); | ||
| 33 | |||
| 32 | std::weak_ptr<Tegra::DebugContext> context_weak; | 34 | std::weak_ptr<Tegra::DebugContext> context_weak; |
| 33 | bool at_breakpoint; | 35 | bool at_breakpoint; |
| 34 | Tegra::DebugContext::Event active_breakpoint; | 36 | Tegra::DebugContext::Event active_breakpoint; |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 67890455a..a5a4aa432 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -16,7 +16,6 @@ | |||
| 16 | #include <fmt/format.h> | 16 | #include <fmt/format.h> |
| 17 | #include "common/common_paths.h" | 17 | #include "common/common_paths.h" |
| 18 | #include "common/common_types.h" | 18 | #include "common/common_types.h" |
| 19 | #include "common/file_util.h" | ||
| 20 | #include "common/logging/log.h" | 19 | #include "common/logging/log.h" |
| 21 | #include "core/file_sys/patch_manager.h" | 20 | #include "core/file_sys/patch_manager.h" |
| 22 | #include "yuzu/compatibility_list.h" | 21 | #include "yuzu/compatibility_list.h" |
| @@ -217,11 +216,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, GMainWindow* parent) | |||
| 217 | tree_view->setContextMenuPolicy(Qt::CustomContextMenu); | 216 | tree_view->setContextMenuPolicy(Qt::CustomContextMenu); |
| 218 | 217 | ||
| 219 | item_model->insertColumns(0, COLUMN_COUNT); | 218 | item_model->insertColumns(0, COLUMN_COUNT); |
| 220 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, "Name"); | 219 | item_model->setHeaderData(COLUMN_NAME, Qt::Horizontal, tr("Name")); |
| 221 | item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, "Compatibility"); | 220 | item_model->setHeaderData(COLUMN_COMPATIBILITY, Qt::Horizontal, tr("Compatibility")); |
| 222 | item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, "Add-ons"); | 221 | item_model->setHeaderData(COLUMN_ADD_ONS, Qt::Horizontal, tr("Add-ons")); |
| 223 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, "File type"); | 222 | item_model->setHeaderData(COLUMN_FILE_TYPE, Qt::Horizontal, tr("File type")); |
| 224 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, "Size"); | 223 | item_model->setHeaderData(COLUMN_SIZE, Qt::Horizontal, tr("Size")); |
| 225 | 224 | ||
| 226 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); | 225 | connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry); |
| 227 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); | 226 | connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu); |
| @@ -387,9 +386,9 @@ void GameList::LoadCompatibilityList() { | |||
| 387 | } | 386 | } |
| 388 | 387 | ||
| 389 | void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { | 388 | void GameList::PopulateAsync(const QString& dir_path, bool deep_scan) { |
| 390 | if (!FileUtil::Exists(dir_path.toStdString()) || | 389 | const QFileInfo dir_info{dir_path}; |
| 391 | !FileUtil::IsDirectory(dir_path.toStdString())) { | 390 | if (!dir_info.exists() || !dir_info.isDir()) { |
| 392 | LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toLocal8Bit().data()); | 391 | LOG_ERROR(Frontend, "Could not find game list folder at {}", dir_path.toStdString()); |
| 393 | search_field->setFilterResult(0, 0); | 392 | search_field->setFilterResult(0, 0); |
| 394 | return; | 393 | return; |
| 395 | } | 394 | } |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 3881aba5f..3d865a12d 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -62,19 +62,24 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, | |||
| 62 | FileSys::VirtualFile update_raw; | 62 | FileSys::VirtualFile update_raw; |
| 63 | loader.ReadUpdateRaw(update_raw); | 63 | loader.ReadUpdateRaw(update_raw); |
| 64 | for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { | 64 | for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { |
| 65 | if (!updatable && kv.first == "Update") | 65 | const bool is_update = kv.first == "Update"; |
| 66 | if (!updatable && is_update) { | ||
| 66 | continue; | 67 | continue; |
| 68 | } | ||
| 69 | |||
| 70 | const QString type = QString::fromStdString(kv.first); | ||
| 67 | 71 | ||
| 68 | if (kv.second.empty()) { | 72 | if (kv.second.empty()) { |
| 69 | out.append(fmt::format("{}\n", kv.first).c_str()); | 73 | out.append(QStringLiteral("%1\n").arg(type)); |
| 70 | } else { | 74 | } else { |
| 71 | auto ver = kv.second; | 75 | auto ver = kv.second; |
| 72 | 76 | ||
| 73 | // Display container name for packed updates | 77 | // Display container name for packed updates |
| 74 | if (ver == "PACKED" && kv.first == "Update") | 78 | if (is_update && ver == "PACKED") { |
| 75 | ver = Loader::GetFileTypeString(loader.GetFileType()); | 79 | ver = Loader::GetFileTypeString(loader.GetFileType()); |
| 80 | } | ||
| 76 | 81 | ||
| 77 | out.append(fmt::format("{} ({})\n", kv.first, ver).c_str()); | 82 | out.append(QStringLiteral("%1 (%2)\n").arg(type, QString::fromStdString(ver))); |
| 78 | } | 83 | } |
| 79 | } | 84 | } |
| 80 | 85 | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index a3bcb134c..b5bfa6741 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. | 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. |
| 11 | #include "core/file_sys/vfs.h" | 11 | #include "core/file_sys/vfs.h" |
| 12 | #include "core/file_sys/vfs_real.h" | 12 | #include "core/file_sys/vfs_real.h" |
| 13 | #include "core/hle/service/acc/profile_manager.h" | ||
| 13 | 14 | ||
| 14 | // These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows | 15 | // These are wrappers to avoid the calls to CreateDirectory and CreateFile becuase of the Windows |
| 15 | // defines. | 16 | // defines. |
| @@ -758,12 +759,43 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 758 | open_target = "Save Data"; | 759 | open_target = "Save Data"; |
| 759 | const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); | 760 | const std::string nand_dir = FileUtil::GetUserPath(FileUtil::UserPath::NANDDir); |
| 760 | ASSERT(program_id != 0); | 761 | ASSERT(program_id != 0); |
| 761 | // TODO(tech4me): Update this to work with arbitrary user profile | 762 | |
| 762 | // Refer to core/hle/service/acc/profile_manager.cpp ProfileManager constructor | 763 | Service::Account::ProfileManager manager{}; |
| 763 | constexpr u128 user_id = {1, 0}; | 764 | const auto user_ids = manager.GetAllUsers(); |
| 765 | QStringList list; | ||
| 766 | for (const auto& user_id : user_ids) { | ||
| 767 | if (user_id == Service::Account::UUID{}) | ||
| 768 | continue; | ||
| 769 | Service::Account::ProfileBase base; | ||
| 770 | if (!manager.GetProfileBase(user_id, base)) | ||
| 771 | continue; | ||
| 772 | |||
| 773 | list.push_back(QString::fromStdString(Common::StringFromFixedZeroTerminatedBuffer( | ||
| 774 | reinterpret_cast<const char*>(base.username.data()), base.username.size()))); | ||
| 775 | } | ||
| 776 | |||
| 777 | bool ok = false; | ||
| 778 | const auto index_string = | ||
| 779 | QInputDialog::getItem(this, tr("Select User"), | ||
| 780 | tr("Please select the user's save data you would like to open."), | ||
| 781 | list, Settings::values.current_user, false, &ok); | ||
| 782 | if (!ok) | ||
| 783 | return; | ||
| 784 | |||
| 785 | const auto index = list.indexOf(index_string); | ||
| 786 | ASSERT(index != -1 && index < 8); | ||
| 787 | |||
| 788 | const auto user_id = manager.GetUser(index); | ||
| 789 | ASSERT(user_id != std::nullopt); | ||
| 764 | path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, | 790 | path = nand_dir + FileSys::SaveDataFactory::GetFullPath(FileSys::SaveDataSpaceId::NandUser, |
| 765 | FileSys::SaveDataType::SaveData, | 791 | FileSys::SaveDataType::SaveData, |
| 766 | program_id, user_id, 0); | 792 | program_id, user_id->uuid, 0); |
| 793 | |||
| 794 | if (!FileUtil::Exists(path)) { | ||
| 795 | FileUtil::CreateFullPath(path); | ||
| 796 | FileUtil::CreateDir(path); | ||
| 797 | } | ||
| 798 | |||
| 767 | break; | 799 | break; |
| 768 | } | 800 | } |
| 769 | case GameListOpenTarget::ModData: { | 801 | case GameListOpenTarget::ModData: { |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 654a15a5c..b456266a6 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/param_package.h" | 10 | #include "common/param_package.h" |
| 11 | #include "core/hle/service/acc/profile_manager.h" | ||
| 11 | #include "core/settings.h" | 12 | #include "core/settings.h" |
| 12 | #include "input_common/main.h" | 13 | #include "input_common/main.h" |
| 13 | #include "yuzu_cmd/config.h" | 14 | #include "yuzu_cmd/config.h" |
| @@ -126,10 +127,10 @@ void Config::ReadValues() { | |||
| 126 | // System | 127 | // System |
| 127 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); | 128 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); |
| 128 | Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true); | 129 | Settings::values.enable_nfc = sdl2_config->GetBoolean("System", "enable_nfc", true); |
| 129 | Settings::values.username = sdl2_config->Get("System", "username", "yuzu"); | 130 | const auto size = sdl2_config->GetInteger("System", "users_size", 0); |
| 130 | if (Settings::values.username.empty()) { | 131 | |
| 131 | Settings::values.username = "yuzu"; | 132 | Settings::values.current_user = std::clamp<int>( |
| 132 | } | 133 | sdl2_config->GetInteger("System", "current_user", 0), 0, Service::Account::MAX_USERS - 1); |
| 133 | 134 | ||
| 134 | // Miscellaneous | 135 | // Miscellaneous |
| 135 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); | 136 | Settings::values.log_filter = sdl2_config->Get("Miscellaneous", "log_filter", "*:Trace"); |