summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt10
-rw-r--r--src/core/core.cpp4
-rw-r--r--src/core/core_timing.cpp79
-rw-r--r--src/core/core_timing.h12
-rw-r--r--src/core/debugger/gdbstub.cpp17
-rw-r--r--src/core/file_sys/program_metadata.cpp8
-rw-r--r--src/core/file_sys/program_metadata.h1
-rw-r--r--src/core/file_sys/vfs.h2
-rw-r--r--src/core/hid/emulated_controller.cpp11
-rw-r--r--src/core/hid/emulated_controller.h2
-rw-r--r--src/core/hle/kernel/k_hardware_timer.cpp4
-rw-r--r--src/core/hle/kernel/k_memory_layout.cpp2
-rw-r--r--src/core/hle/kernel/k_page_table.cpp29
-rw-r--r--src/core/hle/kernel/k_page_table.h3
-rw-r--r--src/core/hle/kernel/k_transfer_memory.cpp89
-rw-r--r--src/core/hle/kernel/k_transfer_memory.h14
-rw-r--r--src/core/hle/kernel/svc/svc_transfer_memory.cpp54
-rw-r--r--src/core/hle/service/am/am.cpp275
-rw-r--r--src/core/hle/service/am/am.h37
-rw-r--r--src/core/hle/service/am/applet_ae.cpp20
-rw-r--r--src/core/hle/service/am/applets/applet_cabinet.h11
-rw-r--r--src/core/hle/service/am/applets/applet_error.cpp5
-rw-r--r--src/core/hle/service/am/applets/applet_general_backend.cpp6
-rw-r--r--src/core/hle/service/am/applets/applets.cpp16
-rw-r--r--src/core/hle/service/am/applets/applets.h49
-rw-r--r--src/core/hle/service/caps/caps.cpp21
-rw-r--r--src/core/hle/service/caps/caps.h81
-rw-r--r--src/core/hle/service/caps/caps_a.cpp239
-rw-r--r--src/core/hle/service/caps/caps_a.h21
-rw-r--r--src/core/hle/service/caps/caps_c.cpp50
-rw-r--r--src/core/hle/service/caps/caps_c.h10
-rw-r--r--src/core/hle/service/caps/caps_manager.cpp342
-rw-r--r--src/core/hle/service/caps/caps_manager.h72
-rw-r--r--src/core/hle/service/caps/caps_result.h35
-rw-r--r--src/core/hle/service/caps/caps_sc.cpp5
-rw-r--r--src/core/hle/service/caps/caps_sc.h6
-rw-r--r--src/core/hle/service/caps/caps_ss.cpp5
-rw-r--r--src/core/hle/service/caps/caps_ss.h6
-rw-r--r--src/core/hle/service/caps/caps_su.cpp9
-rw-r--r--src/core/hle/service/caps/caps_su.h6
-rw-r--r--src/core/hle/service/caps/caps_types.h184
-rw-r--r--src/core/hle/service/caps/caps_u.cpp104
-rw-r--r--src/core/hle/service/caps/caps_u.h12
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp3
-rw-r--r--src/core/hle/service/ldn/ldn.cpp91
-rw-r--r--src/core/hle/service/nfc/common/device.cpp9
-rw-r--r--src/core/hle/service/nifm/nifm.cpp12
-rw-r--r--src/core/hle/service/nifm/nifm.h1
-rw-r--r--src/core/hle/service/ns/ns.cpp27
-rw-r--r--src/core/hle/service/ns/ns.h4
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h14
-rw-r--r--src/core/hle/service/nvnflinger/buffer_item.h2
-rw-r--r--src/core/hle/service/nvnflinger/buffer_slot.h2
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp351
-rw-r--r--src/core/hle/service/nvnflinger/fb_share_buffer_manager.h65
-rw-r--r--src/core/hle/service/nvnflinger/graphic_buffer_producer.h2
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.cpp11
-rw-r--r--src/core/hle/service/nvnflinger/nvnflinger.h9
-rw-r--r--src/core/hle/service/nvnflinger/ui/fence.h3
-rw-r--r--src/core/hle/service/nvnflinger/ui/graphic_buffer.h4
-rw-r--r--src/core/hle/service/pctl/pctl_module.cpp9
-rw-r--r--src/core/hle/service/vi/vi.cpp129
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp2
63 files changed, 2340 insertions, 378 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index d0f76e57e..e4f499135 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -466,14 +466,18 @@ add_library(core STATIC
hle/service/caps/caps_a.h
hle/service/caps/caps_c.cpp
hle/service/caps/caps_c.h
- hle/service/caps/caps_u.cpp
- hle/service/caps/caps_u.h
+ hle/service/caps/caps_manager.cpp
+ hle/service/caps/caps_manager.h
+ hle/service/caps/caps_result.h
hle/service/caps/caps_sc.cpp
hle/service/caps/caps_sc.h
hle/service/caps/caps_ss.cpp
hle/service/caps/caps_ss.h
hle/service/caps/caps_su.cpp
hle/service/caps/caps_su.h
+ hle/service/caps/caps_types.h
+ hle/service/caps/caps_u.cpp
+ hle/service/caps/caps_u.h
hle/service/erpt/erpt.cpp
hle/service/erpt/erpt.h
hle/service/es/es.cpp
@@ -698,6 +702,8 @@ add_library(core STATIC
hle/service/nvnflinger/consumer_base.cpp
hle/service/nvnflinger/consumer_base.h
hle/service/nvnflinger/consumer_listener.h
+ hle/service/nvnflinger/fb_share_buffer_manager.cpp
+ hle/service/nvnflinger/fb_share_buffer_manager.h
hle/service/nvnflinger/graphic_buffer_producer.cpp
hle/service/nvnflinger/graphic_buffer_producer.h
hle/service/nvnflinger/hos_binder_driver_server.cpp
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 08cbb8978..0ab2e3b76 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -1078,6 +1078,10 @@ void System::ApplySettings() {
impl->RefreshTime();
if (IsPoweredOn()) {
+ if (Settings::values.custom_rtc_enabled) {
+ const s64 posix_time{Settings::values.custom_rtc.GetValue()};
+ GetTimeManager().UpdateLocalSystemClockTime(posix_time);
+ }
Renderer().RefreshBaseSettings();
}
}
diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp
index b98a0cb33..e671b270f 100644
--- a/src/core/core_timing.cpp
+++ b/src/core/core_timing.cpp
@@ -32,6 +32,7 @@ struct CoreTiming::Event {
std::uintptr_t user_data;
std::weak_ptr<EventType> type;
s64 reschedule_time;
+ heap_t::handle_type handle{};
// Sort by time, unless the times are the same, in which case sort by
// the order added to the queue
@@ -122,9 +123,9 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
- event_queue.emplace_back(
- Event{next_time.count(), event_fifo_id++, user_data, event_type, 0});
- std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+ auto h{event_queue.emplace(
+ Event{next_time.count(), event_fifo_id++, user_data, event_type, 0})};
+ (*h).handle = h;
}
event.Set();
@@ -138,10 +139,9 @@ void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
std::scoped_lock scope{basic_lock};
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
- event_queue.emplace_back(
- Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
-
- std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+ auto h{event_queue.emplace(Event{next_time.count(), event_fifo_id++, user_data, event_type,
+ resched_time.count()})};
+ (*h).handle = h;
}
event.Set();
@@ -151,15 +151,17 @@ void CoreTiming::UnscheduleEvent(const std::shared_ptr<EventType>& event_type,
std::uintptr_t user_data, bool wait) {
{
std::scoped_lock lk{basic_lock};
- const auto itr =
- std::remove_if(event_queue.begin(), event_queue.end(), [&](const Event& e) {
- return e.type.lock().get() == event_type.get() && e.user_data == user_data;
- });
-
- // Removing random items breaks the invariant so we have to re-establish it.
- if (itr != event_queue.end()) {
- event_queue.erase(itr, event_queue.end());
- std::make_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+
+ std::vector<heap_t::handle_type> to_remove;
+ for (auto itr = event_queue.begin(); itr != event_queue.end(); itr++) {
+ const Event& e = *itr;
+ if (e.type.lock().get() == event_type.get() && e.user_data == user_data) {
+ to_remove.push_back(itr->handle);
+ }
+ }
+
+ for (auto h : to_remove) {
+ event_queue.erase(h);
}
}
@@ -200,35 +202,45 @@ std::optional<s64> CoreTiming::Advance() {
std::scoped_lock lock{advance_lock, basic_lock};
global_timer = GetGlobalTimeNs().count();
- while (!event_queue.empty() && event_queue.front().time <= global_timer) {
- Event evt = std::move(event_queue.front());
- std::pop_heap(event_queue.begin(), event_queue.end(), std::greater<>());
- event_queue.pop_back();
+ while (!event_queue.empty() && event_queue.top().time <= global_timer) {
+ const Event& evt = event_queue.top();
if (const auto event_type{evt.type.lock()}) {
- basic_lock.unlock();
+ if (evt.reschedule_time == 0) {
+ const auto evt_user_data = evt.user_data;
+ const auto evt_time = evt.time;
+
+ event_queue.pop();
+
+ basic_lock.unlock();
+
+ event_type->callback(
+ evt_user_data, evt_time,
+ std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt_time});
+
+ basic_lock.lock();
+ } else {
+ basic_lock.unlock();
- const auto new_schedule_time{event_type->callback(
- evt.user_data, evt.time,
- std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
+ const auto new_schedule_time{event_type->callback(
+ evt.user_data, evt.time,
+ std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
- basic_lock.lock();
+ basic_lock.lock();
- if (evt.reschedule_time != 0) {
const auto next_schedule_time{new_schedule_time.has_value()
? new_schedule_time.value().count()
: evt.reschedule_time};
- // If this event was scheduled into a pause, its time now is going to be way behind.
- // Re-set this event to continue from the end of the pause.
+ // If this event was scheduled into a pause, its time now is going to be way
+ // behind. Re-set this event to continue from the end of the pause.
auto next_time{evt.time + next_schedule_time};
if (evt.time < pause_end_time) {
next_time = pause_end_time + next_schedule_time;
}
- event_queue.emplace_back(
- Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time});
- std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
+ event_queue.update(evt.handle, Event{next_time, event_fifo_id++, evt.user_data,
+ evt.type, next_schedule_time, evt.handle});
}
}
@@ -236,7 +248,7 @@ std::optional<s64> CoreTiming::Advance() {
}
if (!event_queue.empty()) {
- return event_queue.front().time;
+ return event_queue.top().time;
} else {
return std::nullopt;
}
@@ -274,7 +286,8 @@ void CoreTiming::ThreadLoop() {
#endif
}
} else {
- // Queue is empty, wait until another event is scheduled and signals us to continue.
+ // Queue is empty, wait until another event is scheduled and signals us to
+ // continue.
wait_set = true;
event.Wait();
}
diff --git a/src/core/core_timing.h b/src/core/core_timing.h
index c20e906fb..26a8b93a7 100644
--- a/src/core/core_timing.h
+++ b/src/core/core_timing.h
@@ -11,7 +11,8 @@
#include <optional>
#include <string>
#include <thread>
-#include <vector>
+
+#include <boost/heap/fibonacci_heap.hpp>
#include "common/common_types.h"
#include "common/thread.h"
@@ -151,11 +152,10 @@ private:
s64 timer_resolution_ns;
#endif
- // The queue is a min-heap using std::make_heap/push_heap/pop_heap.
- // We don't use std::priority_queue because we need to be able to serialize, unserialize and
- // erase arbitrary events (RemoveEvent()) regardless of the queue order. These aren't
- // accommodated by the standard adaptor class.
- std::vector<Event> event_queue;
+ using heap_t =
+ boost::heap::fibonacci_heap<CoreTiming::Event, boost::heap::compare<std::greater<>>>;
+
+ heap_t event_queue;
u64 event_fifo_id = 0;
std::shared_ptr<EventType> ev_lost;
diff --git a/src/core/debugger/gdbstub.cpp b/src/core/debugger/gdbstub.cpp
index e55831f27..82964f0a1 100644
--- a/src/core/debugger/gdbstub.cpp
+++ b/src/core/debugger/gdbstub.cpp
@@ -2,6 +2,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <atomic>
+#include <codecvt>
+#include <locale>
#include <numeric>
#include <optional>
#include <thread>
@@ -12,6 +14,7 @@
#include "common/logging/log.h"
#include "common/scope_exit.h"
#include "common/settings.h"
+#include "common/string_util.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/debugger/gdbstub.h"
@@ -68,10 +71,16 @@ static std::string EscapeGDB(std::string_view data) {
}
static std::string EscapeXML(std::string_view data) {
+ std::u32string converted = U"[Encoding error]";
+ try {
+ converted = Common::UTF8ToUTF32(data);
+ } catch (std::range_error&) {
+ }
+
std::string escaped;
escaped.reserve(data.size());
- for (char c : data) {
+ for (char32_t c : converted) {
switch (c) {
case '&':
escaped += "&amp;";
@@ -86,7 +95,11 @@ static std::string EscapeXML(std::string_view data) {
escaped += "&gt;";
break;
default:
- escaped += c;
+ if (c > 0x7f) {
+ escaped += fmt::format("&#{};", static_cast<u32>(c));
+ } else {
+ escaped += static_cast<char>(c);
+ }
break;
}
}
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index f00479bd3..8e291ff67 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -5,6 +5,7 @@
#include <vector>
#include "common/logging/log.h"
+#include "common/scope_exit.h"
#include "core/file_sys/program_metadata.h"
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
@@ -95,6 +96,13 @@ Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
return Loader::ResultStatus::Success;
}
+Loader::ResultStatus ProgramMetadata::Reload(VirtualFile file) {
+ const u64 original_program_id = aci_header.title_id;
+ SCOPE_EXIT({ aci_header.title_id = original_program_id; });
+
+ return this->Load(file);
+}
+
/*static*/ ProgramMetadata ProgramMetadata::GetDefault() {
// Allow use of cores 0~3 and thread priorities 1~63.
constexpr u32 default_thread_info_capability = 0x30007F7;
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 2e8960b07..9f8e74b13 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -56,6 +56,7 @@ public:
static ProgramMetadata GetDefault();
Loader::ResultStatus Load(VirtualFile file);
+ Loader::ResultStatus Reload(VirtualFile file);
/// Load from parameters instead of NPDM file, used for KIP
void LoadManual(bool is_64_bit, ProgramAddressSpaceType address_space, s32 main_thread_prio,
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index a93e21f67..a7cd1cae3 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -175,7 +175,7 @@ public:
return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset);
}
- // Renames the file to name. Returns whether or not the operation was successsful.
+ // Renames the file to name. Returns whether or not the operation was successful.
virtual bool Rename(std::string_view name) = 0;
// Returns the full path of this file as a string, recursively
diff --git a/src/core/hid/emulated_controller.cpp b/src/core/hid/emulated_controller.cpp
index 94bd656fe..2af3f06fc 100644
--- a/src/core/hid/emulated_controller.cpp
+++ b/src/core/hid/emulated_controller.cpp
@@ -542,6 +542,7 @@ void EmulatedController::UnloadInput() {
}
void EmulatedController::EnableConfiguration() {
+ std::scoped_lock lock{connect_mutex, npad_mutex};
is_configuring = true;
tmp_is_connected = is_connected;
tmp_npad_type = npad_type;
@@ -1556,7 +1557,7 @@ void EmulatedController::Connect(bool use_temporary_value) {
auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Connected, !is_configuring); });
- std::scoped_lock lock{mutex};
+ std::scoped_lock lock{connect_mutex, mutex};
if (is_configuring) {
tmp_is_connected = true;
return;
@@ -1572,7 +1573,7 @@ void EmulatedController::Connect(bool use_temporary_value) {
void EmulatedController::Disconnect() {
auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Disconnected, !is_configuring); });
- std::scoped_lock lock{mutex};
+ std::scoped_lock lock{connect_mutex, mutex};
if (is_configuring) {
tmp_is_connected = false;
return;
@@ -1586,7 +1587,7 @@ void EmulatedController::Disconnect() {
}
bool EmulatedController::IsConnected(bool get_temporary_value) const {
- std::scoped_lock lock{mutex};
+ std::scoped_lock lock{connect_mutex};
if (get_temporary_value && is_configuring) {
return tmp_is_connected;
}
@@ -1599,7 +1600,7 @@ NpadIdType EmulatedController::GetNpadIdType() const {
}
NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) const {
- std::scoped_lock lock{mutex};
+ std::scoped_lock lock{npad_mutex};
if (get_temporary_value && is_configuring) {
return tmp_npad_type;
}
@@ -1609,7 +1610,7 @@ NpadStyleIndex EmulatedController::GetNpadStyleIndex(bool get_temporary_value) c
void EmulatedController::SetNpadStyleIndex(NpadStyleIndex npad_type_) {
auto trigger_guard =
SCOPE_GUARD({ TriggerOnChange(ControllerTriggerType::Type, !is_configuring); });
- std::scoped_lock lock{mutex};
+ std::scoped_lock lock{mutex, npad_mutex};
if (is_configuring) {
if (tmp_npad_type == npad_type_) {
diff --git a/src/core/hid/emulated_controller.h b/src/core/hid/emulated_controller.h
index 88d77db8d..d4500583e 100644
--- a/src/core/hid/emulated_controller.h
+++ b/src/core/hid/emulated_controller.h
@@ -603,6 +603,8 @@ private:
mutable std::mutex mutex;
mutable std::mutex callback_mutex;
+ mutable std::mutex npad_mutex;
+ mutable std::mutex connect_mutex;
std::unordered_map<int, ControllerUpdateCallback> callback_list;
int last_callback_key = 0;
diff --git a/src/core/hle/kernel/k_hardware_timer.cpp b/src/core/hle/kernel/k_hardware_timer.cpp
index 4dcd53821..8e2e40307 100644
--- a/src/core/hle/kernel/k_hardware_timer.cpp
+++ b/src/core/hle/kernel/k_hardware_timer.cpp
@@ -35,7 +35,9 @@ void KHardwareTimer::DoTask() {
}
// Disable the timer interrupt while we handle this.
- this->DisableInterrupt();
+ // Not necessary due to core timing already having popped this event to call it.
+ // this->DisableInterrupt();
+ m_wakeup_time = std::numeric_limits<s64>::max();
if (const s64 next_time = this->DoInterruptTaskImpl(GetTick());
0 < next_time && next_time <= m_wakeup_time) {
diff --git a/src/core/hle/kernel/k_memory_layout.cpp b/src/core/hle/kernel/k_memory_layout.cpp
index af40092c0..bec714668 100644
--- a/src/core/hle/kernel/k_memory_layout.cpp
+++ b/src/core/hle/kernel/k_memory_layout.cpp
@@ -61,7 +61,7 @@ bool KMemoryRegionTree::Insert(u64 address, size_t size, u32 type_id, u32 new_at
found->Reset(address, inserted_region_last, old_pair, new_attr, type_id);
this->insert(*found);
} else {
- // If we can't re-use, adjust the old region.
+ // If we can't reuse, adjust the old region.
found->Reset(old_address, address - 1, old_pair, old_attr, old_type);
this->insert(*found);
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 9bfc85b34..1fbfbf31f 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -5,6 +5,7 @@
#include "common/assert.h"
#include "common/literals.h"
#include "common/scope_exit.h"
+#include "common/settings.h"
#include "core/core.h"
#include "core/hle/kernel/k_address_space_info.h"
#include "core/hle/kernel/k_memory_block.h"
@@ -337,11 +338,14 @@ Result KPageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_type
}
void KPageTable::Finalize() {
+ auto HostUnmapCallback = [&](KProcessAddress addr, u64 size) {
+ if (Settings::IsFastmemEnabled()) {
+ m_system.DeviceMemory().buffer.Unmap(GetInteger(addr), size);
+ }
+ };
+
// Finalize memory blocks.
- m_memory_block_manager.Finalize(m_memory_block_slab_manager,
- [&](KProcessAddress addr, u64 size) {
- m_memory->UnmapRegion(*m_page_table_impl, addr, size);
- });
+ m_memory_block_manager.Finalize(m_memory_block_slab_manager, std::move(HostUnmapCallback));
// Release any insecure mapped memory.
if (m_mapped_insecure_memory) {
@@ -2945,6 +2949,23 @@ Result KPageTable::UnlockForIpcUserBuffer(KProcessAddress address, size_t size)
KMemoryAttribute::Locked, nullptr));
}
+Result KPageTable::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
+ KMemoryPermission perm) {
+ R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer,
+ KMemoryState::FlagCanTransfer, KMemoryPermission::All,
+ KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
+ KMemoryAttribute::None, perm, KMemoryAttribute::Locked));
+}
+
+Result KPageTable::UnlockForTransferMemory(KProcessAddress address, size_t size,
+ const KPageGroup& pg) {
+ R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer,
+ KMemoryState::FlagCanTransfer, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::All,
+ KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite,
+ KMemoryAttribute::Locked, std::addressof(pg)));
+}
+
Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) {
R_RETURN(this->LockMemoryAndOpen(
out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory,
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index b9e8c6042..7da675f27 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -104,6 +104,9 @@ public:
Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state);
Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state);
+ Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size,
+ KMemoryPermission perm);
+ Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg);
Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size);
Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg);
Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages,
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp
index 13d34125c..0e2e11743 100644
--- a/src/core/hle/kernel/k_transfer_memory.cpp
+++ b/src/core/hle/kernel/k_transfer_memory.cpp
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/scope_exit.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_resource_limit.h"
#include "core/hle/kernel/k_transfer_memory.h"
@@ -9,28 +10,50 @@
namespace Kernel {
KTransferMemory::KTransferMemory(KernelCore& kernel)
- : KAutoObjectWithSlabHeapAndContainer{kernel} {}
+ : KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{kernel} {}
KTransferMemory::~KTransferMemory() = default;
-Result KTransferMemory::Initialize(KProcessAddress address, std::size_t size,
- Svc::MemoryPermission owner_perm) {
+Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size,
+ Svc::MemoryPermission own_perm) {
// Set members.
m_owner = GetCurrentProcessPointer(m_kernel);
- // TODO(bunnei): Lock for transfer memory
+ // Get the owner page table.
+ auto& page_table = m_owner->GetPageTable();
+
+ // Construct the page group, guarding to make sure our state is valid on exit.
+ m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager());
+ auto pg_guard = SCOPE_GUARD({ m_page_group.reset(); });
+
+ // Lock the memory.
+ R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size,
+ ConvertToKMemoryPermission(own_perm)));
// Set remaining tracking members.
m_owner->Open();
- m_owner_perm = owner_perm;
- m_address = address;
- m_size = size;
+ m_owner_perm = own_perm;
+ m_address = addr;
m_is_initialized = true;
+ m_is_mapped = false;
+ // We succeeded.
+ pg_guard.Cancel();
R_SUCCEED();
}
-void KTransferMemory::Finalize() {}
+void KTransferMemory::Finalize() {
+ // Unlock.
+ if (!m_is_mapped) {
+ const size_t size = m_page_group->GetNumPages() * PageSize;
+ ASSERT(R_SUCCEEDED(
+ m_owner->GetPageTable().UnlockForTransferMemory(m_address, size, *m_page_group)));
+ }
+
+ // Close the page group.
+ m_page_group->Close();
+ m_page_group->Finalize();
+}
void KTransferMemory::PostDestroy(uintptr_t arg) {
KProcess* owner = reinterpret_cast<KProcess*>(arg);
@@ -38,4 +61,54 @@ void KTransferMemory::PostDestroy(uintptr_t arg) {
owner->Close();
}
+Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm) {
+ // Validate the size.
+ R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Validate the permission.
+ R_UNLESS(m_owner_perm == map_perm, ResultInvalidState);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Ensure we're not already mapped.
+ R_UNLESS(!m_is_mapped, ResultInvalidState);
+
+ // Map the memory.
+ const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
+ ? KMemoryState::Transfered
+ : KMemoryState::SharedTransfered;
+ R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup(
+ address, *m_page_group, state, KMemoryPermission::UserReadWrite));
+
+ // Mark ourselves as mapped.
+ m_is_mapped = true;
+
+ R_SUCCEED();
+}
+
+Result KTransferMemory::Unmap(KProcessAddress address, size_t size) {
+ // Validate the size.
+ R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Unmap the memory.
+ const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None)
+ ? KMemoryState::Transfered
+ : KMemoryState::SharedTransfered;
+ R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state));
+
+ // Mark ourselves as unmapped.
+ ASSERT(m_is_mapped);
+ m_is_mapped = false;
+
+ R_SUCCEED();
+}
+
+size_t KTransferMemory::GetSize() const {
+ return m_is_initialized ? m_page_group->GetNumPages() * PageSize : 0;
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h
index 54f97ccb4..8a0b08761 100644
--- a/src/core/hle/kernel/k_transfer_memory.h
+++ b/src/core/hle/kernel/k_transfer_memory.h
@@ -3,6 +3,9 @@
#pragma once
+#include <optional>
+
+#include "core/hle/kernel/k_page_group.h"
#include "core/hle/kernel/slab_helpers.h"
#include "core/hle/kernel/svc_types.h"
#include "core/hle/result.h"
@@ -48,16 +51,19 @@ public:
return m_address;
}
- size_t GetSize() const {
- return m_is_initialized ? m_size : 0;
- }
+ size_t GetSize() const;
+
+ Result Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm);
+ Result Unmap(KProcessAddress address, size_t size);
private:
+ std::optional<KPageGroup> m_page_group{};
KProcess* m_owner{};
KProcessAddress m_address{};
+ KLightLock m_lock;
Svc::MemoryPermission m_owner_perm{};
- size_t m_size{};
bool m_is_initialized{};
+ bool m_is_mapped{};
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
index 7d94e7f09..1f97121b3 100644
--- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp
+++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp
@@ -71,15 +71,59 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64
}
Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size,
- MemoryPermission owner_perm) {
- UNIMPLEMENTED();
- R_THROW(ResultNotImplemented);
+ MemoryPermission map_perm) {
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Validate the permission.
+ R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidState);
+
+ // Get the transfer memory.
+ KScopedAutoObject trmem = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KTransferMemory>(trmem_handle);
+ R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle);
+
+ // Verify that the mapping is in range.
+ R_UNLESS(GetCurrentProcess(system.Kernel())
+ .GetPageTable()
+ .CanContain(address, size, KMemoryState::Transfered),
+ ResultInvalidMemoryRegion);
+
+ // Map the transfer memory.
+ R_TRY(trmem->Map(address, size, map_perm));
+
+ // We succeeded.
+ R_SUCCEED();
}
Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address,
uint64_t size) {
- UNIMPLEMENTED();
- R_THROW(ResultNotImplemented);
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Get the transfer memory.
+ KScopedAutoObject trmem = GetCurrentProcess(system.Kernel())
+ .GetHandleTable()
+ .GetObject<KTransferMemory>(trmem_handle);
+ R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle);
+
+ // Verify that the mapping is in range.
+ R_UNLESS(GetCurrentProcess(system.Kernel())
+ .GetPageTable()
+ .CanContain(address, size, KMemoryState::Transfered),
+ ResultInvalidMemoryRegion);
+
+ // Unmap the transfer memory.
+ R_TRY(trmem->Unmap(address, size));
+
+ R_SUCCEED();
}
Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address,
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index e9bd04842..ac376b55a 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -8,6 +8,7 @@
#include "common/settings.h"
#include "common/settings_enums.h"
#include "core/core.h"
+#include "core/core_timing.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
@@ -19,6 +20,7 @@
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
+#include "core/hle/service/am/applets/applet_cabinet.h"
#include "core/hle/service/am/applets/applet_mii_edit_types.h"
#include "core/hle/service/am/applets/applet_profile_select.h"
#include "core/hle/service/am/applets/applet_web_browser.h"
@@ -29,15 +31,17 @@
#include "core/hle/service/apm/apm_controller.h"
#include "core/hle/service/apm/apm_interface.h"
#include "core/hle/service/bcat/backend/backend.h"
-#include "core/hle/service/caps/caps.h"
+#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/ns.h"
+#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/pm/pm.h"
#include "core/hle/service/server_manager.h"
#include "core/hle/service/sm/sm.h"
#include "core/hle/service/vi/vi.h"
+#include "core/hle/service/vi/vi_results.h"
#include "core/memory.h"
namespace Service::AM {
@@ -190,7 +194,7 @@ IDisplayController::IDisplayController(Core::System& system_)
{4, nullptr, "UpdateCallerAppletCaptureImage"},
{5, nullptr, "GetLastForegroundCaptureImageEx"},
{6, nullptr, "GetLastApplicationCaptureImageEx"},
- {7, nullptr, "GetCallerAppletCaptureImageEx"},
+ {7, &IDisplayController::GetCallerAppletCaptureImageEx, "GetCallerAppletCaptureImageEx"},
{8, &IDisplayController::TakeScreenShotOfOwnLayer, "TakeScreenShotOfOwnLayer"},
{9, nullptr, "CopyBetweenCaptureBuffers"},
{10, nullptr, "AcquireLastApplicationCaptureBuffer"},
@@ -208,8 +212,8 @@ IDisplayController::IDisplayController(Core::System& system_)
{23, nullptr, "ReleaseLastApplicationCaptureSharedBuffer"},
{24, nullptr, "AcquireLastForegroundCaptureSharedBuffer"},
{25, nullptr, "ReleaseLastForegroundCaptureSharedBuffer"},
- {26, nullptr, "AcquireCallerAppletCaptureSharedBuffer"},
- {27, nullptr, "ReleaseCallerAppletCaptureSharedBuffer"},
+ {26, &IDisplayController::AcquireCallerAppletCaptureSharedBuffer, "AcquireCallerAppletCaptureSharedBuffer"},
+ {27, &IDisplayController::ReleaseCallerAppletCaptureSharedBuffer, "ReleaseCallerAppletCaptureSharedBuffer"},
{28, nullptr, "TakeScreenShotOfOwnLayerEx"},
};
// clang-format on
@@ -219,6 +223,15 @@ IDisplayController::IDisplayController(Core::System& system_)
IDisplayController::~IDisplayController() = default;
+void IDisplayController::GetCallerAppletCaptureImageEx(HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push(1u);
+ rb.Push(0);
+}
+
void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -226,6 +239,22 @@ void IDisplayController::TakeScreenShotOfOwnLayer(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
+void IDisplayController::AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push(1U);
+ rb.Push(0);
+}
+
+void IDisplayController::ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
IDebugFunctions::IDebugFunctions(Core::System& system_)
: ServiceFramework{system_, "IDebugFunctions"} {
// clang-format off
@@ -285,14 +314,14 @@ ISelfController::ISelfController(Core::System& system_, Nvnflinger::Nvnflinger&
{20, nullptr, "SetDesirableKeyboardLayout"},
{21, nullptr, "GetScreenShotProgramId"},
{40, &ISelfController::CreateManagedDisplayLayer, "CreateManagedDisplayLayer"},
- {41, nullptr, "IsSystemBufferSharingEnabled"},
- {42, nullptr, "GetSystemSharedLayerHandle"},
- {43, nullptr, "GetSystemSharedBufferHandle"},
+ {41, &ISelfController::IsSystemBufferSharingEnabled, "IsSystemBufferSharingEnabled"},
+ {42, &ISelfController::GetSystemSharedLayerHandle, "GetSystemSharedLayerHandle"},
+ {43, &ISelfController::GetSystemSharedBufferHandle, "GetSystemSharedBufferHandle"},
{44, &ISelfController::CreateManagedDisplaySeparableLayer, "CreateManagedDisplaySeparableLayer"},
{45, nullptr, "SetManagedDisplayLayerSeparationMode"},
{46, nullptr, "SetRecordingLayerCompositionEnabled"},
{50, &ISelfController::SetHandlesRequestToDisplay, "SetHandlesRequestToDisplay"},
- {51, nullptr, "ApproveToDisplay"},
+ {51, &ISelfController::ApproveToDisplay, "ApproveToDisplay"},
{60, nullptr, "OverrideAutoSleepTimeAndDimmingTime"},
{61, nullptr, "SetMediaPlaybackState"},
{62, &ISelfController::SetIdleTimeDetectionExtension, "SetIdleTimeDetectionExtension"},
@@ -491,6 +520,50 @@ void ISelfController::CreateManagedDisplayLayer(HLERequestContext& ctx) {
rb.Push(*layer_id);
}
+void ISelfController::IsSystemBufferSharingEnabled(HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(this->EnsureBufferSharingEnabled());
+}
+
+void ISelfController::GetSystemSharedLayerHandle(HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(this->EnsureBufferSharingEnabled());
+ rb.Push<s64>(system_shared_buffer_id);
+ rb.Push<s64>(system_shared_layer_id);
+}
+
+void ISelfController::GetSystemSharedBufferHandle(HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(this->EnsureBufferSharingEnabled());
+ rb.Push<s64>(system_shared_buffer_id);
+}
+
+Result ISelfController::EnsureBufferSharingEnabled() {
+ if (buffer_sharing_enabled) {
+ return ResultSuccess;
+ }
+
+ if (system.GetAppletManager().GetCurrentAppletId() <= Applets::AppletId::Application) {
+ return VI::ResultOperationFailed;
+ }
+
+ const auto display_id = nvnflinger.OpenDisplay("Default");
+ const auto result = nvnflinger.GetSystemBufferManager().Initialize(
+ &system_shared_buffer_id, &system_shared_layer_id, *display_id);
+
+ if (result.IsSuccess()) {
+ buffer_sharing_enabled = true;
+ }
+
+ return result;
+}
+
void ISelfController::CreateManagedDisplaySeparableLayer(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
@@ -516,6 +589,13 @@ void ISelfController::SetHandlesRequestToDisplay(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
+void ISelfController::ApproveToDisplay(HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
void ISelfController::SetIdleTimeDetectionExtension(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
idle_time_detection_extension = rp.Pop<u32>();
@@ -684,9 +764,70 @@ void AppletMessageQueue::OperationModeChanged() {
on_operation_mode_changed->Signal();
}
+ILockAccessor::ILockAccessor(Core::System& system_)
+ : ServiceFramework{system_, "ILockAccessor"}, service_context{system_, "ILockAccessor"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {1, &ILockAccessor::TryLock, "TryLock"},
+ {2, &ILockAccessor::Unlock, "Unlock"},
+ {3, &ILockAccessor::GetEvent, "GetEvent"},
+ {4,&ILockAccessor::IsLocked, "IsLocked"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+
+ lock_event = service_context.CreateEvent("ILockAccessor::LockEvent");
+}
+
+ILockAccessor::~ILockAccessor() = default;
+
+void ILockAccessor::TryLock(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto return_handle = rp.Pop<bool>();
+
+ LOG_WARNING(Service_AM, "(STUBBED) called, return_handle={}", return_handle);
+
+ // TODO: When return_handle is true this function should return the lock handle
+
+ is_locked = true;
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u8>(is_locked);
+}
+
+void ILockAccessor::Unlock(HLERequestContext& ctx) {
+ LOG_INFO(Service_AM, "called");
+
+ is_locked = false;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void ILockAccessor::GetEvent(HLERequestContext& ctx) {
+ LOG_INFO(Service_AM, "called");
+
+ lock_event->Signal();
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(lock_event->GetReadableEvent());
+}
+
+void ILockAccessor::IsLocked(HLERequestContext& ctx) {
+ LOG_INFO(Service_AM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ rb.Push<u8>(is_locked);
+}
+
ICommonStateGetter::ICommonStateGetter(Core::System& system_,
std::shared_ptr<AppletMessageQueue> msg_queue_)
- : ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)} {
+ : ServiceFramework{system_, "ICommonStateGetter"}, msg_queue{std::move(msg_queue_)},
+ service_context{system_, "ICommonStateGetter"} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
@@ -699,14 +840,14 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
{7, nullptr, "GetCradleStatus"},
{8, &ICommonStateGetter::GetBootMode, "GetBootMode"},
{9, &ICommonStateGetter::GetCurrentFocusState, "GetCurrentFocusState"},
- {10, nullptr, "RequestToAcquireSleepLock"},
+ {10, &ICommonStateGetter::RequestToAcquireSleepLock, "RequestToAcquireSleepLock"},
{11, nullptr, "ReleaseSleepLock"},
{12, nullptr, "ReleaseSleepLockTransiently"},
- {13, nullptr, "GetAcquiredSleepLockEvent"},
+ {13, &ICommonStateGetter::GetAcquiredSleepLockEvent, "GetAcquiredSleepLockEvent"},
{14, nullptr, "GetWakeupCount"},
{20, nullptr, "PushToGeneralChannel"},
{30, nullptr, "GetHomeButtonReaderLockAccessor"},
- {31, nullptr, "GetReaderLockAccessorEx"},
+ {31, &ICommonStateGetter::GetReaderLockAccessorEx, "GetReaderLockAccessorEx"},
{32, nullptr, "GetWriterLockAccessorEx"},
{40, nullptr, "GetCradleFwVersion"},
{50, &ICommonStateGetter::IsVrModeEnabled, "IsVrModeEnabled"},
@@ -724,7 +865,7 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
{65, nullptr, "GetApplicationIdByContentActionName"},
{66, &ICommonStateGetter::SetCpuBoostMode, "SetCpuBoostMode"},
{67, nullptr, "CancelCpuBoostMode"},
- {68, nullptr, "GetBuiltInDisplayType"},
+ {68, &ICommonStateGetter::GetBuiltInDisplayType, "GetBuiltInDisplayType"},
{80, &ICommonStateGetter::PerformSystemButtonPressingIfInFocus, "PerformSystemButtonPressingIfInFocus"},
{90, nullptr, "SetPerformanceConfigurationChangedNotification"},
{91, nullptr, "GetCurrentPerformanceConfiguration"},
@@ -745,6 +886,8 @@ ICommonStateGetter::ICommonStateGetter(Core::System& system_,
RegisterHandlers(functions);
+ sleep_lock_event = service_context.CreateEvent("ICommonStateGetter::SleepLockEvent");
+
// Configure applets to be in foreground state
msg_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
msg_queue->PushMessage(AppletMessageQueue::AppletMessage::ChangeIntoForeground);
@@ -793,6 +936,36 @@ void ICommonStateGetter::GetCurrentFocusState(HLERequestContext& ctx) {
rb.Push(static_cast<u8>(FocusState::InFocus));
}
+void ICommonStateGetter::RequestToAcquireSleepLock(HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ // Sleep lock is acquired immediately.
+ sleep_lock_event->Signal();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+void ICommonStateGetter::GetReaderLockAccessorEx(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto unknown = rp.Pop<u32>();
+
+ LOG_INFO(Service_AM, "called, unknown={}", unknown);
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<ILockAccessor>(system);
+}
+
+void ICommonStateGetter::GetAcquiredSleepLockEvent(HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(ResultSuccess);
+ rb.PushCopyObjects(sleep_lock_event->GetReadableEvent());
+}
+
void ICommonStateGetter::IsVrModeEnabled(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
@@ -869,6 +1042,14 @@ void ICommonStateGetter::SetCpuBoostMode(HLERequestContext& ctx) {
apm_sys->SetCpuBoostMode(ctx);
}
+void ICommonStateGetter::GetBuiltInDisplayType(HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(0);
+}
+
void ICommonStateGetter::PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto system_button{rp.PopEnum<SystemButtonType>()};
@@ -1385,7 +1566,19 @@ ILibraryAppletSelfAccessor::ILibraryAppletSelfAccessor(Core::System& system_)
// clang-format on
RegisterHandlers(functions);
- PushInShowMiiEditData();
+ switch (system.GetAppletManager().GetCurrentAppletId()) {
+ case Applets::AppletId::Cabinet:
+ PushInShowCabinetData();
+ break;
+ case Applets::AppletId::MiiEdit:
+ PushInShowMiiEditData();
+ break;
+ case Applets::AppletId::PhotoViewer:
+ PushInShowAlbum();
+ break;
+ default:
+ break;
+ }
}
ILibraryAppletSelfAccessor::~ILibraryAppletSelfAccessor() = default;
@@ -1431,7 +1624,7 @@ void ILibraryAppletSelfAccessor::GetLibraryAppletInfo(HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
const LibraryAppletInfo applet_info{
- .applet_id = Applets::AppletId::MiiEdit,
+ .applet_id = system.GetAppletManager().GetCurrentAppletId(),
.library_applet_mode = Applets::LibraryAppletMode::AllForeground,
};
@@ -1459,6 +1652,52 @@ void ILibraryAppletSelfAccessor::GetCallerAppletIdentityInfo(HLERequestContext&
rb.PushRaw(applet_info);
}
+void ILibraryAppletSelfAccessor::PushInShowAlbum() {
+ const Applets::CommonArguments arguments{
+ .arguments_version = Applets::CommonArgumentVersion::Version3,
+ .size = Applets::CommonArgumentSize::Version3,
+ .library_version = 1,
+ .theme_color = Applets::ThemeColor::BasicBlack,
+ .play_startup_sound = true,
+ .system_tick = system.CoreTiming().GetClockTicks(),
+ };
+
+ std::vector<u8> argument_data(sizeof(arguments));
+ std::vector<u8> settings_data{2};
+ std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
+ queue_data.emplace_back(std::move(argument_data));
+ queue_data.emplace_back(std::move(settings_data));
+}
+
+void ILibraryAppletSelfAccessor::PushInShowCabinetData() {
+ const Applets::CommonArguments arguments{
+ .arguments_version = Applets::CommonArgumentVersion::Version3,
+ .size = Applets::CommonArgumentSize::Version3,
+ .library_version = static_cast<u32>(Applets::CabinetAppletVersion::Version1),
+ .theme_color = Applets::ThemeColor::BasicBlack,
+ .play_startup_sound = true,
+ .system_tick = system.CoreTiming().GetClockTicks(),
+ };
+
+ const Applets::StartParamForAmiiboSettings amiibo_settings{
+ .param_1 = 0,
+ .applet_mode = system.GetAppletManager().GetCabinetMode(),
+ .flags = Applets::CabinetFlags::None,
+ .amiibo_settings_1 = 0,
+ .device_handle = 0,
+ .tag_info{},
+ .register_info{},
+ .amiibo_settings_3{},
+ };
+
+ std::vector<u8> argument_data(sizeof(arguments));
+ std::vector<u8> settings_data(sizeof(amiibo_settings));
+ std::memcpy(argument_data.data(), &arguments, sizeof(arguments));
+ std::memcpy(settings_data.data(), &amiibo_settings, sizeof(amiibo_settings));
+ queue_data.emplace_back(std::move(argument_data));
+ queue_data.emplace_back(std::move(settings_data));
+}
+
void ILibraryAppletSelfAccessor::PushInShowMiiEditData() {
struct MiiEditV3 {
Applets::MiiEditAppletInputCommon common;
@@ -2094,9 +2333,6 @@ void IApplicationFunctions::PrepareForJit(HLERequestContext& ctx) {
void LoopProcess(Nvnflinger::Nvnflinger& nvnflinger, Core::System& system) {
auto message_queue = std::make_shared<AppletMessageQueue>(system);
- // Needed on game boot
- message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged);
-
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService(
@@ -2238,7 +2474,7 @@ void IProcessWindingController::GetLaunchReason(HLERequestContext& ctx) {
}
void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx) {
- const auto applet_id = Applets::AppletId::MiiEdit;
+ const auto applet_id = system.GetAppletManager().GetCurrentAppletId();
const auto applet_mode = Applets::LibraryAppletMode::AllForeground;
LOG_WARNING(Service_AM, "(STUBBED) called with applet_id={:08X}, applet_mode={:08X}", applet_id,
@@ -2259,4 +2495,5 @@ void IProcessWindingController::OpenCallingLibraryApplet(HLERequestContext& ctx)
rb.Push(ResultSuccess);
rb.PushIpcInterface<ILibraryAppletAccessor>(system, applet);
}
+
} // namespace Service::AM
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 5b97eb5e3..4a045cfd4 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -122,7 +122,10 @@ public:
~IDisplayController() override;
private:
+ void GetCallerAppletCaptureImageEx(HLERequestContext& ctx);
void TakeScreenShotOfOwnLayer(HLERequestContext& ctx);
+ void AcquireCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
+ void ReleaseCallerAppletCaptureSharedBuffer(HLERequestContext& ctx);
};
class IDebugFunctions final : public ServiceFramework<IDebugFunctions> {
@@ -150,9 +153,13 @@ private:
void SetRestartMessageEnabled(HLERequestContext& ctx);
void SetOutOfFocusSuspendingEnabled(HLERequestContext& ctx);
void SetAlbumImageOrientation(HLERequestContext& ctx);
+ void IsSystemBufferSharingEnabled(HLERequestContext& ctx);
+ void GetSystemSharedBufferHandle(HLERequestContext& ctx);
+ void GetSystemSharedLayerHandle(HLERequestContext& ctx);
void CreateManagedDisplayLayer(HLERequestContext& ctx);
void CreateManagedDisplaySeparableLayer(HLERequestContext& ctx);
void SetHandlesRequestToDisplay(HLERequestContext& ctx);
+ void ApproveToDisplay(HLERequestContext& ctx);
void SetIdleTimeDetectionExtension(HLERequestContext& ctx);
void GetIdleTimeDetectionExtension(HLERequestContext& ctx);
void ReportUserIsActive(HLERequestContext& ctx);
@@ -164,6 +171,8 @@ private:
void SaveCurrentScreenshot(HLERequestContext& ctx);
void SetRecordVolumeMuted(HLERequestContext& ctx);
+ Result EnsureBufferSharingEnabled();
+
enum class ScreenshotPermission : u32 {
Inherit = 0,
Enable = 1,
@@ -179,10 +188,30 @@ private:
u32 idle_time_detection_extension = 0;
u64 num_fatal_sections_entered = 0;
+ u64 system_shared_buffer_id = 0;
+ u64 system_shared_layer_id = 0;
bool is_auto_sleep_disabled = false;
+ bool buffer_sharing_enabled = false;
ScreenshotPermission screenshot_permission = ScreenshotPermission::Inherit;
};
+class ILockAccessor final : public ServiceFramework<ILockAccessor> {
+public:
+ explicit ILockAccessor(Core::System& system_);
+ ~ILockAccessor() override;
+
+private:
+ void TryLock(HLERequestContext& ctx);
+ void Unlock(HLERequestContext& ctx);
+ void GetEvent(HLERequestContext& ctx);
+ void IsLocked(HLERequestContext& ctx);
+
+ bool is_locked{};
+
+ Kernel::KEvent* lock_event;
+ KernelHelpers::ServiceContext service_context;
+};
+
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
public:
explicit ICommonStateGetter(Core::System& system_,
@@ -223,6 +252,9 @@ private:
void GetEventHandle(HLERequestContext& ctx);
void ReceiveMessage(HLERequestContext& ctx);
void GetCurrentFocusState(HLERequestContext& ctx);
+ void RequestToAcquireSleepLock(HLERequestContext& ctx);
+ void GetAcquiredSleepLockEvent(HLERequestContext& ctx);
+ void GetReaderLockAccessorEx(HLERequestContext& ctx);
void GetDefaultDisplayResolutionChangeEvent(HLERequestContext& ctx);
void GetOperationMode(HLERequestContext& ctx);
void GetPerformanceMode(HLERequestContext& ctx);
@@ -234,12 +266,15 @@ private:
void EndVrModeEx(HLERequestContext& ctx);
void GetDefaultDisplayResolution(HLERequestContext& ctx);
void SetCpuBoostMode(HLERequestContext& ctx);
+ void GetBuiltInDisplayType(HLERequestContext& ctx);
void PerformSystemButtonPressingIfInFocus(HLERequestContext& ctx);
void GetSettingsPlatformRegion(HLERequestContext& ctx);
void SetRequestExitToLibraryAppletAtExecuteNextProgramEnabled(HLERequestContext& ctx);
std::shared_ptr<AppletMessageQueue> msg_queue;
bool vr_mode_state{};
+ Kernel::KEvent* sleep_lock_event;
+ KernelHelpers::ServiceContext service_context;
};
class IStorageImpl {
@@ -311,6 +346,8 @@ private:
void ExitProcessAndReturn(HLERequestContext& ctx);
void GetCallerAppletIdentityInfo(HLERequestContext& ctx);
+ void PushInShowAlbum();
+ void PushInShowCabinetData();
void PushInShowMiiEditData();
std::deque<std::vector<u8>> queue_data;
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index eb12312cc..e30e6478a 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -28,8 +28,8 @@ public:
{11, &ILibraryAppletProxy::GetLibraryAppletCreator, "GetLibraryAppletCreator"},
{20, &ILibraryAppletProxy::OpenLibraryAppletSelfAccessor, "OpenLibraryAppletSelfAccessor"},
{21, &ILibraryAppletProxy::GetAppletCommonFunctions, "GetAppletCommonFunctions"},
- {22, nullptr, "GetHomeMenuFunctions"},
- {23, nullptr, "GetGlobalStateController"},
+ {22, &ILibraryAppletProxy::GetHomeMenuFunctions, "GetHomeMenuFunctions"},
+ {23, &ILibraryAppletProxy::GetGlobalStateController, "GetGlobalStateController"},
{1000, &ILibraryAppletProxy::GetDebugFunctions, "GetDebugFunctions"},
};
// clang-format on
@@ -110,6 +110,22 @@ private:
rb.PushIpcInterface<IAppletCommonFunctions>(system);
}
+ void GetHomeMenuFunctions(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IHomeMenuFunctions>(system);
+ }
+
+ void GetGlobalStateController(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<IGlobalStateController>(system);
+ }
+
void GetDebugFunctions(HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
diff --git a/src/core/hle/service/am/applets/applet_cabinet.h b/src/core/hle/service/am/applets/applet_cabinet.h
index b56427021..f498796f7 100644
--- a/src/core/hle/service/am/applets/applet_cabinet.h
+++ b/src/core/hle/service/am/applets/applet_cabinet.h
@@ -29,6 +29,15 @@ enum class CabinetAppletVersion : u32 {
Version1 = 0x1,
};
+enum class CabinetFlags : u8 {
+ None = 0,
+ DeviceHandle = 1 << 0,
+ TagInfo = 1 << 1,
+ RegisterInfo = 1 << 2,
+ All = DeviceHandle | TagInfo | RegisterInfo,
+};
+DECLARE_ENUM_FLAG_OPERATORS(CabinetFlags)
+
enum class CabinetResult : u8 {
Cancel = 0,
TagInfo = 1 << 1,
@@ -51,7 +60,7 @@ static_assert(sizeof(AmiiboSettingsStartParam) == 0x30,
struct StartParamForAmiiboSettings {
u8 param_1;
Service::NFP::CabinetMode applet_mode;
- u8 flags;
+ CabinetFlags flags;
u8 amiibo_settings_1;
u64 device_handle;
Service::NFP::TagInfo tag_info;
diff --git a/src/core/hle/service/am/applets/applet_error.cpp b/src/core/hle/service/am/applets/applet_error.cpp
index b46ea840c..5d17c353f 100644
--- a/src/core/hle/service/am/applets/applet_error.cpp
+++ b/src/core/hle/service/am/applets/applet_error.cpp
@@ -138,6 +138,10 @@ void Error::Initialize() {
CopyArgumentData(data, args->application_error);
error_code = Result(args->application_error.error_code);
break;
+ case ErrorAppletMode::ShowErrorPctl:
+ CopyArgumentData(data, args->error_record);
+ error_code = Decode64BitError(args->error_record.error_code_64);
+ break;
case ErrorAppletMode::ShowErrorRecord:
CopyArgumentData(data, args->error_record);
error_code = Decode64BitError(args->error_record.error_code_64);
@@ -191,6 +195,7 @@ void Error::Execute() {
frontend.ShowCustomErrorText(error_code, main_text_string, detail_text_string, callback);
break;
}
+ case ErrorAppletMode::ShowErrorPctl:
case ErrorAppletMode::ShowErrorRecord:
reporter.SaveErrorReport(title_id, error_code,
fmt::format("{:016X}", args->error_record.posix_time));
diff --git a/src/core/hle/service/am/applets/applet_general_backend.cpp b/src/core/hle/service/am/applets/applet_general_backend.cpp
index 8b352020e..c0032f652 100644
--- a/src/core/hle/service/am/applets/applet_general_backend.cpp
+++ b/src/core/hle/service/am/applets/applet_general_backend.cpp
@@ -223,9 +223,9 @@ void StubApplet::Initialize() {
const auto data = broker.PeekDataToAppletForDebug();
system.GetReporter().SaveUnimplementedAppletReport(
- static_cast<u32>(id), common_args.arguments_version, common_args.library_version,
- common_args.theme_color, common_args.play_startup_sound, common_args.system_tick,
- data.normal, data.interactive);
+ static_cast<u32>(id), static_cast<u32>(common_args.arguments_version),
+ common_args.library_version, static_cast<u32>(common_args.theme_color),
+ common_args.play_startup_sound, common_args.system_tick, data.normal, data.interactive);
LogCurrentStorage(broker, "Initialize");
}
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 10afbc2da..89d5434af 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -199,6 +199,14 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
return frontend;
}
+NFP::CabinetMode AppletManager::GetCabinetMode() const {
+ return cabinet_mode;
+}
+
+AppletId AppletManager::GetCurrentAppletId() const {
+ return current_applet_id;
+}
+
void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
if (set.cabinet != nullptr) {
frontend.cabinet = std::move(set.cabinet);
@@ -237,6 +245,14 @@ void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
}
}
+void AppletManager::SetCabinetMode(NFP::CabinetMode mode) {
+ cabinet_mode = mode;
+}
+
+void AppletManager::SetCurrentAppletId(AppletId applet_id) {
+ current_applet_id = applet_id;
+}
+
void AppletManager::SetDefaultAppletFrontendSet() {
ClearAll();
SetDefaultAppletsIfMissing();
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index 12f374199..f02bbc450 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -34,6 +34,10 @@ class KEvent;
class KReadableEvent;
} // namespace Kernel
+namespace Service::NFP {
+enum class CabinetMode : u8;
+} // namespace Service::NFP
+
namespace Service::AM {
class IStorage;
@@ -41,6 +45,8 @@ class IStorage;
namespace Applets {
enum class AppletId : u32 {
+ None = 0x00,
+ Application = 0x01,
OverlayDisplay = 0x02,
QLaunch = 0x03,
Starter = 0x04,
@@ -71,6 +77,32 @@ enum class LibraryAppletMode : u32 {
AllForegroundInitiallyHidden = 4,
};
+enum class CommonArgumentVersion : u32 {
+ Version0,
+ Version1,
+ Version2,
+ Version3,
+};
+
+enum class CommonArgumentSize : u32 {
+ Version3 = 0x20,
+};
+
+enum class ThemeColor : u32 {
+ BasicWhite = 0,
+ BasicBlack = 3,
+};
+
+struct CommonArguments {
+ CommonArgumentVersion arguments_version;
+ CommonArgumentSize size;
+ u32 library_version;
+ ThemeColor theme_color;
+ bool play_startup_sound;
+ u64_le system_tick;
+};
+static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
+
class AppletDataBroker final {
public:
explicit AppletDataBroker(Core::System& system_, LibraryAppletMode applet_mode_);
@@ -161,16 +193,6 @@ public:
}
protected:
- struct CommonArguments {
- u32_le arguments_version;
- u32_le size;
- u32_le library_version;
- u32_le theme_color;
- bool play_startup_sound;
- u64_le system_tick;
- };
- static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
-
CommonArguments common_args{};
AppletDataBroker broker;
LibraryAppletMode applet_mode;
@@ -219,8 +241,12 @@ public:
~AppletManager();
const AppletFrontendSet& GetAppletFrontendSet() const;
+ NFP::CabinetMode GetCabinetMode() const;
+ AppletId GetCurrentAppletId() const;
void SetAppletFrontendSet(AppletFrontendSet set);
+ void SetCabinetMode(NFP::CabinetMode mode);
+ void SetCurrentAppletId(AppletId applet_id);
void SetDefaultAppletFrontendSet();
void SetDefaultAppletsIfMissing();
void ClearAll();
@@ -228,6 +254,9 @@ public:
std::shared_ptr<Applet> GetApplet(AppletId id, LibraryAppletMode mode) const;
private:
+ AppletId current_applet_id{};
+ NFP::CabinetMode cabinet_mode{};
+
AppletFrontendSet frontend;
Core::System& system;
};
diff --git a/src/core/hle/service/caps/caps.cpp b/src/core/hle/service/caps/caps.cpp
index 610fe9940..286f9fd10 100644
--- a/src/core/hle/service/caps/caps.cpp
+++ b/src/core/hle/service/caps/caps.cpp
@@ -4,6 +4,7 @@
#include "core/hle/service/caps/caps.h"
#include "core/hle/service/caps/caps_a.h"
#include "core/hle/service/caps/caps_c.h"
+#include "core/hle/service/caps/caps_manager.h"
#include "core/hle/service/caps/caps_sc.h"
#include "core/hle/service/caps/caps_ss.h"
#include "core/hle/service/caps/caps_su.h"
@@ -15,13 +16,21 @@ namespace Service::Capture {
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
+ auto album_manager = std::make_shared<AlbumManager>();
+
+ server_manager->RegisterNamedService(
+ "caps:a", std::make_shared<IAlbumAccessorService>(system, album_manager));
+ server_manager->RegisterNamedService(
+ "caps:c", std::make_shared<IAlbumControlService>(system, album_manager));
+ server_manager->RegisterNamedService(
+ "caps:u", std::make_shared<IAlbumApplicationService>(system, album_manager));
+
+ server_manager->RegisterNamedService("caps:ss", std::make_shared<IScreenShotService>(system));
+ server_manager->RegisterNamedService("caps:sc",
+ std::make_shared<IScreenShotControlService>(system));
+ server_manager->RegisterNamedService("caps:su",
+ std::make_shared<IScreenShotApplicationService>(system));
- server_manager->RegisterNamedService("caps:a", std::make_shared<CAPS_A>(system));
- server_manager->RegisterNamedService("caps:c", std::make_shared<CAPS_C>(system));
- server_manager->RegisterNamedService("caps:u", std::make_shared<CAPS_U>(system));
- server_manager->RegisterNamedService("caps:sc", std::make_shared<CAPS_SC>(system));
- server_manager->RegisterNamedService("caps:ss", std::make_shared<CAPS_SS>(system));
- server_manager->RegisterNamedService("caps:su", std::make_shared<CAPS_SU>(system));
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/caps/caps.h b/src/core/hle/service/caps/caps.h
index 15f0ecfaa..58e9725b8 100644
--- a/src/core/hle/service/caps/caps.h
+++ b/src/core/hle/service/caps/caps.h
@@ -3,93 +3,12 @@
#pragma once
-#include "common/common_funcs.h"
-#include "common/common_types.h"
-
namespace Core {
class System;
}
-namespace Service::SM {
-class ServiceManager;
-}
-
namespace Service::Capture {
-enum class AlbumImageOrientation {
- Orientation0 = 0,
- Orientation1 = 1,
- Orientation2 = 2,
- Orientation3 = 3,
-};
-
-enum class AlbumReportOption : s32 {
- Disable = 0,
- Enable = 1,
-};
-
-enum class ContentType : u8 {
- Screenshot = 0,
- Movie = 1,
- ExtraMovie = 3,
-};
-
-enum class AlbumStorage : u8 {
- NAND = 0,
- SD = 1,
-};
-
-struct AlbumFileDateTime {
- s16 year{};
- s8 month{};
- s8 day{};
- s8 hour{};
- s8 minute{};
- s8 second{};
- s8 uid{};
-};
-static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size.");
-
-struct AlbumEntry {
- u64 size{};
- u64 application_id{};
- AlbumFileDateTime datetime{};
- AlbumStorage storage{};
- ContentType content{};
- INSERT_PADDING_BYTES(6);
-};
-static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size.");
-
-struct AlbumFileEntry {
- u64 size{}; // Size of the entry
- u64 hash{}; // AES256 with hardcoded key over AlbumEntry
- AlbumFileDateTime datetime{};
- AlbumStorage storage{};
- ContentType content{};
- INSERT_PADDING_BYTES(5);
- u8 unknown{1}; // Set to 1 on official SW
-};
-static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size.");
-
-struct ApplicationAlbumEntry {
- u64 size{}; // Size of the entry
- u64 hash{}; // AES256 with hardcoded key over AlbumEntry
- AlbumFileDateTime datetime{};
- AlbumStorage storage{};
- ContentType content{};
- INSERT_PADDING_BYTES(5);
- u8 unknown{1}; // Set to 1 on official SW
-};
-static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size.");
-
-struct ApplicationAlbumFileEntry {
- ApplicationAlbumEntry entry{};
- AlbumFileDateTime datetime{};
- u64 unknown{};
-};
-static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30,
- "ApplicationAlbumFileEntry has incorrect size.");
-
void LoopProcess(Core::System& system);
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp
index 44267b284..e22f72bf6 100644
--- a/src/core/hle/service/caps/caps_a.cpp
+++ b/src/core/hle/service/caps/caps_a.cpp
@@ -1,40 +1,26 @@
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
+#include "common/logging/log.h"
#include "core/hle/service/caps/caps_a.h"
+#include "core/hle/service/caps/caps_manager.h"
+#include "core/hle/service/caps/caps_result.h"
+#include "core/hle/service/caps/caps_types.h"
+#include "core/hle/service/ipc_helpers.h"
namespace Service::Capture {
-class IAlbumAccessorSession final : public ServiceFramework<IAlbumAccessorSession> {
-public:
- explicit IAlbumAccessorSession(Core::System& system_)
- : ServiceFramework{system_, "IAlbumAccessorSession"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {2001, nullptr, "OpenAlbumMovieReadStream"},
- {2002, nullptr, "CloseAlbumMovieReadStream"},
- {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"},
- {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"},
- {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"},
- {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"},
- {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"},
- {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} {
+IAlbumAccessorService::IAlbumAccessorService(Core::System& system_,
+ std::shared_ptr<AlbumManager> album_manager)
+ : ServiceFramework{system_, "caps:a"}, manager{album_manager} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "GetAlbumFileCount"},
{1, nullptr, "GetAlbumFileList"},
{2, nullptr, "LoadAlbumFile"},
- {3, nullptr, "DeleteAlbumFile"},
+ {3, &IAlbumAccessorService::DeleteAlbumFile, "DeleteAlbumFile"},
{4, nullptr, "StorageCopyAlbumFile"},
- {5, nullptr, "IsAlbumMounted"},
+ {5, &IAlbumAccessorService::IsAlbumMounted, "IsAlbumMounted"},
{6, nullptr, "GetAlbumUsage"},
{7, nullptr, "GetAlbumFileSize"},
{8, nullptr, "LoadAlbumFileThumbnail"},
@@ -47,18 +33,18 @@ CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} {
{15, nullptr, "GetAlbumUsage3"},
{16, nullptr, "GetAlbumMountResult"},
{17, nullptr, "GetAlbumUsage16"},
- {18, nullptr, "Unknown18"},
+ {18, &IAlbumAccessorService::Unknown18, "Unknown18"},
{19, nullptr, "Unknown19"},
{100, nullptr, "GetAlbumFileCountEx0"},
- {101, nullptr, "GetAlbumFileListEx0"},
+ {101, &IAlbumAccessorService::GetAlbumFileListEx0, "GetAlbumFileListEx0"},
{202, nullptr, "SaveEditedScreenShot"},
{301, nullptr, "GetLastThumbnail"},
{302, nullptr, "GetLastOverlayMovieThumbnail"},
- {401, nullptr, "GetAutoSavingStorage"},
+ {401, &IAlbumAccessorService::GetAutoSavingStorage, "GetAutoSavingStorage"},
{501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"},
{1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"},
- {1002, nullptr, "LoadAlbumScreenShotImageEx1"},
- {1003, nullptr, "LoadAlbumScreenShotThumbnailImageEx1"},
+ {1002, &IAlbumAccessorService::LoadAlbumScreenShotImageEx1, "LoadAlbumScreenShotImageEx1"},
+ {1003, &IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1, "LoadAlbumScreenShotThumbnailImageEx1"},
{8001, nullptr, "ForceAlbumUnmounted"},
{8002, nullptr, "ResetAlbumMountStatus"},
{8011, nullptr, "RefreshAlbumCache"},
@@ -74,6 +60,199 @@ CAPS_A::CAPS_A(Core::System& system_) : ServiceFramework{system_, "caps:a"} {
RegisterHandlers(functions);
}
-CAPS_A::~CAPS_A() = default;
+IAlbumAccessorService::~IAlbumAccessorService() = default;
+
+void IAlbumAccessorService::DeleteAlbumFile(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto file_id{rp.PopRaw<AlbumFileId>()};
+
+ LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}",
+ file_id.application_id, file_id.storage, file_id.type);
+
+ Result result = manager->DeleteAlbumFile(file_id);
+ result = TranslateResult(result);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IAlbumAccessorService::IsAlbumMounted(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto storage{rp.PopEnum<AlbumStorage>()};
+
+ LOG_INFO(Service_Capture, "called, storage={}", storage);
+
+ Result result = manager->IsAlbumMounted(storage);
+ const bool is_mounted = result.IsSuccess();
+ result = TranslateResult(result);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(result);
+ rb.Push<u8>(is_mounted);
+}
+
+void IAlbumAccessorService::Unknown18(HLERequestContext& ctx) {
+ struct UnknownBuffer {
+ INSERT_PADDING_BYTES(0x10);
+ };
+ static_assert(sizeof(UnknownBuffer) == 0x10, "UnknownBuffer is an invalid size");
+
+ LOG_WARNING(Service_Capture, "(STUBBED) called");
+
+ std::vector<UnknownBuffer> buffer{};
+
+ if (!buffer.empty()) {
+ ctx.WriteBuffer(buffer);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(static_cast<u32>(buffer.size()));
+}
+
+void IAlbumAccessorService::GetAlbumFileListEx0(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto storage{rp.PopEnum<AlbumStorage>()};
+ const auto flags{rp.Pop<u8>()};
+ const auto album_entry_size{ctx.GetWriteBufferNumElements<AlbumEntry>()};
+
+ LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags);
+
+ std::vector<AlbumEntry> entries;
+ Result result = manager->GetAlbumFileList(entries, storage, flags);
+ result = TranslateResult(result);
+
+ entries.resize(std::min(album_entry_size, entries.size()));
+
+ if (!entries.empty()) {
+ ctx.WriteBuffer(entries);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(result);
+ rb.Push(entries.size());
+}
+
+void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) {
+ LOG_WARNING(Service_Capture, "(STUBBED) called");
+
+ bool is_autosaving{};
+ Result result = manager->GetAutoSavingStorage(is_autosaving);
+ result = TranslateResult(result);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(result);
+ rb.Push<u8>(is_autosaving);
+}
+
+void IAlbumAccessorService::LoadAlbumScreenShotImageEx1(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto file_id{rp.PopRaw<AlbumFileId>()};
+ const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()};
+ const auto image_buffer_size{ctx.GetWriteBufferSize(1)};
+
+ LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
+ file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);
+
+ std::vector<u8> image;
+ LoadAlbumScreenShotImageOutput image_output;
+ Result result =
+ manager->LoadAlbumScreenShotImage(image_output, image, file_id, decoder_options);
+ result = TranslateResult(result);
+
+ if (image.size() > image_buffer_size) {
+ result = ResultWorkMemoryError;
+ }
+
+ if (result.IsSuccess()) {
+ ctx.WriteBuffer(image_output, 0);
+ ctx.WriteBuffer(image, 1);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto file_id{rp.PopRaw<AlbumFileId>()};
+ const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()};
+
+ LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}",
+ file_id.application_id, file_id.storage, file_id.type, decoder_options.flags);
+
+ std::vector<u8> image(ctx.GetWriteBufferSize(1));
+ LoadAlbumScreenShotImageOutput image_output;
+ Result result =
+ manager->LoadAlbumScreenShotThumbnail(image_output, image, file_id, decoder_options);
+ result = TranslateResult(result);
+
+ if (result.IsSuccess()) {
+ ctx.WriteBuffer(image_output, 0);
+ ctx.WriteBuffer(image, 1);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+}
+
+Result IAlbumAccessorService::TranslateResult(Result in_result) {
+ if (in_result.IsSuccess()) {
+ return in_result;
+ }
+
+ if ((in_result.raw & 0x3801ff) == ResultUnknown1024.raw) {
+ if (in_result.description - 0x514 < 100) {
+ return ResultInvalidFileData;
+ }
+ if (in_result.description - 0x5dc < 100) {
+ return ResultInvalidFileData;
+ }
+
+ if (in_result.description - 0x578 < 100) {
+ if (in_result == ResultFileCountLimit) {
+ return ResultUnknown22;
+ }
+ return ResultUnknown25;
+ }
+
+ if (in_result.raw < ResultUnknown1801.raw) {
+ if (in_result == ResultUnknown1202) {
+ return ResultUnknown810;
+ }
+ if (in_result == ResultUnknown1203) {
+ return ResultUnknown810;
+ }
+ if (in_result == ResultUnknown1701) {
+ return ResultUnknown5;
+ }
+ } else if (in_result.raw < ResultUnknown1803.raw) {
+ if (in_result == ResultUnknown1801) {
+ return ResultUnknown5;
+ }
+ if (in_result == ResultUnknown1802) {
+ return ResultUnknown6;
+ }
+ } else {
+ if (in_result == ResultUnknown1803) {
+ return ResultUnknown7;
+ }
+ if (in_result == ResultUnknown1804) {
+ return ResultOutOfRange;
+ }
+ }
+ return ResultUnknown1024;
+ }
+
+ if (in_result.module == ErrorModule::FS) {
+ if ((in_result.description >> 0xc < 0x7d) || (in_result.description - 1000 < 2000) ||
+ (((in_result.description - 3000) >> 3) < 0x271)) {
+ // TODO: Translate FS error
+ return in_result;
+ }
+ }
+
+ return in_result;
+}
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h
index 98a21a5ad..c90cff71e 100644
--- a/src/core/hle/service/caps/caps_a.h
+++ b/src/core/hle/service/caps/caps_a.h
@@ -10,11 +10,26 @@ class System;
}
namespace Service::Capture {
+class AlbumManager;
-class CAPS_A final : public ServiceFramework<CAPS_A> {
+class IAlbumAccessorService final : public ServiceFramework<IAlbumAccessorService> {
public:
- explicit CAPS_A(Core::System& system_);
- ~CAPS_A() override;
+ explicit IAlbumAccessorService(Core::System& system_,
+ std::shared_ptr<AlbumManager> album_manager);
+ ~IAlbumAccessorService() override;
+
+private:
+ void DeleteAlbumFile(HLERequestContext& ctx);
+ void IsAlbumMounted(HLERequestContext& ctx);
+ void Unknown18(HLERequestContext& ctx);
+ void GetAlbumFileListEx0(HLERequestContext& ctx);
+ void GetAutoSavingStorage(HLERequestContext& ctx);
+ void LoadAlbumScreenShotImageEx1(HLERequestContext& ctx);
+ void LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx);
+
+ Result TranslateResult(Result in_result);
+
+ std::shared_ptr<AlbumManager> manager = nullptr;
};
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp
index fc77e35cd..1e7fe6474 100644
--- a/src/core/hle/service/caps/caps_c.cpp
+++ b/src/core/hle/service/caps/caps_c.cpp
@@ -3,53 +3,21 @@
#include "common/logging/log.h"
#include "core/hle/service/caps/caps_c.h"
+#include "core/hle/service/caps/caps_manager.h"
+#include "core/hle/service/caps/caps_result.h"
+#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::Capture {
-class IAlbumControlSession final : public ServiceFramework<IAlbumControlSession> {
-public:
- explicit IAlbumControlSession(Core::System& system_)
- : ServiceFramework{system_, "IAlbumControlSession"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {2001, nullptr, "OpenAlbumMovieReadStream"},
- {2002, nullptr, "CloseAlbumMovieReadStream"},
- {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"},
- {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"},
- {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"},
- {2006, nullptr, "GetAlbumMovieReadStreamImageDataSize"},
- {2007, nullptr, "ReadImageDataFromAlbumMovieReadStream"},
- {2008, nullptr, "ReadFileAttributeFromAlbumMovieReadStream"},
- {2401, nullptr, "OpenAlbumMovieWriteStream"},
- {2402, nullptr, "FinishAlbumMovieWriteStream"},
- {2403, nullptr, "CommitAlbumMovieWriteStream"},
- {2404, nullptr, "DiscardAlbumMovieWriteStream"},
- {2405, nullptr, "DiscardAlbumMovieWriteStreamNoDelete"},
- {2406, nullptr, "CommitAlbumMovieWriteStreamEx"},
- {2411, nullptr, "StartAlbumMovieWriteStreamDataSection"},
- {2412, nullptr, "EndAlbumMovieWriteStreamDataSection"},
- {2413, nullptr, "StartAlbumMovieWriteStreamMetaSection"},
- {2414, nullptr, "EndAlbumMovieWriteStreamMetaSection"},
- {2421, nullptr, "ReadDataFromAlbumMovieWriteStream"},
- {2422, nullptr, "WriteDataToAlbumMovieWriteStream"},
- {2424, nullptr, "WriteMetaToAlbumMovieWriteStream"},
- {2431, nullptr, "GetAlbumMovieWriteStreamBrokenReason"},
- {2433, nullptr, "GetAlbumMovieWriteStreamDataSize"},
- {2434, nullptr, "SetAlbumMovieWriteStreamDataSize"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-CAPS_C::CAPS_C(Core::System& system_) : ServiceFramework{system_, "caps:c"} {
+IAlbumControlService::IAlbumControlService(Core::System& system_,
+ std::shared_ptr<AlbumManager> album_manager)
+ : ServiceFramework{system_, "caps:c"}, manager{album_manager} {
// clang-format off
static const FunctionInfo functions[] = {
{1, nullptr, "CaptureRawImage"},
{2, nullptr, "CaptureRawImageWithTimeout"},
- {33, &CAPS_C::SetShimLibraryVersion, "SetShimLibraryVersion"},
+ {33, &IAlbumControlService::SetShimLibraryVersion, "SetShimLibraryVersion"},
{1001, nullptr, "RequestTakingScreenShot"},
{1002, nullptr, "RequestTakingScreenShotWithTimeout"},
{1011, nullptr, "NotifyTakingScreenShotRefused"},
@@ -72,9 +40,9 @@ CAPS_C::CAPS_C(Core::System& system_) : ServiceFramework{system_, "caps:c"} {
RegisterHandlers(functions);
}
-CAPS_C::~CAPS_C() = default;
+IAlbumControlService::~IAlbumControlService() = default;
-void CAPS_C::SetShimLibraryVersion(HLERequestContext& ctx) {
+void IAlbumControlService::SetShimLibraryVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto library_version{rp.Pop<u64>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h
index 537b3a2e3..92ba242db 100644
--- a/src/core/hle/service/caps/caps_c.h
+++ b/src/core/hle/service/caps/caps_c.h
@@ -10,14 +10,18 @@ class System;
}
namespace Service::Capture {
+class AlbumManager;
-class CAPS_C final : public ServiceFramework<CAPS_C> {
+class IAlbumControlService final : public ServiceFramework<IAlbumControlService> {
public:
- explicit CAPS_C(Core::System& system_);
- ~CAPS_C() override;
+ explicit IAlbumControlService(Core::System& system_,
+ std::shared_ptr<AlbumManager> album_manager);
+ ~IAlbumControlService() override;
private:
void SetShimLibraryVersion(HLERequestContext& ctx);
+
+ std::shared_ptr<AlbumManager> manager = nullptr;
};
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp
new file mode 100644
index 000000000..2df6a930a
--- /dev/null
+++ b/src/core/hle/service/caps/caps_manager.cpp
@@ -0,0 +1,342 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <sstream>
+#include <stb_image.h>
+#include <stb_image_resize.h>
+
+#include "common/fs/file.h"
+#include "common/fs/path_util.h"
+#include "common/logging/log.h"
+#include "core/hle/service/caps/caps_manager.h"
+#include "core/hle/service/caps/caps_result.h"
+
+namespace Service::Capture {
+
+AlbumManager::AlbumManager() {}
+
+AlbumManager::~AlbumManager() = default;
+
+Result AlbumManager::DeleteAlbumFile(const AlbumFileId& file_id) {
+ if (file_id.storage > AlbumStorage::Sd) {
+ return ResultInvalidStorage;
+ }
+
+ if (!is_mounted) {
+ return ResultIsNotMounted;
+ }
+
+ std::filesystem::path path;
+ const auto result = GetFile(path, file_id);
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ if (!Common::FS::RemoveFile(path)) {
+ return ResultFileNotFound;
+ }
+
+ return ResultSuccess;
+}
+
+Result AlbumManager::IsAlbumMounted(AlbumStorage storage) {
+ if (storage > AlbumStorage::Sd) {
+ return ResultInvalidStorage;
+ }
+
+ is_mounted = true;
+
+ if (storage == AlbumStorage::Sd) {
+ FindScreenshots();
+ }
+
+ return is_mounted ? ResultSuccess : ResultIsNotMounted;
+}
+
+Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage,
+ u8 flags) const {
+ if (storage > AlbumStorage::Sd) {
+ return ResultInvalidStorage;
+ }
+
+ if (!is_mounted) {
+ return ResultIsNotMounted;
+ }
+
+ for (auto& [file_id, path] : album_files) {
+ if (file_id.storage != storage) {
+ continue;
+ }
+ if (out_entries.size() >= SdAlbumFileLimit) {
+ break;
+ }
+
+ const auto entry_size = Common::FS::GetSize(path);
+ out_entries.push_back({
+ .entry_size = entry_size,
+ .file_id = file_id,
+ });
+ }
+
+ return ResultSuccess;
+}
+
+Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
+ ContentType contex_type, AlbumFileDateTime start_date,
+ AlbumFileDateTime end_date, u64 aruid) const {
+ if (!is_mounted) {
+ return ResultIsNotMounted;
+ }
+
+ for (auto& [file_id, path] : album_files) {
+ if (file_id.type != contex_type) {
+ continue;
+ }
+
+ if (file_id.date > start_date) {
+ continue;
+ }
+
+ if (file_id.date < end_date) {
+ continue;
+ }
+
+ if (out_entries.size() >= SdAlbumFileLimit) {
+ break;
+ }
+
+ const auto entry_size = Common::FS::GetSize(path);
+ ApplicationAlbumFileEntry entry{.entry =
+ {
+ .size = entry_size,
+ .hash{},
+ .datetime = file_id.date,
+ .storage = file_id.storage,
+ .content = contex_type,
+ .unknown = 1,
+ },
+ .datetime = file_id.date,
+ .unknown = {}};
+ out_entries.push_back(entry);
+ }
+
+ return ResultSuccess;
+}
+
+Result AlbumManager::GetAutoSavingStorage(bool& out_is_autosaving) const {
+ out_is_autosaving = false;
+ return ResultSuccess;
+}
+
+Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
+ std::vector<u8>& out_image,
+ const AlbumFileId& file_id,
+ const ScreenShotDecodeOption& decoder_options) const {
+ if (file_id.storage > AlbumStorage::Sd) {
+ return ResultInvalidStorage;
+ }
+
+ if (!is_mounted) {
+ return ResultIsNotMounted;
+ }
+
+ out_image_output = {
+ .width = 1280,
+ .height = 720,
+ .attribute =
+ {
+ .unknown_0{},
+ .orientation = AlbumImageOrientation::None,
+ .unknown_1{},
+ .unknown_2{},
+ },
+ };
+
+ std::filesystem::path path;
+ const auto result = GetFile(path, file_id);
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha);
+
+ return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
+ +static_cast<int>(out_image_output.height), decoder_options.flags);
+}
+
+Result AlbumManager::LoadAlbumScreenShotThumbnail(
+ LoadAlbumScreenShotImageOutput& out_image_output, std::vector<u8>& out_image,
+ const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const {
+ if (file_id.storage > AlbumStorage::Sd) {
+ return ResultInvalidStorage;
+ }
+
+ if (!is_mounted) {
+ return ResultIsNotMounted;
+ }
+
+ out_image_output = {
+ .width = 320,
+ .height = 180,
+ .attribute =
+ {
+ .unknown_0{},
+ .orientation = AlbumImageOrientation::None,
+ .unknown_1{},
+ .unknown_2{},
+ },
+ };
+
+ std::filesystem::path path;
+ const auto result = GetFile(path, file_id);
+
+ if (result.IsError()) {
+ return result;
+ }
+
+ out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha);
+
+ return LoadImage(out_image, path, static_cast<int>(out_image_output.width),
+ +static_cast<int>(out_image_output.height), decoder_options.flags);
+}
+
+Result AlbumManager::GetFile(std::filesystem::path& out_path, const AlbumFileId& file_id) const {
+ const auto file = album_files.find(file_id);
+
+ if (file == album_files.end()) {
+ return ResultFileNotFound;
+ }
+
+ out_path = file->second;
+ return ResultSuccess;
+}
+
+void AlbumManager::FindScreenshots() {
+ is_mounted = false;
+ album_files.clear();
+
+ // TODO: Swap this with a blocking operation.
+ const auto screenshots_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir);
+ Common::FS::IterateDirEntries(
+ screenshots_dir,
+ [this](const std::filesystem::path& full_path) {
+ AlbumEntry entry;
+ if (GetAlbumEntry(entry, full_path).IsError()) {
+ return true;
+ }
+ while (album_files.contains(entry.file_id)) {
+ if (++entry.file_id.date.unique_id == 0) {
+ break;
+ }
+ }
+ album_files[entry.file_id] = full_path;
+ return true;
+ },
+ Common::FS::DirEntryFilter::File);
+
+ is_mounted = true;
+}
+
+Result AlbumManager::GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path) const {
+ std::istringstream line_stream(path.filename().string());
+ std::string date;
+ std::string application;
+ std::string time;
+
+ // Parse filename to obtain entry properties
+ std::getline(line_stream, application, '_');
+ std::getline(line_stream, date, '_');
+ std::getline(line_stream, time, '_');
+
+ std::istringstream date_stream(date);
+ std::istringstream time_stream(time);
+ std::string year;
+ std::string month;
+ std::string day;
+ std::string hour;
+ std::string minute;
+ std::string second;
+
+ std::getline(date_stream, year, '-');
+ std::getline(date_stream, month, '-');
+ std::getline(date_stream, day, '-');
+
+ std::getline(time_stream, hour, '-');
+ std::getline(time_stream, minute, '-');
+ std::getline(time_stream, second, '-');
+
+ try {
+ out_entry = {
+ .entry_size = 1,
+ .file_id{
+ .application_id = static_cast<u64>(std::stoll(application, 0, 16)),
+ .date =
+ {
+ .year = static_cast<u16>(std::stoi(year)),
+ .month = static_cast<u8>(std::stoi(month)),
+ .day = static_cast<u8>(std::stoi(day)),
+ .hour = static_cast<u8>(std::stoi(hour)),
+ .minute = static_cast<u8>(std::stoi(minute)),
+ .second = static_cast<u8>(std::stoi(second)),
+ .unique_id = 0,
+ },
+ .storage = AlbumStorage::Sd,
+ .type = ContentType::Screenshot,
+ .unknown = 1,
+ },
+ };
+ } catch (const std::invalid_argument&) {
+ return ResultUnknown;
+ } catch (const std::out_of_range&) {
+ return ResultUnknown;
+ } catch (const std::exception&) {
+ return ResultUnknown;
+ }
+
+ return ResultSuccess;
+}
+
+Result AlbumManager::LoadImage(std::span<u8> out_image, const std::filesystem::path& path,
+ int width, int height, ScreenShotDecoderFlag flag) const {
+ if (out_image.size() != static_cast<std::size_t>(width * height * STBI_rgb_alpha)) {
+ return ResultUnknown;
+ }
+
+ const Common::FS::IOFile db_file{path, Common::FS::FileAccessMode::Read,
+ Common::FS::FileType::BinaryFile};
+
+ std::vector<u8> raw_file(db_file.GetSize());
+ if (db_file.Read(raw_file) != raw_file.size()) {
+ return ResultUnknown;
+ }
+
+ int filter_flag = STBIR_FILTER_DEFAULT;
+ int original_width, original_height, color_channels;
+ const auto dbi_image =
+ stbi_load_from_memory(raw_file.data(), static_cast<int>(raw_file.size()), &original_width,
+ &original_height, &color_channels, STBI_rgb_alpha);
+
+ if (dbi_image == nullptr) {
+ return ResultUnknown;
+ }
+
+ switch (flag) {
+ case ScreenShotDecoderFlag::EnableFancyUpsampling:
+ filter_flag = STBIR_FILTER_TRIANGLE;
+ break;
+ case ScreenShotDecoderFlag::EnableBlockSmoothing:
+ filter_flag = STBIR_FILTER_BOX;
+ break;
+ default:
+ filter_flag = STBIR_FILTER_DEFAULT;
+ break;
+ }
+
+ stbir_resize_uint8_srgb(dbi_image, original_width, original_height, 0, out_image.data(), width,
+ height, 0, STBI_rgb_alpha, 3, filter_flag);
+
+ return ResultSuccess;
+}
+} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h
new file mode 100644
index 000000000..8337c655c
--- /dev/null
+++ b/src/core/hle/service/caps/caps_manager.h
@@ -0,0 +1,72 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <unordered_map>
+
+#include "common/fs/fs.h"
+#include "core/hle/result.h"
+#include "core/hle/service/caps/caps_types.h"
+
+namespace Core {
+class System;
+}
+
+namespace std {
+// Hash used to create lists from AlbumFileId data
+template <>
+struct hash<Service::Capture::AlbumFileId> {
+ size_t operator()(const Service::Capture::AlbumFileId& pad_id) const noexcept {
+ u64 hash_value = (static_cast<u64>(pad_id.date.year) << 8);
+ hash_value ^= (static_cast<u64>(pad_id.date.month) << 7);
+ hash_value ^= (static_cast<u64>(pad_id.date.day) << 6);
+ hash_value ^= (static_cast<u64>(pad_id.date.hour) << 5);
+ hash_value ^= (static_cast<u64>(pad_id.date.minute) << 4);
+ hash_value ^= (static_cast<u64>(pad_id.date.second) << 3);
+ hash_value ^= (static_cast<u64>(pad_id.date.unique_id) << 2);
+ hash_value ^= (static_cast<u64>(pad_id.storage) << 1);
+ hash_value ^= static_cast<u64>(pad_id.type);
+ return static_cast<size_t>(hash_value);
+ }
+};
+
+} // namespace std
+
+namespace Service::Capture {
+
+class AlbumManager {
+public:
+ explicit AlbumManager();
+ ~AlbumManager();
+
+ Result DeleteAlbumFile(const AlbumFileId& file_id);
+ Result IsAlbumMounted(AlbumStorage storage);
+ Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage,
+ u8 flags) const;
+ Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries,
+ ContentType contex_type, AlbumFileDateTime start_date,
+ AlbumFileDateTime end_date, u64 aruid) const;
+ Result GetAutoSavingStorage(bool& out_is_autosaving) const;
+ Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output,
+ std::vector<u8>& out_image, const AlbumFileId& file_id,
+ const ScreenShotDecodeOption& decoder_options) const;
+ Result LoadAlbumScreenShotThumbnail(LoadAlbumScreenShotImageOutput& out_image_output,
+ std::vector<u8>& out_image, const AlbumFileId& file_id,
+ const ScreenShotDecodeOption& decoder_options) const;
+
+private:
+ static constexpr std::size_t NandAlbumFileLimit = 1000;
+ static constexpr std::size_t SdAlbumFileLimit = 10000;
+
+ void FindScreenshots();
+ Result GetFile(std::filesystem::path& out_path, const AlbumFileId& file_id) const;
+ Result GetAlbumEntry(AlbumEntry& out_entry, const std::filesystem::path& path) const;
+ Result LoadImage(std::span<u8> out_image, const std::filesystem::path& path, int width,
+ int height, ScreenShotDecoderFlag flag) const;
+
+ bool is_mounted{};
+ std::unordered_map<AlbumFileId, std::filesystem::path> album_files;
+};
+
+} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_result.h b/src/core/hle/service/caps/caps_result.h
new file mode 100644
index 000000000..c65e5fb9a
--- /dev/null
+++ b/src/core/hle/service/caps/caps_result.h
@@ -0,0 +1,35 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "core/hle/result.h"
+
+namespace Service::Capture {
+
+constexpr Result ResultWorkMemoryError(ErrorModule::Capture, 3);
+constexpr Result ResultUnknown5(ErrorModule::Capture, 5);
+constexpr Result ResultUnknown6(ErrorModule::Capture, 6);
+constexpr Result ResultUnknown7(ErrorModule::Capture, 7);
+constexpr Result ResultOutOfRange(ErrorModule::Capture, 8);
+constexpr Result ResulInvalidTimestamp(ErrorModule::Capture, 12);
+constexpr Result ResultInvalidStorage(ErrorModule::Capture, 13);
+constexpr Result ResultInvalidFileContents(ErrorModule::Capture, 14);
+constexpr Result ResultIsNotMounted(ErrorModule::Capture, 21);
+constexpr Result ResultUnknown22(ErrorModule::Capture, 22);
+constexpr Result ResultFileNotFound(ErrorModule::Capture, 23);
+constexpr Result ResultInvalidFileData(ErrorModule::Capture, 24);
+constexpr Result ResultUnknown25(ErrorModule::Capture, 25);
+constexpr Result ResultReadBufferShortage(ErrorModule::Capture, 30);
+constexpr Result ResultUnknown810(ErrorModule::Capture, 810);
+constexpr Result ResultUnknown1024(ErrorModule::Capture, 1024);
+constexpr Result ResultUnknown1202(ErrorModule::Capture, 1202);
+constexpr Result ResultUnknown1203(ErrorModule::Capture, 1203);
+constexpr Result ResultFileCountLimit(ErrorModule::Capture, 1401);
+constexpr Result ResultUnknown1701(ErrorModule::Capture, 1701);
+constexpr Result ResultUnknown1801(ErrorModule::Capture, 1801);
+constexpr Result ResultUnknown1802(ErrorModule::Capture, 1802);
+constexpr Result ResultUnknown1803(ErrorModule::Capture, 1803);
+constexpr Result ResultUnknown1804(ErrorModule::Capture, 1804);
+
+} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_sc.cpp b/src/core/hle/service/caps/caps_sc.cpp
index 395b13da7..6117cb7c6 100644
--- a/src/core/hle/service/caps/caps_sc.cpp
+++ b/src/core/hle/service/caps/caps_sc.cpp
@@ -5,7 +5,8 @@
namespace Service::Capture {
-CAPS_SC::CAPS_SC(Core::System& system_) : ServiceFramework{system_, "caps:sc"} {
+IScreenShotControlService::IScreenShotControlService(Core::System& system_)
+ : ServiceFramework{system_, "caps:sc"} {
// clang-format off
static const FunctionInfo functions[] = {
{1, nullptr, "CaptureRawImage"},
@@ -34,6 +35,6 @@ CAPS_SC::CAPS_SC(Core::System& system_) : ServiceFramework{system_, "caps:sc"} {
RegisterHandlers(functions);
}
-CAPS_SC::~CAPS_SC() = default;
+IScreenShotControlService::~IScreenShotControlService() = default;
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_sc.h b/src/core/hle/service/caps/caps_sc.h
index e5600f6d7..d555f4979 100644
--- a/src/core/hle/service/caps/caps_sc.h
+++ b/src/core/hle/service/caps/caps_sc.h
@@ -11,10 +11,10 @@ class System;
namespace Service::Capture {
-class CAPS_SC final : public ServiceFramework<CAPS_SC> {
+class IScreenShotControlService final : public ServiceFramework<IScreenShotControlService> {
public:
- explicit CAPS_SC(Core::System& system_);
- ~CAPS_SC() override;
+ explicit IScreenShotControlService(Core::System& system_);
+ ~IScreenShotControlService() override;
};
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp
index 62b9edd41..d0d1b5425 100644
--- a/src/core/hle/service/caps/caps_ss.cpp
+++ b/src/core/hle/service/caps/caps_ss.cpp
@@ -5,7 +5,8 @@
namespace Service::Capture {
-CAPS_SS::CAPS_SS(Core::System& system_) : ServiceFramework{system_, "caps:ss"} {
+IScreenShotService::IScreenShotService(Core::System& system_)
+ : ServiceFramework{system_, "caps:ss"} {
// clang-format off
static const FunctionInfo functions[] = {
{201, nullptr, "SaveScreenShot"},
@@ -21,6 +22,6 @@ CAPS_SS::CAPS_SS(Core::System& system_) : ServiceFramework{system_, "caps:ss"} {
RegisterHandlers(functions);
}
-CAPS_SS::~CAPS_SS() = default;
+IScreenShotService::~IScreenShotService() = default;
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h
index 718ade485..381e44fd4 100644
--- a/src/core/hle/service/caps/caps_ss.h
+++ b/src/core/hle/service/caps/caps_ss.h
@@ -11,10 +11,10 @@ class System;
namespace Service::Capture {
-class CAPS_SS final : public ServiceFramework<CAPS_SS> {
+class IScreenShotService final : public ServiceFramework<IScreenShotService> {
public:
- explicit CAPS_SS(Core::System& system_);
- ~CAPS_SS() override;
+ explicit IScreenShotService(Core::System& system_);
+ ~IScreenShotService() override;
};
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp
index 3b11cc95c..cad173dc7 100644
--- a/src/core/hle/service/caps/caps_su.cpp
+++ b/src/core/hle/service/caps/caps_su.cpp
@@ -7,10 +7,11 @@
namespace Service::Capture {
-CAPS_SU::CAPS_SU(Core::System& system_) : ServiceFramework{system_, "caps:su"} {
+IScreenShotApplicationService::IScreenShotApplicationService(Core::System& system_)
+ : ServiceFramework{system_, "caps:su"} {
// clang-format off
static const FunctionInfo functions[] = {
- {32, &CAPS_SU::SetShimLibraryVersion, "SetShimLibraryVersion"},
+ {32, &IScreenShotApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"},
{201, nullptr, "SaveScreenShot"},
{203, nullptr, "SaveScreenShotEx0"},
{205, nullptr, "SaveScreenShotEx1"},
@@ -21,9 +22,9 @@ CAPS_SU::CAPS_SU(Core::System& system_) : ServiceFramework{system_, "caps:su"} {
RegisterHandlers(functions);
}
-CAPS_SU::~CAPS_SU() = default;
+IScreenShotApplicationService::~IScreenShotApplicationService() = default;
-void CAPS_SU::SetShimLibraryVersion(HLERequestContext& ctx) {
+void IScreenShotApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto library_version{rp.Pop<u64>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h
index c6398858d..647e3059d 100644
--- a/src/core/hle/service/caps/caps_su.h
+++ b/src/core/hle/service/caps/caps_su.h
@@ -11,10 +11,10 @@ class System;
namespace Service::Capture {
-class CAPS_SU final : public ServiceFramework<CAPS_SU> {
+class IScreenShotApplicationService final : public ServiceFramework<IScreenShotApplicationService> {
public:
- explicit CAPS_SU(Core::System& system_);
- ~CAPS_SU() override;
+ explicit IScreenShotApplicationService(Core::System& system_);
+ ~IScreenShotApplicationService() override;
private:
void SetShimLibraryVersion(HLERequestContext& ctx);
diff --git a/src/core/hle/service/caps/caps_types.h b/src/core/hle/service/caps/caps_types.h
new file mode 100644
index 000000000..bf6061273
--- /dev/null
+++ b/src/core/hle/service/caps/caps_types.h
@@ -0,0 +1,184 @@
+// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Service::Capture {
+
+// This is nn::album::ImageOrientation
+enum class AlbumImageOrientation {
+ None,
+ Rotate90,
+ Rotate180,
+ Rotate270,
+};
+
+// This is nn::album::AlbumReportOption
+enum class AlbumReportOption : s32 {
+ Disable,
+ Enable,
+};
+
+enum class ContentType : u8 {
+ Screenshot = 0,
+ Movie = 1,
+ ExtraMovie = 3,
+};
+
+enum class AlbumStorage : u8 {
+ Nand,
+ Sd,
+};
+
+enum class ScreenShotDecoderFlag : u64 {
+ None = 0,
+ EnableFancyUpsampling = 1 << 0,
+ EnableBlockSmoothing = 1 << 1,
+};
+
+// This is nn::capsrv::AlbumFileDateTime
+struct AlbumFileDateTime {
+ u16 year{};
+ u8 month{};
+ u8 day{};
+ u8 hour{};
+ u8 minute{};
+ u8 second{};
+ u8 unique_id{};
+
+ friend constexpr bool operator==(const AlbumFileDateTime&, const AlbumFileDateTime&) = default;
+ friend constexpr bool operator>(const AlbumFileDateTime& a, const AlbumFileDateTime& b) {
+ if (a.year > b.year) {
+ return true;
+ }
+ if (a.month > b.month) {
+ return true;
+ }
+ if (a.day > b.day) {
+ return true;
+ }
+ if (a.hour > b.hour) {
+ return true;
+ }
+ if (a.minute > b.minute) {
+ return true;
+ }
+ return a.second > b.second;
+ };
+ friend constexpr bool operator<(const AlbumFileDateTime& a, const AlbumFileDateTime& b) {
+ if (a.year < b.year) {
+ return true;
+ }
+ if (a.month < b.month) {
+ return true;
+ }
+ if (a.day < b.day) {
+ return true;
+ }
+ if (a.hour < b.hour) {
+ return true;
+ }
+ if (a.minute < b.minute) {
+ return true;
+ }
+ return a.second < b.second;
+ };
+};
+static_assert(sizeof(AlbumFileDateTime) == 0x8, "AlbumFileDateTime has incorrect size.");
+
+// This is nn::album::AlbumEntry
+struct AlbumFileEntry {
+ u64 size{}; // Size of the entry
+ u64 hash{}; // AES256 with hardcoded key over AlbumEntry
+ AlbumFileDateTime datetime{};
+ AlbumStorage storage{};
+ ContentType content{};
+ INSERT_PADDING_BYTES(5);
+ u8 unknown{}; // Set to 1 on official SW
+};
+static_assert(sizeof(AlbumFileEntry) == 0x20, "AlbumFileEntry has incorrect size.");
+
+struct AlbumFileId {
+ u64 application_id{};
+ AlbumFileDateTime date{};
+ AlbumStorage storage{};
+ ContentType type{};
+ INSERT_PADDING_BYTES(0x5);
+ u8 unknown{};
+
+ friend constexpr bool operator==(const AlbumFileId&, const AlbumFileId&) = default;
+};
+static_assert(sizeof(AlbumFileId) == 0x18, "AlbumFileId is an invalid size");
+
+// This is nn::capsrv::AlbumEntry
+struct AlbumEntry {
+ u64 entry_size{};
+ AlbumFileId file_id{};
+};
+static_assert(sizeof(AlbumEntry) == 0x20, "AlbumEntry has incorrect size.");
+
+// This is nn::capsrv::ApplicationAlbumEntry
+struct ApplicationAlbumEntry {
+ u64 size{}; // Size of the entry
+ u64 hash{}; // AES256 with hardcoded key over AlbumEntry
+ AlbumFileDateTime datetime{};
+ AlbumStorage storage{};
+ ContentType content{};
+ INSERT_PADDING_BYTES(5);
+ u8 unknown{1}; // Set to 1 on official SW
+};
+static_assert(sizeof(ApplicationAlbumEntry) == 0x20, "ApplicationAlbumEntry has incorrect size.");
+
+// This is nn::capsrv::ApplicationAlbumFileEntry
+struct ApplicationAlbumFileEntry {
+ ApplicationAlbumEntry entry{};
+ AlbumFileDateTime datetime{};
+ u64 unknown{};
+};
+static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30,
+ "ApplicationAlbumFileEntry has incorrect size.");
+
+struct ApplicationData {
+ std::array<u8, 0x400> data{};
+ u32 data_size{};
+};
+static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size");
+
+struct ScreenShotAttribute {
+ u32 unknown_0{};
+ AlbumImageOrientation orientation{};
+ u32 unknown_1{};
+ u32 unknown_2{};
+ INSERT_PADDING_BYTES(0x30);
+};
+static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size");
+
+struct ScreenShotDecodeOption {
+ ScreenShotDecoderFlag flags{};
+ INSERT_PADDING_BYTES(0x18);
+};
+static_assert(sizeof(ScreenShotDecodeOption) == 0x20, "ScreenShotDecodeOption is an invalid size");
+
+struct LoadAlbumScreenShotImageOutput {
+ s64 width{};
+ s64 height{};
+ ScreenShotAttribute attribute{};
+ INSERT_PADDING_BYTES(0x400);
+};
+static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450,
+ "LoadAlbumScreenShotImageOutput is an invalid size");
+
+struct LoadAlbumScreenShotImageOutputForApplication {
+ s64 width{};
+ s64 height{};
+ ScreenShotAttribute attribute{};
+ ApplicationData data{};
+ INSERT_PADDING_BYTES(0xAC);
+};
+static_assert(sizeof(LoadAlbumScreenShotImageOutputForApplication) == 0x500,
+ "LoadAlbumScreenShotImageOutput is an invalid size");
+
+} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp
index bffe0f8d0..260f25490 100644
--- a/src/core/hle/service/caps/caps_u.cpp
+++ b/src/core/hle/service/caps/caps_u.cpp
@@ -2,45 +2,29 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/logging/log.h"
-#include "core/hle/service/caps/caps.h"
+#include "core/hle/service/caps/caps_manager.h"
+#include "core/hle/service/caps/caps_types.h"
#include "core/hle/service/caps/caps_u.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::Capture {
-class IAlbumAccessorApplicationSession final
- : public ServiceFramework<IAlbumAccessorApplicationSession> {
-public:
- explicit IAlbumAccessorApplicationSession(Core::System& system_)
- : ServiceFramework{system_, "IAlbumAccessorApplicationSession"} {
- // clang-format off
- static const FunctionInfo functions[] = {
- {2001, nullptr, "OpenAlbumMovieReadStream"},
- {2002, nullptr, "CloseAlbumMovieReadStream"},
- {2003, nullptr, "GetAlbumMovieReadStreamMovieDataSize"},
- {2004, nullptr, "ReadMovieDataFromAlbumMovieReadStream"},
- {2005, nullptr, "GetAlbumMovieReadStreamBrokenReason"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
- }
-};
-
-CAPS_U::CAPS_U(Core::System& system_) : ServiceFramework{system_, "caps:u"} {
+IAlbumApplicationService::IAlbumApplicationService(Core::System& system_,
+ std::shared_ptr<AlbumManager> album_manager)
+ : ServiceFramework{system_, "caps:u"}, manager{album_manager} {
// clang-format off
static const FunctionInfo functions[] = {
- {32, &CAPS_U::SetShimLibraryVersion, "SetShimLibraryVersion"},
- {102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"},
- {103, nullptr, "DeleteAlbumContentsFileForApplication"},
- {104, nullptr, "GetAlbumContentsFileSizeForApplication"},
+ {32, &IAlbumApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"},
+ {102, &IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated, "GetAlbumFileList0AafeAruidDeprecated"},
+ {103, nullptr, "DeleteAlbumFileByAruid"},
+ {104, nullptr, "GetAlbumFileSizeByAruid"},
{105, nullptr, "DeleteAlbumFileByAruidForDebug"},
- {110, nullptr, "LoadAlbumContentsFileScreenShotImageForApplication"},
- {120, nullptr, "LoadAlbumContentsFileThumbnailImageForApplication"},
- {130, nullptr, "PrecheckToCreateContentsForApplication"},
+ {110, nullptr, "LoadAlbumScreenShotImageByAruid"},
+ {120, nullptr, "LoadAlbumScreenShotThumbnailImageByAruid"},
+ {130, nullptr, "PrecheckToCreateContentsByAruid"},
{140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
{141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
- {142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"},
+ {142, &IAlbumApplicationService::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"},
{143, nullptr, "GetAlbumFileList4AaeUidAruid"},
{144, nullptr, "GetAllAlbumFileList3AaeAruid"},
{60002, nullptr, "OpenAccessorSessionForApplication"},
@@ -50,9 +34,9 @@ CAPS_U::CAPS_U(Core::System& system_) : ServiceFramework{system_, "caps:u"} {
RegisterHandlers(functions);
}
-CAPS_U::~CAPS_U() = default;
+IAlbumApplicationService::~IAlbumApplicationService() = default;
-void CAPS_U::SetShimLibraryVersion(HLERequestContext& ctx) {
+void IAlbumApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto library_version{rp.Pop<u64>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
@@ -64,10 +48,7 @@ void CAPS_U::SetShimLibraryVersion(HLERequestContext& ctx) {
rb.Push(ResultSuccess);
}
-void CAPS_U::GetAlbumContentsFileListForApplication(HLERequestContext& ctx) {
- // Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an
- // u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total
- // output entries (which is copied to a s32 by official SW).
+void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto pid{rp.Pop<s32>()};
const auto content_type{rp.PopEnum<ContentType>()};
@@ -75,26 +56,49 @@ void CAPS_U::GetAlbumContentsFileListForApplication(HLERequestContext& ctx) {
const auto end_posix_time{rp.Pop<s64>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- // TODO: Update this when we implement the album.
- // Currently we do not have a method of accessing album entries, set this to 0 for now.
- constexpr u32 total_entries_1{};
- constexpr u32 total_entries_2{};
+ LOG_WARNING(Service_Capture,
+ "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
+ "end_posix_time={}, applet_resource_user_id={}",
+ pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id);
+
+ // TODO: Translate posix to DateTime
+
+ std::vector<ApplicationAlbumFileEntry> entries;
+ const Result result =
+ manager->GetAlbumFileList(entries, content_type, {}, {}, applet_resource_user_id);
- LOG_WARNING(
- Service_Capture,
- "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
- "end_posix_time={}, applet_resource_user_id={}, total_entries_1={}, total_entries_2={}",
- pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
- total_entries_1, total_entries_2);
+ if (!entries.empty()) {
+ ctx.WriteBuffer(entries);
+ }
IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(ResultSuccess);
- rb.Push(total_entries_1);
- rb.Push(total_entries_2);
+ rb.Push(result);
+ rb.Push<u64>(entries.size());
}
-void CAPS_U::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) {
- GetAlbumContentsFileListForApplication(ctx);
+void IAlbumApplicationService::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto pid{rp.Pop<s32>()};
+ const auto content_type{rp.PopEnum<ContentType>()};
+ const auto start_date_time{rp.PopRaw<AlbumFileDateTime>()};
+ const auto end_date_time{rp.PopRaw<AlbumFileDateTime>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_Capture,
+ "(STUBBED) called. pid={}, content_type={}, applet_resource_user_id={}", pid,
+ content_type, applet_resource_user_id);
+
+ std::vector<ApplicationAlbumFileEntry> entries;
+ const Result result = manager->GetAlbumFileList(entries, content_type, start_date_time,
+ end_date_time, applet_resource_user_id);
+
+ if (!entries.empty()) {
+ ctx.WriteBuffer(entries);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(result);
+ rb.Push<u64>(entries.size());
}
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h
index e8dd037d7..9458c128e 100644
--- a/src/core/hle/service/caps/caps_u.h
+++ b/src/core/hle/service/caps/caps_u.h
@@ -10,16 +10,20 @@ class System;
}
namespace Service::Capture {
+class AlbumManager;
-class CAPS_U final : public ServiceFramework<CAPS_U> {
+class IAlbumApplicationService final : public ServiceFramework<IAlbumApplicationService> {
public:
- explicit CAPS_U(Core::System& system_);
- ~CAPS_U() override;
+ explicit IAlbumApplicationService(Core::System& system_,
+ std::shared_ptr<AlbumManager> album_manager);
+ ~IAlbumApplicationService() override;
private:
void SetShimLibraryVersion(HLERequestContext& ctx);
- void GetAlbumContentsFileListForApplication(HLERequestContext& ctx);
+ void GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx);
void GetAlbumFileList3AaeAruid(HLERequestContext& ctx);
+
+ std::shared_ptr<AlbumManager> manager = nullptr;
};
} // namespace Service::Capture
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index c2054e8a0..126cd6ffd 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -855,6 +855,9 @@ FSP_SRV::FSP_SRV(Core::System& system_)
if (Settings::values.enable_fs_access_log) {
access_log_mode = AccessLogMode::SdCard;
}
+
+ // This should be true on creation
+ fsc.SetAutoSaveDataCreation(true);
}
FSP_SRV::~FSP_SRV() = default;
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 9d149a7cd..7927f8264 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -23,19 +23,39 @@ public:
explicit IMonitorService(Core::System& system_) : ServiceFramework{system_, "IMonitorService"} {
// clang-format off
static const FunctionInfo functions[] = {
- {0, nullptr, "GetStateForMonitor"},
+ {0, &IMonitorService::GetStateForMonitor, "GetStateForMonitor"},
{1, nullptr, "GetNetworkInfoForMonitor"},
{2, nullptr, "GetIpv4AddressForMonitor"},
{3, nullptr, "GetDisconnectReasonForMonitor"},
{4, nullptr, "GetSecurityParameterForMonitor"},
{5, nullptr, "GetNetworkConfigForMonitor"},
- {100, nullptr, "InitializeMonitor"},
+ {100, &IMonitorService::InitializeMonitor, "InitializeMonitor"},
{101, nullptr, "FinalizeMonitor"},
};
// clang-format on
RegisterHandlers(functions);
}
+
+private:
+ void GetStateForMonitor(HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.PushEnum(state);
+ }
+
+ void InitializeMonitor(HLERequestContext& ctx) {
+ LOG_INFO(Service_LDN, "called");
+
+ state = State::Initialized;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ State state{State::None};
};
class LDNM final : public ServiceFramework<LDNM> {
@@ -731,14 +751,81 @@ public:
}
};
+class ISfMonitorService final : public ServiceFramework<ISfMonitorService> {
+public:
+ explicit ISfMonitorService(Core::System& system_)
+ : ServiceFramework{system_, "ISfMonitorService"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &ISfMonitorService::Initialize, "Initialize"},
+ {288, &ISfMonitorService::GetGroupInfo, "GetGroupInfo"},
+ {320, nullptr, "GetLinkLevel"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void Initialize(HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(0);
+ }
+
+ void GetGroupInfo(HLERequestContext& ctx) {
+ LOG_WARNING(Service_LDN, "(STUBBED) called");
+
+ struct GroupInfo {
+ std::array<u8, 0x200> info;
+ };
+
+ GroupInfo group_info{};
+
+ ctx.WriteBuffer(group_info);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+};
+
+class LP2PM final : public ServiceFramework<LP2PM> {
+public:
+ explicit LP2PM(Core::System& system_) : ServiceFramework{system_, "lp2p:m"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &LP2PM::CreateMonitorService, "CreateMonitorService"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void CreateMonitorService(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 reserved_input = rp.Pop<u64>();
+
+ LOG_INFO(Service_LDN, "called, reserved_input={}", reserved_input);
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(ResultSuccess);
+ rb.PushIpcInterface<ISfMonitorService>(system);
+ }
+};
+
void LoopProcess(Core::System& system) {
auto server_manager = std::make_unique<ServerManager>(system);
server_manager->RegisterNamedService("ldn:m", std::make_shared<LDNM>(system));
server_manager->RegisterNamedService("ldn:s", std::make_shared<LDNS>(system));
server_manager->RegisterNamedService("ldn:u", std::make_shared<LDNU>(system));
+
server_manager->RegisterNamedService("lp2p:app", std::make_shared<LP2PAPP>(system));
server_manager->RegisterNamedService("lp2p:sys", std::make_shared<LP2PSYS>(system));
+ server_manager->RegisterNamedService("lp2p:m", std::make_shared<LP2PM>(system));
+
ServerManager::RunServer(std::move(server_manager));
}
diff --git a/src/core/hle/service/nfc/common/device.cpp b/src/core/hle/service/nfc/common/device.cpp
index 68c407f81..e7a00deb3 100644
--- a/src/core/hle/service/nfc/common/device.cpp
+++ b/src/core/hle/service/nfc/common/device.cpp
@@ -830,11 +830,6 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe
return ResultWrongDeviceState;
}
- Service::Mii::StoreData store_data{};
- Service::Mii::NfpStoreDataExtension extension{};
- store_data.BuildBase(Mii::Gender::Male);
- extension.SetFromStoreData(store_data);
-
auto& settings = tag_data.settings;
if (tag_data.settings.settings.amiibo_initialized == 0) {
@@ -843,8 +838,8 @@ Result NfcDevice::SetRegisterInfoPrivate(const NFP::RegisterInfoPrivate& registe
}
SetAmiiboName(settings, register_info.amiibo_name);
- tag_data.owner_mii.BuildFromStoreData(store_data);
- tag_data.mii_extension = extension;
+ tag_data.owner_mii.BuildFromStoreData(register_info.mii_store_data);
+ tag_data.mii_extension.SetFromStoreData(register_info.mii_store_data);
tag_data.unknown = 0;
tag_data.unknown2 = {};
settings.country_code_id = 0;
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 21b06d10b..22dc55a6d 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -545,6 +545,16 @@ void IGeneralService::IsAnyInternetRequestAccepted(HLERequestContext& ctx) {
}
}
+void IGeneralService::IsAnyForegroundRequestAccepted(HLERequestContext& ctx) {
+ const bool is_accepted{};
+
+ LOG_WARNING(Service_NIFM, "(STUBBED) called, is_accepted={}", is_accepted);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push<u8>(is_accepted);
+}
+
IGeneralService::IGeneralService(Core::System& system_)
: ServiceFramework{system_, "IGeneralService"}, network{system_.GetRoomNetwork()} {
// clang-format off
@@ -569,7 +579,7 @@ IGeneralService::IGeneralService(Core::System& system_)
{19, nullptr, "SetEthernetCommunicationEnabled"},
{20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"},
{21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"},
- {22, nullptr, "IsAnyForegroundRequestAccepted"},
+ {22, &IGeneralService::IsAnyForegroundRequestAccepted, "IsAnyForegroundRequestAccepted"},
{23, nullptr, "PutToSleep"},
{24, nullptr, "WakeUp"},
{25, nullptr, "GetSsidListVersion"},
diff --git a/src/core/hle/service/nifm/nifm.h b/src/core/hle/service/nifm/nifm.h
index ae99c4695..b74b66438 100644
--- a/src/core/hle/service/nifm/nifm.h
+++ b/src/core/hle/service/nifm/nifm.h
@@ -35,6 +35,7 @@ private:
void GetInternetConnectionStatus(HLERequestContext& ctx);
void IsEthernetCommunicationEnabled(HLERequestContext& ctx);
void IsAnyInternetRequestAccepted(HLERequestContext& ctx);
+ void IsAnyForegroundRequestAccepted(HLERequestContext& ctx);
Network::RoomNetwork& network;
};
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 6e0baf0be..f9e0e272d 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -7,6 +7,7 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/vfs.h"
+#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/glue/glue_manager.h"
#include "core/hle/service/ipc_helpers.h"
#include "core/hle/service/ns/errors.h"
@@ -502,8 +503,8 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_)
static const FunctionInfo functions[] = {
{11, nullptr, "CalculateApplicationOccupiedSize"},
{43, nullptr, "CheckSdCardMountStatus"},
- {47, nullptr, "GetTotalSpaceSize"},
- {48, nullptr, "GetFreeSpaceSize"},
+ {47, &IContentManagementInterface::GetTotalSpaceSize, "GetTotalSpaceSize"},
+ {48, &IContentManagementInterface::GetFreeSpaceSize, "GetFreeSpaceSize"},
{600, nullptr, "CountApplicationContentMeta"},
{601, nullptr, "ListApplicationContentMetaStatus"},
{605, nullptr, "ListApplicationContentMetaStatusWithRightsCheck"},
@@ -516,6 +517,28 @@ IContentManagementInterface::IContentManagementInterface(Core::System& system_)
IContentManagementInterface::~IContentManagementInterface() = default;
+void IContentManagementInterface::GetTotalSpaceSize(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto storage{rp.PopEnum<FileSys::StorageId>()};
+
+ LOG_INFO(Service_Capture, "called, storage={}", storage);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push<u64>(system.GetFileSystemController().GetTotalSpaceSize(storage));
+}
+
+void IContentManagementInterface::GetFreeSpaceSize(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto storage{rp.PopEnum<FileSys::StorageId>()};
+
+ LOG_INFO(Service_Capture, "called, storage={}", storage);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(ResultSuccess);
+ rb.Push<u64>(system.GetFileSystemController().GetFreeSpaceSize(storage));
+}
+
IDocumentInterface::IDocumentInterface(Core::System& system_)
: ServiceFramework{system_, "IDocumentInterface"} {
// clang-format off
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 175dad780..34d2a45dc 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -48,6 +48,10 @@ class IContentManagementInterface final : public ServiceFramework<IContentManage
public:
explicit IContentManagementInterface(Core::System& system_);
~IContentManagementInterface() override;
+
+private:
+ void GetTotalSpaceSize(HLERequestContext& ctx);
+ void GetFreeSpaceSize(HLERequestContext& ctx);
};
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h
index 40c65b430..4c0cc71cd 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.h
+++ b/src/core/hle/service/nvdrv/devices/nvmap.h
@@ -45,13 +45,6 @@ public:
IsSharedMemMapped = 6
};
-private:
- /// Id to use for the next handle that is created.
- u32 next_handle = 0;
-
- /// Id to use for the next object that is created.
- u32 next_id = 0;
-
struct IocCreateParams {
// Input
u32_le size{};
@@ -113,6 +106,13 @@ private:
NvResult IocParam(std::span<const u8> input, std::span<u8> output);
NvResult IocFree(std::span<const u8> input, std::span<u8> output);
+private:
+ /// Id to use for the next handle that is created.
+ u32 next_handle = 0;
+
+ /// Id to use for the next object that is created.
+ u32 next_id = 0;
+
NvCore::Container& container;
NvCore::NvMap& file;
};
diff --git a/src/core/hle/service/nvnflinger/buffer_item.h b/src/core/hle/service/nvnflinger/buffer_item.h
index 7fd808f54..3da8cc3aa 100644
--- a/src/core/hle/service/nvnflinger/buffer_item.h
+++ b/src/core/hle/service/nvnflinger/buffer_item.h
@@ -15,7 +15,7 @@
namespace Service::android {
-class GraphicBuffer;
+struct GraphicBuffer;
class BufferItem final {
public:
diff --git a/src/core/hle/service/nvnflinger/buffer_slot.h b/src/core/hle/service/nvnflinger/buffer_slot.h
index d25bca049..d8c9dec3b 100644
--- a/src/core/hle/service/nvnflinger/buffer_slot.h
+++ b/src/core/hle/service/nvnflinger/buffer_slot.h
@@ -13,7 +13,7 @@
namespace Service::android {
-class GraphicBuffer;
+struct GraphicBuffer;
enum class BufferState : u32 {
Free = 0,
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
new file mode 100644
index 000000000..469a53244
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.cpp
@@ -0,0 +1,351 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <random>
+
+#include "core/core.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/k_system_resource.h"
+#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "core/hle/service/nvdrv/nvdrv.h"
+#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
+#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
+#include "core/hle/service/nvnflinger/pixel_format.h"
+#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
+#include "core/hle/service/vi/layer/vi_layer.h"
+#include "core/hle/service/vi/vi_results.h"
+
+namespace Service::Nvnflinger {
+
+namespace {
+
+Result AllocateIoForProcessAddressSpace(Common::ProcessAddress* out_map_address,
+ std::unique_ptr<Kernel::KPageGroup>* out_page_group,
+ Core::System& system, u32 size) {
+ using Core::Memory::YUZU_PAGESIZE;
+
+ // Allocate memory for the system shared buffer.
+ // FIXME: Because the gmmu can only point to cpu addresses, we need
+ // to map this in the application space to allow it to be used.
+ // FIXME: Add proper smmu emulation.
+ // FIXME: This memory belongs to vi's .data section.
+ auto& kernel = system.Kernel();
+ auto* process = system.ApplicationProcess();
+ auto& page_table = process->GetPageTable();
+
+ // Hold a temporary page group reference while we try to map it.
+ auto pg = std::make_unique<Kernel::KPageGroup>(
+ kernel, std::addressof(kernel.GetSystemSystemResource().GetBlockInfoManager()));
+
+ // Allocate memory from secure pool.
+ R_TRY(kernel.MemoryManager().AllocateAndOpen(
+ pg.get(), size / YUZU_PAGESIZE,
+ Kernel::KMemoryManager::EncodeOption(Kernel::KMemoryManager::Pool::Secure,
+ Kernel::KMemoryManager::Direction::FromBack)));
+
+ // Get bounds of where mapping is possible.
+ const VAddr alias_code_begin = GetInteger(page_table.GetAliasCodeRegionStart());
+ const VAddr alias_code_size = page_table.GetAliasCodeRegionSize() / YUZU_PAGESIZE;
+ const auto state = Kernel::KMemoryState::Io;
+ const auto perm = Kernel::KMemoryPermission::UserReadWrite;
+ std::mt19937_64 rng{process->GetRandomEntropy(0)};
+
+ // Retry up to 64 times to map into alias code range.
+ Result res = ResultSuccess;
+ int i;
+ for (i = 0; i < 64; i++) {
+ *out_map_address = alias_code_begin + ((rng() % alias_code_size) * YUZU_PAGESIZE);
+ res = page_table.MapPageGroup(*out_map_address, *pg, state, perm);
+ if (R_SUCCEEDED(res)) {
+ break;
+ }
+ }
+
+ // Return failure, if necessary
+ R_UNLESS(i < 64, res);
+
+ // Return the mapped page group.
+ *out_page_group = std::move(pg);
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+template <typename T>
+std::span<u8> SerializeIoc(T& params) {
+ return std::span(reinterpret_cast<u8*>(std::addressof(params)), sizeof(T));
+}
+
+Result CreateNvMapHandle(u32* out_nv_map_handle, Nvidia::Devices::nvmap& nvmap, u32 size) {
+ // Create a handle.
+ Nvidia::Devices::nvmap::IocCreateParams create_in_params{
+ .size = size,
+ .handle = 0,
+ };
+ Nvidia::Devices::nvmap::IocCreateParams create_out_params{};
+ R_UNLESS(nvmap.IocCreate(SerializeIoc(create_in_params), SerializeIoc(create_out_params)) ==
+ Nvidia::NvResult::Success,
+ VI::ResultOperationFailed);
+
+ // Assign the output handle.
+ *out_nv_map_handle = create_out_params.handle;
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result FreeNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle) {
+ // Free the handle.
+ Nvidia::Devices::nvmap::IocFreeParams free_in_params{
+ .handle = handle,
+ };
+ Nvidia::Devices::nvmap::IocFreeParams free_out_params{};
+ R_UNLESS(nvmap.IocFree(SerializeIoc(free_in_params), SerializeIoc(free_out_params)) ==
+ Nvidia::NvResult::Success,
+ VI::ResultOperationFailed);
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result AllocNvMapHandle(Nvidia::Devices::nvmap& nvmap, u32 handle, Common::ProcessAddress buffer,
+ u32 size) {
+ // Assign the allocated memory to the handle.
+ Nvidia::Devices::nvmap::IocAllocParams alloc_in_params{
+ .handle = handle,
+ .heap_mask = 0,
+ .flags = {},
+ .align = 0,
+ .kind = 0,
+ .address = GetInteger(buffer),
+ };
+ Nvidia::Devices::nvmap::IocAllocParams alloc_out_params{};
+ R_UNLESS(nvmap.IocAlloc(SerializeIoc(alloc_in_params), SerializeIoc(alloc_out_params)) ==
+ Nvidia::NvResult::Success,
+ VI::ResultOperationFailed);
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result AllocateHandleForBuffer(u32* out_handle, Nvidia::Module& nvdrv,
+ Common::ProcessAddress buffer, u32 size) {
+ // Get the nvmap device.
+ auto nvmap_fd = nvdrv.Open("/dev/nvmap");
+ auto nvmap = nvdrv.GetDevice<Nvidia::Devices::nvmap>(nvmap_fd);
+ ASSERT(nvmap != nullptr);
+
+ // Create a handle.
+ R_TRY(CreateNvMapHandle(out_handle, *nvmap, size));
+
+ // Ensure we maintain a clean state on failure.
+ ON_RESULT_FAILURE {
+ ASSERT(R_SUCCEEDED(FreeNvMapHandle(*nvmap, *out_handle)));
+ };
+
+ // Assign the allocated memory to the handle.
+ R_RETURN(AllocNvMapHandle(*nvmap, *out_handle, buffer, size));
+}
+
+constexpr auto SharedBufferBlockLinearFormat = android::PixelFormat::Rgba8888;
+constexpr u32 SharedBufferBlockLinearBpp = 4;
+
+constexpr u32 SharedBufferBlockLinearWidth = 1280;
+constexpr u32 SharedBufferBlockLinearHeight = 768;
+constexpr u32 SharedBufferBlockLinearStride =
+ SharedBufferBlockLinearWidth * SharedBufferBlockLinearBpp;
+constexpr u32 SharedBufferNumSlots = 7;
+
+constexpr u32 SharedBufferWidth = 1280;
+constexpr u32 SharedBufferHeight = 720;
+constexpr u32 SharedBufferAsync = false;
+
+constexpr u32 SharedBufferSlotSize =
+ SharedBufferBlockLinearWidth * SharedBufferBlockLinearHeight * SharedBufferBlockLinearBpp;
+constexpr u32 SharedBufferSize = SharedBufferSlotSize * SharedBufferNumSlots;
+
+constexpr SharedMemoryPoolLayout SharedBufferPoolLayout = [] {
+ SharedMemoryPoolLayout layout{};
+ layout.num_slots = SharedBufferNumSlots;
+
+ for (u32 i = 0; i < SharedBufferNumSlots; i++) {
+ layout.slots[i].buffer_offset = i * SharedBufferSlotSize;
+ layout.slots[i].size = SharedBufferSlotSize;
+ layout.slots[i].width = SharedBufferWidth;
+ layout.slots[i].height = SharedBufferHeight;
+ }
+
+ return layout;
+}();
+
+void MakeGraphicBuffer(android::BufferQueueProducer& producer, u32 slot, u32 handle) {
+ auto buffer = std::make_shared<android::GraphicBuffer>();
+ buffer->width = SharedBufferWidth;
+ buffer->height = SharedBufferHeight;
+ buffer->stride = SharedBufferBlockLinearStride;
+ buffer->format = SharedBufferBlockLinearFormat;
+ buffer->buffer_id = handle;
+ buffer->offset = slot * SharedBufferSlotSize;
+ ASSERT(producer.SetPreallocatedBuffer(slot, buffer) == android::Status::NoError);
+}
+
+} // namespace
+
+FbShareBufferManager::FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
+ std::shared_ptr<Nvidia::Module> nvdrv)
+ : m_system(system), m_flinger(flinger), m_nvdrv(std::move(nvdrv)) {}
+
+FbShareBufferManager::~FbShareBufferManager() = default;
+
+Result FbShareBufferManager::Initialize(u64* out_buffer_id, u64* out_layer_id, u64 display_id) {
+ std::scoped_lock lk{m_guard};
+
+ // Ensure we have not already created a buffer.
+ R_UNLESS(m_buffer_id == 0, VI::ResultOperationFailed);
+
+ // Allocate memory and space for the shared buffer.
+ Common::ProcessAddress map_address;
+ R_TRY(AllocateIoForProcessAddressSpace(std::addressof(map_address),
+ std::addressof(m_buffer_page_group), m_system,
+ SharedBufferSize));
+
+ // Create an nvmap handle for the buffer and assign the memory to it.
+ R_TRY(AllocateHandleForBuffer(std::addressof(m_buffer_nvmap_handle), *m_nvdrv, map_address,
+ SharedBufferSize));
+
+ // Record the display id.
+ m_display_id = display_id;
+
+ // Create a layer for the display.
+ m_layer_id = m_flinger.CreateLayer(m_display_id).value();
+
+ // Set up the buffer.
+ m_buffer_id = m_next_buffer_id++;
+
+ // Get the layer.
+ VI::Layer* layer = m_flinger.FindLayer(m_display_id, m_layer_id);
+ ASSERT(layer != nullptr);
+
+ // Get the producer and set preallocated buffers.
+ auto& producer = layer->GetBufferQueue();
+ MakeGraphicBuffer(producer, 0, m_buffer_nvmap_handle);
+ MakeGraphicBuffer(producer, 1, m_buffer_nvmap_handle);
+
+ // Assign outputs.
+ *out_buffer_id = m_buffer_id;
+ *out_layer_id = m_layer_id;
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result FbShareBufferManager::GetSharedBufferMemoryHandleId(u64* out_buffer_size,
+ s32* out_nvmap_handle,
+ SharedMemoryPoolLayout* out_pool_layout,
+ u64 buffer_id,
+ u64 applet_resource_user_id) {
+ std::scoped_lock lk{m_guard};
+
+ R_UNLESS(m_buffer_id > 0, VI::ResultNotFound);
+ R_UNLESS(buffer_id == m_buffer_id, VI::ResultNotFound);
+
+ *out_pool_layout = SharedBufferPoolLayout;
+ *out_buffer_size = SharedBufferSize;
+ *out_nvmap_handle = m_buffer_nvmap_handle;
+
+ R_SUCCEED();
+}
+
+Result FbShareBufferManager::GetLayerFromId(VI::Layer** out_layer, u64 layer_id) {
+ // Ensure the layer id is valid.
+ R_UNLESS(m_layer_id > 0 && layer_id == m_layer_id, VI::ResultNotFound);
+
+ // Get the layer.
+ VI::Layer* layer = m_flinger.FindLayer(m_display_id, layer_id);
+ R_UNLESS(layer != nullptr, VI::ResultNotFound);
+
+ // We succeeded.
+ *out_layer = layer;
+ R_SUCCEED();
+}
+
+Result FbShareBufferManager::AcquireSharedFrameBuffer(android::Fence* out_fence,
+ std::array<s32, 4>& out_slot_indexes,
+ s64* out_target_slot, u64 layer_id) {
+ std::scoped_lock lk{m_guard};
+
+ // Get the layer.
+ VI::Layer* layer;
+ R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
+
+ // Get the producer.
+ auto& producer = layer->GetBufferQueue();
+
+ // Get the next buffer from the producer.
+ s32 slot;
+ R_UNLESS(producer.DequeueBuffer(std::addressof(slot), out_fence, SharedBufferAsync != 0,
+ SharedBufferWidth, SharedBufferHeight,
+ SharedBufferBlockLinearFormat, 0) == android::Status::NoError,
+ VI::ResultOperationFailed);
+
+ // Assign remaining outputs.
+ *out_target_slot = slot;
+ out_slot_indexes = {0, 1, -1, -1};
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result FbShareBufferManager::PresentSharedFrameBuffer(android::Fence fence,
+ Common::Rectangle<s32> crop_region,
+ u32 transform, s32 swap_interval,
+ u64 layer_id, s64 slot) {
+ std::scoped_lock lk{m_guard};
+
+ // Get the layer.
+ VI::Layer* layer;
+ R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
+
+ // Get the producer.
+ auto& producer = layer->GetBufferQueue();
+
+ // Request to queue the buffer.
+ std::shared_ptr<android::GraphicBuffer> buffer;
+ R_UNLESS(producer.RequestBuffer(static_cast<s32>(slot), std::addressof(buffer)) ==
+ android::Status::NoError,
+ VI::ResultOperationFailed);
+
+ // Queue the buffer to the producer.
+ android::QueueBufferInput input{};
+ android::QueueBufferOutput output{};
+ input.crop = crop_region;
+ input.fence = fence;
+ input.transform = static_cast<android::NativeWindowTransform>(transform);
+ input.swap_interval = swap_interval;
+ R_UNLESS(producer.QueueBuffer(static_cast<s32>(slot), input, std::addressof(output)) ==
+ android::Status::NoError,
+ VI::ResultOperationFailed);
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+Result FbShareBufferManager::GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event,
+ u64 layer_id) {
+ std::scoped_lock lk{m_guard};
+
+ // Get the layer.
+ VI::Layer* layer;
+ R_TRY(this->GetLayerFromId(std::addressof(layer), layer_id));
+
+ // Get the producer.
+ auto& producer = layer->GetBufferQueue();
+
+ // Set the event.
+ *out_event = std::addressof(producer.GetNativeHandle());
+
+ // We succeeded.
+ R_SUCCEED();
+}
+
+} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h
new file mode 100644
index 000000000..c809c01b4
--- /dev/null
+++ b/src/core/hle/service/nvnflinger/fb_share_buffer_manager.h
@@ -0,0 +1,65 @@
+// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "common/math_util.h"
+#include "core/hle/service/nvnflinger/nvnflinger.h"
+#include "core/hle/service/nvnflinger/ui/fence.h"
+
+namespace Kernel {
+class KPageGroup;
+}
+
+namespace Service::Nvnflinger {
+
+struct SharedMemorySlot {
+ u64 buffer_offset;
+ u64 size;
+ s32 width;
+ s32 height;
+};
+static_assert(sizeof(SharedMemorySlot) == 0x18, "SharedMemorySlot has wrong size");
+
+struct SharedMemoryPoolLayout {
+ s32 num_slots;
+ std::array<SharedMemorySlot, 0x10> slots;
+};
+static_assert(sizeof(SharedMemoryPoolLayout) == 0x188, "SharedMemoryPoolLayout has wrong size");
+
+class FbShareBufferManager final {
+public:
+ explicit FbShareBufferManager(Core::System& system, Nvnflinger& flinger,
+ std::shared_ptr<Nvidia::Module> nvdrv);
+ ~FbShareBufferManager();
+
+ Result Initialize(u64* out_buffer_id, u64* out_layer_handle, u64 display_id);
+ Result GetSharedBufferMemoryHandleId(u64* out_buffer_size, s32* out_nvmap_handle,
+ SharedMemoryPoolLayout* out_pool_layout, u64 buffer_id,
+ u64 applet_resource_user_id);
+ Result AcquireSharedFrameBuffer(android::Fence* out_fence, std::array<s32, 4>& out_slots,
+ s64* out_target_slot, u64 layer_id);
+ Result PresentSharedFrameBuffer(android::Fence fence, Common::Rectangle<s32> crop_region,
+ u32 transform, s32 swap_interval, u64 layer_id, s64 slot);
+ Result GetSharedFrameBufferAcquirableEvent(Kernel::KReadableEvent** out_event, u64 layer_id);
+
+private:
+ Result GetLayerFromId(VI::Layer** out_layer, u64 layer_id);
+
+private:
+ u64 m_next_buffer_id = 1;
+ u64 m_display_id = 0;
+ u64 m_buffer_id = 0;
+ u64 m_layer_id = 0;
+ u32 m_buffer_nvmap_handle = 0;
+ SharedMemoryPoolLayout m_pool_layout = {};
+
+ std::unique_ptr<Kernel::KPageGroup> m_buffer_page_group;
+
+ std::mutex m_guard;
+ Core::System& m_system;
+ Nvnflinger& m_flinger;
+ std::shared_ptr<Nvidia::Module> m_nvdrv;
+};
+
+} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/graphic_buffer_producer.h b/src/core/hle/service/nvnflinger/graphic_buffer_producer.h
index 21d7b31f3..5d7cff7d3 100644
--- a/src/core/hle/service/nvnflinger/graphic_buffer_producer.h
+++ b/src/core/hle/service/nvnflinger/graphic_buffer_producer.h
@@ -19,6 +19,7 @@ class InputParcel;
#pragma pack(push, 1)
struct QueueBufferInput final {
explicit QueueBufferInput(InputParcel& parcel);
+ explicit QueueBufferInput() = default;
void Deflate(s64* timestamp_, bool* is_auto_timestamp_, Common::Rectangle<s32>* crop_,
NativeWindowScalingMode* scaling_mode_, NativeWindowTransform* transform_,
@@ -34,7 +35,6 @@ struct QueueBufferInput final {
*fence_ = fence;
}
-private:
s64 timestamp{};
s32 is_auto_timestamp{};
Common::Rectangle<s32> crop{};
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp
index 21f31f7a0..a07c621d9 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.cpp
+++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp
@@ -17,6 +17,7 @@
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvnflinger/buffer_item_consumer.h"
#include "core/hle/service/nvnflinger/buffer_queue_core.h"
+#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/nvnflinger/ui/graphic_buffer.h"
@@ -331,4 +332,14 @@ s64 Nvnflinger::GetNextTicks() const {
return static_cast<s64>(speed_scale * (1000000000.f / effective_fps));
}
+FbShareBufferManager& Nvnflinger::GetSystemBufferManager() {
+ const auto lock_guard = Lock();
+
+ if (!system_buffer_manager) {
+ system_buffer_manager = std::make_unique<FbShareBufferManager>(system, *this, nvdrv);
+ }
+
+ return *system_buffer_manager;
+}
+
} // namespace Service::Nvnflinger
diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h
index f478c2bc6..14c783582 100644
--- a/src/core/hle/service/nvnflinger/nvnflinger.h
+++ b/src/core/hle/service/nvnflinger/nvnflinger.h
@@ -45,6 +45,9 @@ class BufferQueueProducer;
namespace Service::Nvnflinger {
+class FbShareBufferManager;
+class HosBinderDriverServer;
+
class Nvnflinger final {
public:
explicit Nvnflinger(Core::System& system_, HosBinderDriverServer& hos_binder_driver_server_);
@@ -90,12 +93,16 @@ public:
[[nodiscard]] s64 GetNextTicks() const;
+ FbShareBufferManager& GetSystemBufferManager();
+
private:
struct Layer {
std::unique_ptr<android::BufferQueueCore> core;
std::unique_ptr<android::BufferQueueProducer> producer;
};
+ friend class FbShareBufferManager;
+
private:
[[nodiscard]] std::unique_lock<std::mutex> Lock() const {
return std::unique_lock{*guard};
@@ -140,6 +147,8 @@ private:
std::shared_ptr<Core::Timing::EventType> multi_composition_event;
std::shared_ptr<Core::Timing::EventType> single_composition_event;
+ std::unique_ptr<FbShareBufferManager> system_buffer_manager;
+
std::shared_ptr<std::mutex> guard;
Core::System& system;
diff --git a/src/core/hle/service/nvnflinger/ui/fence.h b/src/core/hle/service/nvnflinger/ui/fence.h
index 536e8156d..177aed758 100644
--- a/src/core/hle/service/nvnflinger/ui/fence.h
+++ b/src/core/hle/service/nvnflinger/ui/fence.h
@@ -20,6 +20,9 @@ public:
static constexpr Fence NoFence() {
Fence fence;
fence.fences[0].id = -1;
+ fence.fences[1].id = -1;
+ fence.fences[2].id = -1;
+ fence.fences[3].id = -1;
return fence;
}
diff --git a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
index 75d1705a8..3eac5cedd 100644
--- a/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
+++ b/src/core/hle/service/nvnflinger/ui/graphic_buffer.h
@@ -12,8 +12,7 @@
namespace Service::android {
-class GraphicBuffer final {
-public:
+struct GraphicBuffer final {
constexpr GraphicBuffer() = default;
constexpr GraphicBuffer(u32 width_, u32 height_, PixelFormat format_, u32 usage_)
@@ -77,7 +76,6 @@ public:
return false;
}
-private:
u32 magic{};
s32 width{};
s32 height{};
diff --git a/src/core/hle/service/pctl/pctl_module.cpp b/src/core/hle/service/pctl/pctl_module.cpp
index 5db1703d1..938330dd0 100644
--- a/src/core/hle/service/pctl/pctl_module.cpp
+++ b/src/core/hle/service/pctl/pctl_module.cpp
@@ -33,7 +33,7 @@ public:
{1001, &IParentalControlService::CheckFreeCommunicationPermission, "CheckFreeCommunicationPermission"},
{1002, nullptr, "ConfirmLaunchApplicationPermission"},
{1003, nullptr, "ConfirmResumeApplicationPermission"},
- {1004, nullptr, "ConfirmSnsPostPermission"},
+ {1004, &IParentalControlService::ConfirmSnsPostPermission, "ConfirmSnsPostPermission"},
{1005, nullptr, "ConfirmSystemSettingsPermission"},
{1006, &IParentalControlService::IsRestrictionTemporaryUnlocked, "IsRestrictionTemporaryUnlocked"},
{1007, nullptr, "RevertRestrictionTemporaryUnlocked"},
@@ -236,6 +236,13 @@ private:
states.free_communication = true;
}
+ void ConfirmSnsPostPermission(HLERequestContext& ctx) {
+ LOG_WARNING(Service_PCTL, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(Error::ResultNoFreeCommunication);
+ }
+
void IsRestrictionTemporaryUnlocked(HLERequestContext& ctx) {
const bool is_temporary_unlocked = false;
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 2eb978379..b1bfb9898 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -20,9 +20,12 @@
#include "core/hle/kernel/k_readable_event.h"
#include "core/hle/kernel/k_thread.h"
#include "core/hle/service/ipc_helpers.h"
+#include "core/hle/service/nvdrv/devices/nvmap.h"
#include "core/hle/service/nvdrv/nvdata.h"
+#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvnflinger/binder.h"
#include "core/hle/service/nvnflinger/buffer_queue_producer.h"
+#include "core/hle/service/nvnflinger/fb_share_buffer_manager.h"
#include "core/hle/service/nvnflinger/hos_binder_driver_server.h"
#include "core/hle/service/nvnflinger/nvnflinger.h"
#include "core/hle/service/nvnflinger/parcel.h"
@@ -131,8 +134,9 @@ private:
class ISystemDisplayService final : public ServiceFramework<ISystemDisplayService> {
public:
- explicit ISystemDisplayService(Core::System& system_)
- : ServiceFramework{system_, "ISystemDisplayService"} {
+ explicit ISystemDisplayService(Core::System& system_, Nvnflinger::Nvnflinger& nvnflinger_)
+ : ServiceFramework{system_, "ISystemDisplayService"}, nvnflinger{nvnflinger_} {
+ // clang-format off
static const FunctionInfo functions[] = {
{1200, nullptr, "GetZOrderCountMin"},
{1202, nullptr, "GetZOrderCountMax"},
@@ -170,22 +174,126 @@ public:
{3217, nullptr, "SetDisplayCmuLuma"},
{3218, nullptr, "SetDisplayCrcMode"},
{6013, nullptr, "GetLayerPresentationSubmissionTimestamps"},
- {8225, nullptr, "GetSharedBufferMemoryHandleId"},
- {8250, nullptr, "OpenSharedLayer"},
+ {8225, &ISystemDisplayService::GetSharedBufferMemoryHandleId, "GetSharedBufferMemoryHandleId"},
+ {8250, &ISystemDisplayService::OpenSharedLayer, "OpenSharedLayer"},
{8251, nullptr, "CloseSharedLayer"},
- {8252, nullptr, "ConnectSharedLayer"},
+ {8252, &ISystemDisplayService::ConnectSharedLayer, "ConnectSharedLayer"},
{8253, nullptr, "DisconnectSharedLayer"},
- {8254, nullptr, "AcquireSharedFrameBuffer"},
- {8255, nullptr, "PresentSharedFrameBuffer"},
- {8256, nullptr, "GetSharedFrameBufferAcquirableEvent"},
+ {8254, &ISystemDisplayService::AcquireSharedFrameBuffer, "AcquireSharedFrameBuffer"},
+ {8255, &ISystemDisplayService::PresentSharedFrameBuffer, "PresentSharedFrameBuffer"},
+ {8256, &ISystemDisplayService::GetSharedFrameBufferAcquirableEvent, "GetSharedFrameBufferAcquirableEvent"},
{8257, nullptr, "FillSharedFrameBufferColor"},
{8258, nullptr, "CancelSharedFrameBuffer"},
{9000, nullptr, "GetDp2hdmiController"},
};
+ // clang-format on
RegisterHandlers(functions);
}
private:
+ void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 buffer_id = rp.PopRaw<u64>();
+
+ LOG_INFO(Service_VI, "called. buffer_id={:#x}", buffer_id);
+
+ struct OutputParameters {
+ s32 nvmap_handle;
+ u64 size;
+ };
+
+ OutputParameters out{};
+ Nvnflinger::SharedMemoryPoolLayout layout{};
+ const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId(
+ &out.size, &out.nvmap_handle, &layout, buffer_id, 0);
+
+ ctx.WriteBuffer(&layout, sizeof(layout));
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(result);
+ rb.PushRaw(out);
+ }
+
+ void OpenSharedLayer(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 layer_id = rp.PopRaw<u64>();
+
+ LOG_INFO(Service_VI, "(STUBBED) called. layer_id={:#x}", layer_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void ConnectSharedLayer(HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const u64 layer_id = rp.PopRaw<u64>();
+
+ LOG_INFO(Service_VI, "(STUBBED) called. layer_id={:#x}", layer_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+ }
+
+ void GetSharedFrameBufferAcquirableEvent(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_VI, "called");
+
+ IPC::RequestParser rp{ctx};
+ const u64 layer_id = rp.PopRaw<u64>();
+
+ Kernel::KReadableEvent* event{};
+ const auto result = nvnflinger.GetSystemBufferManager().GetSharedFrameBufferAcquirableEvent(
+ &event, layer_id);
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(result);
+ rb.PushCopyObjects(event);
+ }
+
+ void AcquireSharedFrameBuffer(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_VI, "called");
+
+ IPC::RequestParser rp{ctx};
+ const u64 layer_id = rp.PopRaw<u64>();
+
+ struct OutputParameters {
+ android::Fence fence;
+ std::array<s32, 4> slots;
+ s64 target_slot;
+ };
+ static_assert(sizeof(OutputParameters) == 0x40, "OutputParameters has wrong size");
+
+ OutputParameters out{};
+ const auto result = nvnflinger.GetSystemBufferManager().AcquireSharedFrameBuffer(
+ &out.fence, out.slots, &out.target_slot, layer_id);
+
+ IPC::ResponseBuilder rb{ctx, 18};
+ rb.Push(result);
+ rb.PushRaw(out);
+ }
+
+ void PresentSharedFrameBuffer(HLERequestContext& ctx) {
+ LOG_DEBUG(Service_VI, "called");
+
+ struct InputParameters {
+ android::Fence fence;
+ Common::Rectangle<s32> crop_region;
+ u32 window_transform;
+ s32 swap_interval;
+ u64 layer_id;
+ s64 surface_id;
+ };
+ static_assert(sizeof(InputParameters) == 0x50, "InputParameters has wrong size");
+
+ IPC::RequestParser rp{ctx};
+ auto input = rp.PopRaw<InputParameters>();
+
+ const auto result = nvnflinger.GetSystemBufferManager().PresentSharedFrameBuffer(
+ input.fence, input.crop_region, input.window_transform, input.swap_interval,
+ input.layer_id, input.surface_id);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(result);
+ }
+
void SetLayerZ(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 layer_id = rp.Pop<u64>();
@@ -228,6 +336,9 @@ private:
rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
rb.Push<u32>(0);
}
+
+private:
+ Nvnflinger::Nvnflinger& nvnflinger;
};
class IManagerDisplayService final : public ServiceFramework<IManagerDisplayService> {
@@ -453,7 +564,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
- rb.PushIpcInterface<ISystemDisplayService>(system);
+ rb.PushIpcInterface<ISystemDisplayService>(system, nv_flinger);
}
void GetManagerDisplayService(HLERequestContext& ctx) {
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 5a42dea48..5c36b71e5 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -118,7 +118,7 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
return {ResultStatus::ErrorMissingNPDM, {}};
}
- const ResultStatus result2 = metadata.Load(npdm);
+ const ResultStatus result2 = metadata.Reload(npdm);
if (result2 != ResultStatus::Success) {
return {result2, {}};
}