diff options
28 files changed, 468 insertions, 427 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 1c0e49c03..ce46a2c2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt | |||
| @@ -118,8 +118,17 @@ message(STATUS "Target architecture: ${ARCHITECTURE}") | |||
| 118 | # Configure C++ standard | 118 | # Configure C++ standard |
| 119 | # =========================== | 119 | # =========================== |
| 120 | 120 | ||
| 121 | set(CMAKE_CXX_STANDARD 17) | 121 | # boost asio's concept usage doesn't play nicely with some compilers yet. |
| 122 | set(CMAKE_CXX_STANDARD_REQUIRED ON) | 122 | add_definitions(-DBOOST_ASIO_DISABLE_CONCEPTS) |
| 123 | if (MSVC) | ||
| 124 | add_compile_options(/std:c++latest) | ||
| 125 | |||
| 126 | # cubeb and boost still make use of deprecated result_of. | ||
| 127 | add_definitions(-D_HAS_DEPRECATED_RESULT_OF) | ||
| 128 | else() | ||
| 129 | set(CMAKE_CXX_STANDARD 20) | ||
| 130 | set(CMAKE_CXX_STANDARD_REQUIRED ON) | ||
| 131 | endif() | ||
| 123 | 132 | ||
| 124 | # Output binaries to bin/ | 133 | # Output binaries to bin/ |
| 125 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) | 134 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin) |
| @@ -330,14 +339,16 @@ elseif(SDL2_FOUND) | |||
| 330 | endif() | 339 | endif() |
| 331 | 340 | ||
| 332 | # Ensure libusb is properly configured (based on dolphin libusb include) | 341 | # Ensure libusb is properly configured (based on dolphin libusb include) |
| 333 | include(FindPkgConfig) | 342 | if(NOT APPLE) |
| 334 | find_package(LibUSB) | 343 | include(FindPkgConfig) |
| 344 | find_package(LibUSB) | ||
| 345 | endif() | ||
| 335 | if (NOT LIBUSB_FOUND) | 346 | if (NOT LIBUSB_FOUND) |
| 336 | add_subdirectory(externals/libusb) | 347 | add_subdirectory(externals/libusb) |
| 348 | set(LIBUSB_INCLUDE_DIR "") | ||
| 337 | set(LIBUSB_LIBRARIES usb) | 349 | set(LIBUSB_LIBRARIES usb) |
| 338 | endif() | 350 | endif() |
| 339 | 351 | ||
| 340 | |||
| 341 | # Prefer the -pthread flag on Linux. | 352 | # Prefer the -pthread flag on Linux. |
| 342 | set(THREADS_PREFER_PTHREAD_FLAG ON) | 353 | set(THREADS_PREFER_PTHREAD_FLAG ON) |
| 343 | find_package(Threads REQUIRED) | 354 | find_package(Threads REQUIRED) |
| @@ -2,7 +2,7 @@ yuzu emulator | |||
| 2 | ============= | 2 | ============= |
| 3 | [](https://travis-ci.com/yuzu-emu/yuzu) | 3 | [](https://travis-ci.com/yuzu-emu/yuzu) |
| 4 | [](https://dev.azure.com/yuzu-emu/yuzu/) | 4 | [](https://dev.azure.com/yuzu-emu/yuzu/) |
| 5 | [](https://discord.gg/XQV6dn9) | 5 | [](https://discord.com/invite/u77vRWY) |
| 6 | 6 | ||
| 7 | yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/). | 7 | yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/). |
| 8 | 8 | ||
| @@ -16,7 +16,7 @@ yuzu is licensed under the GPLv2 (or any later version). Refer to the license.tx | |||
| 16 | 16 | ||
| 17 | Check out our [website](https://yuzu-emu.org/)! | 17 | Check out our [website](https://yuzu-emu.org/)! |
| 18 | 18 | ||
| 19 | For development discussion, please join us on [Discord](https://discord.gg/XQV6dn9). | 19 | For development discussion, please join us on [Discord](https://discord.com/invite/u77vRWY). |
| 20 | 20 | ||
| 21 | ### Development | 21 | ### Development |
| 22 | 22 | ||
diff --git a/src/common/alignment.h b/src/common/alignment.h index f8c49e079..b37044bb6 100644 --- a/src/common/alignment.h +++ b/src/common/alignment.h | |||
| @@ -11,7 +11,9 @@ namespace Common { | |||
| 11 | template <typename T> | 11 | template <typename T> |
| 12 | constexpr T AlignUp(T value, std::size_t size) { | 12 | constexpr T AlignUp(T value, std::size_t size) { |
| 13 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); | 13 | static_assert(std::is_unsigned_v<T>, "T must be an unsigned value."); |
| 14 | return static_cast<T>(value + (size - value % size) % size); | 14 | auto mod{static_cast<T>(value % size)}; |
| 15 | value -= mod; | ||
| 16 | return static_cast<T>(mod == T{0} ? value : value + size); | ||
| 15 | } | 17 | } |
| 16 | 18 | ||
| 17 | template <typename T> | 19 | template <typename T> |
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 8935a62c3..285277ef8 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp | |||
| @@ -12,6 +12,10 @@ | |||
| 12 | 12 | ||
| 13 | namespace FileSys { | 13 | namespace FileSys { |
| 14 | 14 | ||
| 15 | constexpr u64 NAND_USER_SIZE = 0x680000000; // 26624 MiB | ||
| 16 | constexpr u64 NAND_SYSTEM_SIZE = 0xA0000000; // 2560 MiB | ||
| 17 | constexpr u64 NAND_TOTAL_SIZE = 0x747C00000; // 29820 MiB | ||
| 18 | |||
| 15 | BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_) | 19 | BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir dump_root_) |
| 16 | : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), | 20 | : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), |
| 17 | dump_root(std::move(dump_root_)), | 21 | dump_root(std::move(dump_root_)), |
| @@ -110,30 +114,29 @@ VirtualDir BISFactory::GetImageDirectory() const { | |||
| 110 | 114 | ||
| 111 | u64 BISFactory::GetSystemNANDFreeSpace() const { | 115 | u64 BISFactory::GetSystemNANDFreeSpace() const { |
| 112 | const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system"); | 116 | const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system"); |
| 113 | if (sys_dir == nullptr) | 117 | if (sys_dir == nullptr) { |
| 114 | return 0; | 118 | return GetSystemNANDTotalSpace(); |
| 119 | } | ||
| 115 | 120 | ||
| 116 | return GetSystemNANDTotalSpace() - sys_dir->GetSize(); | 121 | return GetSystemNANDTotalSpace() - sys_dir->GetSize(); |
| 117 | } | 122 | } |
| 118 | 123 | ||
| 119 | u64 BISFactory::GetSystemNANDTotalSpace() const { | 124 | u64 BISFactory::GetSystemNANDTotalSpace() const { |
| 120 | return static_cast<u64>(Settings::values.nand_system_size); | 125 | return NAND_SYSTEM_SIZE; |
| 121 | } | 126 | } |
| 122 | 127 | ||
| 123 | u64 BISFactory::GetUserNANDFreeSpace() const { | 128 | u64 BISFactory::GetUserNANDFreeSpace() const { |
| 124 | const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user"); | 129 | // For some reason games such as BioShock 1 checks whether this is exactly 0x680000000 bytes. |
| 125 | if (usr_dir == nullptr) | 130 | // Set the free space to be 1 MiB less than the total as a workaround to this issue. |
| 126 | return 0; | 131 | return GetUserNANDTotalSpace() - 0x100000; |
| 127 | |||
| 128 | return GetUserNANDTotalSpace() - usr_dir->GetSize(); | ||
| 129 | } | 132 | } |
| 130 | 133 | ||
| 131 | u64 BISFactory::GetUserNANDTotalSpace() const { | 134 | u64 BISFactory::GetUserNANDTotalSpace() const { |
| 132 | return static_cast<u64>(Settings::values.nand_user_size); | 135 | return NAND_USER_SIZE; |
| 133 | } | 136 | } |
| 134 | 137 | ||
| 135 | u64 BISFactory::GetFullNANDTotalSpace() const { | 138 | u64 BISFactory::GetFullNANDTotalSpace() const { |
| 136 | return static_cast<u64>(Settings::values.nand_total_size); | 139 | return NAND_TOTAL_SIZE; |
| 137 | } | 140 | } |
| 138 | 141 | ||
| 139 | VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const { | 142 | VirtualDir BISFactory::GetBCATDirectory(u64 title_id) const { |
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index 5113a1ca6..6f732e4d8 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp | |||
| @@ -10,6 +10,8 @@ | |||
| 10 | 10 | ||
| 11 | namespace FileSys { | 11 | namespace FileSys { |
| 12 | 12 | ||
| 13 | constexpr u64 SDMC_TOTAL_SIZE = 0x10000000000; // 1 TiB | ||
| 14 | |||
| 13 | SDMCFactory::SDMCFactory(VirtualDir dir_) | 15 | SDMCFactory::SDMCFactory(VirtualDir dir_) |
| 14 | : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>( | 16 | : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>( |
| 15 | GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), | 17 | GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), |
| @@ -46,7 +48,7 @@ u64 SDMCFactory::GetSDMCFreeSpace() const { | |||
| 46 | } | 48 | } |
| 47 | 49 | ||
| 48 | u64 SDMCFactory::GetSDMCTotalSpace() const { | 50 | u64 SDMCFactory::GetSDMCTotalSpace() const { |
| 49 | return static_cast<u64>(Settings::values.sdmc_size); | 51 | return SDMC_TOTAL_SIZE; |
| 50 | } | 52 | } |
| 51 | 53 | ||
| 52 | } // namespace FileSys | 54 | } // namespace FileSys |
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index e21300a7c..96ce5957c 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp | |||
| @@ -112,19 +112,26 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ | |||
| 112 | const auto new_path = | 112 | const auto new_path = |
| 113 | FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); | 113 | FileUtil::SanitizePath(new_path_, FileUtil::DirectorySeparator::PlatformDefault); |
| 114 | 114 | ||
| 115 | if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || | ||
| 116 | FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) | ||
| 117 | return nullptr; | ||
| 118 | |||
| 119 | if (cache.find(old_path) != cache.end()) { | 115 | if (cache.find(old_path) != cache.end()) { |
| 120 | auto cached = cache[old_path]; | 116 | auto file = cache[old_path].lock(); |
| 121 | if (!cached.expired()) { | 117 | |
| 122 | auto file = cached.lock(); | 118 | if (!cache[old_path].expired()) { |
| 123 | file->Open(new_path, "r+b"); | 119 | file->Close(); |
| 124 | cache.erase(old_path); | 120 | } |
| 125 | cache[new_path] = file; | 121 | |
| 122 | if (!FileUtil::Exists(old_path) || FileUtil::Exists(new_path) || | ||
| 123 | FileUtil::IsDirectory(old_path) || !FileUtil::Rename(old_path, new_path)) { | ||
| 124 | return nullptr; | ||
| 126 | } | 125 | } |
| 126 | |||
| 127 | cache.erase(old_path); | ||
| 128 | file->Open(new_path, "r+b"); | ||
| 129 | cache[new_path] = file; | ||
| 130 | } else { | ||
| 131 | UNREACHABLE(); | ||
| 132 | return nullptr; | ||
| 127 | } | 133 | } |
| 134 | |||
| 128 | return OpenFile(new_path, Mode::ReadWrite); | 135 | return OpenFile(new_path, Mode::ReadWrite); |
| 129 | } | 136 | } |
| 130 | 137 | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 256449aa7..4e7a0bec9 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -1407,7 +1407,19 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) { | |||
| 1407 | u32 supported_languages = 0; | 1407 | u32 supported_languages = 0; |
| 1408 | FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; | 1408 | FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()}; |
| 1409 | 1409 | ||
| 1410 | const auto res = pm.GetControlMetadata(); | 1410 | const auto res = [this] { |
| 1411 | const auto title_id = system.CurrentProcess()->GetTitleID(); | ||
| 1412 | |||
| 1413 | FileSys::PatchManager pm{title_id}; | ||
| 1414 | auto res = pm.GetControlMetadata(); | ||
| 1415 | if (res.first != nullptr) { | ||
| 1416 | return res; | ||
| 1417 | } | ||
| 1418 | |||
| 1419 | FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)}; | ||
| 1420 | return pm_update.GetControlMetadata(); | ||
| 1421 | }(); | ||
| 1422 | |||
| 1411 | if (res.first != nullptr) { | 1423 | if (res.first != nullptr) { |
| 1412 | supported_languages = res.first->GetSupportedLanguages(); | 1424 | supported_languages = res.first->GetSupportedLanguages(); |
| 1413 | } | 1425 | } |
diff --git a/src/core/settings.h b/src/core/settings.h index b3451a704..3eb336f75 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -346,31 +346,6 @@ struct TouchscreenInput { | |||
| 346 | u32 rotation_angle; | 346 | u32 rotation_angle; |
| 347 | }; | 347 | }; |
| 348 | 348 | ||
| 349 | enum class NANDTotalSize : u64 { | ||
| 350 | S29_1GB = 0x747C00000ULL, | ||
| 351 | }; | ||
| 352 | |||
| 353 | enum class NANDUserSize : u64 { | ||
| 354 | S26GB = 0x680000000ULL, | ||
| 355 | }; | ||
| 356 | |||
| 357 | enum class NANDSystemSize : u64 { | ||
| 358 | S2_5GB = 0xA0000000, | ||
| 359 | }; | ||
| 360 | |||
| 361 | enum class SDMCSize : u64 { | ||
| 362 | S1GB = 0x40000000, | ||
| 363 | S2GB = 0x80000000, | ||
| 364 | S4GB = 0x100000000ULL, | ||
| 365 | S8GB = 0x200000000ULL, | ||
| 366 | S16GB = 0x400000000ULL, | ||
| 367 | S32GB = 0x800000000ULL, | ||
| 368 | S64GB = 0x1000000000ULL, | ||
| 369 | S128GB = 0x2000000000ULL, | ||
| 370 | S256GB = 0x4000000000ULL, | ||
| 371 | S1TB = 0x10000000000ULL, | ||
| 372 | }; | ||
| 373 | |||
| 374 | enum class RendererBackend { | 349 | enum class RendererBackend { |
| 375 | OpenGL = 0, | 350 | OpenGL = 0, |
| 376 | Vulkan = 1, | 351 | Vulkan = 1, |
| @@ -508,10 +483,6 @@ struct Values { | |||
| 508 | bool gamecard_inserted; | 483 | bool gamecard_inserted; |
| 509 | bool gamecard_current_game; | 484 | bool gamecard_current_game; |
| 510 | std::string gamecard_path; | 485 | std::string gamecard_path; |
| 511 | NANDTotalSize nand_total_size; | ||
| 512 | NANDSystemSize nand_system_size; | ||
| 513 | NANDUserSize nand_user_size; | ||
| 514 | SDMCSize sdmc_size; | ||
| 515 | 486 | ||
| 516 | // Debugging | 487 | // Debugging |
| 517 | bool record_frame_times; | 488 | bool record_frame_times; |
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt index 3bd76dd23..317c25bad 100644 --- a/src/input_common/CMakeLists.txt +++ b/src/input_common/CMakeLists.txt | |||
| @@ -30,7 +30,8 @@ if(SDL2_FOUND) | |||
| 30 | target_compile_definitions(input_common PRIVATE HAVE_SDL2) | 30 | target_compile_definitions(input_common PRIVATE HAVE_SDL2) |
| 31 | endif() | 31 | endif() |
| 32 | 32 | ||
| 33 | target_link_libraries(input_common PUBLIC ${LIBUSB_LIBRARIES}) | 33 | target_include_directories(input_common SYSTEM PRIVATE ${LIBUSB_INCLUDE_DIR}) |
| 34 | target_link_libraries(input_common PRIVATE ${LIBUSB_LIBRARIES}) | ||
| 34 | 35 | ||
| 35 | create_target_directory_groups(input_common) | 36 | create_target_directory_groups(input_common) |
| 36 | target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) | 37 | target_link_libraries(input_common PUBLIC core PRIVATE common Boost::boost) |
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp index 6d9f4d9eb..38210ffcb 100644 --- a/src/input_common/gcadapter/gc_adapter.cpp +++ b/src/input_common/gcadapter/gc_adapter.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <chrono> | 5 | #include <chrono> |
| 6 | #include <thread> | 6 | #include <thread> |
| 7 | #include <libusb.h> | ||
| 7 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 8 | #include "input_common/gcadapter/gc_adapter.h" | 9 | #include "input_common/gcadapter/gc_adapter.h" |
| 9 | 10 | ||
| @@ -33,7 +34,7 @@ Adapter::Adapter() { | |||
| 33 | } | 34 | } |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | GCPadStatus Adapter::GetPadStatus(int port, const std::array<u8, 37>& adapter_payload) { | 37 | GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) { |
| 37 | GCPadStatus pad = {}; | 38 | GCPadStatus pad = {}; |
| 38 | bool get_origin = false; | 39 | bool get_origin = false; |
| 39 | 40 | ||
| @@ -198,7 +199,7 @@ void Adapter::StartScanThread() { | |||
| 198 | } | 199 | } |
| 199 | 200 | ||
| 200 | detect_thread_running = true; | 201 | detect_thread_running = true; |
| 201 | detect_thread = std::thread([=] { ScanThreadFunc(); }); | 202 | detect_thread = std::thread(&Adapter::ScanThreadFunc, this); |
| 202 | } | 203 | } |
| 203 | 204 | ||
| 204 | void Adapter::StopScanThread() { | 205 | void Adapter::StopScanThread() { |
| @@ -227,7 +228,7 @@ void Adapter::Setup() { | |||
| 227 | } | 228 | } |
| 228 | 229 | ||
| 229 | if (devices != nullptr) { | 230 | if (devices != nullptr) { |
| 230 | for (std::size_t index = 0; index < device_count; ++index) { | 231 | for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) { |
| 231 | if (CheckDeviceAccess(devices[index])) { | 232 | if (CheckDeviceAccess(devices[index])) { |
| 232 | // GC Adapter found and accessible, registering it | 233 | // GC Adapter found and accessible, registering it |
| 233 | GetGCEndpoint(devices[index]); | 234 | GetGCEndpoint(devices[index]); |
| @@ -357,11 +358,11 @@ void Adapter::Reset() { | |||
| 357 | } | 358 | } |
| 358 | } | 359 | } |
| 359 | 360 | ||
| 360 | bool Adapter::DeviceConnected(int port) { | 361 | bool Adapter::DeviceConnected(std::size_t port) { |
| 361 | return adapter_controllers_status[port] != ControllerTypes::None; | 362 | return adapter_controllers_status[port] != ControllerTypes::None; |
| 362 | } | 363 | } |
| 363 | 364 | ||
| 364 | void Adapter::ResetDeviceType(int port) { | 365 | void Adapter::ResetDeviceType(std::size_t port) { |
| 365 | adapter_controllers_status[port] = ControllerTypes::None; | 366 | adapter_controllers_status[port] = ControllerTypes::None; |
| 366 | } | 367 | } |
| 367 | 368 | ||
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h index b1c2a1958..e2cdd6255 100644 --- a/src/input_common/gcadapter/gc_adapter.h +++ b/src/input_common/gcadapter/gc_adapter.h | |||
| @@ -8,10 +8,13 @@ | |||
| 8 | #include <mutex> | 8 | #include <mutex> |
| 9 | #include <thread> | 9 | #include <thread> |
| 10 | #include <unordered_map> | 10 | #include <unordered_map> |
| 11 | #include <libusb.h> | ||
| 12 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 13 | #include "common/threadsafe_queue.h" | 12 | #include "common/threadsafe_queue.h" |
| 14 | 13 | ||
| 14 | struct libusb_context; | ||
| 15 | struct libusb_device; | ||
| 16 | struct libusb_device_handle; | ||
| 17 | |||
| 15 | namespace GCAdapter { | 18 | namespace GCAdapter { |
| 16 | 19 | ||
| 17 | enum { | 20 | enum { |
| @@ -97,6 +100,9 @@ public: | |||
| 97 | void BeginConfiguration(); | 100 | void BeginConfiguration(); |
| 98 | void EndConfiguration(); | 101 | void EndConfiguration(); |
| 99 | 102 | ||
| 103 | /// Returns true if there is a device connected to port | ||
| 104 | bool DeviceConnected(std::size_t port); | ||
| 105 | |||
| 100 | std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); | 106 | std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue(); |
| 101 | const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; | 107 | const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const; |
| 102 | 108 | ||
| @@ -104,7 +110,7 @@ public: | |||
| 104 | const std::array<GCState, 4>& GetPadState() const; | 110 | const std::array<GCState, 4>& GetPadState() const; |
| 105 | 111 | ||
| 106 | private: | 112 | private: |
| 107 | GCPadStatus GetPadStatus(int port, const std::array<u8, 37>& adapter_payload); | 113 | GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload); |
| 108 | 114 | ||
| 109 | void PadToState(const GCPadStatus& pad, GCState& state); | 115 | void PadToState(const GCPadStatus& pad, GCState& state); |
| 110 | 116 | ||
| @@ -116,11 +122,8 @@ private: | |||
| 116 | /// Stop scanning for the adapter | 122 | /// Stop scanning for the adapter |
| 117 | void StopScanThread(); | 123 | void StopScanThread(); |
| 118 | 124 | ||
| 119 | /// Returns true if there is a device connected to port | ||
| 120 | bool DeviceConnected(int port); | ||
| 121 | |||
| 122 | /// Resets status of device connected to port | 125 | /// Resets status of device connected to port |
| 123 | void ResetDeviceType(int port); | 126 | void ResetDeviceType(std::size_t port); |
| 124 | 127 | ||
| 125 | /// Returns true if we successfully gain access to GC Adapter | 128 | /// Returns true if we successfully gain access to GC Adapter |
| 126 | bool CheckDeviceAccess(libusb_device* device); | 129 | bool CheckDeviceAccess(libusb_device* device); |
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp index 385ce8430..b20419ec3 100644 --- a/src/input_common/gcadapter/gc_poller.cpp +++ b/src/input_common/gcadapter/gc_poller.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <list> | 6 | #include <list> |
| 7 | #include <mutex> | 7 | #include <mutex> |
| 8 | #include <utility> | 8 | #include <utility> |
| 9 | #include "common/assert.h" | ||
| 9 | #include "common/threadsafe_queue.h" | 10 | #include "common/threadsafe_queue.h" |
| 10 | #include "input_common/gcadapter/gc_adapter.h" | 11 | #include "input_common/gcadapter/gc_adapter.h" |
| 11 | #include "input_common/gcadapter/gc_poller.h" | 12 | #include "input_common/gcadapter/gc_poller.h" |
| @@ -20,7 +21,10 @@ public: | |||
| 20 | ~GCButton() override; | 21 | ~GCButton() override; |
| 21 | 22 | ||
| 22 | bool GetStatus() const override { | 23 | bool GetStatus() const override { |
| 23 | return gcadapter->GetPadState()[port].buttons.at(button); | 24 | if (gcadapter->DeviceConnected(port)) { |
| 25 | return gcadapter->GetPadState()[port].buttons.at(button); | ||
| 26 | } | ||
| 27 | return false; | ||
| 24 | } | 28 | } |
| 25 | 29 | ||
| 26 | private: | 30 | private: |
| @@ -43,13 +47,17 @@ public: | |||
| 43 | } | 47 | } |
| 44 | 48 | ||
| 45 | bool GetStatus() const override { | 49 | bool GetStatus() const override { |
| 46 | const float axis_value = (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f; | 50 | if (gcadapter->DeviceConnected(port)) { |
| 47 | if (trigger_if_greater) { | 51 | const float axis_value = |
| 48 | // TODO: Might be worthwile to set a slider for the trigger threshold. It is currently | 52 | (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 128.0f; |
| 49 | // always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick | 53 | if (trigger_if_greater) { |
| 50 | return axis_value > threshold; | 54 | // TODO: Might be worthwile to set a slider for the trigger threshold. It is |
| 55 | // currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick | ||
| 56 | return axis_value > threshold; | ||
| 57 | } | ||
| 58 | return axis_value < -threshold; | ||
| 51 | } | 59 | } |
| 52 | return axis_value < -threshold; | 60 | return false; |
| 53 | } | 61 | } |
| 54 | 62 | ||
| 55 | private: | 63 | private: |
| @@ -94,9 +102,12 @@ std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::Param | |||
| 94 | return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater, | 102 | return std::make_unique<GCAxisButton>(port, axis, threshold, trigger_if_greater, |
| 95 | adapter.get()); | 103 | adapter.get()); |
| 96 | } | 104 | } |
| 105 | |||
| 106 | UNREACHABLE(); | ||
| 107 | return nullptr; | ||
| 97 | } | 108 | } |
| 98 | 109 | ||
| 99 | Common::ParamPackage GCButtonFactory::GetNextInput() { | 110 | Common::ParamPackage GCButtonFactory::GetNextInput() const { |
| 100 | Common::ParamPackage params; | 111 | Common::ParamPackage params; |
| 101 | GCAdapter::GCPadStatus pad; | 112 | GCAdapter::GCPadStatus pad; |
| 102 | auto& queue = adapter->GetPadQueue(); | 113 | auto& queue = adapter->GetPadQueue(); |
| @@ -147,11 +158,14 @@ public: | |||
| 147 | : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {} | 158 | : port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter) {} |
| 148 | 159 | ||
| 149 | float GetAxis(int axis) const { | 160 | float GetAxis(int axis) const { |
| 150 | std::lock_guard lock{mutex}; | 161 | if (gcadapter->DeviceConnected(port)) { |
| 151 | // division is not by a perfect 128 to account for some variance in center location | 162 | std::lock_guard lock{mutex}; |
| 152 | // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range | 163 | // division is not by a perfect 128 to account for some variance in center location |
| 153 | // [20-230] | 164 | // e.g. my device idled at 131 in X, 120 in Y, and full range of motion was in range |
| 154 | return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f; | 165 | // [20-230] |
| 166 | return (gcadapter->GetPadState()[port].axes.at(axis) - 128.0f) / 95.0f; | ||
| 167 | } | ||
| 168 | return 0.0f; | ||
| 155 | } | 169 | } |
| 156 | 170 | ||
| 157 | std::pair<float, float> GetAnalog(int axis_x, int axis_y) const { | 171 | std::pair<float, float> GetAnalog(int axis_x, int axis_y) const { |
| @@ -249,7 +263,7 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() { | |||
| 249 | const u8 axis = static_cast<u8>(pad.axis); | 263 | const u8 axis = static_cast<u8>(pad.axis); |
| 250 | if (analog_x_axis == -1) { | 264 | if (analog_x_axis == -1) { |
| 251 | analog_x_axis = axis; | 265 | analog_x_axis = axis; |
| 252 | controller_number = port; | 266 | controller_number = static_cast<int>(port); |
| 253 | } else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) { | 267 | } else if (analog_y_axis == -1 && analog_x_axis != axis && controller_number == port) { |
| 254 | analog_y_axis = axis; | 268 | analog_y_axis = axis; |
| 255 | } | 269 | } |
diff --git a/src/input_common/gcadapter/gc_poller.h b/src/input_common/gcadapter/gc_poller.h index e96af7d51..0527f328f 100644 --- a/src/input_common/gcadapter/gc_poller.h +++ b/src/input_common/gcadapter/gc_poller.h | |||
| @@ -25,7 +25,7 @@ public: | |||
| 25 | */ | 25 | */ |
| 26 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override; | 26 | std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override; |
| 27 | 27 | ||
| 28 | Common::ParamPackage GetNextInput(); | 28 | Common::ParamPackage GetNextInput() const; |
| 29 | 29 | ||
| 30 | /// For device input configuration/polling | 30 | /// For device input configuration/polling |
| 31 | void BeginConfiguration(); | 31 | void BeginConfiguration(); |
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp index fd0af1019..b9d5d0ec3 100644 --- a/src/input_common/main.cpp +++ b/src/input_common/main.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <memory> | 5 | #include <memory> |
| 6 | #include <thread> | 6 | #include <thread> |
| 7 | #include <libusb.h> | ||
| 8 | #include "common/param_package.h" | 7 | #include "common/param_package.h" |
| 9 | #include "input_common/analog_from_button.h" | 8 | #include "input_common/analog_from_button.h" |
| 10 | #include "input_common/gcadapter/gc_adapter.h" | 9 | #include "input_common/gcadapter/gc_adapter.h" |
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp index da5227058..e63c73c4f 100644 --- a/src/input_common/udp/client.cpp +++ b/src/input_common/udp/client.cpp | |||
| @@ -234,7 +234,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob( | |||
| 234 | std::function<void(Status)> status_callback, | 234 | std::function<void(Status)> status_callback, |
| 235 | std::function<void(u16, u16, u16, u16)> data_callback) { | 235 | std::function<void(u16, u16, u16, u16)> data_callback) { |
| 236 | 236 | ||
| 237 | std::thread([=] { | 237 | std::thread([=, this] { |
| 238 | constexpr u16 CALIBRATION_THRESHOLD = 100; | 238 | constexpr u16 CALIBRATION_THRESHOLD = 100; |
| 239 | 239 | ||
| 240 | u16 min_x{UINT16_MAX}; | 240 | u16 min_x{UINT16_MAX}; |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 380ed532b..7625871c2 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -332,23 +332,23 @@ private: | |||
| 332 | 332 | ||
| 333 | if constexpr (has_extended_dynamic_state) { | 333 | if constexpr (has_extended_dynamic_state) { |
| 334 | // With extended dynamic states we can specify the length and stride of a vertex buffer | 334 | // With extended dynamic states we can specify the length and stride of a vertex buffer |
| 335 | // std::array<VkDeviceSize, N> sizes; | 335 | std::array<VkDeviceSize, N> sizes; |
| 336 | std::array<u16, N> strides; | 336 | std::array<u16, N> strides; |
| 337 | // std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin()); | 337 | std::copy(vertex.sizes.begin(), vertex.sizes.begin() + N, sizes.begin()); |
| 338 | std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin()); | 338 | std::copy(vertex.strides.begin(), vertex.strides.begin() + N, strides.begin()); |
| 339 | 339 | ||
| 340 | if constexpr (is_indexed) { | 340 | if constexpr (is_indexed) { |
| 341 | scheduler.Record( | 341 | scheduler.Record( |
| 342 | [buffers, offsets, strides, index = index](vk::CommandBuffer cmdbuf) { | 342 | [buffers, offsets, sizes, strides, index = index](vk::CommandBuffer cmdbuf) { |
| 343 | cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type); | 343 | cmdbuf.BindIndexBuffer(index.buffer, index.offset, index.type); |
| 344 | cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(), | 344 | cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(), |
| 345 | offsets.data(), nullptr, | 345 | offsets.data(), sizes.data(), |
| 346 | ExpandStrides(strides).data()); | 346 | ExpandStrides(strides).data()); |
| 347 | }); | 347 | }); |
| 348 | } else { | 348 | } else { |
| 349 | scheduler.Record([buffers, offsets, strides](vk::CommandBuffer cmdbuf) { | 349 | scheduler.Record([buffers, offsets, sizes, strides](vk::CommandBuffer cmdbuf) { |
| 350 | cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(), | 350 | cmdbuf.BindVertexBuffers2EXT(0, static_cast<u32>(N), buffers.data(), |
| 351 | offsets.data(), nullptr, | 351 | offsets.data(), sizes.data(), |
| 352 | ExpandStrides(strides).data()); | 352 | ExpandStrides(strides).data()); |
| 353 | }); | 353 | }); |
| 354 | } | 354 | } |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 5f175b989..a862b2610 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -104,11 +104,13 @@ add_executable(yuzu | |||
| 104 | game_list_p.h | 104 | game_list_p.h |
| 105 | game_list_worker.cpp | 105 | game_list_worker.cpp |
| 106 | game_list_worker.h | 106 | game_list_worker.h |
| 107 | hotkeys.cpp | ||
| 108 | hotkeys.h | ||
| 109 | install_dialog.cpp | ||
| 110 | install_dialog.h | ||
| 107 | loading_screen.cpp | 111 | loading_screen.cpp |
| 108 | loading_screen.h | 112 | loading_screen.h |
| 109 | loading_screen.ui | 113 | loading_screen.ui |
| 110 | hotkeys.cpp | ||
| 111 | hotkeys.h | ||
| 112 | main.cpp | 114 | main.cpp |
| 113 | main.h | 115 | main.h |
| 114 | main.ui | 116 | main.ui |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 430e78e5f..9e9b38214 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -505,22 +505,6 @@ void Config::ReadDataStorageValues() { | |||
| 505 | ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool(); | 505 | ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool(); |
| 506 | Settings::values.gamecard_path = | 506 | Settings::values.gamecard_path = |
| 507 | ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString(); | 507 | ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString(); |
| 508 | Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>( | ||
| 509 | ReadSetting(QStringLiteral("nand_total_size"), | ||
| 510 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB))) | ||
| 511 | .toULongLong()); | ||
| 512 | Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>( | ||
| 513 | ReadSetting(QStringLiteral("nand_user_size"), | ||
| 514 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB))) | ||
| 515 | .toULongLong()); | ||
| 516 | Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>( | ||
| 517 | ReadSetting(QStringLiteral("nand_system_size"), | ||
| 518 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB))) | ||
| 519 | .toULongLong()); | ||
| 520 | Settings::values.sdmc_size = static_cast<Settings::SDMCSize>( | ||
| 521 | ReadSetting(QStringLiteral("sdmc_size"), | ||
| 522 | QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB))) | ||
| 523 | .toULongLong()); | ||
| 524 | 508 | ||
| 525 | qt_config->endGroup(); | 509 | qt_config->endGroup(); |
| 526 | } | 510 | } |
| @@ -1034,18 +1018,7 @@ void Config::SaveDataStorageValues() { | |||
| 1034 | false); | 1018 | false); |
| 1035 | WriteSetting(QStringLiteral("gamecard_path"), | 1019 | WriteSetting(QStringLiteral("gamecard_path"), |
| 1036 | QString::fromStdString(Settings::values.gamecard_path), QStringLiteral("")); | 1020 | QString::fromStdString(Settings::values.gamecard_path), QStringLiteral("")); |
| 1037 | WriteSetting(QStringLiteral("nand_total_size"), | 1021 | |
| 1038 | QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_total_size)), | ||
| 1039 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB))); | ||
| 1040 | WriteSetting(QStringLiteral("nand_user_size"), | ||
| 1041 | QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_user_size)), | ||
| 1042 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB))); | ||
| 1043 | WriteSetting(QStringLiteral("nand_system_size"), | ||
| 1044 | QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_system_size)), | ||
| 1045 | QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB))); | ||
| 1046 | WriteSetting(QStringLiteral("sdmc_size"), | ||
| 1047 | QVariant::fromValue<u64>(static_cast<u64>(Settings::values.sdmc_size)), | ||
| 1048 | QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB))); | ||
| 1049 | qt_config->endGroup(); | 1022 | qt_config->endGroup(); |
| 1050 | } | 1023 | } |
| 1051 | 1024 | ||
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp index 835ee821c..a089f5733 100644 --- a/src/yuzu/configuration/configure_filesystem.cpp +++ b/src/yuzu/configuration/configure_filesystem.cpp | |||
| @@ -11,19 +11,6 @@ | |||
| 11 | #include "yuzu/configuration/configure_filesystem.h" | 11 | #include "yuzu/configuration/configure_filesystem.h" |
| 12 | #include "yuzu/uisettings.h" | 12 | #include "yuzu/uisettings.h" |
| 13 | 13 | ||
| 14 | namespace { | ||
| 15 | |||
| 16 | template <typename T> | ||
| 17 | void SetComboBoxFromData(QComboBox* combo_box, T data) { | ||
| 18 | const auto index = combo_box->findData(QVariant::fromValue(static_cast<u64>(data))); | ||
| 19 | if (index >= combo_box->count() || index < 0) | ||
| 20 | return; | ||
| 21 | |||
| 22 | combo_box->setCurrentIndex(index); | ||
| 23 | } | ||
| 24 | |||
| 25 | } // Anonymous namespace | ||
| 26 | |||
| 27 | ConfigureFilesystem::ConfigureFilesystem(QWidget* parent) | 14 | ConfigureFilesystem::ConfigureFilesystem(QWidget* parent) |
| 28 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) { | 15 | : QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) { |
| 29 | ui->setupUi(this); | 16 | ui->setupUi(this); |
| @@ -73,11 +60,6 @@ void ConfigureFilesystem::setConfiguration() { | |||
| 73 | 60 | ||
| 74 | ui->cache_game_list->setChecked(UISettings::values.cache_game_list); | 61 | ui->cache_game_list->setChecked(UISettings::values.cache_game_list); |
| 75 | 62 | ||
| 76 | SetComboBoxFromData(ui->nand_size, Settings::values.nand_total_size); | ||
| 77 | SetComboBoxFromData(ui->usrnand_size, Settings::values.nand_user_size); | ||
| 78 | SetComboBoxFromData(ui->sysnand_size, Settings::values.nand_system_size); | ||
| 79 | SetComboBoxFromData(ui->sdmc_size, Settings::values.sdmc_size); | ||
| 80 | |||
| 81 | UpdateEnabledControls(); | 63 | UpdateEnabledControls(); |
| 82 | } | 64 | } |
| 83 | 65 | ||
| @@ -98,15 +80,6 @@ void ConfigureFilesystem::applyConfiguration() { | |||
| 98 | Settings::values.dump_nso = ui->dump_nso->isChecked(); | 80 | Settings::values.dump_nso = ui->dump_nso->isChecked(); |
| 99 | 81 | ||
| 100 | UISettings::values.cache_game_list = ui->cache_game_list->isChecked(); | 82 | UISettings::values.cache_game_list = ui->cache_game_list->isChecked(); |
| 101 | |||
| 102 | Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>( | ||
| 103 | ui->nand_size->itemData(ui->nand_size->currentIndex()).toULongLong()); | ||
| 104 | Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>( | ||
| 105 | ui->nand_size->itemData(ui->sysnand_size->currentIndex()).toULongLong()); | ||
| 106 | Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>( | ||
| 107 | ui->nand_size->itemData(ui->usrnand_size->currentIndex()).toULongLong()); | ||
| 108 | Settings::values.sdmc_size = static_cast<Settings::SDMCSize>( | ||
| 109 | ui->nand_size->itemData(ui->sdmc_size->currentIndex()).toULongLong()); | ||
| 110 | } | 83 | } |
| 111 | 84 | ||
| 112 | void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) { | 85 | void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) { |
diff --git a/src/yuzu/configuration/configure_filesystem.ui b/src/yuzu/configuration/configure_filesystem.ui index 58cd07f52..84bea0600 100644 --- a/src/yuzu/configuration/configure_filesystem.ui +++ b/src/yuzu/configuration/configure_filesystem.ui | |||
| @@ -116,127 +116,6 @@ | |||
| 116 | </widget> | 116 | </widget> |
| 117 | </item> | 117 | </item> |
| 118 | <item> | 118 | <item> |
| 119 | <widget class="QGroupBox" name="groupBox_3"> | ||
| 120 | <property name="title"> | ||
| 121 | <string>Storage Sizes</string> | ||
| 122 | </property> | ||
| 123 | <layout class="QGridLayout" name="gridLayout_3"> | ||
| 124 | <item row="3" column="0"> | ||
| 125 | <widget class="QLabel" name="label_5"> | ||
| 126 | <property name="text"> | ||
| 127 | <string>SD Card</string> | ||
| 128 | </property> | ||
| 129 | </widget> | ||
| 130 | </item> | ||
| 131 | <item row="1" column="0"> | ||
| 132 | <widget class="QLabel" name="label_4"> | ||
| 133 | <property name="text"> | ||
| 134 | <string>System NAND</string> | ||
| 135 | </property> | ||
| 136 | </widget> | ||
| 137 | </item> | ||
| 138 | <item row="1" column="1"> | ||
| 139 | <widget class="QComboBox" name="sysnand_size"> | ||
| 140 | <item> | ||
| 141 | <property name="text"> | ||
| 142 | <string>2.5 GB</string> | ||
| 143 | </property> | ||
| 144 | </item> | ||
| 145 | </widget> | ||
| 146 | </item> | ||
| 147 | <item row="3" column="1"> | ||
| 148 | <widget class="QComboBox" name="sdmc_size"> | ||
| 149 | <property name="currentText"> | ||
| 150 | <string>32 GB</string> | ||
| 151 | </property> | ||
| 152 | <item> | ||
| 153 | <property name="text"> | ||
| 154 | <string>1 GB</string> | ||
| 155 | </property> | ||
| 156 | </item> | ||
| 157 | <item> | ||
| 158 | <property name="text"> | ||
| 159 | <string>2 GB</string> | ||
| 160 | </property> | ||
| 161 | </item> | ||
| 162 | <item> | ||
| 163 | <property name="text"> | ||
| 164 | <string>4 GB</string> | ||
| 165 | </property> | ||
| 166 | </item> | ||
| 167 | <item> | ||
| 168 | <property name="text"> | ||
| 169 | <string>8 GB</string> | ||
| 170 | </property> | ||
| 171 | </item> | ||
| 172 | <item> | ||
| 173 | <property name="text"> | ||
| 174 | <string>16 GB</string> | ||
| 175 | </property> | ||
| 176 | </item> | ||
| 177 | <item> | ||
| 178 | <property name="text"> | ||
| 179 | <string>32 GB</string> | ||
| 180 | </property> | ||
| 181 | </item> | ||
| 182 | <item> | ||
| 183 | <property name="text"> | ||
| 184 | <string>64 GB</string> | ||
| 185 | </property> | ||
| 186 | </item> | ||
| 187 | <item> | ||
| 188 | <property name="text"> | ||
| 189 | <string>128 GB</string> | ||
| 190 | </property> | ||
| 191 | </item> | ||
| 192 | <item> | ||
| 193 | <property name="text"> | ||
| 194 | <string>256 GB</string> | ||
| 195 | </property> | ||
| 196 | </item> | ||
| 197 | <item> | ||
| 198 | <property name="text"> | ||
| 199 | <string>1 TB</string> | ||
| 200 | </property> | ||
| 201 | </item> | ||
| 202 | </widget> | ||
| 203 | </item> | ||
| 204 | <item row="2" column="1"> | ||
| 205 | <widget class="QComboBox" name="usrnand_size"> | ||
| 206 | <item> | ||
| 207 | <property name="text"> | ||
| 208 | <string>26 GB</string> | ||
| 209 | </property> | ||
| 210 | </item> | ||
| 211 | </widget> | ||
| 212 | </item> | ||
| 213 | <item row="2" column="0"> | ||
| 214 | <widget class="QLabel" name="label_6"> | ||
| 215 | <property name="text"> | ||
| 216 | <string>User NAND</string> | ||
| 217 | </property> | ||
| 218 | </widget> | ||
| 219 | </item> | ||
| 220 | <item row="0" column="0"> | ||
| 221 | <widget class="QLabel" name="label_7"> | ||
| 222 | <property name="text"> | ||
| 223 | <string>NAND</string> | ||
| 224 | </property> | ||
| 225 | </widget> | ||
| 226 | </item> | ||
| 227 | <item row="0" column="1"> | ||
| 228 | <widget class="QComboBox" name="nand_size"> | ||
| 229 | <item> | ||
| 230 | <property name="text"> | ||
| 231 | <string>29.1 GB</string> | ||
| 232 | </property> | ||
| 233 | </item> | ||
| 234 | </widget> | ||
| 235 | </item> | ||
| 236 | </layout> | ||
| 237 | </widget> | ||
| 238 | </item> | ||
| 239 | <item> | ||
| 240 | <widget class="QGroupBox" name="groupBox_4"> | 119 | <widget class="QGroupBox" name="groupBox_4"> |
| 241 | <property name="title"> | 120 | <property name="title"> |
| 242 | <string>Patch Manager</string> | 121 | <string>Patch Manager</string> |
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp index 1fb62d1cf..20316c9cc 100644 --- a/src/yuzu/configuration/configure_general.cpp +++ b/src/yuzu/configuration/configure_general.cpp | |||
| @@ -65,6 +65,8 @@ void ConfigureGeneral::ApplyConfiguration() { | |||
| 65 | Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() == | 65 | Settings::values.use_frame_limit.SetValue(ui->toggle_frame_limit->checkState() == |
| 66 | Qt::Checked); | 66 | Qt::Checked); |
| 67 | Settings::values.frame_limit.SetValue(ui->frame_limit->value()); | 67 | Settings::values.frame_limit.SetValue(ui->frame_limit->value()); |
| 68 | } | ||
| 69 | if (Settings::values.use_multi_core.UsingGlobal()) { | ||
| 68 | Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked()); | 70 | Settings::values.use_multi_core.SetValue(ui->use_multi_core->isChecked()); |
| 69 | } | 71 | } |
| 70 | } else { | 72 | } else { |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index bfb600df0..ab7fc7a24 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -531,8 +531,8 @@ void GameList::AddPermDirPopup(QMenu& context_menu, QModelIndex selected) { | |||
| 531 | UISettings::GameDir& game_dir = | 531 | UISettings::GameDir& game_dir = |
| 532 | *selected.data(GameListDir::GameDirRole).value<UISettings::GameDir*>(); | 532 | *selected.data(GameListDir::GameDirRole).value<UISettings::GameDir*>(); |
| 533 | 533 | ||
| 534 | QAction* move_up = context_menu.addAction(tr(u8"\U000025b2 Move Up")); | 534 | QAction* move_up = context_menu.addAction(tr("\u25B2 Move Up")); |
| 535 | QAction* move_down = context_menu.addAction(tr(u8"\U000025bc Move Down ")); | 535 | QAction* move_down = context_menu.addAction(tr("\u25bc Move Down")); |
| 536 | QAction* open_directory_location = context_menu.addAction(tr("Open Directory Location")); | 536 | QAction* open_directory_location = context_menu.addAction(tr("Open Directory Location")); |
| 537 | 537 | ||
| 538 | const int row = selected.row(); | 538 | const int row = selected.row(); |
diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp new file mode 100644 index 000000000..06b0b1874 --- /dev/null +++ b/src/yuzu/install_dialog.cpp | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <QCheckBox> | ||
| 6 | #include <QDialogButtonBox> | ||
| 7 | #include <QFileInfo> | ||
| 8 | #include <QHBoxLayout> | ||
| 9 | #include <QLabel> | ||
| 10 | #include <QListWidget> | ||
| 11 | #include <QVBoxLayout> | ||
| 12 | #include "yuzu/install_dialog.h" | ||
| 13 | #include "yuzu/uisettings.h" | ||
| 14 | |||
| 15 | InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialog(parent) { | ||
| 16 | file_list = new QListWidget(this); | ||
| 17 | |||
| 18 | for (const QString& file : files) { | ||
| 19 | QListWidgetItem* item = new QListWidgetItem(QFileInfo(file).fileName(), file_list); | ||
| 20 | item->setData(Qt::UserRole, file); | ||
| 21 | item->setFlags(item->flags() | Qt::ItemIsUserCheckable); | ||
| 22 | item->setCheckState(Qt::Checked); | ||
| 23 | } | ||
| 24 | |||
| 25 | file_list->setMinimumWidth((file_list->sizeHintForColumn(0) * 11) / 10); | ||
| 26 | |||
| 27 | vbox_layout = new QVBoxLayout; | ||
| 28 | |||
| 29 | hbox_layout = new QHBoxLayout; | ||
| 30 | |||
| 31 | description = new QLabel(tr("Please confirm these are the files you wish to install.")); | ||
| 32 | |||
| 33 | update_description = | ||
| 34 | new QLabel(tr("Installing an Update or DLC will overwrite the previously installed one.")); | ||
| 35 | |||
| 36 | buttons = new QDialogButtonBox; | ||
| 37 | buttons->addButton(QDialogButtonBox::Cancel); | ||
| 38 | buttons->addButton(tr("Install"), QDialogButtonBox::AcceptRole); | ||
| 39 | |||
| 40 | connect(buttons, &QDialogButtonBox::accepted, this, &InstallDialog::accept); | ||
| 41 | connect(buttons, &QDialogButtonBox::rejected, this, &InstallDialog::reject); | ||
| 42 | |||
| 43 | hbox_layout->addWidget(buttons); | ||
| 44 | |||
| 45 | vbox_layout->addWidget(description); | ||
| 46 | vbox_layout->addWidget(update_description); | ||
| 47 | vbox_layout->addWidget(file_list); | ||
| 48 | vbox_layout->addLayout(hbox_layout); | ||
| 49 | |||
| 50 | setLayout(vbox_layout); | ||
| 51 | setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); | ||
| 52 | setWindowTitle(tr("Install Files to NAND")); | ||
| 53 | } | ||
| 54 | |||
| 55 | InstallDialog::~InstallDialog() = default; | ||
| 56 | |||
| 57 | QStringList InstallDialog::GetFiles() const { | ||
| 58 | QStringList files; | ||
| 59 | |||
| 60 | for (int i = 0; i < file_list->count(); ++i) { | ||
| 61 | const QListWidgetItem* item = file_list->item(i); | ||
| 62 | if (item->checkState() == Qt::Checked) { | ||
| 63 | files.append(item->data(Qt::UserRole).toString()); | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | return files; | ||
| 68 | } | ||
| 69 | |||
| 70 | int InstallDialog::GetMinimumWidth() const { | ||
| 71 | return file_list->width(); | ||
| 72 | } | ||
diff --git a/src/yuzu/install_dialog.h b/src/yuzu/install_dialog.h new file mode 100644 index 000000000..e4aba1b06 --- /dev/null +++ b/src/yuzu/install_dialog.h | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <QDialog> | ||
| 8 | |||
| 9 | class QCheckBox; | ||
| 10 | class QDialogButtonBox; | ||
| 11 | class QHBoxLayout; | ||
| 12 | class QLabel; | ||
| 13 | class QListWidget; | ||
| 14 | class QVBoxLayout; | ||
| 15 | |||
| 16 | class InstallDialog : public QDialog { | ||
| 17 | Q_OBJECT | ||
| 18 | |||
| 19 | public: | ||
| 20 | explicit InstallDialog(QWidget* parent, const QStringList& files); | ||
| 21 | ~InstallDialog() override; | ||
| 22 | |||
| 23 | QStringList GetFiles() const; | ||
| 24 | bool ShouldOverwriteFiles() const; | ||
| 25 | int GetMinimumWidth() const; | ||
| 26 | |||
| 27 | private: | ||
| 28 | QListWidget* file_list; | ||
| 29 | |||
| 30 | QVBoxLayout* vbox_layout; | ||
| 31 | QHBoxLayout* hbox_layout; | ||
| 32 | |||
| 33 | QLabel* description; | ||
| 34 | QLabel* update_description; | ||
| 35 | QDialogButtonBox* buttons; | ||
| 36 | }; | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 4d501a8f9..432379705 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -107,6 +107,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 107 | #include "yuzu/game_list.h" | 107 | #include "yuzu/game_list.h" |
| 108 | #include "yuzu/game_list_p.h" | 108 | #include "yuzu/game_list_p.h" |
| 109 | #include "yuzu/hotkeys.h" | 109 | #include "yuzu/hotkeys.h" |
| 110 | #include "yuzu/install_dialog.h" | ||
| 110 | #include "yuzu/loading_screen.h" | 111 | #include "yuzu/loading_screen.h" |
| 111 | #include "yuzu/main.h" | 112 | #include "yuzu/main.h" |
| 112 | #include "yuzu/uisettings.h" | 113 | #include "yuzu/uisettings.h" |
| @@ -847,6 +848,9 @@ void GMainWindow::ConnectWidgetEvents() { | |||
| 847 | connect(game_list, &GameList::OpenPerGameGeneralRequested, this, | 848 | connect(game_list, &GameList::OpenPerGameGeneralRequested, this, |
| 848 | &GMainWindow::OnGameListOpenPerGameProperties); | 849 | &GMainWindow::OnGameListOpenPerGameProperties); |
| 849 | 850 | ||
| 851 | connect(this, &GMainWindow::UpdateInstallProgress, this, | ||
| 852 | &GMainWindow::IncrementInstallProgress); | ||
| 853 | |||
| 850 | connect(this, &GMainWindow::EmulationStarting, render_window, | 854 | connect(this, &GMainWindow::EmulationStarting, render_window, |
| 851 | &GRenderWindow::OnEmulationStarting); | 855 | &GRenderWindow::OnEmulationStarting); |
| 852 | connect(this, &GMainWindow::EmulationStopping, render_window, | 856 | connect(this, &GMainWindow::EmulationStopping, render_window, |
| @@ -1593,187 +1597,255 @@ void GMainWindow::OnMenuLoadFolder() { | |||
| 1593 | } | 1597 | } |
| 1594 | } | 1598 | } |
| 1595 | 1599 | ||
| 1600 | void GMainWindow::IncrementInstallProgress() { | ||
| 1601 | install_progress->setValue(install_progress->value() + 1); | ||
| 1602 | } | ||
| 1603 | |||
| 1596 | void GMainWindow::OnMenuInstallToNAND() { | 1604 | void GMainWindow::OnMenuInstallToNAND() { |
| 1597 | const QString file_filter = | 1605 | const QString file_filter = |
| 1598 | tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " | 1606 | tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " |
| 1599 | "(*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge " | 1607 | "(*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge " |
| 1600 | "Image (*.xci)"); | 1608 | "Image (*.xci)"); |
| 1601 | QString filename = QFileDialog::getOpenFileName(this, tr("Install File"), | ||
| 1602 | UISettings::values.roms_path, file_filter); | ||
| 1603 | 1609 | ||
| 1604 | if (filename.isEmpty()) { | 1610 | QStringList filenames = QFileDialog::getOpenFileNames( |
| 1611 | this, tr("Install Files"), UISettings::values.roms_path, file_filter); | ||
| 1612 | |||
| 1613 | if (filenames.isEmpty()) { | ||
| 1605 | return; | 1614 | return; |
| 1606 | } | 1615 | } |
| 1607 | 1616 | ||
| 1617 | InstallDialog installDialog(this, filenames); | ||
| 1618 | if (installDialog.exec() == QDialog::Rejected) { | ||
| 1619 | return; | ||
| 1620 | } | ||
| 1621 | |||
| 1622 | const QStringList files = installDialog.GetFiles(); | ||
| 1623 | |||
| 1624 | if (files.isEmpty()) { | ||
| 1625 | return; | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | int remaining = filenames.size(); | ||
| 1629 | |||
| 1630 | // This would only overflow above 2^43 bytes (8.796 TB) | ||
| 1631 | int total_size = 0; | ||
| 1632 | for (const QString& file : files) { | ||
| 1633 | total_size += static_cast<int>(QFile(file).size() / 0x1000); | ||
| 1634 | } | ||
| 1635 | if (total_size < 0) { | ||
| 1636 | LOG_CRITICAL(Frontend, "Attempting to install too many files, aborting."); | ||
| 1637 | return; | ||
| 1638 | } | ||
| 1639 | |||
| 1640 | QStringList new_files{}; // Newly installed files that do not yet exist in the NAND | ||
| 1641 | QStringList overwritten_files{}; // Files that overwrote those existing in the NAND | ||
| 1642 | QStringList failed_files{}; // Files that failed to install due to errors | ||
| 1643 | |||
| 1644 | ui.action_Install_File_NAND->setEnabled(false); | ||
| 1645 | |||
| 1646 | install_progress = new QProgressDialog(QStringLiteral(""), tr("Cancel"), 0, total_size, this); | ||
| 1647 | install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & | ||
| 1648 | ~Qt::WindowMaximizeButtonHint); | ||
| 1649 | install_progress->setAttribute(Qt::WA_DeleteOnClose, true); | ||
| 1650 | install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40); | ||
| 1651 | install_progress->show(); | ||
| 1652 | |||
| 1653 | for (const QString& file : files) { | ||
| 1654 | install_progress->setWindowTitle(tr("%n file(s) remaining", "", remaining)); | ||
| 1655 | install_progress->setLabelText( | ||
| 1656 | tr("Installing file \"%1\"...").arg(QFileInfo(file).fileName())); | ||
| 1657 | |||
| 1658 | QFuture<InstallResult> future; | ||
| 1659 | InstallResult result; | ||
| 1660 | |||
| 1661 | if (file.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || | ||
| 1662 | file.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { | ||
| 1663 | |||
| 1664 | future = QtConcurrent::run([this, &file] { return InstallNSPXCI(file); }); | ||
| 1665 | |||
| 1666 | while (!future.isFinished()) { | ||
| 1667 | QCoreApplication::processEvents(); | ||
| 1668 | } | ||
| 1669 | |||
| 1670 | result = future.result(); | ||
| 1671 | |||
| 1672 | } else { | ||
| 1673 | result = InstallNCA(file); | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); | ||
| 1677 | |||
| 1678 | switch (result) { | ||
| 1679 | case InstallResult::Success: | ||
| 1680 | new_files.append(QFileInfo(file).fileName()); | ||
| 1681 | break; | ||
| 1682 | case InstallResult::Overwrite: | ||
| 1683 | overwritten_files.append(QFileInfo(file).fileName()); | ||
| 1684 | break; | ||
| 1685 | case InstallResult::Failure: | ||
| 1686 | failed_files.append(QFileInfo(file).fileName()); | ||
| 1687 | break; | ||
| 1688 | } | ||
| 1689 | |||
| 1690 | --remaining; | ||
| 1691 | } | ||
| 1692 | |||
| 1693 | install_progress->close(); | ||
| 1694 | |||
| 1695 | const QString install_results = | ||
| 1696 | (new_files.isEmpty() ? QStringLiteral("") | ||
| 1697 | : tr("%n file(s) were newly installed\n", "", new_files.size())) + | ||
| 1698 | (overwritten_files.isEmpty() | ||
| 1699 | ? QStringLiteral("") | ||
| 1700 | : tr("%n file(s) were overwritten\n", "", overwritten_files.size())) + | ||
| 1701 | (failed_files.isEmpty() ? QStringLiteral("") | ||
| 1702 | : tr("%n file(s) failed to install\n", "", failed_files.size())); | ||
| 1703 | |||
| 1704 | QMessageBox::information(this, tr("Install Results"), install_results); | ||
| 1705 | game_list->PopulateAsync(UISettings::values.game_dirs); | ||
| 1706 | FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP + | ||
| 1707 | "game_list"); | ||
| 1708 | ui.action_Install_File_NAND->setEnabled(true); | ||
| 1709 | } | ||
| 1710 | |||
| 1711 | InstallResult GMainWindow::InstallNSPXCI(const QString& filename) { | ||
| 1608 | const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, | 1712 | const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, |
| 1609 | const FileSys::VirtualFile& dest, std::size_t block_size) { | 1713 | const FileSys::VirtualFile& dest, std::size_t block_size) { |
| 1610 | if (src == nullptr || dest == nullptr) | 1714 | if (src == nullptr || dest == nullptr) { |
| 1611 | return false; | 1715 | return false; |
| 1612 | if (!dest->Resize(src->GetSize())) | 1716 | } |
| 1717 | if (!dest->Resize(src->GetSize())) { | ||
| 1613 | return false; | 1718 | return false; |
| 1719 | } | ||
| 1614 | 1720 | ||
| 1615 | std::array<u8, 0x1000> buffer{}; | 1721 | std::array<u8, 0x1000> buffer{}; |
| 1616 | const int progress_maximum = static_cast<int>(src->GetSize() / buffer.size()); | ||
| 1617 | |||
| 1618 | QProgressDialog progress( | ||
| 1619 | tr("Installing file \"%1\"...").arg(QString::fromStdString(src->GetName())), | ||
| 1620 | tr("Cancel"), 0, progress_maximum, this); | ||
| 1621 | progress.setWindowModality(Qt::WindowModal); | ||
| 1622 | 1722 | ||
| 1623 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { | 1723 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
| 1624 | if (progress.wasCanceled()) { | 1724 | if (install_progress->wasCanceled()) { |
| 1625 | dest->Resize(0); | 1725 | dest->Resize(0); |
| 1626 | return false; | 1726 | return false; |
| 1627 | } | 1727 | } |
| 1628 | 1728 | ||
| 1629 | const int progress_value = static_cast<int>(i / buffer.size()); | 1729 | emit UpdateInstallProgress(); |
| 1630 | progress.setValue(progress_value); | ||
| 1631 | 1730 | ||
| 1632 | const auto read = src->Read(buffer.data(), buffer.size(), i); | 1731 | const auto read = src->Read(buffer.data(), buffer.size(), i); |
| 1633 | dest->Write(buffer.data(), read, i); | 1732 | dest->Write(buffer.data(), read, i); |
| 1634 | } | 1733 | } |
| 1635 | |||
| 1636 | return true; | 1734 | return true; |
| 1637 | }; | 1735 | }; |
| 1638 | 1736 | ||
| 1639 | const auto success = [this]() { | 1737 | std::shared_ptr<FileSys::NSP> nsp; |
| 1640 | QMessageBox::information(this, tr("Successfully Installed"), | 1738 | if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { |
| 1641 | tr("The file was successfully installed.")); | 1739 | nsp = std::make_shared<FileSys::NSP>( |
| 1642 | game_list->PopulateAsync(UISettings::values.game_dirs); | 1740 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); |
| 1643 | FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + | 1741 | if (nsp->IsExtractedType()) { |
| 1644 | DIR_SEP + "game_list"); | 1742 | return InstallResult::Failure; |
| 1645 | }; | ||
| 1646 | |||
| 1647 | const auto failed = [this]() { | ||
| 1648 | QMessageBox::warning( | ||
| 1649 | this, tr("Failed to Install"), | ||
| 1650 | tr("There was an error while attempting to install the provided file. It " | ||
| 1651 | "could have an incorrect format or be missing metadata. Please " | ||
| 1652 | "double-check your file and try again.")); | ||
| 1653 | }; | ||
| 1654 | |||
| 1655 | const auto overwrite = [this]() { | ||
| 1656 | return QMessageBox::question(this, tr("Failed to Install"), | ||
| 1657 | tr("The file you are attempting to install already exists " | ||
| 1658 | "in the cache. Would you like to overwrite it?")) == | ||
| 1659 | QMessageBox::Yes; | ||
| 1660 | }; | ||
| 1661 | |||
| 1662 | if (filename.endsWith(QStringLiteral("xci"), Qt::CaseInsensitive) || | ||
| 1663 | filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { | ||
| 1664 | std::shared_ptr<FileSys::NSP> nsp; | ||
| 1665 | if (filename.endsWith(QStringLiteral("nsp"), Qt::CaseInsensitive)) { | ||
| 1666 | nsp = std::make_shared<FileSys::NSP>( | ||
| 1667 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | ||
| 1668 | if (nsp->IsExtractedType()) | ||
| 1669 | failed(); | ||
| 1670 | } else { | ||
| 1671 | const auto xci = std::make_shared<FileSys::XCI>( | ||
| 1672 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | ||
| 1673 | nsp = xci->GetSecurePartitionNSP(); | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | if (nsp->GetStatus() != Loader::ResultStatus::Success) { | ||
| 1677 | failed(); | ||
| 1678 | return; | ||
| 1679 | } | ||
| 1680 | const auto res = Core::System::GetInstance() | ||
| 1681 | .GetFileSystemController() | ||
| 1682 | .GetUserNANDContents() | ||
| 1683 | ->InstallEntry(*nsp, false, qt_raw_copy); | ||
| 1684 | if (res == FileSys::InstallResult::Success) { | ||
| 1685 | success(); | ||
| 1686 | } else { | ||
| 1687 | if (res == FileSys::InstallResult::ErrorAlreadyExists) { | ||
| 1688 | if (overwrite()) { | ||
| 1689 | const auto res2 = Core::System::GetInstance() | ||
| 1690 | .GetFileSystemController() | ||
| 1691 | .GetUserNANDContents() | ||
| 1692 | ->InstallEntry(*nsp, true, qt_raw_copy); | ||
| 1693 | if (res2 == FileSys::InstallResult::Success) { | ||
| 1694 | success(); | ||
| 1695 | } else { | ||
| 1696 | failed(); | ||
| 1697 | } | ||
| 1698 | } | ||
| 1699 | } else { | ||
| 1700 | failed(); | ||
| 1701 | } | ||
| 1702 | } | 1743 | } |
| 1703 | } else { | 1744 | } else { |
| 1704 | const auto nca = std::make_shared<FileSys::NCA>( | 1745 | const auto xci = std::make_shared<FileSys::XCI>( |
| 1705 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | 1746 | vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); |
| 1706 | const auto id = nca->GetStatus(); | 1747 | nsp = xci->GetSecurePartitionNSP(); |
| 1748 | } | ||
| 1707 | 1749 | ||
| 1708 | // Game updates necessary are missing base RomFS | 1750 | if (nsp->GetStatus() != Loader::ResultStatus::Success) { |
| 1709 | if (id != Loader::ResultStatus::Success && | 1751 | return InstallResult::Failure; |
| 1710 | id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | 1752 | } |
| 1711 | failed(); | 1753 | const auto res = |
| 1712 | return; | 1754 | Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->InstallEntry( |
| 1713 | } | 1755 | *nsp, true, qt_raw_copy); |
| 1756 | if (res == FileSys::InstallResult::Success) { | ||
| 1757 | return InstallResult::Success; | ||
| 1758 | } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { | ||
| 1759 | return InstallResult::Overwrite; | ||
| 1760 | } else { | ||
| 1761 | return InstallResult::Failure; | ||
| 1762 | } | ||
| 1763 | } | ||
| 1714 | 1764 | ||
| 1715 | const QStringList tt_options{tr("System Application"), | 1765 | InstallResult GMainWindow::InstallNCA(const QString& filename) { |
| 1716 | tr("System Archive"), | 1766 | const auto qt_raw_copy = [this](const FileSys::VirtualFile& src, |
| 1717 | tr("System Application Update"), | 1767 | const FileSys::VirtualFile& dest, std::size_t block_size) { |
| 1718 | tr("Firmware Package (Type A)"), | 1768 | if (src == nullptr || dest == nullptr) { |
| 1719 | tr("Firmware Package (Type B)"), | 1769 | return false; |
| 1720 | tr("Game"), | ||
| 1721 | tr("Game Update"), | ||
| 1722 | tr("Game DLC"), | ||
| 1723 | tr("Delta Title")}; | ||
| 1724 | bool ok; | ||
| 1725 | const auto item = QInputDialog::getItem( | ||
| 1726 | this, tr("Select NCA Install Type..."), | ||
| 1727 | tr("Please select the type of title you would like to install this NCA as:\n(In " | ||
| 1728 | "most instances, the default 'Game' is fine.)"), | ||
| 1729 | tt_options, 5, false, &ok); | ||
| 1730 | |||
| 1731 | auto index = tt_options.indexOf(item); | ||
| 1732 | if (!ok || index == -1) { | ||
| 1733 | QMessageBox::warning(this, tr("Failed to Install"), | ||
| 1734 | tr("The title type you selected for the NCA is invalid.")); | ||
| 1735 | return; | ||
| 1736 | } | 1770 | } |
| 1737 | 1771 | if (!dest->Resize(src->GetSize())) { | |
| 1738 | // If index is equal to or past Game, add the jump in TitleType. | 1772 | return false; |
| 1739 | if (index >= 5) { | ||
| 1740 | index += static_cast<size_t>(FileSys::TitleType::Application) - | ||
| 1741 | static_cast<size_t>(FileSys::TitleType::FirmwarePackageB); | ||
| 1742 | } | 1773 | } |
| 1743 | 1774 | ||
| 1744 | FileSys::InstallResult res; | 1775 | std::array<u8, 0x1000> buffer{}; |
| 1745 | if (index >= static_cast<s32>(FileSys::TitleType::Application)) { | ||
| 1746 | res = Core::System::GetInstance() | ||
| 1747 | .GetFileSystemController() | ||
| 1748 | .GetUserNANDContents() | ||
| 1749 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, | ||
| 1750 | qt_raw_copy); | ||
| 1751 | } else { | ||
| 1752 | res = Core::System::GetInstance() | ||
| 1753 | .GetFileSystemController() | ||
| 1754 | .GetSystemNANDContents() | ||
| 1755 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false, | ||
| 1756 | qt_raw_copy); | ||
| 1757 | } | ||
| 1758 | 1776 | ||
| 1759 | if (res == FileSys::InstallResult::Success) { | 1777 | for (std::size_t i = 0; i < src->GetSize(); i += buffer.size()) { |
| 1760 | success(); | 1778 | if (install_progress->wasCanceled()) { |
| 1761 | } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { | 1779 | dest->Resize(0); |
| 1762 | if (overwrite()) { | 1780 | return false; |
| 1763 | const auto res2 = Core::System::GetInstance() | ||
| 1764 | .GetFileSystemController() | ||
| 1765 | .GetUserNANDContents() | ||
| 1766 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), | ||
| 1767 | true, qt_raw_copy); | ||
| 1768 | if (res2 == FileSys::InstallResult::Success) { | ||
| 1769 | success(); | ||
| 1770 | } else { | ||
| 1771 | failed(); | ||
| 1772 | } | ||
| 1773 | } | 1781 | } |
| 1774 | } else { | 1782 | |
| 1775 | failed(); | 1783 | emit UpdateInstallProgress(); |
| 1784 | |||
| 1785 | const auto read = src->Read(buffer.data(), buffer.size(), i); | ||
| 1786 | dest->Write(buffer.data(), read, i); | ||
| 1776 | } | 1787 | } |
| 1788 | return true; | ||
| 1789 | }; | ||
| 1790 | |||
| 1791 | const auto nca = | ||
| 1792 | std::make_shared<FileSys::NCA>(vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); | ||
| 1793 | const auto id = nca->GetStatus(); | ||
| 1794 | |||
| 1795 | // Game updates necessary are missing base RomFS | ||
| 1796 | if (id != Loader::ResultStatus::Success && | ||
| 1797 | id != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | ||
| 1798 | return InstallResult::Failure; | ||
| 1799 | } | ||
| 1800 | |||
| 1801 | const QStringList tt_options{tr("System Application"), | ||
| 1802 | tr("System Archive"), | ||
| 1803 | tr("System Application Update"), | ||
| 1804 | tr("Firmware Package (Type A)"), | ||
| 1805 | tr("Firmware Package (Type B)"), | ||
| 1806 | tr("Game"), | ||
| 1807 | tr("Game Update"), | ||
| 1808 | tr("Game DLC"), | ||
| 1809 | tr("Delta Title")}; | ||
| 1810 | bool ok; | ||
| 1811 | const auto item = QInputDialog::getItem( | ||
| 1812 | this, tr("Select NCA Install Type..."), | ||
| 1813 | tr("Please select the type of title you would like to install this NCA as:\n(In " | ||
| 1814 | "most instances, the default 'Game' is fine.)"), | ||
| 1815 | tt_options, 5, false, &ok); | ||
| 1816 | |||
| 1817 | auto index = tt_options.indexOf(item); | ||
| 1818 | if (!ok || index == -1) { | ||
| 1819 | QMessageBox::warning(this, tr("Failed to Install"), | ||
| 1820 | tr("The title type you selected for the NCA is invalid.")); | ||
| 1821 | return InstallResult::Failure; | ||
| 1822 | } | ||
| 1823 | |||
| 1824 | // If index is equal to or past Game, add the jump in TitleType. | ||
| 1825 | if (index >= 5) { | ||
| 1826 | index += static_cast<size_t>(FileSys::TitleType::Application) - | ||
| 1827 | static_cast<size_t>(FileSys::TitleType::FirmwarePackageB); | ||
| 1828 | } | ||
| 1829 | |||
| 1830 | FileSys::InstallResult res; | ||
| 1831 | if (index >= static_cast<s32>(FileSys::TitleType::Application)) { | ||
| 1832 | res = Core::System::GetInstance() | ||
| 1833 | .GetFileSystemController() | ||
| 1834 | .GetUserNANDContents() | ||
| 1835 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy); | ||
| 1836 | } else { | ||
| 1837 | res = Core::System::GetInstance() | ||
| 1838 | .GetFileSystemController() | ||
| 1839 | .GetSystemNANDContents() | ||
| 1840 | ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy); | ||
| 1841 | } | ||
| 1842 | |||
| 1843 | if (res == FileSys::InstallResult::Success) { | ||
| 1844 | return InstallResult::Success; | ||
| 1845 | } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { | ||
| 1846 | return InstallResult::Overwrite; | ||
| 1847 | } else { | ||
| 1848 | return InstallResult::Failure; | ||
| 1777 | } | 1849 | } |
| 1778 | } | 1850 | } |
| 1779 | 1851 | ||
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 8e3d39c38..adff65fb5 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -28,6 +28,7 @@ class MicroProfileDialog; | |||
| 28 | class ProfilerWidget; | 28 | class ProfilerWidget; |
| 29 | class QLabel; | 29 | class QLabel; |
| 30 | class QPushButton; | 30 | class QPushButton; |
| 31 | class QProgressDialog; | ||
| 31 | class WaitTreeWidget; | 32 | class WaitTreeWidget; |
| 32 | enum class GameListOpenTarget; | 33 | enum class GameListOpenTarget; |
| 33 | class GameListPlaceholder; | 34 | class GameListPlaceholder; |
| @@ -47,6 +48,12 @@ enum class EmulatedDirectoryTarget { | |||
| 47 | SDMC, | 48 | SDMC, |
| 48 | }; | 49 | }; |
| 49 | 50 | ||
| 51 | enum class InstallResult { | ||
| 52 | Success, | ||
| 53 | Overwrite, | ||
| 54 | Failure, | ||
| 55 | }; | ||
| 56 | |||
| 50 | enum class ReinitializeKeyBehavior { | 57 | enum class ReinitializeKeyBehavior { |
| 51 | NoWarning, | 58 | NoWarning, |
| 52 | Warning, | 59 | Warning, |
| @@ -102,6 +109,8 @@ signals: | |||
| 102 | // Signal that tells widgets to update icons to use the current theme | 109 | // Signal that tells widgets to update icons to use the current theme |
| 103 | void UpdateThemedIcons(); | 110 | void UpdateThemedIcons(); |
| 104 | 111 | ||
| 112 | void UpdateInstallProgress(); | ||
| 113 | |||
| 105 | void ErrorDisplayFinished(); | 114 | void ErrorDisplayFinished(); |
| 106 | 115 | ||
| 107 | void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid); | 116 | void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid); |
| @@ -198,6 +207,7 @@ private slots: | |||
| 198 | void OnGameListOpenPerGameProperties(const std::string& file); | 207 | void OnGameListOpenPerGameProperties(const std::string& file); |
| 199 | void OnMenuLoadFile(); | 208 | void OnMenuLoadFile(); |
| 200 | void OnMenuLoadFolder(); | 209 | void OnMenuLoadFolder(); |
| 210 | void IncrementInstallProgress(); | ||
| 201 | void OnMenuInstallToNAND(); | 211 | void OnMenuInstallToNAND(); |
| 202 | void OnMenuRecentFile(); | 212 | void OnMenuRecentFile(); |
| 203 | void OnConfigure(); | 213 | void OnConfigure(); |
| @@ -218,6 +228,8 @@ private slots: | |||
| 218 | 228 | ||
| 219 | private: | 229 | private: |
| 220 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); | 230 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); |
| 231 | InstallResult InstallNSPXCI(const QString& filename); | ||
| 232 | InstallResult InstallNCA(const QString& filename); | ||
| 221 | void UpdateWindowTitle(const std::string& title_name = {}, | 233 | void UpdateWindowTitle(const std::string& title_name = {}, |
| 222 | const std::string& title_version = {}); | 234 | const std::string& title_version = {}); |
| 223 | void UpdateStatusBar(); | 235 | void UpdateStatusBar(); |
| @@ -272,6 +284,9 @@ private: | |||
| 272 | 284 | ||
| 273 | HotkeyRegistry hotkey_registry; | 285 | HotkeyRegistry hotkey_registry; |
| 274 | 286 | ||
| 287 | // Install progress dialog | ||
| 288 | QProgressDialog* install_progress; | ||
| 289 | |||
| 275 | protected: | 290 | protected: |
| 276 | void dropEvent(QDropEvent* event) override; | 291 | void dropEvent(QDropEvent* event) override; |
| 277 | void dragEnterEvent(QDragEnterEvent* event) override; | 292 | void dragEnterEvent(QDragEnterEvent* event) override; |
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui index bee6e107e..c3a1d715e 100644 --- a/src/yuzu/main.ui +++ b/src/yuzu/main.ui | |||
| @@ -130,7 +130,7 @@ | |||
| 130 | <bool>true</bool> | 130 | <bool>true</bool> |
| 131 | </property> | 131 | </property> |
| 132 | <property name="text"> | 132 | <property name="text"> |
| 133 | <string>Install File to NAND...</string> | 133 | <string>Install Files to NAND...</string> |
| 134 | </property> | 134 | </property> |
| 135 | </action> | 135 | </action> |
| 136 | <action name="action_Load_File"> | 136 | <action name="action_Load_File"> |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 441d8e8f7..7773228c8 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -335,15 +335,6 @@ void Config::ReadValues() { | |||
| 335 | Settings::values.gamecard_current_game = | 335 | Settings::values.gamecard_current_game = |
| 336 | sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false); | 336 | sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false); |
| 337 | Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", ""); | 337 | Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", ""); |
| 338 | Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(sdl2_config->GetInteger( | ||
| 339 | "Data Storage", "nand_total_size", static_cast<long>(Settings::NANDTotalSize::S29_1GB))); | ||
| 340 | Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(sdl2_config->GetInteger( | ||
| 341 | "Data Storage", "nand_user_size", static_cast<long>(Settings::NANDUserSize::S26GB))); | ||
| 342 | Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>( | ||
| 343 | sdl2_config->GetInteger("Data Storage", "nand_system_size", | ||
| 344 | static_cast<long>(Settings::NANDSystemSize::S2_5GB))); | ||
| 345 | Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(sdl2_config->GetInteger( | ||
| 346 | "Data Storage", "sdmc_size", static_cast<long>(Settings::SDMCSize::S16GB))); | ||
| 347 | 338 | ||
| 348 | // System | 339 | // System |
| 349 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); | 340 | Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); |