summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/web_result.h1
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/crypto/partition_data_manager.cpp4
-rw-r--r--src/core/file_sys/bis_factory.cpp12
-rw-r--r--src/core/file_sys/bis_factory.h8
-rw-r--r--src/core/file_sys/control_metadata.cpp13
-rw-r--r--src/core/file_sys/control_metadata.h1
-rw-r--r--src/core/file_sys/patch_manager.cpp2
-rw-r--r--src/core/file_sys/registered_cache.cpp14
-rw-r--r--src/core/file_sys/registered_cache.h12
-rw-r--r--src/core/file_sys/sdmc_factory.cpp8
-rw-r--r--src/core/file_sys/sdmc_factory.h4
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp18
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp13
-rw-r--r--src/core/hle/service/filesystem/filesystem.h8
-rw-r--r--src/core/hle/service/ns/pl_u.cpp2
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp22
-rw-r--r--src/core/loader/loader.cpp3
-rw-r--r--src/core/loader/loader.h1
-rw-r--r--src/core/loader/nro.cpp10
-rw-r--r--src/core/loader/nro.h2
-rw-r--r--src/core/loader/nso.cpp23
-rw-r--r--src/core/loader/nso.h6
-rw-r--r--src/video_core/engines/shader_bytecode.h2
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp8
-rw-r--r--src/web_service/telemetry_json.cpp90
-rw-r--r--src/web_service/telemetry_json.h24
-rw-r--r--src/web_service/verify_login.cpp1
-rw-r--r--src/web_service/web_backend.cpp235
-rw-r--r--src/web_service/web_backend.h58
-rw-r--r--src/yuzu/game_list_worker.cpp19
-rw-r--r--src/yuzu/game_list_worker.h2
-rw-r--r--src/yuzu/main.cpp10
34 files changed, 347 insertions, 296 deletions
diff --git a/src/common/web_result.h b/src/common/web_result.h
index 969926674..8bfa2141d 100644
--- a/src/common/web_result.h
+++ b/src/common/web_result.h
@@ -5,6 +5,7 @@
5#pragma once 5#pragma once
6 6
7#include <string> 7#include <string>
8#include "common/common_types.h"
8 9
9namespace Common { 10namespace Common {
10struct WebResult { 11struct WebResult {
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 4fddaafd1..78986deb5 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -400,8 +400,8 @@ create_target_directory_groups(core)
400target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) 400target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
401target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives) 401target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
402if (ENABLE_WEB_SERVICE) 402if (ENABLE_WEB_SERVICE)
403 add_definitions(-DENABLE_WEB_SERVICE) 403 target_compile_definitions(core PRIVATE -DENABLE_WEB_SERVICE)
404 target_link_libraries(core PUBLIC json-headers web_service) 404 target_link_libraries(core PRIVATE web_service)
405endif() 405endif()
406 406
407if (ARCHITECTURE_x86_64) 407if (ARCHITECTURE_x86_64)
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index ed5e2b145..25cee1f3a 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -303,8 +303,8 @@ FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir,
303 303
304PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir) 304PartitionDataManager::PartitionDataManager(const FileSys::VirtualDir& sysdata_dir)
305 : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")), 305 : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")),
306 fuses(FindFileInDirWithNames(sysdata_dir, "fuse")), 306 fuses(FindFileInDirWithNames(sysdata_dir, "fuses")),
307 kfuses(FindFileInDirWithNames(sysdata_dir, "kfuse")), 307 kfuses(FindFileInDirWithNames(sysdata_dir, "kfuses")),
308 package2({ 308 package2({
309 FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"), 309 FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"),
310 FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"), 310 FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"),
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 6102ef476..76a2b7e86 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -10,19 +10,19 @@ namespace FileSys {
10 10
11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_) 11BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)), 12 : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
13 sysnand_cache(std::make_shared<RegisteredCache>( 13 sysnand_cache(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), 14 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
15 usrnand_cache(std::make_shared<RegisteredCache>( 15 usrnand_cache(std::make_unique<RegisteredCache>(
16 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} 16 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
17 17
18BISFactory::~BISFactory() = default; 18BISFactory::~BISFactory() = default;
19 19
20std::shared_ptr<RegisteredCache> BISFactory::GetSystemNANDContents() const { 20RegisteredCache* BISFactory::GetSystemNANDContents() const {
21 return sysnand_cache; 21 return sysnand_cache.get();
22} 22}
23 23
24std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const { 24RegisteredCache* BISFactory::GetUserNANDContents() const {
25 return usrnand_cache; 25 return usrnand_cache.get();
26} 26}
27 27
28VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { 28VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index c352e0925..364d309bd 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -20,8 +20,8 @@ public:
20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root); 20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
21 ~BISFactory(); 21 ~BISFactory();
22 22
23 std::shared_ptr<RegisteredCache> GetSystemNANDContents() const; 23 RegisteredCache* GetSystemNANDContents() const;
24 std::shared_ptr<RegisteredCache> GetUserNANDContents() const; 24 RegisteredCache* GetUserNANDContents() const;
25 25
26 VirtualDir GetModificationLoadRoot(u64 title_id) const; 26 VirtualDir GetModificationLoadRoot(u64 title_id) const;
27 27
@@ -29,8 +29,8 @@ private:
29 VirtualDir nand_root; 29 VirtualDir nand_root;
30 VirtualDir load_root; 30 VirtualDir load_root;
31 31
32 std::shared_ptr<RegisteredCache> sysnand_cache; 32 std::unique_ptr<RegisteredCache> sysnand_cache;
33 std::shared_ptr<RegisteredCache> usrnand_cache; 33 std::unique_ptr<RegisteredCache> usrnand_cache;
34}; 34};
35 35
36} // namespace FileSys 36} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 5b1177a03..a012c2be9 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -17,11 +17,13 @@ const std::array<const char*, 15> LANGUAGE_NAMES = {
17}; 17};
18 18
19std::string LanguageEntry::GetApplicationName() const { 19std::string LanguageEntry::GetApplicationName() const {
20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200); 20 return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(),
21 application_name.size());
21} 22}
22 23
23std::string LanguageEntry::GetDeveloperName() const { 24std::string LanguageEntry::GetDeveloperName() const {
24 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(), 0x100); 25 return Common::StringFromFixedZeroTerminatedBuffer(developer_name.data(),
26 developer_name.size());
25} 27}
26 28
27NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) { 29NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
@@ -56,7 +58,12 @@ u64 NACP::GetTitleId() const {
56 return raw->title_id; 58 return raw->title_id;
57} 59}
58 60
61u64 NACP::GetDLCBaseTitleId() const {
62 return raw->dlc_base_title_id;
63}
64
59std::string NACP::GetVersionString() const { 65std::string NACP::GetVersionString() const {
60 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), 0x10); 66 return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(),
67 raw->version_string.size());
61} 68}
62} // namespace FileSys 69} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index 43d6f0719..141f7e056 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -79,6 +79,7 @@ public:
79 std::string GetApplicationName(Language language = Language::Default) const; 79 std::string GetApplicationName(Language language = Language::Default) const;
80 std::string GetDeveloperName(Language language = Language::Default) const; 80 std::string GetDeveloperName(Language language = Language::Default) const;
81 u64 GetTitleId() const; 81 u64 GetTitleId() const;
82 u64 GetDLCBaseTitleId() const;
82 std::string GetVersionString() const; 83 std::string GetVersionString() const;
83 84
84private: 85private:
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 019caebe9..c15ac8e19 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -346,7 +346,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
346} 346}
347 347
348std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const { 348std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
349 const auto& installed{Service::FileSystem::GetUnionContents()}; 349 const auto installed{Service::FileSystem::GetUnionContents()};
350 350
351 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control); 351 const auto base_control_nca = installed->GetEntry(title_id, ContentRecordType::Control);
352 if (base_control_nca == nullptr) 352 if (base_control_nca == nullptr)
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index e9b040689..1febb398e 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -308,14 +308,14 @@ VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
308 return GetEntryRaw(entry.title_id, entry.type); 308 return GetEntryRaw(entry.title_id, entry.type);
309} 309}
310 310
311std::shared_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { 311std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
312 const auto raw = GetEntryRaw(title_id, type); 312 const auto raw = GetEntryRaw(title_id, type);
313 if (raw == nullptr) 313 if (raw == nullptr)
314 return nullptr; 314 return nullptr;
315 return std::make_shared<NCA>(raw); 315 return std::make_unique<NCA>(raw);
316} 316}
317 317
318std::shared_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const { 318std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
319 return GetEntry(entry.title_id, entry.type); 319 return GetEntry(entry.title_id, entry.type);
320} 320}
321 321
@@ -516,7 +516,7 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
516 }) != yuzu_meta.end(); 516 }) != yuzu_meta.end();
517} 517}
518 518
519RegisteredCacheUnion::RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches) 519RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches)
520 : caches(std::move(caches)) {} 520 : caches(std::move(caches)) {}
521 521
522void RegisteredCacheUnion::Refresh() { 522void RegisteredCacheUnion::Refresh() {
@@ -572,14 +572,14 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const
572 return GetEntryRaw(entry.title_id, entry.type); 572 return GetEntryRaw(entry.title_id, entry.type);
573} 573}
574 574
575std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { 575std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const {
576 const auto raw = GetEntryRaw(title_id, type); 576 const auto raw = GetEntryRaw(title_id, type);
577 if (raw == nullptr) 577 if (raw == nullptr)
578 return nullptr; 578 return nullptr;
579 return std::make_shared<NCA>(raw); 579 return std::make_unique<NCA>(raw);
580} 580}
581 581
582std::shared_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { 582std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const {
583 return GetEntry(entry.title_id, entry.type); 583 return GetEntry(entry.title_id, entry.type);
584} 584}
585 585
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index c0cd59fc5..5ddacba47 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -88,8 +88,8 @@ public:
88 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 88 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
89 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 89 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
90 90
91 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 91 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
92 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 92 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
93 93
94 std::vector<RegisteredCacheEntry> ListEntries() const; 94 std::vector<RegisteredCacheEntry> ListEntries() const;
95 // If a parameter is not boost::none, it will be filtered for from all entries. 95 // If a parameter is not boost::none, it will be filtered for from all entries.
@@ -142,7 +142,7 @@ private:
142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. 142// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface.
143class RegisteredCacheUnion { 143class RegisteredCacheUnion {
144public: 144public:
145 explicit RegisteredCacheUnion(std::vector<std::shared_ptr<RegisteredCache>> caches); 145 explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches);
146 146
147 void Refresh(); 147 void Refresh();
148 148
@@ -157,8 +157,8 @@ public:
157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 157 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const; 158 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
159 159
160 std::shared_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 160 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
161 std::shared_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const; 161 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
162 162
163 std::vector<RegisteredCacheEntry> ListEntries() const; 163 std::vector<RegisteredCacheEntry> ListEntries() const;
164 // If a parameter is not boost::none, it will be filtered for from all entries. 164 // If a parameter is not boost::none, it will be filtered for from all entries.
@@ -168,7 +168,7 @@ public:
168 boost::optional<u64> title_id = boost::none) const; 168 boost::optional<u64> title_id = boost::none) const;
169 169
170private: 170private:
171 std::vector<std::shared_ptr<RegisteredCache>> caches; 171 std::vector<RegisteredCache*> caches;
172}; 172};
173 173
174} // namespace FileSys 174} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index d66a9c9a4..bd3a57058 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -10,10 +10,10 @@
10namespace FileSys { 10namespace FileSys {
11 11
12SDMCFactory::SDMCFactory(VirtualDir dir_) 12SDMCFactory::SDMCFactory(VirtualDir dir_)
13 : dir(std::move(dir_)), contents(std::make_shared<RegisteredCache>( 13 : dir(std::move(dir_)), contents(std::make_unique<RegisteredCache>(
14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), 14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
15 [](const VirtualFile& file, const NcaID& id) { 15 [](const VirtualFile& file, const NcaID& id) {
16 return std::make_shared<NAX>(file, id)->GetDecrypted(); 16 return NAX{file, id}.GetDecrypted();
17 })) {} 17 })) {}
18 18
19SDMCFactory::~SDMCFactory() = default; 19SDMCFactory::~SDMCFactory() = default;
@@ -22,8 +22,8 @@ ResultVal<VirtualDir> SDMCFactory::Open() {
22 return MakeResult<VirtualDir>(dir); 22 return MakeResult<VirtualDir>(dir);
23} 23}
24 24
25std::shared_ptr<RegisteredCache> SDMCFactory::GetSDMCContents() const { 25RegisteredCache* SDMCFactory::GetSDMCContents() const {
26 return contents; 26 return contents.get();
27} 27}
28 28
29} // namespace FileSys 29} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index ea12149de..42794ba5b 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -19,12 +19,12 @@ public:
19 ~SDMCFactory(); 19 ~SDMCFactory();
20 20
21 ResultVal<VirtualDir> Open(); 21 ResultVal<VirtualDir> Open();
22 std::shared_ptr<RegisteredCache> GetSDMCContents() const; 22 RegisteredCache* GetSDMCContents() const;
23 23
24private: 24private:
25 VirtualDir dir; 25 VirtualDir dir;
26 26
27 std::shared_ptr<RegisteredCache> contents; 27 std::unique_ptr<RegisteredCache> contents;
28}; 28};
29 29
30} // namespace FileSys 30} // namespace FileSys
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 0ecfb5af1..518161bf7 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -7,8 +7,10 @@
7#include <vector> 7#include <vector>
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/file_sys/content_archive.h" 9#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h"
10#include "core/file_sys/nca_metadata.h" 11#include "core/file_sys/nca_metadata.h"
11#include "core/file_sys/partition_filesystem.h" 12#include "core/file_sys/partition_filesystem.h"
13#include "core/file_sys/patch_manager.h"
12#include "core/file_sys/registered_cache.h" 14#include "core/file_sys/registered_cache.h"
13#include "core/hle/ipc_helpers.h" 15#include "core/hle/ipc_helpers.h"
14#include "core/hle/kernel/process.h" 16#include "core/hle/kernel/process.h"
@@ -19,7 +21,7 @@
19namespace Service::AOC { 21namespace Service::AOC {
20 22
21constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000; 23constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
22constexpr u64 DLC_BASE_TO_AOC_ID_MASK = 0x1000; 24constexpr u64 DLC_BASE_TO_AOC_ID = 0x1000;
23 25
24static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) { 26static bool CheckAOCTitleIDMatchesBase(u64 base, u64 aoc) {
25 return (aoc & DLC_BASE_TITLE_ID_MASK) == base; 27 return (aoc & DLC_BASE_TITLE_ID_MASK) == base;
@@ -97,14 +99,24 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
97 99
98 ctx.WriteBuffer(out); 100 ctx.WriteBuffer(out);
99 101
100 IPC::ResponseBuilder rb{ctx, 2}; 102 IPC::ResponseBuilder rb{ctx, 3};
101 rb.Push(RESULT_SUCCESS); 103 rb.Push(RESULT_SUCCESS);
104 rb.Push(count);
102} 105}
103 106
104void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) { 107void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
105 IPC::ResponseBuilder rb{ctx, 4}; 108 IPC::ResponseBuilder rb{ctx, 4};
106 rb.Push(RESULT_SUCCESS); 109 rb.Push(RESULT_SUCCESS);
107 rb.Push(Core::System::GetInstance().CurrentProcess()->GetTitleID() | DLC_BASE_TO_AOC_ID_MASK); 110 const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
111 FileSys::PatchManager pm{title_id};
112
113 const auto res = pm.GetControlMetadata();
114 if (res.first == nullptr) {
115 rb.Push(title_id + DLC_BASE_TO_AOC_ID);
116 return;
117 }
118
119 rb.Push(res.first->GetDLCBaseTitleId());
108} 120}
109 121
110void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) { 122void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index e06712603..e32a7c48e 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -319,13 +319,12 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
319 return sdmc_factory->Open(); 319 return sdmc_factory->Open();
320} 320}
321 321
322std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() { 322std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
323 return std::make_shared<FileSys::RegisteredCacheUnion>( 323 return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
324 std::vector<std::shared_ptr<FileSys::RegisteredCache>>{ 324 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
325 GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
326} 325}
327 326
328std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() { 327FileSys::RegisteredCache* GetSystemNANDContents() {
329 LOG_TRACE(Service_FS, "Opening System NAND Contents"); 328 LOG_TRACE(Service_FS, "Opening System NAND Contents");
330 329
331 if (bis_factory == nullptr) 330 if (bis_factory == nullptr)
@@ -334,7 +333,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents() {
334 return bis_factory->GetSystemNANDContents(); 333 return bis_factory->GetSystemNANDContents();
335} 334}
336 335
337std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() { 336FileSys::RegisteredCache* GetUserNANDContents() {
338 LOG_TRACE(Service_FS, "Opening User NAND Contents"); 337 LOG_TRACE(Service_FS, "Opening User NAND Contents");
339 338
340 if (bis_factory == nullptr) 339 if (bis_factory == nullptr)
@@ -343,7 +342,7 @@ std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents() {
343 return bis_factory->GetUserNANDContents(); 342 return bis_factory->GetUserNANDContents();
344} 343}
345 344
346std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents() { 345FileSys::RegisteredCache* GetSDMCContents() {
347 LOG_TRACE(Service_FS, "Opening SDMC Contents"); 346 LOG_TRACE(Service_FS, "Opening SDMC Contents");
348 347
349 if (sdmc_factory == nullptr) 348 if (sdmc_factory == nullptr)
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 2df1faeb0..6ca5c5636 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -47,11 +47,11 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
47 FileSys::SaveDataDescriptor save_struct); 47 FileSys::SaveDataDescriptor save_struct);
48ResultVal<FileSys::VirtualDir> OpenSDMC(); 48ResultVal<FileSys::VirtualDir> OpenSDMC();
49 49
50std::shared_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); 50std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
51 51
52std::shared_ptr<FileSys::RegisteredCache> GetSystemNANDContents(); 52FileSys::RegisteredCache* GetSystemNANDContents();
53std::shared_ptr<FileSys::RegisteredCache> GetUserNANDContents(); 53FileSys::RegisteredCache* GetUserNANDContents();
54std::shared_ptr<FileSys::RegisteredCache> GetSDMCContents(); 54FileSys::RegisteredCache* GetSDMCContents();
55 55
56FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); 56FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
57 57
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 4b2f758a8..44accecb7 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -161,7 +161,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
161 }; 161 };
162 RegisterHandlers(functions); 162 RegisterHandlers(functions);
163 // Attempt to load shared font data from disk 163 // Attempt to load shared font data from disk
164 const auto nand = FileSystem::GetSystemNANDContents(); 164 const auto* nand = FileSystem::GetSystemNANDContents();
165 std::size_t offset = 0; 165 std::size_t offset = 0;
166 // Rebuild shared fonts from data ncas 166 // Rebuild shared fonts from data ncas
167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), 167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 951fd8257..8518dddcb 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -139,14 +139,22 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
139 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", 139 for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
140 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { 140 "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
141 const FileSys::VirtualFile module_file = dir->GetFile(module); 141 const FileSys::VirtualFile module_file = dir->GetFile(module);
142 if (module_file != nullptr) { 142 if (module_file == nullptr) {
143 const VAddr load_addr = next_load_addr; 143 continue;
144 next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr,
145 std::strcmp(module, "rtld") == 0, pm);
146 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
147 // Register module with GDBStub
148 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
149 } 144 }
145
146 const VAddr load_addr = next_load_addr;
147 const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
148 const auto tentative_next_load_addr =
149 AppLoader_NSO::LoadModule(*module_file, load_addr, should_pass_arguments, pm);
150 if (!tentative_next_load_addr) {
151 return ResultStatus::ErrorLoadingNSO;
152 }
153
154 next_load_addr = *tentative_next_load_addr;
155 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
156 // Register module with GDBStub
157 GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
150 } 158 }
151 159
152 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()); 160 process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 91659ec17..9cd0b0ccd 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
93 return "unknown"; 93 return "unknown";
94} 94}
95 95
96constexpr std::array<const char*, 59> RESULT_MESSAGES{ 96constexpr std::array<const char*, 60> RESULT_MESSAGES{
97 "The operation completed successfully.", 97 "The operation completed successfully.",
98 "The loader requested to load is already loaded.", 98 "The loader requested to load is already loaded.",
99 "The operation is not implemented.", 99 "The operation is not implemented.",
@@ -128,6 +128,7 @@ constexpr std::array<const char*, 59> RESULT_MESSAGES{
128 "The RomFS could not be found.", 128 "The RomFS could not be found.",
129 "The ELF file has incorrect size as determined by the header.", 129 "The ELF file has incorrect size as determined by the header.",
130 "There was a general error loading the NRO into emulated memory.", 130 "There was a general error loading the NRO into emulated memory.",
131 "There was a general error loading the NSO into emulated memory.",
131 "There is no icon available.", 132 "There is no icon available.",
132 "There is no control data available.", 133 "There is no control data available.",
133 "The NAX file has a bad header.", 134 "The NAX file has a bad header.",
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 0e0333db5..e562b3a04 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -90,6 +90,7 @@ enum class ResultStatus : u16 {
90 ErrorNoRomFS, 90 ErrorNoRomFS,
91 ErrorIncorrectELFFileSize, 91 ErrorIncorrectELFFileSize,
92 ErrorLoadingNRO, 92 ErrorLoadingNRO,
93 ErrorLoadingNSO,
93 ErrorNoIcon, 94 ErrorNoIcon,
94 ErrorNoControl, 95 ErrorNoControl,
95 ErrorBadNAXHeader, 96 ErrorBadNAXHeader,
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 576fe692a..243b499f2 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -127,10 +127,10 @@ static constexpr u32 PageAlignSize(u32 size) {
127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 127 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
128} 128}
129 129
130bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { 130bool AppLoader_NRO::LoadNro(const FileSys::VfsFile& file, VAddr load_base) {
131 // Read NSO header 131 // Read NSO header
132 NroHeader nro_header{}; 132 NroHeader nro_header{};
133 if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { 133 if (sizeof(NroHeader) != file.ReadObject(&nro_header)) {
134 return {}; 134 return {};
135 } 135 }
136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { 136 if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
@@ -138,7 +138,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
138 } 138 }
139 139
140 // Build program image 140 // Build program image
141 std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size)); 141 std::vector<u8> program_image = file.ReadBytes(PageAlignSize(nro_header.file_size));
142 if (program_image.size() != PageAlignSize(nro_header.file_size)) { 142 if (program_image.size() != PageAlignSize(nro_header.file_size)) {
143 return {}; 143 return {};
144 } 144 }
@@ -182,7 +182,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) {
182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); 182 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
183 183
184 // Register module with GDBStub 184 // Register module with GDBStub
185 GDBStub::RegisterModule(file->GetName(), load_base, load_base); 185 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
186 186
187 return true; 187 return true;
188} 188}
@@ -195,7 +195,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
195 // Load NRO 195 // Load NRO
196 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 196 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
197 197
198 if (!LoadNro(file, base_address)) { 198 if (!LoadNro(*file, base_address)) {
199 return ResultStatus::ErrorLoadingNRO; 199 return ResultStatus::ErrorLoadingNRO;
200 } 200 }
201 201
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 04b46119a..50ee5a78a 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -41,7 +41,7 @@ public:
41 bool IsRomFSUpdatable() const override; 41 bool IsRomFSUpdatable() const override;
42 42
43private: 43private:
44 bool LoadNro(FileSys::VirtualFile file, VAddr load_base); 44 bool LoadNro(const FileSys::VfsFile& file, VAddr load_base);
45 45
46 std::vector<u8> icon_data; 46 std::vector<u8> icon_data;
47 std::unique_ptr<FileSys::NACP> nacp; 47 std::unique_ptr<FileSys::NACP> nacp;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index ba57db9bf..68efca5c0 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -93,17 +93,14 @@ static constexpr u32 PageAlignSize(u32 size) {
93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; 93 return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
94} 94}
95 95
96VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base, 96std::optional<VAddr> AppLoader_NSO::LoadModule(const FileSys::VfsFile& file, VAddr load_base,
97 bool should_pass_arguments, 97 bool should_pass_arguments,
98 boost::optional<FileSys::PatchManager> pm) { 98 std::optional<FileSys::PatchManager> pm) {
99 if (file == nullptr) 99 if (file.GetSize() < sizeof(NsoHeader))
100 return {};
101
102 if (file->GetSize() < sizeof(NsoHeader))
103 return {}; 100 return {};
104 101
105 NsoHeader nso_header{}; 102 NsoHeader nso_header{};
106 if (sizeof(NsoHeader) != file->ReadObject(&nso_header)) 103 if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
107 return {}; 104 return {};
108 105
109 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) 106 if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
@@ -114,7 +111,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
114 std::vector<u8> program_image; 111 std::vector<u8> program_image;
115 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { 112 for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
116 std::vector<u8> data = 113 std::vector<u8> data =
117 file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); 114 file.ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset);
118 if (nso_header.IsSegmentCompressed(i)) { 115 if (nso_header.IsSegmentCompressed(i)) {
119 data = DecompressSegment(data, nso_header.segments[i]); 116 data = DecompressSegment(data, nso_header.segments[i]);
120 } 117 }
@@ -157,7 +154,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
157 program_image.resize(image_size); 154 program_image.resize(image_size);
158 155
159 // Apply patches if necessary 156 // Apply patches if necessary
160 if (pm != boost::none && pm->HasNSOPatch(nso_header.build_id)) { 157 if (pm && pm->HasNSOPatch(nso_header.build_id)) {
161 std::vector<u8> pi_header(program_image.size() + 0x100); 158 std::vector<u8> pi_header(program_image.size() + 0x100);
162 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader)); 159 std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
163 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size()); 160 std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
@@ -172,7 +169,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base,
172 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base); 169 Core::CurrentProcess()->LoadModule(std::move(codeset), load_base);
173 170
174 // Register module with GDBStub 171 // Register module with GDBStub
175 GDBStub::RegisterModule(file->GetName(), load_base, load_base); 172 GDBStub::RegisterModule(file.GetName(), load_base, load_base);
176 173
177 return load_base + image_size; 174 return load_base + image_size;
178} 175}
@@ -184,7 +181,9 @@ ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
184 181
185 // Load module 182 // Load module
186 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress(); 183 const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
187 LoadModule(file, base_address, true); 184 if (!LoadModule(*file, base_address, true)) {
185 return ResultStatus::ErrorLoadingNSO;
186 }
188 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); 187 LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
189 188
190 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); 189 process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 70ab3b718..433306139 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <optional>
7#include "common/common_types.h" 8#include "common/common_types.h"
8#include "core/file_sys/patch_manager.h" 9#include "core/file_sys/patch_manager.h"
9#include "core/loader/linker.h" 10#include "core/loader/linker.h"
@@ -36,8 +37,9 @@ public:
36 return IdentifyType(file); 37 return IdentifyType(file);
37 } 38 }
38 39
39 static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base, bool should_pass_arguments, 40 static std::optional<VAddr> LoadModule(const FileSys::VfsFile& file, VAddr load_base,
40 boost::optional<FileSys::PatchManager> pm = boost::none); 41 bool should_pass_arguments,
42 std::optional<FileSys::PatchManager> pm = {});
41 43
42 ResultStatus Load(Kernel::Process& process) override; 44 ResultStatus Load(Kernel::Process& process) override;
43}; 45};
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index 9a59b65b3..f356f9a03 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -267,7 +267,7 @@ enum class ControlCode : u64 {
267 GTU = 12, 267 GTU = 12,
268 NEU = 13, 268 NEU = 13,
269 GEU = 14, 269 GEU = 14,
270 // 270 T = 15,
271 OFF = 16, 271 OFF = 16,
272 LO = 17, 272 LO = 17,
273 SFF = 18, 273 SFF = 18,
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 84582c777..8d5f277e2 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -286,7 +286,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
286 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment)); 286 &ubo, sizeof(ubo), static_cast<std::size_t>(uniform_buffer_alignment));
287 287
288 // Bind the buffer 288 // Bind the buffer
289 glBindBufferRange(GL_UNIFORM_BUFFER, stage, buffer_cache.GetHandle(), offset, sizeof(ubo)); 289 glBindBufferRange(GL_UNIFORM_BUFFER, static_cast<GLuint>(stage), buffer_cache.GetHandle(),
290 offset, static_cast<GLsizeiptr>(sizeof(ubo)));
290 291
291 Shader shader{shader_cache.GetStageProgram(program)}; 292 Shader shader{shader_cache.GetStageProgram(program)};
292 293
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 8dfb49507..ca063d90d 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -1436,7 +1436,6 @@ private:
1436 1436
1437 break; 1437 break;
1438 } 1438 }
1439
1440 case OpCode::Type::Shift: { 1439 case OpCode::Type::Shift: {
1441 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true); 1440 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
1442 std::string op_b; 1441 std::string op_b;
@@ -1478,7 +1477,6 @@ private:
1478 } 1477 }
1479 break; 1478 break;
1480 } 1479 }
1481
1482 case OpCode::Type::ArithmeticIntegerImmediate: { 1480 case OpCode::Type::ArithmeticIntegerImmediate: {
1483 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8); 1481 std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
1484 std::string op_b = std::to_string(instr.alu.imm20_32.Value()); 1482 std::string op_b = std::to_string(instr.alu.imm20_32.Value());
@@ -2626,14 +2624,14 @@ private:
2626 const std::string pred = 2624 const std::string pred =
2627 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0); 2625 GetPredicateCondition(instr.csetp.pred39, instr.csetp.neg_pred39 != 0);
2628 const std::string combiner = GetPredicateCombiner(instr.csetp.op); 2626 const std::string combiner = GetPredicateCombiner(instr.csetp.op);
2629 const std::string controlCode = regs.GetControlCode(instr.csetp.cc); 2627 const std::string control_code = regs.GetControlCode(instr.csetp.cc);
2630 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) { 2628 if (instr.csetp.pred3 != static_cast<u64>(Pred::UnusedIndex)) {
2631 SetPredicate(instr.csetp.pred3, 2629 SetPredicate(instr.csetp.pred3,
2632 '(' + controlCode + ") " + combiner + " (" + pred + ')'); 2630 '(' + control_code + ") " + combiner + " (" + pred + ')');
2633 } 2631 }
2634 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) { 2632 if (instr.csetp.pred0 != static_cast<u64>(Pred::UnusedIndex)) {
2635 SetPredicate(instr.csetp.pred0, 2633 SetPredicate(instr.csetp.pred0,
2636 "!(" + controlCode + ") " + combiner + " (" + pred + ')'); 2634 "!(" + control_code + ") " + combiner + " (" + pred + ')');
2637 } 2635 }
2638 break; 2636 break;
2639 } 2637 }
diff --git a/src/web_service/telemetry_json.cpp b/src/web_service/telemetry_json.cpp
index 033ea1ea4..0a8f2bd9e 100644
--- a/src/web_service/telemetry_json.cpp
+++ b/src/web_service/telemetry_json.cpp
@@ -2,96 +2,114 @@
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 <thread> 5#include <json.hpp>
6#include "common/assert.h"
7#include "common/detached_tasks.h" 6#include "common/detached_tasks.h"
7#include "common/web_result.h"
8#include "web_service/telemetry_json.h" 8#include "web_service/telemetry_json.h"
9#include "web_service/web_backend.h" 9#include "web_service/web_backend.h"
10 10
11namespace WebService { 11namespace WebService {
12 12
13TelemetryJson::TelemetryJson(const std::string& host, const std::string& username, 13struct TelemetryJson::Impl {
14 const std::string& token) 14 Impl(std::string host, std::string username, std::string token)
15 : host(std::move(host)), username(std::move(username)), token(std::move(token)) {} 15 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {}
16TelemetryJson::~TelemetryJson() = default;
17 16
18template <class T> 17 nlohmann::json& TopSection() {
19void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) { 18 return sections[static_cast<u8>(Telemetry::FieldType::None)];
20 sections[static_cast<u8>(type)][name] = value; 19 }
21}
22 20
23void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) { 21 const nlohmann::json& TopSection() const {
24 TopSection()[name] = sections[static_cast<unsigned>(type)]; 22 return sections[static_cast<u8>(Telemetry::FieldType::None)];
25} 23 }
24
25 template <class T>
26 void Serialize(Telemetry::FieldType type, const std::string& name, T value) {
27 sections[static_cast<u8>(type)][name] = value;
28 }
29
30 void SerializeSection(Telemetry::FieldType type, const std::string& name) {
31 TopSection()[name] = sections[static_cast<unsigned>(type)];
32 }
33
34 nlohmann::json output;
35 std::array<nlohmann::json, 7> sections;
36 std::string host;
37 std::string username;
38 std::string token;
39};
40
41TelemetryJson::TelemetryJson(std::string host, std::string username, std::string token)
42 : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
43TelemetryJson::~TelemetryJson() = default;
26 44
27void TelemetryJson::Visit(const Telemetry::Field<bool>& field) { 45void TelemetryJson::Visit(const Telemetry::Field<bool>& field) {
28 Serialize(field.GetType(), field.GetName(), field.GetValue()); 46 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
29} 47}
30 48
31void TelemetryJson::Visit(const Telemetry::Field<double>& field) { 49void TelemetryJson::Visit(const Telemetry::Field<double>& field) {
32 Serialize(field.GetType(), field.GetName(), field.GetValue()); 50 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
33} 51}
34 52
35void TelemetryJson::Visit(const Telemetry::Field<float>& field) { 53void TelemetryJson::Visit(const Telemetry::Field<float>& field) {
36 Serialize(field.GetType(), field.GetName(), field.GetValue()); 54 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
37} 55}
38 56
39void TelemetryJson::Visit(const Telemetry::Field<u8>& field) { 57void TelemetryJson::Visit(const Telemetry::Field<u8>& field) {
40 Serialize(field.GetType(), field.GetName(), field.GetValue()); 58 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
41} 59}
42 60
43void TelemetryJson::Visit(const Telemetry::Field<u16>& field) { 61void TelemetryJson::Visit(const Telemetry::Field<u16>& field) {
44 Serialize(field.GetType(), field.GetName(), field.GetValue()); 62 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
45} 63}
46 64
47void TelemetryJson::Visit(const Telemetry::Field<u32>& field) { 65void TelemetryJson::Visit(const Telemetry::Field<u32>& field) {
48 Serialize(field.GetType(), field.GetName(), field.GetValue()); 66 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
49} 67}
50 68
51void TelemetryJson::Visit(const Telemetry::Field<u64>& field) { 69void TelemetryJson::Visit(const Telemetry::Field<u64>& field) {
52 Serialize(field.GetType(), field.GetName(), field.GetValue()); 70 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
53} 71}
54 72
55void TelemetryJson::Visit(const Telemetry::Field<s8>& field) { 73void TelemetryJson::Visit(const Telemetry::Field<s8>& field) {
56 Serialize(field.GetType(), field.GetName(), field.GetValue()); 74 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
57} 75}
58 76
59void TelemetryJson::Visit(const Telemetry::Field<s16>& field) { 77void TelemetryJson::Visit(const Telemetry::Field<s16>& field) {
60 Serialize(field.GetType(), field.GetName(), field.GetValue()); 78 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
61} 79}
62 80
63void TelemetryJson::Visit(const Telemetry::Field<s32>& field) { 81void TelemetryJson::Visit(const Telemetry::Field<s32>& field) {
64 Serialize(field.GetType(), field.GetName(), field.GetValue()); 82 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
65} 83}
66 84
67void TelemetryJson::Visit(const Telemetry::Field<s64>& field) { 85void TelemetryJson::Visit(const Telemetry::Field<s64>& field) {
68 Serialize(field.GetType(), field.GetName(), field.GetValue()); 86 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
69} 87}
70 88
71void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) { 89void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) {
72 Serialize(field.GetType(), field.GetName(), field.GetValue()); 90 impl->Serialize(field.GetType(), field.GetName(), field.GetValue());
73} 91}
74 92
75void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) { 93void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) {
76 Serialize(field.GetType(), field.GetName(), std::string(field.GetValue())); 94 impl->Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
77} 95}
78 96
79void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) { 97void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) {
80 Serialize(field.GetType(), field.GetName(), field.GetValue().count()); 98 impl->Serialize(field.GetType(), field.GetName(), field.GetValue().count());
81} 99}
82 100
83void TelemetryJson::Complete() { 101void TelemetryJson::Complete() {
84 SerializeSection(Telemetry::FieldType::App, "App"); 102 impl->SerializeSection(Telemetry::FieldType::App, "App");
85 SerializeSection(Telemetry::FieldType::Session, "Session"); 103 impl->SerializeSection(Telemetry::FieldType::Session, "Session");
86 SerializeSection(Telemetry::FieldType::Performance, "Performance"); 104 impl->SerializeSection(Telemetry::FieldType::Performance, "Performance");
87 SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback"); 105 impl->SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
88 SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig"); 106 impl->SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
89 SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem"); 107 impl->SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
90 108
91 auto content = TopSection().dump(); 109 auto content = impl->TopSection().dump();
92 // Send the telemetry async but don't handle the errors since they were written to the log 110 // Send the telemetry async but don't handle the errors since they were written to the log
93 Common::DetachedTasks::AddTask( 111 Common::DetachedTasks::AddTask(
94 [host{this->host}, username{this->username}, token{this->token}, content]() { 112 [host{impl->host}, username{impl->username}, token{impl->token}, content]() {
95 Client{host, username, token}.PostJson("/telemetry", content, true); 113 Client{host, username, token}.PostJson("/telemetry", content, true);
96 }); 114 });
97} 115}
diff --git a/src/web_service/telemetry_json.h b/src/web_service/telemetry_json.h
index 0fe6f9a3e..93371414a 100644
--- a/src/web_service/telemetry_json.h
+++ b/src/web_service/telemetry_json.h
@@ -4,11 +4,9 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <chrono>
8#include <string> 8#include <string>
9#include <json.hpp>
10#include "common/telemetry.h" 9#include "common/telemetry.h"
11#include "common/web_result.h"
12 10
13namespace WebService { 11namespace WebService {
14 12
@@ -18,8 +16,8 @@ namespace WebService {
18 */ 16 */
19class TelemetryJson : public Telemetry::VisitorInterface { 17class TelemetryJson : public Telemetry::VisitorInterface {
20public: 18public:
21 TelemetryJson(const std::string& host, const std::string& username, const std::string& token); 19 TelemetryJson(std::string host, std::string username, std::string token);
22 ~TelemetryJson(); 20 ~TelemetryJson() override;
23 21
24 void Visit(const Telemetry::Field<bool>& field) override; 22 void Visit(const Telemetry::Field<bool>& field) override;
25 void Visit(const Telemetry::Field<double>& field) override; 23 void Visit(const Telemetry::Field<double>& field) override;
@@ -39,20 +37,8 @@ public:
39 void Complete() override; 37 void Complete() override;
40 38
41private: 39private:
42 nlohmann::json& TopSection() { 40 struct Impl;
43 return sections[static_cast<u8>(Telemetry::FieldType::None)]; 41 std::unique_ptr<Impl> impl;
44 }
45
46 template <class T>
47 void Serialize(Telemetry::FieldType type, const std::string& name, T value);
48
49 void SerializeSection(Telemetry::FieldType type, const std::string& name);
50
51 nlohmann::json output;
52 std::array<nlohmann::json, 7> sections;
53 std::string host;
54 std::string username;
55 std::string token;
56}; 42};
57 43
58} // namespace WebService 44} // namespace WebService
diff --git a/src/web_service/verify_login.cpp b/src/web_service/verify_login.cpp
index 124aa3863..ca4b43b93 100644
--- a/src/web_service/verify_login.cpp
+++ b/src/web_service/verify_login.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 <json.hpp> 5#include <json.hpp>
6#include "common/web_result.h"
6#include "web_service/verify_login.h" 7#include "web_service/verify_login.h"
7#include "web_service/web_backend.h" 8#include "web_service/web_backend.h"
8 9
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 787b0fbcb..b7737b615 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -3,9 +3,11 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cstdlib> 5#include <cstdlib>
6#include <mutex>
6#include <string> 7#include <string>
7#include <thread>
8#include <LUrlParser.h> 8#include <LUrlParser.h>
9#include <httplib.h>
10#include "common/common_types.h"
9#include "common/logging/log.h" 11#include "common/logging/log.h"
10#include "common/web_result.h" 12#include "common/web_result.h"
11#include "core/settings.h" 13#include "core/settings.h"
@@ -20,99 +22,132 @@ constexpr u32 HTTPS_PORT = 443;
20 22
21constexpr u32 TIMEOUT_SECONDS = 30; 23constexpr u32 TIMEOUT_SECONDS = 30;
22 24
23Client::JWTCache Client::jwt_cache{}; 25struct Client::Impl {
26 Impl(std::string host, std::string username, std::string token)
27 : host{std::move(host)}, username{std::move(username)}, token{std::move(token)} {
28 std::lock_guard<std::mutex> lock(jwt_cache.mutex);
29 if (this->username == jwt_cache.username && this->token == jwt_cache.token) {
30 jwt = jwt_cache.jwt;
31 }
32 }
33
34 /// A generic function handles POST, GET and DELETE request together
35 Common::WebResult GenericJson(const std::string& method, const std::string& path,
36 const std::string& data, bool allow_anonymous) {
37 if (jwt.empty()) {
38 UpdateJWT();
39 }
40
41 if (jwt.empty() && !allow_anonymous) {
42 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests");
43 return Common::WebResult{Common::WebResult::Code::CredentialsMissing,
44 "Credentials needed"};
45 }
46
47 auto result = GenericJson(method, path, data, jwt);
48 if (result.result_string == "401") {
49 // Try again with new JWT
50 UpdateJWT();
51 result = GenericJson(method, path, data, jwt);
52 }
24 53
25Client::Client(const std::string& host, const std::string& username, const std::string& token) 54 return result;
26 : host(host), username(username), token(token) {
27 std::lock_guard<std::mutex> lock(jwt_cache.mutex);
28 if (username == jwt_cache.username && token == jwt_cache.token) {
29 jwt = jwt_cache.jwt;
30 } 55 }
31}
32 56
33Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, 57 /**
34 const std::string& data, const std::string& jwt, 58 * A generic function with explicit authentication method specified
35 const std::string& username, const std::string& token) { 59 * JWT is used if the jwt parameter is not empty
36 if (cli == nullptr) { 60 * username + token is used if jwt is empty but username and token are not empty
37 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host); 61 * anonymous if all of jwt, username and token are empty
38 int port; 62 */
39 if (parsedUrl.m_Scheme == "http") { 63 Common::WebResult GenericJson(const std::string& method, const std::string& path,
40 if (!parsedUrl.GetPort(&port)) { 64 const std::string& data, const std::string& jwt = "",
41 port = HTTP_PORT; 65 const std::string& username = "", const std::string& token = "") {
42 } 66 if (cli == nullptr) {
43 cli = 67 auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
44 std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port, TIMEOUT_SECONDS); 68 int port;
45 } else if (parsedUrl.m_Scheme == "https") { 69 if (parsedUrl.m_Scheme == "http") {
46 if (!parsedUrl.GetPort(&port)) { 70 if (!parsedUrl.GetPort(&port)) {
47 port = HTTPS_PORT; 71 port = HTTP_PORT;
72 }
73 cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port,
74 TIMEOUT_SECONDS);
75 } else if (parsedUrl.m_Scheme == "https") {
76 if (!parsedUrl.GetPort(&port)) {
77 port = HTTPS_PORT;
78 }
79 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
80 TIMEOUT_SECONDS);
81 } else {
82 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
83 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
48 } 84 }
49 cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port,
50 TIMEOUT_SECONDS);
51 } else {
52 LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
53 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Bad URL scheme"};
54 } 85 }
55 } 86 if (cli == nullptr) {
56 if (cli == nullptr) { 87 LOG_ERROR(WebService, "Invalid URL {}", host + path);
57 LOG_ERROR(WebService, "Invalid URL {}", host + path); 88 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"};
58 return Common::WebResult{Common::WebResult::Code::InvalidURL, "Invalid URL"}; 89 }
59 }
60 90
61 httplib::Headers params; 91 httplib::Headers params;
62 if (!jwt.empty()) { 92 if (!jwt.empty()) {
63 params = { 93 params = {
64 {std::string("Authorization"), fmt::format("Bearer {}", jwt)}, 94 {std::string("Authorization"), fmt::format("Bearer {}", jwt)},
65 }; 95 };
66 } else if (!username.empty()) { 96 } else if (!username.empty()) {
67 params = { 97 params = {
68 {std::string("x-username"), username}, 98 {std::string("x-username"), username},
69 {std::string("x-token"), token}, 99 {std::string("x-token"), token},
100 };
101 }
102
103 params.emplace(std::string("api-version"),
104 std::string(API_VERSION.begin(), API_VERSION.end()));
105 if (method != "GET") {
106 params.emplace(std::string("Content-Type"), std::string("application/json"));
70 }; 107 };
71 }
72 108
73 params.emplace(std::string("api-version"), std::string(API_VERSION.begin(), API_VERSION.end())); 109 httplib::Request request;
74 if (method != "GET") { 110 request.method = method;
75 params.emplace(std::string("Content-Type"), std::string("application/json")); 111 request.path = path;
76 }; 112 request.headers = params;
113 request.body = data;
77 114
78 httplib::Request request; 115 httplib::Response response;
79 request.method = method;
80 request.path = path;
81 request.headers = params;
82 request.body = data;
83 116
84 httplib::Response response; 117 if (!cli->send(request, response)) {
118 LOG_ERROR(WebService, "{} to {} returned null", method, host + path);
119 return Common::WebResult{Common::WebResult::Code::LibError, "Null response"};
120 }
85 121
86 if (!cli->send(request, response)) { 122 if (response.status >= 400) {
87 LOG_ERROR(WebService, "{} to {} returned null", method, host + path); 123 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
88 return Common::WebResult{Common::WebResult::Code::LibError, "Null response"}; 124 response.status);
89 } 125 return Common::WebResult{Common::WebResult::Code::HttpError,
126 std::to_string(response.status)};
127 }
90 128
91 if (response.status >= 400) { 129 auto content_type = response.headers.find("content-type");
92 LOG_ERROR(WebService, "{} to {} returned error status code: {}", method, host + path,
93 response.status);
94 return Common::WebResult{Common::WebResult::Code::HttpError,
95 std::to_string(response.status)};
96 }
97 130
98 auto content_type = response.headers.find("content-type"); 131 if (content_type == response.headers.end()) {
132 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path);
133 return Common::WebResult{Common::WebResult::Code::WrongContent, ""};
134 }
99 135
100 if (content_type == response.headers.end()) { 136 if (content_type->second.find("application/json") == std::string::npos &&
101 LOG_ERROR(WebService, "{} to {} returned no content", method, host + path); 137 content_type->second.find("text/html; charset=utf-8") == std::string::npos) {
102 return Common::WebResult{Common::WebResult::Code::WrongContent, ""}; 138 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path,
139 content_type->second);
140 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"};
141 }
142 return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
103 } 143 }
104 144
105 if (content_type->second.find("application/json") == std::string::npos && 145 // Retrieve a new JWT from given username and token
106 content_type->second.find("text/html; charset=utf-8") == std::string::npos) { 146 void UpdateJWT() {
107 LOG_ERROR(WebService, "{} to {} returned wrong content: {}", method, host + path, 147 if (username.empty() || token.empty()) {
108 content_type->second); 148 return;
109 return Common::WebResult{Common::WebResult::Code::WrongContent, "Wrong content"}; 149 }
110 }
111 return Common::WebResult{Common::WebResult::Code::Success, "", response.body};
112}
113 150
114void Client::UpdateJWT() {
115 if (!username.empty() && !token.empty()) {
116 auto result = GenericJson("POST", "/jwt/internal", "", "", username, token); 151 auto result = GenericJson("POST", "/jwt/internal", "", "", username, token);
117 if (result.result_code != Common::WebResult::Code::Success) { 152 if (result.result_code != Common::WebResult::Code::Success) {
118 LOG_ERROR(WebService, "UpdateJWT failed"); 153 LOG_ERROR(WebService, "UpdateJWT failed");
@@ -123,27 +158,39 @@ void Client::UpdateJWT() {
123 jwt_cache.jwt = jwt = result.returned_data; 158 jwt_cache.jwt = jwt = result.returned_data;
124 } 159 }
125 } 160 }
126}
127 161
128Common::WebResult Client::GenericJson(const std::string& method, const std::string& path, 162 std::string host;
129 const std::string& data, bool allow_anonymous) { 163 std::string username;
130 if (jwt.empty()) { 164 std::string token;
131 UpdateJWT(); 165 std::string jwt;
132 } 166 std::unique_ptr<httplib::Client> cli;
167
168 struct JWTCache {
169 std::mutex mutex;
170 std::string username;
171 std::string token;
172 std::string jwt;
173 };
174 static inline JWTCache jwt_cache;
175};
133 176
134 if (jwt.empty() && !allow_anonymous) { 177Client::Client(std::string host, std::string username, std::string token)
135 LOG_ERROR(WebService, "Credentials must be provided for authenticated requests"); 178 : impl{std::make_unique<Impl>(std::move(host), std::move(username), std::move(token))} {}
136 return Common::WebResult{Common::WebResult::Code::CredentialsMissing, "Credentials needed"};
137 }
138 179
139 auto result = GenericJson(method, path, data, jwt); 180Client::~Client() = default;
140 if (result.result_string == "401") { 181
141 // Try again with new JWT 182Common::WebResult Client::PostJson(const std::string& path, const std::string& data,
142 UpdateJWT(); 183 bool allow_anonymous) {
143 result = GenericJson(method, path, data, jwt); 184 return impl->GenericJson("POST", path, data, allow_anonymous);
144 } 185}
186
187Common::WebResult Client::GetJson(const std::string& path, bool allow_anonymous) {
188 return impl->GenericJson("GET", path, "", allow_anonymous);
189}
145 190
146 return result; 191Common::WebResult Client::DeleteJson(const std::string& path, const std::string& data,
192 bool allow_anonymous) {
193 return impl->GenericJson("DELETE", path, data, allow_anonymous);
147} 194}
148 195
149} // namespace WebService 196} // namespace WebService
diff --git a/src/web_service/web_backend.h b/src/web_service/web_backend.h
index d75fbcc15..c637e09df 100644
--- a/src/web_service/web_backend.h
+++ b/src/web_service/web_backend.h
@@ -4,23 +4,19 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <functional> 7#include <memory>
8#include <mutex>
9#include <string> 8#include <string>
10#include <tuple>
11#include <httplib.h>
12#include "common/common_types.h"
13#include "common/web_result.h"
14 9
15namespace httplib { 10namespace Common {
16class Client; 11struct WebResult;
17} 12}
18 13
19namespace WebService { 14namespace WebService {
20 15
21class Client { 16class Client {
22public: 17public:
23 Client(const std::string& host, const std::string& username, const std::string& token); 18 Client(std::string host, std::string username, std::string token);
19 ~Client();
24 20
25 /** 21 /**
26 * Posts JSON to the specified path. 22 * Posts JSON to the specified path.
@@ -30,9 +26,7 @@ public:
30 * @return the result of the request. 26 * @return the result of the request.
31 */ 27 */
32 Common::WebResult PostJson(const std::string& path, const std::string& data, 28 Common::WebResult PostJson(const std::string& path, const std::string& data,
33 bool allow_anonymous) { 29 bool allow_anonymous);
34 return GenericJson("POST", path, data, allow_anonymous);
35 }
36 30
37 /** 31 /**
38 * Gets JSON from the specified path. 32 * Gets JSON from the specified path.
@@ -40,9 +34,7 @@ public:
40 * @param allow_anonymous If true, allow anonymous unauthenticated requests. 34 * @param allow_anonymous If true, allow anonymous unauthenticated requests.
41 * @return the result of the request. 35 * @return the result of the request.
42 */ 36 */
43 Common::WebResult GetJson(const std::string& path, bool allow_anonymous) { 37 Common::WebResult GetJson(const std::string& path, bool allow_anonymous);
44 return GenericJson("GET", path, "", allow_anonymous);
45 }
46 38
47 /** 39 /**
48 * Deletes JSON to the specified path. 40 * Deletes JSON to the specified path.
@@ -52,41 +44,11 @@ public:
52 * @return the result of the request. 44 * @return the result of the request.
53 */ 45 */
54 Common::WebResult DeleteJson(const std::string& path, const std::string& data, 46 Common::WebResult DeleteJson(const std::string& path, const std::string& data,
55 bool allow_anonymous) { 47 bool allow_anonymous);
56 return GenericJson("DELETE", path, data, allow_anonymous);
57 }
58 48
59private: 49private:
60 /// A generic function handles POST, GET and DELETE request together 50 struct Impl;
61 Common::WebResult GenericJson(const std::string& method, const std::string& path, 51 std::unique_ptr<Impl> impl;
62 const std::string& data, bool allow_anonymous);
63
64 /**
65 * A generic function with explicit authentication method specified
66 * JWT is used if the jwt parameter is not empty
67 * username + token is used if jwt is empty but username and token are not empty
68 * anonymous if all of jwt, username and token are empty
69 */
70 Common::WebResult GenericJson(const std::string& method, const std::string& path,
71 const std::string& data, const std::string& jwt = "",
72 const std::string& username = "", const std::string& token = "");
73
74 // Retrieve a new JWT from given username and token
75 void UpdateJWT();
76
77 std::string host;
78 std::string username;
79 std::string token;
80 std::string jwt;
81 std::unique_ptr<httplib::Client> cli;
82
83 struct JWTCache {
84 std::mutex mutex;
85 std::string username;
86 std::string token;
87 std::string jwt;
88 };
89 static JWTCache jwt_cache;
90}; 52};
91 53
92} // namespace WebService 54} // namespace WebService
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp
index 8f99a1c78..3881aba5f 100644
--- a/src/yuzu/game_list_worker.cpp
+++ b/src/yuzu/game_list_worker.cpp
@@ -96,7 +96,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
96 FileSys::ContentRecordType::Program); 96 FileSys::ContentRecordType::Program);
97 97
98 for (const auto& game : installed_games) { 98 for (const auto& game : installed_games) {
99 const auto& file = cache->GetEntryUnparsed(game); 99 const auto file = cache->GetEntryUnparsed(game);
100 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file); 100 std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
101 if (!loader) 101 if (!loader)
102 continue; 102 continue;
@@ -107,7 +107,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
107 loader->ReadProgramId(program_id); 107 loader->ReadProgramId(program_id);
108 108
109 const FileSys::PatchManager patch{program_id}; 109 const FileSys::PatchManager patch{program_id};
110 const auto& control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control); 110 const auto control = cache->GetEntry(game.title_id, FileSys::ContentRecordType::Control);
111 if (control != nullptr) 111 if (control != nullptr)
112 GetMetadataFromControlNCA(patch, *control, icon, name); 112 GetMetadataFromControlNCA(patch, *control, icon, name);
113 113
@@ -135,9 +135,10 @@ void GameListWorker::AddInstalledTitlesToGameList() {
135 FileSys::ContentRecordType::Control); 135 FileSys::ContentRecordType::Control);
136 136
137 for (const auto& entry : control_data) { 137 for (const auto& entry : control_data) {
138 const auto nca = cache->GetEntry(entry); 138 auto nca = cache->GetEntry(entry);
139 if (nca != nullptr) 139 if (nca != nullptr) {
140 nca_control_map.insert_or_assign(entry.title_id, nca); 140 nca_control_map.insert_or_assign(entry.title_id, std::move(nca));
141 }
141 } 142 }
142} 143}
143 144
@@ -153,9 +154,11 @@ void GameListWorker::FillControlMap(const std::string& dir_path) {
153 QFileInfo file_info(physical_name.c_str()); 154 QFileInfo file_info(physical_name.c_str());
154 if (!is_dir && file_info.suffix().toStdString() == "nca") { 155 if (!is_dir && file_info.suffix().toStdString() == "nca") {
155 auto nca = 156 auto nca =
156 std::make_shared<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read)); 157 std::make_unique<FileSys::NCA>(vfs->OpenFile(physical_name, FileSys::Mode::Read));
157 if (nca->GetType() == FileSys::NCAContentType::Control) 158 if (nca->GetType() == FileSys::NCAContentType::Control) {
158 nca_control_map.insert_or_assign(nca->GetTitleId(), nca); 159 const u64 title_id = nca->GetTitleId();
160 nca_control_map.insert_or_assign(title_id, std::move(nca));
161 }
159 } 162 }
160 return true; 163 return true;
161 }; 164 };
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h
index 09d20c42f..0e42d0bde 100644
--- a/src/yuzu/game_list_worker.h
+++ b/src/yuzu/game_list_worker.h
@@ -63,7 +63,7 @@ private:
63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0); 63 void AddFstEntriesToGameList(const std::string& dir_path, unsigned int recursion = 0);
64 64
65 std::shared_ptr<FileSys::VfsFilesystem> vfs; 65 std::shared_ptr<FileSys::VfsFilesystem> vfs;
66 std::map<u64, std::shared_ptr<FileSys::NCA>> nca_control_map; 66 std::map<u64, std::unique_ptr<FileSys::NCA>> nca_control_map;
67 QStringList watch_list; 67 QStringList watch_list;
68 QString dir_path; 68 QString dir_path;
69 bool deep_scan; 69 bool deep_scan;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index cc92ea5b8..bef9df00d 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -1428,8 +1428,12 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1428 QMessageBox::warning( 1428 QMessageBox::warning(
1429 this, tr("Warning Missing Derivation Components"), 1429 this, tr("Warning Missing Derivation Components"),
1430 tr("The following are missing from your configuration that may hinder key " 1430 tr("The following are missing from your configuration that may hinder key "
1431 "derivation. It will be attempted but may not complete.\n\n") + 1431 "derivation. It will be attempted but may not complete.<br><br>") +
1432 errors); 1432 errors +
1433 tr("<br><br>You can get all of these and dump all of your games easily by "
1434 "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the "
1435 "quickstart guide</a>. Alternatively, you can use another method of dumping "
1436 "to obtain all of your keys."));
1433 } 1437 }
1434 1438
1435 QProgressDialog prog; 1439 QProgressDialog prog;
@@ -1563,7 +1567,7 @@ void GMainWindow::UpdateUITheme() {
1563 emit UpdateThemedIcons(); 1567 emit UpdateThemedIcons();
1564} 1568}
1565 1569
1566void GMainWindow::SetDiscordEnabled(bool state) { 1570void GMainWindow::SetDiscordEnabled([[maybe_unused]] bool state) {
1567#ifdef USE_DISCORD_PRESENCE 1571#ifdef USE_DISCORD_PRESENCE
1568 if (state) { 1572 if (state) {
1569 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>(); 1573 discord_rpc = std::make_unique<DiscordRPC::DiscordImpl>();