early-access version 3949
This commit is contained in:
parent
e1e4fc0bdc
commit
c50160452e
16 changed files with 11045 additions and 91 deletions
|
@ -291,6 +291,7 @@ find_package(lz4 REQUIRED)
|
||||||
find_package(nlohmann_json 3.8 REQUIRED)
|
find_package(nlohmann_json 3.8 REQUIRED)
|
||||||
find_package(Opus 1.3 MODULE)
|
find_package(Opus 1.3 MODULE)
|
||||||
find_package(RenderDoc MODULE)
|
find_package(RenderDoc MODULE)
|
||||||
|
find_package(stb MODULE)
|
||||||
find_package(VulkanMemoryAllocator CONFIG)
|
find_package(VulkanMemoryAllocator CONFIG)
|
||||||
find_package(ZLIB 1.2 REQUIRED)
|
find_package(ZLIB 1.2 REQUIRED)
|
||||||
find_package(zstd 1.5 REQUIRED)
|
find_package(zstd 1.5 REQUIRED)
|
||||||
|
|
31
CMakeModules/Findstb.cmake
Executable file
31
CMakeModules/Findstb.cmake
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
# SPDX-FileCopyrightText: 2023 Alexandre Bouvier <contact@amb.tf>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
find_path(stb_image_INCLUDE_DIR stb_image.h PATH_SUFFIXES stb)
|
||||||
|
find_path(stb_image_resize_INCLUDE_DIR stb_image_resize.h PATH_SUFFIXES stb)
|
||||||
|
find_path(stb_image_write_INCLUDE_DIR stb_image_write.h PATH_SUFFIXES stb)
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(stb
|
||||||
|
REQUIRED_VARS
|
||||||
|
stb_image_INCLUDE_DIR
|
||||||
|
stb_image_resize_INCLUDE_DIR
|
||||||
|
stb_image_write_INCLUDE_DIR
|
||||||
|
)
|
||||||
|
|
||||||
|
if (stb_FOUND AND NOT TARGET stb::headers)
|
||||||
|
add_library(stb::headers INTERFACE IMPORTED)
|
||||||
|
set_property(TARGET stb::headers PROPERTY
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES
|
||||||
|
"${stb_image_INCLUDE_DIR}"
|
||||||
|
"${stb_image_resize_INCLUDE_DIR}"
|
||||||
|
"${stb_image_write_INCLUDE_DIR}"
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
mark_as_advanced(
|
||||||
|
stb_image_INCLUDE_DIR
|
||||||
|
stb_image_resize_INCLUDE_DIR
|
||||||
|
stb_image_write_INCLUDE_DIR
|
||||||
|
)
|
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 3948.
|
This is the source code for early-access 3949.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
10
externals/CMakeLists.txt
vendored
10
externals/CMakeLists.txt
vendored
|
@ -134,6 +134,10 @@ endif()
|
||||||
|
|
||||||
# Opus
|
# Opus
|
||||||
if (NOT TARGET Opus::opus)
|
if (NOT TARGET Opus::opus)
|
||||||
|
set(OPUS_BUILD_TESTING OFF)
|
||||||
|
set(OPUS_BUILD_PROGRAMS OFF)
|
||||||
|
set(OPUS_INSTALL_PKG_CONFIG_MODULE OFF)
|
||||||
|
set(OPUS_INSTALL_CMAKE_CONFIG_MODULE OFF)
|
||||||
add_subdirectory(opus)
|
add_subdirectory(opus)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -168,9 +172,13 @@ if (NOT TARGET LLVM::Demangle)
|
||||||
add_library(LLVM::Demangle ALIAS demangle)
|
add_library(LLVM::Demangle ALIAS demangle)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_library(stb stb/stb_dxt.cpp stb/stb_image.cpp stb/stb_image_resize.cpp stb/stb_image_write.cpp)
|
add_library(stb stb/stb_dxt.cpp)
|
||||||
target_include_directories(stb PUBLIC ./stb)
|
target_include_directories(stb PUBLIC ./stb)
|
||||||
|
|
||||||
|
if (NOT TARGET stb::headers)
|
||||||
|
add_library(stb::headers ALIAS stb)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
add_library(bc_decoder bc_decoder/bc_decoder.cpp)
|
||||||
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
target_include_directories(bc_decoder PUBLIC ./bc_decoder)
|
||||||
|
|
||||||
|
|
7
externals/libusb/CMakeLists.txt
vendored
7
externals/libusb/CMakeLists.txt
vendored
|
@ -49,11 +49,6 @@ if (MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux") OR APPLE)
|
||||||
|
|
||||||
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE)
|
set(LIBUSB_INCLUDE_DIRS "${LIBUSB_SRC_DIR}/libusb" CACHE PATH "libusb headers path" FORCE)
|
||||||
|
|
||||||
# MINGW: causes "externals/libusb/libusb/libusb/os/windows_winusb.c:1427:2: error: conversion to non-scalar type requested", so cannot statically link it for now.
|
|
||||||
if (NOT MINGW)
|
|
||||||
set(LIBUSB_CFLAGS "-DGUID_DEVINTERFACE_USB_DEVICE=\\(GUID\\){0xA5DCBF10,0x6530,0x11D2,{0x90,0x1F,0x00,0xC0,0x4F,0xB9,0x51,0xED}}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
make_directory("${LIBUSB_PREFIX}")
|
make_directory("${LIBUSB_PREFIX}")
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
|
@ -146,8 +141,6 @@ else() # MINGW OR (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
|
||||||
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
|
target_include_directories(usb BEFORE PRIVATE libusb/msvc)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2)
|
|
||||||
target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}")
|
|
||||||
else()
|
else()
|
||||||
target_include_directories(usb
|
target_include_directories(usb
|
||||||
# turns out other projects also have "config.h", so make sure the
|
# turns out other projects also have "config.h", so make sure the
|
||||||
|
|
7221
externals/stb/stb_image.h
vendored
7221
externals/stb/stb_image.h
vendored
File diff suppressed because it is too large
Load diff
2214
externals/stb/stb_image_resize.h
vendored
2214
externals/stb/stb_image_resize.h
vendored
File diff suppressed because it is too large
Load diff
1435
externals/stb/stb_image_write.h
vendored
1435
externals/stb/stb_image_write.h
vendored
File diff suppressed because it is too large
Load diff
|
@ -120,6 +120,8 @@ add_library(common STATIC
|
||||||
socket_types.h
|
socket_types.h
|
||||||
spin_lock.cpp
|
spin_lock.cpp
|
||||||
spin_lock.h
|
spin_lock.h
|
||||||
|
stb.cpp
|
||||||
|
stb.h
|
||||||
steady_clock.cpp
|
steady_clock.cpp
|
||||||
steady_clock.h
|
steady_clock.h
|
||||||
stream.cpp
|
stream.cpp
|
||||||
|
@ -208,6 +210,8 @@ if (MSVC)
|
||||||
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
/we4254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
|
||||||
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
/we4800 # Implicit conversion from 'type' to bool. Possible information loss
|
||||||
)
|
)
|
||||||
|
else()
|
||||||
|
set_source_files_properties(stb.cpp PROPERTIES COMPILE_OPTIONS "-Wno-implicit-fallthrough;-Wno-missing-declarations;-Wno-missing-field-initializers")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
|
@ -223,7 +227,7 @@ endif()
|
||||||
|
|
||||||
create_target_directory_groups(common)
|
create_target_directory_groups(common)
|
||||||
|
|
||||||
target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile Threads::Threads)
|
target_link_libraries(common PUBLIC Boost::context Boost::headers fmt::fmt microprofile stb::headers Threads::Threads)
|
||||||
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
|
target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd LLVM::Demangle)
|
||||||
|
|
||||||
if (ANDROID)
|
if (ANDROID)
|
||||||
|
|
8
src/common/stb.cpp
Executable file
8
src/common/stb.cpp
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_RESIZE_IMPLEMENTATION
|
||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
|
||||||
|
#include "common/stb.h"
|
8
src/common/stb.h
Executable file
8
src/common/stb.h
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stb_image.h>
|
||||||
|
#include <stb_image_resize.h>
|
||||||
|
#include <stb_image_write.h>
|
|
@ -2,13 +2,11 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stb_image.h>
|
|
||||||
#include <stb_image_resize.h>
|
|
||||||
#include <stb_image_write.h>
|
|
||||||
|
|
||||||
#include "common/fs/file.h"
|
#include "common/fs/file.h"
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
|
#include "common/stb.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/hle/service/caps/caps_manager.h"
|
#include "core/hle/service/caps/caps_manager.h"
|
||||||
#include "core/hle/service/caps/caps_result.h"
|
#include "core/hle/service/caps/caps_result.h"
|
||||||
|
@ -409,6 +407,12 @@ Result AlbumManager::LoadImage(std::span<u8> out_image, const std::filesystem::p
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void PNGToMemory(void* context, void* png, int len) {
|
||||||
|
std::vector<u8>* png_image = static_cast<std::vector<u8>*>(context);
|
||||||
|
png_image->reserve(len);
|
||||||
|
std::memcpy(png_image->data(), png, len);
|
||||||
|
}
|
||||||
|
|
||||||
Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const u8> image,
|
Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const u8> image,
|
||||||
u64 title_id, const AlbumFileDateTime& date) const {
|
u64 title_id, const AlbumFileDateTime& date) const {
|
||||||
const auto screenshot_path =
|
const auto screenshot_path =
|
||||||
|
@ -422,16 +426,12 @@ Result AlbumManager::SaveImage(ApplicationAlbumEntry& out_entry, std::span<const
|
||||||
const Common::FS::IOFile db_file{file_path, Common::FS::FileAccessMode::Write,
|
const Common::FS::IOFile db_file{file_path, Common::FS::FileAccessMode::Write,
|
||||||
Common::FS::FileType::BinaryFile};
|
Common::FS::FileType::BinaryFile};
|
||||||
|
|
||||||
s32 len;
|
std::vector<u8> png_image;
|
||||||
const u8* png = stbi_write_png_to_mem(image.data(), 0, 1280, 720, STBI_rgb_alpha, &len);
|
if (!stbi_write_png_to_func(PNGToMemory, &png_image, 1280, 720, STBI_rgb_alpha, image.data(),
|
||||||
|
0)) {
|
||||||
if (!png) {
|
|
||||||
return ResultFileCountLimit;
|
return ResultFileCountLimit;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> png_image(len);
|
|
||||||
std::memcpy(png_image.data(), png, len);
|
|
||||||
|
|
||||||
if (db_file.Write(png_image) != png_image.size()) {
|
if (db_file.Write(png_image) != png_image.size()) {
|
||||||
return ResultFileCountLimit;
|
return ResultFileCountLimit;
|
||||||
}
|
}
|
||||||
|
|
|
@ -380,7 +380,6 @@ void GameList::UnloadController() {
|
||||||
|
|
||||||
GameList::~GameList() {
|
GameList::~GameList() {
|
||||||
UnloadController();
|
UnloadController();
|
||||||
emit ShouldCancelWorker();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameList::SetFilterFocus() {
|
void GameList::SetFilterFocus() {
|
||||||
|
@ -397,6 +396,10 @@ void GameList::ClearFilter() {
|
||||||
search_field->clear();
|
search_field->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameList::WorkerEvent() {
|
||||||
|
current_worker->ProcessEvents(this);
|
||||||
|
}
|
||||||
|
|
||||||
void GameList::AddDirEntry(GameListDir* entry_items) {
|
void GameList::AddDirEntry(GameListDir* entry_items) {
|
||||||
item_model->invisibleRootItem()->appendRow(entry_items);
|
item_model->invisibleRootItem()->appendRow(entry_items);
|
||||||
tree_view->setExpanded(
|
tree_view->setExpanded(
|
||||||
|
@ -826,28 +829,21 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
|
||||||
tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size);
|
tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size);
|
||||||
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
|
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
|
||||||
|
|
||||||
// Before deleting rows, cancel the worker so that it is not using them
|
// Cancel any existing worker.
|
||||||
emit ShouldCancelWorker();
|
current_worker.reset();
|
||||||
|
|
||||||
// Delete any rows that might already exist if we're repopulating
|
// Delete any rows that might already exist if we're repopulating
|
||||||
item_model->removeRows(0, item_model->rowCount());
|
item_model->removeRows(0, item_model->rowCount());
|
||||||
search_field->clear();
|
search_field->clear();
|
||||||
|
|
||||||
GameListWorker* worker =
|
current_worker = std::make_unique<GameListWorker>(vfs, provider, game_dirs, compatibility_list,
|
||||||
new GameListWorker(vfs, provider, game_dirs, compatibility_list, play_time_manager, system);
|
play_time_manager, system);
|
||||||
|
|
||||||
connect(worker, &GameListWorker::EntryReady, this, &GameList::AddEntry, Qt::QueuedConnection);
|
// Get events from the worker as data becomes available
|
||||||
connect(worker, &GameListWorker::DirEntryReady, this, &GameList::AddDirEntry,
|
connect(current_worker.get(), &GameListWorker::DataAvailable, this, &GameList::WorkerEvent,
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
connect(worker, &GameListWorker::Finished, this, &GameList::DonePopulating,
|
|
||||||
Qt::QueuedConnection);
|
|
||||||
// Use DirectConnection here because worker->Cancel() is thread-safe and we want it to
|
|
||||||
// cancel without delay.
|
|
||||||
connect(this, &GameList::ShouldCancelWorker, worker, &GameListWorker::Cancel,
|
|
||||||
Qt::DirectConnection);
|
|
||||||
|
|
||||||
QThreadPool::globalInstance()->start(worker);
|
QThreadPool::globalInstance()->start(current_worker.get());
|
||||||
current_worker = std::move(worker);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameList::SaveInterfaceLayout() {
|
void GameList::SaveInterfaceLayout() {
|
||||||
|
|
|
@ -109,7 +109,6 @@ signals:
|
||||||
void BootGame(const QString& game_path, u64 program_id, std::size_t program_index,
|
void BootGame(const QString& game_path, u64 program_id, std::size_t program_index,
|
||||||
StartGameType type, AmLaunchType launch_type);
|
StartGameType type, AmLaunchType launch_type);
|
||||||
void GameChosen(const QString& game_path, const u64 title_id = 0);
|
void GameChosen(const QString& game_path, const u64 title_id = 0);
|
||||||
void ShouldCancelWorker();
|
|
||||||
void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
|
void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
|
||||||
const std::string& game_path);
|
const std::string& game_path);
|
||||||
void OpenTransferableShaderCacheRequested(u64 program_id);
|
void OpenTransferableShaderCacheRequested(u64 program_id);
|
||||||
|
@ -138,11 +137,16 @@ private slots:
|
||||||
void OnUpdateThemedIcons();
|
void OnUpdateThemedIcons();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class GameListWorker;
|
||||||
|
void WorkerEvent();
|
||||||
|
|
||||||
void AddDirEntry(GameListDir* entry_items);
|
void AddDirEntry(GameListDir* entry_items);
|
||||||
void AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent);
|
void AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent);
|
||||||
void ValidateEntry(const QModelIndex& item);
|
|
||||||
void DonePopulating(const QStringList& watch_list);
|
void DonePopulating(const QStringList& watch_list);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ValidateEntry(const QModelIndex& item);
|
||||||
|
|
||||||
void RefreshGameDirectory();
|
void RefreshGameDirectory();
|
||||||
|
|
||||||
void ToggleFavorite(u64 program_id);
|
void ToggleFavorite(u64 program_id);
|
||||||
|
@ -165,7 +169,7 @@ private:
|
||||||
QVBoxLayout* layout = nullptr;
|
QVBoxLayout* layout = nullptr;
|
||||||
QTreeView* tree_view = nullptr;
|
QTreeView* tree_view = nullptr;
|
||||||
QStandardItemModel* item_model = nullptr;
|
QStandardItemModel* item_model = nullptr;
|
||||||
GameListWorker* current_worker = nullptr;
|
std::unique_ptr<GameListWorker> current_worker;
|
||||||
QFileSystemWatcher* watcher = nullptr;
|
QFileSystemWatcher* watcher = nullptr;
|
||||||
ControllerNavigation* controller_navigation = nullptr;
|
ControllerNavigation* controller_navigation = nullptr;
|
||||||
CompatibilityList compatibility_list;
|
CompatibilityList compatibility_list;
|
||||||
|
|
|
@ -233,10 +233,53 @@ GameListWorker::GameListWorker(FileSys::VirtualFilesystem vfs_,
|
||||||
const PlayTime::PlayTimeManager& play_time_manager_,
|
const PlayTime::PlayTimeManager& play_time_manager_,
|
||||||
Core::System& system_)
|
Core::System& system_)
|
||||||
: vfs{std::move(vfs_)}, provider{provider_}, game_dirs{game_dirs_},
|
: vfs{std::move(vfs_)}, provider{provider_}, game_dirs{game_dirs_},
|
||||||
compatibility_list{compatibility_list_},
|
compatibility_list{compatibility_list_}, play_time_manager{play_time_manager_}, system{
|
||||||
play_time_manager{play_time_manager_}, system{system_} {}
|
system_} {
|
||||||
|
// We want the game list to manage our lifetime.
|
||||||
|
setAutoDelete(false);
|
||||||
|
}
|
||||||
|
|
||||||
GameListWorker::~GameListWorker() = default;
|
GameListWorker::~GameListWorker() {
|
||||||
|
this->disconnect();
|
||||||
|
stop_requested.store(true);
|
||||||
|
processing_completed.Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameListWorker::ProcessEvents(GameList* game_list) {
|
||||||
|
while (true) {
|
||||||
|
std::function<void(GameList*)> func;
|
||||||
|
{
|
||||||
|
// Lock queue to protect concurrent modification.
|
||||||
|
std::scoped_lock lk(lock);
|
||||||
|
|
||||||
|
// If we can't pop a function, return.
|
||||||
|
if (queued_events.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop a function.
|
||||||
|
func = std::move(queued_events.back());
|
||||||
|
queued_events.pop_back();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the function.
|
||||||
|
func(game_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
void GameListWorker::RecordEvent(F&& func) {
|
||||||
|
{
|
||||||
|
// Lock queue to protect concurrent modification.
|
||||||
|
std::scoped_lock lk(lock);
|
||||||
|
|
||||||
|
// Add the function into the front of the queue.
|
||||||
|
queued_events.emplace_front(std::move(func));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data now available.
|
||||||
|
emit DataAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
|
void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
|
||||||
using namespace FileSys;
|
using namespace FileSys;
|
||||||
|
@ -284,9 +327,9 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
|
||||||
GetMetadataFromControlNCA(patch, *control, icon, name);
|
GetMetadataFromControlNCA(patch, *control, icon, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader,
|
auto entry = MakeGameListEntry(file->GetFullPath(), name, file->GetSize(), icon, *loader,
|
||||||
program_id, compatibility_list, play_time_manager, patch),
|
program_id, compatibility_list, play_time_manager, patch);
|
||||||
parent_dir);
|
RecordEvent([=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,11 +403,12 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||||
const FileSys::PatchManager patch{id, system.GetFileSystemController(),
|
const FileSys::PatchManager patch{id, system.GetFileSystemController(),
|
||||||
system.GetContentProvider()};
|
system.GetContentProvider()};
|
||||||
|
|
||||||
emit EntryReady(MakeGameListEntry(physical_name, name,
|
auto entry = MakeGameListEntry(
|
||||||
Common::FS::GetSize(physical_name), icon,
|
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
|
||||||
*loader, id, compatibility_list,
|
id, compatibility_list, play_time_manager, patch);
|
||||||
play_time_manager, patch),
|
|
||||||
parent_dir);
|
RecordEvent(
|
||||||
|
[=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::vector<u8> icon;
|
std::vector<u8> icon;
|
||||||
|
@ -376,11 +420,12 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||||
const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
|
const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
|
||||||
system.GetContentProvider()};
|
system.GetContentProvider()};
|
||||||
|
|
||||||
emit EntryReady(MakeGameListEntry(physical_name, name,
|
auto entry = MakeGameListEntry(
|
||||||
Common::FS::GetSize(physical_name), icon,
|
physical_name, name, Common::FS::GetSize(physical_name), icon, *loader,
|
||||||
*loader, program_id, compatibility_list,
|
program_id, compatibility_list, play_time_manager, patch);
|
||||||
play_time_manager, patch),
|
|
||||||
parent_dir);
|
RecordEvent(
|
||||||
|
[=](GameList* game_list) { game_list->AddEntry(entry, parent_dir); });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (is_dir) {
|
} else if (is_dir) {
|
||||||
|
@ -399,25 +444,34 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListWorker::run() {
|
void GameListWorker::run() {
|
||||||
|
watch_list.clear();
|
||||||
provider->ClearAllEntries();
|
provider->ClearAllEntries();
|
||||||
|
|
||||||
|
const auto DirEntryReady = [&](GameListDir* game_list_dir) {
|
||||||
|
RecordEvent([=](GameList* game_list) { game_list->AddDirEntry(game_list_dir); });
|
||||||
|
};
|
||||||
|
|
||||||
for (UISettings::GameDir& game_dir : game_dirs) {
|
for (UISettings::GameDir& game_dir : game_dirs) {
|
||||||
|
if (stop_requested) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (game_dir.path == QStringLiteral("SDMC")) {
|
if (game_dir.path == QStringLiteral("SDMC")) {
|
||||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
|
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SdmcDir);
|
||||||
emit DirEntryReady(game_list_dir);
|
DirEntryReady(game_list_dir);
|
||||||
AddTitlesToGameList(game_list_dir);
|
AddTitlesToGameList(game_list_dir);
|
||||||
} else if (game_dir.path == QStringLiteral("UserNAND")) {
|
} else if (game_dir.path == QStringLiteral("UserNAND")) {
|
||||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
|
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::UserNandDir);
|
||||||
emit DirEntryReady(game_list_dir);
|
DirEntryReady(game_list_dir);
|
||||||
AddTitlesToGameList(game_list_dir);
|
AddTitlesToGameList(game_list_dir);
|
||||||
} else if (game_dir.path == QStringLiteral("SysNAND")) {
|
} else if (game_dir.path == QStringLiteral("SysNAND")) {
|
||||||
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
|
auto* const game_list_dir = new GameListDir(game_dir, GameListItemType::SysNandDir);
|
||||||
emit DirEntryReady(game_list_dir);
|
DirEntryReady(game_list_dir);
|
||||||
AddTitlesToGameList(game_list_dir);
|
AddTitlesToGameList(game_list_dir);
|
||||||
} else {
|
} else {
|
||||||
watch_list.append(game_dir.path);
|
watch_list.append(game_dir.path);
|
||||||
auto* const game_list_dir = new GameListDir(game_dir);
|
auto* const game_list_dir = new GameListDir(game_dir);
|
||||||
emit DirEntryReady(game_list_dir);
|
DirEntryReady(game_list_dir);
|
||||||
ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(),
|
ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(),
|
||||||
game_dir.deep_scan, game_list_dir);
|
game_dir.deep_scan, game_list_dir);
|
||||||
ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(),
|
ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(),
|
||||||
|
@ -425,12 +479,6 @@ void GameListWorker::run() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
emit Finished(watch_list);
|
RecordEvent([=](GameList* game_list) { game_list->DonePopulating(watch_list); });
|
||||||
processing_completed.Set();
|
processing_completed.Set();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameListWorker::Cancel() {
|
|
||||||
this->disconnect();
|
|
||||||
stop_requested.store(true);
|
|
||||||
processing_completed.Wait();
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <deque>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -20,6 +21,7 @@ namespace Core {
|
||||||
class System;
|
class System;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GameList;
|
||||||
class QStandardItem;
|
class QStandardItem;
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
@ -46,24 +48,22 @@ public:
|
||||||
/// Starts the processing of directory tree information.
|
/// Starts the processing of directory tree information.
|
||||||
void run() override;
|
void run() override;
|
||||||
|
|
||||||
/// Tells the worker that it should no longer continue processing. Thread-safe.
|
public:
|
||||||
void Cancel();
|
/**
|
||||||
|
* Synchronously processes any events queued by the worker.
|
||||||
|
*
|
||||||
|
* AddDirEntry is called on the game list for every discovered directory.
|
||||||
|
* AddEntry is called on the game list for every discovered program.
|
||||||
|
* DonePopulating is called on the game list when processing completes.
|
||||||
|
*/
|
||||||
|
void ProcessEvents(GameList* game_list);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/**
|
void DataAvailable();
|
||||||
* The `EntryReady` signal is emitted once an entry has been prepared and is ready
|
|
||||||
* to be added to the game list.
|
|
||||||
* @param entry_items a list with `QStandardItem`s that make up the columns of the new
|
|
||||||
* entry.
|
|
||||||
*/
|
|
||||||
void DirEntryReady(GameListDir* entry_items);
|
|
||||||
void EntryReady(QList<QStandardItem*> entry_items, GameListDir* parent_dir);
|
|
||||||
|
|
||||||
/**
|
private:
|
||||||
* After the worker has traversed the game directory looking for entries, this signal is
|
template <typename F>
|
||||||
* emitted with a list of folders that should be watched for changes as well.
|
void RecordEvent(F&& func);
|
||||||
*/
|
|
||||||
void Finished(QStringList watch_list);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AddTitlesToGameList(GameListDir* parent_dir);
|
void AddTitlesToGameList(GameListDir* parent_dir);
|
||||||
|
@ -84,8 +84,11 @@ private:
|
||||||
|
|
||||||
QStringList watch_list;
|
QStringList watch_list;
|
||||||
|
|
||||||
Common::Event processing_completed;
|
std::mutex lock;
|
||||||
|
std::condition_variable cv;
|
||||||
|
std::deque<std::function<void(GameList*)>> queued_events;
|
||||||
std::atomic_bool stop_requested = false;
|
std::atomic_bool stop_requested = false;
|
||||||
|
Common::Event processing_completed;
|
||||||
|
|
||||||
Core::System& system;
|
Core::System& system;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue