Compare commits
69 Commits
android-99
...
android-10
Author | SHA1 | Date | |
---|---|---|---|
8df7144ada | |||
85a89ca3e3 | |||
26776c0e60 | |||
134ecca9b0 | |||
c5f1ec8040 | |||
765ea9b79d | |||
c5bdc0054c | |||
bd05ace08d | |||
fa56518f20 | |||
b577d7a55f | |||
2244b613cf | |||
c73bb33ff1 | |||
bcce184e60 | |||
326ebbb2fa | |||
07143ce15c | |||
dbc73c6c6c | |||
8becf13e8b | |||
9e2ebb24df | |||
90c56f5dc1 | |||
4b0291172e | |||
12e4757cf3 | |||
144c0734f5 | |||
f40f65f5d2 | |||
03c3f936cf | |||
66f41da365 | |||
7a986d731b | |||
eae0570a1c | |||
b57d98f847 | |||
762ac5aa9f | |||
1a4abd184f | |||
9524d7034c | |||
36d18e457b | |||
db562bc08d | |||
18672e6a78 | |||
32ad99701d | |||
63c5340cc4 | |||
a94371f67b | |||
22e4add562 | |||
b1a7bbd458 | |||
27ab2a6e13 | |||
68ea0a2b72 | |||
a8bd02acd8 | |||
3e4edbe007 | |||
4a9240599a | |||
053a16799e | |||
82c845dc2f | |||
3aa6d4d8ce | |||
9b961dddb4 | |||
224b6036a4 | |||
2c3281c66b | |||
1591923f91 | |||
56e5d99684 | |||
ca75c9125d | |||
1a4874e178 | |||
c00b63b9e1 | |||
c8602e1b1f | |||
faa6c35e78 | |||
8b64878258 | |||
a5fb9de6fa | |||
c4ec76edba | |||
d9456f0a11 | |||
48b67fc4a0 | |||
6c246f2ac5 | |||
a34565727b | |||
98cac9410c | |||
26f9d1f122 | |||
e69eebb14a | |||
0145c89879 | |||
cc4736fa58 |
@ -11,7 +11,6 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externals/cmake-modul
|
||||
include(DownloadExternals)
|
||||
include(CMakeDependentOption)
|
||||
include(CTest)
|
||||
include(FetchContent)
|
||||
|
||||
# Set bundled sdl2/qt as dependent options.
|
||||
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
|
||||
@ -99,47 +98,8 @@ if (ANDROID AND YUZU_DOWNLOAD_ANDROID_VVL)
|
||||
DESTINATION "${vvl_lib_path}")
|
||||
endif()
|
||||
|
||||
# On Android, fetch and compile libcxx before doing anything else
|
||||
if (ANDROID)
|
||||
set(CMAKE_SKIP_INSTALL_RULES ON)
|
||||
set(LLVM_VERSION "15.0.6")
|
||||
|
||||
# Note: even though libcxx and libcxxabi have separate releases on the project page,
|
||||
# the separated releases cannot be compiled. Only in-tree builds work. Therefore we
|
||||
# must fetch the source release for the entire llvm tree.
|
||||
FetchContent_Declare(llvm
|
||||
URL "https://github.com/llvm/llvm-project/releases/download/llvmorg-${LLVM_VERSION}/llvm-project-${LLVM_VERSION}.src.tar.xz"
|
||||
URL_HASH SHA256=9d53ad04dc60cb7b30e810faf64c5ab8157dadef46c8766f67f286238256ff92
|
||||
TLS_VERIFY TRUE
|
||||
)
|
||||
FetchContent_MakeAvailable(llvm)
|
||||
|
||||
# libcxx has support for most of the range library, but it's gated behind a flag:
|
||||
add_compile_definitions(_LIBCPP_ENABLE_EXPERIMENTAL)
|
||||
|
||||
# Disable standard header inclusion
|
||||
set(ANDROID_STL "none")
|
||||
|
||||
# libcxxabi
|
||||
set(LIBCXXABI_INCLUDE_TESTS OFF)
|
||||
set(LIBCXXABI_ENABLE_SHARED FALSE)
|
||||
set(LIBCXXABI_ENABLE_STATIC TRUE)
|
||||
set(LIBCXXABI_LIBCXX_INCLUDES "${LIBCXX_TARGET_INCLUDE_DIRECTORY}" CACHE STRING "" FORCE)
|
||||
add_subdirectory("${llvm_SOURCE_DIR}/libcxxabi" "${llvm_BINARY_DIR}/libcxxabi")
|
||||
link_libraries(cxxabi_static)
|
||||
|
||||
# libcxx
|
||||
set(LIBCXX_ABI_NAMESPACE "__ndk1" CACHE STRING "" FORCE)
|
||||
set(LIBCXX_CXX_ABI "libcxxabi")
|
||||
set(LIBCXX_INCLUDE_TESTS OFF)
|
||||
set(LIBCXX_INCLUDE_BENCHMARKS OFF)
|
||||
set(LIBCXX_INCLUDE_DOCS OFF)
|
||||
set(LIBCXX_ENABLE_SHARED FALSE)
|
||||
set(LIBCXX_ENABLE_STATIC TRUE)
|
||||
set(LIBCXX_ENABLE_ASSERTIONS FALSE)
|
||||
add_subdirectory("${llvm_SOURCE_DIR}/libcxx" "${llvm_BINARY_DIR}/libcxx")
|
||||
set_target_properties(cxx-headers PROPERTIES INTERFACE_COMPILE_OPTIONS "-isystem${CMAKE_BINARY_DIR}/${LIBCXX_INSTALL_INCLUDE_DIR}")
|
||||
link_libraries(cxx_static cxx-headers)
|
||||
endif()
|
||||
|
||||
if (YUZU_USE_BUNDLED_VCPKG)
|
||||
@ -329,7 +289,7 @@ find_package(Boost 1.79.0 REQUIRED context)
|
||||
find_package(enet 1.3 MODULE)
|
||||
find_package(fmt 9 REQUIRED)
|
||||
find_package(inih 52 MODULE COMPONENTS INIReader)
|
||||
find_package(LLVM 17 MODULE COMPONENTS Demangle)
|
||||
find_package(LLVM 17.0.2 MODULE COMPONENTS Demangle)
|
||||
find_package(lz4 REQUIRED)
|
||||
find_package(nlohmann_json 3.8 REQUIRED)
|
||||
find_package(Opus 1.3 MODULE)
|
||||
@ -400,6 +360,9 @@ function(set_yuzu_qt_components)
|
||||
if (ENABLE_QT_TRANSLATION)
|
||||
list(APPEND YUZU_QT_COMPONENTS2 LinguistTools)
|
||||
endif()
|
||||
if (USE_DISCORD_PRESENCE)
|
||||
list(APPEND YUZU_QT_COMPONENTS2 Network)
|
||||
endif()
|
||||
set(YUZU_QT_COMPONENTS ${YUZU_QT_COMPONENTS2} PARENT_SCOPE)
|
||||
endfunction(set_yuzu_qt_components)
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
| Pull Request | Commit | Title | Author | Merged? |
|
||||
|----|----|----|----|----|
|
||||
| [11649](https://github.com/yuzu-emu/yuzu//pull/11649) | [`a5fb9de6f`](https://github.com/yuzu-emu/yuzu//pull/11649/files) | android: Driver manager | [t895](https://github.com/t895/) | Yes |
|
||||
|
||||
|
||||
End of merge log. You can find the original README.md below the break.
|
||||
|
@ -27,7 +27,7 @@ android {
|
||||
namespace = "org.yuzu.yuzu_emu"
|
||||
|
||||
compileSdkVersion = "android-34"
|
||||
ndkVersion = "25.2.9519653"
|
||||
ndkVersion = "26.1.10909125"
|
||||
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
@ -203,23 +203,23 @@ ktlint {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.core:core-ktx:1.10.1")
|
||||
implementation("androidx.core:core-ktx:1.12.0")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.0")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.1")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.fragment:fragment-ktx:1.6.0")
|
||||
implementation("androidx.fragment:fragment-ktx:1.6.1")
|
||||
implementation("androidx.documentfile:documentfile:1.0.1")
|
||||
implementation("com.google.android.material:material:1.9.0")
|
||||
implementation("androidx.preference:preference:1.2.0")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.1")
|
||||
implementation("androidx.preference:preference-ktx:1.2.1")
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2")
|
||||
implementation("io.coil-kt:coil:2.2.2")
|
||||
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||
implementation("androidx.window:window:1.2.0-beta03")
|
||||
implementation("org.ini4j:ini4j:0.5.4")
|
||||
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
|
||||
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.6.0")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.6.0")
|
||||
implementation("androidx.navigation:navigation-fragment-ktx:2.7.4")
|
||||
implementation("androidx.navigation:navigation-ui-ktx:2.7.4")
|
||||
implementation("info.debatty:java-string-similarity:2.0.0")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
android:appCategory="game"
|
||||
android:localeConfig="@xml/locales_config"
|
||||
android:banner="@drawable/tv_banner"
|
||||
android:extractNativeLibs="true"
|
||||
android:fullBackupContent="@xml/data_extraction_rules"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules_api_31"
|
||||
android:enableOnBackInvokedCallback="true">
|
||||
|
@ -26,6 +26,7 @@ import org.yuzu.yuzu_emu.model.DriverViewModel
|
||||
import org.yuzu.yuzu_emu.model.HomeViewModel
|
||||
import org.yuzu.yuzu_emu.utils.FileUtil
|
||||
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
class DriverManagerFragment : Fragment() {
|
||||
@ -154,29 +155,29 @@ class DriverManagerFragment : Fragment() {
|
||||
R.string.installing_driver,
|
||||
false
|
||||
) {
|
||||
val driverPath =
|
||||
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}"
|
||||
val driverFile = File(driverPath)
|
||||
|
||||
// Ignore file exceptions when a user selects an invalid zip
|
||||
try {
|
||||
GpuDriverHelper.copyDriverToInternalStorage(result)
|
||||
if (!GpuDriverHelper.copyDriverToInternalStorage(result)) {
|
||||
throw IOException("Driver failed validation!")
|
||||
}
|
||||
} catch (_: IOException) {
|
||||
if (driverFile.exists()) {
|
||||
driverFile.delete()
|
||||
}
|
||||
return@newInstance getString(R.string.select_gpu_driver_error)
|
||||
}
|
||||
|
||||
val driverData = GpuDriverHelper.customDriverData
|
||||
if (driverData.name == null) {
|
||||
return@newInstance getString(R.string.select_gpu_driver_error)
|
||||
}
|
||||
|
||||
val driverData = GpuDriverHelper.getMetadataFromZip(driverFile)
|
||||
val driverInList =
|
||||
driverViewModel.driverList.value.firstOrNull { it.second == driverData }
|
||||
if (driverInList != null) {
|
||||
return@newInstance getString(R.string.driver_already_installed)
|
||||
} else {
|
||||
driverViewModel.addDriver(
|
||||
Pair(
|
||||
"${GpuDriverHelper.driverStoragePath}/${FileUtil.getFilename(result)}",
|
||||
driverData
|
||||
)
|
||||
)
|
||||
driverViewModel.addDriver(Pair(driverPath, driverData))
|
||||
driverViewModel.setNewDriverInstalled(true)
|
||||
}
|
||||
return@newInstance Any()
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.0.2" apply false
|
||||
id("com.android.library") version "8.0.2" apply false
|
||||
id("com.android.application") version "8.1.2" apply false
|
||||
id("com.android.library") version "8.1.2" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.8.21" apply false
|
||||
}
|
||||
|
||||
|
@ -204,6 +204,10 @@ void SinkStream::ProcessAudioOutAndRender(std::span<s16> output_buffer, std::siz
|
||||
// paused and we'll desync, so just play silence.
|
||||
if (system.IsPaused() || system.IsShuttingDown()) {
|
||||
if (system.IsShuttingDown()) {
|
||||
{
|
||||
std::scoped_lock lk{release_mutex};
|
||||
queued_buffers.store(0);
|
||||
}
|
||||
release_cv.notify_one();
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,13 @@
|
||||
#include <condition_variable>
|
||||
#include <stop_token>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
namespace Common {
|
||||
|
||||
template <typename Condvar, typename Lock, typename Pred>
|
||||
void CondvarWait(Condvar& cv, std::unique_lock<Lock>& lk, std::stop_token token, Pred&& pred) {
|
||||
cv.wait(lk, token, std::move(pred));
|
||||
cv.wait(lk, token, std::forward<Pred>(pred));
|
||||
}
|
||||
|
||||
template <typename Rep, typename Period>
|
||||
@ -109,7 +110,7 @@ public:
|
||||
|
||||
// Insert the callback.
|
||||
stop_state_callback ret = ++m_next_callback;
|
||||
m_callbacks.emplace(ret, move(f));
|
||||
m_callbacks.emplace(ret, std::move(f));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -162,7 +163,7 @@ private:
|
||||
friend class stop_source;
|
||||
template <typename Callback>
|
||||
friend class stop_callback;
|
||||
stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(move(stop_state)) {}
|
||||
stop_token(shared_ptr<polyfill::stop_state> stop_state) : m_stop_state(std::move(stop_state)) {}
|
||||
|
||||
private:
|
||||
shared_ptr<polyfill::stop_state> m_stop_state;
|
||||
@ -198,7 +199,7 @@ public:
|
||||
private:
|
||||
friend class jthread;
|
||||
explicit stop_source(shared_ptr<polyfill::stop_state> stop_state)
|
||||
: m_stop_state(move(stop_state)) {}
|
||||
: m_stop_state(std::move(stop_state)) {}
|
||||
|
||||
private:
|
||||
shared_ptr<polyfill::stop_state> m_stop_state;
|
||||
@ -218,16 +219,16 @@ public:
|
||||
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
||||
: m_stop_state(st.m_stop_state) {
|
||||
if (m_stop_state) {
|
||||
m_callback = m_stop_state->insert_callback(move(cb));
|
||||
m_callback = m_stop_state->insert_callback(std::move(cb));
|
||||
}
|
||||
}
|
||||
template <typename C>
|
||||
requires constructible_from<Callback, C>
|
||||
explicit stop_callback(stop_token&& st,
|
||||
C&& cb) noexcept(is_nothrow_constructible_v<Callback, C>)
|
||||
: m_stop_state(move(st.m_stop_state)) {
|
||||
: m_stop_state(std::move(st.m_stop_state)) {
|
||||
if (m_stop_state) {
|
||||
m_callback = m_stop_state->insert_callback(move(cb));
|
||||
m_callback = m_stop_state->insert_callback(std::move(cb));
|
||||
}
|
||||
}
|
||||
~stop_callback() {
|
||||
@ -260,7 +261,7 @@ public:
|
||||
typename = enable_if_t<!is_same_v<remove_cvref_t<F>, jthread>>>
|
||||
explicit jthread(F&& f, Args&&... args)
|
||||
: m_stop_state(make_shared<polyfill::stop_state>()),
|
||||
m_thread(make_thread(move(f), move(args)...)) {}
|
||||
m_thread(make_thread(std::forward<F>(f), std::forward<Args>(args)...)) {}
|
||||
|
||||
~jthread() {
|
||||
if (joinable()) {
|
||||
@ -317,9 +318,9 @@ private:
|
||||
template <typename F, typename... Args>
|
||||
thread make_thread(F&& f, Args&&... args) {
|
||||
if constexpr (is_invocable_v<decay_t<F>, stop_token, decay_t<Args>...>) {
|
||||
return thread(move(f), get_stop_token(), move(args)...);
|
||||
return thread(std::forward<F>(f), get_stop_token(), std::forward<Args>(args)...);
|
||||
} else {
|
||||
return thread(move(f), move(args)...);
|
||||
return thread(std::forward<F>(f), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -45,6 +45,7 @@ SWITCHABLE(CpuAccuracy, true);
|
||||
SWITCHABLE(FullscreenMode, true);
|
||||
SWITCHABLE(GpuAccuracy, true);
|
||||
SWITCHABLE(Language, true);
|
||||
SWITCHABLE(MemoryLayout, true);
|
||||
SWITCHABLE(NvdecEmulation, false);
|
||||
SWITCHABLE(Region, true);
|
||||
SWITCHABLE(RendererBackend, true);
|
||||
@ -61,6 +62,10 @@ SWITCHABLE(u32, false);
|
||||
SWITCHABLE(u8, false);
|
||||
SWITCHABLE(u8, true);
|
||||
|
||||
// Used in UISettings
|
||||
// TODO see if we can move this to uisettings.cpp
|
||||
SWITCHABLE(ConfirmStop, true);
|
||||
|
||||
#undef SETTING
|
||||
#undef SWITCHABLE
|
||||
#endif
|
||||
|
@ -67,6 +67,7 @@ SWITCHABLE(CpuAccuracy, true);
|
||||
SWITCHABLE(FullscreenMode, true);
|
||||
SWITCHABLE(GpuAccuracy, true);
|
||||
SWITCHABLE(Language, true);
|
||||
SWITCHABLE(MemoryLayout, true);
|
||||
SWITCHABLE(NvdecEmulation, false);
|
||||
SWITCHABLE(Region, true);
|
||||
SWITCHABLE(RendererBackend, true);
|
||||
@ -83,6 +84,10 @@ SWITCHABLE(u32, false);
|
||||
SWITCHABLE(u8, false);
|
||||
SWITCHABLE(u8, true);
|
||||
|
||||
// Used in UISettings
|
||||
// TODO see if we can move this to uisettings.h
|
||||
SWITCHABLE(ConfirmStop, true);
|
||||
|
||||
#undef SETTING
|
||||
#undef SWITCHABLE
|
||||
#endif
|
||||
|
@ -133,6 +133,8 @@ ENUM(CpuAccuracy, Auto, Accurate, Unsafe, Paranoid);
|
||||
|
||||
ENUM(MemoryLayout, Memory_4Gb, Memory_6Gb, Memory_8Gb);
|
||||
|
||||
ENUM(ConfirmStop, Ask_Always, Ask_Based_On_Game, Ask_Never);
|
||||
|
||||
ENUM(FullscreenMode, Borderless, Exclusive);
|
||||
|
||||
ENUM(NvdecEmulation, Off, Cpu, Gpu);
|
||||
|
@ -116,11 +116,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
}
|
||||
}
|
||||
|
||||
if (concat.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
|
||||
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(dir->GetName(),
|
||||
std::move(concat));
|
||||
}
|
||||
|
||||
if (Common::FS::IsDir(path)) {
|
||||
|
@ -107,62 +107,56 @@ static u64 romfs_get_hash_table_count(u64 num_entries) {
|
||||
|
||||
void RomFSBuildContext::VisitDirectory(VirtualDir romfs_dir, VirtualDir ext_dir,
|
||||
std::shared_ptr<RomFSBuildDirectoryContext> parent) {
|
||||
std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
|
||||
for (auto& child_romfs_file : romfs_dir->GetFiles()) {
|
||||
const auto name = child_romfs_file->GetName();
|
||||
const auto child = std::make_shared<RomFSBuildFileContext>();
|
||||
// Set child's path.
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
|
||||
child->path = parent->path + "/" + name;
|
||||
|
||||
const auto entries = romfs_dir->GetEntries();
|
||||
if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& kv : entries) {
|
||||
if (kv.second == VfsEntryType::Directory) {
|
||||
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
|
||||
// Set child's path.
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
||||
child->path = parent->path + "/" + kv.first;
|
||||
// Sanity check on path_len
|
||||
ASSERT(child->path_len < FS_MAX_PATH);
|
||||
|
||||
if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) {
|
||||
continue;
|
||||
}
|
||||
child->source = std::move(child_romfs_file);
|
||||
|
||||
// Sanity check on path_len
|
||||
ASSERT(child->path_len < FS_MAX_PATH);
|
||||
|
||||
if (AddDirectory(parent, child)) {
|
||||
child_dirs.push_back(child);
|
||||
}
|
||||
} else {
|
||||
const auto child = std::make_shared<RomFSBuildFileContext>();
|
||||
// Set child's path.
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
||||
child->path = parent->path + "/" + kv.first;
|
||||
|
||||
if (ext_dir != nullptr && ext_dir->GetFile(kv.first + ".stub") != nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanity check on path_len
|
||||
ASSERT(child->path_len < FS_MAX_PATH);
|
||||
|
||||
child->source = romfs_dir->GetFile(kv.first);
|
||||
|
||||
if (ext_dir != nullptr) {
|
||||
if (const auto ips = ext_dir->GetFile(kv.first + ".ips")) {
|
||||
if (auto patched = PatchIPS(child->source, ips)) {
|
||||
child->source = std::move(patched);
|
||||
}
|
||||
if (ext_dir != nullptr) {
|
||||
if (const auto ips = ext_dir->GetFile(name + ".ips")) {
|
||||
if (auto patched = PatchIPS(child->source, ips)) {
|
||||
child->source = std::move(patched);
|
||||
}
|
||||
}
|
||||
|
||||
child->size = child->source->GetSize();
|
||||
|
||||
AddFile(parent, child);
|
||||
}
|
||||
|
||||
child->size = child->source->GetSize();
|
||||
|
||||
AddFile(parent, child);
|
||||
}
|
||||
|
||||
for (auto& child : child_dirs) {
|
||||
auto subdir_name = std::string_view(child->path).substr(child->cur_path_ofs);
|
||||
auto child_romfs_dir = romfs_dir->GetSubdirectory(subdir_name);
|
||||
auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(subdir_name) : nullptr;
|
||||
for (auto& child_romfs_dir : romfs_dir->GetSubdirectories()) {
|
||||
const auto name = child_romfs_dir->GetName();
|
||||
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
|
||||
// Set child's path.
|
||||
child->cur_path_ofs = parent->path_len + 1;
|
||||
child->path_len = child->cur_path_ofs + static_cast<u32>(name.size());
|
||||
child->path = parent->path + "/" + name;
|
||||
|
||||
if (ext_dir != nullptr && ext_dir->GetFile(name + ".stub") != nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sanity check on path_len
|
||||
ASSERT(child->path_len < FS_MAX_PATH);
|
||||
|
||||
if (!AddDirectory(parent, child)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto child_ext_dir = ext_dir != nullptr ? ext_dir->GetSubdirectory(name) : nullptr;
|
||||
this->VisitDirectory(child_romfs_dir, child_ext_dir, child);
|
||||
}
|
||||
}
|
||||
@ -293,7 +287,7 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||
|
||||
cur_entry.name_size = name_size;
|
||||
|
||||
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
|
||||
out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, std::move(cur_file->source));
|
||||
std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
|
||||
std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
|
||||
Common::AlignUp(cur_entry.name_size, 4));
|
||||
|
@ -377,16 +377,16 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
||||
|
||||
auto romfs_dir = FindSubdirectoryCaseless(subdir, "romfs");
|
||||
if (romfs_dir != nullptr)
|
||||
layers.push_back(std::make_shared<CachedVfsDirectory>(romfs_dir));
|
||||
layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(romfs_dir)));
|
||||
|
||||
auto ext_dir = FindSubdirectoryCaseless(subdir, "romfs_ext");
|
||||
if (ext_dir != nullptr)
|
||||
layers_ext.push_back(std::make_shared<CachedVfsDirectory>(ext_dir));
|
||||
layers_ext.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(ext_dir)));
|
||||
|
||||
if (type == ContentRecordType::HtmlDocument) {
|
||||
auto manual_dir = FindSubdirectoryCaseless(subdir, "manual_html");
|
||||
if (manual_dir != nullptr)
|
||||
layers.push_back(std::make_shared<CachedVfsDirectory>(manual_dir));
|
||||
layers.emplace_back(std::make_shared<CachedVfsDirectory>(std::move(manual_dir)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
||||
return;
|
||||
}
|
||||
|
||||
layers.push_back(std::move(extracted));
|
||||
layers.emplace_back(std::move(extracted));
|
||||
|
||||
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
|
||||
if (layered == nullptr) {
|
||||
|
@ -322,7 +322,8 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& open_di
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
|
||||
auto name = concat.front()->GetName();
|
||||
return ConcatenatedVfsFile::MakeConcatenatedFile(std::move(name), std::move(concat));
|
||||
}
|
||||
|
||||
VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
|
||||
|
@ -133,7 +133,7 @@ VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
|
||||
out = out->GetSubdirectories().front();
|
||||
}
|
||||
|
||||
return std::make_shared<CachedVfsDirectory>(out);
|
||||
return std::make_shared<CachedVfsDirectory>(std::move(out));
|
||||
}
|
||||
|
||||
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
||||
@ -141,8 +141,7 @@ VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
||||
return nullptr;
|
||||
|
||||
RomFSBuildContext ctx{dir, ext};
|
||||
auto file_map = ctx.Build();
|
||||
return ConcatenatedVfsFile::MakeConcatenatedFile(0, file_map, dir->GetName());
|
||||
return ConcatenatedVfsFile::MakeConcatenatedFile(0, dir->GetName(), ctx.Build());
|
||||
}
|
||||
|
||||
} // namespace FileSys
|
||||
|
@ -6,13 +6,13 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
CachedVfsDirectory::CachedVfsDirectory(VirtualDir& source_dir)
|
||||
CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir)
|
||||
: name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) {
|
||||
for (auto& dir : source_dir->GetSubdirectories()) {
|
||||
dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(dir));
|
||||
dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(std::move(dir)));
|
||||
}
|
||||
for (auto& file : source_dir->GetFiles()) {
|
||||
files.emplace(file->GetName(), file);
|
||||
files.emplace(file->GetName(), std::move(file));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ namespace FileSys {
|
||||
|
||||
class CachedVfsDirectory : public ReadOnlyVfsDirectory {
|
||||
public:
|
||||
CachedVfsDirectory(VirtualDir& source_directory);
|
||||
CachedVfsDirectory(VirtualDir&& source_directory);
|
||||
|
||||
~CachedVfsDirectory() override;
|
||||
VirtualFile GetFile(std::string_view file_name) const override;
|
||||
|
@ -10,7 +10,7 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
ConcatenatedVfsFile::ConcatenatedVfsFile(ConcatenationMap&& concatenation_map_, std::string&& name_)
|
||||
ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_)
|
||||
: concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) {
|
||||
DEBUG_ASSERT(this->VerifyContinuity());
|
||||
}
|
||||
@ -30,8 +30,8 @@ bool ConcatenatedVfsFile::VerifyContinuity() const {
|
||||
|
||||
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
|
||||
|
||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualFile>& files,
|
||||
std::string&& name) {
|
||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name,
|
||||
std::vector<VirtualFile>&& files) {
|
||||
// Fold trivial cases.
|
||||
if (files.empty()) {
|
||||
return nullptr;
|
||||
@ -46,20 +46,21 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(const std::vector<VirtualF
|
||||
u64 last_offset = 0;
|
||||
|
||||
for (auto& file : files) {
|
||||
const auto size = file->GetSize();
|
||||
|
||||
concatenation_map.emplace_back(ConcatenationEntry{
|
||||
.offset = last_offset,
|
||||
.file = file,
|
||||
.file = std::move(file),
|
||||
});
|
||||
|
||||
last_offset += file->GetSize();
|
||||
last_offset += size;
|
||||
}
|
||||
|
||||
return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
|
||||
return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
|
||||
}
|
||||
|
||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
|
||||
const std::multimap<u64, VirtualFile>& files,
|
||||
std::string&& name) {
|
||||
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte, std::string&& name,
|
||||
std::multimap<u64, VirtualFile>&& files) {
|
||||
// Fold trivial cases.
|
||||
if (files.empty()) {
|
||||
return nullptr;
|
||||
@ -76,6 +77,8 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
|
||||
|
||||
// Iteration of a multimap is ordered, so offset will be strictly non-decreasing.
|
||||
for (auto& [offset, file] : files) {
|
||||
const auto size = file->GetSize();
|
||||
|
||||
if (offset > last_offset) {
|
||||
concatenation_map.emplace_back(ConcatenationEntry{
|
||||
.offset = last_offset,
|
||||
@ -85,13 +88,13 @@ VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
|
||||
|
||||
concatenation_map.emplace_back(ConcatenationEntry{
|
||||
.offset = offset,
|
||||
.file = file,
|
||||
.file = std::move(file),
|
||||
});
|
||||
|
||||
last_offset = offset + file->GetSize();
|
||||
last_offset = offset + size;
|
||||
}
|
||||
|
||||
return VirtualFile(new ConcatenatedVfsFile(std::move(concatenation_map), std::move(name)));
|
||||
return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
|
||||
}
|
||||
|
||||
std::string ConcatenatedVfsFile::GetName() const {
|
||||
|
@ -24,22 +24,20 @@ private:
|
||||
};
|
||||
using ConcatenationMap = std::vector<ConcatenationEntry>;
|
||||
|
||||
explicit ConcatenatedVfsFile(std::vector<ConcatenationEntry>&& concatenation_map,
|
||||
std::string&& name);
|
||||
explicit ConcatenatedVfsFile(std::string&& name,
|
||||
std::vector<ConcatenationEntry>&& concatenation_map);
|
||||
bool VerifyContinuity() const;
|
||||
|
||||
public:
|
||||
~ConcatenatedVfsFile() override;
|
||||
|
||||
/// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
|
||||
static VirtualFile MakeConcatenatedFile(const std::vector<VirtualFile>& files,
|
||||
std::string&& name);
|
||||
static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector<VirtualFile>&& files);
|
||||
|
||||
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
|
||||
/// gaps with a given filler byte.
|
||||
static VirtualFile MakeConcatenatedFile(u8 filler_byte,
|
||||
const std::multimap<u64, VirtualFile>& files,
|
||||
std::string&& name);
|
||||
static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name,
|
||||
std::multimap<u64, VirtualFile>&& files);
|
||||
|
||||
std::string GetName() const override;
|
||||
std::size_t GetSize() const override;
|
||||
|
@ -38,7 +38,7 @@ VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) cons
|
||||
for (const auto& layer : dirs) {
|
||||
auto dir = layer->GetDirectoryRelative(path);
|
||||
if (dir != nullptr) {
|
||||
out.push_back(std::move(dir));
|
||||
out.emplace_back(std::move(dir));
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,11 +62,11 @@ std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
||||
std::set<std::string, std::less<>> out_names;
|
||||
|
||||
for (const auto& layer : dirs) {
|
||||
for (const auto& file : layer->GetFiles()) {
|
||||
for (auto& file : layer->GetFiles()) {
|
||||
auto file_name = file->GetName();
|
||||
if (!out_names.contains(file_name)) {
|
||||
out_names.emplace(std::move(file_name));
|
||||
out.push_back(file);
|
||||
out.emplace_back(std::move(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -86,7 +86,7 @@ std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
|
||||
std::vector<VirtualDir> out;
|
||||
out.reserve(names.size());
|
||||
for (const auto& subdir : names)
|
||||
out.push_back(GetSubdirectory(subdir));
|
||||
out.emplace_back(GetSubdirectory(subdir));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
@ -3405,6 +3405,11 @@ Result KPageTable::LockMemoryAndOpen(KPageGroup* out_pg, KPhysicalAddress* out_K
|
||||
new_attr, KMemoryBlockDisableMergeAttribute::Locked,
|
||||
KMemoryBlockDisableMergeAttribute::None);
|
||||
|
||||
// If we have an output page group, open.
|
||||
if (out_pg) {
|
||||
out_pg->Open();
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ public:
|
||||
: ServiceFramework{system_, "IManagerForSystemService"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "CheckAvailability"},
|
||||
{0, &IManagerForSystemService::CheckAvailability, "CheckAvailability"},
|
||||
{1, nullptr, "GetAccountId"},
|
||||
{2, nullptr, "EnsureIdTokenCacheAsync"},
|
||||
{3, nullptr, "LoadIdTokenCache"},
|
||||
@ -78,6 +78,13 @@ public:
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
void CheckAvailability(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
};
|
||||
|
||||
// 3.0.0+
|
||||
@ -400,13 +407,13 @@ protected:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto base = rp.PopRaw<ProfileBase>();
|
||||
|
||||
const auto user_data = ctx.ReadBuffer();
|
||||
const auto image_data = ctx.ReadBuffer(1);
|
||||
const auto image_data = ctx.ReadBufferA(0);
|
||||
const auto user_data = ctx.ReadBufferX(0);
|
||||
|
||||
LOG_DEBUG(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.RawString());
|
||||
LOG_INFO(Service_ACC, "called, username='{}', timestamp={:016X}, uuid=0x{}",
|
||||
Common::StringFromFixedZeroTerminatedBuffer(
|
||||
reinterpret_cast<const char*>(base.username.data()), base.username.size()),
|
||||
base.timestamp, base.user_uuid.RawString());
|
||||
|
||||
if (user_data.size() < sizeof(UserData)) {
|
||||
LOG_ERROR(Service_ACC, "UserData buffer too small!");
|
||||
@ -837,6 +844,29 @@ void Module::Interface::InitializeApplicationInfoV2(HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Module::Interface::BeginUserRegistration(HLERequestContext& ctx) {
|
||||
const auto user_id = Common::UUID::MakeRandom();
|
||||
profile_manager->CreateNewUser(user_id, "yuzu");
|
||||
|
||||
LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 6};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushRaw(user_id);
|
||||
}
|
||||
|
||||
void Module::Interface::CompleteUserRegistration(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_INFO(Service_ACC, "called, uuid={}", user_id.FormattedString());
|
||||
|
||||
profile_manager->WriteUserSaveFile();
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void Module::Interface::GetProfileEditor(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
Common::UUID user_id = rp.PopRaw<Common::UUID>();
|
||||
@ -880,6 +910,17 @@ void Module::Interface::StoreSaveDataThumbnailApplication(HLERequestContext& ctx
|
||||
StoreSaveDataThumbnail(ctx, uuid, tid);
|
||||
}
|
||||
|
||||
void Module::Interface::GetBaasAccountManagerForSystemService(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
||||
LOG_INFO(Service_ACC, "called, uuid=0x{}", uuid.RawString());
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.PushIpcInterface<IManagerForSystemService>(system, uuid);
|
||||
}
|
||||
|
||||
void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto uuid = rp.PopRaw<Common::UUID>();
|
||||
|
@ -33,10 +33,13 @@ public:
|
||||
void TrySelectUserWithoutInteraction(HLERequestContext& ctx);
|
||||
void IsUserAccountSwitchLocked(HLERequestContext& ctx);
|
||||
void InitializeApplicationInfoV2(HLERequestContext& ctx);
|
||||
void BeginUserRegistration(HLERequestContext& ctx);
|
||||
void CompleteUserRegistration(HLERequestContext& ctx);
|
||||
void GetProfileEditor(HLERequestContext& ctx);
|
||||
void ListQualifiedUsers(HLERequestContext& ctx);
|
||||
void ListOpenContextStoredUsers(HLERequestContext& ctx);
|
||||
void StoreSaveDataThumbnailApplication(HLERequestContext& ctx);
|
||||
void GetBaasAccountManagerForSystemService(HLERequestContext& ctx);
|
||||
void StoreSaveDataThumbnailSystem(HLERequestContext& ctx);
|
||||
|
||||
private:
|
||||
|
@ -23,7 +23,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
|
||||
{99, nullptr, "DebugActivateOpenContextRetention"},
|
||||
{100, nullptr, "GetUserRegistrationNotifier"},
|
||||
{101, nullptr, "GetUserStateChangeNotifier"},
|
||||
{102, nullptr, "GetBaasAccountManagerForSystemService"},
|
||||
{102, &ACC_SU::GetBaasAccountManagerForSystemService, "GetBaasAccountManagerForSystemService"},
|
||||
{103, nullptr, "GetBaasUserAvailabilityChangeNotifier"},
|
||||
{104, nullptr, "GetProfileUpdateNotifier"},
|
||||
{105, nullptr, "CheckNetworkServiceAvailabilityAsync"},
|
||||
@ -40,8 +40,8 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module_, std::shared_ptr<ProfileManager>
|
||||
{152, nullptr, "LoadSignedDeviceIdentifierCacheForNintendoAccount"},
|
||||
{190, nullptr, "GetUserLastOpenedApplication"},
|
||||
{191, nullptr, "ActivateOpenContextHolder"},
|
||||
{200, nullptr, "BeginUserRegistration"},
|
||||
{201, nullptr, "CompleteUserRegistration"},
|
||||
{200, &ACC_SU::BeginUserRegistration, "BeginUserRegistration"},
|
||||
{201, &ACC_SU::CompleteUserRegistration, "CompleteUserRegistration"},
|
||||
{202, nullptr, "CancelUserRegistration"},
|
||||
{203, nullptr, "DeleteUser"},
|
||||
{204, nullptr, "SetUserPosition"},
|
||||
|
@ -96,9 +96,10 @@ public:
|
||||
bool SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& profile_new,
|
||||
const UserData& data_new);
|
||||
|
||||
void WriteUserSaveFile();
|
||||
|
||||
private:
|
||||
void ParseUserSaveFile();
|
||||
void WriteUserSaveFile();
|
||||
std::optional<std::size_t> AddToProfiles(const ProfileInfo& profile);
|
||||
bool RemoveProfileAtIndex(std::size_t index);
|
||||
|
||||
|
@ -210,8 +210,8 @@ IDisplayController::IDisplayController(Core::System& system_)
|
||||
{21, nullptr, "ClearAppletTransitionBuffer"},
|
||||
{22, nullptr, "AcquireLastApplicationCaptureSharedBuffer"},
|
||||
{23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"},
|
||||
{24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"},
|
||||
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
|
||||
{24, &IDisplayController::AcquireLastForegroundCaptureSharedBuffer, "AcquireLastForegroundCaptureSharedBuffer"},
|
||||
{25, &IDisplayController::ReleaseLastForegroundCaptureSharedBuffer, "ReleaseLastForegroundCaptureSharedBuffer"},
|
||||
{26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"},
|
||||
{27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"},
|
||||
{28, nullptr, "TakeScreenShotOfOwnLayerEx"},
|
||||
@ -239,6 +239,22 @@ void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IDisplayController::AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push(1U);
|
||||
rb.Push(0);
|
||||
}
|
||||
|
||||
void IDisplayController::ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ResultSuccess);
|
||||
}
|
||||
|
||||
void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
@ -1557,7 +1573,7 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
|
||||
{100, nullptr, "CreateGameMovieTrimmer"},
|
||||
{101, nullptr, "ReserveResourceForMovieOperation"},
|
||||
{102, nullptr, "UnreserveResourceForMovieOperation"},
|
||||
{110, nullptr, "GetMainAppletAvailableUsers"},
|
||||
{110, &ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers, "GetMainAppletAvailableUsers"},
|
||||
{120, nullptr, "GetLaunchStorageInfoForDebug"},
|
||||
{130, nullptr, "GetGpuErrorDetectedSystemEvent"},
|
||||
{140, nullptr, "SetApplicationMemoryReservation"},
|
||||
@ -1652,6 +1668,25 @@ void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext&
|
||||
rb.PushRaw(applet_info);
|
||||
}
|
||||
|
||||
void ILibraryAppletSelfAccessor::GetMainAppletAvailableUsers(HLERequestContext& ctx) {
|
||||
const Service::Account::ProfileManager manager{};
|
||||
bool is_empty{true};
|
||||
s32 user_count{-1};
|
||||
|
||||
LOG_INFO(Service_AM, "called");
|
||||
|
||||
if (manager.GetUserCount() > 0) {
|
||||
is_empty = false;
|
||||
user_count = static_cast<s32>(manager.GetUserCount());
|
||||
ctx.WriteBuffer(manager.GetAllUsers());
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 4};
|
||||
rb.Push(ResultSuccess);
|
||||
rb.Push<u8>(is_empty);
|
||||
rb.Push(user_count);
|
||||
}
|
||||
|
||||
void ILibraryAppletSelfAccessor::PushInShowAlbum() {
|
||||
const Applets::CommonArguments arguments{
|
||||
.arguments_version = Applets::CommonArgumentVersion::Version3,
|
||||
|
@ -124,6 +124,8 @@ public:
|
||||
private:
|
||||
void GetCallerAppletCaptureImageEx(HLERequestContext& ctx);
|
||||
void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
|
||||
void AcquireLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
|
||||
void ReleaseLastForegroundCaptureSharedBuffer(HLERequestContext& ctx);
|
||||
void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
|
||||
void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
|
||||
};
|
||||
@ -345,6 +347,7 @@ private:
|
||||
void GetLibraryAppletInfo(HLERequestContext& ctx);
|
||||
void ExitProcessAndReturn(HLERequestContext& ctx);
|
||||
void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
|
||||
void GetMainAppletAvailableUsers(HLERequestContext& ctx);
|
||||
|
||||
void PushInShowAlbum();
|
||||
void PushInShowCabinetData();
|
||||
|
@ -23,6 +23,17 @@
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/memory.h"
|
||||
|
||||
namespace {
|
||||
static thread_local std::array read_buffer_data_a{
|
||||
Common::ScratchBuffer<u8>(),
|
||||
Common::ScratchBuffer<u8>(),
|
||||
};
|
||||
static thread_local std::array read_buffer_data_x{
|
||||
Common::ScratchBuffer<u8>(),
|
||||
Common::ScratchBuffer<u8>(),
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
namespace Service {
|
||||
|
||||
SessionRequestHandler::SessionRequestHandler(Kernel::KernelCore& kernel_, const char* service_name_)
|
||||
@ -328,26 +339,57 @@ std::vector<u8> HLERequestContext::ReadBufferCopy(std::size_t buffer_index) cons
|
||||
}
|
||||
}
|
||||
|
||||
std::span<const u8> HLERequestContext::ReadBufferA(std::size_t buffer_index) const {
|
||||
static thread_local std::array read_buffer_a{
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
};
|
||||
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorA().size() > buffer_index, { return {}; },
|
||||
"BufferDescriptorA invalid buffer_index {}", buffer_index);
|
||||
auto& read_buffer = read_buffer_a[buffer_index];
|
||||
return read_buffer.Read(BufferDescriptorA()[buffer_index].Address(),
|
||||
BufferDescriptorA()[buffer_index].Size(),
|
||||
&read_buffer_data_a[buffer_index]);
|
||||
}
|
||||
|
||||
std::span<const u8> HLERequestContext::ReadBufferX(std::size_t buffer_index) const {
|
||||
static thread_local std::array read_buffer_x{
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
};
|
||||
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorX().size() > buffer_index, { return {}; },
|
||||
"BufferDescriptorX invalid buffer_index {}", buffer_index);
|
||||
auto& read_buffer = read_buffer_x[buffer_index];
|
||||
return read_buffer.Read(BufferDescriptorX()[buffer_index].Address(),
|
||||
BufferDescriptorX()[buffer_index].Size(),
|
||||
&read_buffer_data_x[buffer_index]);
|
||||
}
|
||||
|
||||
std::span<const u8> HLERequestContext::ReadBuffer(std::size_t buffer_index) const {
|
||||
static thread_local std::array read_buffer_a{
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
};
|
||||
static thread_local std::array read_buffer_data_a{
|
||||
Common::ScratchBuffer<u8>(),
|
||||
Common::ScratchBuffer<u8>(),
|
||||
};
|
||||
static thread_local std::array read_buffer_x{
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
Core::Memory::CpuGuestMemory<u8, Core::Memory::GuestMemoryFlags::SafeRead>(memory, 0, 0),
|
||||
};
|
||||
static thread_local std::array read_buffer_data_x{
|
||||
Common::ScratchBuffer<u8>(),
|
||||
Common::ScratchBuffer<u8>(),
|
||||
};
|
||||
|
||||
const bool is_buffer_a{BufferDescriptorA().size() > buffer_index &&
|
||||
BufferDescriptorA()[buffer_index].Size()};
|
||||
const bool is_buffer_x{BufferDescriptorX().size() > buffer_index &&
|
||||
BufferDescriptorX()[buffer_index].Size()};
|
||||
|
||||
if (is_buffer_a && is_buffer_x) {
|
||||
LOG_WARNING(Input, "Both buffer descriptors are available a.size={}, x.size={}",
|
||||
BufferDescriptorA()[buffer_index].Size(),
|
||||
BufferDescriptorX()[buffer_index].Size());
|
||||
}
|
||||
|
||||
if (is_buffer_a) {
|
||||
ASSERT_OR_EXECUTE_MSG(
|
||||
BufferDescriptorA().size() > buffer_index, { return {}; },
|
||||
|
@ -253,6 +253,12 @@ public:
|
||||
return domain_message_header.has_value();
|
||||
}
|
||||
|
||||
/// Helper function to get a span of a buffer using the buffer descriptor A
|
||||
[[nodiscard]] std::span<const u8> ReadBufferA(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to get a span of a buffer using the buffer descriptor X
|
||||
[[nodiscard]] std::span<const u8> ReadBufferX(std::size_t buffer_index = 0) const;
|
||||
|
||||
/// Helper function to get a span of a buffer using the appropriate buffer descriptor
|
||||
[[nodiscard]] std::span<const u8> ReadBuffer(std::size_t buffer_index = 0) const;
|
||||
|
||||
|
@ -41,6 +41,7 @@ void CoreData::BuildRandom(Age age, Gender gender, Race race) {
|
||||
}
|
||||
}
|
||||
|
||||
SetDefault();
|
||||
SetGender(gender);
|
||||
SetFavoriteColor(MiiUtil::GetRandomValue(FavoriteColor::Max));
|
||||
SetRegionMove(0);
|
||||
|
@ -58,14 +58,8 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = [&ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
|
||||
return std::span<const u8>{};
|
||||
}();
|
||||
const auto data1 = ctx.ReadBufferA(0);
|
||||
const auto data2 = ctx.ReadBufferX(0);
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, type={:02X}, process_id={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
@ -85,14 +79,8 @@ private:
|
||||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto process_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = [&ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
|
||||
return std::span<const u8>{};
|
||||
}();
|
||||
const auto data1 = ctx.ReadBufferA(0);
|
||||
const auto data2 = ctx.ReadBufferX(0);
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, type={:02X}, user_id={:016X}{:016X}, process_id={:016X}, "
|
||||
@ -137,14 +125,8 @@ private:
|
||||
IPC::RequestParser rp{ctx};
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = [&ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
|
||||
return std::span<const u8>{};
|
||||
}();
|
||||
const auto data1 = ctx.ReadBufferA(0);
|
||||
const auto data2 = ctx.ReadBufferX(0);
|
||||
|
||||
LOG_DEBUG(Service_PREPO, "called, title_id={:016X}, data1_size={:016X}, data2_size={:016X}",
|
||||
title_id, data1.size(), data2.size());
|
||||
@ -161,14 +143,8 @@ private:
|
||||
const auto user_id = rp.PopRaw<u128>();
|
||||
const auto title_id = rp.PopRaw<u64>();
|
||||
|
||||
const auto data1 = ctx.ReadBuffer(0);
|
||||
const auto data2 = [&ctx] {
|
||||
if (ctx.CanReadBuffer(1)) {
|
||||
return ctx.ReadBuffer(1);
|
||||
}
|
||||
|
||||
return std::span<const u8>{};
|
||||
}();
|
||||
const auto data1 = ctx.ReadBufferA(0);
|
||||
const auto data2 = ctx.ReadBufferX(0);
|
||||
|
||||
LOG_DEBUG(Service_PREPO,
|
||||
"called, user_id={:016X}{:016X}, title_id={:016X}, data1_size={:016X}, "
|
||||
|
@ -338,6 +338,7 @@ void UDPClient::StartCommunication(std::size_t client, const std::string& host,
|
||||
for (std::size_t index = 0; index < PADS_PER_CLIENT; ++index) {
|
||||
const PadIdentifier identifier = GetPadIdentifier(client * PADS_PER_CLIENT + index);
|
||||
PreSetController(identifier);
|
||||
PreSetMotion(identifier, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,8 +46,8 @@ TEST_CASE("UniqueFunction", "[common]") {
|
||||
Noisy noisy;
|
||||
REQUIRE(noisy.state == "Default constructed");
|
||||
|
||||
Common::UniqueFunction<void> func = [noisy = std::move(noisy)] {
|
||||
REQUIRE(noisy.state == "Move constructed");
|
||||
Common::UniqueFunction<void> func = [noisy_inner = std::move(noisy)] {
|
||||
REQUIRE(noisy_inner.state == "Move constructed");
|
||||
};
|
||||
REQUIRE(noisy.state == "Moved away");
|
||||
func();
|
||||
@ -101,7 +101,7 @@ TEST_CASE("UniqueFunction", "[common]") {
|
||||
};
|
||||
Foo object{&num_destroyed};
|
||||
{
|
||||
Common::UniqueFunction<void> func = [object = std::move(object)] {};
|
||||
Common::UniqueFunction<void> func = [object_inner = std::move(object)] {};
|
||||
REQUIRE(num_destroyed == 0);
|
||||
}
|
||||
REQUIRE(num_destroyed == 1);
|
||||
|
@ -62,7 +62,11 @@ using BufferId = SlotId;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
using namespace Common::Literals;
|
||||
|
||||
#ifdef __APPLE__
|
||||
constexpr u32 NUM_VERTEX_BUFFERS = 16;
|
||||
#else
|
||||
constexpr u32 NUM_VERTEX_BUFFERS = 32;
|
||||
#endif
|
||||
constexpr u32 NUM_TRANSFORM_FEEDBACK_BUFFERS = 4;
|
||||
constexpr u32 NUM_GRAPHICS_UNIFORM_BUFFERS = 18;
|
||||
constexpr u32 NUM_COMPUTE_UNIFORM_BUFFERS = 8;
|
||||
|
@ -48,8 +48,14 @@ void DrawManager::ProcessMethodCall(u32 method, u32 argument) {
|
||||
SetInlineIndexBuffer(regs.inline_index_4x8.index3);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(vertex_array_instance_first):
|
||||
DrawArrayInstanced(regs.vertex_array_instance_first.topology.Value(),
|
||||
regs.vertex_array_instance_first.start.Value(),
|
||||
regs.vertex_array_instance_first.count.Value(), false);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(vertex_array_instance_subsequent): {
|
||||
LOG_WARNING(HW_GPU, "(STUBBED) called");
|
||||
DrawArrayInstanced(regs.vertex_array_instance_subsequent.topology.Value(),
|
||||
regs.vertex_array_instance_subsequent.start.Value(),
|
||||
regs.vertex_array_instance_subsequent.count.Value(), true);
|
||||
break;
|
||||
}
|
||||
case MAXWELL3D_REG_INDEX(draw_texture.src_y0): {
|
||||
@ -84,6 +90,22 @@ void DrawManager::DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 ve
|
||||
ProcessDraw(false, num_instances);
|
||||
}
|
||||
|
||||
void DrawManager::DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
|
||||
bool subsequent) {
|
||||
draw_state.topology = topology;
|
||||
draw_state.vertex_buffer.first = vertex_first;
|
||||
draw_state.vertex_buffer.count = vertex_count;
|
||||
|
||||
if (!subsequent) {
|
||||
draw_state.instance_count = 1;
|
||||
}
|
||||
|
||||
draw_state.base_instance = draw_state.instance_count - 1;
|
||||
draw_state.draw_mode = DrawMode::Instance;
|
||||
draw_state.instance_count++;
|
||||
ProcessDraw(false, 1);
|
||||
}
|
||||
|
||||
void DrawManager::DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count,
|
||||
u32 base_index, u32 base_instance, u32 num_instances) {
|
||||
const auto& regs{maxwell3d->regs};
|
||||
|
@ -66,6 +66,8 @@ public:
|
||||
|
||||
void DrawArray(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
|
||||
u32 base_instance, u32 num_instances);
|
||||
void DrawArrayInstanced(PrimitiveTopology topology, u32 vertex_first, u32 vertex_count,
|
||||
bool subsequent);
|
||||
|
||||
void DrawIndex(PrimitiveTopology topology, u32 index_first, u32 index_count, u32 base_index,
|
||||
u32 base_instance, u32 num_instances);
|
||||
|
@ -137,16 +137,6 @@ bool Codec::CreateGpuAvDevice() {
|
||||
break;
|
||||
}
|
||||
if ((config->methods & HW_CONFIG_METHOD) != 0 && config->device_type == type) {
|
||||
#if defined(__unix__)
|
||||
// Some linux decoding backends are reported to crash with this config method
|
||||
// TODO(ameerj): Properly support this method
|
||||
if ((config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_FRAMES_CTX) != 0) {
|
||||
// skip zero-copy decoders, we don't currently support them
|
||||
LOG_DEBUG(Service_NVDRV, "Skipping decoder {} with unsupported capability {}.",
|
||||
av_hwdevice_get_type_name(type), config->methods);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
LOG_INFO(Service_NVDRV, "Using {} GPU decoder", av_hwdevice_get_type_name(type));
|
||||
av_codec_ctx->pix_fmt = config->pix_fmt;
|
||||
return true;
|
||||
|
@ -19,6 +19,7 @@ set(SHADER_FILES
|
||||
block_linear_unswizzle_2d.comp
|
||||
block_linear_unswizzle_3d.comp
|
||||
convert_abgr8_to_d24s8.frag
|
||||
convert_abgr8_to_d32f.frag
|
||||
convert_d32f_to_abgr8.frag
|
||||
convert_d24s8_to_abgr8.frag
|
||||
convert_depth_to_float.frag
|
||||
|
15
src/video_core/host_shaders/convert_abgr8_to_d32f.frag
Normal file
15
src/video_core/host_shaders/convert_abgr8_to_d32f.frag
Normal file
@ -0,0 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#version 450
|
||||
|
||||
layout(binding = 0) uniform sampler2D color_texture;
|
||||
|
||||
void main() {
|
||||
ivec2 coord = ivec2(gl_FragCoord.xy);
|
||||
vec4 color = texelFetch(color_texture, coord, 0).abgr;
|
||||
|
||||
float value = color.a * (color.r + color.g + color.b) / 3.0f;
|
||||
|
||||
gl_FragDepth = value;
|
||||
}
|
@ -3,16 +3,16 @@
|
||||
|
||||
#version 450
|
||||
|
||||
precision mediump int;
|
||||
precision highp float;
|
||||
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
layout(binding = 1) uniform isampler2D stencil_tex;
|
||||
layout(binding = 1) uniform usampler2D stencil_tex;
|
||||
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
void main() {
|
||||
ivec2 coord = ivec2(gl_FragCoord.xy);
|
||||
uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
|
||||
uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
|
||||
|
||||
highp uint depth_val =
|
||||
uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0));
|
||||
lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r;
|
||||
|
@ -9,6 +9,6 @@ layout(location = 0) out vec4 color;
|
||||
|
||||
void main() {
|
||||
ivec2 coord = ivec2(gl_FragCoord.xy);
|
||||
float depth = textureLod(depth_tex, coord, 0).r;
|
||||
float depth = texelFetch(depth_tex, coord, 0).r;
|
||||
color = vec4(depth, depth, depth, 1.0);
|
||||
}
|
||||
|
@ -3,16 +3,16 @@
|
||||
|
||||
#version 450
|
||||
|
||||
precision mediump int;
|
||||
precision highp float;
|
||||
|
||||
layout(binding = 0) uniform sampler2D depth_tex;
|
||||
layout(binding = 1) uniform isampler2D stencil_tex;
|
||||
layout(binding = 1) uniform usampler2D stencil_tex;
|
||||
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
void main() {
|
||||
ivec2 coord = ivec2(gl_FragCoord.xy);
|
||||
uint depth = uint(textureLod(depth_tex, coord, 0).r * (exp2(24.0) - 1.0f));
|
||||
uint stencil = uint(textureLod(stencil_tex, coord, 0).r);
|
||||
|
||||
highp uint depth_val =
|
||||
uint(textureLod(depth_tex, coord, 0).r * (exp2(32.0) - 1.0));
|
||||
lowp uint stencil_val = textureLod(stencil_tex, coord, 0).r;
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "common/settings.h"
|
||||
#include "video_core/host_shaders/blit_color_float_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_abgr8_to_d24s8_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_abgr8_to_d32f_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_d24s8_to_abgr8_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_d32f_to_abgr8_frag_spv.h"
|
||||
#include "video_core/host_shaders/convert_depth_to_float_frag_spv.h"
|
||||
@ -434,6 +435,7 @@ BlitImageHelper::BlitImageHelper(const Device& device_, Scheduler& scheduler_,
|
||||
convert_depth_to_float_frag(BuildShader(device, CONVERT_DEPTH_TO_FLOAT_FRAG_SPV)),
|
||||
convert_float_to_depth_frag(BuildShader(device, CONVERT_FLOAT_TO_DEPTH_FRAG_SPV)),
|
||||
convert_abgr8_to_d24s8_frag(BuildShader(device, CONVERT_ABGR8_TO_D24S8_FRAG_SPV)),
|
||||
convert_abgr8_to_d32f_frag(BuildShader(device, CONVERT_ABGR8_TO_D32F_FRAG_SPV)),
|
||||
convert_d32f_to_abgr8_frag(BuildShader(device, CONVERT_D32F_TO_ABGR8_FRAG_SPV)),
|
||||
convert_d24s8_to_abgr8_frag(BuildShader(device, CONVERT_D24S8_TO_ABGR8_FRAG_SPV)),
|
||||
convert_s8d24_to_abgr8_frag(BuildShader(device, CONVERT_S8D24_TO_ABGR8_FRAG_SPV)),
|
||||
@ -559,6 +561,13 @@ void BlitImageHelper::ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer,
|
||||
Convert(*convert_abgr8_to_d24s8_pipeline, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer,
|
||||
const ImageView& src_image_view) {
|
||||
ConvertPipelineDepthTargetEx(convert_abgr8_to_d32f_pipeline, dst_framebuffer->RenderPass(),
|
||||
convert_abgr8_to_d32f_frag);
|
||||
Convert(*convert_abgr8_to_d32f_pipeline, dst_framebuffer, src_image_view);
|
||||
}
|
||||
|
||||
void BlitImageHelper::ConvertD32FToABGR8(const Framebuffer* dst_framebuffer,
|
||||
ImageView& src_image_view) {
|
||||
ConvertPipelineColorTargetEx(convert_d32f_to_abgr8_pipeline, dst_framebuffer->RenderPass(),
|
||||
|
@ -67,6 +67,8 @@ public:
|
||||
|
||||
void ConvertABGR8ToD24S8(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
|
||||
|
||||
void ConvertABGR8ToD32F(const Framebuffer* dst_framebuffer, const ImageView& src_image_view);
|
||||
|
||||
void ConvertD32FToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
|
||||
|
||||
void ConvertD24S8ToABGR8(const Framebuffer* dst_framebuffer, ImageView& src_image_view);
|
||||
@ -130,6 +132,7 @@ private:
|
||||
vk::ShaderModule convert_depth_to_float_frag;
|
||||
vk::ShaderModule convert_float_to_depth_frag;
|
||||
vk::ShaderModule convert_abgr8_to_d24s8_frag;
|
||||
vk::ShaderModule convert_abgr8_to_d32f_frag;
|
||||
vk::ShaderModule convert_d32f_to_abgr8_frag;
|
||||
vk::ShaderModule convert_d24s8_to_abgr8_frag;
|
||||
vk::ShaderModule convert_s8d24_to_abgr8_frag;
|
||||
@ -149,6 +152,7 @@ private:
|
||||
vk::Pipeline convert_d16_to_r16_pipeline;
|
||||
vk::Pipeline convert_r16_to_d16_pipeline;
|
||||
vk::Pipeline convert_abgr8_to_d24s8_pipeline;
|
||||
vk::Pipeline convert_abgr8_to_d32f_pipeline;
|
||||
vk::Pipeline convert_d32f_to_abgr8_pipeline;
|
||||
vk::Pipeline convert_d24s8_to_abgr8_pipeline;
|
||||
vk::Pipeline convert_s8d24_to_abgr8_pipeline;
|
||||
|
@ -1436,6 +1436,7 @@ void QueryCacheRuntime::Barriers(bool is_prebarrier) {
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT,
|
||||
};
|
||||
impl->scheduler.RequestOutsideRenderPassOperationContext();
|
||||
if (is_prebarrier) {
|
||||
impl->scheduler.Record([](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
|
@ -975,6 +975,19 @@ void RasterizerVulkan::UpdateScissorsState(Tegra::Engines::Maxwell3D::Regs& regs
|
||||
if (!state_tracker.TouchScissors()) {
|
||||
return;
|
||||
}
|
||||
if (!regs.viewport_scale_offset_enabled) {
|
||||
const auto x = static_cast<float>(regs.surface_clip.x);
|
||||
const auto y = static_cast<float>(regs.surface_clip.y);
|
||||
const auto width = static_cast<float>(regs.surface_clip.width);
|
||||
const auto height = static_cast<float>(regs.surface_clip.height);
|
||||
VkRect2D scissor;
|
||||
scissor.offset.x = static_cast<u32>(x);
|
||||
scissor.offset.y = static_cast<u32>(y);
|
||||
scissor.extent.width = static_cast<u32>(width != 0.0f ? width : 1.0f);
|
||||
scissor.extent.height = static_cast<u32>(height != 0.0f ? height : 1.0f);
|
||||
scheduler.Record([scissor](vk::CommandBuffer cmdbuf) { cmdbuf.SetScissor(0, scissor); });
|
||||
return;
|
||||
}
|
||||
u32 up_scale = 1;
|
||||
u32 down_shift = 0;
|
||||
if (texture_cache.IsRescaling()) {
|
||||
|
@ -19,7 +19,7 @@ VkAttachmentDescription AttachmentDescription(const Device& device, PixelFormat
|
||||
VkSampleCountFlagBits samples) {
|
||||
using MaxwellToVK::SurfaceFormat;
|
||||
return {
|
||||
.flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT,
|
||||
.flags = {},
|
||||
.format = SurfaceFormat(device, FormatType::Optimal, true, format).format,
|
||||
.samples = samples,
|
||||
.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD,
|
||||
|
@ -1194,6 +1194,11 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
|
||||
return blit_image_helper.ConvertD16ToR16(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::A8B8G8R8_SRGB:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::A8B8G8R8_UNORM:
|
||||
if (src_view.format == PixelFormat::S8_UINT_D24_UNORM) {
|
||||
return blit_image_helper.ConvertD24S8ToABGR8(dst, src_view);
|
||||
@ -1205,6 +1210,16 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
|
||||
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::B8G8R8A8_SRGB:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::B8G8R8A8_UNORM:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32FToABGR8(dst, src_view);
|
||||
}
|
||||
break;
|
||||
case PixelFormat::R32_FLOAT:
|
||||
if (src_view.format == PixelFormat::D32_FLOAT) {
|
||||
return blit_image_helper.ConvertD32ToR32(dst, src_view);
|
||||
@ -1222,6 +1237,12 @@ void TextureCacheRuntime::ConvertImage(Framebuffer* dst, ImageView& dst_view, Im
|
||||
}
|
||||
break;
|
||||
case PixelFormat::D32_FLOAT:
|
||||
if (src_view.format == PixelFormat::A8B8G8R8_UNORM ||
|
||||
src_view.format == PixelFormat::B8G8R8A8_UNORM ||
|
||||
src_view.format == PixelFormat::A8B8G8R8_SRGB ||
|
||||
src_view.format == PixelFormat::B8G8R8A8_SRGB) {
|
||||
return blit_image_helper.ConvertABGR8ToD32F(dst, src_view);
|
||||
}
|
||||
if (src_view.format == PixelFormat::R32_FLOAT) {
|
||||
return blit_image_helper.ConvertR32ToD32(dst, src_view);
|
||||
}
|
||||
@ -2034,7 +2055,7 @@ void TextureCacheRuntime::TransitionImageLayout(Image& image) {
|
||||
},
|
||||
};
|
||||
scheduler.RequestOutsideRenderPassOperationContext();
|
||||
scheduler.Record([barrier = barrier](vk::CommandBuffer cmdbuf) {
|
||||
scheduler.Record([barrier](vk::CommandBuffer cmdbuf) {
|
||||
cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, barrier);
|
||||
});
|
||||
|
@ -10,19 +10,23 @@
|
||||
#include "video_core/texture_cache/image_info.h"
|
||||
#include "video_core/texture_cache/image_view_base.h"
|
||||
#include "video_core/texture_cache/render_targets.h"
|
||||
#include "video_core/texture_cache/samples_helper.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
std::string Name(const ImageBase& image) {
|
||||
const GPUVAddr gpu_addr = image.gpu_addr;
|
||||
const ImageInfo& info = image.info;
|
||||
const u32 width = info.size.width;
|
||||
const u32 height = info.size.height;
|
||||
u32 width = info.size.width;
|
||||
u32 height = info.size.height;
|
||||
const u32 depth = info.size.depth;
|
||||
const u32 num_layers = image.info.resources.layers;
|
||||
const u32 num_levels = image.info.resources.levels;
|
||||
std::string resource;
|
||||
if (image.info.num_samples > 1) {
|
||||
const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(image.info.num_samples);
|
||||
width >>= samples_x;
|
||||
height >>= samples_y;
|
||||
resource += fmt::format(":{}xMSAA", image.info.num_samples);
|
||||
}
|
||||
if (num_layers > 1) {
|
||||
|
@ -24,7 +24,7 @@ namespace VideoCommon {
|
||||
return {2, 2};
|
||||
}
|
||||
ASSERT_MSG(false, "Invalid number of samples={}", num_samples);
|
||||
return {1, 1};
|
||||
return {0, 0};
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int NumSamples(Tegra::Texture::MsaaMode msaa_mode) {
|
||||
|
@ -167,6 +167,13 @@ template <u32 GOB_EXTENT>
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Extent3D TileShift(const LevelInfo& info, u32 level) {
|
||||
if (level == 0 && info.num_levels == 1) {
|
||||
return Extent3D{
|
||||
.width = info.block.width,
|
||||
.height = info.block.height,
|
||||
.depth = info.block.depth,
|
||||
};
|
||||
}
|
||||
const Extent3D blocks = NumLevelBlocks(info, level);
|
||||
return Extent3D{
|
||||
.width = AdjustTileSize(info.block.width, GOB_SIZE_X, blocks.width),
|
||||
@ -1293,9 +1300,9 @@ u32 MapSizeBytes(const ImageBase& image) {
|
||||
|
||||
static_assert(CalculateLevelSize(LevelInfo{{1920, 1080, 1}, {0, 2, 0}, {1, 1}, 2, 0, 1}, 0) ==
|
||||
0x7f8000);
|
||||
static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0, 1}, 0) == 0x4000);
|
||||
static_assert(CalculateLevelSize(LevelInfo{{32, 32, 1}, {0, 0, 4}, {1, 1}, 4, 0, 1}, 0) == 0x40000);
|
||||
|
||||
static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0, 1}, 0) == 0x4000);
|
||||
static_assert(CalculateLevelSize(LevelInfo{{128, 8, 1}, {0, 4, 0}, {1, 1}, 4, 0, 1}, 0) == 0x40000);
|
||||
|
||||
static_assert(CalculateLevelOffset(PixelFormat::R8_SINT, {1920, 1080, 1}, {0, 2, 0}, 0, 7) ==
|
||||
0x2afc00);
|
||||
|
@ -384,7 +384,7 @@ if (USE_DISCORD_PRESENCE)
|
||||
discord_impl.cpp
|
||||
discord_impl.h
|
||||
)
|
||||
target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib)
|
||||
target_link_libraries(yuzu PRIVATE DiscordRPC::discord-rpc httplib::httplib Qt${QT_MAJOR_VERSION}::Network)
|
||||
target_compile_definitions(yuzu PRIVATE -DUSE_DISCORD_PRESENCE)
|
||||
endif()
|
||||
|
||||
|
@ -128,8 +128,8 @@ const std::array<UISettings::Shortcut, 22> Config::default_hotkeys{{
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F11"), QStringLiteral("Home+B"), Qt::WindowShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+O"), QStringLiteral(""), Qt::WidgetWithChildrenShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F2"), QStringLiteral("Home+A"), Qt::WidgetWithChildrenShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral(""), Qt::WindowShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral(""), Qt::WindowShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F6"), QStringLiteral("R+Plus+Minus"), Qt::WindowShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("F5"), QStringLiteral("L+Plus+Minus"), Qt::WindowShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F7"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Reset")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F6"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
|
||||
{QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Start/Stop")), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")), {QStringLiteral("Ctrl+F5"), QStringLiteral(""), Qt::ApplicationShortcut, false}},
|
||||
|
@ -115,17 +115,9 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
|
||||
for (std::size_t i = 0; i < player_tabs.size(); ++i) {
|
||||
player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
|
||||
player_tabs[i]->layout()->addWidget(player_controllers[i]);
|
||||
connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
|
||||
connect(player_connected[i], &QCheckBox::clicked, [this, i](int checked) {
|
||||
// Ensures that the controllers are always connected in sequential order
|
||||
if (is_connected) {
|
||||
for (std::size_t index = 0; index <= i; ++index) {
|
||||
player_connected[index]->setChecked(is_connected);
|
||||
}
|
||||
} else {
|
||||
for (std::size_t index = i; index < player_tabs.size(); ++index) {
|
||||
player_connected[index]->setChecked(is_connected);
|
||||
}
|
||||
}
|
||||
this->propagateMouseClickOnPlayers(i, checked, true);
|
||||
});
|
||||
connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this,
|
||||
&ConfigureInput::UpdateAllInputDevices);
|
||||
@ -183,6 +175,30 @@ void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
|
||||
LoadConfiguration();
|
||||
}
|
||||
|
||||
void ConfigureInput::propagateMouseClickOnPlayers(size_t player_index, bool checked, bool origin) {
|
||||
// Origin has already been toggled
|
||||
if (!origin) {
|
||||
player_connected[player_index]->setChecked(checked);
|
||||
}
|
||||
|
||||
if (checked) {
|
||||
// Check all previous buttons when checked
|
||||
if (player_index > 0) {
|
||||
propagateMouseClickOnPlayers(player_index - 1, checked, false);
|
||||
}
|
||||
} else {
|
||||
// Unchecked all following buttons when unchecked
|
||||
if (player_index < player_tabs.size() - 1) {
|
||||
// Reconnect current player if it was the last one checked
|
||||
// (player number was reduced by more than one)
|
||||
if (origin && player_connected[player_index + 1]->checkState() == Qt::Checked) {
|
||||
player_connected[player_index]->setCheckState(Qt::Checked);
|
||||
}
|
||||
propagateMouseClickOnPlayers(player_index + 1, checked, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<QWidget*> ConfigureInput::GetSubTabs() const {
|
||||
return {
|
||||
ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5,
|
||||
|
@ -56,6 +56,7 @@ private:
|
||||
void UpdateDockedState(bool is_handheld);
|
||||
void UpdateAllInputDevices();
|
||||
void UpdateAllInputProfiles(std::size_t player_index);
|
||||
void propagateMouseClickOnPlayers(size_t player_index, bool origin, bool checked);
|
||||
|
||||
/// Load configuration settings.
|
||||
void LoadConfiguration();
|
||||
|
@ -157,6 +157,7 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
|
||||
INSERT(UISettings, select_user_on_boot, "Prompt for user on game boot", "");
|
||||
INSERT(UISettings, pause_when_in_background, "Pause emulation when in background", "");
|
||||
INSERT(UISettings, confirm_before_closing, "Confirm exit while emulation is running", "");
|
||||
INSERT(UISettings, confirm_before_stopping, "Confirm before stopping emulation", "");
|
||||
INSERT(UISettings, hide_mouse, "Hide mouse on inactivity", "");
|
||||
INSERT(UISettings, controller_applet_disabled, "Disable controller applet", "");
|
||||
|
||||
@ -383,6 +384,13 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
|
||||
translations->insert(
|
||||
{Settings::EnumMetadata<Settings::ConsoleMode>::Index(),
|
||||
{PAIR(ConsoleMode, Docked, "Docked"), PAIR(ConsoleMode, Handheld, "Handheld")}});
|
||||
translations->insert(
|
||||
{Settings::EnumMetadata<Settings::ConfirmStop>::Index(),
|
||||
{
|
||||
PAIR(ConfirmStop, Ask_Always, "Always ask (Default)"),
|
||||
PAIR(ConfirmStop, Ask_Based_On_Game, "Only if game specifies not to stop"),
|
||||
PAIR(ConfirmStop, Ask_Never, "Never ask"),
|
||||
}});
|
||||
|
||||
#undef PAIR
|
||||
#undef CTX_PAIR
|
||||
|
@ -826,12 +826,13 @@ void GameList::PopulateAsync(QVector<UISettings::GameDir>& game_dirs) {
|
||||
tree_view->setColumnHidden(COLUMN_SIZE, !UISettings::values.show_size);
|
||||
tree_view->setColumnHidden(COLUMN_PLAY_TIME, !UISettings::values.show_play_time);
|
||||
|
||||
// Before deleting rows, cancel the worker so that it is not using them
|
||||
emit ShouldCancelWorker();
|
||||
|
||||
// Delete any rows that might already exist if we're repopulating
|
||||
item_model->removeRows(0, item_model->rowCount());
|
||||
search_field->clear();
|
||||
|
||||
emit ShouldCancelWorker();
|
||||
|
||||
GameListWorker* worker =
|
||||
new GameListWorker(vfs, provider, game_dirs, compatibility_list, play_time_manager, system);
|
||||
|
||||
|
@ -293,7 +293,7 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
|
||||
void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan,
|
||||
GameListDir* parent_dir) {
|
||||
const auto callback = [this, target, parent_dir](const std::filesystem::path& path) -> bool {
|
||||
if (stop_processing) {
|
||||
if (stop_requested) {
|
||||
// Breaks the callback loop.
|
||||
return false;
|
||||
}
|
||||
@ -399,7 +399,6 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
|
||||
}
|
||||
|
||||
void GameListWorker::run() {
|
||||
stop_processing = false;
|
||||
provider->ClearAllEntries();
|
||||
|
||||
for (UISettings::GameDir& game_dir : game_dirs) {
|
||||
@ -427,9 +426,11 @@ void GameListWorker::run() {
|
||||
}
|
||||
|
||||
emit Finished(watch_list);
|
||||
processing_completed.Set();
|
||||
}
|
||||
|
||||
void GameListWorker::Cancel() {
|
||||
this->disconnect();
|
||||
stop_processing = true;
|
||||
stop_requested.store(true);
|
||||
processing_completed.Wait();
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <QRunnable>
|
||||
#include <QString>
|
||||
|
||||
#include "common/thread.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/play_time_manager.h"
|
||||
|
||||
@ -82,7 +83,9 @@ private:
|
||||
const PlayTime::PlayTimeManager& play_time_manager;
|
||||
|
||||
QStringList watch_list;
|
||||
std::atomic_bool stop_processing;
|
||||
|
||||
Common::Event processing_completed;
|
||||
std::atomic_bool stop_requested = false;
|
||||
|
||||
Core::System& system;
|
||||
};
|
||||
|
@ -67,6 +67,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#define QT_NO_OPENGL
|
||||
#include <QClipboard>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileDialog>
|
||||
#include <QInputDialog>
|
||||
@ -76,6 +77,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
|
||||
#include <QPushButton>
|
||||
#include <QScreen>
|
||||
#include <QShortcut>
|
||||
#include <QStandardPaths>
|
||||
#include <QStatusBar>
|
||||
#include <QString>
|
||||
#include <QSysInfo>
|
||||
@ -209,7 +211,7 @@ void GMainWindow::ShowTelemetryCallout() {
|
||||
tr("<a href='https://yuzu-emu.org/help/feature/telemetry/'>Anonymous "
|
||||
"data is collected</a> to help improve yuzu. "
|
||||
"<br/><br/>Would you like to share your usage data with us?");
|
||||
if (QMessageBox::question(this, tr("Telemetry"), telemetry_message) != QMessageBox::Yes) {
|
||||
if (!question(this, tr("Telemetry"), telemetry_message)) {
|
||||
Settings::values.enable_telemetry = false;
|
||||
system->ApplySettings();
|
||||
}
|
||||
@ -2418,9 +2420,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT
|
||||
}
|
||||
}();
|
||||
|
||||
if (QMessageBox::question(this, tr("Remove Entry"), entry_question,
|
||||
QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::No) != QMessageBox::Yes) {
|
||||
if (!question(this, tr("Remove Entry"), entry_question, QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::No)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2519,8 +2520,8 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ
|
||||
}
|
||||
}();
|
||||
|
||||
if (QMessageBox::question(this, tr("Remove File"), question, QMessageBox::Yes | QMessageBox::No,
|
||||
QMessageBox::No) != QMessageBox::Yes) {
|
||||
if (!GMainWindow::question(this, tr("Remove File"), question,
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2869,44 +2870,50 @@ void GMainWindow::OnGameListCreateShortcut(u64 program_id, const std::string& ga
|
||||
#endif // __linux__
|
||||
|
||||
std::filesystem::path target_directory{};
|
||||
// Determine target directory for shortcut
|
||||
#if defined(WIN32)
|
||||
const char* home = std::getenv("USERPROFILE");
|
||||
#else
|
||||
const char* home = std::getenv("HOME");
|
||||
#endif
|
||||
const std::filesystem::path home_path = (home == nullptr ? "~" : home);
|
||||
const char* xdg_data_home = std::getenv("XDG_DATA_HOME");
|
||||
|
||||
if (target == GameListShortcutTarget::Desktop) {
|
||||
target_directory = home_path / "Desktop";
|
||||
if (!Common::FS::IsDir(target_directory)) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Create Shortcut"),
|
||||
tr("Cannot create shortcut on desktop. Path \"%1\" does not exist.")
|
||||
.arg(QString::fromStdString(target_directory.generic_string())),
|
||||
QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
}
|
||||
} else if (target == GameListShortcutTarget::Applications) {
|
||||
target_directory = (xdg_data_home == nullptr ? home_path / ".local/share" : xdg_data_home) /
|
||||
"applications";
|
||||
if (!Common::FS::CreateDirs(target_directory)) {
|
||||
QMessageBox::critical(
|
||||
this, tr("Create Shortcut"),
|
||||
tr("Cannot create shortcut in applications menu. Path \"%1\" "
|
||||
"does not exist and cannot be created.")
|
||||
.arg(QString::fromStdString(target_directory.generic_string())),
|
||||
QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
switch (target) {
|
||||
case GameListShortcutTarget::Desktop: {
|
||||
const QString desktop_path =
|
||||
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
target_directory = desktop_path.toUtf8().toStdString();
|
||||
break;
|
||||
}
|
||||
case GameListShortcutTarget::Applications: {
|
||||
const QString applications_path =
|
||||
QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
|
||||
if (applications_path.isEmpty()) {
|
||||
const char* home = std::getenv("HOME");
|
||||
if (home != nullptr) {
|
||||
target_directory = std::filesystem::path(home) / ".local/share/applications";
|
||||
}
|
||||
} else {
|
||||
target_directory = applications_path.toUtf8().toStdString();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
const QDir dir(QString::fromStdString(target_directory.generic_string()));
|
||||
if (!dir.exists()) {
|
||||
QMessageBox::critical(this, tr("Create Shortcut"),
|
||||
tr("Cannot create shortcut. Path \"%1\" does not exist.")
|
||||
.arg(QString::fromStdString(target_directory.generic_string())),
|
||||
QMessageBox::StandardButton::Ok);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string game_file_name = std::filesystem::path(game_path).filename().string();
|
||||
// Determine full paths for icon and shortcut
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
const char* home = std::getenv("HOME");
|
||||
const std::filesystem::path home_path = (home == nullptr ? "~" : home);
|
||||
const char* xdg_data_home = std::getenv("XDG_DATA_HOME");
|
||||
|
||||
std::filesystem::path system_icons_path =
|
||||
(xdg_data_home == nullptr ? home_path / ".local/share/" : xdg_data_home) /
|
||||
(xdg_data_home == nullptr ? home_path / ".local/share/"
|
||||
: std::filesystem::path(xdg_data_home)) /
|
||||
"icons/hicolor/256x256";
|
||||
if (!Common::FS::CreateDirs(system_icons_path)) {
|
||||
QMessageBox::critical(
|
||||
@ -3401,10 +3408,13 @@ void GMainWindow::OnRestartGame() {
|
||||
if (!system->IsPoweredOn()) {
|
||||
return;
|
||||
}
|
||||
// Make a copy since ShutdownGame edits game_path
|
||||
const auto current_game = QString(current_game_path);
|
||||
ShutdownGame();
|
||||
BootGame(current_game);
|
||||
|
||||
if (ConfirmShutdownGame()) {
|
||||
// Make a copy since ShutdownGame edits game_path
|
||||
const auto current_game = QString(current_game_path);
|
||||
ShutdownGame();
|
||||
BootGame(current_game);
|
||||
}
|
||||
}
|
||||
|
||||
void GMainWindow::OnPauseGame() {
|
||||
@ -3426,18 +3436,39 @@ void GMainWindow::OnPauseContinueGame() {
|
||||
}
|
||||
|
||||
void GMainWindow::OnStopGame() {
|
||||
if (system->GetExitLocked() && !ConfirmForceLockedExit()) {
|
||||
return;
|
||||
if (ConfirmShutdownGame()) {
|
||||
play_time_manager->Stop();
|
||||
// Update game list to show new play time
|
||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||
if (OnShutdownBegin()) {
|
||||
OnShutdownBeginDialog();
|
||||
} else {
|
||||
OnEmulationStopped();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
play_time_manager->Stop();
|
||||
// Update game list to show new play time
|
||||
game_list->PopulateAsync(UISettings::values.game_dirs);
|
||||
if (OnShutdownBegin()) {
|
||||
OnShutdownBeginDialog();
|
||||
bool GMainWindow::ConfirmShutdownGame() {
|
||||
if (UISettings::values.confirm_before_stopping.GetValue() == ConfirmStop::Ask_Always) {
|
||||
if (system->GetExitLocked()) {
|
||||
if (!ConfirmForceLockedExit()) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!ConfirmChangeGame()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
OnEmulationStopped();
|
||||
if (UISettings::values.confirm_before_stopping.GetValue() ==
|
||||
ConfirmStop::Ask_Based_On_Game &&
|
||||
system->GetExitLocked()) {
|
||||
if (!ConfirmForceLockedExit()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void GMainWindow::OnLoadComplete() {
|
||||
@ -3817,22 +3848,11 @@ void GMainWindow::OnTasRecord() {
|
||||
const bool is_recording = input_subsystem->GetTas()->Record();
|
||||
if (!is_recording) {
|
||||
is_tas_recording_dialog_active = true;
|
||||
ControllerNavigation* controller_navigation =
|
||||
new ControllerNavigation(system->HIDCore(), this);
|
||||
// Use QMessageBox instead of question so we can link controller navigation
|
||||
QMessageBox* box_dialog = new QMessageBox();
|
||||
box_dialog->setWindowTitle(tr("TAS Recording"));
|
||||
box_dialog->setText(tr("Overwrite file of player 1?"));
|
||||
box_dialog->setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
box_dialog->setDefaultButton(QMessageBox::Yes);
|
||||
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
|
||||
[box_dialog](Qt::Key key) {
|
||||
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
|
||||
QCoreApplication::postEvent(box_dialog, event);
|
||||
});
|
||||
int res = box_dialog->exec();
|
||||
controller_navigation->UnloadController();
|
||||
input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
|
||||
|
||||
bool answer = question(this, tr("TAS Recording"), tr("Overwrite file of player 1?"),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
|
||||
input_subsystem->GetTas()->SaveRecording(answer);
|
||||
is_tas_recording_dialog_active = false;
|
||||
}
|
||||
OnTasStateChanged();
|
||||
@ -4073,6 +4093,29 @@ void GMainWindow::OnLoadAmiibo() {
|
||||
LoadAmiibo(filename);
|
||||
}
|
||||
|
||||
bool GMainWindow::question(QWidget* parent, const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons,
|
||||
QMessageBox::StandardButton defaultButton) {
|
||||
|
||||
QMessageBox* box_dialog = new QMessageBox(parent);
|
||||
box_dialog->setWindowTitle(title);
|
||||
box_dialog->setText(text);
|
||||
box_dialog->setStandardButtons(buttons);
|
||||
box_dialog->setDefaultButton(defaultButton);
|
||||
|
||||
ControllerNavigation* controller_navigation =
|
||||
new ControllerNavigation(system->HIDCore(), box_dialog);
|
||||
connect(controller_navigation, &ControllerNavigation::TriggerKeyboardEvent,
|
||||
[box_dialog](Qt::Key key) {
|
||||
QKeyEvent* event = new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier);
|
||||
QCoreApplication::postEvent(box_dialog, event);
|
||||
});
|
||||
int res = box_dialog->exec();
|
||||
|
||||
controller_navigation->UnloadController();
|
||||
return res == QMessageBox::Yes;
|
||||
}
|
||||
|
||||
void GMainWindow::LoadAmiibo(const QString& filename) {
|
||||
auto* virtual_amiibo = input_subsystem->GetVirtualAmiibo();
|
||||
const QString title = tr("Error loading Amiibo data");
|
||||
@ -4806,8 +4849,7 @@ bool GMainWindow::ConfirmClose() {
|
||||
return true;
|
||||
}
|
||||
const auto text = tr("Are you sure you want to close yuzu?");
|
||||
const auto answer = QMessageBox::question(this, tr("yuzu"), text);
|
||||
return answer != QMessageBox::No;
|
||||
return question(this, tr("yuzu"), text);
|
||||
}
|
||||
|
||||
void GMainWindow::closeEvent(QCloseEvent* event) {
|
||||
@ -4900,11 +4942,11 @@ bool GMainWindow::ConfirmChangeGame() {
|
||||
if (emu_thread == nullptr)
|
||||
return true;
|
||||
|
||||
const auto answer = QMessageBox::question(
|
||||
// Use custom question to link controller navigation
|
||||
return question(
|
||||
this, tr("yuzu"),
|
||||
tr("Are you sure you want to stop the emulation? Any unsaved progress will be lost."),
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
return answer != QMessageBox::No;
|
||||
QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
|
||||
}
|
||||
|
||||
bool GMainWindow::ConfirmForceLockedExit() {
|
||||
@ -4914,8 +4956,7 @@ bool GMainWindow::ConfirmForceLockedExit() {
|
||||
const auto text = tr("The currently running application has requested yuzu to not exit.\n\n"
|
||||
"Would you like to bypass this and exit anyway?");
|
||||
|
||||
const auto answer = QMessageBox::question(this, tr("yuzu"), text);
|
||||
return answer != QMessageBox::No;
|
||||
return question(this, tr("yuzu"), text);
|
||||
}
|
||||
|
||||
void GMainWindow::RequestGameExit() {
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <optional>
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include <QTranslator>
|
||||
|
||||
@ -15,6 +16,7 @@
|
||||
#include "input_common/drivers/tas_input.h"
|
||||
#include "yuzu/compatibility_list.h"
|
||||
#include "yuzu/hotkeys.h"
|
||||
#include "yuzu/util/controller_navigation.h"
|
||||
|
||||
#ifdef __unix__
|
||||
#include <QVariant>
|
||||
@ -424,6 +426,11 @@ private:
|
||||
bool CheckSystemArchiveDecryption();
|
||||
bool CheckFirmwarePresence();
|
||||
void ConfigureFilesystemProvider(const std::string& filepath);
|
||||
/**
|
||||
* Open (or not) the right confirm dialog based on current setting and game exit lock
|
||||
* @returns true if the player confirmed or the settings do no require it
|
||||
*/
|
||||
bool ConfirmShutdownGame();
|
||||
|
||||
QString GetTasStateDescription() const;
|
||||
bool CreateShortcut(const std::string& shortcut_path, const std::string& title,
|
||||
@ -431,6 +438,17 @@ private:
|
||||
const std::string& command, const std::string& arguments,
|
||||
const std::string& categories, const std::string& keywords);
|
||||
|
||||
/**
|
||||
* Mimic the behavior of QMessageBox::question but link controller navigation to the dialog
|
||||
* The only difference is that it returns a boolean.
|
||||
*
|
||||
* @returns true if buttons contains QMessageBox::Yes and the user clicks on the "Yes" button.
|
||||
*/
|
||||
bool question(QWidget* parent, const QString& title, const QString& text,
|
||||
QMessageBox::StandardButtons buttons =
|
||||
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
|
||||
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
|
||||
|
||||
std::unique_ptr<Ui::MainWindow> ui;
|
||||
|
||||
std::unique_ptr<Core::System> system;
|
||||
|
@ -16,7 +16,9 @@
|
||||
#include "common/settings_enums.h"
|
||||
|
||||
using Settings::Category;
|
||||
using Settings::ConfirmStop;
|
||||
using Settings::Setting;
|
||||
using Settings::SwitchableSetting;
|
||||
|
||||
#ifndef CANNOT_EXPLICITLY_INSTANTIATE
|
||||
namespace Settings {
|
||||
@ -94,6 +96,15 @@ struct Values {
|
||||
Setting<bool> confirm_before_closing{
|
||||
linkage, true, "confirmClose", Category::UiGeneral, Settings::Specialization::Default,
|
||||
true, true};
|
||||
|
||||
SwitchableSetting<ConfirmStop> confirm_before_stopping{linkage,
|
||||
ConfirmStop::Ask_Always,
|
||||
"confirmStop",
|
||||
Category::UiGeneral,
|
||||
Settings::Specialization::Default,
|
||||
true,
|
||||
true};
|
||||
|
||||
Setting<bool> first_start{linkage, true, "firstStart", Category::Ui};
|
||||
Setting<bool> pause_when_in_background{linkage,
|
||||
false,
|
||||
|
Reference in New Issue
Block a user