summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/settings.h6
-rw-r--r--src/common/settings_enums.h2
-rw-r--r--src/core/CMakeLists.txt19
-rw-r--r--src/core/device_memory_manager.inc12
-rw-r--r--src/core/file_sys/fs_filesystem.h27
-rw-r--r--src/core/file_sys/fs_memory_management.h8
-rw-r--r--src/core/file_sys/fs_path.h2
-rw-r--r--src/core/file_sys/fs_path_utility.h7
-rw-r--r--src/core/file_sys/fs_string_util.h15
-rw-r--r--src/core/file_sys/fsa/fs_i_directory.h91
-rw-r--r--src/core/file_sys/fsa/fs_i_file.h167
-rw-r--r--src/core/file_sys/fsa/fs_i_filesystem.h206
-rw-r--r--src/core/file_sys/fssrv/fssrv_sf_path.h36
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_directory.cpp72
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_directory.h13
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_file.cpp124
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_file.h21
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp283
-rw-r--r--src/core/hle/service/filesystem/fsp/fs_i_filesystem.h60
-rw-r--r--src/core/hle/service/psc/ovln/ovln_types.h21
-rw-r--r--src/core/hle/service/psc/ovln/receiver.cpp24
-rw-r--r--src/core/hle/service/psc/ovln/receiver.h16
-rw-r--r--src/core/hle/service/psc/ovln/receiver_service.cpp28
-rw-r--r--src/core/hle/service/psc/ovln/receiver_service.h22
-rw-r--r--src/core/hle/service/psc/ovln/sender.cpp32
-rw-r--r--src/core/hle/service/psc/ovln/sender.h21
-rw-r--r--src/core/hle/service/psc/ovln/sender_service.cpp30
-rw-r--r--src/core/hle/service/psc/ovln/sender_service.h23
-rw-r--r--src/core/hle/service/psc/pm_control.cpp28
-rw-r--r--src/core/hle/service/psc/pm_control.h16
-rw-r--r--src/core/hle/service/psc/pm_module.cpp24
-rw-r--r--src/core/hle/service/psc/pm_module.h16
-rw-r--r--src/core/hle/service/psc/pm_service.cpp28
-rw-r--r--src/core/hle/service/psc/pm_service.h22
-rw-r--r--src/core/hle/service/psc/psc.cpp71
-rw-r--r--src/core/hle/service/psc/psc.h4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv.cpp4
-rw-r--r--src/shader_recompiler/backend/spirv/emit_spirv_special.cpp8
-rw-r--r--src/shader_recompiler/profile.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp1
-rw-r--r--src/video_core/vulkan_common/vulkan_device.cpp14
-rw-r--r--src/video_core/vulkan_common/vulkan_device.h5
-rw-r--r--src/yuzu/configuration/shared_translation.cpp10
43 files changed, 1180 insertions, 460 deletions
diff --git a/src/common/settings.h b/src/common/settings.h
index aa054dc24..b2b071e7e 100644
--- a/src/common/settings.h
+++ b/src/common/settings.h
@@ -384,6 +384,12 @@ struct Values {
384 AstcRecompression::Bc3, 384 AstcRecompression::Bc3,
385 "astc_recompression", 385 "astc_recompression",
386 Category::RendererAdvanced}; 386 Category::RendererAdvanced};
387 SwitchableSetting<VramUsageMode, true> vram_usage_mode{linkage,
388 VramUsageMode::Conservative,
389 VramUsageMode::Conservative,
390 VramUsageMode::Aggressive,
391 "vram_usage_mode",
392 Category::RendererAdvanced};
387 SwitchableSetting<bool> async_presentation{linkage, 393 SwitchableSetting<bool> async_presentation{linkage,
388#ifdef ANDROID 394#ifdef ANDROID
389 true, 395 true,
diff --git a/src/common/settings_enums.h b/src/common/settings_enums.h
index f42367e67..6e247e930 100644
--- a/src/common/settings_enums.h
+++ b/src/common/settings_enums.h
@@ -122,6 +122,8 @@ ENUM(AstcRecompression, Uncompressed, Bc1, Bc3);
122 122
123ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed); 123ENUM(VSyncMode, Immediate, Mailbox, Fifo, FifoRelaxed);
124 124
125ENUM(VramUsageMode, Conservative, Aggressive);
126
125ENUM(RendererBackend, OpenGL, Vulkan, Null); 127ENUM(RendererBackend, OpenGL, Vulkan, Null);
126 128
127ENUM(ShaderBackend, Glsl, Glasm, SpirV); 129ENUM(ShaderBackend, Glsl, Glasm, SpirV);
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index f67a12f8f..a7b760fa2 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -59,8 +59,12 @@ add_library(core STATIC
59 file_sys/fs_path.h 59 file_sys/fs_path.h
60 file_sys/fs_path_utility.h 60 file_sys/fs_path_utility.h
61 file_sys/fs_string_util.h 61 file_sys/fs_string_util.h
62 file_sys/fsa/fs_i_directory.h
63 file_sys/fsa/fs_i_file.h
64 file_sys/fsa/fs_i_filesystem.h
62 file_sys/fsmitm_romfsbuild.cpp 65 file_sys/fsmitm_romfsbuild.cpp
63 file_sys/fsmitm_romfsbuild.h 66 file_sys/fsmitm_romfsbuild.h
67 file_sys/fssrv/fssrv_sf_path.h
64 file_sys/fssystem/fs_i_storage.h 68 file_sys/fssystem/fs_i_storage.h
65 file_sys/fssystem/fs_types.h 69 file_sys/fssystem/fs_types.h
66 file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp 70 file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
@@ -891,6 +895,21 @@ add_library(core STATIC
891 hle/service/pm/pm.h 895 hle/service/pm/pm.h
892 hle/service/prepo/prepo.cpp 896 hle/service/prepo/prepo.cpp
893 hle/service/prepo/prepo.h 897 hle/service/prepo/prepo.h
898 hle/service/psc/ovln/ovln_types.h
899 hle/service/psc/ovln/receiver_service.cpp
900 hle/service/psc/ovln/receiver_service.h
901 hle/service/psc/ovln/receiver.cpp
902 hle/service/psc/ovln/receiver.h
903 hle/service/psc/ovln/sender_service.cpp
904 hle/service/psc/ovln/sender_service.h
905 hle/service/psc/ovln/sender.cpp
906 hle/service/psc/ovln/sender.h
907 hle/service/psc/pm_control.cpp
908 hle/service/psc/pm_control.h
909 hle/service/psc/pm_module.cpp
910 hle/service/psc/pm_module.h
911 hle/service/psc/pm_service.cpp
912 hle/service/psc/pm_service.h
894 hle/service/psc/psc.cpp 913 hle/service/psc/psc.cpp
895 hle/service/psc/psc.h 914 hle/service/psc/psc.h
896 hle/service/psc/time/alarms.cpp 915 hle/service/psc/time/alarms.cpp
diff --git a/src/core/device_memory_manager.inc b/src/core/device_memory_manager.inc
index 37c1e69c3..f104d495b 100644
--- a/src/core/device_memory_manager.inc
+++ b/src/core/device_memory_manager.inc
@@ -522,13 +522,17 @@ void DeviceMemoryManager<Traits>::UpdatePagesCachedCount(DAddr addr, size_t size
522 auto* memory_device_inter = registered_processes[asid.id]; 522 auto* memory_device_inter = registered_processes[asid.id];
523 const auto release_pending = [&] { 523 const auto release_pending = [&] {
524 if (uncache_bytes > 0) { 524 if (uncache_bytes > 0) {
525 MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS, 525 if (memory_device_inter != nullptr) {
526 uncache_bytes, false); 526 MarkRegionCaching(memory_device_inter, uncache_begin << Memory::YUZU_PAGEBITS,
527 uncache_bytes, false);
528 }
527 uncache_bytes = 0; 529 uncache_bytes = 0;
528 } 530 }
529 if (cache_bytes > 0) { 531 if (cache_bytes > 0) {
530 MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS, 532 if (memory_device_inter != nullptr) {
531 cache_bytes, true); 533 MarkRegionCaching(memory_device_inter, cache_begin << Memory::YUZU_PAGEBITS,
534 cache_bytes, true);
535 }
532 cache_bytes = 0; 536 cache_bytes = 0;
533 } 537 }
534 }; 538 };
diff --git a/src/core/file_sys/fs_filesystem.h b/src/core/file_sys/fs_filesystem.h
index 7f237b7fa..329b5aca5 100644
--- a/src/core/file_sys/fs_filesystem.h
+++ b/src/core/file_sys/fs_filesystem.h
@@ -23,6 +23,8 @@ enum class OpenDirectoryMode : u64 {
23 File = (1 << 1), 23 File = (1 << 1),
24 24
25 All = (Directory | File), 25 All = (Directory | File),
26
27 NotRequireFileSize = (1ULL << 31),
26}; 28};
27DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode) 29DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
28 30
@@ -36,4 +38,29 @@ enum class CreateOption : u8 {
36 BigFile = (1 << 0), 38 BigFile = (1 << 0),
37}; 39};
38 40
41struct FileSystemAttribute {
42 u8 dir_entry_name_length_max_defined;
43 u8 file_entry_name_length_max_defined;
44 u8 dir_path_name_length_max_defined;
45 u8 file_path_name_length_max_defined;
46 INSERT_PADDING_BYTES_NOINIT(0x5);
47 u8 utf16_dir_entry_name_length_max_defined;
48 u8 utf16_file_entry_name_length_max_defined;
49 u8 utf16_dir_path_name_length_max_defined;
50 u8 utf16_file_path_name_length_max_defined;
51 INSERT_PADDING_BYTES_NOINIT(0x18);
52 s32 dir_entry_name_length_max;
53 s32 file_entry_name_length_max;
54 s32 dir_path_name_length_max;
55 s32 file_path_name_length_max;
56 INSERT_PADDING_WORDS_NOINIT(0x5);
57 s32 utf16_dir_entry_name_length_max;
58 s32 utf16_file_entry_name_length_max;
59 s32 utf16_dir_path_name_length_max;
60 s32 utf16_file_path_name_length_max;
61 INSERT_PADDING_WORDS_NOINIT(0x18);
62 INSERT_PADDING_WORDS_NOINIT(0x1);
63};
64static_assert(sizeof(FileSystemAttribute) == 0xC0, "FileSystemAttribute has incorrect size");
65
39} // namespace FileSys 66} // namespace FileSys
diff --git a/src/core/file_sys/fs_memory_management.h b/src/core/file_sys/fs_memory_management.h
index f03c6354b..080017c5d 100644
--- a/src/core/file_sys/fs_memory_management.h
+++ b/src/core/file_sys/fs_memory_management.h
@@ -10,7 +10,7 @@ namespace FileSys {
10 10
11constexpr size_t RequiredAlignment = alignof(u64); 11constexpr size_t RequiredAlignment = alignof(u64);
12 12
13void* AllocateUnsafe(size_t size) { 13inline void* AllocateUnsafe(size_t size) {
14 // Allocate 14 // Allocate
15 void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment}); 15 void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
16 16
@@ -21,16 +21,16 @@ void* AllocateUnsafe(size_t size) {
21 return ptr; 21 return ptr;
22} 22}
23 23
24void DeallocateUnsafe(void* ptr, size_t size) { 24inline void DeallocateUnsafe(void* ptr, size_t size) {
25 // Deallocate the pointer 25 // Deallocate the pointer
26 ::operator delete(ptr, std::align_val_t{RequiredAlignment}); 26 ::operator delete(ptr, std::align_val_t{RequiredAlignment});
27} 27}
28 28
29void* Allocate(size_t size) { 29inline void* Allocate(size_t size) {
30 return AllocateUnsafe(size); 30 return AllocateUnsafe(size);
31} 31}
32 32
33void Deallocate(void* ptr, size_t size) { 33inline void Deallocate(void* ptr, size_t size) {
34 // If the pointer is non-null, deallocate it 34 // If the pointer is non-null, deallocate it
35 if (ptr != nullptr) { 35 if (ptr != nullptr) {
36 DeallocateUnsafe(ptr, size); 36 DeallocateUnsafe(ptr, size);
diff --git a/src/core/file_sys/fs_path.h b/src/core/file_sys/fs_path.h
index 56ba08a6a..1566e82b9 100644
--- a/src/core/file_sys/fs_path.h
+++ b/src/core/file_sys/fs_path.h
@@ -381,7 +381,7 @@ public:
381 381
382 // Check that it's possible for us to remove a child 382 // Check that it's possible for us to remove a child
383 auto* p = m_write_buffer.Get(); 383 auto* p = m_write_buffer.Get();
384 s32 len = std::strlen(p); 384 s32 len = static_cast<s32>(std::strlen(p));
385 R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented); 385 R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented);
386 386
387 // Handle a trailing separator 387 // Handle a trailing separator
diff --git a/src/core/file_sys/fs_path_utility.h b/src/core/file_sys/fs_path_utility.h
index 5643141f9..cdfd8c772 100644
--- a/src/core/file_sys/fs_path_utility.h
+++ b/src/core/file_sys/fs_path_utility.h
@@ -426,9 +426,10 @@ public:
426 R_SUCCEED(); 426 R_SUCCEED();
427 } 427 }
428 428
429 static Result Normalize(char* dst, size_t* out_len, const char* path, size_t max_out_size, 429 static constexpr Result Normalize(char* dst, size_t* out_len, const char* path,
430 bool is_windows_path, bool is_drive_relative_path, 430 size_t max_out_size, bool is_windows_path,
431 bool allow_all_characters = false) { 431 bool is_drive_relative_path,
432 bool allow_all_characters = false) {
432 // Use StringTraits names for remainder of scope 433 // Use StringTraits names for remainder of scope
433 using namespace StringTraits; 434 using namespace StringTraits;
434 435
diff --git a/src/core/file_sys/fs_string_util.h b/src/core/file_sys/fs_string_util.h
index 874e09054..c751a8f1a 100644
--- a/src/core/file_sys/fs_string_util.h
+++ b/src/core/file_sys/fs_string_util.h
@@ -20,6 +20,11 @@ constexpr int Strlen(const T* str) {
20} 20}
21 21
22template <typename T> 22template <typename T>
23constexpr int Strnlen(const T* str, std::size_t count) {
24 return Strnlen(str, static_cast<int>(count));
25}
26
27template <typename T>
23constexpr int Strnlen(const T* str, int count) { 28constexpr int Strnlen(const T* str, int count) {
24 ASSERT(str != nullptr); 29 ASSERT(str != nullptr);
25 ASSERT(count >= 0); 30 ASSERT(count >= 0);
@@ -33,6 +38,11 @@ constexpr int Strnlen(const T* str, int count) {
33} 38}
34 39
35template <typename T> 40template <typename T>
41constexpr int Strncmp(const T* lhs, const T* rhs, std::size_t count) {
42 return Strncmp(lhs, rhs, static_cast<int>(count));
43}
44
45template <typename T>
36constexpr int Strncmp(const T* lhs, const T* rhs, int count) { 46constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
37 ASSERT(lhs != nullptr); 47 ASSERT(lhs != nullptr);
38 ASSERT(rhs != nullptr); 48 ASSERT(rhs != nullptr);
@@ -52,6 +62,11 @@ constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
52} 62}
53 63
54template <typename T> 64template <typename T>
65static constexpr int Strlcpy(T* dst, const T* src, std::size_t count) {
66 return Strlcpy<T>(dst, src, static_cast<int>(count));
67}
68
69template <typename T>
55static constexpr int Strlcpy(T* dst, const T* src, int count) { 70static constexpr int Strlcpy(T* dst, const T* src, int count) {
56 ASSERT(dst != nullptr); 71 ASSERT(dst != nullptr);
57 ASSERT(src != nullptr); 72 ASSERT(src != nullptr);
diff --git a/src/core/file_sys/fsa/fs_i_directory.h b/src/core/file_sys/fsa/fs_i_directory.h
new file mode 100644
index 000000000..c8e895eab
--- /dev/null
+++ b/src/core/file_sys/fsa/fs_i_directory.h
@@ -0,0 +1,91 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/common_types.h"
7#include "core/file_sys/errors.h"
8#include "core/file_sys/fs_directory.h"
9#include "core/file_sys/fs_file.h"
10#include "core/file_sys/fs_filesystem.h"
11#include "core/file_sys/savedata_factory.h"
12#include "core/file_sys/vfs/vfs.h"
13#include "core/hle/result.h"
14
15namespace FileSys::Fsa {
16
17class IDirectory {
18public:
19 explicit IDirectory(VirtualDir backend_, OpenDirectoryMode mode)
20 : backend(std::move(backend_)) {
21 // TODO(DarkLordZach): Verify that this is the correct behavior.
22 // Build entry index now to save time later.
23 if (True(mode & OpenDirectoryMode::Directory)) {
24 BuildEntryIndex(backend->GetSubdirectories(), DirectoryEntryType::Directory);
25 }
26 if (True(mode & OpenDirectoryMode::File)) {
27 BuildEntryIndex(backend->GetFiles(), DirectoryEntryType::File);
28 }
29 }
30 virtual ~IDirectory() {}
31
32 Result Read(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
33 R_UNLESS(out_count != nullptr, ResultNullptrArgument);
34 if (max_entries == 0) {
35 *out_count = 0;
36 R_SUCCEED();
37 }
38 R_UNLESS(out_entries != nullptr, ResultNullptrArgument);
39 R_UNLESS(max_entries > 0, ResultInvalidArgument);
40 R_RETURN(this->DoRead(out_count, out_entries, max_entries));
41 }
42
43 Result GetEntryCount(s64* out) {
44 R_UNLESS(out != nullptr, ResultNullptrArgument);
45 R_RETURN(this->DoGetEntryCount(out));
46 }
47
48private:
49 Result DoRead(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
50 const u64 actual_entries =
51 std::min(static_cast<u64>(max_entries), entries.size() - next_entry_index);
52 const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
53 const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
54 const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
55
56 next_entry_index += actual_entries;
57 *out_count = actual_entries;
58
59 std::memcpy(out_entries, begin, range_size);
60
61 R_SUCCEED();
62 }
63
64 Result DoGetEntryCount(s64* out) {
65 *out = entries.size() - next_entry_index;
66 R_SUCCEED();
67 }
68
69 // TODO: Remove this when VFS is gone
70 template <typename T>
71 void BuildEntryIndex(const std::vector<T>& new_data, DirectoryEntryType type) {
72 entries.reserve(entries.size() + new_data.size());
73
74 for (const auto& new_entry : new_data) {
75 auto name = new_entry->GetName();
76
77 if (type == DirectoryEntryType::File && name == GetSaveDataSizeFileName()) {
78 continue;
79 }
80
81 entries.emplace_back(name, static_cast<s8>(type),
82 type == DirectoryEntryType::Directory ? 0 : new_entry->GetSize());
83 }
84 }
85
86 VirtualDir backend;
87 std::vector<DirectoryEntry> entries;
88 u64 next_entry_index = 0;
89};
90
91} // namespace FileSys::Fsa
diff --git a/src/core/file_sys/fsa/fs_i_file.h b/src/core/file_sys/fsa/fs_i_file.h
new file mode 100644
index 000000000..1188ae8ca
--- /dev/null
+++ b/src/core/file_sys/fsa/fs_i_file.h
@@ -0,0 +1,167 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/overflow.h"
7#include "core/file_sys/errors.h"
8#include "core/file_sys/fs_file.h"
9#include "core/file_sys/fs_filesystem.h"
10#include "core/file_sys/fs_operate_range.h"
11#include "core/file_sys/vfs/vfs.h"
12#include "core/file_sys/vfs/vfs_types.h"
13#include "core/hle/result.h"
14
15namespace FileSys::Fsa {
16
17class IFile {
18public:
19 explicit IFile(VirtualFile backend_) : backend(std::move(backend_)) {}
20 virtual ~IFile() {}
21
22 Result Read(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
23 // Check that we have an output pointer
24 R_UNLESS(out != nullptr, ResultNullptrArgument);
25
26 // If we have nothing to read, just succeed
27 if (size == 0) {
28 *out = 0;
29 R_SUCCEED();
30 }
31
32 // Check that the read is valid
33 R_UNLESS(buffer != nullptr, ResultNullptrArgument);
34 R_UNLESS(offset >= 0, ResultOutOfRange);
35 R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
36
37 // Do the read
38 R_RETURN(this->DoRead(out, offset, buffer, size, option));
39 }
40
41 Result Read(size_t* out, s64 offset, void* buffer, size_t size) {
42 R_RETURN(this->Read(out, offset, buffer, size, ReadOption::None));
43 }
44
45 Result GetSize(s64* out) {
46 R_UNLESS(out != nullptr, ResultNullptrArgument);
47 R_RETURN(this->DoGetSize(out));
48 }
49
50 Result Flush() {
51 R_RETURN(this->DoFlush());
52 }
53
54 Result Write(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
55 // Handle the zero-size case
56 if (size == 0) {
57 if (option.HasFlushFlag()) {
58 R_TRY(this->Flush());
59 }
60 R_SUCCEED();
61 }
62
63 // Check the write is valid
64 R_UNLESS(buffer != nullptr, ResultNullptrArgument);
65 R_UNLESS(offset >= 0, ResultOutOfRange);
66 R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
67
68 R_RETURN(this->DoWrite(offset, buffer, size, option));
69 }
70
71 Result SetSize(s64 size) {
72 R_UNLESS(size >= 0, ResultOutOfRange);
73 R_RETURN(this->DoSetSize(size));
74 }
75
76 Result OperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
77 const void* src, size_t src_size) {
78 R_RETURN(this->DoOperateRange(dst, dst_size, op_id, offset, size, src, src_size));
79 }
80
81 Result OperateRange(OperationId op_id, s64 offset, s64 size) {
82 R_RETURN(this->DoOperateRange(nullptr, 0, op_id, offset, size, nullptr, 0));
83 }
84
85protected:
86 Result DryRead(size_t* out, s64 offset, size_t size, const ReadOption& option,
87 OpenMode open_mode) {
88 // Check that we can read
89 R_UNLESS(static_cast<u32>(open_mode & OpenMode::Read) != 0, ResultReadNotPermitted);
90
91 // Get the file size, and validate our offset
92 s64 file_size = 0;
93 R_TRY(this->DoGetSize(std::addressof(file_size)));
94 R_UNLESS(offset <= file_size, ResultOutOfRange);
95
96 *out = static_cast<size_t>(std::min(file_size - offset, static_cast<s64>(size)));
97 R_SUCCEED();
98 }
99
100 Result DrySetSize(s64 size, OpenMode open_mode) {
101 // Check that we can write
102 R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
103 R_SUCCEED();
104 }
105
106 Result DryWrite(bool* out_append, s64 offset, size_t size, const WriteOption& option,
107 OpenMode open_mode) {
108 // Check that we can write
109 R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
110
111 // Get the file size
112 s64 file_size = 0;
113 R_TRY(this->DoGetSize(&file_size));
114
115 // Determine if we need to append
116 *out_append = false;
117 if (file_size < offset + static_cast<s64>(size)) {
118 R_UNLESS(static_cast<u32>(open_mode & OpenMode::AllowAppend) != 0,
119 ResultFileExtensionWithoutOpenModeAllowAppend);
120 *out_append = true;
121 }
122
123 R_SUCCEED();
124 }
125
126private:
127 Result DoRead(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
128 const auto read_size = backend->Read(static_cast<u8*>(buffer), size, offset);
129 *out = read_size;
130
131 R_SUCCEED();
132 }
133
134 Result DoGetSize(s64* out) {
135 *out = backend->GetSize();
136 R_SUCCEED();
137 }
138
139 Result DoFlush() {
140 // Exists for SDK compatibiltity -- No need to flush file.
141 R_SUCCEED();
142 }
143
144 Result DoWrite(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
145 const std::size_t written = backend->Write(static_cast<const u8*>(buffer), size, offset);
146
147 ASSERT_MSG(written == size,
148 "Could not write all bytes to file (requested={:016X}, actual={:016X}).", size,
149 written);
150
151 R_SUCCEED();
152 }
153
154 Result DoSetSize(s64 size) {
155 backend->Resize(size);
156 R_SUCCEED();
157 }
158
159 Result DoOperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
160 const void* src, size_t src_size) {
161 R_THROW(ResultNotImplemented);
162 }
163
164 VirtualFile backend;
165};
166
167} // namespace FileSys::Fsa
diff --git a/src/core/file_sys/fsa/fs_i_filesystem.h b/src/core/file_sys/fsa/fs_i_filesystem.h
new file mode 100644
index 000000000..8172190f4
--- /dev/null
+++ b/src/core/file_sys/fsa/fs_i_filesystem.h
@@ -0,0 +1,206 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/file_sys/errors.h"
7#include "core/file_sys/fs_filesystem.h"
8#include "core/file_sys/fs_path.h"
9#include "core/file_sys/vfs/vfs_types.h"
10#include "core/hle/result.h"
11#include "core/hle/service/filesystem/filesystem.h"
12
13namespace FileSys::Fsa {
14
15class IFile;
16class IDirectory;
17
18enum class QueryId : u32 {
19 SetConcatenationFileAttribute = 0,
20 UpdateMac = 1,
21 IsSignedSystemPartitionOnSdCardValid = 2,
22 QueryUnpreparedFileInformation = 3,
23};
24
25class IFileSystem {
26public:
27 explicit IFileSystem(VirtualDir backend_) : backend{std::move(backend_)} {}
28 virtual ~IFileSystem() {}
29
30 Result CreateFile(const Path& path, s64 size, CreateOption option) {
31 R_UNLESS(size >= 0, ResultOutOfRange);
32 R_RETURN(this->DoCreateFile(path, size, static_cast<int>(option)));
33 }
34
35 Result CreateFile(const Path& path, s64 size) {
36 R_RETURN(this->CreateFile(path, size, CreateOption::None));
37 }
38
39 Result DeleteFile(const Path& path) {
40 R_RETURN(this->DoDeleteFile(path));
41 }
42
43 Result CreateDirectory(const Path& path) {
44 R_RETURN(this->DoCreateDirectory(path));
45 }
46
47 Result DeleteDirectory(const Path& path) {
48 R_RETURN(this->DoDeleteDirectory(path));
49 }
50
51 Result DeleteDirectoryRecursively(const Path& path) {
52 R_RETURN(this->DoDeleteDirectoryRecursively(path));
53 }
54
55 Result RenameFile(const Path& old_path, const Path& new_path) {
56 R_RETURN(this->DoRenameFile(old_path, new_path));
57 }
58
59 Result RenameDirectory(const Path& old_path, const Path& new_path) {
60 R_RETURN(this->DoRenameDirectory(old_path, new_path));
61 }
62
63 Result GetEntryType(DirectoryEntryType* out, const Path& path) {
64 R_RETURN(this->DoGetEntryType(out, path));
65 }
66
67 Result OpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
68 R_UNLESS(out_file != nullptr, ResultNullptrArgument);
69 R_UNLESS(static_cast<u32>(mode & OpenMode::ReadWrite) != 0, ResultInvalidOpenMode);
70 R_UNLESS(static_cast<u32>(mode & ~OpenMode::All) == 0, ResultInvalidOpenMode);
71 R_RETURN(this->DoOpenFile(out_file, path, mode));
72 }
73
74 Result OpenDirectory(VirtualDir* out_dir, const Path& path, OpenDirectoryMode mode) {
75 R_UNLESS(out_dir != nullptr, ResultNullptrArgument);
76 R_UNLESS(static_cast<u64>(mode & OpenDirectoryMode::All) != 0, ResultInvalidOpenMode);
77 R_UNLESS(static_cast<u64>(
78 mode & ~(OpenDirectoryMode::All | OpenDirectoryMode::NotRequireFileSize)) == 0,
79 ResultInvalidOpenMode);
80 R_RETURN(this->DoOpenDirectory(out_dir, path, mode));
81 }
82
83 Result Commit() {
84 R_RETURN(this->DoCommit());
85 }
86
87 Result GetFreeSpaceSize(s64* out, const Path& path) {
88 R_UNLESS(out != nullptr, ResultNullptrArgument);
89 R_RETURN(this->DoGetFreeSpaceSize(out, path));
90 }
91
92 Result GetTotalSpaceSize(s64* out, const Path& path) {
93 R_UNLESS(out != nullptr, ResultNullptrArgument);
94 R_RETURN(this->DoGetTotalSpaceSize(out, path));
95 }
96
97 Result CleanDirectoryRecursively(const Path& path) {
98 R_RETURN(this->DoCleanDirectoryRecursively(path));
99 }
100
101 Result GetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
102 R_UNLESS(out != nullptr, ResultNullptrArgument);
103 R_RETURN(this->DoGetFileTimeStampRaw(out, path));
104 }
105
106 Result QueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
107 const Path& path) {
108 R_RETURN(this->DoQueryEntry(dst, dst_size, src, src_size, query, path));
109 }
110
111 // These aren't accessible as commands
112 Result CommitProvisionally(s64 counter) {
113 R_RETURN(this->DoCommitProvisionally(counter));
114 }
115
116 Result Rollback() {
117 R_RETURN(this->DoRollback());
118 }
119
120 Result Flush() {
121 R_RETURN(this->DoFlush());
122 }
123
124private:
125 Result DoCreateFile(const Path& path, s64 size, int flags) {
126 R_RETURN(backend.CreateFile(path.GetString(), size));
127 }
128
129 Result DoDeleteFile(const Path& path) {
130 R_RETURN(backend.DeleteFile(path.GetString()));
131 }
132
133 Result DoCreateDirectory(const Path& path) {
134 R_RETURN(backend.CreateDirectory(path.GetString()));
135 }
136
137 Result DoDeleteDirectory(const Path& path) {
138 R_RETURN(backend.DeleteDirectory(path.GetString()));
139 }
140
141 Result DoDeleteDirectoryRecursively(const Path& path) {
142 R_RETURN(backend.DeleteDirectoryRecursively(path.GetString()));
143 }
144
145 Result DoRenameFile(const Path& old_path, const Path& new_path) {
146 R_RETURN(backend.RenameFile(old_path.GetString(), new_path.GetString()));
147 }
148
149 Result DoRenameDirectory(const Path& old_path, const Path& new_path) {
150 R_RETURN(backend.RenameDirectory(old_path.GetString(), new_path.GetString()));
151 }
152
153 Result DoGetEntryType(DirectoryEntryType* out, const Path& path) {
154 R_RETURN(backend.GetEntryType(out, path.GetString()));
155 }
156
157 Result DoOpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
158 R_RETURN(backend.OpenFile(out_file, path.GetString(), mode));
159 }
160
161 Result DoOpenDirectory(VirtualDir* out_directory, const Path& path, OpenDirectoryMode mode) {
162 R_RETURN(backend.OpenDirectory(out_directory, path.GetString()));
163 }
164
165 Result DoCommit() {
166 R_THROW(ResultNotImplemented);
167 }
168
169 Result DoGetFreeSpaceSize(s64* out, const Path& path) {
170 R_THROW(ResultNotImplemented);
171 }
172
173 Result DoGetTotalSpaceSize(s64* out, const Path& path) {
174 R_THROW(ResultNotImplemented);
175 }
176
177 Result DoCleanDirectoryRecursively(const Path& path) {
178 R_RETURN(backend.CleanDirectoryRecursively(path.GetString()));
179 }
180
181 Result DoGetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
182 R_RETURN(backend.GetFileTimeStampRaw(out, path.GetString()));
183 }
184
185 Result DoQueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
186 const Path& path) {
187 R_THROW(ResultNotImplemented);
188 }
189
190 // These aren't accessible as commands
191 Result DoCommitProvisionally(s64 counter) {
192 R_THROW(ResultNotImplemented);
193 }
194
195 Result DoRollback() {
196 R_THROW(ResultNotImplemented);
197 }
198
199 Result DoFlush() {
200 R_THROW(ResultNotImplemented);
201 }
202
203 Service::FileSystem::VfsDirectoryServiceWrapper backend;
204};
205
206} // namespace FileSys::Fsa
diff --git a/src/core/file_sys/fssrv/fssrv_sf_path.h b/src/core/file_sys/fssrv/fssrv_sf_path.h
new file mode 100644
index 000000000..a0c0b2dac
--- /dev/null
+++ b/src/core/file_sys/fssrv/fssrv_sf_path.h
@@ -0,0 +1,36 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/file_sys/fs_directory.h"
7
8namespace FileSys::Sf {
9
10struct Path {
11 char str[EntryNameLengthMax + 1];
12
13 static constexpr Path Encode(const char* p) {
14 Path path = {};
15 for (size_t i = 0; i < sizeof(path) - 1; i++) {
16 path.str[i] = p[i];
17 if (p[i] == '\x00') {
18 break;
19 }
20 }
21 return path;
22 }
23
24 static constexpr size_t GetPathLength(const Path& path) {
25 size_t len = 0;
26 for (size_t i = 0; i < sizeof(path) - 1 && path.str[i] != '\x00'; i++) {
27 len++;
28 }
29 return len;
30 }
31};
32static_assert(std::is_trivially_copyable_v<Path>, "Path must be trivially copyable.");
33
34using FspPath = Path;
35
36} // namespace FileSys::Sf
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp b/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp
index 39690018b..8483394d0 100644
--- a/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp
+++ b/src/core/hle/service/filesystem/fsp/fs_i_directory.cpp
@@ -3,82 +3,34 @@
3 3
4#include "core/file_sys/fs_filesystem.h" 4#include "core/file_sys/fs_filesystem.h"
5#include "core/file_sys/savedata_factory.h" 5#include "core/file_sys/savedata_factory.h"
6#include "core/hle/service/cmif_serialization.h"
6#include "core/hle/service/filesystem/fsp/fs_i_directory.h" 7#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
7#include "core/hle/service/ipc_helpers.h"
8 8
9namespace Service::FileSystem { 9namespace Service::FileSystem {
10 10
11template <typename T> 11IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir directory_,
12static void BuildEntryIndex(std::vector<FileSys::DirectoryEntry>& entries,
13 const std::vector<T>& new_data, FileSys::DirectoryEntryType type) {
14 entries.reserve(entries.size() + new_data.size());
15
16 for (const auto& new_entry : new_data) {
17 auto name = new_entry->GetName();
18
19 if (type == FileSys::DirectoryEntryType::File &&
20 name == FileSys::GetSaveDataSizeFileName()) {
21 continue;
22 }
23
24 entries.emplace_back(name, static_cast<s8>(type),
25 type == FileSys::DirectoryEntryType::Directory ? 0
26 : new_entry->GetSize());
27 }
28}
29
30IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
31 FileSys::OpenDirectoryMode mode) 12 FileSys::OpenDirectoryMode mode)
32 : ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) { 13 : ServiceFramework{system_, "IDirectory"},
14 backend(std::make_unique<FileSys::Fsa::IDirectory>(directory_, mode)) {
33 static const FunctionInfo functions[] = { 15 static const FunctionInfo functions[] = {
34 {0, &IDirectory::Read, "Read"}, 16 {0, D<&IDirectory::Read>, "Read"},
35 {1, &IDirectory::GetEntryCount, "GetEntryCount"}, 17 {1, D<&IDirectory::GetEntryCount>, "GetEntryCount"},
36 }; 18 };
37 RegisterHandlers(functions); 19 RegisterHandlers(functions);
38
39 // TODO(DarkLordZach): Verify that this is the correct behavior.
40 // Build entry index now to save time later.
41 if (True(mode & FileSys::OpenDirectoryMode::Directory)) {
42 BuildEntryIndex(entries, backend->GetSubdirectories(),
43 FileSys::DirectoryEntryType::Directory);
44 }
45 if (True(mode & FileSys::OpenDirectoryMode::File)) {
46 BuildEntryIndex(entries, backend->GetFiles(), FileSys::DirectoryEntryType::File);
47 }
48} 20}
49 21
50void IDirectory::Read(HLERequestContext& ctx) { 22Result IDirectory::Read(
23 Out<s64> out_count,
24 const OutArray<FileSys::DirectoryEntry, BufferAttr_HipcMapAlias> out_entries) {
51 LOG_DEBUG(Service_FS, "called."); 25 LOG_DEBUG(Service_FS, "called.");
52 26
53 // Calculate how many entries we can fit in the output buffer 27 R_RETURN(backend->Read(out_count, out_entries.data(), out_entries.size()));
54 const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::DirectoryEntry>();
55
56 // Cap at total number of entries.
57 const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
58
59 // Determine data start and end
60 const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
61 const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
62 const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
63
64 next_entry_index += actual_entries;
65
66 // Write the data to memory
67 ctx.WriteBuffer(begin, range_size);
68
69 IPC::ResponseBuilder rb{ctx, 4};
70 rb.Push(ResultSuccess);
71 rb.Push(actual_entries);
72} 28}
73 29
74void IDirectory::GetEntryCount(HLERequestContext& ctx) { 30Result IDirectory::GetEntryCount(Out<s64> out_count) {
75 LOG_DEBUG(Service_FS, "called"); 31 LOG_DEBUG(Service_FS, "called");
76 32
77 u64 count = entries.size() - next_entry_index; 33 R_RETURN(backend->GetEntryCount(out_count));
78
79 IPC::ResponseBuilder rb{ctx, 4};
80 rb.Push(ResultSuccess);
81 rb.Push(count);
82} 34}
83 35
84} // namespace Service::FileSystem 36} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_directory.h b/src/core/hle/service/filesystem/fsp/fs_i_directory.h
index 793ecfcd7..b6251f7fd 100644
--- a/src/core/hle/service/filesystem/fsp/fs_i_directory.h
+++ b/src/core/hle/service/filesystem/fsp/fs_i_directory.h
@@ -3,7 +3,9 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/file_sys/fsa/fs_i_directory.h"
6#include "core/file_sys/vfs/vfs.h" 7#include "core/file_sys/vfs/vfs.h"
8#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/filesystem/filesystem.h" 9#include "core/hle/service/filesystem/filesystem.h"
8#include "core/hle/service/service.h" 10#include "core/hle/service/service.h"
9 11
@@ -15,16 +17,15 @@ namespace Service::FileSystem {
15 17
16class IDirectory final : public ServiceFramework<IDirectory> { 18class IDirectory final : public ServiceFramework<IDirectory> {
17public: 19public:
18 explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_, 20 explicit IDirectory(Core::System& system_, FileSys::VirtualDir directory_,
19 FileSys::OpenDirectoryMode mode); 21 FileSys::OpenDirectoryMode mode);
20 22
21private: 23private:
22 FileSys::VirtualDir backend; 24 std::unique_ptr<FileSys::Fsa::IDirectory> backend;
23 std::vector<FileSys::DirectoryEntry> entries;
24 u64 next_entry_index = 0;
25 25
26 void Read(HLERequestContext& ctx); 26 Result Read(Out<s64> out_count,
27 void GetEntryCount(HLERequestContext& ctx); 27 const OutArray<FileSys::DirectoryEntry, BufferAttr_HipcMapAlias> out_entries);
28 Result GetEntryCount(Out<s64> out_count);
28}; 29};
29 30
30} // namespace Service::FileSystem 31} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_file.cpp b/src/core/hle/service/filesystem/fsp/fs_i_file.cpp
index 9a18f6ec5..a355d46ae 100644
--- a/src/core/hle/service/filesystem/fsp/fs_i_file.cpp
+++ b/src/core/hle/service/filesystem/fsp/fs_i_file.cpp
@@ -2,126 +2,64 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "core/file_sys/errors.h" 4#include "core/file_sys/errors.h"
5#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/filesystem/fsp/fs_i_file.h" 6#include "core/hle/service/filesystem/fsp/fs_i_file.h"
6#include "core/hle/service/ipc_helpers.h"
7 7
8namespace Service::FileSystem { 8namespace Service::FileSystem {
9 9
10IFile::IFile(Core::System& system_, FileSys::VirtualFile backend_) 10IFile::IFile(Core::System& system_, FileSys::VirtualFile file_)
11 : ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) { 11 : ServiceFramework{system_, "IFile"}, backend{std::make_unique<FileSys::Fsa::IFile>(file_)} {
12 // clang-format off
12 static const FunctionInfo functions[] = { 13 static const FunctionInfo functions[] = {
13 {0, &IFile::Read, "Read"}, 14 {0, D<&IFile::Read>, "Read"},
14 {1, &IFile::Write, "Write"}, 15 {1, D<&IFile::Write>, "Write"},
15 {2, &IFile::Flush, "Flush"}, 16 {2, D<&IFile::Flush>, "Flush"},
16 {3, &IFile::SetSize, "SetSize"}, 17 {3, D<&IFile::SetSize>, "SetSize"},
17 {4, &IFile::GetSize, "GetSize"}, 18 {4, D<&IFile::GetSize>, "GetSize"},
18 {5, nullptr, "OperateRange"}, 19 {5, nullptr, "OperateRange"},
19 {6, nullptr, "OperateRangeWithBuffer"}, 20 {6, nullptr, "OperateRangeWithBuffer"},
20 }; 21 };
22 // clang-format on
21 RegisterHandlers(functions); 23 RegisterHandlers(functions);
22} 24}
23 25
24void IFile::Read(HLERequestContext& ctx) { 26Result IFile::Read(
25 IPC::RequestParser rp{ctx}; 27 FileSys::ReadOption option, Out<s64> out_size, s64 offset,
26 const u64 option = rp.Pop<u64>(); 28 const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer,
27 const s64 offset = rp.Pop<s64>(); 29 s64 size) {
28 const s64 length = rp.Pop<s64>(); 30 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
29 31 size);
30 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
31
32 // Error checking
33 if (length < 0) {
34 LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
35 IPC::ResponseBuilder rb{ctx, 2};
36 rb.Push(FileSys::ResultInvalidSize);
37 return;
38 }
39 if (offset < 0) {
40 LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
41 IPC::ResponseBuilder rb{ctx, 2};
42 rb.Push(FileSys::ResultInvalidOffset);
43 return;
44 }
45 32
46 // Read the data from the Storage backend 33 // Read the data from the Storage backend
47 std::vector<u8> output = backend->ReadBytes(length, offset); 34 R_RETURN(
48 35 backend->Read(reinterpret_cast<size_t*>(out_size.Get()), offset, out_buffer.data(), size));
49 // Write the data to memory
50 ctx.WriteBuffer(output);
51
52 IPC::ResponseBuilder rb{ctx, 4};
53 rb.Push(ResultSuccess);
54 rb.Push(static_cast<u64>(output.size()));
55} 36}
56 37
57void IFile::Write(HLERequestContext& ctx) { 38Result IFile::Write(
58 IPC::RequestParser rp{ctx}; 39 const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
59 const u64 option = rp.Pop<u64>(); 40 FileSys::WriteOption option, s64 offset, s64 size) {
60 const s64 offset = rp.Pop<s64>(); 41 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
61 const s64 length = rp.Pop<s64>(); 42 size);
62
63 LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
64
65 // Error checking
66 if (length < 0) {
67 LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
68 IPC::ResponseBuilder rb{ctx, 2};
69 rb.Push(FileSys::ResultInvalidSize);
70 return;
71 }
72 if (offset < 0) {
73 LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
74 IPC::ResponseBuilder rb{ctx, 2};
75 rb.Push(FileSys::ResultInvalidOffset);
76 return;
77 }
78
79 const auto data = ctx.ReadBuffer();
80 43
81 ASSERT_MSG(static_cast<s64>(data.size()) <= length, 44 R_RETURN(backend->Write(offset, buffer.data(), size, option));
82 "Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
83 length, data.size());
84
85 // Write the data to the Storage backend
86 const auto write_size =
87 static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
88 const std::size_t written = backend->Write(data.data(), write_size, offset);
89
90 ASSERT_MSG(static_cast<s64>(written) == length,
91 "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
92 written);
93
94 IPC::ResponseBuilder rb{ctx, 2};
95 rb.Push(ResultSuccess);
96} 45}
97 46
98void IFile::Flush(HLERequestContext& ctx) { 47Result IFile::Flush() {
99 LOG_DEBUG(Service_FS, "called"); 48 LOG_DEBUG(Service_FS, "called");
100 49
101 // Exists for SDK compatibiltity -- No need to flush file. 50 R_RETURN(backend->Flush());
102
103 IPC::ResponseBuilder rb{ctx, 2};
104 rb.Push(ResultSuccess);
105} 51}
106 52
107void IFile::SetSize(HLERequestContext& ctx) { 53Result IFile::SetSize(s64 size) {
108 IPC::RequestParser rp{ctx};
109 const u64 size = rp.Pop<u64>();
110 LOG_DEBUG(Service_FS, "called, size={}", size); 54 LOG_DEBUG(Service_FS, "called, size={}", size);
111 55
112 backend->Resize(size); 56 R_RETURN(backend->SetSize(size));
113
114 IPC::ResponseBuilder rb{ctx, 2};
115 rb.Push(ResultSuccess);
116} 57}
117 58
118void IFile::GetSize(HLERequestContext& ctx) { 59Result IFile::GetSize(Out<s64> out_size) {
119 const u64 size = backend->GetSize(); 60 LOG_DEBUG(Service_FS, "called");
120 LOG_DEBUG(Service_FS, "called, size={}", size);
121 61
122 IPC::ResponseBuilder rb{ctx, 4}; 62 R_RETURN(backend->GetSize(out_size));
123 rb.Push(ResultSuccess);
124 rb.Push<u64>(size);
125} 63}
126 64
127} // namespace Service::FileSystem 65} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_file.h b/src/core/hle/service/filesystem/fsp/fs_i_file.h
index 5e5430c67..e8599ee2f 100644
--- a/src/core/hle/service/filesystem/fsp/fs_i_file.h
+++ b/src/core/hle/service/filesystem/fsp/fs_i_file.h
@@ -3,6 +3,8 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "core/file_sys/fsa/fs_i_file.h"
7#include "core/hle/service/cmif_types.h"
6#include "core/hle/service/filesystem/filesystem.h" 8#include "core/hle/service/filesystem/filesystem.h"
7#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
8 10
@@ -10,16 +12,21 @@ namespace Service::FileSystem {
10 12
11class IFile final : public ServiceFramework<IFile> { 13class IFile final : public ServiceFramework<IFile> {
12public: 14public:
13 explicit IFile(Core::System& system_, FileSys::VirtualFile backend_); 15 explicit IFile(Core::System& system_, FileSys::VirtualFile file_);
14 16
15private: 17private:
16 FileSys::VirtualFile backend; 18 std::unique_ptr<FileSys::Fsa::IFile> backend;
17 19
18 void Read(HLERequestContext& ctx); 20 Result Read(FileSys::ReadOption option, Out<s64> out_size, s64 offset,
19 void Write(HLERequestContext& ctx); 21 const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure>
20 void Flush(HLERequestContext& ctx); 22 out_buffer,
21 void SetSize(HLERequestContext& ctx); 23 s64 size);
22 void GetSize(HLERequestContext& ctx); 24 Result Write(
25 const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
26 FileSys::WriteOption option, s64 offset, s64 size);
27 Result Flush();
28 Result SetSize(s64 size);
29 Result GetSize(Out<s64> out_size);
23}; 30};
24 31
25} // namespace Service::FileSystem 32} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp
index efa394dd1..d881e144d 100644
--- a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp
+++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.cpp
@@ -2,261 +2,172 @@
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include "common/string_util.h" 4#include "common/string_util.h"
5#include "core/file_sys/fssrv/fssrv_sf_path.h"
6#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/filesystem/fsp/fs_i_directory.h" 7#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
6#include "core/hle/service/filesystem/fsp/fs_i_file.h" 8#include "core/hle/service/filesystem/fsp/fs_i_file.h"
7#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h" 9#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h"
8#include "core/hle/service/ipc_helpers.h"
9 10
10namespace Service::FileSystem { 11namespace Service::FileSystem {
11 12
12IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_) 13IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_)
13 : ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move( 14 : ServiceFramework{system_, "IFileSystem"}, backend{std::make_unique<FileSys::Fsa::IFileSystem>(
14 size_)} { 15 dir_)},
16 size_getter{std::move(size_getter_)} {
15 static const FunctionInfo functions[] = { 17 static const FunctionInfo functions[] = {
16 {0, &IFileSystem::CreateFile, "CreateFile"}, 18 {0, D<&IFileSystem::CreateFile>, "CreateFile"},
17 {1, &IFileSystem::DeleteFile, "DeleteFile"}, 19 {1, D<&IFileSystem::DeleteFile>, "DeleteFile"},
18 {2, &IFileSystem::CreateDirectory, "CreateDirectory"}, 20 {2, D<&IFileSystem::CreateDirectory>, "CreateDirectory"},
19 {3, &IFileSystem::DeleteDirectory, "DeleteDirectory"}, 21 {3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
20 {4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"}, 22 {4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
21 {5, &IFileSystem::RenameFile, "RenameFile"}, 23 {5, D<&IFileSystem::RenameFile>, "RenameFile"},
22 {6, nullptr, "RenameDirectory"}, 24 {6, nullptr, "RenameDirectory"},
23 {7, &IFileSystem::GetEntryType, "GetEntryType"}, 25 {7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
24 {8, &IFileSystem::OpenFile, "OpenFile"}, 26 {8, D<&IFileSystem::OpenFile>, "OpenFile"},
25 {9, &IFileSystem::OpenDirectory, "OpenDirectory"}, 27 {9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
26 {10, &IFileSystem::Commit, "Commit"}, 28 {10, D<&IFileSystem::Commit>, "Commit"},
27 {11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"}, 29 {11, D<&IFileSystem::GetFreeSpaceSize>, "GetFreeSpaceSize"},
28 {12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"}, 30 {12, D<&IFileSystem::GetTotalSpaceSize>, "GetTotalSpaceSize"},
29 {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"}, 31 {13, D<&IFileSystem::CleanDirectoryRecursively>, "CleanDirectoryRecursively"},
30 {14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"}, 32 {14, D<&IFileSystem::GetFileTimeStampRaw>, "GetFileTimeStampRaw"},
31 {15, nullptr, "QueryEntry"}, 33 {15, nullptr, "QueryEntry"},
32 {16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"}, 34 {16, D<&IFileSystem::GetFileSystemAttribute>, "GetFileSystemAttribute"},
33 }; 35 };
34 RegisterHandlers(functions); 36 RegisterHandlers(functions);
35} 37}
36 38
37void IFileSystem::CreateFile(HLERequestContext& ctx) { 39Result IFileSystem::CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
38 IPC::RequestParser rp{ctx}; 40 s32 option, s64 size) {
41 LOG_DEBUG(Service_FS, "called. file={}, option=0x{:X}, size=0x{:08X}", path->str, option, size);
39 42
40 const auto file_buffer = ctx.ReadBuffer(); 43 R_RETURN(backend->CreateFile(FileSys::Path(path->str), size));
41 const std::string name = Common::StringFromBuffer(file_buffer);
42
43 const u64 file_mode = rp.Pop<u64>();
44 const u32 file_size = rp.Pop<u32>();
45
46 LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode,
47 file_size);
48
49 IPC::ResponseBuilder rb{ctx, 2};
50 rb.Push(backend.CreateFile(name, file_size));
51} 44}
52 45
53void IFileSystem::DeleteFile(HLERequestContext& ctx) { 46Result IFileSystem::DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
54 const auto file_buffer = ctx.ReadBuffer(); 47 LOG_DEBUG(Service_FS, "called. file={}", path->str);
55 const std::string name = Common::StringFromBuffer(file_buffer);
56 48
57 LOG_DEBUG(Service_FS, "called. file={}", name); 49 R_RETURN(backend->DeleteFile(FileSys::Path(path->str)));
58
59 IPC::ResponseBuilder rb{ctx, 2};
60 rb.Push(backend.DeleteFile(name));
61} 50}
62 51
63void IFileSystem::CreateDirectory(HLERequestContext& ctx) { 52Result IFileSystem::CreateDirectory(
64 const auto file_buffer = ctx.ReadBuffer(); 53 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
65 const std::string name = Common::StringFromBuffer(file_buffer); 54 LOG_DEBUG(Service_FS, "called. directory={}", path->str);
66
67 LOG_DEBUG(Service_FS, "called. directory={}", name);
68 55
69 IPC::ResponseBuilder rb{ctx, 2}; 56 R_RETURN(backend->CreateDirectory(FileSys::Path(path->str)));
70 rb.Push(backend.CreateDirectory(name));
71} 57}
72 58
73void IFileSystem::DeleteDirectory(HLERequestContext& ctx) { 59Result IFileSystem::DeleteDirectory(
74 const auto file_buffer = ctx.ReadBuffer(); 60 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
75 const std::string name = Common::StringFromBuffer(file_buffer); 61 LOG_DEBUG(Service_FS, "called. directory={}", path->str);
76
77 LOG_DEBUG(Service_FS, "called. directory={}", name);
78 62
79 IPC::ResponseBuilder rb{ctx, 2}; 63 R_RETURN(backend->DeleteDirectory(FileSys::Path(path->str)));
80 rb.Push(backend.DeleteDirectory(name));
81} 64}
82 65
83void IFileSystem::DeleteDirectoryRecursively(HLERequestContext& ctx) { 66Result IFileSystem::DeleteDirectoryRecursively(
84 const auto file_buffer = ctx.ReadBuffer(); 67 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
85 const std::string name = Common::StringFromBuffer(file_buffer); 68 LOG_DEBUG(Service_FS, "called. directory={}", path->str);
86 69
87 LOG_DEBUG(Service_FS, "called. directory={}", name); 70 R_RETURN(backend->DeleteDirectoryRecursively(FileSys::Path(path->str)));
88
89 IPC::ResponseBuilder rb{ctx, 2};
90 rb.Push(backend.DeleteDirectoryRecursively(name));
91} 71}
92 72
93void IFileSystem::CleanDirectoryRecursively(HLERequestContext& ctx) { 73Result IFileSystem::CleanDirectoryRecursively(
94 const auto file_buffer = ctx.ReadBuffer(); 74 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
95 const std::string name = Common::StringFromBuffer(file_buffer); 75 LOG_DEBUG(Service_FS, "called. Directory: {}", path->str);
96
97 LOG_DEBUG(Service_FS, "called. Directory: {}", name);
98 76
99 IPC::ResponseBuilder rb{ctx, 2}; 77 R_RETURN(backend->CleanDirectoryRecursively(FileSys::Path(path->str)));
100 rb.Push(backend.CleanDirectoryRecursively(name));
101} 78}
102 79
103void IFileSystem::RenameFile(HLERequestContext& ctx) { 80Result IFileSystem::RenameFile(
104 const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0)); 81 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
105 const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1)); 82 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path) {
106 83 LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", old_path->str, new_path->str);
107 LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
108 84
109 IPC::ResponseBuilder rb{ctx, 2}; 85 R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
110 rb.Push(backend.RenameFile(src_name, dst_name));
111} 86}
112 87
113void IFileSystem::OpenFile(HLERequestContext& ctx) { 88Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
114 IPC::RequestParser rp{ctx}; 89 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
115 90 u32 mode) {
116 const auto file_buffer = ctx.ReadBuffer(); 91 LOG_DEBUG(Service_FS, "called. file={}, mode={}", path->str, mode);
117 const std::string name = Common::StringFromBuffer(file_buffer);
118
119 const auto mode = static_cast<FileSys::OpenMode>(rp.Pop<u32>());
120
121 LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode);
122 92
123 FileSys::VirtualFile vfs_file{}; 93 FileSys::VirtualFile vfs_file{};
124 auto result = backend.OpenFile(&vfs_file, name, mode); 94 R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(path->str),
125 if (result != ResultSuccess) { 95 static_cast<FileSys::OpenMode>(mode)));
126 IPC::ResponseBuilder rb{ctx, 2};
127 rb.Push(result);
128 return;
129 }
130
131 auto file = std::make_shared<IFile>(system, vfs_file);
132
133 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
134 rb.Push(ResultSuccess);
135 rb.PushIpcInterface<IFile>(std::move(file));
136}
137
138void IFileSystem::OpenDirectory(HLERequestContext& ctx) {
139 IPC::RequestParser rp{ctx};
140 96
141 const auto file_buffer = ctx.ReadBuffer(); 97 *out_interface = std::make_shared<IFile>(system, vfs_file);
142 const std::string name = Common::StringFromBuffer(file_buffer); 98 R_SUCCEED();
143 const auto mode = rp.PopRaw<FileSys::OpenDirectoryMode>(); 99}
144 100
145 LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode); 101Result IFileSystem::OpenDirectory(OutInterface<IDirectory> out_interface,
102 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
103 u32 mode) {
104 LOG_DEBUG(Service_FS, "called. directory={}, mode={}", path->str, mode);
146 105
147 FileSys::VirtualDir vfs_dir{}; 106 FileSys::VirtualDir vfs_dir{};
148 auto result = backend.OpenDirectory(&vfs_dir, name); 107 R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(path->str),
149 if (result != ResultSuccess) { 108 static_cast<FileSys::OpenDirectoryMode>(mode)));
150 IPC::ResponseBuilder rb{ctx, 2};
151 rb.Push(result);
152 return;
153 }
154
155 auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode);
156
157 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
158 rb.Push(ResultSuccess);
159 rb.PushIpcInterface<IDirectory>(std::move(directory));
160}
161 109
162void IFileSystem::GetEntryType(HLERequestContext& ctx) { 110 *out_interface = std::make_shared<IDirectory>(system, vfs_dir,
163 const auto file_buffer = ctx.ReadBuffer(); 111 static_cast<FileSys::OpenDirectoryMode>(mode));
164 const std::string name = Common::StringFromBuffer(file_buffer); 112 R_SUCCEED();
113}
165 114
166 LOG_DEBUG(Service_FS, "called. file={}", name); 115Result IFileSystem::GetEntryType(
116 Out<u32> out_type, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
117 LOG_DEBUG(Service_FS, "called. file={}", path->str);
167 118
168 FileSys::DirectoryEntryType vfs_entry_type{}; 119 FileSys::DirectoryEntryType vfs_entry_type{};
169 auto result = backend.GetEntryType(&vfs_entry_type, name); 120 R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(path->str)));
170 if (result != ResultSuccess) { 121
171 IPC::ResponseBuilder rb{ctx, 2}; 122 *out_type = static_cast<u32>(vfs_entry_type);
172 rb.Push(result); 123 R_SUCCEED();
173 return;
174 }
175
176 IPC::ResponseBuilder rb{ctx, 3};
177 rb.Push(ResultSuccess);
178 rb.Push<u32>(static_cast<u32>(vfs_entry_type));
179} 124}
180 125
181void IFileSystem::Commit(HLERequestContext& ctx) { 126Result IFileSystem::Commit() {
182 LOG_WARNING(Service_FS, "(STUBBED) called"); 127 LOG_WARNING(Service_FS, "(STUBBED) called");
183 128
184 IPC::ResponseBuilder rb{ctx, 2}; 129 R_SUCCEED();
185 rb.Push(ResultSuccess);
186} 130}
187 131
188void IFileSystem::GetFreeSpaceSize(HLERequestContext& ctx) { 132Result IFileSystem::GetFreeSpaceSize(
133 Out<s64> out_size, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
189 LOG_DEBUG(Service_FS, "called"); 134 LOG_DEBUG(Service_FS, "called");
190 135
191 IPC::ResponseBuilder rb{ctx, 4}; 136 *out_size = size_getter.get_free_size();
192 rb.Push(ResultSuccess); 137 R_SUCCEED();
193 rb.Push(size.get_free_size());
194} 138}
195 139
196void IFileSystem::GetTotalSpaceSize(HLERequestContext& ctx) { 140Result IFileSystem::GetTotalSpaceSize(
141 Out<s64> out_size, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
197 LOG_DEBUG(Service_FS, "called"); 142 LOG_DEBUG(Service_FS, "called");
198 143
199 IPC::ResponseBuilder rb{ctx, 4}; 144 *out_size = size_getter.get_total_size();
200 rb.Push(ResultSuccess); 145 R_SUCCEED();
201 rb.Push(size.get_total_size());
202} 146}
203 147
204void IFileSystem::GetFileTimeStampRaw(HLERequestContext& ctx) { 148Result IFileSystem::GetFileTimeStampRaw(
205 const auto file_buffer = ctx.ReadBuffer(); 149 Out<FileSys::FileTimeStampRaw> out_timestamp,
206 const std::string name = Common::StringFromBuffer(file_buffer); 150 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
207 151 LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", path->str);
208 LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
209 152
210 FileSys::FileTimeStampRaw vfs_timestamp{}; 153 FileSys::FileTimeStampRaw vfs_timestamp{};
211 auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name); 154 R_TRY(backend->GetFileTimeStampRaw(&vfs_timestamp, FileSys::Path(path->str)));
212 if (result != ResultSuccess) { 155
213 IPC::ResponseBuilder rb{ctx, 2}; 156 *out_timestamp = vfs_timestamp;
214 rb.Push(result); 157 R_SUCCEED();
215 return;
216 }
217
218 IPC::ResponseBuilder rb{ctx, 10};
219 rb.Push(ResultSuccess);
220 rb.PushRaw(vfs_timestamp);
221} 158}
222 159
223void IFileSystem::GetFileSystemAttribute(HLERequestContext& ctx) { 160Result IFileSystem::GetFileSystemAttribute(Out<FileSys::FileSystemAttribute> out_attribute) {
224 LOG_WARNING(Service_FS, "(STUBBED) called"); 161 LOG_WARNING(Service_FS, "(STUBBED) called");
225 162
226 struct FileSystemAttribute { 163 FileSys::FileSystemAttribute savedata_attribute{};
227 u8 dir_entry_name_length_max_defined;
228 u8 file_entry_name_length_max_defined;
229 u8 dir_path_name_length_max_defined;
230 u8 file_path_name_length_max_defined;
231 INSERT_PADDING_BYTES_NOINIT(0x5);
232 u8 utf16_dir_entry_name_length_max_defined;
233 u8 utf16_file_entry_name_length_max_defined;
234 u8 utf16_dir_path_name_length_max_defined;
235 u8 utf16_file_path_name_length_max_defined;
236 INSERT_PADDING_BYTES_NOINIT(0x18);
237 s32 dir_entry_name_length_max;
238 s32 file_entry_name_length_max;
239 s32 dir_path_name_length_max;
240 s32 file_path_name_length_max;
241 INSERT_PADDING_WORDS_NOINIT(0x5);
242 s32 utf16_dir_entry_name_length_max;
243 s32 utf16_file_entry_name_length_max;
244 s32 utf16_dir_path_name_length_max;
245 s32 utf16_file_path_name_length_max;
246 INSERT_PADDING_WORDS_NOINIT(0x18);
247 INSERT_PADDING_WORDS_NOINIT(0x1);
248 };
249 static_assert(sizeof(FileSystemAttribute) == 0xc0, "FileSystemAttribute has incorrect size");
250
251 FileSystemAttribute savedata_attribute{};
252 savedata_attribute.dir_entry_name_length_max_defined = true; 164 savedata_attribute.dir_entry_name_length_max_defined = true;
253 savedata_attribute.file_entry_name_length_max_defined = true; 165 savedata_attribute.file_entry_name_length_max_defined = true;
254 savedata_attribute.dir_entry_name_length_max = 0x40; 166 savedata_attribute.dir_entry_name_length_max = 0x40;
255 savedata_attribute.file_entry_name_length_max = 0x40; 167 savedata_attribute.file_entry_name_length_max = 0x40;
256 168
257 IPC::ResponseBuilder rb{ctx, 50}; 169 *out_attribute = savedata_attribute;
258 rb.Push(ResultSuccess); 170 R_SUCCEED();
259 rb.PushRaw(savedata_attribute);
260} 171}
261 172
262} // namespace Service::FileSystem 173} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h
index b06b3ef0e..113369203 100644
--- a/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h
+++ b/src/core/hle/service/filesystem/fsp/fs_i_filesystem.h
@@ -3,36 +3,58 @@
3 3
4#pragma once 4#pragma once
5 5
6#include "common/common_funcs.h"
7#include "core/file_sys/fs_filesystem.h"
8#include "core/file_sys/fsa/fs_i_filesystem.h"
6#include "core/file_sys/vfs/vfs.h" 9#include "core/file_sys/vfs/vfs.h"
10#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/filesystem/filesystem.h" 11#include "core/hle/service/filesystem/filesystem.h"
8#include "core/hle/service/filesystem/fsp/fsp_util.h" 12#include "core/hle/service/filesystem/fsp/fsp_util.h"
9#include "core/hle/service/service.h" 13#include "core/hle/service/service.h"
10 14
15namespace FileSys::Sf {
16struct Path;
17}
18
11namespace Service::FileSystem { 19namespace Service::FileSystem {
12 20
21class IFile;
22class IDirectory;
23
13class IFileSystem final : public ServiceFramework<IFileSystem> { 24class IFileSystem final : public ServiceFramework<IFileSystem> {
14public: 25public:
15 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_); 26 explicit IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_);
16 27
17 void CreateFile(HLERequestContext& ctx); 28 Result CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, s32 option,
18 void DeleteFile(HLERequestContext& ctx); 29 s64 size);
19 void CreateDirectory(HLERequestContext& ctx); 30 Result DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
20 void DeleteDirectory(HLERequestContext& ctx); 31 Result CreateDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
21 void DeleteDirectoryRecursively(HLERequestContext& ctx); 32 Result DeleteDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
22 void CleanDirectoryRecursively(HLERequestContext& ctx); 33 Result DeleteDirectoryRecursively(
23 void RenameFile(HLERequestContext& ctx); 34 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
24 void OpenFile(HLERequestContext& ctx); 35 Result CleanDirectoryRecursively(
25 void OpenDirectory(HLERequestContext& ctx); 36 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
26 void GetEntryType(HLERequestContext& ctx); 37 Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
27 void Commit(HLERequestContext& ctx); 38 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
28 void GetFreeSpaceSize(HLERequestContext& ctx); 39 Result OpenFile(OutInterface<IFile> out_interface,
29 void GetTotalSpaceSize(HLERequestContext& ctx); 40 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
30 void GetFileTimeStampRaw(HLERequestContext& ctx); 41 Result OpenDirectory(OutInterface<IDirectory> out_interface,
31 void GetFileSystemAttribute(HLERequestContext& ctx); 42 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
43 u32 mode);
44 Result GetEntryType(Out<u32> out_type,
45 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
46 Result Commit();
47 Result GetFreeSpaceSize(Out<s64> out_size,
48 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
49 Result GetTotalSpaceSize(Out<s64> out_size,
50 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
51 Result GetFileTimeStampRaw(Out<FileSys::FileTimeStampRaw> out_timestamp,
52 const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
53 Result GetFileSystemAttribute(Out<FileSys::FileSystemAttribute> out_attribute);
32 54
33private: 55private:
34 VfsDirectoryServiceWrapper backend; 56 std::unique_ptr<FileSys::Fsa::IFileSystem> backend;
35 SizeGetter size; 57 SizeGetter size_getter;
36}; 58};
37 59
38} // namespace Service::FileSystem 60} // namespace Service::FileSystem
diff --git a/src/core/hle/service/psc/ovln/ovln_types.h b/src/core/hle/service/psc/ovln/ovln_types.h
new file mode 100644
index 000000000..343b05dcc
--- /dev/null
+++ b/src/core/hle/service/psc/ovln/ovln_types.h
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "common/bit_field.h"
7#include "common/common_types.h"
8
9namespace Service::PSC {
10
11using OverlayNotification = std::array<u64, 0x10>;
12static_assert(sizeof(OverlayNotification) == 0x80, "OverlayNotification has incorrect size");
13
14union MessageFlags {
15 u64 raw;
16 BitField<0, 8, u64> message_type;
17 BitField<8, 8, u64> queue_type;
18};
19static_assert(sizeof(MessageFlags) == 0x8, "MessageFlags has incorrect size");
20
21} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/ovln/receiver.cpp b/src/core/hle/service/psc/ovln/receiver.cpp
new file mode 100644
index 000000000..85f62816d
--- /dev/null
+++ b/src/core/hle/service/psc/ovln/receiver.cpp
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/ovln/receiver.h"
5
6namespace Service::PSC {
7
8IReceiver::IReceiver(Core::System& system_) : ServiceFramework{system_, "IReceiver"} {
9 // clang-format off
10 static const FunctionInfo functions[] = {
11 {0, nullptr, "AddSource"},
12 {1, nullptr, "RemoveSource"},
13 {2, nullptr, "GetReceiveEventHandle"},
14 {3, nullptr, "Receive"},
15 {4, nullptr, "ReceiveWithTick"},
16 };
17 // clang-format on
18
19 RegisterHandlers(functions);
20}
21
22IReceiver::~IReceiver() = default;
23
24} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/ovln/receiver.h b/src/core/hle/service/psc/ovln/receiver.h
new file mode 100644
index 000000000..c47a4ff7e
--- /dev/null
+++ b/src/core/hle/service/psc/ovln/receiver.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::PSC {
9
10class IReceiver final : public ServiceFramework<IReceiver> {
11public:
12 explicit IReceiver(Core::System& system_);
13 ~IReceiver() override;
14};
15
16} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/ovln/receiver_service.cpp b/src/core/hle/service/psc/ovln/receiver_service.cpp
new file mode 100644
index 000000000..bb988e905
--- /dev/null
+++ b/src/core/hle/service/psc/ovln/receiver_service.cpp
@@ -0,0 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/psc/ovln/receiver.h"
6#include "core/hle/service/psc/ovln/receiver_service.h"
7
8namespace Service::PSC {
9
10IReceiverService::IReceiverService(Core::System& system_) : ServiceFramework{system_, "ovln:rcv"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, D<&IReceiverService::OpenReceiver>, "OpenReceiver"},
14 };
15 // clang-format on
16
17 RegisterHandlers(functions);
18}
19
20IReceiverService::~IReceiverService() = default;
21
22Result IReceiverService::OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver) {
23 LOG_DEBUG(Service_PSC, "called");
24 *out_receiver = std::make_shared<IReceiver>(system);
25 R_SUCCEED();
26}
27
28} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/ovln/receiver_service.h b/src/core/hle/service/psc/ovln/receiver_service.h
new file mode 100644
index 000000000..b3b31ba4a
--- /dev/null
+++ b/src/core/hle/service/psc/ovln/receiver_service.h
@@ -0,0 +1,22 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Service::PSC {
10
11class IReceiver;
12
13class IReceiverService final : public ServiceFramework<IReceiverService> {
14public:
15 explicit IReceiverService(Core::System& system_);
16 ~IReceiverService() override;
17
18private:
19 Result OpenReceiver(Out<SharedPointer<IReceiver>> out_receiver);
20};
21
22} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/ovln/sender.cpp b/src/core/hle/service/psc/ovln/sender.cpp
new file mode 100644
index 000000000..3227a56f2
--- /dev/null
+++ b/src/core/hle/service/psc/ovln/sender.cpp
@@ -0,0 +1,32 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/psc/ovln/sender.h"
6
7namespace Service::PSC {
8
9ISender::ISender(Core::System& system_) : ServiceFramework{system_, "ISender"} {
10 // clang-format off
11 static const FunctionInfo functions[] = {
12 {0, D<&ISender::Send>, "Send"},
13 {1, nullptr, "GetUnreceivedMessageCount"},
14 };
15 // clang-format on
16
17 RegisterHandlers(functions);
18}
19
20ISender::~ISender() = default;
21
22Result ISender::Send(const OverlayNotification& notification, MessageFlags flags) {
23 std::string data;
24 for (const auto m : notification) {
25 data += fmt::format("{:016X} ", m);
26 }
27
28 LOG_WARNING(Service_PSC, "(STUBBED) called, flags={} notification={}", flags.raw, data);
29 R_SUCCEED();
30}
31
32} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/ovln/sender.h b/src/core/hle/service/psc/ovln/sender.h
new file mode 100644
index 000000000..c1575428e
--- /dev/null
+++ b/src/core/hle/service/psc/ovln/sender.h
@@ -0,0 +1,21 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/psc/ovln/ovln_types.h"
8#include "core/hle/service/service.h"
9
10namespace Service::PSC {
11
12class ISender final : public ServiceFramework<ISender> {
13public:
14 explicit ISender(Core::System& system_);
15 ~ISender() override;
16
17private:
18 Result Send(const OverlayNotification& notification, MessageFlags flags);
19};
20
21} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/ovln/sender_service.cpp b/src/core/hle/service/psc/ovln/sender_service.cpp
new file mode 100644
index 000000000..18d2c83a3
--- /dev/null
+++ b/src/core/hle/service/psc/ovln/sender_service.cpp
@@ -0,0 +1,30 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/psc/ovln/sender.h"
6#include "core/hle/service/psc/ovln/sender_service.h"
7
8namespace Service::PSC {
9
10ISenderService::ISenderService(Core::System& system_) : ServiceFramework{system_, "ovln:snd"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, D<&ISenderService::OpenSender>, "OpenSender"},
14 };
15 // clang-format on
16
17 RegisterHandlers(functions);
18}
19
20ISenderService::~ISenderService() = default;
21
22Result ISenderService::OpenSender(Out<SharedPointer<ISender>> out_sender, u32 sender_id,
23 std::array<u64, 2> data) {
24 LOG_WARNING(Service_PSC, "(STUBBED) called, sender_id={}, data={:016X} {:016X}", sender_id,
25 data[0], data[1]);
26 *out_sender = std::make_shared<ISender>(system);
27 R_SUCCEED();
28}
29
30} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/ovln/sender_service.h b/src/core/hle/service/psc/ovln/sender_service.h
new file mode 100644
index 000000000..10027701f
--- /dev/null
+++ b/src/core/hle/service/psc/ovln/sender_service.h
@@ -0,0 +1,23 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Service::PSC {
10
11class ISender;
12
13class ISenderService final : public ServiceFramework<ISenderService> {
14public:
15 explicit ISenderService(Core::System& system_);
16 ~ISenderService() override;
17
18private:
19 Result OpenSender(Out<SharedPointer<ISender>> out_sender, u32 sender_id,
20 std::array<u64, 2> data);
21};
22
23} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/pm_control.cpp b/src/core/hle/service/psc/pm_control.cpp
new file mode 100644
index 000000000..7dedb7662
--- /dev/null
+++ b/src/core/hle/service/psc/pm_control.cpp
@@ -0,0 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/pm_control.h"
5
6namespace Service::PSC {
7
8IPmControl::IPmControl(Core::System& system_) : ServiceFramework{system_, "psc:c"} {
9 // clang-format off
10 static const FunctionInfo functions[] = {
11 {0, nullptr, "Initialize"},
12 {1, nullptr, "DispatchRequest"},
13 {2, nullptr, "GetResult"},
14 {3, nullptr, "GetState"},
15 {4, nullptr, "Cancel"},
16 {5, nullptr, "PrintModuleInformation"},
17 {6, nullptr, "GetModuleInformation"},
18 {10, nullptr, "AcquireStateLock"},
19 {11, nullptr, "HasStateLock"},
20 };
21 // clang-format on
22
23 RegisterHandlers(functions);
24}
25
26IPmControl::~IPmControl() = default;
27
28} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/pm_control.h b/src/core/hle/service/psc/pm_control.h
new file mode 100644
index 000000000..e0ae2f39c
--- /dev/null
+++ b/src/core/hle/service/psc/pm_control.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::PSC {
9
10class IPmControl final : public ServiceFramework<IPmControl> {
11public:
12 explicit IPmControl(Core::System& system_);
13 ~IPmControl() override;
14};
15
16} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/pm_module.cpp b/src/core/hle/service/psc/pm_module.cpp
new file mode 100644
index 000000000..74dc7ed4e
--- /dev/null
+++ b/src/core/hle/service/psc/pm_module.cpp
@@ -0,0 +1,24 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/psc/pm_module.h"
5
6namespace Service::PSC {
7
8IPmModule::IPmModule(Core::System& system_) : ServiceFramework{system_, "IPmModule"} {
9 // clang-format off
10 static const FunctionInfo functions[] = {
11 {0, nullptr, "Initialize"},
12 {1, nullptr, "GetRequest"},
13 {2, nullptr, "Acknowledge"},
14 {3, nullptr, "Finalize"},
15 {4, nullptr, "AcknowledgeEx"},
16 };
17 // clang-format on
18
19 RegisterHandlers(functions);
20}
21
22IPmModule::~IPmModule() = default;
23
24} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/pm_module.h b/src/core/hle/service/psc/pm_module.h
new file mode 100644
index 000000000..b3a2d2584
--- /dev/null
+++ b/src/core/hle/service/psc/pm_module.h
@@ -0,0 +1,16 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/service.h"
7
8namespace Service::PSC {
9
10class IPmModule final : public ServiceFramework<IPmModule> {
11public:
12 explicit IPmModule(Core::System& system_);
13 ~IPmModule() override;
14};
15
16} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/pm_service.cpp b/src/core/hle/service/psc/pm_service.cpp
new file mode 100644
index 000000000..c4e0ad228
--- /dev/null
+++ b/src/core/hle/service/psc/pm_service.cpp
@@ -0,0 +1,28 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include "core/hle/service/cmif_serialization.h"
5#include "core/hle/service/psc/pm_module.h"
6#include "core/hle/service/psc/pm_service.h"
7
8namespace Service::PSC {
9
10IPmService::IPmService(Core::System& system_) : ServiceFramework{system_, "psc:m"} {
11 // clang-format off
12 static const FunctionInfo functions[] = {
13 {0, D<&IPmService::GetPmModule>, "GetPmModule"},
14 };
15 // clang-format on
16
17 RegisterHandlers(functions);
18}
19
20IPmService::~IPmService() = default;
21
22Result IPmService::GetPmModule(Out<SharedPointer<IPmModule>> out_module) {
23 LOG_DEBUG(Service_PSC, "called");
24 *out_module = std::make_shared<IPmModule>(system);
25 R_SUCCEED();
26}
27
28} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/pm_service.h b/src/core/hle/service/psc/pm_service.h
new file mode 100644
index 000000000..08e14c6f8
--- /dev/null
+++ b/src/core/hle/service/psc/pm_service.h
@@ -0,0 +1,22 @@
1// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include "core/hle/service/cmif_types.h"
7#include "core/hle/service/service.h"
8
9namespace Service::PSC {
10
11class IPmModule;
12
13class IPmService final : public ServiceFramework<IPmService> {
14public:
15 explicit IPmService(Core::System& system_);
16 ~IPmService() override;
17
18private:
19 Result GetPmModule(Out<SharedPointer<IPmModule>> out_module);
20};
21
22} // namespace Service::PSC
diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp
index 44310756b..e1762d694 100644
--- a/src/core/hle/service/psc/psc.cpp
+++ b/src/core/hle/service/psc/psc.cpp
@@ -1,11 +1,10 @@
1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project 1// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later 2// SPDX-License-Identifier: GPL-2.0-or-later
3 3
4#include <memory> 4#include "core/hle/service/psc/ovln/receiver_service.h"
5 5#include "core/hle/service/psc/ovln/sender_service.h"
6#include "common/logging/log.h" 6#include "core/hle/service/psc/pm_control.h"
7#include "core/core.h" 7#include "core/hle/service/psc/pm_service.h"
8#include "core/hle/service/ipc_helpers.h"
9#include "core/hle/service/psc/psc.h" 8#include "core/hle/service/psc/psc.h"
10#include "core/hle/service/psc/time/manager.h" 9#include "core/hle/service/psc/time/manager.h"
11#include "core/hle/service/psc/time/power_state_service.h" 10#include "core/hle/service/psc/time/power_state_service.h"
@@ -15,71 +14,13 @@
15 14
16namespace Service::PSC { 15namespace Service::PSC {
17 16
18class IPmControl final : public ServiceFramework<IPmControl> {
19public:
20 explicit IPmControl(Core::System& system_) : ServiceFramework{system_, "psc:c"} {
21 // clang-format off
22 static const FunctionInfo functions[] = {
23 {0, nullptr, "Initialize"},
24 {1, nullptr, "DispatchRequest"},
25 {2, nullptr, "GetResult"},
26 {3, nullptr, "GetState"},
27 {4, nullptr, "Cancel"},
28 {5, nullptr, "PrintModuleInformation"},
29 {6, nullptr, "GetModuleInformation"},
30 {10, nullptr, "AcquireStateLock"},
31 {11, nullptr, "HasStateLock"},
32 };
33 // clang-format on
34
35 RegisterHandlers(functions);
36 }
37};
38
39class IPmModule final : public ServiceFramework<IPmModule> {
40public:
41 explicit IPmModule(Core::System& system_) : ServiceFramework{system_, "IPmModule"} {
42 // clang-format off
43 static const FunctionInfo functions[] = {
44 {0, nullptr, "Initialize"},
45 {1, nullptr, "GetRequest"},
46 {2, nullptr, "Acknowledge"},
47 {3, nullptr, "Finalize"},
48 {4, nullptr, "AcknowledgeEx"},
49 };
50 // clang-format on
51
52 RegisterHandlers(functions);
53 }
54};
55
56class IPmService final : public ServiceFramework<IPmService> {
57public:
58 explicit IPmService(Core::System& system_) : ServiceFramework{system_, "psc:m"} {
59 // clang-format off
60 static const FunctionInfo functions[] = {
61 {0, &IPmService::GetPmModule, "GetPmModule"},
62 };
63 // clang-format on
64
65 RegisterHandlers(functions);
66 }
67
68private:
69 void GetPmModule(HLERequestContext& ctx) {
70 LOG_DEBUG(Service_PSC, "called");
71
72 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
73 rb.Push(ResultSuccess);
74 rb.PushIpcInterface<IPmModule>(system);
75 }
76};
77
78void LoopProcess(Core::System& system) { 17void LoopProcess(Core::System& system) {
79 auto server_manager = std::make_unique<ServerManager>(system); 18 auto server_manager = std::make_unique<ServerManager>(system);
80 19
81 server_manager->RegisterNamedService("psc:c", std::make_shared<IPmControl>(system)); 20 server_manager->RegisterNamedService("psc:c", std::make_shared<IPmControl>(system));
82 server_manager->RegisterNamedService("psc:m", std::make_shared<IPmService>(system)); 21 server_manager->RegisterNamedService("psc:m", std::make_shared<IPmService>(system));
22 server_manager->RegisterNamedService("ovln:rcv", std::make_shared<IReceiverService>(system));
23 server_manager->RegisterNamedService("ovln:snd", std::make_shared<ISenderService>(system));
83 24
84 auto time = std::make_shared<Time::TimeManager>(system); 25 auto time = std::make_shared<Time::TimeManager>(system);
85 26
diff --git a/src/core/hle/service/psc/psc.h b/src/core/hle/service/psc/psc.h
index 459137f42..c83d07ca8 100644
--- a/src/core/hle/service/psc/psc.h
+++ b/src/core/hle/service/psc/psc.h
@@ -7,10 +7,6 @@ namespace Core {
7class System; 7class System;
8} 8}
9 9
10namespace Service::SM {
11class ServiceManager;
12}
13
14namespace Service::PSC { 10namespace Service::PSC {
15 11
16void LoopProcess(Core::System& system); 12void LoopProcess(Core::System& system);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index 0031fa5fb..3f9698d6b 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -261,7 +261,9 @@ void DefineEntryPoint(const IR::Program& program, EmitContext& ctx, Id main) {
261 case Stage::Geometry: 261 case Stage::Geometry:
262 execution_model = spv::ExecutionModel::Geometry; 262 execution_model = spv::ExecutionModel::Geometry;
263 ctx.AddCapability(spv::Capability::Geometry); 263 ctx.AddCapability(spv::Capability::Geometry);
264 ctx.AddCapability(spv::Capability::GeometryStreams); 264 if (ctx.profile.support_geometry_streams) {
265 ctx.AddCapability(spv::Capability::GeometryStreams);
266 }
265 switch (ctx.runtime_info.input_topology) { 267 switch (ctx.runtime_info.input_topology) {
266 case InputTopology::Points: 268 case InputTopology::Points:
267 ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints); 269 ctx.AddExecutionMode(main, spv::ExecutionMode::InputPoints);
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
index 9f7b6bb4b..f60da758e 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_special.cpp
@@ -129,7 +129,9 @@ void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
129 if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) { 129 if (ctx.runtime_info.convert_depth_mode && !ctx.profile.support_native_ndc) {
130 ConvertDepthMode(ctx); 130 ConvertDepthMode(ctx);
131 } 131 }
132 if (stream.IsImmediate()) { 132 if (!ctx.profile.support_geometry_streams) {
133 throw NotImplementedException("Geometry streams");
134 } else if (stream.IsImmediate()) {
133 ctx.OpEmitStreamVertex(ctx.Def(stream)); 135 ctx.OpEmitStreamVertex(ctx.Def(stream));
134 } else { 136 } else {
135 LOG_WARNING(Shader_SPIRV, "Stream is not immediate"); 137 LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
@@ -140,7 +142,9 @@ void EmitEmitVertex(EmitContext& ctx, const IR::Value& stream) {
140} 142}
141 143
142void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) { 144void EmitEndPrimitive(EmitContext& ctx, const IR::Value& stream) {
143 if (stream.IsImmediate()) { 145 if (!ctx.profile.support_geometry_streams) {
146 throw NotImplementedException("Geometry streams");
147 } else if (stream.IsImmediate()) {
144 ctx.OpEndStreamPrimitive(ctx.Def(stream)); 148 ctx.OpEndStreamPrimitive(ctx.Def(stream));
145 } else { 149 } else {
146 LOG_WARNING(Shader_SPIRV, "Stream is not immediate"); 150 LOG_WARNING(Shader_SPIRV, "Stream is not immediate");
diff --git a/src/shader_recompiler/profile.h b/src/shader_recompiler/profile.h
index 7578d41cc..90e46bb1b 100644
--- a/src/shader_recompiler/profile.h
+++ b/src/shader_recompiler/profile.h
@@ -44,6 +44,7 @@ struct Profile {
44 bool support_gl_derivative_control{}; 44 bool support_gl_derivative_control{};
45 bool support_scaled_attributes{}; 45 bool support_scaled_attributes{};
46 bool support_multi_viewport{}; 46 bool support_multi_viewport{};
47 bool support_geometry_streams{};
47 48
48 bool warp_size_potentially_larger_than_guest{}; 49 bool warp_size_potentially_larger_than_guest{};
49 50
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index 20f7a9702..d34b585d6 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -352,6 +352,7 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
352 .support_native_ndc = device.IsExtDepthClipControlSupported(), 352 .support_native_ndc = device.IsExtDepthClipControlSupported(),
353 .support_scaled_attributes = !device.MustEmulateScaledFormats(), 353 .support_scaled_attributes = !device.MustEmulateScaledFormats(),
354 .support_multi_viewport = device.SupportsMultiViewport(), 354 .support_multi_viewport = device.SupportsMultiViewport(),
355 .support_geometry_streams = device.AreTransformFeedbackGeometryStreamsSupported(),
355 356
356 .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(), 357 .warp_size_potentially_larger_than_guest = device.IsWarpSizePotentiallyBiggerThanGuest(),
357 358
diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp
index d7216d349..b94924a58 100644
--- a/src/video_core/vulkan_common/vulkan_device.cpp
+++ b/src/video_core/vulkan_common/vulkan_device.cpp
@@ -1297,10 +1297,6 @@ u64 Device::GetDeviceMemoryUsage() const {
1297} 1297}
1298 1298
1299void Device::CollectPhysicalMemoryInfo() { 1299void Device::CollectPhysicalMemoryInfo() {
1300 // Account for resolution scaling in memory limits
1301 const size_t normal_memory = 6_GiB;
1302 const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1);
1303
1304 // Calculate limits using memory budget 1300 // Calculate limits using memory budget
1305 VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{}; 1301 VkPhysicalDeviceMemoryBudgetPropertiesEXT budget{};
1306 budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT; 1302 budget.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT;
@@ -1331,7 +1327,15 @@ void Device::CollectPhysicalMemoryInfo() {
1331 if (!is_integrated) { 1327 if (!is_integrated) {
1332 const u64 reserve_memory = std::min<u64>(device_access_memory / 8, 1_GiB); 1328 const u64 reserve_memory = std::min<u64>(device_access_memory / 8, 1_GiB);
1333 device_access_memory -= reserve_memory; 1329 device_access_memory -= reserve_memory;
1334 device_access_memory = std::min<u64>(device_access_memory, normal_memory + scaler_memory); 1330
1331 if (Settings::values.vram_usage_mode.GetValue() != Settings::VramUsageMode::Aggressive) {
1332 // Account for resolution scaling in memory limits
1333 const size_t normal_memory = 6_GiB;
1334 const size_t scaler_memory = 1_GiB * Settings::values.resolution_info.ScaleUp(1);
1335 device_access_memory =
1336 std::min<u64>(device_access_memory, normal_memory + scaler_memory);
1337 }
1338
1335 return; 1339 return;
1336 } 1340 }
1337 const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage); 1341 const s64 available_memory = static_cast<s64>(device_access_memory - device_initial_usage);
diff --git a/src/video_core/vulkan_common/vulkan_device.h b/src/video_core/vulkan_common/vulkan_device.h
index a2ec26697..e3abe8ddf 100644
--- a/src/video_core/vulkan_common/vulkan_device.h
+++ b/src/video_core/vulkan_common/vulkan_device.h
@@ -499,6 +499,11 @@ public:
499 return extensions.transform_feedback; 499 return extensions.transform_feedback;
500 } 500 }
501 501
502 /// Returns true if the device supports VK_EXT_transform_feedback properly.
503 bool AreTransformFeedbackGeometryStreamsSupported() const {
504 return features.transform_feedback.geometryStreams;
505 }
506
502 /// Returns true if the device supports VK_EXT_custom_border_color. 507 /// Returns true if the device supports VK_EXT_custom_border_color.
503 bool IsExtCustomBorderColorSupported() const { 508 bool IsExtCustomBorderColorSupported() const {
504 return extensions.custom_border_color; 509 return extensions.custom_border_color;
diff --git a/src/yuzu/configuration/shared_translation.cpp b/src/yuzu/configuration/shared_translation.cpp
index d138b53c8..0549e8ae4 100644
--- a/src/yuzu/configuration/shared_translation.cpp
+++ b/src/yuzu/configuration/shared_translation.cpp
@@ -164,6 +164,11 @@ std::unique_ptr<TranslationMap> InitializeTranslations(QWidget* parent) {
164 "the emulator to decompress to an intermediate format any card supports, RGBA8.\n" 164 "the emulator to decompress to an intermediate format any card supports, RGBA8.\n"
165 "This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but " 165 "This option recompresses RGBA8 to either the BC1 or BC3 format, saving VRAM but "
166 "negatively affecting image quality.")); 166 "negatively affecting image quality."));
167 INSERT(Settings, vram_usage_mode, tr("VRAM Usage Mode:"),
168 tr("Selects whether the emulator should prefer to conserve memory or make maximum usage "
169 "of available video memory for performance. Has no effect on integrated graphics. "
170 "Aggressive mode may severely impact the performance of other applications such as "
171 "recording software."));
167 INSERT( 172 INSERT(
168 Settings, vsync_mode, tr("VSync Mode:"), 173 Settings, vsync_mode, tr("VSync Mode:"),
169 tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen " 174 tr("FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen "
@@ -315,6 +320,11 @@ std::unique_ptr<ComboboxTranslationMap> ComboboxEnumeration(QWidget* parent) {
315 PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")), 320 PAIR(AstcRecompression, Bc1, tr("BC1 (Low quality)")),
316 PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")), 321 PAIR(AstcRecompression, Bc3, tr("BC3 (Medium quality)")),
317 }}); 322 }});
323 translations->insert({Settings::EnumMetadata<Settings::VramUsageMode>::Index(),
324 {
325 PAIR(VramUsageMode, Conservative, tr("Conservative")),
326 PAIR(VramUsageMode, Aggressive, tr("Aggressive")),
327 }});
318 translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(), 328 translations->insert({Settings::EnumMetadata<Settings::RendererBackend>::Index(),
319 { 329 {
320#ifdef HAS_OPENGL 330#ifdef HAS_OPENGL