summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/common_paths.h6
-rw-r--r--src/common/file_util.cpp68
-rw-r--r--src/common/file_util.h27
-rw-r--r--src/common/logging/backend.cpp25
-rw-r--r--src/common/logging/backend.h7
-rw-r--r--src/common/logging/filter.cpp75
-rw-r--r--src/common/logging/filter.h6
-rw-r--r--src/common/param_package.cpp19
-rw-r--r--src/common/param_package.h2
-rw-r--r--src/common/synchronized_wrapper.h85
-rw-r--r--src/core/arm/arm_interface.h7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.cpp10
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic.h2
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp14
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h2
-rw-r--r--src/core/file_sys/errors.h16
-rw-r--r--src/core/file_sys/partition_filesystem.cpp12
-rw-r--r--src/core/file_sys/vfs.cpp2
-rw-r--r--src/core/file_sys/vfs.h27
-rw-r--r--src/core/file_sys/vfs_offset.cpp5
-rw-r--r--src/core/file_sys/vfs_offset.h2
-rw-r--r--src/core/file_sys/vfs_real.cpp16
-rw-r--r--src/core/hle/ipc_helpers.h19
-rw-r--r--src/core/hle/kernel/scheduler.cpp3
-rw-r--r--src/core/hle/kernel/thread.cpp1
-rw-r--r--src/core/hle/kernel/thread.h9
-rw-r--r--src/core/hle/service/apm/interface.cpp17
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp4
-rw-r--r--src/core/hle/service/ns/pl_u.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp30
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h8
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp2
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp7
-rw-r--r--src/core/telemetry_session.cpp6
-rw-r--r--src/tests/core/arm/arm_test_common.cpp9
-rw-r--r--src/tests/core/arm/arm_test_common.h7
-rw-r--r--src/video_core/gpu.cpp6
-rw-r--r--src/video_core/gpu.h9
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_shader_manager.h1
-rw-r--r--src/yuzu/configuration/config.cpp2
-rw-r--r--src/yuzu/configuration/configure_debug.cpp2
-rw-r--r--src/yuzu/debugger/graphics/graphics_surface.cpp6
-rw-r--r--src/yuzu/main.cpp18
-rw-r--r--src/yuzu_cmd/config.cpp2
-rw-r--r--src/yuzu_cmd/yuzu.cpp21
48 files changed, 338 insertions, 304 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index f49a31612..d5d4f6f82 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -63,7 +63,6 @@ add_library(common STATIC
63 string_util.cpp 63 string_util.cpp
64 string_util.h 64 string_util.h
65 swap.h 65 swap.h
66 synchronized_wrapper.h
67 telemetry.cpp 66 telemetry.cpp
68 telemetry.h 67 telemetry.h
69 thread.cpp 68 thread.cpp
diff --git a/src/common/common_paths.h b/src/common/common_paths.h
index 9bf3efaf2..6799a357a 100644
--- a/src/common/common_paths.h
+++ b/src/common/common_paths.h
@@ -26,7 +26,7 @@
26#define USA_DIR "USA" 26#define USA_DIR "USA"
27#define JAP_DIR "JAP" 27#define JAP_DIR "JAP"
28 28
29// Subdirs in the User dir returned by GetUserPath(D_USER_IDX) 29// Subdirs in the User dir returned by GetUserPath(UserPath::UserDir)
30#define CONFIG_DIR "config" 30#define CONFIG_DIR "config"
31#define CACHE_DIR "cache" 31#define CACHE_DIR "cache"
32#define SDMC_DIR "sdmc" 32#define SDMC_DIR "sdmc"
@@ -35,11 +35,11 @@
35#define LOG_DIR "log" 35#define LOG_DIR "log"
36 36
37// Filenames 37// Filenames
38// Files in the directory returned by GetUserPath(D_CONFIG_IDX) 38// Files in the directory returned by GetUserPath(UserPath::ConfigDir)
39#define EMU_CONFIG "emu.ini" 39#define EMU_CONFIG "emu.ini"
40#define DEBUGGER_CONFIG "debugger.ini" 40#define DEBUGGER_CONFIG "debugger.ini"
41#define LOGGER_CONFIG "logger.ini" 41#define LOGGER_CONFIG "logger.ini"
42// Files in the directory returned by GetUserPath(D_LOGS_IDX) 42// Files in the directory returned by GetUserPath(UserPath::LogDir)
43#define LOG_FILE "yuzu_log.txt" 43#define LOG_FILE "yuzu_log.txt"
44 44
45// Sys files 45// Sys files
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index c882ab39f..1e28f7cbb 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <sstream> 5#include <sstream>
6#include <unordered_map>
6#include "common/assert.h" 7#include "common/assert.h"
7#include "common/common_funcs.h" 8#include "common/common_funcs.h"
8#include "common/common_paths.h" 9#include "common/common_paths.h"
@@ -681,67 +682,68 @@ std::string GetSysDirectory() {
681 682
682// Returns a string with a yuzu data dir or file in the user's home 683// Returns a string with a yuzu data dir or file in the user's home
683// directory. To be used in "multi-user" mode (that is, installed). 684// directory. To be used in "multi-user" mode (that is, installed).
684const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath) { 685const std::string& GetUserPath(UserPath path, const std::string& new_path) {
685 static std::string paths[NUM_PATH_INDICES]; 686 static std::unordered_map<UserPath, std::string> paths;
687 auto& user_path = paths[UserPath::UserDir];
686 688
687 // Set up all paths and files on the first run 689 // Set up all paths and files on the first run
688 if (paths[D_USER_IDX].empty()) { 690 if (user_path.empty()) {
689#ifdef _WIN32 691#ifdef _WIN32
690 paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; 692 user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
691 if (!FileUtil::IsDirectory(paths[D_USER_IDX])) { 693 if (!FileUtil::IsDirectory(user_path)) {
692 paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; 694 user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
693 } else { 695 } else {
694 LOG_INFO(Common_Filesystem, "Using the local user directory"); 696 LOG_INFO(Common_Filesystem, "Using the local user directory");
695 } 697 }
696 698
697 paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; 699 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
698 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; 700 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
699#else 701#else
700 if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) { 702 if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
701 paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; 703 user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
702 paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; 704 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
703 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; 705 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
704 } else { 706 } else {
705 std::string data_dir = GetUserDirectory("XDG_DATA_HOME"); 707 std::string data_dir = GetUserDirectory("XDG_DATA_HOME");
706 std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME"); 708 std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME");
707 std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME"); 709 std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME");
708 710
709 paths[D_USER_IDX] = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP; 711 user_path = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
710 paths[D_CONFIG_IDX] = config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP; 712 paths.emplace(UserPath::ConfigDir, config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP);
711 paths[D_CACHE_IDX] = cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP; 713 paths.emplace(UserPath::CacheDir, cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP);
712 } 714 }
713#endif 715#endif
714 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; 716 paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
715 paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; 717 paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
716 paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP; 718 paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
717 // TODO: Put the logs in a better location for each OS 719 // TODO: Put the logs in a better location for each OS
718 paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOG_DIR DIR_SEP; 720 paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP);
719 } 721 }
720 722
721 if (!newPath.empty()) { 723 if (!new_path.empty()) {
722 if (!FileUtil::IsDirectory(newPath)) { 724 if (!FileUtil::IsDirectory(new_path)) {
723 LOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath); 725 LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path);
724 return paths[DirIDX]; 726 return paths[path];
725 } else { 727 } else {
726 paths[DirIDX] = newPath; 728 paths[path] = new_path;
727 } 729 }
728 730
729 switch (DirIDX) { 731 switch (path) {
730 case D_ROOT_IDX: 732 case UserPath::RootDir:
731 paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP; 733 user_path = paths[UserPath::RootDir] + DIR_SEP;
732 break; 734 break;
733 735
734 case D_USER_IDX: 736 case UserPath::UserDir:
735 paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP; 737 user_path = paths[UserPath::RootDir] + DIR_SEP;
736 paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; 738 paths[UserPath::ConfigDir] = user_path + CONFIG_DIR DIR_SEP;
737 paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP; 739 paths[UserPath::CacheDir] = user_path + CACHE_DIR DIR_SEP;
738 paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP; 740 paths[UserPath::SDMCDir] = user_path + SDMC_DIR DIR_SEP;
739 paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP; 741 paths[UserPath::NANDDir] = user_path + NAND_DIR DIR_SEP;
740 break; 742 break;
741 } 743 }
742 } 744 }
743 745
744 return paths[DirIDX]; 746 return paths[path];
745} 747}
746 748
747size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) { 749size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 1f38b1560..ff01bf0ff 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -16,21 +16,20 @@
16#include "common/string_util.h" 16#include "common/string_util.h"
17#endif 17#endif
18 18
19// User directory indices for GetUserPath
20enum {
21 D_USER_IDX,
22 D_ROOT_IDX,
23 D_CONFIG_IDX,
24 D_CACHE_IDX,
25 D_SDMC_IDX,
26 D_NAND_IDX,
27 D_SYSDATA_IDX,
28 D_LOGS_IDX,
29 NUM_PATH_INDICES
30};
31
32namespace FileUtil { 19namespace FileUtil {
33 20
21// User paths for GetUserPath
22enum class UserPath {
23 CacheDir,
24 ConfigDir,
25 LogDir,
26 NANDDir,
27 RootDir,
28 SDMCDir,
29 SysDataDir,
30 UserDir,
31};
32
34// FileSystem tree node/ 33// FileSystem tree node/
35struct FSTEntry { 34struct FSTEntry {
36 bool isDirectory; 35 bool isDirectory;
@@ -123,7 +122,7 @@ bool SetCurrentDir(const std::string& directory);
123 122
124// Returns a pointer to a string with a yuzu data dir in the user's home 123// Returns a pointer to a string with a yuzu data dir in the user's home
125// directory. To be used in "multi-user" mode (that is, installed). 124// directory. To be used in "multi-user" mode (that is, installed).
126const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath = ""); 125const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
127 126
128// Returns the path to where the sys file are 127// Returns the path to where the sys file are
129std::string GetSysDirectory(); 128std::string GetSysDirectory();
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index ed1e93cc2..59b999935 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -3,19 +3,20 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <atomic>
7#include <chrono> 7#include <chrono>
8#include <climits> 8#include <climits>
9#include <condition_variable> 9#include <condition_variable>
10#include <memory> 10#include <memory>
11#include <mutex>
11#include <thread> 12#include <thread>
13#include <vector>
12#ifdef _WIN32 14#ifdef _WIN32
13#include <share.h> // For _SH_DENYWR 15#include <share.h> // For _SH_DENYWR
14#else 16#else
15#define _SH_DENYWR 0 17#define _SH_DENYWR 0
16#endif 18#endif
17#include "common/assert.h" 19#include "common/assert.h"
18#include "common/common_funcs.h" // snprintf compatibility define
19#include "common/logging/backend.h" 20#include "common/logging/backend.h"
20#include "common/logging/log.h" 21#include "common/logging/log.h"
21#include "common/logging/text_formatter.h" 22#include "common/logging/text_formatter.h"
@@ -48,11 +49,11 @@ public:
48 backends.push_back(std::move(backend)); 49 backends.push_back(std::move(backend));
49 } 50 }
50 51
51 void RemoveBackend(const std::string& backend_name) { 52 void RemoveBackend(std::string_view backend_name) {
52 std::lock_guard<std::mutex> lock(writing_mutex); 53 std::lock_guard<std::mutex> lock(writing_mutex);
53 auto it = std::remove_if(backends.begin(), backends.end(), [&backend_name](const auto& i) { 54 const auto it =
54 return !strcmp(i->GetName(), backend_name.c_str()); 55 std::remove_if(backends.begin(), backends.end(),
55 }); 56 [&backend_name](const auto& i) { return backend_name == i->GetName(); });
56 backends.erase(it, backends.end()); 57 backends.erase(it, backends.end());
57 } 58 }
58 59
@@ -64,10 +65,10 @@ public:
64 filter = f; 65 filter = f;
65 } 66 }
66 67
67 Backend* GetBackend(const std::string& backend_name) { 68 Backend* GetBackend(std::string_view backend_name) {
68 auto it = std::find_if(backends.begin(), backends.end(), [&backend_name](const auto& i) { 69 const auto it =
69 return !strcmp(i->GetName(), backend_name.c_str()); 70 std::find_if(backends.begin(), backends.end(),
70 }); 71 [&backend_name](const auto& i) { return backend_name == i->GetName(); });
71 if (it == backends.end()) 72 if (it == backends.end())
72 return nullptr; 73 return nullptr;
73 return it->get(); 74 return it->get();
@@ -265,11 +266,11 @@ void AddBackend(std::unique_ptr<Backend> backend) {
265 Impl::Instance().AddBackend(std::move(backend)); 266 Impl::Instance().AddBackend(std::move(backend));
266} 267}
267 268
268void RemoveBackend(const std::string& backend_name) { 269void RemoveBackend(std::string_view backend_name) {
269 Impl::Instance().RemoveBackend(backend_name); 270 Impl::Instance().RemoveBackend(backend_name);
270} 271}
271 272
272Backend* GetBackend(const std::string& backend_name) { 273Backend* GetBackend(std::string_view backend_name) {
273 return Impl::Instance().GetBackend(backend_name); 274 return Impl::Instance().GetBackend(backend_name);
274} 275}
275 276
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h
index 57cdf6b2d..b3f4b9cef 100644
--- a/src/common/logging/backend.h
+++ b/src/common/logging/backend.h
@@ -4,10 +4,9 @@
4#pragma once 4#pragma once
5 5
6#include <chrono> 6#include <chrono>
7#include <cstdarg>
8#include <memory> 7#include <memory>
9#include <string> 8#include <string>
10#include <utility> 9#include <string_view>
11#include "common/file_util.h" 10#include "common/file_util.h"
12#include "common/logging/filter.h" 11#include "common/logging/filter.h"
13#include "common/logging/log.h" 12#include "common/logging/log.h"
@@ -106,9 +105,9 @@ private:
106 105
107void AddBackend(std::unique_ptr<Backend> backend); 106void AddBackend(std::unique_ptr<Backend> backend);
108 107
109void RemoveBackend(const std::string& backend_name); 108void RemoveBackend(std::string_view backend_name);
110 109
111Backend* GetBackend(const std::string& backend_name); 110Backend* GetBackend(std::string_view backend_name);
112 111
113/** 112/**
114 * Returns the name of the passed log class as a C-string. Subclasses are separated by periods 113 * Returns the name of the passed log class as a C-string. Subclasses are separated by periods
diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp
index 6ed087beb..2dd331152 100644
--- a/src/common/logging/filter.cpp
+++ b/src/common/logging/filter.cpp
@@ -8,39 +8,9 @@
8#include "common/string_util.h" 8#include "common/string_util.h"
9 9
10namespace Log { 10namespace Log {
11 11namespace {
12Filter::Filter(Level default_level) {
13 ResetAll(default_level);
14}
15
16void Filter::ResetAll(Level level) {
17 class_levels.fill(level);
18}
19
20void Filter::SetClassLevel(Class log_class, Level level) {
21 class_levels[static_cast<size_t>(log_class)] = level;
22}
23
24void Filter::ParseFilterString(const std::string& filter_str) {
25 auto clause_begin = filter_str.cbegin();
26 while (clause_begin != filter_str.cend()) {
27 auto clause_end = std::find(clause_begin, filter_str.cend(), ' ');
28
29 // If clause isn't empty
30 if (clause_end != clause_begin) {
31 ParseFilterRule(clause_begin, clause_end);
32 }
33
34 if (clause_end != filter_str.cend()) {
35 // Skip over the whitespace
36 ++clause_end;
37 }
38 clause_begin = clause_end;
39 }
40}
41
42template <typename It> 12template <typename It>
43static Level GetLevelByName(const It begin, const It end) { 13Level GetLevelByName(const It begin, const It end) {
44 for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) { 14 for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) {
45 const char* level_name = GetLevelName(static_cast<Level>(i)); 15 const char* level_name = GetLevelName(static_cast<Level>(i));
46 if (Common::ComparePartialString(begin, end, level_name)) { 16 if (Common::ComparePartialString(begin, end, level_name)) {
@@ -51,7 +21,7 @@ static Level GetLevelByName(const It begin, const It end) {
51} 21}
52 22
53template <typename It> 23template <typename It>
54static Class GetClassByName(const It begin, const It end) { 24Class GetClassByName(const It begin, const It end) {
55 for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) { 25 for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
56 const char* level_name = GetLogClassName(static_cast<Class>(i)); 26 const char* level_name = GetLogClassName(static_cast<Class>(i));
57 if (Common::ComparePartialString(begin, end, level_name)) { 27 if (Common::ComparePartialString(begin, end, level_name)) {
@@ -61,8 +31,8 @@ static Class GetClassByName(const It begin, const It end) {
61 return Class::Count; 31 return Class::Count;
62} 32}
63 33
64bool Filter::ParseFilterRule(const std::string::const_iterator begin, 34template <typename Iterator>
65 const std::string::const_iterator end) { 35bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
66 auto level_separator = std::find(begin, end, ':'); 36 auto level_separator = std::find(begin, end, ':');
67 if (level_separator == end) { 37 if (level_separator == end) {
68 LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}", 38 LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}",
@@ -77,7 +47,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
77 } 47 }
78 48
79 if (Common::ComparePartialString(begin, level_separator, "*")) { 49 if (Common::ComparePartialString(begin, level_separator, "*")) {
80 ResetAll(level); 50 instance.ResetAll(level);
81 return true; 51 return true;
82 } 52 }
83 53
@@ -87,9 +57,40 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
87 return false; 57 return false;
88 } 58 }
89 59
90 SetClassLevel(log_class, level); 60 instance.SetClassLevel(log_class, level);
91 return true; 61 return true;
92} 62}
63} // Anonymous namespace
64
65Filter::Filter(Level default_level) {
66 ResetAll(default_level);
67}
68
69void Filter::ResetAll(Level level) {
70 class_levels.fill(level);
71}
72
73void Filter::SetClassLevel(Class log_class, Level level) {
74 class_levels[static_cast<size_t>(log_class)] = level;
75}
76
77void Filter::ParseFilterString(std::string_view filter_view) {
78 auto clause_begin = filter_view.cbegin();
79 while (clause_begin != filter_view.cend()) {
80 auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
81
82 // If clause isn't empty
83 if (clause_end != clause_begin) {
84 ParseFilterRule(*this, clause_begin, clause_end);
85 }
86
87 if (clause_end != filter_view.cend()) {
88 // Skip over the whitespace
89 ++clause_end;
90 }
91 clause_begin = clause_end;
92 }
93}
93 94
94bool Filter::CheckMessage(Class log_class, Level level) const { 95bool Filter::CheckMessage(Class log_class, Level level) const {
95 return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]); 96 return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);
diff --git a/src/common/logging/filter.h b/src/common/logging/filter.h
index 2a4f7c845..d5ffc5a58 100644
--- a/src/common/logging/filter.h
+++ b/src/common/logging/filter.h
@@ -6,7 +6,7 @@
6 6
7#include <array> 7#include <array>
8#include <cstddef> 8#include <cstddef>
9#include <string> 9#include <string_view>
10#include "common/logging/log.h" 10#include "common/logging/log.h"
11 11
12namespace Log { 12namespace Log {
@@ -40,9 +40,7 @@ public:
40 * - `Service:Info` -- Sets the level of Service to Info. 40 * - `Service:Info` -- Sets the level of Service to Info.
41 * - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace. 41 * - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace.
42 */ 42 */
43 void ParseFilterString(const std::string& filter_str); 43 void ParseFilterString(std::string_view filter_view);
44 bool ParseFilterRule(const std::string::const_iterator start,
45 const std::string::const_iterator end);
46 44
47 /// Matches class/level combination against the filter, returning true if it passed. 45 /// Matches class/level combination against the filter, returning true if it passed.
48 bool CheckMessage(Class log_class, Level level) const; 46 bool CheckMessage(Class log_class, Level level) const;
diff --git a/src/common/param_package.cpp b/src/common/param_package.cpp
index e0df430ab..9526ca0c6 100644
--- a/src/common/param_package.cpp
+++ b/src/common/param_package.cpp
@@ -3,7 +3,9 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <array> 5#include <array>
6#include <utility>
6#include <vector> 7#include <vector>
8
7#include "common/logging/log.h" 9#include "common/logging/log.h"
8#include "common/param_package.h" 10#include "common/param_package.h"
9#include "common/string_util.h" 11#include "common/string_util.h"
@@ -12,10 +14,11 @@ namespace Common {
12 14
13constexpr char KEY_VALUE_SEPARATOR = ':'; 15constexpr char KEY_VALUE_SEPARATOR = ':';
14constexpr char PARAM_SEPARATOR = ','; 16constexpr char PARAM_SEPARATOR = ',';
17
15constexpr char ESCAPE_CHARACTER = '$'; 18constexpr char ESCAPE_CHARACTER = '$';
16const std::string KEY_VALUE_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '0'}; 19constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
17const std::string PARAM_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '1'}; 20constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
18const std::string ESCAPE_CHARACTER_ESCAPE{ESCAPE_CHARACTER, '2'}; 21constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
19 22
20ParamPackage::ParamPackage(const std::string& serialized) { 23ParamPackage::ParamPackage(const std::string& serialized) {
21 std::vector<std::string> pairs; 24 std::vector<std::string> pairs;
@@ -35,7 +38,7 @@ ParamPackage::ParamPackage(const std::string& serialized) {
35 part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER}); 38 part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER});
36 } 39 }
37 40
38 Set(key_value[0], key_value[1]); 41 Set(key_value[0], std::move(key_value[1]));
39 } 42 }
40} 43}
41 44
@@ -101,16 +104,16 @@ float ParamPackage::Get(const std::string& key, float default_value) const {
101 } 104 }
102} 105}
103 106
104void ParamPackage::Set(const std::string& key, const std::string& value) { 107void ParamPackage::Set(const std::string& key, std::string value) {
105 data[key] = value; 108 data.insert_or_assign(key, std::move(value));
106} 109}
107 110
108void ParamPackage::Set(const std::string& key, int value) { 111void ParamPackage::Set(const std::string& key, int value) {
109 data[key] = std::to_string(value); 112 data.insert_or_assign(key, std::to_string(value));
110} 113}
111 114
112void ParamPackage::Set(const std::string& key, float value) { 115void ParamPackage::Set(const std::string& key, float value) {
113 data[key] = std::to_string(value); 116 data.insert_or_assign(key, std::to_string(value));
114} 117}
115 118
116bool ParamPackage::Has(const std::string& key) const { 119bool ParamPackage::Has(const std::string& key) const {
diff --git a/src/common/param_package.h b/src/common/param_package.h
index c4c11b221..7842cd4ef 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -28,7 +28,7 @@ public:
28 std::string Get(const std::string& key, const std::string& default_value) const; 28 std::string Get(const std::string& key, const std::string& default_value) const;
29 int Get(const std::string& key, int default_value) const; 29 int Get(const std::string& key, int default_value) const;
30 float Get(const std::string& key, float default_value) const; 30 float Get(const std::string& key, float default_value) const;
31 void Set(const std::string& key, const std::string& value); 31 void Set(const std::string& key, std::string value);
32 void Set(const std::string& key, int value); 32 void Set(const std::string& key, int value);
33 void Set(const std::string& key, float value); 33 void Set(const std::string& key, float value);
34 bool Has(const std::string& key) const; 34 bool Has(const std::string& key) const;
diff --git a/src/common/synchronized_wrapper.h b/src/common/synchronized_wrapper.h
deleted file mode 100644
index 4a1984c46..000000000
--- a/src/common/synchronized_wrapper.h
+++ /dev/null
@@ -1,85 +0,0 @@
1// Copyright 2015 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <algorithm>
8#include <mutex>
9
10namespace Common {
11
12template <typename T>
13class SynchronizedWrapper;
14
15/**
16 * Synchronized reference, that keeps a SynchronizedWrapper's mutex locked during its lifetime. This
17 * greatly reduces the chance that someone will access the wrapped resource without locking the
18 * mutex.
19 */
20template <typename T>
21class SynchronizedRef {
22public:
23 SynchronizedRef(SynchronizedWrapper<T>& wrapper) : wrapper(&wrapper) {
24 wrapper.mutex.lock();
25 }
26
27 SynchronizedRef(SynchronizedRef&) = delete;
28 SynchronizedRef(SynchronizedRef&& o) : wrapper(o.wrapper) {
29 o.wrapper = nullptr;
30 }
31
32 ~SynchronizedRef() {
33 if (wrapper)
34 wrapper->mutex.unlock();
35 }
36
37 SynchronizedRef& operator=(SynchronizedRef&) = delete;
38 SynchronizedRef& operator=(SynchronizedRef&& o) {
39 std::swap(wrapper, o.wrapper);
40 return *this;
41 }
42
43 T& operator*() {
44 return wrapper->data;
45 }
46 const T& operator*() const {
47 return wrapper->data;
48 }
49
50 T* operator->() {
51 return &wrapper->data;
52 }
53 const T* operator->() const {
54 return &wrapper->data;
55 }
56
57private:
58 SynchronizedWrapper<T>* wrapper;
59};
60
61/**
62 * Wraps an object, only allowing access to it via a locking reference wrapper. Good to ensure no
63 * one forgets to lock a mutex before acessing an object. To access the wrapped object construct a
64 * SyncronizedRef on this wrapper. Inspired by Rust's Mutex type
65 * (http://doc.rust-lang.org/std/sync/struct.Mutex.html).
66 */
67template <typename T>
68class SynchronizedWrapper {
69public:
70 template <typename... Args>
71 SynchronizedWrapper(Args&&... args) : data(std::forward<Args>(args)...) {}
72
73 SynchronizedRef<T> Lock() {
74 return {*this};
75 }
76
77private:
78 template <typename U>
79 friend class SynchronizedRef;
80
81 std::mutex mutex;
82 T data;
83};
84
85} // namespace Common
diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h
index 8416e73b0..b0d7ced7f 100644
--- a/src/core/arm/arm_interface.h
+++ b/src/core/arm/arm_interface.h
@@ -20,9 +20,6 @@ public:
20 u64 cpsr; 20 u64 cpsr;
21 std::array<u128, 32> fpu_registers; 21 std::array<u128, 32> fpu_registers;
22 u64 fpscr; 22 u64 fpscr;
23
24 // TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT
25 VAddr tls_address;
26 }; 23 };
27 24
28 /// Runs the CPU until an event happens 25 /// Runs the CPU until an event happens
@@ -104,6 +101,10 @@ public:
104 101
105 virtual void SetTlsAddress(VAddr address) = 0; 102 virtual void SetTlsAddress(VAddr address) = 0;
106 103
104 virtual u64 GetTPIDR_EL0() const = 0;
105
106 virtual void SetTPIDR_EL0(u64 value) = 0;
107
107 /** 108 /**
108 * Saves the current CPU context 109 * Saves the current CPU context
109 * @param ctx Thread context to save 110 * @param ctx Thread context to save
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp
index 3572ee7b9..5d7efc9b6 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic.cpp
@@ -196,6 +196,14 @@ void ARM_Dynarmic::SetTlsAddress(u64 address) {
196 cb->tpidrro_el0 = address; 196 cb->tpidrro_el0 = address;
197} 197}
198 198
199u64 ARM_Dynarmic::GetTPIDR_EL0() const {
200 return cb->tpidr_el0;
201}
202
203void ARM_Dynarmic::SetTPIDR_EL0(u64 value) {
204 cb->tpidr_el0 = value;
205}
206
199void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { 207void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
200 ctx.cpu_registers = jit->GetRegisters(); 208 ctx.cpu_registers = jit->GetRegisters();
201 ctx.sp = jit->GetSP(); 209 ctx.sp = jit->GetSP();
@@ -203,7 +211,6 @@ void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
203 ctx.cpsr = jit->GetPstate(); 211 ctx.cpsr = jit->GetPstate();
204 ctx.fpu_registers = jit->GetVectors(); 212 ctx.fpu_registers = jit->GetVectors();
205 ctx.fpscr = jit->GetFpcr(); 213 ctx.fpscr = jit->GetFpcr();
206 ctx.tls_address = cb->tpidrro_el0;
207} 214}
208 215
209void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) { 216void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
@@ -213,7 +220,6 @@ void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
213 jit->SetPstate(static_cast<u32>(ctx.cpsr)); 220 jit->SetPstate(static_cast<u32>(ctx.cpsr));
214 jit->SetVectors(ctx.fpu_registers); 221 jit->SetVectors(ctx.fpu_registers);
215 jit->SetFpcr(static_cast<u32>(ctx.fpscr)); 222 jit->SetFpcr(static_cast<u32>(ctx.fpscr));
216 cb->tpidrro_el0 = ctx.tls_address;
217} 223}
218 224
219void ARM_Dynarmic::PrepareReschedule() { 225void ARM_Dynarmic::PrepareReschedule() {
diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h
index ed724c3f1..a9891ac4f 100644
--- a/src/core/arm/dynarmic/arm_dynarmic.h
+++ b/src/core/arm/dynarmic/arm_dynarmic.h
@@ -34,6 +34,8 @@ public:
34 void SetCPSR(u32 cpsr) override; 34 void SetCPSR(u32 cpsr) override;
35 VAddr GetTlsAddress() const override; 35 VAddr GetTlsAddress() const override;
36 void SetTlsAddress(VAddr address) override; 36 void SetTlsAddress(VAddr address) override;
37 void SetTPIDR_EL0(u64 value) override;
38 u64 GetTPIDR_EL0() const override;
37 39
38 void SaveContext(ThreadContext& ctx) override; 40 void SaveContext(ThreadContext& ctx) override;
39 void LoadContext(const ThreadContext& ctx) override; 41 void LoadContext(const ThreadContext& ctx) override;
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
index d2d699e9b..4c11f35a4 100644
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ b/src/core/arm/unicorn/arm_unicorn.cpp
@@ -169,6 +169,16 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
169 CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base)); 169 CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
170} 170}
171 171
172u64 ARM_Unicorn::GetTPIDR_EL0() const {
173 u64 value{};
174 CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDR_EL0, &value));
175 return value;
176}
177
178void ARM_Unicorn::SetTPIDR_EL0(u64 value) {
179 CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value));
180}
181
172void ARM_Unicorn::Run() { 182void ARM_Unicorn::Run() {
173 if (GDBStub::IsServerEnabled()) { 183 if (GDBStub::IsServerEnabled()) {
174 ExecuteInstructions(std::max(4000000, 0)); 184 ExecuteInstructions(std::max(4000000, 0));
@@ -220,8 +230,6 @@ void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {
220 230
221 CHECKED(uc_reg_read_batch(uc, uregs, tregs, 31)); 231 CHECKED(uc_reg_read_batch(uc, uregs, tregs, 31));
222 232
223 ctx.tls_address = GetTlsAddress();
224
225 for (int i = 0; i < 32; ++i) { 233 for (int i = 0; i < 32; ++i) {
226 uregs[i] = UC_ARM64_REG_Q0 + i; 234 uregs[i] = UC_ARM64_REG_Q0 + i;
227 tregs[i] = &ctx.fpu_registers[i]; 235 tregs[i] = &ctx.fpu_registers[i];
@@ -249,8 +257,6 @@ void ARM_Unicorn::LoadContext(const ARM_Interface::ThreadContext& ctx) {
249 257
250 CHECKED(uc_reg_write_batch(uc, uregs, tregs, 31)); 258 CHECKED(uc_reg_write_batch(uc, uregs, tregs, 31));
251 259
252 SetTlsAddress(ctx.tls_address);
253
254 for (auto i = 0; i < 32; ++i) { 260 for (auto i = 0; i < 32; ++i) {
255 uregs[i] = UC_ARM64_REG_Q0 + i; 261 uregs[i] = UC_ARM64_REG_Q0 + i;
256 tregs[i] = (void*)&ctx.fpu_registers[i]; 262 tregs[i] = (void*)&ctx.fpu_registers[i];
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
index a78a0acf2..af7943352 100644
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ b/src/core/arm/unicorn/arm_unicorn.h
@@ -28,6 +28,8 @@ public:
28 void SetCPSR(u32 cpsr) override; 28 void SetCPSR(u32 cpsr) override;
29 VAddr GetTlsAddress() const override; 29 VAddr GetTlsAddress() const override;
30 void SetTlsAddress(VAddr address) override; 30 void SetTlsAddress(VAddr address) override;
31 void SetTPIDR_EL0(u64 value) override;
32 u64 GetTPIDR_EL0() const override;
31 void SaveContext(ThreadContext& ctx) override; 33 void SaveContext(ThreadContext& ctx) override;
32 void LoadContext(const ThreadContext& ctx) override; 34 void LoadContext(const ThreadContext& ctx) override;
33 void PrepareReschedule() override; 35 void PrepareReschedule() override;
diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h
index a152dbd33..fea0593c7 100644
--- a/src/core/file_sys/errors.h
+++ b/src/core/file_sys/errors.h
@@ -20,13 +20,13 @@ enum {
20constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound); 20constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
21 21
22// TODO(bunnei): Replace these with correct errors for Switch OS 22// TODO(bunnei): Replace these with correct errors for Switch OS
23constexpr ResultCode ERROR_INVALID_PATH(ResultCode(-1)); 23constexpr ResultCode ERROR_INVALID_PATH(-1);
24constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ResultCode(-1)); 24constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
25constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ResultCode(-1)); 25constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
26constexpr ResultCode ERROR_FILE_NOT_FOUND(ResultCode(-1)); 26constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
27constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ResultCode(-1)); 27constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
28constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ResultCode(-1)); 28constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
29constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ResultCode(-1)); 29constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
30constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(ResultCode(-1)); 30constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
31 31
32} // namespace FileSys 32} // namespace FileSys
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index 7ccca1089..8d2bd9f6b 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -2,7 +2,12 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <cstddef>
7#include <cstring>
8#include <iterator>
5#include <utility> 9#include <utility>
10
6#include "common/file_util.h" 11#include "common/file_util.h"
7#include "common/logging/log.h" 12#include "common/logging/log.h"
8#include "core/file_sys/partition_filesystem.h" 13#include "core/file_sys/partition_filesystem.h"
@@ -99,14 +104,15 @@ void PartitionFilesystem::PrintDebugInfo() const {
99} 104}
100 105
101bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 106bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
102 auto iter = std::find(pfs_files.begin(), pfs_files.end(), file); 107 const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
103 if (iter == pfs_files.end()) 108 if (iter == pfs_files.end())
104 return false; 109 return false;
105 110
106 pfs_files[iter - pfs_files.begin()] = pfs_files.back(); 111 const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter);
112 pfs_files[offset] = pfs_files.back();
107 pfs_files.pop_back(); 113 pfs_files.pop_back();
108 114
109 pfs_dirs.emplace_back(dir); 115 pfs_dirs.emplace_back(std::move(dir));
110 116
111 return true; 117 return true;
112} 118}
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index 16c8ad90b..3f690f12a 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -42,7 +42,7 @@ bool VfsFile::WriteByte(u8 data, size_t offset) {
42 return Write(&data, 1, offset) == 1; 42 return Write(&data, 1, offset) == 1;
43} 43}
44 44
45size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) { 45size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
46 return Write(data.data(), data.size(), offset); 46 return Write(data.data(), data.size(), offset);
47} 47}
48 48
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index a5213e0cc..db3c77eac 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -59,8 +59,7 @@ struct VfsFile : NonCopyable {
59 // Returns the number of bytes (sizeof(T)*number_elements) read successfully. 59 // Returns the number of bytes (sizeof(T)*number_elements) read successfully.
60 template <typename T> 60 template <typename T>
61 size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const { 61 size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const {
62 static_assert(std::is_trivially_copyable<T>::value, 62 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
63 "Data type must be trivially copyable.");
64 63
65 return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); 64 return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
66 } 65 }
@@ -69,8 +68,7 @@ struct VfsFile : NonCopyable {
69 // Returns the number of bytes read successfully. 68 // Returns the number of bytes read successfully.
70 template <typename T> 69 template <typename T>
71 size_t ReadBytes(T* data, size_t size, size_t offset = 0) const { 70 size_t ReadBytes(T* data, size_t size, size_t offset = 0) const {
72 static_assert(std::is_trivially_copyable<T>::value, 71 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
73 "Data type must be trivially copyable.");
74 return Read(reinterpret_cast<u8*>(data), size, offset); 72 return Read(reinterpret_cast<u8*>(data), size, offset);
75 } 73 }
76 74
@@ -78,8 +76,7 @@ struct VfsFile : NonCopyable {
78 // Returns the number of bytes read successfully (sizeof(T)). 76 // Returns the number of bytes read successfully (sizeof(T)).
79 template <typename T> 77 template <typename T>
80 size_t ReadObject(T* data, size_t offset = 0) const { 78 size_t ReadObject(T* data, size_t offset = 0) const {
81 static_assert(std::is_trivially_copyable<T>::value, 79 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
82 "Data type must be trivially copyable.");
83 return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); 80 return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
84 } 81 }
85 82
@@ -88,33 +85,29 @@ struct VfsFile : NonCopyable {
88 virtual bool WriteByte(u8 data, size_t offset = 0); 85 virtual bool WriteByte(u8 data, size_t offset = 0);
89 // Writes a vector of bytes to offset in file and returns the number of bytes successfully 86 // Writes a vector of bytes to offset in file and returns the number of bytes successfully
90 // written. 87 // written.
91 virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0); 88 virtual size_t WriteBytes(const std::vector<u8>& data, size_t offset = 0);
92 89
93 // Writes an array of type T, size number_elements to offset in file. 90 // Writes an array of type T, size number_elements to offset in file.
94 // Returns the number of bytes (sizeof(T)*number_elements) written successfully. 91 // Returns the number of bytes (sizeof(T)*number_elements) written successfully.
95 template <typename T> 92 template <typename T>
96 size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) { 93 size_t WriteArray(const T* data, size_t number_elements, size_t offset = 0) {
97 static_assert(std::is_trivially_copyable<T>::value, 94 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
98 "Data type must be trivially copyable.");
99
100 return Write(data, number_elements * sizeof(T), offset); 95 return Write(data, number_elements * sizeof(T), offset);
101 } 96 }
102 97
103 // Writes size bytes starting at memory location data to offset in file. 98 // Writes size bytes starting at memory location data to offset in file.
104 // Returns the number of bytes written successfully. 99 // Returns the number of bytes written successfully.
105 template <typename T> 100 template <typename T>
106 size_t WriteBytes(T* data, size_t size, size_t offset = 0) { 101 size_t WriteBytes(const T* data, size_t size, size_t offset = 0) {
107 static_assert(std::is_trivially_copyable<T>::value, 102 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
108 "Data type must be trivially copyable."); 103 return Write(reinterpret_cast<const u8*>(data), size, offset);
109 return Write(reinterpret_cast<u8*>(data), size, offset);
110 } 104 }
111 105
112 // Writes one object of type T to offset in file. 106 // Writes one object of type T to offset in file.
113 // Returns the number of bytes written successfully (sizeof(T)). 107 // Returns the number of bytes written successfully (sizeof(T)).
114 template <typename T> 108 template <typename T>
115 size_t WriteObject(const T& data, size_t offset = 0) { 109 size_t WriteObject(const T& data, size_t offset = 0) {
116 static_assert(std::is_trivially_copyable<T>::value, 110 static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
117 "Data type must be trivially copyable.");
118 return Write(&data, sizeof(T), offset); 111 return Write(&data, sizeof(T), offset);
119 } 112 }
120 113
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index 31fdd9aa1..217e02235 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -2,6 +2,7 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
5#include <utility> 6#include <utility>
6 7
7#include "core/file_sys/vfs_offset.h" 8#include "core/file_sys/vfs_offset.h"
@@ -75,7 +76,7 @@ bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) {
75 return false; 76 return false;
76} 77}
77 78
78size_t OffsetVfsFile::WriteBytes(std::vector<u8> data, size_t r_offset) { 79size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) {
79 return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); 80 return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
80} 81}
81 82
@@ -88,7 +89,7 @@ size_t OffsetVfsFile::GetOffset() const {
88} 89}
89 90
90size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const { 91size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const {
91 return std::max<size_t>(std::min<size_t>(size - r_offset, r_size), 0); 92 return std::clamp(r_size, size_t{0}, size - r_offset);
92} 93}
93 94
94} // namespace FileSys 95} // namespace FileSys
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
index 2e16e47eb..ded4827f5 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs_offset.h
@@ -28,7 +28,7 @@ struct OffsetVfsFile : public VfsFile {
28 std::vector<u8> ReadBytes(size_t size, size_t offset) const override; 28 std::vector<u8> ReadBytes(size_t size, size_t offset) const override;
29 std::vector<u8> ReadAllBytes() const override; 29 std::vector<u8> ReadAllBytes() const override;
30 bool WriteByte(u8 data, size_t offset) override; 30 bool WriteByte(u8 data, size_t offset) override;
31 size_t WriteBytes(std::vector<u8> data, size_t offset) override; 31 size_t WriteBytes(const std::vector<u8>& data, size_t offset) override;
32 32
33 bool Rename(const std::string& name) override; 33 bool Rename(const std::string& name) override;
34 34
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 22c858e0d..f27fb1f2a 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -2,6 +2,11 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm>
6#include <cstddef>
7#include <iterator>
8#include <utility>
9
5#include "common/common_paths.h" 10#include "common/common_paths.h"
6#include "common/logging/log.h" 11#include "common/logging/log.h"
7#include "core/file_sys/vfs_real.h" 12#include "core/file_sys/vfs_real.h"
@@ -104,11 +109,11 @@ RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
104} 109}
105 110
106std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const { 111std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
107 return std::vector<std::shared_ptr<VfsFile>>(files); 112 return files;
108} 113}
109 114
110std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const { 115std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
111 return std::vector<std::shared_ptr<VfsDirectory>>(subdirectories); 116 return subdirectories;
112} 117}
113 118
114bool RealVfsDirectory::IsWritable() const { 119bool RealVfsDirectory::IsWritable() const {
@@ -163,14 +168,15 @@ bool RealVfsDirectory::Rename(const std::string& name) {
163} 168}
164 169
165bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 170bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
166 auto iter = std::find(files.begin(), files.end(), file); 171 const auto iter = std::find(files.begin(), files.end(), file);
167 if (iter == files.end()) 172 if (iter == files.end())
168 return false; 173 return false;
169 174
170 files[iter - files.begin()] = files.back(); 175 const std::ptrdiff_t offset = std::distance(files.begin(), iter);
176 files[offset] = files.back();
171 files.pop_back(); 177 files.pop_back();
172 178
173 subdirectories.emplace_back(dir); 179 subdirectories.emplace_back(std::move(dir));
174 180
175 return true; 181 return true;
176} 182}
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 24605a273..8b5b06f31 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -175,6 +175,25 @@ public:
175 void Push(const First& first_value, const Other&... other_values); 175 void Push(const First& first_value, const Other&... other_values);
176 176
177 /** 177 /**
178 * Helper function for pushing strongly-typed enumeration values.
179 *
180 * @tparam Enum The enumeration type to be pushed
181 *
182 * @param value The value to push.
183 *
184 * @note The underlying size of the enumeration type is the size of the
185 * data that gets pushed. e.g. "enum class SomeEnum : u16" will
186 * push a u16-sized amount of data.
187 */
188 template <typename Enum>
189 void PushEnum(Enum value) {
190 static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call.");
191 static_assert(!std::is_convertible_v<Enum, int>,
192 "enum type in PushEnum must be a strongly typed enum.");
193 Push(static_cast<std::underlying_type_t<Enum>>(value));
194 }
195
196 /**
178 * @brief Copies the content of the given trivially copyable class to the buffer as a normal 197 * @brief Copies the content of the given trivially copyable class to the buffer as a normal
179 * param 198 * param
180 * @note: The input class must be correctly packed/padded to fit hardware layout. 199 * @note: The input class must be correctly packed/padded to fit hardware layout.
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index f7e25cbf5..e307eec98 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -56,6 +56,8 @@ void Scheduler::SwitchContext(Thread* new_thread) {
56 if (previous_thread) { 56 if (previous_thread) {
57 previous_thread->last_running_ticks = CoreTiming::GetTicks(); 57 previous_thread->last_running_ticks = CoreTiming::GetTicks();
58 cpu_core->SaveContext(previous_thread->context); 58 cpu_core->SaveContext(previous_thread->context);
59 // Save the TPIDR_EL0 system register in case it was modified.
60 previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0();
59 61
60 if (previous_thread->status == ThreadStatus::Running) { 62 if (previous_thread->status == ThreadStatus::Running) {
61 // This is only the case when a reschedule is triggered without the current thread 63 // This is only the case when a reschedule is triggered without the current thread
@@ -87,6 +89,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
87 89
88 cpu_core->LoadContext(new_thread->context); 90 cpu_core->LoadContext(new_thread->context);
89 cpu_core->SetTlsAddress(new_thread->GetTLSAddress()); 91 cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
92 cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
90 cpu_core->ClearExclusiveState(); 93 cpu_core->ClearExclusiveState();
91 } else { 94 } else {
92 current_thread = nullptr; 95 current_thread = nullptr;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 53f2e861e..cd85c4b7c 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -312,6 +312,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
312 thread->status = ThreadStatus::Dormant; 312 thread->status = ThreadStatus::Dormant;
313 thread->entry_point = entry_point; 313 thread->entry_point = entry_point;
314 thread->stack_top = stack_top; 314 thread->stack_top = stack_top;
315 thread->tpidr_el0 = 0;
315 thread->nominal_priority = thread->current_priority = priority; 316 thread->nominal_priority = thread->current_priority = priority;
316 thread->last_running_ticks = CoreTiming::GetTicks(); 317 thread->last_running_ticks = CoreTiming::GetTicks();
317 thread->processor_id = processor_id; 318 thread->processor_id = processor_id;
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 47881ec20..6218960d2 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -183,6 +183,14 @@ public:
183 } 183 }
184 184
185 /* 185 /*
186 * Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
187 * @returns The value of the TPIDR_EL0 register.
188 */
189 u64 GetTPIDR_EL0() const {
190 return tpidr_el0;
191 }
192
193 /*
186 * Returns the address of the current thread's command buffer, located in the TLS. 194 * Returns the address of the current thread's command buffer, located in the TLS.
187 * @returns VAddr of the thread's command buffer. 195 * @returns VAddr of the thread's command buffer.
188 */ 196 */
@@ -213,6 +221,7 @@ public:
213 s32 processor_id; 221 s32 processor_id;
214 222
215 VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread 223 VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
224 u64 tpidr_el0; ///< TPIDR_EL0 read/write system register.
216 225
217 SharedPtr<Process> owner_process; ///< Process that owns this thread 226 SharedPtr<Process> owner_process; ///< Process that owns this thread
218 227
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index 751d73f8d..ce943d829 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -20,6 +20,21 @@ public:
20 } 20 }
21 21
22private: 22private:
23 enum class PerformanceConfiguration : u32 {
24 Config1 = 0x00010000,
25 Config2 = 0x00010001,
26 Config3 = 0x00010002,
27 Config4 = 0x00020000,
28 Config5 = 0x00020001,
29 Config6 = 0x00020002,
30 Config7 = 0x00020003,
31 Config8 = 0x00020004,
32 Config9 = 0x00020005,
33 Config10 = 0x00020006,
34 Config11 = 0x92220007,
35 Config12 = 0x92220008,
36 };
37
23 void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) { 38 void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) {
24 IPC::RequestParser rp{ctx}; 39 IPC::RequestParser rp{ctx};
25 40
@@ -40,7 +55,7 @@ private:
40 55
41 IPC::ResponseBuilder rb{ctx, 3}; 56 IPC::ResponseBuilder rb{ctx, 3};
42 rb.Push(RESULT_SUCCESS); 57 rb.Push(RESULT_SUCCESS);
43 rb.Push<u32>(0); // Performance configuration 58 rb.Push<u32>(static_cast<u32>(PerformanceConfiguration::Config1));
44 59
45 LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode)); 60 LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode));
46 } 61 }
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index dffcdfbaf..671e0b8d0 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -272,9 +272,9 @@ void RegisterFileSystems() {
272 sdmc_factory = nullptr; 272 sdmc_factory = nullptr;
273 273
274 auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>( 274 auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
275 FileUtil::GetUserPath(D_NAND_IDX), FileSys::Mode::Write); 275 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::Write);
276 auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>( 276 auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
277 FileUtil::GetUserPath(D_SDMC_IDX), FileSys::Mode::Write); 277 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::Write);
278 278
279 auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); 279 auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
280 save_data_factory = std::move(savedata); 280 save_data_factory = std::move(savedata);
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 691b1d106..bad27894a 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -42,7 +42,7 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
42 RegisterHandlers(functions); 42 RegisterHandlers(functions);
43 43
44 // Attempt to load shared font data from disk 44 // Attempt to load shared font data from disk
45 const std::string filepath{FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT}; 45 const std::string filepath{FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + SHARED_FONT};
46 FileUtil::CreateFullPath(filepath); // Create path if not already created 46 FileUtil::CreateFullPath(filepath); // Create path if not already created
47 FileUtil::IOFile file(filepath, "rb"); 47 FileUtil::IOFile file(filepath, "rb");
48 48
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 8de870596..126782573 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -42,6 +42,9 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
42 if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) { 42 if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) {
43 return SubmitGPFIFO(input, output); 43 return SubmitGPFIFO(input, output);
44 } 44 }
45 if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
46 return KickoffPB(input, output);
47 }
45 } 48 }
46 49
47 UNIMPLEMENTED_MSG("Unimplemented ioctl"); 50 UNIMPLEMENTED_MSG("Unimplemented ioctl");
@@ -127,14 +130,37 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
127 IoctlSubmitGpfifo params{}; 130 IoctlSubmitGpfifo params{};
128 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo)); 131 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
129 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", 132 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
130 params.gpfifo, params.num_entries, params.flags); 133 params.address, params.num_entries, params.flags);
131 134
132 auto entries = std::vector<IoctlGpfifoEntry>(); 135 auto entries = std::vector<IoctlGpfifoEntry>();
133 entries.resize(params.num_entries); 136 entries.resize(params.num_entries);
134 std::memcpy(&entries[0], &input.data()[sizeof(IoctlSubmitGpfifo)], 137 std::memcpy(&entries[0], &input.data()[sizeof(IoctlSubmitGpfifo)],
135 params.num_entries * sizeof(IoctlGpfifoEntry)); 138 params.num_entries * sizeof(IoctlGpfifoEntry));
136 for (auto entry : entries) { 139 for (auto entry : entries) {
137 VAddr va_addr = entry.Address(); 140 Tegra::GPUVAddr va_addr = entry.Address();
141 Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz);
142 }
143 params.fence_out.id = 0;
144 params.fence_out.value = 0;
145 std::memcpy(output.data(), &params, output.size());
146 return 0;
147}
148
149u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) {
150 if (input.size() < sizeof(IoctlSubmitGpfifo)) {
151 UNIMPLEMENTED();
152 }
153 IoctlSubmitGpfifo params{};
154 std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
155 LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
156 params.address, params.num_entries, params.flags);
157
158 std::vector<IoctlGpfifoEntry> entries(params.num_entries);
159 Memory::ReadBlock(params.address, entries.data(),
160 params.num_entries * sizeof(IoctlGpfifoEntry));
161
162 for (auto entry : entries) {
163 Tegra::GPUVAddr va_addr = entry.Address();
138 Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz); 164 Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz);
139 } 165 }
140 params.fence_out.id = 0; 166 params.fence_out.id = 0;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
index c9f6b9b6a..aa8df2e6e 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -15,6 +15,7 @@ namespace Service::Nvidia::Devices {
15class nvmap; 15class nvmap;
16constexpr u32 NVGPU_IOCTL_MAGIC('H'); 16constexpr u32 NVGPU_IOCTL_MAGIC('H');
17constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8); 17constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
18constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
18 19
19class nvhost_gpu final : public nvdevice { 20class nvhost_gpu final : public nvdevice {
20public: 21public:
@@ -158,14 +159,14 @@ private:
158 BitField<31, 1, u32_le> unk2; 159 BitField<31, 1, u32_le> unk2;
159 }; 160 };
160 161
161 VAddr Address() const { 162 Tegra::GPUVAddr Address() const {
162 return (static_cast<VAddr>(gpu_va_hi) << 32) | entry0; 163 return (static_cast<Tegra::GPUVAddr>(gpu_va_hi) << 32) | entry0;
163 } 164 }
164 }; 165 };
165 static_assert(sizeof(IoctlGpfifoEntry) == 8, "IoctlGpfifoEntry is incorrect size"); 166 static_assert(sizeof(IoctlGpfifoEntry) == 8, "IoctlGpfifoEntry is incorrect size");
166 167
167 struct IoctlSubmitGpfifo { 168 struct IoctlSubmitGpfifo {
168 u64_le gpfifo; // (ignored) pointer to gpfifo fence structs 169 u64_le address; // pointer to gpfifo entry structs
169 u32_le num_entries; // number of fence objects being submitted 170 u32_le num_entries; // number of fence objects being submitted
170 u32_le flags; 171 u32_le flags;
171 IoctlFence fence_out; // returned new fence object for others to wait on 172 IoctlFence fence_out; // returned new fence object for others to wait on
@@ -193,6 +194,7 @@ private:
193 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); 194 u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
194 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); 195 u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
195 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); 196 u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
197 u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output);
196 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output); 198 u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
197 u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); 199 u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
198 200
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index b10efd5c9..1b497b814 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -101,7 +101,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
101 {8, &NVDRV::SetClientPID, "SetClientPID"}, 101 {8, &NVDRV::SetClientPID, "SetClientPID"},
102 {9, nullptr, "DumpGraphicsMemoryInfo"}, 102 {9, nullptr, "DumpGraphicsMemoryInfo"},
103 {10, nullptr, "InitializeDevtools"}, 103 {10, nullptr, "InitializeDevtools"},
104 {11, nullptr, "Ioctl2"}, 104 {11, &NVDRV::Ioctl, "Ioctl2"},
105 {12, nullptr, "Ioctl3"}, 105 {12, nullptr, "Ioctl3"},
106 {13, &NVDRV::FinishInitialize, "FinishInitialize"}, 106 {13, &NVDRV::FinishInitialize, "FinishInitialize"},
107 }; 107 };
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 19b8667ba..394963a69 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -83,16 +83,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
83 VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; 83 VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
84 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 84 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
85 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { 85 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
86 const VAddr load_addr = next_load_addr;
87 const FileSys::VirtualFile module_file = dir->GetFile(module); 86 const FileSys::VirtualFile module_file = dir->GetFile(module);
88 if (module_file != nullptr) 87 if (module_file != nullptr) {
88 const VAddr load_addr = next_load_addr;
89 next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr); 89 next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr);
90 if (next_load_addr) {
91 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); 90 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
92 // Register module with GDBStub 91 // Register module with GDBStub
93 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false); 92 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
94 } else {
95 next_load_addr = load_addr;
96 } 93 }
97 } 94 }
98 95
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index b9a603df3..69aa7a7be 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -37,7 +37,8 @@ static u64 GenerateTelemetryId() {
37 37
38u64 GetTelemetryId() { 38u64 GetTelemetryId() {
39 u64 telemetry_id{}; 39 u64 telemetry_id{};
40 static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"}; 40 static const std::string& filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
41 "telemetry_id"};
41 42
42 if (FileUtil::Exists(filename)) { 43 if (FileUtil::Exists(filename)) {
43 FileUtil::IOFile file(filename, "rb"); 44 FileUtil::IOFile file(filename, "rb");
@@ -61,7 +62,8 @@ u64 GetTelemetryId() {
61 62
62u64 RegenerateTelemetryId() { 63u64 RegenerateTelemetryId() {
63 const u64 new_telemetry_id{GenerateTelemetryId()}; 64 const u64 new_telemetry_id{GenerateTelemetryId()};
64 static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"}; 65 static const std::string& filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
66 "telemetry_id"};
65 67
66 FileUtil::IOFile file(filename, "wb"); 68 FileUtil::IOFile file(filename, "wb");
67 if (!file.IsOpen()) { 69 if (!file.IsOpen()) {
diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp
index 7f9f27e19..539746246 100644
--- a/src/tests/core/arm/arm_test_common.cpp
+++ b/src/tests/core/arm/arm_test_common.cpp
@@ -10,8 +10,6 @@
10 10
11namespace ArmTests { 11namespace ArmTests {
12 12
13static Memory::PageTable* page_table = nullptr;
14
15TestEnvironment::TestEnvironment(bool mutable_memory_) 13TestEnvironment::TestEnvironment(bool mutable_memory_)
16 : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) { 14 : mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
17 15
@@ -67,10 +65,13 @@ boost::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) {
67} 65}
68 66
69boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) { 67boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
70 auto iter = data.find(addr); 68 const auto iter = data.find(addr);
69
71 if (iter == data.end()) { 70 if (iter == data.end()) {
72 return addr; // Some arbitrary data 71 // Some arbitrary data
72 return static_cast<u8>(addr);
73 } 73 }
74
74 return iter->second; 75 return iter->second;
75} 76}
76 77
diff --git a/src/tests/core/arm/arm_test_common.h b/src/tests/core/arm/arm_test_common.h
index b66922d61..7fdbda494 100644
--- a/src/tests/core/arm/arm_test_common.h
+++ b/src/tests/core/arm/arm_test_common.h
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once
6
5#include <tuple> 7#include <tuple>
6#include <unordered_map> 8#include <unordered_map>
7#include <vector> 9#include <vector>
@@ -9,6 +11,10 @@
9#include "common/common_types.h" 11#include "common/common_types.h"
10#include "core/memory_hook.h" 12#include "core/memory_hook.h"
11 13
14namespace Memory {
15struct PageTable;
16}
17
12namespace ArmTests { 18namespace ArmTests {
13 19
14struct WriteRecord { 20struct WriteRecord {
@@ -79,6 +85,7 @@ private:
79 bool mutable_memory; 85 bool mutable_memory;
80 std::shared_ptr<TestMemory> test_memory; 86 std::shared_ptr<TestMemory> test_memory;
81 std::vector<WriteRecord> write_records; 87 std::vector<WriteRecord> write_records;
88 Memory::PageTable* page_table = nullptr;
82}; 89};
83 90
84} // namespace ArmTests 91} // namespace ArmTests
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index e36483145..a003bc9e3 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -20,7 +20,11 @@ GPU::GPU() {
20 20
21GPU::~GPU() = default; 21GPU::~GPU() = default;
22 22
23const Tegra::Engines::Maxwell3D& GPU::Get3DEngine() const { 23const Engines::Maxwell3D& GPU::Maxwell3D() const {
24 return *maxwell_3d;
25}
26
27Engines::Maxwell3D& GPU::Maxwell3D() {
24 return *maxwell_3d; 28 return *maxwell_3d;
25} 29}
26 30
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index 60930e997..a32148ecd 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -93,15 +93,14 @@ public:
93 /// Processes a command list stored at the specified address in GPU memory. 93 /// Processes a command list stored at the specified address in GPU memory.
94 void ProcessCommandList(GPUVAddr address, u32 size); 94 void ProcessCommandList(GPUVAddr address, u32 size);
95 95
96 /// Returns a const reference to the Maxwell3D GPU engine.
97 const Engines::Maxwell3D& Maxwell3D() const;
98
96 /// Returns a reference to the Maxwell3D GPU engine. 99 /// Returns a reference to the Maxwell3D GPU engine.
97 const Engines::Maxwell3D& Get3DEngine() const; 100 Engines::Maxwell3D& Maxwell3D();
98 101
99 std::unique_ptr<MemoryManager> memory_manager; 102 std::unique_ptr<MemoryManager> memory_manager;
100 103
101 Engines::Maxwell3D& Maxwell3D() {
102 return *maxwell_3d;
103 }
104
105private: 104private:
106 /// Writes a single register in the engine bound to the specified subchannel 105 /// Writes a single register in the engine bound to the specified subchannel
107 void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params); 106 void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index f75999557..65a2fd5e8 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -634,8 +634,8 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
634u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint program, 634u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint program,
635 u32 current_bindpoint, 635 u32 current_bindpoint,
636 const std::vector<GLShader::ConstBufferEntry>& entries) { 636 const std::vector<GLShader::ConstBufferEntry>& entries) {
637 auto& gpu = Core::System::GetInstance().GPU(); 637 const auto& gpu = Core::System::GetInstance().GPU();
638 auto& maxwell3d = gpu.Get3DEngine(); 638 const auto& maxwell3d = gpu.Maxwell3D();
639 639
640 // Reset all buffer draw state for this stage. 640 // Reset all buffer draw state for this stage.
641 for (auto& buffer : state.draw.const_buffers[static_cast<size_t>(stage)]) { 641 for (auto& buffer : state.draw.const_buffers[static_cast<size_t>(stage)]) {
@@ -644,7 +644,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
644 } 644 }
645 645
646 // Upload only the enabled buffers from the 16 constbuffers of each shader stage 646 // Upload only the enabled buffers from the 16 constbuffers of each shader stage
647 auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)]; 647 const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)];
648 648
649 for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { 649 for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
650 const auto& used_buffer = entries[bindpoint]; 650 const auto& used_buffer = entries[bindpoint];
@@ -700,8 +700,8 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
700 700
701u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit, 701u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit,
702 const std::vector<GLShader::SamplerEntry>& entries) { 702 const std::vector<GLShader::SamplerEntry>& entries) {
703 auto& gpu = Core::System::GetInstance().GPU(); 703 const auto& gpu = Core::System::GetInstance().GPU();
704 auto& maxwell3d = gpu.Get3DEngine(); 704 const auto& maxwell3d = gpu.Maxwell3D();
705 705
706 ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units), 706 ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units),
707 "Exceeded the number of active textures."); 707 "Exceeded the number of active textures.");
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp
index 1aa437f76..e81fcbbc4 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp
@@ -10,8 +10,9 @@
10namespace GLShader { 10namespace GLShader {
11 11
12namespace Impl { 12namespace Impl {
13void SetShaderUniformBlockBinding(GLuint shader, const char* name, 13static void SetShaderUniformBlockBinding(GLuint shader, const char* name,
14 Maxwell3D::Regs::ShaderStage binding, size_t expected_size) { 14 Maxwell3D::Regs::ShaderStage binding,
15 size_t expected_size) {
15 GLuint ub_index = glGetUniformBlockIndex(shader, name); 16 GLuint ub_index = glGetUniformBlockIndex(shader, name);
16 if (ub_index != GL_INVALID_INDEX) { 17 if (ub_index != GL_INVALID_INDEX) {
17 GLint ub_size = 0; 18 GLint ub_size = 0;
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h
index 4295c20a6..e29d551e1 100644
--- a/src/video_core/renderer_opengl/gl_shader_manager.h
+++ b/src/video_core/renderer_opengl/gl_shader_manager.h
@@ -21,7 +21,6 @@ using Tegra::Engines::Maxwell3D;
21 21
22namespace Impl { 22namespace Impl {
23void SetShaderUniformBlockBindings(GLuint shader); 23void SetShaderUniformBlockBindings(GLuint shader);
24void SetShaderSamplerBindings(GLuint shader);
25} // namespace Impl 24} // namespace Impl
26 25
27/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned 26/// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 62754a1a9..98969fe10 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -10,7 +10,7 @@
10 10
11Config::Config() { 11Config::Config() {
12 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 12 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
13 qt_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "qt-config.ini"; 13 qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
14 FileUtil::CreateFullPath(qt_config_loc); 14 FileUtil::CreateFullPath(qt_config_loc);
15 qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat); 15 qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
16 16
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 241db4ae3..5e66239ff 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -19,7 +19,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
19 ui->setupUi(this); 19 ui->setupUi(this);
20 this->setConfiguration(); 20 this->setConfiguration();
21 connect(ui->open_log_button, &QPushButton::pressed, []() { 21 connect(ui->open_log_button, &QPushButton::pressed, []() {
22 QString path = QString::fromStdString(FileUtil::GetUserPath(D_LOGS_IDX)); 22 QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
23 QDesktopServices::openUrl(QUrl::fromLocalFile(path)); 23 QDesktopServices::openUrl(QUrl::fromLocalFile(path));
24 }); 24 });
25} 25}
diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp
index 1fbca8ad0..c41ff693b 100644
--- a/src/yuzu/debugger/graphics/graphics_surface.cpp
+++ b/src/yuzu/debugger/graphics/graphics_surface.cpp
@@ -336,9 +336,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
336 // TODO: Store a reference to the registers in the debug context instead of accessing them 336 // TODO: Store a reference to the registers in the debug context instead of accessing them
337 // directly... 337 // directly...
338 338
339 auto& registers = gpu.Get3DEngine().regs; 339 const auto& registers = gpu.Maxwell3D().regs;
340 auto& rt = registers.rt[static_cast<size_t>(surface_source) - 340 const auto& rt = registers.rt[static_cast<size_t>(surface_source) -
341 static_cast<size_t>(Source::RenderTarget0)]; 341 static_cast<size_t>(Source::RenderTarget0)];
342 342
343 surface_address = rt.Address(); 343 surface_address = rt.Address();
344 surface_width = rt.width; 344 surface_width = rt.width;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 16812e077..16a95bb19 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -909,6 +909,16 @@ void GMainWindow::UpdateUITheme() {
909#undef main 909#undef main
910#endif 910#endif
911 911
912static void InitializeLogging() {
913 Log::Filter log_filter;
914 log_filter.ParseFilterString(Settings::values.log_filter);
915 Log::SetGlobalFilter(log_filter);
916
917 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
918 FileUtil::CreateFullPath(log_dir);
919 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
920}
921
912int main(int argc, char* argv[]) { 922int main(int argc, char* argv[]) {
913 MicroProfileOnThreadCreate("Frontend"); 923 MicroProfileOnThreadCreate("Frontend");
914 SCOPE_EXIT({ MicroProfileShutdown(); }); 924 SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -927,13 +937,7 @@ int main(int argc, char* argv[]) {
927 937
928 GMainWindow main_window; 938 GMainWindow main_window;
929 // After settings have been loaded by GMainWindow, apply the filter 939 // After settings have been loaded by GMainWindow, apply the filter
930 Log::Filter log_filter; 940 InitializeLogging();
931 log_filter.ParseFilterString(Settings::values.log_filter);
932 Log::SetGlobalFilter(log_filter);
933 FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
934 Log::AddBackend(
935 std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
936
937 main_window.show(); 941 main_window.show();
938 return app.exec(); 942 return app.exec();
939} 943}
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 723e8b4cc..cea1a5e62 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -15,7 +15,7 @@
15 15
16Config::Config() { 16Config::Config() {
17 // TODO: Don't hardcode the path; let the frontend decide where to put the config files. 17 // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
18 sdl2_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "sdl2-config.ini"; 18 sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-config.ini";
19 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc); 19 sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
20 20
21 Reload(); 21 Reload();
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 24db1065a..b5392c499 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -56,6 +56,18 @@ static void PrintVersion() {
56 std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl; 56 std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
57} 57}
58 58
59static void InitializeLogging() {
60 Log::Filter log_filter(Log::Level::Debug);
61 log_filter.ParseFilterString(Settings::values.log_filter);
62 Log::SetGlobalFilter(log_filter);
63
64 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
65
66 const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
67 FileUtil::CreateFullPath(log_dir);
68 Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
69}
70
59/// Application entry point 71/// Application entry point
60int main(int argc, char** argv) { 72int main(int argc, char** argv) {
61 Config config; 73 Config config;
@@ -124,14 +136,7 @@ int main(int argc, char** argv) {
124 LocalFree(argv_w); 136 LocalFree(argv_w);
125#endif 137#endif
126 138
127 Log::Filter log_filter(Log::Level::Debug); 139 InitializeLogging();
128 log_filter.ParseFilterString(Settings::values.log_filter);
129 Log::SetGlobalFilter(log_filter);
130
131 Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
132 FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
133 Log::AddBackend(
134 std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
135 140
136 MicroProfileOnThreadCreate("EmuThread"); 141 MicroProfileOnThreadCreate("EmuThread");
137 SCOPE_EXIT({ MicroProfileShutdown(); }); 142 SCOPE_EXIT({ MicroProfileShutdown(); });