diff options
72 files changed, 1487 insertions, 368 deletions
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp index 3aea9b0f2..5005ba519 100644 --- a/src/audio_core/algorithm/interpolate.cpp +++ b/src/audio_core/algorithm/interpolate.cpp | |||
| @@ -54,8 +54,9 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input, | |||
| 54 | double l = 0.0; | 54 | double l = 0.0; |
| 55 | double r = 0.0; | 55 | double r = 0.0; |
| 56 | for (std::size_t j = 0; j < h.size(); j++) { | 56 | for (std::size_t j = 0; j < h.size(); j++) { |
| 57 | l += Lanczos(taps, pos + j - taps + 1) * h[j][0]; | 57 | const double lanczos_calc = Lanczos(taps, pos + j - taps + 1); |
| 58 | r += Lanczos(taps, pos + j - taps + 1) * h[j][1]; | 58 | l += lanczos_calc * h[j][0]; |
| 59 | r += lanczos_calc * h[j][1]; | ||
| 59 | } | 60 | } |
| 60 | output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0))); | 61 | output.emplace_back(static_cast<s16>(std::clamp(l, -32768.0, 32767.0))); |
| 61 | output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0))); | 62 | output.emplace_back(static_cast<s16>(std::clamp(r, -32768.0, 32767.0))); |
diff --git a/src/audio_core/audio_out.cpp b/src/audio_core/audio_out.cpp index 0c8f5b18e..cbba17632 100644 --- a/src/audio_core/audio_out.cpp +++ b/src/audio_core/audio_out.cpp | |||
| @@ -30,8 +30,7 @@ static Stream::Format ChannelsToStreamFormat(u32 num_channels) { | |||
| 30 | StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, | 30 | StreamPtr AudioOut::OpenStream(u32 sample_rate, u32 num_channels, std::string&& name, |
| 31 | Stream::ReleaseCallback&& release_callback) { | 31 | Stream::ReleaseCallback&& release_callback) { |
| 32 | if (!sink) { | 32 | if (!sink) { |
| 33 | const SinkDetails& sink_details = GetSinkDetails(Settings::values.sink_id); | 33 | sink = CreateSinkFromID(Settings::values.sink_id, Settings::values.audio_device_id); |
| 34 | sink = sink_details.factory(Settings::values.audio_device_id); | ||
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | return std::make_shared<Stream>( | 36 | return std::make_shared<Stream>( |
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp index 2e59894ab..2683f3a5f 100644 --- a/src/audio_core/audio_renderer.cpp +++ b/src/audio_core/audio_renderer.cpp | |||
| @@ -285,8 +285,11 @@ void AudioRenderer::VoiceState::RefreshBuffer() { | |||
| 285 | break; | 285 | break; |
| 286 | } | 286 | } |
| 287 | 287 | ||
| 288 | samples = | 288 | // Only interpolate when necessary, expensive. |
| 289 | Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, STREAM_SAMPLE_RATE); | 289 | if (GetInfo().sample_rate != STREAM_SAMPLE_RATE) { |
| 290 | samples = Interpolate(interp_state, std::move(samples), GetInfo().sample_rate, | ||
| 291 | STREAM_SAMPLE_RATE); | ||
| 292 | } | ||
| 290 | 293 | ||
| 291 | is_refresh_pending = false; | 294 | is_refresh_pending = false; |
| 292 | } | 295 | } |
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp index d31a1c844..097328901 100644 --- a/src/audio_core/cubeb_sink.cpp +++ b/src/audio_core/cubeb_sink.cpp | |||
| @@ -107,7 +107,7 @@ private: | |||
| 107 | static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state); | 107 | static void StateCallback(cubeb_stream* stream, void* user_data, cubeb_state state); |
| 108 | }; | 108 | }; |
| 109 | 109 | ||
| 110 | CubebSink::CubebSink(std::string target_device_name) { | 110 | CubebSink::CubebSink(std::string_view target_device_name) { |
| 111 | if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) { | 111 | if (cubeb_init(&ctx, "yuzu", nullptr) != CUBEB_OK) { |
| 112 | LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); | 112 | LOG_CRITICAL(Audio_Sink, "cubeb_init failed"); |
| 113 | return; | 113 | return; |
diff --git a/src/audio_core/cubeb_sink.h b/src/audio_core/cubeb_sink.h index 59cbf05e9..efb9d1634 100644 --- a/src/audio_core/cubeb_sink.h +++ b/src/audio_core/cubeb_sink.h | |||
| @@ -15,7 +15,7 @@ namespace AudioCore { | |||
| 15 | 15 | ||
| 16 | class CubebSink final : public Sink { | 16 | class CubebSink final : public Sink { |
| 17 | public: | 17 | public: |
| 18 | explicit CubebSink(std::string device_id); | 18 | explicit CubebSink(std::string_view device_id); |
| 19 | ~CubebSink() override; | 19 | ~CubebSink() override; |
| 20 | 20 | ||
| 21 | SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels, | 21 | SinkStream& AcquireSinkStream(u32 sample_rate, u32 num_channels, |
diff --git a/src/audio_core/null_sink.h b/src/audio_core/null_sink.h index a78d78893..61a28d542 100644 --- a/src/audio_core/null_sink.h +++ b/src/audio_core/null_sink.h | |||
| @@ -10,7 +10,7 @@ namespace AudioCore { | |||
| 10 | 10 | ||
| 11 | class NullSink final : public Sink { | 11 | class NullSink final : public Sink { |
| 12 | public: | 12 | public: |
| 13 | explicit NullSink(std::string){}; | 13 | explicit NullSink(std::string_view) {} |
| 14 | ~NullSink() override = default; | 14 | ~NullSink() override = default; |
| 15 | 15 | ||
| 16 | SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/, | 16 | SinkStream& AcquireSinkStream(u32 /*sample_rate*/, u32 /*num_channels*/, |
diff --git a/src/audio_core/sink_details.cpp b/src/audio_core/sink_details.cpp index 67cf1f3b2..a848eb1c9 100644 --- a/src/audio_core/sink_details.cpp +++ b/src/audio_core/sink_details.cpp | |||
| @@ -14,31 +14,68 @@ | |||
| 14 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 15 | 15 | ||
| 16 | namespace AudioCore { | 16 | namespace AudioCore { |
| 17 | namespace { | ||
| 18 | struct SinkDetails { | ||
| 19 | using FactoryFn = std::unique_ptr<Sink> (*)(std::string_view); | ||
| 20 | using ListDevicesFn = std::vector<std::string> (*)(); | ||
| 17 | 21 | ||
| 18 | // g_sink_details is ordered in terms of desirability, with the best choice at the top. | 22 | /// Name for this sink. |
| 19 | const std::vector<SinkDetails> g_sink_details = { | 23 | const char* id; |
| 24 | /// A method to call to construct an instance of this type of sink. | ||
| 25 | FactoryFn factory; | ||
| 26 | /// A method to call to list available devices. | ||
| 27 | ListDevicesFn list_devices; | ||
| 28 | }; | ||
| 29 | |||
| 30 | // sink_details is ordered in terms of desirability, with the best choice at the top. | ||
| 31 | constexpr SinkDetails sink_details[] = { | ||
| 20 | #ifdef HAVE_CUBEB | 32 | #ifdef HAVE_CUBEB |
| 21 | SinkDetails{"cubeb", &std::make_unique<CubebSink, std::string>, &ListCubebSinkDevices}, | 33 | SinkDetails{"cubeb", |
| 34 | [](std::string_view device_id) -> std::unique_ptr<Sink> { | ||
| 35 | return std::make_unique<CubebSink>(device_id); | ||
| 36 | }, | ||
| 37 | &ListCubebSinkDevices}, | ||
| 22 | #endif | 38 | #endif |
| 23 | SinkDetails{"null", &std::make_unique<NullSink, std::string>, | 39 | SinkDetails{"null", |
| 40 | [](std::string_view device_id) -> std::unique_ptr<Sink> { | ||
| 41 | return std::make_unique<NullSink>(device_id); | ||
| 42 | }, | ||
| 24 | [] { return std::vector<std::string>{"null"}; }}, | 43 | [] { return std::vector<std::string>{"null"}; }}, |
| 25 | }; | 44 | }; |
| 26 | 45 | ||
| 27 | const SinkDetails& GetSinkDetails(std::string_view sink_id) { | 46 | const SinkDetails& GetSinkDetails(std::string_view sink_id) { |
| 28 | auto iter = | 47 | auto iter = |
| 29 | std::find_if(g_sink_details.begin(), g_sink_details.end(), | 48 | std::find_if(std::begin(sink_details), std::end(sink_details), |
| 30 | [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); | 49 | [sink_id](const auto& sink_detail) { return sink_detail.id == sink_id; }); |
| 31 | 50 | ||
| 32 | if (sink_id == "auto" || iter == g_sink_details.end()) { | 51 | if (sink_id == "auto" || iter == std::end(sink_details)) { |
| 33 | if (sink_id != "auto") { | 52 | if (sink_id != "auto") { |
| 34 | LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id {}", sink_id); | 53 | LOG_ERROR(Audio, "AudioCore::SelectSink given invalid sink_id {}", sink_id); |
| 35 | } | 54 | } |
| 36 | // Auto-select. | 55 | // Auto-select. |
| 37 | // g_sink_details is ordered in terms of desirability, with the best choice at the front. | 56 | // sink_details is ordered in terms of desirability, with the best choice at the front. |
| 38 | iter = g_sink_details.begin(); | 57 | iter = std::begin(sink_details); |
| 39 | } | 58 | } |
| 40 | 59 | ||
| 41 | return *iter; | 60 | return *iter; |
| 42 | } | 61 | } |
| 62 | } // Anonymous namespace | ||
| 63 | |||
| 64 | std::vector<const char*> GetSinkIDs() { | ||
| 65 | std::vector<const char*> sink_ids(std::size(sink_details)); | ||
| 66 | |||
| 67 | std::transform(std::begin(sink_details), std::end(sink_details), std::begin(sink_ids), | ||
| 68 | [](const auto& sink) { return sink.id; }); | ||
| 69 | |||
| 70 | return sink_ids; | ||
| 71 | } | ||
| 72 | |||
| 73 | std::vector<std::string> GetDeviceListForSink(std::string_view sink_id) { | ||
| 74 | return GetSinkDetails(sink_id).list_devices(); | ||
| 75 | } | ||
| 76 | |||
| 77 | std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id) { | ||
| 78 | return GetSinkDetails(sink_id).factory(device_id); | ||
| 79 | } | ||
| 43 | 80 | ||
| 44 | } // namespace AudioCore | 81 | } // namespace AudioCore |
diff --git a/src/audio_core/sink_details.h b/src/audio_core/sink_details.h index 03534b187..bc8786270 100644 --- a/src/audio_core/sink_details.h +++ b/src/audio_core/sink_details.h | |||
| @@ -4,34 +4,21 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <functional> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | 7 | #include <string> |
| 10 | #include <string_view> | 8 | #include <string_view> |
| 11 | #include <utility> | ||
| 12 | #include <vector> | 9 | #include <vector> |
| 13 | 10 | ||
| 14 | namespace AudioCore { | 11 | namespace AudioCore { |
| 15 | 12 | ||
| 16 | class Sink; | 13 | class Sink; |
| 17 | 14 | ||
| 18 | struct SinkDetails { | 15 | /// Retrieves the IDs for all available audio sinks. |
| 19 | using FactoryFn = std::function<std::unique_ptr<Sink>(std::string)>; | 16 | std::vector<const char*> GetSinkIDs(); |
| 20 | using ListDevicesFn = std::function<std::vector<std::string>()>; | ||
| 21 | 17 | ||
| 22 | SinkDetails(const char* id_, FactoryFn factory_, ListDevicesFn list_devices_) | 18 | /// Gets the list of devices for a particular sink identified by the given ID. |
| 23 | : id(id_), factory(std::move(factory_)), list_devices(std::move(list_devices_)) {} | 19 | std::vector<std::string> GetDeviceListForSink(std::string_view sink_id); |
| 24 | 20 | ||
| 25 | /// Name for this sink. | 21 | /// Creates an audio sink identified by the given device ID. |
| 26 | const char* id; | 22 | std::unique_ptr<Sink> CreateSinkFromID(std::string_view sink_id, std::string_view device_id); |
| 27 | /// A method to call to construct an instance of this type of sink. | ||
| 28 | FactoryFn factory; | ||
| 29 | /// A method to call to list available devices. | ||
| 30 | ListDevicesFn list_devices; | ||
| 31 | }; | ||
| 32 | |||
| 33 | extern const std::vector<SinkDetails> g_sink_details; | ||
| 34 | |||
| 35 | const SinkDetails& GetSinkDetails(std::string_view sink_id); | ||
| 36 | 23 | ||
| 37 | } // namespace AudioCore | 24 | } // namespace AudioCore |
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 5753b871a..12f6d0114 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -13,7 +13,7 @@ | |||
| 13 | #include <vector> | 13 | #include <vector> |
| 14 | #ifdef _WIN32 | 14 | #ifdef _WIN32 |
| 15 | #include <share.h> // For _SH_DENYWR | 15 | #include <share.h> // For _SH_DENYWR |
| 16 | #include <windows.h> // For OutputDebugStringA | 16 | #include <windows.h> // For OutputDebugStringW |
| 17 | #else | 17 | #else |
| 18 | #define _SH_DENYWR 0 | 18 | #define _SH_DENYWR 0 |
| 19 | #endif | 19 | #endif |
| @@ -148,7 +148,7 @@ void FileBackend::Write(const Entry& entry) { | |||
| 148 | 148 | ||
| 149 | void DebuggerBackend::Write(const Entry& entry) { | 149 | void DebuggerBackend::Write(const Entry& entry) { |
| 150 | #ifdef _WIN32 | 150 | #ifdef _WIN32 |
| 151 | ::OutputDebugStringA(FormatLogMessage(entry).append(1, '\n').c_str()); | 151 | ::OutputDebugStringW(Common::UTF8ToUTF16W(FormatLogMessage(entry).append(1, '\n')).c_str()); |
| 152 | #endif | 152 | #endif |
| 153 | } | 153 | } |
| 154 | 154 | ||
diff --git a/src/common/thread_queue_list.h b/src/common/thread_queue_list.h index 133122c5f..e7594db68 100644 --- a/src/common/thread_queue_list.h +++ b/src/common/thread_queue_list.h | |||
| @@ -49,6 +49,22 @@ struct ThreadQueueList { | |||
| 49 | return T(); | 49 | return T(); |
| 50 | } | 50 | } |
| 51 | 51 | ||
| 52 | template <typename UnaryPredicate> | ||
| 53 | T get_first_filter(UnaryPredicate filter) const { | ||
| 54 | const Queue* cur = first; | ||
| 55 | while (cur != nullptr) { | ||
| 56 | if (!cur->data.empty()) { | ||
| 57 | for (const auto& item : cur->data) { | ||
| 58 | if (filter(item)) | ||
| 59 | return item; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | cur = cur->next_nonempty; | ||
| 63 | } | ||
| 64 | |||
| 65 | return T(); | ||
| 66 | } | ||
| 67 | |||
| 52 | T pop_first() { | 68 | T pop_first() { |
| 53 | Queue* cur = first; | 69 | Queue* cur = first; |
| 54 | while (cur != nullptr) { | 70 | while (cur != nullptr) { |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 795fabc65..ce7851538 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | #include <thread> | 8 | #include <thread> |
| 9 | #include <utility> | 9 | #include <utility> |
| 10 | 10 | ||
| 11 | #include "common/file_util.h" | ||
| 11 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 12 | #include "common/string_util.h" | 13 | #include "common/string_util.h" |
| 13 | #include "core/arm/exclusive_monitor.h" | 14 | #include "core/arm/exclusive_monitor.h" |
| @@ -40,7 +41,6 @@ namespace Core { | |||
| 40 | 41 | ||
| 41 | /*static*/ System System::s_instance; | 42 | /*static*/ System System::s_instance; |
| 42 | 43 | ||
| 43 | namespace { | ||
| 44 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | 44 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, |
| 45 | const std::string& path) { | 45 | const std::string& path) { |
| 46 | // To account for split 00+01+etc files. | 46 | // To account for split 00+01+etc files. |
| @@ -69,11 +69,13 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 69 | return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); | 69 | return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName()); |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | if (FileUtil::IsDirectory(path)) | ||
| 73 | return vfs->OpenFile(path + "/" + "main", FileSys::Mode::Read); | ||
| 74 | |||
| 72 | return vfs->OpenFile(path, FileSys::Mode::Read); | 75 | return vfs->OpenFile(path, FileSys::Mode::Read); |
| 73 | } | 76 | } |
| 74 | } // Anonymous namespace | ||
| 75 | |||
| 76 | struct System::Impl { | 77 | struct System::Impl { |
| 78 | |||
| 77 | Cpu& CurrentCpuCore() { | 79 | Cpu& CurrentCpuCore() { |
| 78 | return cpu_core_manager.GetCurrentCore(); | 80 | return cpu_core_manager.GetCurrentCore(); |
| 79 | } | 81 | } |
diff --git a/src/core/core.h b/src/core/core.h index be71bd437..71031dfcf 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include <string> | 9 | #include <string> |
| 10 | 10 | ||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/file_sys/vfs_types.h" | ||
| 12 | #include "core/hle/kernel/object.h" | 13 | #include "core/hle/kernel/object.h" |
| 13 | 14 | ||
| 14 | namespace Core::Frontend { | 15 | namespace Core::Frontend { |
| @@ -55,6 +56,9 @@ class TelemetrySession; | |||
| 55 | 56 | ||
| 56 | struct PerfStatsResults; | 57 | struct PerfStatsResults; |
| 57 | 58 | ||
| 59 | FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | ||
| 60 | const std::string& path); | ||
| 61 | |||
| 58 | class System { | 62 | class System { |
| 59 | public: | 63 | public: |
| 60 | System(const System&) = delete; | 64 | System(const System&) = delete; |
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h index 12bb90ec8..6690aa575 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory.h | |||
| @@ -29,8 +29,8 @@ struct Entry { | |||
| 29 | filename[copy_size] = '\0'; | 29 | filename[copy_size] = '\0'; |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | char filename[0x300]; | 32 | char filename[0x301]; |
| 33 | INSERT_PADDING_BYTES(4); | 33 | INSERT_PADDING_BYTES(3); |
| 34 | EntryType type; | 34 | EntryType type; |
| 35 | INSERT_PADDING_BYTES(3); | 35 | INSERT_PADDING_BYTES(3); |
| 36 | u64 file_size; | 36 | u64 file_size; |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index 6b14e08be..61706966e 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -56,6 +56,10 @@ PatchManager::PatchManager(u64 title_id) : title_id(title_id) {} | |||
| 56 | 56 | ||
| 57 | PatchManager::~PatchManager() = default; | 57 | PatchManager::~PatchManager() = default; |
| 58 | 58 | ||
| 59 | u64 PatchManager::GetTitleID() const { | ||
| 60 | return title_id; | ||
| 61 | } | ||
| 62 | |||
| 59 | VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | 63 | VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { |
| 60 | LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); | 64 | LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id); |
| 61 | 65 | ||
| @@ -73,11 +77,15 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 73 | 77 | ||
| 74 | const auto installed = Service::FileSystem::GetUnionContents(); | 78 | const auto installed = Service::FileSystem::GetUnionContents(); |
| 75 | 79 | ||
| 80 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 81 | const auto update_disabled = | ||
| 82 | std::find(disabled.begin(), disabled.end(), "Update") != disabled.end(); | ||
| 83 | |||
| 76 | // Game Updates | 84 | // Game Updates |
| 77 | const auto update_tid = GetUpdateTitleID(title_id); | 85 | const auto update_tid = GetUpdateTitleID(title_id); |
| 78 | const auto update = installed.GetEntry(update_tid, ContentRecordType::Program); | 86 | const auto update = installed.GetEntry(update_tid, ContentRecordType::Program); |
| 79 | 87 | ||
| 80 | if (update != nullptr && update->GetExeFS() != nullptr && | 88 | if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr && |
| 81 | update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | 89 | update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { |
| 82 | LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", | 90 | LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully", |
| 83 | FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); | 91 | FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); |
| @@ -95,6 +103,9 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 95 | std::vector<VirtualDir> layers; | 103 | std::vector<VirtualDir> layers; |
| 96 | layers.reserve(patch_dirs.size() + 1); | 104 | layers.reserve(patch_dirs.size() + 1); |
| 97 | for (const auto& subdir : patch_dirs) { | 105 | for (const auto& subdir : patch_dirs) { |
| 106 | if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) | ||
| 107 | continue; | ||
| 108 | |||
| 98 | auto exefs_dir = subdir->GetSubdirectory("exefs"); | 109 | auto exefs_dir = subdir->GetSubdirectory("exefs"); |
| 99 | if (exefs_dir != nullptr) | 110 | if (exefs_dir != nullptr) |
| 100 | layers.push_back(std::move(exefs_dir)); | 111 | layers.push_back(std::move(exefs_dir)); |
| @@ -111,11 +122,16 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 111 | return exefs; | 122 | return exefs; |
| 112 | } | 123 | } |
| 113 | 124 | ||
| 114 | static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, | 125 | std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualDir>& patch_dirs, |
| 115 | const std::string& build_id) { | 126 | const std::string& build_id) const { |
| 127 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 128 | |||
| 116 | std::vector<VirtualFile> out; | 129 | std::vector<VirtualFile> out; |
| 117 | out.reserve(patch_dirs.size()); | 130 | out.reserve(patch_dirs.size()); |
| 118 | for (const auto& subdir : patch_dirs) { | 131 | for (const auto& subdir : patch_dirs) { |
| 132 | if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) | ||
| 133 | continue; | ||
| 134 | |||
| 119 | auto exefs_dir = subdir->GetSubdirectory("exefs"); | 135 | auto exefs_dir = subdir->GetSubdirectory("exefs"); |
| 120 | if (exefs_dir != nullptr) { | 136 | if (exefs_dir != nullptr) { |
| 121 | for (const auto& file : exefs_dir->GetFiles()) { | 137 | for (const auto& file : exefs_dir->GetFiles()) { |
| @@ -228,6 +244,7 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t | |||
| 228 | return; | 244 | return; |
| 229 | } | 245 | } |
| 230 | 246 | ||
| 247 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 231 | auto patch_dirs = load_dir->GetSubdirectories(); | 248 | auto patch_dirs = load_dir->GetSubdirectories(); |
| 232 | std::sort(patch_dirs.begin(), patch_dirs.end(), | 249 | std::sort(patch_dirs.begin(), patch_dirs.end(), |
| 233 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 250 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); |
| @@ -237,6 +254,9 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t | |||
| 237 | layers.reserve(patch_dirs.size() + 1); | 254 | layers.reserve(patch_dirs.size() + 1); |
| 238 | layers_ext.reserve(patch_dirs.size() + 1); | 255 | layers_ext.reserve(patch_dirs.size() + 1); |
| 239 | for (const auto& subdir : patch_dirs) { | 256 | for (const auto& subdir : patch_dirs) { |
| 257 | if (std::find(disabled.begin(), disabled.end(), subdir->GetName()) != disabled.end()) | ||
| 258 | continue; | ||
| 259 | |||
| 240 | auto romfs_dir = subdir->GetSubdirectory("romfs"); | 260 | auto romfs_dir = subdir->GetSubdirectory("romfs"); |
| 241 | if (romfs_dir != nullptr) | 261 | if (romfs_dir != nullptr) |
| 242 | layers.push_back(std::move(romfs_dir)); | 262 | layers.push_back(std::move(romfs_dir)); |
| @@ -266,13 +286,12 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t | |||
| 266 | VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, | 286 | VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type, |
| 267 | VirtualFile update_raw) const { | 287 | VirtualFile update_raw) const { |
| 268 | const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}", | 288 | const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}", |
| 269 | title_id, static_cast<u8>(type)) | 289 | title_id, static_cast<u8>(type)); |
| 270 | .c_str(); | ||
| 271 | 290 | ||
| 272 | if (type == ContentRecordType::Program || type == ContentRecordType::Data) | 291 | if (type == ContentRecordType::Program || type == ContentRecordType::Data) |
| 273 | LOG_INFO(Loader, log_string); | 292 | LOG_INFO(Loader, "{}", log_string); |
| 274 | else | 293 | else |
| 275 | LOG_DEBUG(Loader, log_string); | 294 | LOG_DEBUG(Loader, "{}", log_string); |
| 276 | 295 | ||
| 277 | if (romfs == nullptr) | 296 | if (romfs == nullptr) |
| 278 | return romfs; | 297 | return romfs; |
| @@ -282,7 +301,12 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content | |||
| 282 | // Game Updates | 301 | // Game Updates |
| 283 | const auto update_tid = GetUpdateTitleID(title_id); | 302 | const auto update_tid = GetUpdateTitleID(title_id); |
| 284 | const auto update = installed.GetEntryRaw(update_tid, type); | 303 | const auto update = installed.GetEntryRaw(update_tid, type); |
| 285 | if (update != nullptr) { | 304 | |
| 305 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 306 | const auto update_disabled = | ||
| 307 | std::find(disabled.begin(), disabled.end(), "Update") != disabled.end(); | ||
| 308 | |||
| 309 | if (!update_disabled && update != nullptr) { | ||
| 286 | const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset); | 310 | const auto new_nca = std::make_shared<NCA>(update, romfs, ivfc_offset); |
| 287 | if (new_nca->GetStatus() == Loader::ResultStatus::Success && | 311 | if (new_nca->GetStatus() == Loader::ResultStatus::Success && |
| 288 | new_nca->GetRomFS() != nullptr) { | 312 | new_nca->GetRomFS() != nullptr) { |
| @@ -290,7 +314,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content | |||
| 290 | FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); | 314 | FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0))); |
| 291 | romfs = new_nca->GetRomFS(); | 315 | romfs = new_nca->GetRomFS(); |
| 292 | } | 316 | } |
| 293 | } else if (update_raw != nullptr) { | 317 | } else if (!update_disabled && update_raw != nullptr) { |
| 294 | const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset); | 318 | const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset); |
| 295 | if (new_nca->GetStatus() == Loader::ResultStatus::Success && | 319 | if (new_nca->GetStatus() == Loader::ResultStatus::Success && |
| 296 | new_nca->GetRomFS() != nullptr) { | 320 | new_nca->GetRomFS() != nullptr) { |
| @@ -320,25 +344,30 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 320 | VirtualFile update_raw) const { | 344 | VirtualFile update_raw) const { |
| 321 | std::map<std::string, std::string, std::less<>> out; | 345 | std::map<std::string, std::string, std::less<>> out; |
| 322 | const auto installed = Service::FileSystem::GetUnionContents(); | 346 | const auto installed = Service::FileSystem::GetUnionContents(); |
| 347 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 323 | 348 | ||
| 324 | // Game Updates | 349 | // Game Updates |
| 325 | const auto update_tid = GetUpdateTitleID(title_id); | 350 | const auto update_tid = GetUpdateTitleID(title_id); |
| 326 | PatchManager update{update_tid}; | 351 | PatchManager update{update_tid}; |
| 327 | auto [nacp, discard_icon_file] = update.GetControlMetadata(); | 352 | auto [nacp, discard_icon_file] = update.GetControlMetadata(); |
| 328 | 353 | ||
| 354 | const auto update_disabled = | ||
| 355 | std::find(disabled.begin(), disabled.end(), "Update") != disabled.end(); | ||
| 356 | const auto update_label = update_disabled ? "[D] Update" : "Update"; | ||
| 357 | |||
| 329 | if (nacp != nullptr) { | 358 | if (nacp != nullptr) { |
| 330 | out.insert_or_assign("Update", nacp->GetVersionString()); | 359 | out.insert_or_assign(update_label, nacp->GetVersionString()); |
| 331 | } else { | 360 | } else { |
| 332 | if (installed.HasEntry(update_tid, ContentRecordType::Program)) { | 361 | if (installed.HasEntry(update_tid, ContentRecordType::Program)) { |
| 333 | const auto meta_ver = installed.GetEntryVersion(update_tid); | 362 | const auto meta_ver = installed.GetEntryVersion(update_tid); |
| 334 | if (meta_ver.value_or(0) == 0) { | 363 | if (meta_ver.value_or(0) == 0) { |
| 335 | out.insert_or_assign("Update", ""); | 364 | out.insert_or_assign(update_label, ""); |
| 336 | } else { | 365 | } else { |
| 337 | out.insert_or_assign( | 366 | out.insert_or_assign( |
| 338 | "Update", FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements)); | 367 | update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements)); |
| 339 | } | 368 | } |
| 340 | } else if (update_raw != nullptr) { | 369 | } else if (update_raw != nullptr) { |
| 341 | out.insert_or_assign("Update", "PACKED"); | 370 | out.insert_or_assign(update_label, "PACKED"); |
| 342 | } | 371 | } |
| 343 | } | 372 | } |
| 344 | 373 | ||
| @@ -378,7 +407,9 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 378 | if (types.empty()) | 407 | if (types.empty()) |
| 379 | continue; | 408 | continue; |
| 380 | 409 | ||
| 381 | out.insert_or_assign(mod->GetName(), types); | 410 | const auto mod_disabled = |
| 411 | std::find(disabled.begin(), disabled.end(), mod->GetName()) != disabled.end(); | ||
| 412 | out.insert_or_assign(mod_disabled ? "[D] " + mod->GetName() : mod->GetName(), types); | ||
| 382 | } | 413 | } |
| 383 | } | 414 | } |
| 384 | 415 | ||
| @@ -401,7 +432,9 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 401 | 432 | ||
| 402 | list += fmt::format("{}", dlc_match.back().title_id & 0x7FF); | 433 | list += fmt::format("{}", dlc_match.back().title_id & 0x7FF); |
| 403 | 434 | ||
| 404 | out.insert_or_assign("DLC", std::move(list)); | 435 | const auto dlc_disabled = |
| 436 | std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end(); | ||
| 437 | out.insert_or_assign(dlc_disabled ? "[D] DLC" : "DLC", std::move(list)); | ||
| 405 | } | 438 | } |
| 406 | 439 | ||
| 407 | return out; | 440 | return out; |
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h index 7d168837f..b8a1652fd 100644 --- a/src/core/file_sys/patch_manager.h +++ b/src/core/file_sys/patch_manager.h | |||
| @@ -30,6 +30,8 @@ public: | |||
| 30 | explicit PatchManager(u64 title_id); | 30 | explicit PatchManager(u64 title_id); |
| 31 | ~PatchManager(); | 31 | ~PatchManager(); |
| 32 | 32 | ||
| 33 | u64 GetTitleID() const; | ||
| 34 | |||
| 33 | // Currently tracked ExeFS patches: | 35 | // Currently tracked ExeFS patches: |
| 34 | // - Game Updates | 36 | // - Game Updates |
| 35 | VirtualDir PatchExeFS(VirtualDir exefs) const; | 37 | VirtualDir PatchExeFS(VirtualDir exefs) const; |
| @@ -63,6 +65,9 @@ public: | |||
| 63 | std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; | 65 | std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const; |
| 64 | 66 | ||
| 65 | private: | 67 | private: |
| 68 | std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs, | ||
| 69 | const std::string& build_id) const; | ||
| 70 | |||
| 66 | u64 title_id; | 71 | u64 title_id; |
| 67 | }; | 72 | }; |
| 68 | 73 | ||
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 5434f2149..bd50fedc7 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -18,7 +18,11 @@ std::string SaveDataDescriptor::DebugInfo() const { | |||
| 18 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); | 18 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} | 21 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { |
| 22 | // Delete all temporary storages | ||
| 23 | // On hardware, it is expected that temporary storage be empty at first use. | ||
| 24 | dir->DeleteSubdirectoryRecursive("temp"); | ||
| 25 | } | ||
| 22 | 26 | ||
| 23 | SaveDataFactory::~SaveDataFactory() = default; | 27 | SaveDataFactory::~SaveDataFactory() = default; |
| 24 | 28 | ||
| @@ -120,6 +124,8 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ | |||
| 120 | case SaveDataType::TemporaryStorage: | 124 | case SaveDataType::TemporaryStorage: |
| 121 | return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], | 125 | return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], |
| 122 | title_id); | 126 | title_id); |
| 127 | case SaveDataType::CacheStorage: | ||
| 128 | return fmt::format("{}save/cache/{:016X}", out, title_id); | ||
| 123 | default: | 129 | default: |
| 124 | ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); | 130 | ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); |
| 125 | } | 131 | } |
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index 2a0088040..bd4919610 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h | |||
| @@ -17,8 +17,10 @@ namespace FileSys { | |||
| 17 | enum class SaveDataSpaceId : u8 { | 17 | enum class SaveDataSpaceId : u8 { |
| 18 | NandSystem = 0, | 18 | NandSystem = 0, |
| 19 | NandUser = 1, | 19 | NandUser = 1, |
| 20 | SdCard = 2, | 20 | SdCardSystem = 2, |
| 21 | TemporaryStorage = 3, | 21 | TemporaryStorage = 3, |
| 22 | SdCardUser = 4, | ||
| 23 | ProperSystem = 100, | ||
| 22 | }; | 24 | }; |
| 23 | 25 | ||
| 24 | enum class SaveDataType : u8 { | 26 | enum class SaveDataType : u8 { |
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 211bf6686..5356a4a3f 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp | |||
| @@ -66,6 +66,7 @@ ResultCode Process::ClearSignalState() { | |||
| 66 | 66 | ||
| 67 | void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { | 67 | void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { |
| 68 | program_id = metadata.GetTitleID(); | 68 | program_id = metadata.GetTitleID(); |
| 69 | ideal_processor = metadata.GetMainThreadCore(); | ||
| 69 | is_64bit_process = metadata.Is64BitProgram(); | 70 | is_64bit_process = metadata.Is64BitProgram(); |
| 70 | vm_manager.Reset(metadata.GetAddressSpaceType()); | 71 | vm_manager.Reset(metadata.GetAddressSpaceType()); |
| 71 | } | 72 | } |
| @@ -149,7 +150,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { | |||
| 149 | vm_manager | 150 | vm_manager |
| 150 | .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size, | 151 | .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size, |
| 151 | std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, | 152 | std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, |
| 152 | MemoryState::Mapped) | 153 | MemoryState::Stack) |
| 153 | .Unwrap(); | 154 | .Unwrap(); |
| 154 | 155 | ||
| 155 | vm_manager.LogLayout(); | 156 | vm_manager.LogLayout(); |
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index bcb9ac4b8..459eedfa6 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h | |||
| @@ -262,8 +262,7 @@ public: | |||
| 262 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); | 262 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); |
| 263 | ResultCode HeapFree(VAddr target, u32 size); | 263 | ResultCode HeapFree(VAddr target, u32 size); |
| 264 | 264 | ||
| 265 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, | 265 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state); |
| 266 | MemoryState state = MemoryState::Mapped); | ||
| 267 | 266 | ||
| 268 | ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); | 267 | ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size); |
| 269 | 268 | ||
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp index 5a5f4cef1..df4d6cf0a 100644 --- a/src/core/hle/kernel/scheduler.cpp +++ b/src/core/hle/kernel/scheduler.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "core/arm/arm_interface.h" | 10 | #include "core/arm/arm_interface.h" |
| 11 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/core_cpu.h" | ||
| 12 | #include "core/core_timing.h" | 13 | #include "core/core_timing.h" |
| 13 | #include "core/hle/kernel/kernel.h" | 14 | #include "core/hle/kernel/kernel.h" |
| 14 | #include "core/hle/kernel/process.h" | 15 | #include "core/hle/kernel/process.h" |
| @@ -179,4 +180,69 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) { | |||
| 179 | ready_queue.prepare(priority); | 180 | ready_queue.prepare(priority); |
| 180 | } | 181 | } |
| 181 | 182 | ||
| 183 | Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const { | ||
| 184 | std::lock_guard<std::mutex> lock(scheduler_mutex); | ||
| 185 | |||
| 186 | const u32 mask = 1U << core; | ||
| 187 | return ready_queue.get_first_filter([mask, maximum_priority](Thread const* thread) { | ||
| 188 | return (thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority; | ||
| 189 | }); | ||
| 190 | } | ||
| 191 | |||
| 192 | void Scheduler::YieldWithoutLoadBalancing(Thread* thread) { | ||
| 193 | ASSERT(thread != nullptr); | ||
| 194 | // Avoid yielding if the thread isn't even running. | ||
| 195 | ASSERT(thread->GetStatus() == ThreadStatus::Running); | ||
| 196 | |||
| 197 | // Sanity check that the priority is valid | ||
| 198 | ASSERT(thread->GetPriority() < THREADPRIO_COUNT); | ||
| 199 | |||
| 200 | // Yield this thread -- sleep for zero time and force reschedule to different thread | ||
| 201 | WaitCurrentThread_Sleep(); | ||
| 202 | GetCurrentThread()->WakeAfterDelay(0); | ||
| 203 | } | ||
| 204 | |||
| 205 | void Scheduler::YieldWithLoadBalancing(Thread* thread) { | ||
| 206 | ASSERT(thread != nullptr); | ||
| 207 | const auto priority = thread->GetPriority(); | ||
| 208 | const auto core = static_cast<u32>(thread->GetProcessorID()); | ||
| 209 | |||
| 210 | // Avoid yielding if the thread isn't even running. | ||
| 211 | ASSERT(thread->GetStatus() == ThreadStatus::Running); | ||
| 212 | |||
| 213 | // Sanity check that the priority is valid | ||
| 214 | ASSERT(priority < THREADPRIO_COUNT); | ||
| 215 | |||
| 216 | // Sleep for zero time to be able to force reschedule to different thread | ||
| 217 | WaitCurrentThread_Sleep(); | ||
| 218 | GetCurrentThread()->WakeAfterDelay(0); | ||
| 219 | |||
| 220 | Thread* suggested_thread = nullptr; | ||
| 221 | |||
| 222 | // Search through all of the cpu cores (except this one) for a suggested thread. | ||
| 223 | // Take the first non-nullptr one | ||
| 224 | for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) { | ||
| 225 | const auto res = | ||
| 226 | Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread( | ||
| 227 | core, priority); | ||
| 228 | |||
| 229 | // If scheduler provides a suggested thread | ||
| 230 | if (res != nullptr) { | ||
| 231 | // And its better than the current suggested thread (or is the first valid one) | ||
| 232 | if (suggested_thread == nullptr || | ||
| 233 | suggested_thread->GetPriority() > res->GetPriority()) { | ||
| 234 | suggested_thread = res; | ||
| 235 | } | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | // If a suggested thread was found, queue that for this core | ||
| 240 | if (suggested_thread != nullptr) | ||
| 241 | suggested_thread->ChangeCore(core, suggested_thread->GetAffinityMask()); | ||
| 242 | } | ||
| 243 | |||
| 244 | void Scheduler::YieldAndWaitForLoadBalancing(Thread* thread) { | ||
| 245 | UNIMPLEMENTED_MSG("Wait for load balancing thread yield type is not implemented!"); | ||
| 246 | } | ||
| 247 | |||
| 182 | } // namespace Kernel | 248 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h index c63032b7d..97ced4dfc 100644 --- a/src/core/hle/kernel/scheduler.h +++ b/src/core/hle/kernel/scheduler.h | |||
| @@ -51,6 +51,75 @@ public: | |||
| 51 | /// Sets the priority of a thread in the scheduler | 51 | /// Sets the priority of a thread in the scheduler |
| 52 | void SetThreadPriority(Thread* thread, u32 priority); | 52 | void SetThreadPriority(Thread* thread, u32 priority); |
| 53 | 53 | ||
| 54 | /// Gets the next suggested thread for load balancing | ||
| 55 | Thread* GetNextSuggestedThread(u32 core, u32 minimum_priority) const; | ||
| 56 | |||
| 57 | /** | ||
| 58 | * YieldWithoutLoadBalancing -- analogous to normal yield on a system | ||
| 59 | * Moves the thread to the end of the ready queue for its priority, and then reschedules the | ||
| 60 | * system to the new head of the queue. | ||
| 61 | * | ||
| 62 | * Example (Single Core -- but can be extrapolated to multi): | ||
| 63 | * ready_queue[prio=0]: ThreadA, ThreadB, ThreadC (->exec order->) | ||
| 64 | * Currently Running: ThreadR | ||
| 65 | * | ||
| 66 | * ThreadR calls YieldWithoutLoadBalancing | ||
| 67 | * | ||
| 68 | * ThreadR is moved to the end of ready_queue[prio=0]: | ||
| 69 | * ready_queue[prio=0]: ThreadA, ThreadB, ThreadC, ThreadR (->exec order->) | ||
| 70 | * Currently Running: Nothing | ||
| 71 | * | ||
| 72 | * System is rescheduled (ThreadA is popped off of queue): | ||
| 73 | * ready_queue[prio=0]: ThreadB, ThreadC, ThreadR (->exec order->) | ||
| 74 | * Currently Running: ThreadA | ||
| 75 | * | ||
| 76 | * If the queue is empty at time of call, no yielding occurs. This does not cross between cores | ||
| 77 | * or priorities at all. | ||
| 78 | */ | ||
| 79 | void YieldWithoutLoadBalancing(Thread* thread); | ||
| 80 | |||
| 81 | /** | ||
| 82 | * YieldWithLoadBalancing -- yield but with better selection of the new running thread | ||
| 83 | * Moves the current thread to the end of the ready queue for its priority, then selects a | ||
| 84 | * 'suggested thread' (a thread on a different core that could run on this core) from the | ||
| 85 | * scheduler, changes its core, and reschedules the current core to that thread. | ||
| 86 | * | ||
| 87 | * Example (Dual Core -- can be extrapolated to Quad Core, this is just normal yield if it were | ||
| 88 | * single core): | ||
| 89 | * ready_queue[core=0][prio=0]: ThreadA, ThreadB (affinities not pictured as irrelevant | ||
| 90 | * ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only] | ||
| 91 | * Currently Running: ThreadQ on Core 0 || ThreadP on Core 1 | ||
| 92 | * | ||
| 93 | * ThreadQ calls YieldWithLoadBalancing | ||
| 94 | * | ||
| 95 | * ThreadQ is moved to the end of ready_queue[core=0][prio=0]: | ||
| 96 | * ready_queue[core=0][prio=0]: ThreadA, ThreadB | ||
| 97 | * ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only] | ||
| 98 | * Currently Running: ThreadQ on Core 0 || ThreadP on Core 1 | ||
| 99 | * | ||
| 100 | * A list of suggested threads for each core is compiled | ||
| 101 | * Suggested Threads: {ThreadC on Core 1} | ||
| 102 | * If this were quad core (as the switch is), there could be between 0 and 3 threads in this | ||
| 103 | * list. If there are more than one, the thread is selected by highest prio. | ||
| 104 | * | ||
| 105 | * ThreadC is core changed to Core 0: | ||
| 106 | * ready_queue[core=0][prio=0]: ThreadC, ThreadA, ThreadB, ThreadQ | ||
| 107 | * ready_queue[core=1][prio=0]: ThreadD | ||
| 108 | * Currently Running: None on Core 0 || ThreadP on Core 1 | ||
| 109 | * | ||
| 110 | * System is rescheduled (ThreadC is popped off of queue): | ||
| 111 | * ready_queue[core=0][prio=0]: ThreadA, ThreadB, ThreadQ | ||
| 112 | * ready_queue[core=1][prio=0]: ThreadD | ||
| 113 | * Currently Running: ThreadC on Core 0 || ThreadP on Core 1 | ||
| 114 | * | ||
| 115 | * If no suggested threads can be found this will behave just as normal yield. If there are | ||
| 116 | * multiple candidates for the suggested thread on a core, the highest prio is taken. | ||
| 117 | */ | ||
| 118 | void YieldWithLoadBalancing(Thread* thread); | ||
| 119 | |||
| 120 | /// Currently unknown -- asserts as unimplemented on call | ||
| 121 | void YieldAndWaitForLoadBalancing(Thread* thread); | ||
| 122 | |||
| 54 | /// Returns a list of all threads managed by the scheduler | 123 | /// Returns a list of all threads managed by the scheduler |
| 55 | const std::vector<SharedPtr<Thread>>& GetThreadList() const { | 124 | const std::vector<SharedPtr<Thread>>& GetThreadList() const { |
| 56 | return thread_list; | 125 | return thread_list; |
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 0494581f5..22d0c1dd5 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp | |||
| @@ -17,13 +17,13 @@ namespace Kernel { | |||
| 17 | SharedMemory::SharedMemory(KernelCore& kernel) : Object{kernel} {} | 17 | SharedMemory::SharedMemory(KernelCore& kernel) : Object{kernel} {} |
| 18 | SharedMemory::~SharedMemory() = default; | 18 | SharedMemory::~SharedMemory() = default; |
| 19 | 19 | ||
| 20 | SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Process> owner_process, | 20 | SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_process, u64 size, |
| 21 | u64 size, MemoryPermission permissions, | 21 | MemoryPermission permissions, |
| 22 | MemoryPermission other_permissions, VAddr address, | 22 | MemoryPermission other_permissions, VAddr address, |
| 23 | MemoryRegion region, std::string name) { | 23 | MemoryRegion region, std::string name) { |
| 24 | SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel)); | 24 | SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel)); |
| 25 | 25 | ||
| 26 | shared_memory->owner_process = std::move(owner_process); | 26 | shared_memory->owner_process = owner_process; |
| 27 | shared_memory->name = std::move(name); | 27 | shared_memory->name = std::move(name); |
| 28 | shared_memory->size = size; | 28 | shared_memory->size = size; |
| 29 | shared_memory->permissions = permissions; | 29 | shared_memory->permissions = permissions; |
| @@ -39,15 +39,15 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce | |||
| 39 | shared_memory->backing_block.get()); | 39 | shared_memory->backing_block.get()); |
| 40 | } | 40 | } |
| 41 | } else { | 41 | } else { |
| 42 | auto& vm_manager = shared_memory->owner_process->VMManager(); | 42 | const auto& vm_manager = shared_memory->owner_process->VMManager(); |
| 43 | 43 | ||
| 44 | // The memory is already available and mapped in the owner process. | 44 | // The memory is already available and mapped in the owner process. |
| 45 | auto vma = vm_manager.FindVMA(address); | 45 | const auto vma = vm_manager.FindVMA(address); |
| 46 | ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address"); | 46 | ASSERT_MSG(vm_manager.IsValidHandle(vma), "Invalid memory address"); |
| 47 | ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); | 47 | ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); |
| 48 | 48 | ||
| 49 | // The returned VMA might be a bigger one encompassing the desired address. | 49 | // The returned VMA might be a bigger one encompassing the desired address. |
| 50 | auto vma_offset = address - vma->first; | 50 | const auto vma_offset = address - vma->first; |
| 51 | ASSERT_MSG(vma_offset + size <= vma->second.size, | 51 | ASSERT_MSG(vma_offset + size <= vma->second.size, |
| 52 | "Shared memory exceeds bounds of mapped block"); | 52 | "Shared memory exceeds bounds of mapped block"); |
| 53 | 53 | ||
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 0b48db699..dab2a6bea 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h | |||
| @@ -45,8 +45,8 @@ public: | |||
| 45 | * linear heap. | 45 | * linear heap. |
| 46 | * @param name Optional object name, used for debugging purposes. | 46 | * @param name Optional object name, used for debugging purposes. |
| 47 | */ | 47 | */ |
| 48 | static SharedPtr<SharedMemory> Create(KernelCore& kernel, SharedPtr<Process> owner_process, | 48 | static SharedPtr<SharedMemory> Create(KernelCore& kernel, Process* owner_process, u64 size, |
| 49 | u64 size, MemoryPermission permissions, | 49 | MemoryPermission permissions, |
| 50 | MemoryPermission other_permissions, VAddr address = 0, | 50 | MemoryPermission other_permissions, VAddr address = 0, |
| 51 | MemoryRegion region = MemoryRegion::BASE, | 51 | MemoryRegion region = MemoryRegion::BASE, |
| 52 | std::string name = "Unknown"); | 52 | std::string name = "Unknown"); |
| @@ -139,7 +139,7 @@ private: | |||
| 139 | /// Permission restrictions applied to other processes mapping the block. | 139 | /// Permission restrictions applied to other processes mapping the block. |
| 140 | MemoryPermission other_permissions{}; | 140 | MemoryPermission other_permissions{}; |
| 141 | /// Process that created this shared memory block. | 141 | /// Process that created this shared memory block. |
| 142 | SharedPtr<Process> owner_process; | 142 | Process* owner_process; |
| 143 | /// Address of shared memory block in the owner process if specified. | 143 | /// Address of shared memory block in the owner process if specified. |
| 144 | VAddr base_address = 0; | 144 | VAddr base_address = 0; |
| 145 | /// Name of shared memory object. | 145 | /// Name of shared memory object. |
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index 84df2040e..348a22904 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #include "core/hle/lock.h" | 35 | #include "core/hle/lock.h" |
| 36 | #include "core/hle/result.h" | 36 | #include "core/hle/result.h" |
| 37 | #include "core/hle/service/service.h" | 37 | #include "core/hle/service/service.h" |
| 38 | #include "core/memory.h" | ||
| 38 | 39 | ||
| 39 | namespace Kernel { | 40 | namespace Kernel { |
| 40 | namespace { | 41 | namespace { |
| @@ -239,7 +240,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) { | |||
| 239 | } | 240 | } |
| 240 | 241 | ||
| 241 | const VMManager::VMAHandle iter = vm_manager.FindVMA(addr); | 242 | const VMManager::VMAHandle iter = vm_manager.FindVMA(addr); |
| 242 | if (iter == vm_manager.vma_map.end()) { | 243 | if (!vm_manager.IsValidHandle(iter)) { |
| 243 | LOG_ERROR(Kernel_SVC, "Unable to find VMA for address=0x{:016X}", addr); | 244 | LOG_ERROR(Kernel_SVC, "Unable to find VMA for address=0x{:016X}", addr); |
| 244 | return ERR_INVALID_ADDRESS_STATE; | 245 | return ERR_INVALID_ADDRESS_STATE; |
| 245 | } | 246 | } |
| @@ -273,7 +274,7 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) { | |||
| 273 | return result; | 274 | return result; |
| 274 | } | 275 | } |
| 275 | 276 | ||
| 276 | return current_process->MirrorMemory(dst_addr, src_addr, size); | 277 | return current_process->MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack); |
| 277 | } | 278 | } |
| 278 | 279 | ||
| 279 | /// Unmaps a region that was previously mapped with svcMapMemory | 280 | /// Unmaps a region that was previously mapped with svcMapMemory |
| @@ -1066,10 +1067,9 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 | |||
| 1066 | return shared_memory->Unmap(*current_process, addr); | 1067 | return shared_memory->Unmap(*current_process, addr); |
| 1067 | } | 1068 | } |
| 1068 | 1069 | ||
| 1069 | /// Query process memory | 1070 | static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address, |
| 1070 | static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/, | 1071 | Handle process_handle, VAddr address) { |
| 1071 | Handle process_handle, u64 addr) { | 1072 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address); |
| 1072 | LOG_TRACE(Kernel_SVC, "called process=0x{:08X} addr={:X}", process_handle, addr); | ||
| 1073 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1073 | const auto& handle_table = Core::CurrentProcess()->GetHandleTable(); |
| 1074 | SharedPtr<Process> process = handle_table.Get<Process>(process_handle); | 1074 | SharedPtr<Process> process = handle_table.Get<Process>(process_handle); |
| 1075 | if (!process) { | 1075 | if (!process) { |
| @@ -1077,26 +1077,34 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i | |||
| 1077 | process_handle); | 1077 | process_handle); |
| 1078 | return ERR_INVALID_HANDLE; | 1078 | return ERR_INVALID_HANDLE; |
| 1079 | } | 1079 | } |
| 1080 | auto vma = process->VMManager().FindVMA(addr); | 1080 | |
| 1081 | memory_info->attributes = 0; | 1081 | const auto& vm_manager = process->VMManager(); |
| 1082 | if (vma == process->VMManager().vma_map.end()) { | 1082 | const MemoryInfo memory_info = vm_manager.QueryMemory(address); |
| 1083 | memory_info->base_address = 0; | 1083 | |
| 1084 | memory_info->permission = static_cast<u32>(VMAPermission::None); | 1084 | Memory::Write64(memory_info_address, memory_info.base_address); |
| 1085 | memory_info->size = 0; | 1085 | Memory::Write64(memory_info_address + 8, memory_info.size); |
| 1086 | memory_info->type = static_cast<u32>(MemoryState::Unmapped); | 1086 | Memory::Write32(memory_info_address + 16, memory_info.state); |
| 1087 | } else { | 1087 | Memory::Write32(memory_info_address + 20, memory_info.attributes); |
| 1088 | memory_info->base_address = vma->second.base; | 1088 | Memory::Write32(memory_info_address + 24, memory_info.permission); |
| 1089 | memory_info->permission = static_cast<u32>(vma->second.permissions); | 1089 | Memory::Write32(memory_info_address + 32, memory_info.ipc_ref_count); |
| 1090 | memory_info->size = vma->second.size; | 1090 | Memory::Write32(memory_info_address + 28, memory_info.device_ref_count); |
| 1091 | memory_info->type = static_cast<u32>(vma->second.meminfo_state); | 1091 | Memory::Write32(memory_info_address + 36, 0); |
| 1092 | } | 1092 | |
| 1093 | // Page info appears to be currently unused by the kernel and is always set to zero. | ||
| 1094 | Memory::Write32(page_info_address, 0); | ||
| 1095 | |||
| 1093 | return RESULT_SUCCESS; | 1096 | return RESULT_SUCCESS; |
| 1094 | } | 1097 | } |
| 1095 | 1098 | ||
| 1096 | /// Query memory | 1099 | static ResultCode QueryMemory(VAddr memory_info_address, VAddr page_info_address, |
| 1097 | static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAddr addr) { | 1100 | VAddr query_address) { |
| 1098 | LOG_TRACE(Kernel_SVC, "called, addr={:X}", addr); | 1101 | LOG_TRACE(Kernel_SVC, |
| 1099 | return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr); | 1102 | "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, " |
| 1103 | "query_address=0x{:016X}", | ||
| 1104 | memory_info_address, page_info_address, query_address); | ||
| 1105 | |||
| 1106 | return QueryProcessMemory(memory_info_address, page_info_address, CurrentProcess, | ||
| 1107 | query_address); | ||
| 1100 | } | 1108 | } |
| 1101 | 1109 | ||
| 1102 | /// Exits the current process | 1110 | /// Exits the current process |
| @@ -1200,18 +1208,38 @@ static void ExitThread() { | |||
| 1200 | static void SleepThread(s64 nanoseconds) { | 1208 | static void SleepThread(s64 nanoseconds) { |
| 1201 | LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); | 1209 | LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds); |
| 1202 | 1210 | ||
| 1203 | // Don't attempt to yield execution if there are no available threads to run, | 1211 | enum class SleepType : s64 { |
| 1204 | // this way we avoid a useless reschedule to the idle thread. | 1212 | YieldWithoutLoadBalancing = 0, |
| 1205 | if (nanoseconds == 0 && !Core::System::GetInstance().CurrentScheduler().HaveReadyThreads()) | 1213 | YieldWithLoadBalancing = -1, |
| 1206 | return; | 1214 | YieldAndWaitForLoadBalancing = -2, |
| 1215 | }; | ||
| 1207 | 1216 | ||
| 1208 | // Sleep current thread and check for next thread to schedule | 1217 | if (nanoseconds <= 0) { |
| 1209 | WaitCurrentThread_Sleep(); | 1218 | auto& scheduler{Core::System::GetInstance().CurrentScheduler()}; |
| 1219 | switch (static_cast<SleepType>(nanoseconds)) { | ||
| 1220 | case SleepType::YieldWithoutLoadBalancing: | ||
| 1221 | scheduler.YieldWithoutLoadBalancing(GetCurrentThread()); | ||
| 1222 | break; | ||
| 1223 | case SleepType::YieldWithLoadBalancing: | ||
| 1224 | scheduler.YieldWithLoadBalancing(GetCurrentThread()); | ||
| 1225 | break; | ||
| 1226 | case SleepType::YieldAndWaitForLoadBalancing: | ||
| 1227 | scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread()); | ||
| 1228 | break; | ||
| 1229 | default: | ||
| 1230 | UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds); | ||
| 1231 | } | ||
| 1232 | } else { | ||
| 1233 | // Sleep current thread and check for next thread to schedule | ||
| 1234 | WaitCurrentThread_Sleep(); | ||
| 1210 | 1235 | ||
| 1211 | // Create an event to wake the thread up after the specified nanosecond delay has passed | 1236 | // Create an event to wake the thread up after the specified nanosecond delay has passed |
| 1212 | GetCurrentThread()->WakeAfterDelay(nanoseconds); | 1237 | GetCurrentThread()->WakeAfterDelay(nanoseconds); |
| 1238 | } | ||
| 1213 | 1239 | ||
| 1214 | Core::System::GetInstance().PrepareReschedule(); | 1240 | // Reschedule all CPU cores |
| 1241 | for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i) | ||
| 1242 | Core::System::GetInstance().CpuCore(i).PrepareReschedule(); | ||
| 1215 | } | 1243 | } |
| 1216 | 1244 | ||
| 1217 | /// Wait process wide key atomic | 1245 | /// Wait process wide key atomic |
| @@ -1483,9 +1511,9 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 | |||
| 1483 | } | 1511 | } |
| 1484 | 1512 | ||
| 1485 | auto& kernel = Core::System::GetInstance().Kernel(); | 1513 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 1486 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1514 | auto process = kernel.CurrentProcess(); |
| 1487 | const auto shared_mem_handle = SharedMemory::Create( | 1515 | auto& handle_table = process->GetHandleTable(); |
| 1488 | kernel, handle_table.Get<Process>(CurrentProcess), size, perms, perms, addr); | 1516 | const auto shared_mem_handle = SharedMemory::Create(kernel, process, size, perms, perms, addr); |
| 1489 | 1517 | ||
| 1490 | CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); | 1518 | CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); |
| 1491 | return RESULT_SUCCESS; | 1519 | return RESULT_SUCCESS; |
| @@ -1595,10 +1623,9 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss | |||
| 1595 | } | 1623 | } |
| 1596 | 1624 | ||
| 1597 | auto& kernel = Core::System::GetInstance().Kernel(); | 1625 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 1598 | auto& handle_table = Core::CurrentProcess()->GetHandleTable(); | 1626 | auto process = kernel.CurrentProcess(); |
| 1599 | auto shared_mem_handle = | 1627 | auto& handle_table = process->GetHandleTable(); |
| 1600 | SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size, | 1628 | auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms); |
| 1601 | local_perms, remote_perms); | ||
| 1602 | 1629 | ||
| 1603 | CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); | 1630 | CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle)); |
| 1604 | return RESULT_SUCCESS; | 1631 | return RESULT_SUCCESS; |
| @@ -1904,7 +1931,7 @@ static const FunctionDef SVC_Table[] = { | |||
| 1904 | {0x73, nullptr, "SetProcessMemoryPermission"}, | 1931 | {0x73, nullptr, "SetProcessMemoryPermission"}, |
| 1905 | {0x74, nullptr, "MapProcessMemory"}, | 1932 | {0x74, nullptr, "MapProcessMemory"}, |
| 1906 | {0x75, nullptr, "UnmapProcessMemory"}, | 1933 | {0x75, nullptr, "UnmapProcessMemory"}, |
| 1907 | {0x76, nullptr, "QueryProcessMemory"}, | 1934 | {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"}, |
| 1908 | {0x77, nullptr, "MapProcessCodeMemory"}, | 1935 | {0x77, nullptr, "MapProcessCodeMemory"}, |
| 1909 | {0x78, nullptr, "UnmapProcessCodeMemory"}, | 1936 | {0x78, nullptr, "UnmapProcessCodeMemory"}, |
| 1910 | {0x79, nullptr, "CreateProcess"}, | 1937 | {0x79, nullptr, "CreateProcess"}, |
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h index b06aac4ec..c37ae0f98 100644 --- a/src/core/hle/kernel/svc.h +++ b/src/core/hle/kernel/svc.h | |||
| @@ -8,22 +8,6 @@ | |||
| 8 | 8 | ||
| 9 | namespace Kernel { | 9 | namespace Kernel { |
| 10 | 10 | ||
| 11 | struct MemoryInfo { | ||
| 12 | u64 base_address; | ||
| 13 | u64 size; | ||
| 14 | u32 type; | ||
| 15 | u32 attributes; | ||
| 16 | u32 permission; | ||
| 17 | u32 device_refcount; | ||
| 18 | u32 ipc_refcount; | ||
| 19 | INSERT_PADDING_WORDS(1); | ||
| 20 | }; | ||
| 21 | static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size."); | ||
| 22 | |||
| 23 | struct PageInfo { | ||
| 24 | u64 flags; | ||
| 25 | }; | ||
| 26 | |||
| 27 | void CallSVC(u32 immediate); | 11 | void CallSVC(u32 immediate); |
| 28 | 12 | ||
| 29 | } // namespace Kernel | 13 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h index 24aef46c9..2f758b959 100644 --- a/src/core/hle/kernel/svc_wrap.h +++ b/src/core/hle/kernel/svc_wrap.h | |||
| @@ -7,9 +7,7 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "core/arm/arm_interface.h" | 8 | #include "core/arm/arm_interface.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/hle/kernel/svc.h" | ||
| 11 | #include "core/hle/result.h" | 10 | #include "core/hle/result.h" |
| 12 | #include "core/memory.h" | ||
| 13 | 11 | ||
| 14 | namespace Kernel { | 12 | namespace Kernel { |
| 15 | 13 | ||
| @@ -129,7 +127,12 @@ void SvcWrap() { | |||
| 129 | template <ResultCode func(u64, u64, u32, u32)> | 127 | template <ResultCode func(u64, u64, u32, u32)> |
| 130 | void SvcWrap() { | 128 | void SvcWrap() { |
| 131 | FuncReturn( | 129 | FuncReturn( |
| 132 | func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw); | 130 | func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw); |
| 131 | } | ||
| 132 | |||
| 133 | template <ResultCode func(u64, u64, u32, u64)> | ||
| 134 | void SvcWrap() { | ||
| 135 | FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw); | ||
| 133 | } | 136 | } |
| 134 | 137 | ||
| 135 | template <ResultCode func(u32, u64, u32)> | 138 | template <ResultCode func(u32, u64, u32)> |
| @@ -191,21 +194,6 @@ void SvcWrap() { | |||
| 191 | FuncReturn(retval); | 194 | FuncReturn(retval); |
| 192 | } | 195 | } |
| 193 | 196 | ||
| 194 | template <ResultCode func(MemoryInfo*, PageInfo*, u64)> | ||
| 195 | void SvcWrap() { | ||
| 196 | MemoryInfo memory_info = {}; | ||
| 197 | PageInfo page_info = {}; | ||
| 198 | u32 retval = func(&memory_info, &page_info, Param(2)).raw; | ||
| 199 | |||
| 200 | Memory::Write64(Param(0), memory_info.base_address); | ||
| 201 | Memory::Write64(Param(0) + 8, memory_info.size); | ||
| 202 | Memory::Write32(Param(0) + 16, memory_info.type); | ||
| 203 | Memory::Write32(Param(0) + 20, memory_info.attributes); | ||
| 204 | Memory::Write32(Param(0) + 24, memory_info.permission); | ||
| 205 | |||
| 206 | FuncReturn(retval); | ||
| 207 | } | ||
| 208 | |||
| 209 | template <ResultCode func(u32*, u64, u64, u32)> | 197 | template <ResultCode func(u32*, u64, u64, u32)> |
| 210 | void SvcWrap() { | 198 | void SvcWrap() { |
| 211 | u32 param_1 = 0; | 199 | u32 param_1 = 0; |
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index d384d50db..77aec099a 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h | |||
| @@ -26,6 +26,7 @@ enum ThreadPriority : u32 { | |||
| 26 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps | 26 | THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps |
| 27 | THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps | 27 | THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps |
| 28 | THREADPRIO_LOWEST = 63, ///< Lowest thread priority | 28 | THREADPRIO_LOWEST = 63, ///< Lowest thread priority |
| 29 | THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities. | ||
| 29 | }; | 30 | }; |
| 30 | 31 | ||
| 31 | enum ThreadProcessorId : s32 { | 32 | enum ThreadProcessorId : s32 { |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 100f8f6bf..d3b55a51e 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -25,14 +25,14 @@ static const char* GetMemoryStateName(MemoryState state) { | |||
| 25 | "CodeMutable", "Heap", | 25 | "CodeMutable", "Heap", |
| 26 | "Shared", "Unknown1", | 26 | "Shared", "Unknown1", |
| 27 | "ModuleCodeStatic", "ModuleCodeMutable", | 27 | "ModuleCodeStatic", "ModuleCodeMutable", |
| 28 | "IpcBuffer0", "Mapped", | 28 | "IpcBuffer0", "Stack", |
| 29 | "ThreadLocal", "TransferMemoryIsolated", | 29 | "ThreadLocal", "TransferMemoryIsolated", |
| 30 | "TransferMemory", "ProcessMemory", | 30 | "TransferMemory", "ProcessMemory", |
| 31 | "Unknown2", "IpcBuffer1", | 31 | "Inaccessible", "IpcBuffer1", |
| 32 | "IpcBuffer3", "KernelStack", | 32 | "IpcBuffer3", "KernelStack", |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | return names[static_cast<int>(state)]; | 35 | return names[ToSvcMemoryState(state)]; |
| 36 | } | 36 | } |
| 37 | 37 | ||
| 38 | bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { | 38 | bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { |
| @@ -87,6 +87,10 @@ VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { | |||
| 87 | } | 87 | } |
| 88 | } | 88 | } |
| 89 | 89 | ||
| 90 | bool VMManager::IsValidHandle(VMAHandle handle) const { | ||
| 91 | return handle != vma_map.cend(); | ||
| 92 | } | ||
| 93 | |||
| 90 | ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, | 94 | ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target, |
| 91 | std::shared_ptr<std::vector<u8>> block, | 95 | std::shared_ptr<std::vector<u8>> block, |
| 92 | std::size_t offset, u64 size, | 96 | std::size_t offset, u64 size, |
| @@ -298,6 +302,25 @@ ResultCode VMManager::HeapFree(VAddr target, u64 size) { | |||
| 298 | return RESULT_SUCCESS; | 302 | return RESULT_SUCCESS; |
| 299 | } | 303 | } |
| 300 | 304 | ||
| 305 | MemoryInfo VMManager::QueryMemory(VAddr address) const { | ||
| 306 | const auto vma = FindVMA(address); | ||
| 307 | MemoryInfo memory_info{}; | ||
| 308 | |||
| 309 | if (IsValidHandle(vma)) { | ||
| 310 | memory_info.base_address = vma->second.base; | ||
| 311 | memory_info.permission = static_cast<u32>(vma->second.permissions); | ||
| 312 | memory_info.size = vma->second.size; | ||
| 313 | memory_info.state = ToSvcMemoryState(vma->second.meminfo_state); | ||
| 314 | } else { | ||
| 315 | memory_info.base_address = address_space_end; | ||
| 316 | memory_info.permission = static_cast<u32>(VMAPermission::None); | ||
| 317 | memory_info.size = 0 - address_space_end; | ||
| 318 | memory_info.state = static_cast<u32>(MemoryState::Inaccessible); | ||
| 319 | } | ||
| 320 | |||
| 321 | return memory_info; | ||
| 322 | } | ||
| 323 | |||
| 301 | ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { | 324 | ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) { |
| 302 | const auto vma = FindVMA(src_addr); | 325 | const auto vma = FindVMA(src_addr); |
| 303 | 326 | ||
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index d522404fe..10bacac3e 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h | |||
| @@ -43,26 +43,129 @@ enum class VMAPermission : u8 { | |||
| 43 | ReadWriteExecute = Read | Write | Execute, | 43 | ReadWriteExecute = Read | Write | Execute, |
| 44 | }; | 44 | }; |
| 45 | 45 | ||
| 46 | /// Set of values returned in MemoryInfo.state by svcQueryMemory. | 46 | // clang-format off |
| 47 | /// Represents memory states and any relevant flags, as used by the kernel. | ||
| 48 | /// svcQueryMemory interprets these by masking away all but the first eight | ||
| 49 | /// bits when storing memory state into a MemoryInfo instance. | ||
| 47 | enum class MemoryState : u32 { | 50 | enum class MemoryState : u32 { |
| 48 | Unmapped = 0x0, | 51 | Mask = 0xFF, |
| 49 | Io = 0x1, | 52 | FlagProtect = 1U << 8, |
| 50 | Normal = 0x2, | 53 | FlagDebug = 1U << 9, |
| 51 | CodeStatic = 0x3, | 54 | FlagIPC0 = 1U << 10, |
| 52 | CodeMutable = 0x4, | 55 | FlagIPC3 = 1U << 11, |
| 53 | Heap = 0x5, | 56 | FlagIPC1 = 1U << 12, |
| 54 | Shared = 0x6, | 57 | FlagMapped = 1U << 13, |
| 55 | ModuleCodeStatic = 0x8, | 58 | FlagCode = 1U << 14, |
| 56 | ModuleCodeMutable = 0x9, | 59 | FlagAlias = 1U << 15, |
| 57 | IpcBuffer0 = 0xA, | 60 | FlagModule = 1U << 16, |
| 58 | Mapped = 0xB, | 61 | FlagTransfer = 1U << 17, |
| 59 | ThreadLocal = 0xC, | 62 | FlagQueryPhysicalAddressAllowed = 1U << 18, |
| 60 | TransferMemoryIsolated = 0xD, | 63 | FlagSharedDevice = 1U << 19, |
| 61 | TransferMemory = 0xE, | 64 | FlagSharedDeviceAligned = 1U << 20, |
| 62 | ProcessMemory = 0xF, | 65 | FlagIPCBuffer = 1U << 21, |
| 63 | IpcBuffer1 = 0x11, | 66 | FlagMemoryPoolAllocated = 1U << 22, |
| 64 | IpcBuffer3 = 0x12, | 67 | FlagMapProcess = 1U << 23, |
| 65 | KernelStack = 0x13, | 68 | FlagUncached = 1U << 24, |
| 69 | FlagCodeMemory = 1U << 25, | ||
| 70 | |||
| 71 | // Convenience flag sets to reduce repetition | ||
| 72 | IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1, | ||
| 73 | |||
| 74 | CodeFlags = FlagDebug | IPCFlags | FlagMapped | FlagCode | FlagQueryPhysicalAddressAllowed | | ||
| 75 | FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||
| 76 | |||
| 77 | DataFlags = FlagProtect | IPCFlags | FlagMapped | FlagAlias | FlagTransfer | | ||
| 78 | FlagQueryPhysicalAddressAllowed | FlagSharedDevice | FlagSharedDeviceAligned | | ||
| 79 | FlagMemoryPoolAllocated | FlagIPCBuffer | FlagUncached, | ||
| 80 | |||
| 81 | Unmapped = 0x00, | ||
| 82 | Io = 0x01 | FlagMapped, | ||
| 83 | Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed, | ||
| 84 | CodeStatic = 0x03 | CodeFlags | FlagMapProcess, | ||
| 85 | CodeMutable = 0x04 | CodeFlags | FlagMapProcess | FlagCodeMemory, | ||
| 86 | Heap = 0x05 | DataFlags | FlagCodeMemory, | ||
| 87 | Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated, | ||
| 88 | ModuleCodeStatic = 0x08 | CodeFlags | FlagModule | FlagMapProcess, | ||
| 89 | ModuleCodeMutable = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory, | ||
| 90 | |||
| 91 | IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated | | ||
| 92 | IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned, | ||
| 93 | |||
| 94 | Stack = 0x0B | FlagMapped | IPCFlags | FlagQueryPhysicalAddressAllowed | | ||
| 95 | FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||
| 96 | |||
| 97 | ThreadLocal = 0x0C | FlagMapped | FlagMemoryPoolAllocated, | ||
| 98 | |||
| 99 | TransferMemoryIsolated = 0x0D | IPCFlags | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||
| 100 | FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated | | ||
| 101 | FlagUncached, | ||
| 102 | |||
| 103 | TransferMemory = 0x0E | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||
| 104 | FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||
| 105 | |||
| 106 | ProcessMemory = 0x0F | FlagIPC3 | FlagIPC1 | FlagMapped | FlagMemoryPoolAllocated, | ||
| 107 | |||
| 108 | // Used to signify an inaccessible or invalid memory region with memory queries | ||
| 109 | Inaccessible = 0x10, | ||
| 110 | |||
| 111 | IpcBuffer1 = 0x11 | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||
| 112 | FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||
| 113 | |||
| 114 | IpcBuffer3 = 0x12 | FlagIPC3 | FlagMapped | FlagQueryPhysicalAddressAllowed | | ||
| 115 | FlagSharedDeviceAligned | FlagMemoryPoolAllocated, | ||
| 116 | |||
| 117 | KernelStack = 0x13 | FlagMapped, | ||
| 118 | }; | ||
| 119 | // clang-format on | ||
| 120 | |||
| 121 | constexpr MemoryState operator|(MemoryState lhs, MemoryState rhs) { | ||
| 122 | return static_cast<MemoryState>(u32(lhs) | u32(rhs)); | ||
| 123 | } | ||
| 124 | |||
| 125 | constexpr MemoryState operator&(MemoryState lhs, MemoryState rhs) { | ||
| 126 | return static_cast<MemoryState>(u32(lhs) & u32(rhs)); | ||
| 127 | } | ||
| 128 | |||
| 129 | constexpr MemoryState operator^(MemoryState lhs, MemoryState rhs) { | ||
| 130 | return static_cast<MemoryState>(u32(lhs) ^ u32(rhs)); | ||
| 131 | } | ||
| 132 | |||
| 133 | constexpr MemoryState operator~(MemoryState lhs) { | ||
| 134 | return static_cast<MemoryState>(~u32(lhs)); | ||
| 135 | } | ||
| 136 | |||
| 137 | constexpr MemoryState& operator|=(MemoryState& lhs, MemoryState rhs) { | ||
| 138 | lhs = lhs | rhs; | ||
| 139 | return lhs; | ||
| 140 | } | ||
| 141 | |||
| 142 | constexpr MemoryState& operator&=(MemoryState& lhs, MemoryState rhs) { | ||
| 143 | lhs = lhs & rhs; | ||
| 144 | return lhs; | ||
| 145 | } | ||
| 146 | |||
| 147 | constexpr MemoryState& operator^=(MemoryState& lhs, MemoryState rhs) { | ||
| 148 | lhs = lhs ^ rhs; | ||
| 149 | return lhs; | ||
| 150 | } | ||
| 151 | |||
| 152 | constexpr u32 ToSvcMemoryState(MemoryState state) { | ||
| 153 | return static_cast<u32>(state & MemoryState::Mask); | ||
| 154 | } | ||
| 155 | |||
| 156 | struct MemoryInfo { | ||
| 157 | u64 base_address; | ||
| 158 | u64 size; | ||
| 159 | u32 state; | ||
| 160 | u32 attributes; | ||
| 161 | u32 permission; | ||
| 162 | u32 ipc_ref_count; | ||
| 163 | u32 device_ref_count; | ||
| 164 | }; | ||
| 165 | static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size."); | ||
| 166 | |||
| 167 | struct PageInfo { | ||
| 168 | u32 flags; | ||
| 66 | }; | 169 | }; |
| 67 | 170 | ||
| 68 | /** | 171 | /** |
| @@ -113,16 +216,10 @@ struct VirtualMemoryArea { | |||
| 113 | * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ | 216 | * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ |
| 114 | */ | 217 | */ |
| 115 | class VMManager final { | 218 | class VMManager final { |
| 219 | using VMAMap = std::map<VAddr, VirtualMemoryArea>; | ||
| 220 | |||
| 116 | public: | 221 | public: |
| 117 | /** | 222 | using VMAHandle = VMAMap::const_iterator; |
| 118 | * A map covering the entirety of the managed address space, keyed by the `base` field of each | ||
| 119 | * VMA. It must always be modified by splitting or merging VMAs, so that the invariant | ||
| 120 | * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be | ||
| 121 | * merged when possible so that no two similar and adjacent regions exist that have not been | ||
| 122 | * merged. | ||
| 123 | */ | ||
| 124 | std::map<VAddr, VirtualMemoryArea> vma_map; | ||
| 125 | using VMAHandle = decltype(vma_map)::const_iterator; | ||
| 126 | 223 | ||
| 127 | VMManager(); | 224 | VMManager(); |
| 128 | ~VMManager(); | 225 | ~VMManager(); |
| @@ -133,6 +230,9 @@ public: | |||
| 133 | /// Finds the VMA in which the given address is included in, or `vma_map.end()`. | 230 | /// Finds the VMA in which the given address is included in, or `vma_map.end()`. |
| 134 | VMAHandle FindVMA(VAddr target) const; | 231 | VMAHandle FindVMA(VAddr target) const; |
| 135 | 232 | ||
| 233 | /// Indicates whether or not the given handle is within the VMA map. | ||
| 234 | bool IsValidHandle(VMAHandle handle) const; | ||
| 235 | |||
| 136 | // TODO(yuriks): Should these functions actually return the handle? | 236 | // TODO(yuriks): Should these functions actually return the handle? |
| 137 | 237 | ||
| 138 | /** | 238 | /** |
| @@ -189,8 +289,15 @@ public: | |||
| 189 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); | 289 | ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms); |
| 190 | ResultCode HeapFree(VAddr target, u64 size); | 290 | ResultCode HeapFree(VAddr target, u64 size); |
| 191 | 291 | ||
| 192 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, | 292 | ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state); |
| 193 | MemoryState state = MemoryState::Mapped); | 293 | |
| 294 | /// Queries the memory manager for information about the given address. | ||
| 295 | /// | ||
| 296 | /// @param address The address to query the memory manager about for information. | ||
| 297 | /// | ||
| 298 | /// @return A MemoryInfo instance containing information about the given address. | ||
| 299 | /// | ||
| 300 | MemoryInfo QueryMemory(VAddr address) const; | ||
| 194 | 301 | ||
| 195 | /** | 302 | /** |
| 196 | * Scans all VMAs and updates the page table range of any that use the given vector as backing | 303 | * Scans all VMAs and updates the page table range of any that use the given vector as backing |
| @@ -281,7 +388,7 @@ public: | |||
| 281 | Memory::PageTable page_table; | 388 | Memory::PageTable page_table; |
| 282 | 389 | ||
| 283 | private: | 390 | private: |
| 284 | using VMAIter = decltype(vma_map)::iterator; | 391 | using VMAIter = VMAMap::iterator; |
| 285 | 392 | ||
| 286 | /// Converts a VMAHandle to a mutable VMAIter. | 393 | /// Converts a VMAHandle to a mutable VMAIter. |
| 287 | VMAIter StripIterConstness(const VMAHandle& iter); | 394 | VMAIter StripIterConstness(const VMAHandle& iter); |
| @@ -328,6 +435,15 @@ private: | |||
| 328 | /// Clears out the page table | 435 | /// Clears out the page table |
| 329 | void ClearPageTable(); | 436 | void ClearPageTable(); |
| 330 | 437 | ||
| 438 | /** | ||
| 439 | * A map covering the entirety of the managed address space, keyed by the `base` field of each | ||
| 440 | * VMA. It must always be modified by splitting or merging VMAs, so that the invariant | ||
| 441 | * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be | ||
| 442 | * merged when possible so that no two similar and adjacent regions exist that have not been | ||
| 443 | * merged. | ||
| 444 | */ | ||
| 445 | VMAMap vma_map; | ||
| 446 | |||
| 331 | u32 address_space_width = 0; | 447 | u32 address_space_width = 0; |
| 332 | VAddr address_space_base = 0; | 448 | VAddr address_space_base = 0; |
| 333 | VAddr address_space_end = 0; | 449 | VAddr address_space_end = 0; |
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp index 0417fdb92..b506bc3dd 100644 --- a/src/core/hle/service/aoc/aoc_u.cpp +++ b/src/core/hle/service/aoc/aoc_u.cpp | |||
| @@ -20,6 +20,7 @@ | |||
| 20 | #include "core/hle/service/aoc/aoc_u.h" | 20 | #include "core/hle/service/aoc/aoc_u.h" |
| 21 | #include "core/hle/service/filesystem/filesystem.h" | 21 | #include "core/hle/service/filesystem/filesystem.h" |
| 22 | #include "core/loader/loader.h" | 22 | #include "core/loader/loader.h" |
| 23 | #include "core/settings.h" | ||
| 23 | 24 | ||
| 24 | namespace Service::AOC { | 25 | namespace Service::AOC { |
| 25 | 26 | ||
| @@ -76,6 +77,13 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 76 | rb.Push(RESULT_SUCCESS); | 77 | rb.Push(RESULT_SUCCESS); |
| 77 | 78 | ||
| 78 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 79 | const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 80 | |||
| 81 | const auto& disabled = Settings::values.disabled_addons[current]; | ||
| 82 | if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) { | ||
| 83 | rb.Push<u32>(0); | ||
| 84 | return; | ||
| 85 | } | ||
| 86 | |||
| 79 | rb.Push<u32>(static_cast<u32>( | 87 | rb.Push<u32>(static_cast<u32>( |
| 80 | std::count_if(add_on_content.begin(), add_on_content.end(), | 88 | std::count_if(add_on_content.begin(), add_on_content.end(), |
| 81 | [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); | 89 | [current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); }))); |
| @@ -96,6 +104,10 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) { | |||
| 96 | out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); | 104 | out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF)); |
| 97 | } | 105 | } |
| 98 | 106 | ||
| 107 | const auto& disabled = Settings::values.disabled_addons[current]; | ||
| 108 | if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) | ||
| 109 | out = {}; | ||
| 110 | |||
| 99 | if (out.size() < offset) { | 111 | if (out.size() < offset) { |
| 100 | IPC::ResponseBuilder rb{ctx, 2}; | 112 | IPC::ResponseBuilder rb{ctx, 2}; |
| 101 | // TODO(DarkLordZach): Find the correct error code. | 113 | // TODO(DarkLordZach): Find the correct error code. |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index d2ffd5776..74c4e583b 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -45,8 +45,12 @@ public: | |||
| 45 | explicit IStorage(FileSys::VirtualFile backend_) | 45 | explicit IStorage(FileSys::VirtualFile backend_) |
| 46 | : ServiceFramework("IStorage"), backend(std::move(backend_)) { | 46 | : ServiceFramework("IStorage"), backend(std::move(backend_)) { |
| 47 | static const FunctionInfo functions[] = { | 47 | static const FunctionInfo functions[] = { |
| 48 | {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, | 48 | {0, &IStorage::Read, "Read"}, |
| 49 | {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, | 49 | {1, nullptr, "Write"}, |
| 50 | {2, nullptr, "Flush"}, | ||
| 51 | {3, nullptr, "SetSize"}, | ||
| 52 | {4, &IStorage::GetSize, "GetSize"}, | ||
| 53 | {5, nullptr, "OperateRange"}, | ||
| 50 | }; | 54 | }; |
| 51 | RegisterHandlers(functions); | 55 | RegisterHandlers(functions); |
| 52 | } | 56 | } |
| @@ -83,6 +87,15 @@ private: | |||
| 83 | IPC::ResponseBuilder rb{ctx, 2}; | 87 | IPC::ResponseBuilder rb{ctx, 2}; |
| 84 | rb.Push(RESULT_SUCCESS); | 88 | rb.Push(RESULT_SUCCESS); |
| 85 | } | 89 | } |
| 90 | |||
| 91 | void GetSize(Kernel::HLERequestContext& ctx) { | ||
| 92 | const u64 size = backend->GetSize(); | ||
| 93 | LOG_DEBUG(Service_FS, "called, size={}", size); | ||
| 94 | |||
| 95 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 96 | rb.Push(RESULT_SUCCESS); | ||
| 97 | rb.Push<u64>(size); | ||
| 98 | } | ||
| 86 | }; | 99 | }; |
| 87 | 100 | ||
| 88 | class IFile final : public ServiceFramework<IFile> { | 101 | class IFile final : public ServiceFramework<IFile> { |
| @@ -796,9 +809,18 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& | |||
| 796 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | 809 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { |
| 797 | LOG_WARNING(Service_FS, "(STUBBED) called"); | 810 | LOG_WARNING(Service_FS, "(STUBBED) called"); |
| 798 | 811 | ||
| 812 | enum class LogMode : u32 { | ||
| 813 | Off, | ||
| 814 | Log, | ||
| 815 | RedirectToSdCard, | ||
| 816 | LogToSdCard = Log | RedirectToSdCard, | ||
| 817 | }; | ||
| 818 | |||
| 819 | // Given we always want to receive logging information, | ||
| 820 | // we always specify logging as enabled. | ||
| 799 | IPC::ResponseBuilder rb{ctx, 3}; | 821 | IPC::ResponseBuilder rb{ctx, 3}; |
| 800 | rb.Push(RESULT_SUCCESS); | 822 | rb.Push(RESULT_SUCCESS); |
| 801 | rb.Push<u32>(5); | 823 | rb.PushEnum(LogMode::Log); |
| 802 | } | 824 | } |
| 803 | 825 | ||
| 804 | void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { | 826 | void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 453d90a22..13bcefe07 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -408,13 +408,13 @@ private: | |||
| 408 | using SHA256Hash = std::array<u8, 0x20>; | 408 | using SHA256Hash = std::array<u8, 0x20>; |
| 409 | 409 | ||
| 410 | struct NROHeader { | 410 | struct NROHeader { |
| 411 | u32_le entrypoint_insn; | 411 | INSERT_PADDING_WORDS(1); |
| 412 | u32_le mod_offset; | 412 | u32_le mod_offset; |
| 413 | INSERT_PADDING_WORDS(2); | 413 | INSERT_PADDING_WORDS(2); |
| 414 | u32_le magic; | 414 | u32_le magic; |
| 415 | INSERT_PADDING_WORDS(1); | 415 | u32_le version; |
| 416 | u32_le nro_size; | 416 | u32_le nro_size; |
| 417 | INSERT_PADDING_WORDS(1); | 417 | u32_le flags; |
| 418 | u32_le text_offset; | 418 | u32_le text_offset; |
| 419 | u32_le text_size; | 419 | u32_le text_size; |
| 420 | u32_le ro_offset; | 420 | u32_le ro_offset; |
| @@ -430,9 +430,10 @@ private: | |||
| 430 | 430 | ||
| 431 | struct NRRHeader { | 431 | struct NRRHeader { |
| 432 | u32_le magic; | 432 | u32_le magic; |
| 433 | INSERT_PADDING_BYTES(0x1C); | 433 | INSERT_PADDING_BYTES(12); |
| 434 | u64_le title_id_mask; | 434 | u64_le title_id_mask; |
| 435 | u64_le title_id_pattern; | 435 | u64_le title_id_pattern; |
| 436 | INSERT_PADDING_BYTES(16); | ||
| 436 | std::array<u8, 0x100> modulus; | 437 | std::array<u8, 0x100> modulus; |
| 437 | std::array<u8, 0x100> signature_1; | 438 | std::array<u8, 0x100> signature_1; |
| 438 | std::array<u8, 0x100> signature_2; | 439 | std::array<u8, 0x100> signature_2; |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 3bfce0110..0a650f36c 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp | |||
| @@ -137,6 +137,10 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector< | |||
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | static void PushGPUEntries(Tegra::CommandList&& entries) { | 139 | static void PushGPUEntries(Tegra::CommandList&& entries) { |
| 140 | if (entries.empty()) { | ||
| 141 | return; | ||
| 142 | } | ||
| 143 | |||
| 140 | auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()}; | 144 | auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()}; |
| 141 | dma_pusher.Push(std::move(entries)); | 145 | dma_pusher.Push(std::move(entries)); |
| 142 | dma_pusher.DispatchCalls(); | 146 | dma_pusher.DispatchCalls(); |
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 1ec340466..d25b80ab0 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -70,10 +70,6 @@ | |||
| 70 | #include "core/hle/service/vi/vi.h" | 70 | #include "core/hle/service/vi/vi.h" |
| 71 | #include "core/hle/service/wlan/wlan.h" | 71 | #include "core/hle/service/wlan/wlan.h" |
| 72 | 72 | ||
| 73 | using Kernel::ClientPort; | ||
| 74 | using Kernel::ServerPort; | ||
| 75 | using Kernel::SharedPtr; | ||
| 76 | |||
| 77 | namespace Service { | 73 | namespace Service { |
| 78 | 74 | ||
| 79 | /** | 75 | /** |
| @@ -101,33 +97,33 @@ ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_ses | |||
| 101 | ServiceFrameworkBase::~ServiceFrameworkBase() = default; | 97 | ServiceFrameworkBase::~ServiceFrameworkBase() = default; |
| 102 | 98 | ||
| 103 | void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { | 99 | void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) { |
| 104 | ASSERT(port == nullptr); | 100 | ASSERT(!port_installed); |
| 105 | port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); | 101 | |
| 102 | auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap(); | ||
| 106 | port->SetHleHandler(shared_from_this()); | 103 | port->SetHleHandler(shared_from_this()); |
| 104 | port_installed = true; | ||
| 107 | } | 105 | } |
| 108 | 106 | ||
| 109 | void ServiceFrameworkBase::InstallAsNamedPort() { | 107 | void ServiceFrameworkBase::InstallAsNamedPort() { |
| 110 | ASSERT(port == nullptr); | 108 | ASSERT(!port_installed); |
| 111 | 109 | ||
| 112 | auto& kernel = Core::System::GetInstance().Kernel(); | 110 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 113 | SharedPtr<ServerPort> server_port; | 111 | auto [server_port, client_port] = |
| 114 | SharedPtr<ClientPort> client_port; | 112 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); |
| 115 | std::tie(server_port, client_port) = | ||
| 116 | ServerPort::CreatePortPair(kernel, max_sessions, service_name); | ||
| 117 | server_port->SetHleHandler(shared_from_this()); | 113 | server_port->SetHleHandler(shared_from_this()); |
| 118 | kernel.AddNamedPort(service_name, std::move(client_port)); | 114 | kernel.AddNamedPort(service_name, std::move(client_port)); |
| 115 | port_installed = true; | ||
| 119 | } | 116 | } |
| 120 | 117 | ||
| 121 | Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { | 118 | Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() { |
| 122 | ASSERT(port == nullptr); | 119 | ASSERT(!port_installed); |
| 123 | 120 | ||
| 124 | auto& kernel = Core::System::GetInstance().Kernel(); | 121 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 125 | Kernel::SharedPtr<Kernel::ServerPort> server_port; | 122 | auto [server_port, client_port] = |
| 126 | Kernel::SharedPtr<Kernel::ClientPort> client_port; | ||
| 127 | std::tie(server_port, client_port) = | ||
| 128 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); | 123 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name); |
| 129 | port = MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)).Unwrap(); | 124 | auto port = MakeResult(std::move(server_port)).Unwrap(); |
| 130 | port->SetHleHandler(shared_from_this()); | 125 | port->SetHleHandler(shared_from_this()); |
| 126 | port_installed = true; | ||
| 131 | return client_port; | 127 | return client_port; |
| 132 | } | 128 | } |
| 133 | 129 | ||
| @@ -152,8 +148,7 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext | |||
| 152 | } | 148 | } |
| 153 | buf.push_back('}'); | 149 | buf.push_back('}'); |
| 154 | 150 | ||
| 155 | LOG_ERROR(Service, "unknown / unimplemented {}", fmt::to_string(buf)); | 151 | UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf)); |
| 156 | UNIMPLEMENTED(); | ||
| 157 | } | 152 | } |
| 158 | 153 | ||
| 159 | void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { | 154 | void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index 98483ecf1..029533628 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h | |||
| @@ -96,11 +96,9 @@ private: | |||
| 96 | /// Maximum number of concurrent sessions that this service can handle. | 96 | /// Maximum number of concurrent sessions that this service can handle. |
| 97 | u32 max_sessions; | 97 | u32 max_sessions; |
| 98 | 98 | ||
| 99 | /** | 99 | /// Flag to store if a port was already create/installed to detect multiple install attempts, |
| 100 | * Port where incoming connections will be received. Only created when InstallAsService() or | 100 | /// which is not supported. |
| 101 | * InstallAsNamedPort() are called. | 101 | bool port_installed = false; |
| 102 | */ | ||
| 103 | Kernel::SharedPtr<Kernel::ServerPort> port; | ||
| 104 | 102 | ||
| 105 | /// Function used to safely up-cast pointers to the derived class before invoking a handler. | 103 | /// Function used to safely up-cast pointers to the derived class before invoking a handler. |
| 106 | InvokerFn* handler_invoker; | 104 | InvokerFn* handler_invoker; |
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 0d0f63a78..d73530086 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp | |||
| @@ -54,13 +54,11 @@ ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService | |||
| 54 | return ERR_ALREADY_REGISTERED; | 54 | return ERR_ALREADY_REGISTERED; |
| 55 | 55 | ||
| 56 | auto& kernel = Core::System::GetInstance().Kernel(); | 56 | auto& kernel = Core::System::GetInstance().Kernel(); |
| 57 | Kernel::SharedPtr<Kernel::ServerPort> server_port; | 57 | auto [server_port, client_port] = |
| 58 | Kernel::SharedPtr<Kernel::ClientPort> client_port; | ||
| 59 | std::tie(server_port, client_port) = | ||
| 60 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name); | 58 | Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name); |
| 61 | 59 | ||
| 62 | registered_services.emplace(std::move(name), std::move(client_port)); | 60 | registered_services.emplace(std::move(name), std::move(client_port)); |
| 63 | return MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)); | 61 | return MakeResult(std::move(server_port)); |
| 64 | } | 62 | } |
| 65 | 63 | ||
| 66 | ResultCode ServiceManager::UnregisterService(const std::string& name) { | 64 | ResultCode ServiceManager::UnregisterService(const std::string& name) { |
| @@ -83,7 +81,7 @@ ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort( | |||
| 83 | return ERR_SERVICE_NOT_REGISTERED; | 81 | return ERR_SERVICE_NOT_REGISTERED; |
| 84 | } | 82 | } |
| 85 | 83 | ||
| 86 | return MakeResult<Kernel::SharedPtr<Kernel::ClientPort>>(it->second); | 84 | return MakeResult(it->second); |
| 87 | } | 85 | } |
| 88 | 86 | ||
| 89 | ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToService( | 87 | ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToService( |
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index d109ed2b5..1615cb5a8 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h | |||
| @@ -33,7 +33,7 @@ public: | |||
| 33 | */ | 33 | */ |
| 34 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 34 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 35 | 35 | ||
| 36 | FileType GetFileType() override { | 36 | FileType GetFileType() const override { |
| 37 | return IdentifyType(file); | 37 | return IdentifyType(file); |
| 38 | } | 38 | } |
| 39 | 39 | ||
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 6af76441c..a2d33021c 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h | |||
| @@ -22,7 +22,7 @@ public: | |||
| 22 | */ | 22 | */ |
| 23 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 23 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 24 | 24 | ||
| 25 | FileType GetFileType() override { | 25 | FileType GetFileType() const override { |
| 26 | return IdentifyType(file); | 26 | return IdentifyType(file); |
| 27 | } | 27 | } |
| 28 | 28 | ||
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 7686634bf..0838e303b 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include <vector> | 12 | #include <vector> |
| 13 | 13 | ||
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "core/file_sys/control_metadata.h" | ||
| 15 | #include "core/file_sys/vfs.h" | 16 | #include "core/file_sys/vfs.h" |
| 16 | 17 | ||
| 17 | namespace Kernel { | 18 | namespace Kernel { |
| @@ -131,7 +132,7 @@ public: | |||
| 131 | * Returns the type of this file | 132 | * Returns the type of this file |
| 132 | * @return FileType corresponding to the loaded file | 133 | * @return FileType corresponding to the loaded file |
| 133 | */ | 134 | */ |
| 134 | virtual FileType GetFileType() = 0; | 135 | virtual FileType GetFileType() const = 0; |
| 135 | 136 | ||
| 136 | /** | 137 | /** |
| 137 | * Load the application and return the created Process instance | 138 | * Load the application and return the created Process instance |
| @@ -243,6 +244,15 @@ public: | |||
| 243 | return ResultStatus::ErrorNotImplemented; | 244 | return ResultStatus::ErrorNotImplemented; |
| 244 | } | 245 | } |
| 245 | 246 | ||
| 247 | /** | ||
| 248 | * Get the developer of the application | ||
| 249 | * @param developer Reference to store the application developer into | ||
| 250 | * @return ResultStatus result of function | ||
| 251 | */ | ||
| 252 | virtual ResultStatus ReadDeveloper(std::string& developer) { | ||
| 253 | return ResultStatus::ErrorNotImplemented; | ||
| 254 | } | ||
| 255 | |||
| 246 | protected: | 256 | protected: |
| 247 | FileSys::VirtualFile file; | 257 | FileSys::VirtualFile file; |
| 248 | bool is_loaded = false; | 258 | bool is_loaded = false; |
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp index 42f4a777b..a093e3d36 100644 --- a/src/core/loader/nax.cpp +++ b/src/core/loader/nax.cpp | |||
| @@ -37,7 +37,7 @@ FileType AppLoader_NAX::IdentifyType(const FileSys::VirtualFile& file) { | |||
| 37 | return IdentifyTypeImpl(nax); | 37 | return IdentifyTypeImpl(nax); |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | FileType AppLoader_NAX::GetFileType() { | 40 | FileType AppLoader_NAX::GetFileType() const { |
| 41 | return IdentifyTypeImpl(*nax); | 41 | return IdentifyTypeImpl(*nax); |
| 42 | } | 42 | } |
| 43 | 43 | ||
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h index b4d93bd01..0a97511b8 100644 --- a/src/core/loader/nax.h +++ b/src/core/loader/nax.h | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | */ | 31 | */ |
| 32 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 32 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 33 | 33 | ||
| 34 | FileType GetFileType() override; | 34 | FileType GetFileType() const override; |
| 35 | 35 | ||
| 36 | ResultStatus Load(Kernel::Process& process) override; | 36 | ResultStatus Load(Kernel::Process& process) override; |
| 37 | 37 | ||
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 95d9b73a1..cbbe701d2 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h | |||
| @@ -29,7 +29,7 @@ public: | |||
| 29 | */ | 29 | */ |
| 30 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 30 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 31 | 31 | ||
| 32 | FileType GetFileType() override { | 32 | FileType GetFileType() const override { |
| 33 | return IdentifyType(file); | 33 | return IdentifyType(file); |
| 34 | } | 34 | } |
| 35 | 35 | ||
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 6deff3a51..013d629c0 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -33,7 +33,7 @@ public: | |||
| 33 | */ | 33 | */ |
| 34 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 34 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 35 | 35 | ||
| 36 | FileType GetFileType() override { | 36 | FileType GetFileType() const override { |
| 37 | return IdentifyType(file); | 37 | return IdentifyType(file); |
| 38 | } | 38 | } |
| 39 | 39 | ||
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 0c1defbb6..135b6ea5a 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -37,7 +37,7 @@ public: | |||
| 37 | */ | 37 | */ |
| 38 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 38 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 39 | 39 | ||
| 40 | FileType GetFileType() override { | 40 | FileType GetFileType() const override { |
| 41 | return IdentifyType(file); | 41 | return IdentifyType(file); |
| 42 | } | 42 | } |
| 43 | 43 | ||
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 080d89904..b4ab88ae8 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp | |||
| @@ -151,4 +151,11 @@ ResultStatus AppLoader_NSP::ReadTitle(std::string& title) { | |||
| 151 | title = nacp_file->GetApplicationName(); | 151 | title = nacp_file->GetApplicationName(); |
| 152 | return ResultStatus::Success; | 152 | return ResultStatus::Success; |
| 153 | } | 153 | } |
| 154 | |||
| 155 | ResultStatus AppLoader_NSP::ReadDeveloper(std::string& developer) { | ||
| 156 | if (nacp_file == nullptr) | ||
| 157 | return ResultStatus::ErrorNoControl; | ||
| 158 | developer = nacp_file->GetDeveloperName(); | ||
| 159 | return ResultStatus::Success; | ||
| 160 | } | ||
| 154 | } // namespace Loader | 161 | } // namespace Loader |
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index db91cd01e..2b1e0719b 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | */ | 31 | */ |
| 32 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 32 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 33 | 33 | ||
| 34 | FileType GetFileType() override { | 34 | FileType GetFileType() const override { |
| 35 | return IdentifyType(file); | 35 | return IdentifyType(file); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| @@ -43,6 +43,7 @@ public: | |||
| 43 | ResultStatus ReadProgramId(u64& out_program_id) override; | 43 | ResultStatus ReadProgramId(u64& out_program_id) override; |
| 44 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; | 44 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; |
| 45 | ResultStatus ReadTitle(std::string& title) override; | 45 | ResultStatus ReadTitle(std::string& title) override; |
| 46 | ResultStatus ReadDeveloper(std::string& developer) override; | ||
| 46 | 47 | ||
| 47 | private: | 48 | private: |
| 48 | std::unique_ptr<FileSys::NSP> nsp; | 49 | std::unique_ptr<FileSys::NSP> nsp; |
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 461607c95..bd5a83b49 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp | |||
| @@ -120,4 +120,11 @@ ResultStatus AppLoader_XCI::ReadTitle(std::string& title) { | |||
| 120 | title = nacp_file->GetApplicationName(); | 120 | title = nacp_file->GetApplicationName(); |
| 121 | return ResultStatus::Success; | 121 | return ResultStatus::Success; |
| 122 | } | 122 | } |
| 123 | |||
| 124 | ResultStatus AppLoader_XCI::ReadDeveloper(std::string& developer) { | ||
| 125 | if (nacp_file == nullptr) | ||
| 126 | return ResultStatus::ErrorNoControl; | ||
| 127 | developer = nacp_file->GetDeveloperName(); | ||
| 128 | return ResultStatus::Success; | ||
| 129 | } | ||
| 123 | } // namespace Loader | 130 | } // namespace Loader |
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h index 46f8dfc9e..15d1b1a23 100644 --- a/src/core/loader/xci.h +++ b/src/core/loader/xci.h | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | */ | 31 | */ |
| 32 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 32 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 33 | 33 | ||
| 34 | FileType GetFileType() override { | 34 | FileType GetFileType() const override { |
| 35 | return IdentifyType(file); | 35 | return IdentifyType(file); |
| 36 | } | 36 | } |
| 37 | 37 | ||
| @@ -43,6 +43,7 @@ public: | |||
| 43 | ResultStatus ReadProgramId(u64& out_program_id) override; | 43 | ResultStatus ReadProgramId(u64& out_program_id) override; |
| 44 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; | 44 | ResultStatus ReadIcon(std::vector<u8>& buffer) override; |
| 45 | ResultStatus ReadTitle(std::string& title) override; | 45 | ResultStatus ReadTitle(std::string& title) override; |
| 46 | ResultStatus ReadDeveloper(std::string& developer) override; | ||
| 46 | 47 | ||
| 47 | private: | 48 | private: |
| 48 | std::unique_ptr<FileSys::XCI> xci; | 49 | std::unique_ptr<FileSys::XCI> xci; |
diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 41fd2a6a0..643afdee8 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -125,14 +125,13 @@ void RemoveDebugHook(PageTable& page_table, VAddr base, u64 size, MemoryHookPoin | |||
| 125 | * using a VMA from the current process | 125 | * using a VMA from the current process |
| 126 | */ | 126 | */ |
| 127 | static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { | 127 | static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { |
| 128 | u8* direct_pointer = nullptr; | 128 | const auto& vm_manager = process.VMManager(); |
| 129 | |||
| 130 | auto& vm_manager = process.VMManager(); | ||
| 131 | 129 | ||
| 132 | auto it = vm_manager.FindVMA(vaddr); | 130 | const auto it = vm_manager.FindVMA(vaddr); |
| 133 | ASSERT(it != vm_manager.vma_map.end()); | 131 | DEBUG_ASSERT(vm_manager.IsValidHandle(it)); |
| 134 | 132 | ||
| 135 | auto& vma = it->second; | 133 | u8* direct_pointer = nullptr; |
| 134 | const auto& vma = it->second; | ||
| 136 | switch (vma.type) { | 135 | switch (vma.type) { |
| 137 | case Kernel::VMAType::AllocatedMemoryBlock: | 136 | case Kernel::VMAType::AllocatedMemoryBlock: |
| 138 | direct_pointer = vma.backing_block->data() + vma.offset; | 137 | direct_pointer = vma.backing_block->data() + vma.offset; |
diff --git a/src/core/settings.h b/src/core/settings.h index a0c5fd447..de01b05c0 100644 --- a/src/core/settings.h +++ b/src/core/settings.h | |||
| @@ -6,8 +6,10 @@ | |||
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <atomic> | 8 | #include <atomic> |
| 9 | #include <map> | ||
| 9 | #include <optional> | 10 | #include <optional> |
| 10 | #include <string> | 11 | #include <string> |
| 12 | #include <vector> | ||
| 11 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 12 | 14 | ||
| 13 | namespace Settings { | 15 | namespace Settings { |
| @@ -411,6 +413,9 @@ struct Values { | |||
| 411 | std::string web_api_url; | 413 | std::string web_api_url; |
| 412 | std::string yuzu_username; | 414 | std::string yuzu_username; |
| 413 | std::string yuzu_token; | 415 | std::string yuzu_token; |
| 416 | |||
| 417 | // Add-Ons | ||
| 418 | std::map<u64, std::vector<std::string>> disabled_addons; | ||
| 414 | } extern values; | 419 | } extern values; |
| 415 | 420 | ||
| 416 | void Apply(); | 421 | void Apply(); |
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index a3b08c740..09ed74d78 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -103,13 +103,8 @@ bool VerifyLogin(const std::string& username, const std::string& token) { | |||
| 103 | 103 | ||
| 104 | TelemetrySession::TelemetrySession() { | 104 | TelemetrySession::TelemetrySession() { |
| 105 | #ifdef ENABLE_WEB_SERVICE | 105 | #ifdef ENABLE_WEB_SERVICE |
| 106 | if (Settings::values.enable_telemetry) { | 106 | backend = std::make_unique<WebService::TelemetryJson>( |
| 107 | backend = std::make_unique<WebService::TelemetryJson>(Settings::values.web_api_url, | 107 | Settings::values.web_api_url, Settings::values.yuzu_username, Settings::values.yuzu_token); |
| 108 | Settings::values.yuzu_username, | ||
| 109 | Settings::values.yuzu_token); | ||
| 110 | } else { | ||
| 111 | backend = std::make_unique<Telemetry::NullVisitor>(); | ||
| 112 | } | ||
| 113 | #else | 108 | #else |
| 114 | backend = std::make_unique<Telemetry::NullVisitor>(); | 109 | backend = std::make_unique<Telemetry::NullVisitor>(); |
| 115 | #endif | 110 | #endif |
| @@ -180,7 +175,8 @@ TelemetrySession::~TelemetrySession() { | |||
| 180 | // This is just a placeholder to wrap up the session once the core completes and this is | 175 | // This is just a placeholder to wrap up the session once the core completes and this is |
| 181 | // destroyed. This will be moved elsewhere once we are actually doing real I/O with the service. | 176 | // destroyed. This will be moved elsewhere once we are actually doing real I/O with the service. |
| 182 | field_collection.Accept(*backend); | 177 | field_collection.Accept(*backend); |
| 183 | backend->Complete(); | 178 | if (Settings::values.enable_telemetry) |
| 179 | backend->Complete(); | ||
| 184 | backend = nullptr; | 180 | backend = nullptr; |
| 185 | } | 181 | } |
| 186 | 182 | ||
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index 5ea094e64..5198cd268 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -575,7 +575,7 @@ union Instruction { | |||
| 575 | 575 | ||
| 576 | union { | 576 | union { |
| 577 | BitField<39, 2, u64> tab5cb8_2; | 577 | BitField<39, 2, u64> tab5cb8_2; |
| 578 | BitField<41, 3, u64> tab5c68_1; | 578 | BitField<41, 3, u64> postfactor; |
| 579 | BitField<44, 2, u64> tab5c68_0; | 579 | BitField<44, 2, u64> tab5c68_0; |
| 580 | BitField<48, 1, u64> negate_b; | 580 | BitField<48, 1, u64> negate_b; |
| 581 | } fmul; | 581 | } fmul; |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp index 038b25c75..aea6bf1af 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp | |||
| @@ -2,7 +2,9 @@ | |||
| 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 <boost/functional/hash.hpp> | ||
| 5 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 7 | #include "common/hash.h" | ||
| 6 | #include "core/core.h" | 8 | #include "core/core.h" |
| 7 | #include "core/memory.h" | 9 | #include "core/memory.h" |
| 8 | #include "video_core/engines/maxwell_3d.h" | 10 | #include "video_core/engines/maxwell_3d.h" |
| @@ -66,14 +68,17 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type) | |||
| 66 | // stage here. | 68 | // stage here. |
| 67 | setup.SetProgramB(GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB))); | 69 | setup.SetProgramB(GetShaderCode(GetShaderAddress(Maxwell::ShaderProgram::VertexB))); |
| 68 | case Maxwell::ShaderProgram::VertexB: | 70 | case Maxwell::ShaderProgram::VertexB: |
| 71 | CalculateProperties(); | ||
| 69 | program_result = GLShader::GenerateVertexShader(setup); | 72 | program_result = GLShader::GenerateVertexShader(setup); |
| 70 | gl_type = GL_VERTEX_SHADER; | 73 | gl_type = GL_VERTEX_SHADER; |
| 71 | break; | 74 | break; |
| 72 | case Maxwell::ShaderProgram::Geometry: | 75 | case Maxwell::ShaderProgram::Geometry: |
| 76 | CalculateProperties(); | ||
| 73 | program_result = GLShader::GenerateGeometryShader(setup); | 77 | program_result = GLShader::GenerateGeometryShader(setup); |
| 74 | gl_type = GL_GEOMETRY_SHADER; | 78 | gl_type = GL_GEOMETRY_SHADER; |
| 75 | break; | 79 | break; |
| 76 | case Maxwell::ShaderProgram::Fragment: | 80 | case Maxwell::ShaderProgram::Fragment: |
| 81 | CalculateProperties(); | ||
| 77 | program_result = GLShader::GenerateFragmentShader(setup); | 82 | program_result = GLShader::GenerateFragmentShader(setup); |
| 78 | gl_type = GL_FRAGMENT_SHADER; | 83 | gl_type = GL_FRAGMENT_SHADER; |
| 79 | break; | 84 | break; |
| @@ -140,6 +145,46 @@ GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program, | |||
| 140 | return target_program.handle; | 145 | return target_program.handle; |
| 141 | }; | 146 | }; |
| 142 | 147 | ||
| 148 | static bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) { | ||
| 149 | // sched instructions appear once every 4 instructions. | ||
| 150 | static constexpr std::size_t SchedPeriod = 4; | ||
| 151 | const std::size_t absolute_offset = offset - main_offset; | ||
| 152 | return (absolute_offset % SchedPeriod) == 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | static std::size_t CalculateProgramSize(const GLShader::ProgramCode& program) { | ||
| 156 | constexpr std::size_t start_offset = 10; | ||
| 157 | std::size_t offset = start_offset; | ||
| 158 | std::size_t size = start_offset * sizeof(u64); | ||
| 159 | while (offset < program.size()) { | ||
| 160 | const u64 inst = program[offset]; | ||
| 161 | if (!IsSchedInstruction(offset, start_offset)) { | ||
| 162 | if (inst == 0 || (inst >> 52) == 0x50b) { | ||
| 163 | break; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | size += sizeof(inst); | ||
| 167 | offset++; | ||
| 168 | } | ||
| 169 | return size; | ||
| 170 | } | ||
| 171 | |||
| 172 | void CachedShader::CalculateProperties() { | ||
| 173 | setup.program.real_size = CalculateProgramSize(setup.program.code); | ||
| 174 | setup.program.real_size_b = 0; | ||
| 175 | setup.program.unique_identifier = Common::CityHash64( | ||
| 176 | reinterpret_cast<const char*>(setup.program.code.data()), setup.program.real_size); | ||
| 177 | if (program_type == Maxwell::ShaderProgram::VertexA) { | ||
| 178 | std::size_t seed = 0; | ||
| 179 | boost::hash_combine(seed, setup.program.unique_identifier); | ||
| 180 | setup.program.real_size_b = CalculateProgramSize(setup.program.code_b); | ||
| 181 | const u64 identifier_b = Common::CityHash64( | ||
| 182 | reinterpret_cast<const char*>(setup.program.code_b.data()), setup.program.real_size_b); | ||
| 183 | boost::hash_combine(seed, identifier_b); | ||
| 184 | setup.program.unique_identifier = static_cast<u64>(seed); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 143 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {} | 188 | ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer) : RasterizerCache{rasterizer} {} |
| 144 | 189 | ||
| 145 | Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { | 190 | Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) { |
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h index 08f470de3..b4ef6030d 100644 --- a/src/video_core/renderer_opengl/gl_shader_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_cache.h | |||
| @@ -81,6 +81,8 @@ private: | |||
| 81 | GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology, | 81 | GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology, |
| 82 | u32 max_vertices, const std::string& debug_name); | 82 | u32 max_vertices, const std::string& debug_name); |
| 83 | 83 | ||
| 84 | void CalculateProperties(); | ||
| 85 | |||
| 84 | VAddr addr; | 86 | VAddr addr; |
| 85 | std::size_t shader_length; | 87 | std::size_t shader_length; |
| 86 | Maxwell::ShaderProgram program_type; | 88 | Maxwell::ShaderProgram program_type; |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index 4fc09cac6..bd61af463 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -928,7 +928,7 @@ private: | |||
| 928 | case Attribute::Index::FrontFacing: | 928 | case Attribute::Index::FrontFacing: |
| 929 | // TODO(Subv): Find out what the values are for the other elements. | 929 | // TODO(Subv): Find out what the values are for the other elements. |
| 930 | ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); | 930 | ASSERT(stage == Maxwell3D::Regs::ShaderStage::Fragment); |
| 931 | return "vec4(0, 0, 0, uintBitsToFloat(gl_FrontFacing ? 1 : 0))"; | 931 | return "vec4(0, 0, 0, intBitsToFloat(gl_FrontFacing ? -1 : 0))"; |
| 932 | default: | 932 | default: |
| 933 | const u32 index{static_cast<u32>(attribute) - | 933 | const u32 index{static_cast<u32>(attribute) - |
| 934 | static_cast<u32>(Attribute::Index::Attribute_0)}; | 934 | static_cast<u32>(Attribute::Index::Attribute_0)}; |
| @@ -1681,7 +1681,7 @@ private: | |||
| 1681 | for (size_t i = 0; i < coord_count; ++i) { | 1681 | for (size_t i = 0; i < coord_count; ++i) { |
| 1682 | const bool last = (i == (coord_count - 1)) && (coord_count > 1); | 1682 | const bool last = (i == (coord_count - 1)) && (coord_count > 1); |
| 1683 | coord += regs.GetRegisterAsFloat(last ? last_coord_register : coord_register + i); | 1683 | coord += regs.GetRegisterAsFloat(last ? last_coord_register : coord_register + i); |
| 1684 | if (!last) { | 1684 | if (i < coord_count - 1) { |
| 1685 | coord += ','; | 1685 | coord += ','; |
| 1686 | } | 1686 | } |
| 1687 | } | 1687 | } |
| @@ -1702,6 +1702,99 @@ private: | |||
| 1702 | is_array, (coord_count > 2 ? 1 : 0))); | 1702 | is_array, (coord_count > 2 ? 1 : 0))); |
| 1703 | } | 1703 | } |
| 1704 | 1704 | ||
| 1705 | std::pair<std::string, std::string> GetTLD4Code(const Instruction& instr, | ||
| 1706 | const Tegra::Shader::TextureType texture_type, | ||
| 1707 | const bool depth_compare, const bool is_array) { | ||
| 1708 | |||
| 1709 | const size_t coord_count = TextureCoordinates(texture_type); | ||
| 1710 | const size_t total_coord_count = coord_count + (is_array ? 1 : 0); | ||
| 1711 | const size_t total_reg_count = total_coord_count + (depth_compare ? 1 : 0); | ||
| 1712 | |||
| 1713 | constexpr std::array<const char*, 5> coord_container{ | ||
| 1714 | {"", "", "vec2 coord = vec2(", "vec3 coord = vec3(", "vec4 coord = vec4("}}; | ||
| 1715 | |||
| 1716 | // If enabled arrays index is always stored in the gpr8 field | ||
| 1717 | const u64 array_register = instr.gpr8.Value(); | ||
| 1718 | // First coordinate index is the gpr8 or gpr8 + 1 when arrays are used | ||
| 1719 | const u64 coord_register = array_register + (is_array ? 1 : 0); | ||
| 1720 | |||
| 1721 | std::string coord = coord_container[total_coord_count]; | ||
| 1722 | for (size_t i = 0; i < coord_count;) { | ||
| 1723 | coord += regs.GetRegisterAsFloat(coord_register + i); | ||
| 1724 | ++i; | ||
| 1725 | if (i != coord_count) { | ||
| 1726 | coord += ','; | ||
| 1727 | } | ||
| 1728 | } | ||
| 1729 | |||
| 1730 | if (is_array) { | ||
| 1731 | coord += ',' + regs.GetRegisterAsInteger(array_register); | ||
| 1732 | } | ||
| 1733 | coord += ");"; | ||
| 1734 | |||
| 1735 | const std::string sampler = | ||
| 1736 | GetSampler(instr.sampler, texture_type, is_array, depth_compare); | ||
| 1737 | |||
| 1738 | std::string texture = "textureGather(" + sampler + ", coord, "; | ||
| 1739 | if (depth_compare) { | ||
| 1740 | // Depth is always stored in the register signaled by gpr20 | ||
| 1741 | texture += regs.GetRegisterAsFloat(instr.gpr20.Value()) + ')'; | ||
| 1742 | } else { | ||
| 1743 | texture += std::to_string(instr.tld4.component) + ')'; | ||
| 1744 | } | ||
| 1745 | return std::make_pair(coord, texture); | ||
| 1746 | } | ||
| 1747 | |||
| 1748 | std::pair<std::string, std::string> GetTLDSCode(const Instruction& instr, | ||
| 1749 | const Tegra::Shader::TextureType texture_type, | ||
| 1750 | const bool is_array) { | ||
| 1751 | |||
| 1752 | const size_t coord_count = TextureCoordinates(texture_type); | ||
| 1753 | const size_t total_coord_count = coord_count + (is_array ? 1 : 0); | ||
| 1754 | const bool lod_enabled = | ||
| 1755 | instr.tlds.GetTextureProcessMode() == Tegra::Shader::TextureProcessMode::LL; | ||
| 1756 | |||
| 1757 | constexpr std::array<const char*, 4> coord_container{ | ||
| 1758 | {"", "int coord = (", "ivec2 coord = ivec2(", "ivec3 coord = ivec3("}}; | ||
| 1759 | |||
| 1760 | std::string coord = coord_container[total_coord_count]; | ||
| 1761 | |||
| 1762 | // If enabled arrays index is always stored in the gpr8 field | ||
| 1763 | const u64 array_register = instr.gpr8.Value(); | ||
| 1764 | |||
| 1765 | // if is array gpr20 is used | ||
| 1766 | const u64 coord_register = is_array ? instr.gpr20.Value() : instr.gpr8.Value(); | ||
| 1767 | |||
| 1768 | const u64 last_coord_register = | ||
| 1769 | ((coord_count > 2) || (coord_count == 2 && !lod_enabled)) && !is_array | ||
| 1770 | ? static_cast<u64>(instr.gpr20.Value()) | ||
| 1771 | : coord_register + 1; | ||
| 1772 | |||
| 1773 | for (size_t i = 0; i < coord_count; ++i) { | ||
| 1774 | const bool last = (i == (coord_count - 1)) && (coord_count > 1); | ||
| 1775 | coord += regs.GetRegisterAsInteger(last ? last_coord_register : coord_register + i); | ||
| 1776 | if (i < coord_count - 1) { | ||
| 1777 | coord += ','; | ||
| 1778 | } | ||
| 1779 | } | ||
| 1780 | if (is_array) { | ||
| 1781 | coord += ',' + regs.GetRegisterAsInteger(array_register); | ||
| 1782 | } | ||
| 1783 | coord += ");"; | ||
| 1784 | |||
| 1785 | const std::string sampler = GetSampler(instr.sampler, texture_type, is_array, false); | ||
| 1786 | |||
| 1787 | std::string texture = "texelFetch(" + sampler + ", coords"; | ||
| 1788 | |||
| 1789 | if (lod_enabled) { | ||
| 1790 | // When lod is used always is in grp20 | ||
| 1791 | texture += ", " + regs.GetRegisterAsInteger(instr.gpr20) + ')'; | ||
| 1792 | } else { | ||
| 1793 | texture += ", 0)"; | ||
| 1794 | } | ||
| 1795 | return std::make_pair(coord, texture); | ||
| 1796 | } | ||
| 1797 | |||
| 1705 | /** | 1798 | /** |
| 1706 | * Compiles a single instruction from Tegra to GLSL. | 1799 | * Compiles a single instruction from Tegra to GLSL. |
| 1707 | * @param offset the offset of the Tegra shader instruction. | 1800 | * @param offset the offset of the Tegra shader instruction. |
| @@ -1774,9 +1867,6 @@ private: | |||
| 1774 | UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0, | 1867 | UNIMPLEMENTED_IF_MSG(instr.fmul.tab5cb8_2 != 0, |
| 1775 | "FMUL tab5cb8_2({}) is not implemented", | 1868 | "FMUL tab5cb8_2({}) is not implemented", |
| 1776 | instr.fmul.tab5cb8_2.Value()); | 1869 | instr.fmul.tab5cb8_2.Value()); |
| 1777 | UNIMPLEMENTED_IF_MSG(instr.fmul.tab5c68_1 != 0, | ||
| 1778 | "FMUL tab5cb8_1({}) is not implemented", | ||
| 1779 | instr.fmul.tab5c68_1.Value()); | ||
| 1780 | UNIMPLEMENTED_IF_MSG( | 1870 | UNIMPLEMENTED_IF_MSG( |
| 1781 | instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented", | 1871 | instr.fmul.tab5c68_0 != 1, "FMUL tab5cb8_0({}) is not implemented", |
| 1782 | instr.fmul.tab5c68_0 | 1872 | instr.fmul.tab5c68_0 |
| @@ -1786,7 +1876,26 @@ private: | |||
| 1786 | 1876 | ||
| 1787 | op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); | 1877 | op_b = GetOperandAbsNeg(op_b, false, instr.fmul.negate_b); |
| 1788 | 1878 | ||
| 1789 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, | 1879 | std::string postfactor_op; |
| 1880 | if (instr.fmul.postfactor != 0) { | ||
| 1881 | s8 postfactor = static_cast<s8>(instr.fmul.postfactor); | ||
| 1882 | |||
| 1883 | // postfactor encoded as 3-bit 1's complement in instruction, | ||
| 1884 | // interpreted with below logic. | ||
| 1885 | if (postfactor >= 4) { | ||
| 1886 | postfactor = 7 - postfactor; | ||
| 1887 | } else { | ||
| 1888 | postfactor = 0 - postfactor; | ||
| 1889 | } | ||
| 1890 | |||
| 1891 | if (postfactor > 0) { | ||
| 1892 | postfactor_op = " * " + std::to_string(1 << postfactor); | ||
| 1893 | } else { | ||
| 1894 | postfactor_op = " / " + std::to_string(1 << -postfactor); | ||
| 1895 | } | ||
| 1896 | } | ||
| 1897 | |||
| 1898 | regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + postfactor_op, 1, 1, | ||
| 1790 | instr.alu.saturate_d, 0, true); | 1899 | instr.alu.saturate_d, 0, true); |
| 1791 | break; | 1900 | break; |
| 1792 | } | 1901 | } |
| @@ -2825,9 +2934,6 @@ private: | |||
| 2825 | const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; | 2934 | const Tegra::Shader::TextureType texture_type{instr.tlds.GetTextureType()}; |
| 2826 | const bool is_array{instr.tlds.IsArrayTexture()}; | 2935 | const bool is_array{instr.tlds.IsArrayTexture()}; |
| 2827 | 2936 | ||
| 2828 | ASSERT(texture_type == Tegra::Shader::TextureType::Texture2D); | ||
| 2829 | ASSERT(is_array == false); | ||
| 2830 | |||
| 2831 | UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | 2937 | UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), |
| 2832 | "NODEP is not implemented"); | 2938 | "NODEP is not implemented"); |
| 2833 | UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), | 2939 | UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI), |
| @@ -2835,54 +2941,16 @@ private: | |||
| 2835 | UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), | 2941 | UNIMPLEMENTED_IF_MSG(instr.tlds.UsesMiscMode(Tegra::Shader::TextureMiscMode::MZ), |
| 2836 | "MZ is not implemented"); | 2942 | "MZ is not implemented"); |
| 2837 | 2943 | ||
| 2838 | u32 extra_op_offset = 0; | 2944 | const auto [coord, texture] = GetTLDSCode(instr, texture_type, is_array); |
| 2839 | |||
| 2840 | ShaderScopedScope scope = shader.Scope(); | ||
| 2841 | |||
| 2842 | switch (texture_type) { | ||
| 2843 | case Tegra::Shader::TextureType::Texture1D: { | ||
| 2844 | const std::string x = regs.GetRegisterAsInteger(instr.gpr8); | ||
| 2845 | shader.AddLine("float coords = " + x + ';'); | ||
| 2846 | break; | ||
| 2847 | } | ||
| 2848 | case Tegra::Shader::TextureType::Texture2D: { | ||
| 2849 | UNIMPLEMENTED_IF_MSG(is_array, "Unhandled 2d array texture"); | ||
| 2850 | |||
| 2851 | const std::string x = regs.GetRegisterAsInteger(instr.gpr8); | ||
| 2852 | const std::string y = regs.GetRegisterAsInteger(instr.gpr20); | ||
| 2853 | // shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");"); | ||
| 2854 | shader.AddLine("ivec2 coords = ivec2(" + x + ", " + y + ");"); | ||
| 2855 | extra_op_offset = 1; | ||
| 2856 | break; | ||
| 2857 | } | ||
| 2858 | default: | ||
| 2859 | UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<u32>(texture_type)); | ||
| 2860 | } | ||
| 2861 | const std::string sampler = | ||
| 2862 | GetSampler(instr.sampler, texture_type, is_array, false); | ||
| 2863 | 2945 | ||
| 2864 | const std::string texture = [&]() { | 2946 | const auto scope = shader.Scope(); |
| 2865 | switch (instr.tlds.GetTextureProcessMode()) { | ||
| 2866 | case Tegra::Shader::TextureProcessMode::LZ: | ||
| 2867 | return "texelFetch(" + sampler + ", coords, 0)"; | ||
| 2868 | case Tegra::Shader::TextureProcessMode::LL: | ||
| 2869 | shader.AddLine( | ||
| 2870 | "float lod = " + | ||
| 2871 | regs.GetRegisterAsInteger(instr.gpr20.Value() + extra_op_offset) + ';'); | ||
| 2872 | return "texelFetch(" + sampler + ", coords, lod)"; | ||
| 2873 | default: | ||
| 2874 | UNIMPLEMENTED_MSG("Unhandled texture process mode {}", | ||
| 2875 | static_cast<u32>(instr.tlds.GetTextureProcessMode())); | ||
| 2876 | return "texelFetch(" + sampler + ", coords, 0)"; | ||
| 2877 | } | ||
| 2878 | }(); | ||
| 2879 | 2947 | ||
| 2880 | WriteTexsInstructionFloat(instr, texture); | 2948 | shader.AddLine(coord); |
| 2949 | shader.AddLine("vec4 texture_tmp = " + texture + ';'); | ||
| 2950 | WriteTexsInstructionFloat(instr, "texture_tmp"); | ||
| 2881 | break; | 2951 | break; |
| 2882 | } | 2952 | } |
| 2883 | case OpCode::Id::TLD4: { | 2953 | case OpCode::Id::TLD4: { |
| 2884 | ASSERT(instr.tld4.texture_type == Tegra::Shader::TextureType::Texture2D); | ||
| 2885 | ASSERT(instr.tld4.array == 0); | ||
| 2886 | 2954 | ||
| 2887 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), | 2955 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP), |
| 2888 | "NODEP is not implemented"); | 2956 | "NODEP is not implemented"); |
| @@ -2892,56 +2960,29 @@ private: | |||
| 2892 | "NDV is not implemented"); | 2960 | "NDV is not implemented"); |
| 2893 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), | 2961 | UNIMPLEMENTED_IF_MSG(instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP), |
| 2894 | "PTP is not implemented"); | 2962 | "PTP is not implemented"); |
| 2963 | |||
| 2964 | auto texture_type = instr.tld4.texture_type.Value(); | ||
| 2895 | const bool depth_compare = | 2965 | const bool depth_compare = |
| 2896 | instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); | 2966 | instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); |
| 2897 | auto texture_type = instr.tld4.texture_type.Value(); | 2967 | const bool is_array = instr.tld4.array != 0; |
| 2898 | u32 num_coordinates = TextureCoordinates(texture_type); | ||
| 2899 | if (depth_compare) | ||
| 2900 | num_coordinates += 1; | ||
| 2901 | 2968 | ||
| 2902 | const auto scope = shader.Scope(); | 2969 | const auto [coord, texture] = |
| 2903 | 2970 | GetTLD4Code(instr, texture_type, depth_compare, is_array); | |
| 2904 | switch (num_coordinates) { | ||
| 2905 | case 2: { | ||
| 2906 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2907 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2908 | shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); | ||
| 2909 | break; | ||
| 2910 | } | ||
| 2911 | case 3: { | ||
| 2912 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2913 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2914 | const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2); | ||
| 2915 | shader.AddLine("vec3 coords = vec3(" + x + ", " + y + ", " + z + ");"); | ||
| 2916 | break; | ||
| 2917 | } | ||
| 2918 | default: | ||
| 2919 | UNIMPLEMENTED_MSG("Unhandled coordinates number {}", | ||
| 2920 | static_cast<u32>(num_coordinates)); | ||
| 2921 | const std::string x = regs.GetRegisterAsFloat(instr.gpr8); | ||
| 2922 | const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2923 | shader.AddLine("vec2 coords = vec2(" + x + ", " + y + ");"); | ||
| 2924 | texture_type = Tegra::Shader::TextureType::Texture2D; | ||
| 2925 | } | ||
| 2926 | 2971 | ||
| 2927 | const std::string sampler = | 2972 | const auto scope = shader.Scope(); |
| 2928 | GetSampler(instr.sampler, texture_type, false, depth_compare); | ||
| 2929 | 2973 | ||
| 2930 | const std::string texture = "textureGather(" + sampler + ", coords, " + | 2974 | shader.AddLine(coord); |
| 2931 | std::to_string(instr.tld4.component) + ')'; | 2975 | std::size_t dest_elem{}; |
| 2932 | 2976 | ||
| 2933 | if (depth_compare) { | 2977 | shader.AddLine("vec4 texture_tmp = " + texture + ';'); |
| 2934 | regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false); | 2978 | for (std::size_t elem = 0; elem < 4; ++elem) { |
| 2935 | } else { | 2979 | if (!instr.tex.IsComponentEnabled(elem)) { |
| 2936 | std::size_t dest_elem{}; | 2980 | // Skip disabled components |
| 2937 | for (std::size_t elem = 0; elem < 4; ++elem) { | 2981 | continue; |
| 2938 | if (!instr.tex.IsComponentEnabled(elem)) { | ||
| 2939 | // Skip disabled components | ||
| 2940 | continue; | ||
| 2941 | } | ||
| 2942 | regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem); | ||
| 2943 | ++dest_elem; | ||
| 2944 | } | 2982 | } |
| 2983 | regs.SetRegisterToFloat(instr.gpr0, elem, "texture_tmp", 1, 4, false, | ||
| 2984 | dest_elem); | ||
| 2985 | ++dest_elem; | ||
| 2945 | } | 2986 | } |
| 2946 | break; | 2987 | break; |
| 2947 | } | 2988 | } |
| @@ -2955,28 +2996,31 @@ private: | |||
| 2955 | 2996 | ||
| 2956 | const auto scope = shader.Scope(); | 2997 | const auto scope = shader.Scope(); |
| 2957 | 2998 | ||
| 2999 | std::string coords; | ||
| 3000 | |||
| 2958 | const bool depth_compare = | 3001 | const bool depth_compare = |
| 2959 | instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); | 3002 | instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC); |
| 2960 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); | 3003 | |
| 2961 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 2962 | // TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction. | ||
| 2963 | const std::string sampler = GetSampler( | 3004 | const std::string sampler = GetSampler( |
| 2964 | instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); | 3005 | instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare); |
| 2965 | if (depth_compare) { | ||
| 2966 | // Note: TLD4S coordinate encoding works just like TEXS's | ||
| 2967 | const std::string op_y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 2968 | shader.AddLine("vec3 coords = vec3(" + op_a + ", " + op_y + ", " + op_b + ");"); | ||
| 2969 | } else { | ||
| 2970 | shader.AddLine("vec2 coords = vec2(" + op_a + ", " + op_b + ");"); | ||
| 2971 | } | ||
| 2972 | 3006 | ||
| 2973 | std::string texture = "textureGather(" + sampler + ", coords, " + | 3007 | const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8); |
| 2974 | std::to_string(instr.tld4s.component) + ')'; | 3008 | coords = "vec2 coords = vec2(" + op_a + ", "; |
| 2975 | if (depth_compare) { | 3009 | std::string texture = "textureGather(" + sampler + ", coords, "; |
| 2976 | texture = "vec4(" + texture + ')'; | ||
| 2977 | } | ||
| 2978 | 3010 | ||
| 2979 | WriteTexsInstructionFloat(instr, texture); | 3011 | if (!depth_compare) { |
| 3012 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 3013 | coords += op_b + ");"; | ||
| 3014 | texture += std::to_string(instr.tld4s.component) + ')'; | ||
| 3015 | } else { | ||
| 3016 | const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1); | ||
| 3017 | const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20); | ||
| 3018 | coords += op_b + ");"; | ||
| 3019 | texture += op_c + ')'; | ||
| 3020 | } | ||
| 3021 | shader.AddLine(coords); | ||
| 3022 | shader.AddLine("vec4 texture_tmp = " + texture + ';'); | ||
| 3023 | WriteTexsInstructionFloat(instr, "texture_tmp"); | ||
| 2980 | break; | 3024 | break; |
| 2981 | } | 3025 | } |
| 2982 | case OpCode::Id::TXQ: { | 3026 | case OpCode::Id::TXQ: { |
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.cpp b/src/video_core/renderer_opengl/gl_shader_gen.cpp index 23ed91e27..5d0819dc5 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.cpp +++ b/src/video_core/renderer_opengl/gl_shader_gen.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 <fmt/format.h> | ||
| 5 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 6 | #include "video_core/engines/maxwell_3d.h" | 7 | #include "video_core/engines/maxwell_3d.h" |
| 7 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" | 8 | #include "video_core/renderer_opengl/gl_shader_decompiler.h" |
| @@ -16,6 +17,8 @@ static constexpr u32 PROGRAM_OFFSET{10}; | |||
| 16 | ProgramResult GenerateVertexShader(const ShaderSetup& setup) { | 17 | ProgramResult GenerateVertexShader(const ShaderSetup& setup) { |
| 17 | std::string out = "#version 430 core\n"; | 18 | std::string out = "#version 430 core\n"; |
| 18 | out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; | 19 | out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; |
| 20 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | ||
| 21 | out += "// Shader Unique Id: VS" + id + "\n\n"; | ||
| 19 | out += Decompiler::GetCommonDeclarations(); | 22 | out += Decompiler::GetCommonDeclarations(); |
| 20 | 23 | ||
| 21 | out += R"( | 24 | out += R"( |
| @@ -84,6 +87,8 @@ void main() { | |||
| 84 | ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { | 87 | ProgramResult GenerateGeometryShader(const ShaderSetup& setup) { |
| 85 | // Version is intentionally skipped in shader generation, it's added by the lazy compilation. | 88 | // Version is intentionally skipped in shader generation, it's added by the lazy compilation. |
| 86 | std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; | 89 | std::string out = "#extension GL_ARB_separate_shader_objects : enable\n\n"; |
| 90 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | ||
| 91 | out += "// Shader Unique Id: GS" + id + "\n\n"; | ||
| 87 | out += Decompiler::GetCommonDeclarations(); | 92 | out += Decompiler::GetCommonDeclarations(); |
| 88 | out += "bool exec_geometry();\n"; | 93 | out += "bool exec_geometry();\n"; |
| 89 | 94 | ||
| @@ -117,6 +122,8 @@ void main() { | |||
| 117 | ProgramResult GenerateFragmentShader(const ShaderSetup& setup) { | 122 | ProgramResult GenerateFragmentShader(const ShaderSetup& setup) { |
| 118 | std::string out = "#version 430 core\n"; | 123 | std::string out = "#version 430 core\n"; |
| 119 | out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; | 124 | out += "#extension GL_ARB_separate_shader_objects : enable\n\n"; |
| 125 | const std::string id = fmt::format("{:016x}", setup.program.unique_identifier); | ||
| 126 | out += "// Shader Unique Id: FS" + id + "\n\n"; | ||
| 120 | out += Decompiler::GetCommonDeclarations(); | 127 | out += Decompiler::GetCommonDeclarations(); |
| 121 | out += "bool exec_fragment();\n"; | 128 | out += "bool exec_fragment();\n"; |
| 122 | 129 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_gen.h b/src/video_core/renderer_opengl/gl_shader_gen.h index 4fa6d7612..fcc20d3b4 100644 --- a/src/video_core/renderer_opengl/gl_shader_gen.h +++ b/src/video_core/renderer_opengl/gl_shader_gen.h | |||
| @@ -177,6 +177,9 @@ struct ShaderSetup { | |||
| 177 | struct { | 177 | struct { |
| 178 | ProgramCode code; | 178 | ProgramCode code; |
| 179 | ProgramCode code_b; // Used for dual vertex shaders | 179 | ProgramCode code_b; // Used for dual vertex shaders |
| 180 | u64 unique_identifier; | ||
| 181 | std::size_t real_size; | ||
| 182 | std::size_t real_size_b; | ||
| 180 | } program; | 183 | } program; |
| 181 | 184 | ||
| 182 | /// Used in scenarios where we have a dual vertex shaders | 185 | /// Used in scenarios where we have a dual vertex shaders |
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index cfca8f4a8..3232aa8fb 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt | |||
| @@ -35,6 +35,8 @@ add_executable(yuzu | |||
| 35 | configuration/configure_mouse_advanced.h | 35 | configuration/configure_mouse_advanced.h |
| 36 | configuration/configure_system.cpp | 36 | configuration/configure_system.cpp |
| 37 | configuration/configure_system.h | 37 | configuration/configure_system.h |
| 38 | configuration/configure_per_general.cpp | ||
| 39 | configuration/configure_per_general.h | ||
| 38 | configuration/configure_touchscreen_advanced.cpp | 40 | configuration/configure_touchscreen_advanced.cpp |
| 39 | configuration/configure_touchscreen_advanced.h | 41 | configuration/configure_touchscreen_advanced.h |
| 40 | configuration/configure_web.cpp | 42 | configuration/configure_web.cpp |
| @@ -86,6 +88,7 @@ set(UIS | |||
| 86 | configuration/configure_input.ui | 88 | configuration/configure_input.ui |
| 87 | configuration/configure_input_player.ui | 89 | configuration/configure_input_player.ui |
| 88 | configuration/configure_mouse_advanced.ui | 90 | configuration/configure_mouse_advanced.ui |
| 91 | configuration/configure_per_general.ui | ||
| 89 | configuration/configure_system.ui | 92 | configuration/configure_system.ui |
| 90 | configuration/configure_touchscreen_advanced.ui | 93 | configuration/configure_touchscreen_advanced.ui |
| 91 | configuration/configure_web.ui | 94 | configuration/configure_web.ui |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index c26161169..eb2077b0f 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -441,6 +441,21 @@ void Config::ReadValues() { | |||
| 441 | Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString(); | 441 | Settings::values.yuzu_token = qt_config->value("yuzu_token").toString().toStdString(); |
| 442 | qt_config->endGroup(); | 442 | qt_config->endGroup(); |
| 443 | 443 | ||
| 444 | const auto size = qt_config->beginReadArray("DisabledAddOns"); | ||
| 445 | for (int i = 0; i < size; ++i) { | ||
| 446 | qt_config->setArrayIndex(i); | ||
| 447 | const auto title_id = qt_config->value("title_id", 0).toULongLong(); | ||
| 448 | std::vector<std::string> out; | ||
| 449 | const auto d_size = qt_config->beginReadArray("disabled"); | ||
| 450 | for (int j = 0; j < d_size; ++j) { | ||
| 451 | qt_config->setArrayIndex(j); | ||
| 452 | out.push_back(qt_config->value("d", "").toString().toStdString()); | ||
| 453 | } | ||
| 454 | qt_config->endArray(); | ||
| 455 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | ||
| 456 | } | ||
| 457 | qt_config->endArray(); | ||
| 458 | |||
| 444 | qt_config->beginGroup("UI"); | 459 | qt_config->beginGroup("UI"); |
| 445 | UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); | 460 | UISettings::values.theme = qt_config->value("theme", UISettings::themes[0].second).toString(); |
| 446 | UISettings::values.enable_discord_presence = | 461 | UISettings::values.enable_discord_presence = |
| @@ -645,6 +660,21 @@ void Config::SaveValues() { | |||
| 645 | qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); | 660 | qt_config->setValue("yuzu_token", QString::fromStdString(Settings::values.yuzu_token)); |
| 646 | qt_config->endGroup(); | 661 | qt_config->endGroup(); |
| 647 | 662 | ||
| 663 | qt_config->beginWriteArray("DisabledAddOns"); | ||
| 664 | int i = 0; | ||
| 665 | for (const auto& elem : Settings::values.disabled_addons) { | ||
| 666 | qt_config->setArrayIndex(i); | ||
| 667 | qt_config->setValue("title_id", QVariant::fromValue<u64>(elem.first)); | ||
| 668 | qt_config->beginWriteArray("disabled"); | ||
| 669 | for (std::size_t j = 0; j < elem.second.size(); ++j) { | ||
| 670 | qt_config->setArrayIndex(j); | ||
| 671 | qt_config->setValue("d", QString::fromStdString(elem.second[j])); | ||
| 672 | } | ||
| 673 | qt_config->endArray(); | ||
| 674 | ++i; | ||
| 675 | } | ||
| 676 | qt_config->endArray(); | ||
| 677 | |||
| 648 | qt_config->beginGroup("UI"); | 678 | qt_config->beginGroup("UI"); |
| 649 | qt_config->setValue("theme", UISettings::values.theme); | 679 | qt_config->setValue("theme", UISettings::values.theme); |
| 650 | qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); | 680 | qt_config->setValue("enable_discord_presence", UISettings::values.enable_discord_presence); |
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp index eb1da0f9e..5d9ccc6e8 100644 --- a/src/yuzu/configuration/configure_audio.cpp +++ b/src/yuzu/configuration/configure_audio.cpp | |||
| @@ -17,8 +17,8 @@ ConfigureAudio::ConfigureAudio(QWidget* parent) | |||
| 17 | 17 | ||
| 18 | ui->output_sink_combo_box->clear(); | 18 | ui->output_sink_combo_box->clear(); |
| 19 | ui->output_sink_combo_box->addItem("auto"); | 19 | ui->output_sink_combo_box->addItem("auto"); |
| 20 | for (const auto& sink_detail : AudioCore::g_sink_details) { | 20 | for (const char* id : AudioCore::GetSinkIDs()) { |
| 21 | ui->output_sink_combo_box->addItem(sink_detail.id); | 21 | ui->output_sink_combo_box->addItem(id); |
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | connect(ui->volume_slider, &QSlider::valueChanged, this, | 24 | connect(ui->volume_slider, &QSlider::valueChanged, this, |
| @@ -97,8 +97,7 @@ void ConfigureAudio::updateAudioDevices(int sink_index) { | |||
| 97 | ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); | 97 | ui->audio_device_combo_box->addItem(AudioCore::auto_device_name); |
| 98 | 98 | ||
| 99 | const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); | 99 | const std::string sink_id = ui->output_sink_combo_box->itemText(sink_index).toStdString(); |
| 100 | const std::vector<std::string> device_list = AudioCore::GetSinkDetails(sink_id).list_devices(); | 100 | for (const auto& device : AudioCore::GetDeviceListForSink(sink_id)) { |
| 101 | for (const auto& device : device_list) { | ||
| 102 | ui->audio_device_combo_box->addItem(QString::fromStdString(device)); | 101 | ui->audio_device_combo_box->addItem(QString::fromStdString(device)); |
| 103 | } | 102 | } |
| 104 | } | 103 | } |
diff --git a/src/yuzu/configuration/configure_per_general.cpp b/src/yuzu/configuration/configure_per_general.cpp new file mode 100644 index 000000000..80109b434 --- /dev/null +++ b/src/yuzu/configuration/configure_per_general.cpp | |||
| @@ -0,0 +1,170 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <memory> | ||
| 7 | #include <utility> | ||
| 8 | |||
| 9 | #include <QHeaderView> | ||
| 10 | #include <QMenu> | ||
| 11 | #include <QMessageBox> | ||
| 12 | #include <QStandardItemModel> | ||
| 13 | #include <QString> | ||
| 14 | #include <QTimer> | ||
| 15 | #include <QTreeView> | ||
| 16 | |||
| 17 | #include "core/file_sys/control_metadata.h" | ||
| 18 | #include "core/file_sys/patch_manager.h" | ||
| 19 | #include "core/file_sys/xts_archive.h" | ||
| 20 | #include "core/loader/loader.h" | ||
| 21 | #include "ui_configure_per_general.h" | ||
| 22 | #include "yuzu/configuration/config.h" | ||
| 23 | #include "yuzu/configuration/configure_input.h" | ||
| 24 | #include "yuzu/configuration/configure_per_general.h" | ||
| 25 | #include "yuzu/ui_settings.h" | ||
| 26 | #include "yuzu/util/util.h" | ||
| 27 | |||
| 28 | ConfigurePerGameGeneral::ConfigurePerGameGeneral(QWidget* parent, u64 title_id) | ||
| 29 | : QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGameGeneral>()), title_id(title_id) { | ||
| 30 | |||
| 31 | ui->setupUi(this); | ||
| 32 | setFocusPolicy(Qt::ClickFocus); | ||
| 33 | setWindowTitle(tr("Properties")); | ||
| 34 | |||
| 35 | layout = new QVBoxLayout; | ||
| 36 | tree_view = new QTreeView; | ||
| 37 | item_model = new QStandardItemModel(tree_view); | ||
| 38 | tree_view->setModel(item_model); | ||
| 39 | tree_view->setAlternatingRowColors(true); | ||
| 40 | tree_view->setSelectionMode(QHeaderView::SingleSelection); | ||
| 41 | tree_view->setSelectionBehavior(QHeaderView::SelectRows); | ||
| 42 | tree_view->setVerticalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 43 | tree_view->setHorizontalScrollMode(QHeaderView::ScrollPerPixel); | ||
| 44 | tree_view->setSortingEnabled(true); | ||
| 45 | tree_view->setEditTriggers(QHeaderView::NoEditTriggers); | ||
| 46 | tree_view->setUniformRowHeights(true); | ||
| 47 | tree_view->setContextMenuPolicy(Qt::NoContextMenu); | ||
| 48 | |||
| 49 | item_model->insertColumns(0, 2); | ||
| 50 | item_model->setHeaderData(0, Qt::Horizontal, "Patch Name"); | ||
| 51 | item_model->setHeaderData(1, Qt::Horizontal, "Version"); | ||
| 52 | |||
| 53 | // We must register all custom types with the Qt Automoc system so that we are able to use it | ||
| 54 | // with signals/slots. In this case, QList falls under the umbrells of custom types. | ||
| 55 | qRegisterMetaType<QList<QStandardItem*>>("QList<QStandardItem*>"); | ||
| 56 | |||
| 57 | layout->setContentsMargins(0, 0, 0, 0); | ||
| 58 | layout->setSpacing(0); | ||
| 59 | layout->addWidget(tree_view); | ||
| 60 | |||
| 61 | ui->scrollArea->setLayout(layout); | ||
| 62 | |||
| 63 | scene = new QGraphicsScene; | ||
| 64 | ui->icon_view->setScene(scene); | ||
| 65 | |||
| 66 | connect(item_model, &QStandardItemModel::itemChanged, | ||
| 67 | [] { UISettings::values.is_game_list_reload_pending.exchange(true); }); | ||
| 68 | |||
| 69 | this->loadConfiguration(); | ||
| 70 | } | ||
| 71 | |||
| 72 | ConfigurePerGameGeneral::~ConfigurePerGameGeneral() = default; | ||
| 73 | |||
| 74 | void ConfigurePerGameGeneral::applyConfiguration() { | ||
| 75 | std::vector<std::string> disabled_addons; | ||
| 76 | |||
| 77 | for (const auto& item : list_items) { | ||
| 78 | const auto disabled = item.front()->checkState() == Qt::Unchecked; | ||
| 79 | if (disabled) | ||
| 80 | disabled_addons.push_back(item.front()->text().toStdString()); | ||
| 81 | } | ||
| 82 | |||
| 83 | Settings::values.disabled_addons[title_id] = disabled_addons; | ||
| 84 | } | ||
| 85 | |||
| 86 | void ConfigurePerGameGeneral::loadFromFile(FileSys::VirtualFile file) { | ||
| 87 | this->file = std::move(file); | ||
| 88 | this->loadConfiguration(); | ||
| 89 | } | ||
| 90 | |||
| 91 | void ConfigurePerGameGeneral::loadConfiguration() { | ||
| 92 | if (file == nullptr) | ||
| 93 | return; | ||
| 94 | |||
| 95 | const auto loader = Loader::GetLoader(file); | ||
| 96 | |||
| 97 | ui->display_title_id->setText(fmt::format("{:016X}", title_id).c_str()); | ||
| 98 | |||
| 99 | FileSys::PatchManager pm{title_id}; | ||
| 100 | const auto control = pm.GetControlMetadata(); | ||
| 101 | |||
| 102 | if (control.first != nullptr) { | ||
| 103 | ui->display_version->setText(QString::fromStdString(control.first->GetVersionString())); | ||
| 104 | ui->display_name->setText(QString::fromStdString(control.first->GetApplicationName())); | ||
| 105 | ui->display_developer->setText(QString::fromStdString(control.first->GetDeveloperName())); | ||
| 106 | } else { | ||
| 107 | std::string title; | ||
| 108 | if (loader->ReadTitle(title) == Loader::ResultStatus::Success) | ||
| 109 | ui->display_name->setText(QString::fromStdString(title)); | ||
| 110 | |||
| 111 | std::string developer; | ||
| 112 | if (loader->ReadDeveloper(developer) == Loader::ResultStatus::Success) | ||
| 113 | ui->display_developer->setText(QString::fromStdString(developer)); | ||
| 114 | |||
| 115 | ui->display_version->setText(QStringLiteral("1.0.0")); | ||
| 116 | } | ||
| 117 | |||
| 118 | if (control.second != nullptr) { | ||
| 119 | scene->clear(); | ||
| 120 | |||
| 121 | QPixmap map; | ||
| 122 | const auto bytes = control.second->ReadAllBytes(); | ||
| 123 | map.loadFromData(bytes.data(), bytes.size()); | ||
| 124 | |||
| 125 | scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), | ||
| 126 | Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); | ||
| 127 | } else { | ||
| 128 | std::vector<u8> bytes; | ||
| 129 | if (loader->ReadIcon(bytes) == Loader::ResultStatus::Success) { | ||
| 130 | scene->clear(); | ||
| 131 | |||
| 132 | QPixmap map; | ||
| 133 | map.loadFromData(bytes.data(), bytes.size()); | ||
| 134 | |||
| 135 | scene->addPixmap(map.scaled(ui->icon_view->width(), ui->icon_view->height(), | ||
| 136 | Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | |||
| 140 | FileSys::VirtualFile update_raw; | ||
| 141 | loader->ReadUpdateRaw(update_raw); | ||
| 142 | |||
| 143 | const auto& disabled = Settings::values.disabled_addons[title_id]; | ||
| 144 | |||
| 145 | for (const auto& patch : pm.GetPatchVersionNames(update_raw)) { | ||
| 146 | QStandardItem* first_item = new QStandardItem; | ||
| 147 | const auto name = QString::fromStdString(patch.first).replace("[D] ", ""); | ||
| 148 | first_item->setText(name); | ||
| 149 | first_item->setCheckable(true); | ||
| 150 | |||
| 151 | const auto patch_disabled = | ||
| 152 | std::find(disabled.begin(), disabled.end(), name.toStdString()) != disabled.end(); | ||
| 153 | |||
| 154 | first_item->setCheckState(patch_disabled ? Qt::Unchecked : Qt::Checked); | ||
| 155 | |||
| 156 | list_items.push_back(QList<QStandardItem*>{ | ||
| 157 | first_item, new QStandardItem{QString::fromStdString(patch.second)}}); | ||
| 158 | item_model->appendRow(list_items.back()); | ||
| 159 | } | ||
| 160 | |||
| 161 | tree_view->setColumnWidth(0, 5 * tree_view->width() / 16); | ||
| 162 | |||
| 163 | ui->display_filename->setText(QString::fromStdString(file->GetName())); | ||
| 164 | |||
| 165 | ui->display_format->setText( | ||
| 166 | QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))); | ||
| 167 | |||
| 168 | const auto valueText = ReadableByteSize(file->GetSize()); | ||
| 169 | ui->display_size->setText(valueText); | ||
| 170 | } | ||
diff --git a/src/yuzu/configuration/configure_per_general.h b/src/yuzu/configuration/configure_per_general.h new file mode 100644 index 000000000..a4494446c --- /dev/null +++ b/src/yuzu/configuration/configure_per_general.h | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // Copyright 2016 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 <memory> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include <QKeyEvent> | ||
| 11 | #include <QList> | ||
| 12 | #include <QWidget> | ||
| 13 | |||
| 14 | #include "core/file_sys/vfs_types.h" | ||
| 15 | |||
| 16 | class QTreeView; | ||
| 17 | class QGraphicsScene; | ||
| 18 | class QStandardItem; | ||
| 19 | class QStandardItemModel; | ||
| 20 | |||
| 21 | namespace Ui { | ||
| 22 | class ConfigurePerGameGeneral; | ||
| 23 | } | ||
| 24 | |||
| 25 | class ConfigurePerGameGeneral : public QDialog { | ||
| 26 | Q_OBJECT | ||
| 27 | |||
| 28 | public: | ||
| 29 | explicit ConfigurePerGameGeneral(QWidget* parent, u64 title_id); | ||
| 30 | ~ConfigurePerGameGeneral() override; | ||
| 31 | |||
| 32 | /// Save all button configurations to settings file | ||
| 33 | void applyConfiguration(); | ||
| 34 | |||
| 35 | void loadFromFile(FileSys::VirtualFile file); | ||
| 36 | |||
| 37 | private: | ||
| 38 | std::unique_ptr<Ui::ConfigurePerGameGeneral> ui; | ||
| 39 | FileSys::VirtualFile file; | ||
| 40 | u64 title_id; | ||
| 41 | |||
| 42 | QVBoxLayout* layout; | ||
| 43 | QTreeView* tree_view; | ||
| 44 | QStandardItemModel* item_model; | ||
| 45 | QGraphicsScene* scene; | ||
| 46 | |||
| 47 | std::vector<QList<QStandardItem*>> list_items; | ||
| 48 | |||
| 49 | void loadConfiguration(); | ||
| 50 | }; | ||
diff --git a/src/yuzu/configuration/configure_per_general.ui b/src/yuzu/configuration/configure_per_general.ui new file mode 100644 index 000000000..8fdd96fa4 --- /dev/null +++ b/src/yuzu/configuration/configure_per_general.ui | |||
| @@ -0,0 +1,276 @@ | |||
| 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | <ui version="4.0"> | ||
| 3 | <class>ConfigurePerGameGeneral</class> | ||
| 4 | <widget class="QDialog" name="ConfigurePerGameGeneral"> | ||
| 5 | <property name="geometry"> | ||
| 6 | <rect> | ||
| 7 | <x>0</x> | ||
| 8 | <y>0</y> | ||
| 9 | <width>400</width> | ||
| 10 | <height>520</height> | ||
| 11 | </rect> | ||
| 12 | </property> | ||
| 13 | <property name="windowTitle"> | ||
| 14 | <string>ConfigurePerGameGeneral</string> | ||
| 15 | </property> | ||
| 16 | <layout class="QHBoxLayout" name="HorizontalLayout"> | ||
| 17 | <item> | ||
| 18 | <layout class="QVBoxLayout" name="VerticalLayout"> | ||
| 19 | <item> | ||
| 20 | <widget class="QGroupBox" name="GeneralGroupBox"> | ||
| 21 | <property name="title"> | ||
| 22 | <string>Info</string> | ||
| 23 | </property> | ||
| 24 | <layout class="QHBoxLayout" name="GeneralHorizontalLayout"> | ||
| 25 | <item> | ||
| 26 | <layout class="QGridLayout" name="gridLayout_2"> | ||
| 27 | <item row="6" column="1" colspan="2"> | ||
| 28 | <widget class="QLineEdit" name="display_filename"> | ||
| 29 | <property name="enabled"> | ||
| 30 | <bool>true</bool> | ||
| 31 | </property> | ||
| 32 | <property name="readOnly"> | ||
| 33 | <bool>true</bool> | ||
| 34 | </property> | ||
| 35 | </widget> | ||
| 36 | </item> | ||
| 37 | <item row="0" column="1"> | ||
| 38 | <widget class="QLineEdit" name="display_name"> | ||
| 39 | <property name="enabled"> | ||
| 40 | <bool>true</bool> | ||
| 41 | </property> | ||
| 42 | <property name="readOnly"> | ||
| 43 | <bool>true</bool> | ||
| 44 | </property> | ||
| 45 | </widget> | ||
| 46 | </item> | ||
| 47 | <item row="1" column="0"> | ||
| 48 | <widget class="QLabel" name="label_2"> | ||
| 49 | <property name="text"> | ||
| 50 | <string>Developer</string> | ||
| 51 | </property> | ||
| 52 | </widget> | ||
| 53 | </item> | ||
| 54 | <item row="5" column="1" colspan="2"> | ||
| 55 | <widget class="QLineEdit" name="display_size"> | ||
| 56 | <property name="enabled"> | ||
| 57 | <bool>true</bool> | ||
| 58 | </property> | ||
| 59 | <property name="readOnly"> | ||
| 60 | <bool>true</bool> | ||
| 61 | </property> | ||
| 62 | </widget> | ||
| 63 | </item> | ||
| 64 | <item row="0" column="0"> | ||
| 65 | <widget class="QLabel" name="label"> | ||
| 66 | <property name="text"> | ||
| 67 | <string>Name</string> | ||
| 68 | </property> | ||
| 69 | </widget> | ||
| 70 | </item> | ||
| 71 | <item row="6" column="0"> | ||
| 72 | <widget class="QLabel" name="label_7"> | ||
| 73 | <property name="text"> | ||
| 74 | <string>Filename</string> | ||
| 75 | </property> | ||
| 76 | </widget> | ||
| 77 | </item> | ||
| 78 | <item row="2" column="0"> | ||
| 79 | <widget class="QLabel" name="label_3"> | ||
| 80 | <property name="text"> | ||
| 81 | <string>Version</string> | ||
| 82 | </property> | ||
| 83 | </widget> | ||
| 84 | </item> | ||
| 85 | <item row="4" column="0"> | ||
| 86 | <widget class="QLabel" name="label_5"> | ||
| 87 | <property name="text"> | ||
| 88 | <string>Format</string> | ||
| 89 | </property> | ||
| 90 | </widget> | ||
| 91 | </item> | ||
| 92 | <item row="2" column="1"> | ||
| 93 | <widget class="QLineEdit" name="display_version"> | ||
| 94 | <property name="enabled"> | ||
| 95 | <bool>true</bool> | ||
| 96 | </property> | ||
| 97 | <property name="readOnly"> | ||
| 98 | <bool>true</bool> | ||
| 99 | </property> | ||
| 100 | </widget> | ||
| 101 | </item> | ||
| 102 | <item row="4" column="1"> | ||
| 103 | <widget class="QLineEdit" name="display_format"> | ||
| 104 | <property name="enabled"> | ||
| 105 | <bool>true</bool> | ||
| 106 | </property> | ||
| 107 | <property name="readOnly"> | ||
| 108 | <bool>true</bool> | ||
| 109 | </property> | ||
| 110 | </widget> | ||
| 111 | </item> | ||
| 112 | <item row="5" column="0"> | ||
| 113 | <widget class="QLabel" name="label_6"> | ||
| 114 | <property name="text"> | ||
| 115 | <string>Size</string> | ||
| 116 | </property> | ||
| 117 | </widget> | ||
| 118 | </item> | ||
| 119 | <item row="1" column="1"> | ||
| 120 | <widget class="QLineEdit" name="display_developer"> | ||
| 121 | <property name="enabled"> | ||
| 122 | <bool>true</bool> | ||
| 123 | </property> | ||
| 124 | <property name="readOnly"> | ||
| 125 | <bool>true</bool> | ||
| 126 | </property> | ||
| 127 | </widget> | ||
| 128 | </item> | ||
| 129 | <item row="3" column="0"> | ||
| 130 | <widget class="QLabel" name="label_4"> | ||
| 131 | <property name="text"> | ||
| 132 | <string>Title ID</string> | ||
| 133 | </property> | ||
| 134 | </widget> | ||
| 135 | </item> | ||
| 136 | <item row="3" column="1"> | ||
| 137 | <widget class="QLineEdit" name="display_title_id"> | ||
| 138 | <property name="enabled"> | ||
| 139 | <bool>true</bool> | ||
| 140 | </property> | ||
| 141 | <property name="readOnly"> | ||
| 142 | <bool>true</bool> | ||
| 143 | </property> | ||
| 144 | </widget> | ||
| 145 | </item> | ||
| 146 | <item row="0" column="2" rowspan="5"> | ||
| 147 | <widget class="QGraphicsView" name="icon_view"> | ||
| 148 | <property name="sizePolicy"> | ||
| 149 | <sizepolicy hsizetype="Maximum" vsizetype="Maximum"> | ||
| 150 | <horstretch>0</horstretch> | ||
| 151 | <verstretch>0</verstretch> | ||
| 152 | </sizepolicy> | ||
| 153 | </property> | ||
| 154 | <property name="minimumSize"> | ||
| 155 | <size> | ||
| 156 | <width>128</width> | ||
| 157 | <height>128</height> | ||
| 158 | </size> | ||
| 159 | </property> | ||
| 160 | <property name="maximumSize"> | ||
| 161 | <size> | ||
| 162 | <width>128</width> | ||
| 163 | <height>128</height> | ||
| 164 | </size> | ||
| 165 | </property> | ||
| 166 | <property name="verticalScrollBarPolicy"> | ||
| 167 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 168 | </property> | ||
| 169 | <property name="horizontalScrollBarPolicy"> | ||
| 170 | <enum>Qt::ScrollBarAlwaysOff</enum> | ||
| 171 | </property> | ||
| 172 | <property name="sizeAdjustPolicy"> | ||
| 173 | <enum>QAbstractScrollArea::AdjustToContents</enum> | ||
| 174 | </property> | ||
| 175 | <property name="interactive"> | ||
| 176 | <bool>false</bool> | ||
| 177 | </property> | ||
| 178 | </widget> | ||
| 179 | </item> | ||
| 180 | </layout> | ||
| 181 | </item> | ||
| 182 | </layout> | ||
| 183 | </widget> | ||
| 184 | </item> | ||
| 185 | <item> | ||
| 186 | <widget class="QGroupBox" name="PerformanceGroupBox"> | ||
| 187 | <property name="title"> | ||
| 188 | <string>Add-Ons</string> | ||
| 189 | </property> | ||
| 190 | <layout class="QHBoxLayout" name="PerformanceHorizontalLayout"> | ||
| 191 | <item> | ||
| 192 | <widget class="QScrollArea" name="scrollArea"> | ||
| 193 | <property name="widgetResizable"> | ||
| 194 | <bool>true</bool> | ||
| 195 | </property> | ||
| 196 | <widget class="QWidget" name="scrollAreaWidgetContents"> | ||
| 197 | <property name="geometry"> | ||
| 198 | <rect> | ||
| 199 | <x>0</x> | ||
| 200 | <y>0</y> | ||
| 201 | <width>350</width> | ||
| 202 | <height>169</height> | ||
| 203 | </rect> | ||
| 204 | </property> | ||
| 205 | </widget> | ||
| 206 | </widget> | ||
| 207 | </item> | ||
| 208 | <item> | ||
| 209 | <layout class="QVBoxLayout" name="PerformanceVerticalLayout"/> | ||
| 210 | </item> | ||
| 211 | </layout> | ||
| 212 | </widget> | ||
| 213 | </item> | ||
| 214 | <item> | ||
| 215 | <spacer name="verticalSpacer"> | ||
| 216 | <property name="orientation"> | ||
| 217 | <enum>Qt::Vertical</enum> | ||
| 218 | </property> | ||
| 219 | <property name="sizeType"> | ||
| 220 | <enum>QSizePolicy::Fixed</enum> | ||
| 221 | </property> | ||
| 222 | <property name="sizeHint" stdset="0"> | ||
| 223 | <size> | ||
| 224 | <width>20</width> | ||
| 225 | <height>40</height> | ||
| 226 | </size> | ||
| 227 | </property> | ||
| 228 | </spacer> | ||
| 229 | </item> | ||
| 230 | <item> | ||
| 231 | <widget class="QDialogButtonBox" name="buttonBox"> | ||
| 232 | <property name="standardButtons"> | ||
| 233 | <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> | ||
| 234 | </property> | ||
| 235 | </widget> | ||
| 236 | </item> | ||
| 237 | </layout> | ||
| 238 | </item> | ||
| 239 | </layout> | ||
| 240 | </widget> | ||
| 241 | <resources/> | ||
| 242 | <connections> | ||
| 243 | <connection> | ||
| 244 | <sender>buttonBox</sender> | ||
| 245 | <signal>accepted()</signal> | ||
| 246 | <receiver>ConfigurePerGameGeneral</receiver> | ||
| 247 | <slot>accept()</slot> | ||
| 248 | <hints> | ||
| 249 | <hint type="sourcelabel"> | ||
| 250 | <x>269</x> | ||
| 251 | <y>567</y> | ||
| 252 | </hint> | ||
| 253 | <hint type="destinationlabel"> | ||
| 254 | <x>269</x> | ||
| 255 | <y>294</y> | ||
| 256 | </hint> | ||
| 257 | </hints> | ||
| 258 | </connection> | ||
| 259 | <connection> | ||
| 260 | <sender>buttonBox</sender> | ||
| 261 | <signal>rejected()</signal> | ||
| 262 | <receiver>ConfigurePerGameGeneral</receiver> | ||
| 263 | <slot>reject()</slot> | ||
| 264 | <hints> | ||
| 265 | <hint type="sourcelabel"> | ||
| 266 | <x>269</x> | ||
| 267 | <y>567</y> | ||
| 268 | </hint> | ||
| 269 | <hint type="destinationlabel"> | ||
| 270 | <x>269</x> | ||
| 271 | <y>294</y> | ||
| 272 | </hint> | ||
| 273 | </hints> | ||
| 274 | </connection> | ||
| 275 | </connections> | ||
| 276 | </ui> | ||
diff --git a/src/yuzu/debugger/wait_tree.cpp b/src/yuzu/debugger/wait_tree.cpp index f9c18ede4..6b3a757e0 100644 --- a/src/yuzu/debugger/wait_tree.cpp +++ b/src/yuzu/debugger/wait_tree.cpp | |||
| @@ -75,7 +75,7 @@ std::vector<std::unique_ptr<WaitTreeThread>> WaitTreeItem::MakeThreadItemList() | |||
| 75 | return item_list; | 75 | return item_list; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | WaitTreeText::WaitTreeText(const QString& t) : text(t) {} | 78 | WaitTreeText::WaitTreeText(QString t) : text(std::move(t)) {} |
| 79 | WaitTreeText::~WaitTreeText() = default; | 79 | WaitTreeText::~WaitTreeText() = default; |
| 80 | 80 | ||
| 81 | QString WaitTreeText::GetText() const { | 81 | QString WaitTreeText::GetText() const { |
diff --git a/src/yuzu/debugger/wait_tree.h b/src/yuzu/debugger/wait_tree.h index 492fb6ac9..e639ef412 100644 --- a/src/yuzu/debugger/wait_tree.h +++ b/src/yuzu/debugger/wait_tree.h | |||
| @@ -52,7 +52,7 @@ private: | |||
| 52 | class WaitTreeText : public WaitTreeItem { | 52 | class WaitTreeText : public WaitTreeItem { |
| 53 | Q_OBJECT | 53 | Q_OBJECT |
| 54 | public: | 54 | public: |
| 55 | explicit WaitTreeText(const QString& text); | 55 | explicit WaitTreeText(QString text); |
| 56 | ~WaitTreeText() override; | 56 | ~WaitTreeText() override; |
| 57 | 57 | ||
| 58 | QString GetText() const override; | 58 | QString GetText() const override; |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index b52a50915..8e9524fd6 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -333,6 +333,8 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { | |||
| 333 | QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); | 333 | QAction* dump_romfs = context_menu.addAction(tr("Dump RomFS")); |
| 334 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); | 334 | QAction* copy_tid = context_menu.addAction(tr("Copy Title ID to Clipboard")); |
| 335 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); | 335 | QAction* navigate_to_gamedb_entry = context_menu.addAction(tr("Navigate to GameDB entry")); |
| 336 | context_menu.addSeparator(); | ||
| 337 | QAction* properties = context_menu.addAction(tr("Properties")); | ||
| 336 | 338 | ||
| 337 | open_save_location->setEnabled(program_id != 0); | 339 | open_save_location->setEnabled(program_id != 0); |
| 338 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); | 340 | auto it = FindMatchingCompatibilityEntry(compatibility_list, program_id); |
| @@ -346,6 +348,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) { | |||
| 346 | connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); | 348 | connect(copy_tid, &QAction::triggered, [&]() { emit CopyTIDRequested(program_id); }); |
| 347 | connect(navigate_to_gamedb_entry, &QAction::triggered, | 349 | connect(navigate_to_gamedb_entry, &QAction::triggered, |
| 348 | [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); | 350 | [&]() { emit NavigateToGamedbEntryRequested(program_id, compatibility_list); }); |
| 351 | connect(properties, &QAction::triggered, [&]() { emit OpenPerGameGeneralRequested(path); }); | ||
| 349 | 352 | ||
| 350 | context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); | 353 | context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location)); |
| 351 | } | 354 | } |
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 05e115e19..b317eb2fc 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -70,6 +70,7 @@ signals: | |||
| 70 | void CopyTIDRequested(u64 program_id); | 70 | void CopyTIDRequested(u64 program_id); |
| 71 | void NavigateToGamedbEntryRequested(u64 program_id, | 71 | void NavigateToGamedbEntryRequested(u64 program_id, |
| 72 | const CompatibilityList& compatibility_list); | 72 | const CompatibilityList& compatibility_list); |
| 73 | void OpenPerGameGeneralRequested(const std::string& file); | ||
| 73 | 74 | ||
| 74 | private slots: | 75 | private slots: |
| 75 | void onTextChanged(const QString& newText); | 76 | void onTextChanged(const QString& newText); |
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 9fd074223..b37710f59 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -62,7 +62,7 @@ QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, | |||
| 62 | FileSys::VirtualFile update_raw; | 62 | FileSys::VirtualFile update_raw; |
| 63 | loader.ReadUpdateRaw(update_raw); | 63 | loader.ReadUpdateRaw(update_raw); |
| 64 | for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { | 64 | for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) { |
| 65 | const bool is_update = kv.first == "Update"; | 65 | const bool is_update = kv.first == "Update" || kv.first == "[D] Update"; |
| 66 | if (!updatable && is_update) { | 66 | if (!updatable && is_update) { |
| 67 | continue; | 67 | continue; |
| 68 | } | 68 | } |
| @@ -99,12 +99,14 @@ QList<QStandardItem*> MakeGameListEntry(const std::string& path, const std::stri | |||
| 99 | compatibility = it->second.first; | 99 | compatibility = it->second.first; |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | const auto file_type = loader.GetFileType(); | ||
| 103 | const auto file_type_string = QString::fromStdString(Loader::GetFileTypeString(file_type)); | ||
| 104 | |||
| 102 | QList<QStandardItem*> list{ | 105 | QList<QStandardItem*> list{ |
| 103 | new GameListItemPath( | 106 | new GameListItemPath(FormatGameName(path), icon, QString::fromStdString(name), |
| 104 | FormatGameName(path), icon, QString::fromStdString(name), | 107 | file_type_string, program_id), |
| 105 | QString::fromStdString(Loader::GetFileTypeString(loader.GetFileType())), program_id), | ||
| 106 | new GameListItemCompat(compatibility), | 108 | new GameListItemCompat(compatibility), |
| 107 | new GameListItem(QString::fromStdString(Loader::GetFileTypeString(loader.GetFileType()))), | 109 | new GameListItem(file_type_string), |
| 108 | new GameListItemSize(FileUtil::GetSize(path)), | 110 | new GameListItemSize(FileUtil::GetSize(path)), |
| 109 | }; | 111 | }; |
| 110 | 112 | ||
| @@ -196,12 +198,16 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||
| 196 | const bool is_dir = FileUtil::IsDirectory(physical_name); | 198 | const bool is_dir = FileUtil::IsDirectory(physical_name); |
| 197 | if (!is_dir && | 199 | if (!is_dir && |
| 198 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { | 200 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { |
| 199 | std::unique_ptr<Loader::AppLoader> loader = | 201 | auto loader = Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); |
| 200 | Loader::GetLoader(vfs->OpenFile(physical_name, FileSys::Mode::Read)); | 202 | if (!loader) { |
| 201 | if (!loader || ((loader->GetFileType() == Loader::FileType::Unknown || | ||
| 202 | loader->GetFileType() == Loader::FileType::Error) && | ||
| 203 | !UISettings::values.show_unknown)) | ||
| 204 | return true; | 203 | return true; |
| 204 | } | ||
| 205 | |||
| 206 | const auto file_type = loader->GetFileType(); | ||
| 207 | if ((file_type == Loader::FileType::Unknown || file_type == Loader::FileType::Error) && | ||
| 208 | !UISettings::values.show_unknown) { | ||
| 209 | return true; | ||
| 210 | } | ||
| 205 | 211 | ||
| 206 | std::vector<u8> icon; | 212 | std::vector<u8> icon; |
| 207 | const auto res1 = loader->ReadIcon(icon); | 213 | const auto res1 = loader->ReadIcon(icon); |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 22c207a3a..90b212ba5 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | 9 | ||
| 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. | 10 | // VFS includes must be before glad as they will conflict with Windows file api, which uses defines. |
| 11 | #include "applets/software_keyboard.h" | 11 | #include "applets/software_keyboard.h" |
| 12 | #include "configuration/configure_per_general.h" | ||
| 12 | #include "core/file_sys/vfs.h" | 13 | #include "core/file_sys/vfs.h" |
| 13 | #include "core/file_sys/vfs_real.h" | 14 | #include "core/file_sys/vfs_real.h" |
| 14 | #include "core/hle/service/acc/profile_manager.h" | 15 | #include "core/hle/service/acc/profile_manager.h" |
| @@ -441,6 +442,8 @@ void GMainWindow::ConnectWidgetEvents() { | |||
| 441 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); | 442 | connect(game_list, &GameList::CopyTIDRequested, this, &GMainWindow::OnGameListCopyTID); |
| 442 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, | 443 | connect(game_list, &GameList::NavigateToGamedbEntryRequested, this, |
| 443 | &GMainWindow::OnGameListNavigateToGamedbEntry); | 444 | &GMainWindow::OnGameListNavigateToGamedbEntry); |
| 445 | connect(game_list, &GameList::OpenPerGameGeneralRequested, this, | ||
| 446 | &GMainWindow::OnGameListOpenPerGameProperties); | ||
| 444 | 447 | ||
| 445 | connect(this, &GMainWindow::EmulationStarting, render_window, | 448 | connect(this, &GMainWindow::EmulationStarting, render_window, |
| 446 | &GRenderWindow::OnEmulationStarting); | 449 | &GRenderWindow::OnEmulationStarting); |
| @@ -988,6 +991,32 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, | |||
| 988 | QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory)); | 991 | QDesktopServices::openUrl(QUrl("https://yuzu-emu.org/game/" + directory)); |
| 989 | } | 992 | } |
| 990 | 993 | ||
| 994 | void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) { | ||
| 995 | u64 title_id{}; | ||
| 996 | const auto v_file = Core::GetGameFileFromPath(vfs, file); | ||
| 997 | const auto loader = Loader::GetLoader(v_file); | ||
| 998 | if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { | ||
| 999 | QMessageBox::information(this, tr("Properties"), | ||
| 1000 | tr("The game properties could not be loaded.")); | ||
| 1001 | return; | ||
| 1002 | } | ||
| 1003 | |||
| 1004 | ConfigurePerGameGeneral dialog(this, title_id); | ||
| 1005 | dialog.loadFromFile(v_file); | ||
| 1006 | auto result = dialog.exec(); | ||
| 1007 | if (result == QDialog::Accepted) { | ||
| 1008 | dialog.applyConfiguration(); | ||
| 1009 | |||
| 1010 | const auto reload = UISettings::values.is_game_list_reload_pending.exchange(false); | ||
| 1011 | if (reload) { | ||
| 1012 | game_list->PopulateAsync(UISettings::values.gamedir, | ||
| 1013 | UISettings::values.gamedir_deepscan); | ||
| 1014 | } | ||
| 1015 | |||
| 1016 | config->Save(); | ||
| 1017 | } | ||
| 1018 | } | ||
| 1019 | |||
| 991 | void GMainWindow::OnMenuLoadFile() { | 1020 | void GMainWindow::OnMenuLoadFile() { |
| 992 | const QString extensions = | 1021 | const QString extensions = |
| 993 | QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main"); | 1022 | QString("*.").append(GameList::supported_file_extensions.join(" *.")).append(" main"); |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 674e73412..ca9c50367 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -168,6 +168,7 @@ private slots: | |||
| 168 | void OnGameListCopyTID(u64 program_id); | 168 | void OnGameListCopyTID(u64 program_id); |
| 169 | void OnGameListNavigateToGamedbEntry(u64 program_id, | 169 | void OnGameListNavigateToGamedbEntry(u64 program_id, |
| 170 | const CompatibilityList& compatibility_list); | 170 | const CompatibilityList& compatibility_list); |
| 171 | void OnGameListOpenPerGameProperties(const std::string& file); | ||
| 171 | void OnMenuLoadFile(); | 172 | void OnMenuLoadFile(); |
| 172 | void OnMenuLoadFolder(); | 173 | void OnMenuLoadFolder(); |
| 173 | void OnMenuInstallToNAND(); | 174 | void OnMenuInstallToNAND(); |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 097c1fbe3..fe0d1eebf 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.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 <memory> | 5 | #include <memory> |
| 6 | #include <sstream> | ||
| 6 | #include <SDL.h> | 7 | #include <SDL.h> |
| 7 | #include <inih/cpp/INIReader.h> | 8 | #include <inih/cpp/INIReader.h> |
| 8 | #include "common/file_util.h" | 9 | #include "common/file_util.h" |
| @@ -369,6 +370,23 @@ void Config::ReadValues() { | |||
| 369 | Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); | 370 | Settings::values.dump_exefs = sdl2_config->GetBoolean("Debugging", "dump_exefs", false); |
| 370 | Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); | 371 | Settings::values.dump_nso = sdl2_config->GetBoolean("Debugging", "dump_nso", false); |
| 371 | 372 | ||
| 373 | const auto title_list = sdl2_config->Get("AddOns", "title_ids", ""); | ||
| 374 | std::stringstream ss(title_list); | ||
| 375 | std::string line; | ||
| 376 | while (std::getline(ss, line, '|')) { | ||
| 377 | const auto title_id = std::stoul(line, nullptr, 16); | ||
| 378 | const auto disabled_list = sdl2_config->Get("AddOns", "disabled_" + line, ""); | ||
| 379 | |||
| 380 | std::stringstream inner_ss(disabled_list); | ||
| 381 | std::string inner_line; | ||
| 382 | std::vector<std::string> out; | ||
| 383 | while (std::getline(inner_ss, inner_line, '|')) { | ||
| 384 | out.push_back(inner_line); | ||
| 385 | } | ||
| 386 | |||
| 387 | Settings::values.disabled_addons.insert_or_assign(title_id, out); | ||
| 388 | } | ||
| 389 | |||
| 372 | // Web Service | 390 | // Web Service |
| 373 | Settings::values.enable_telemetry = | 391 | Settings::values.enable_telemetry = |
| 374 | sdl2_config->GetBoolean("WebService", "enable_telemetry", true); | 392 | sdl2_config->GetBoolean("WebService", "enable_telemetry", true); |
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index d73669f36..0f3f8da50 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -221,5 +221,12 @@ web_api_url = https://api.yuzu-emu.org | |||
| 221 | # See https://profile.yuzu-emu.org/ for more info | 221 | # See https://profile.yuzu-emu.org/ for more info |
| 222 | yuzu_username = | 222 | yuzu_username = |
| 223 | yuzu_token = | 223 | yuzu_token = |
| 224 | |||
| 225 | [AddOns] | ||
| 226 | # Used to disable add-ons | ||
| 227 | # List of title IDs of games that will have add-ons disabled (separated by '|'): | ||
| 228 | title_ids = | ||
| 229 | # For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|') | ||
| 230 | # e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey | ||
| 224 | )"; | 231 | )"; |
| 225 | } | 232 | } |