Compare commits

..

27 Commits

Author SHA1 Message Date
yuzubot
f44933930d Android 260 2024-02-22 00:57:08 +00:00
yuzubot
e96826419b Merge yuzu-emu#13096 2024-02-22 00:57:08 +00:00
yuzubot
3f232161be Merge yuzu-emu#13075 2024-02-22 00:57:08 +00:00
yuzubot
98eb3df15c Merge yuzu-emu#13000 2024-02-22 00:57:08 +00:00
yuzubot
74ba9dde58 Merge yuzu-emu#12982 2024-02-22 00:57:07 +00:00
yuzubot
f3f63aa1e7 Merge yuzu-emu#12749 2024-02-22 00:57:07 +00:00
yuzubot
1bea1ee418 Merge yuzu-emu#12461 2024-02-22 00:57:07 +00:00
liamwhite
8bbc209950 Merge pull request #13105 from t895/connection-fix
android: Misc controller fixes
2024-02-21 10:43:46 -05:00
liamwhite
9e1a67b950 fs: add missing mutex header for member (#13106) 2024-02-21 16:43:05 +01:00
t895
de5422b1fd android: Connect controllers with supported styles
If you tried to connect a controller that was previously configured with an unsupported style for your game, when you try to connect that controller, it will immediately disconnect. This ensures that the controller that is being connected will be changed to the first supported style index before being connected.
2024-02-21 08:37:55 -05:00
t895
45f450fca5 android: Add additional check for hasMapping
Controls can have no mapping if they are either "[empty]" or and empty string. This was causing an issue if you reset mapping on all controllers and then tried to play a game. The check to determine whether auto mapping was required would fail and leave you will no mapped controllers. This feels a bit like user error but it smooths things out if you forget so I see it as necessary.
2024-02-21 08:17:30 -05:00
t895
9a3fd76b25 android: Enable all controller styles on emulation shutdown 2024-02-21 08:13:54 -05:00
Charles Lombardo
60fc6df407 Merge pull request #13099 from t895/default-fix
android: Fix extra stick setting default values
2024-02-21 07:02:58 -05:00
t895
de2d496e71 android: Fix extra stick setting default values
The default value was accidentally hardcoded for all extra stick settings
2024-02-20 22:13:59 -05:00
liamwhite
7b5bdd076d Merge pull request #13095 from liamwhite/ns-oops
ns: fix alignment of uid type
2024-02-20 21:19:35 -05:00
Matías Locatti
e0c17a2113 Merge pull request #10529 from liamwhite/critical-spacing
caches: make critical reclamation less eager and possible in more cases
2024-02-20 23:19:27 -03:00
Liam
b107435a3f ns: fix alignment of uid type 2024-02-20 18:43:44 -05:00
liamwhite
4e1fcd4a63 Merge pull request #13091 from t895/device-renaming
android: Expose device name setting
2024-02-20 18:30:54 -05:00
liamwhite
ea4a96b45e Merge pull request #13079 from liamwhite/vi3
vi: misc fixes
2024-02-20 18:30:47 -05:00
t895
6a90db8c19 android: Expose device name setting 2024-02-20 08:16:38 -05:00
t895
0e5972b0b5 android: Add StringInputSetting settings item 2024-02-20 08:06:56 -05:00
Liam
5f7608a7c6 vi: ignore shared buffer destruction failure on termination 2024-02-20 00:02:56 -05:00
Liam
668ff0db3a vi: remove superfluous locking in shared buffer manager 2024-02-19 23:59:35 -05:00
Liam
9f159dd62c nvnflinger/vi: don't recreate buffer queue on open/close 2024-02-19 23:59:35 -05:00
Liam
368bf2211f texture_cache: tweak iteration tracking change 2024-02-11 13:41:13 -05:00
Liam
de8a623932 texture_cache: avoid overestimation of ASTC texture sizes 2024-02-11 13:41:13 -05:00
Liam
865a0186b6 caches: make critical reclamation less eager and possible in more cases 2024-02-11 13:41:13 -05:00
36 changed files with 962 additions and 434 deletions

View File

@@ -1,11 +1,11 @@
| Pull Request | Commit | Title | Author | Merged? |
|----|----|----|----|----|
| [10529](https://github.com/yuzu-emu/yuzu//pull/10529) | [`368bf2211`](https://github.com/yuzu-emu/yuzu//pull/10529/files) | caches: make critical reclamation less eager and possible in more cases | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12461](https://github.com/yuzu-emu/yuzu//pull/12461) | [`2831f5dc6`](https://github.com/yuzu-emu/yuzu//pull/12461/files) | Rework Nvdec and VIC to fix out-of-order videos, and speed up decoding. | [Kelebek1](https://github.com/Kelebek1/) | Yes |
| [12749](https://github.com/yuzu-emu/yuzu//pull/12749) | [`aad4b0d6f`](https://github.com/yuzu-emu/yuzu//pull/12749/files) | general: workarounds for SMMU syncing issues | [liamwhite](https://github.com/liamwhite/) | Yes |
| [12982](https://github.com/yuzu-emu/yuzu//pull/12982) | [`ef5027712`](https://github.com/yuzu-emu/yuzu//pull/12982/files) | fs: Add FileSystemAccessor and use cmif serialization | [FearlessTobi](https://github.com/FearlessTobi/) | Yes |
| [13000](https://github.com/yuzu-emu/yuzu//pull/13000) | [`461eaca7e`](https://github.com/yuzu-emu/yuzu//pull/13000/files) | device_memory_manager: skip unregistered interfaces on invalidate | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13075](https://github.com/yuzu-emu/yuzu//pull/13075) | [`f46dc3168`](https://github.com/yuzu-emu/yuzu//pull/13075/files) | shader_recompiler: throw on missing geometry streams in geometry shaders | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13079](https://github.com/yuzu-emu/yuzu//pull/13079) | [`e462bff09`](https://github.com/yuzu-emu/yuzu//pull/13079/files) | vi: misc fixes | [liamwhite](https://github.com/liamwhite/) | Yes |
| [13096](https://github.com/yuzu-emu/yuzu//pull/13096) | [`0a8759057`](https://github.com/yuzu-emu/yuzu//pull/13096/files) | texture_cache: use two-pass collection for costly load resources | [liamwhite](https://github.com/liamwhite/) | Yes |
End of merge log. You can find the original README.md below the break.

View File

@@ -64,17 +64,17 @@ data class PlayerInput(
fun hasMapping(): Boolean {
var hasMapping = false
buttons.forEach {
if (it != "[empty]") {
if (it != "[empty]" && it.isNotEmpty()) {
hasMapping = true
}
}
analogs.forEach {
if (it != "[empty]") {
if (it != "[empty]" && it.isNotEmpty()) {
hasMapping = true
}
}
motions.forEach {
if (it != "[empty]") {
if (it != "[empty]" && it.isNotEmpty()) {
hasMapping = true
}
}

View File

@@ -6,7 +6,8 @@ package org.yuzu.yuzu_emu.features.settings.model
import org.yuzu.yuzu_emu.utils.NativeConfig
enum class StringSetting(override val key: String) : AbstractStringSetting {
DRIVER_PATH("driver_path");
DRIVER_PATH("driver_path"),
DEVICE_NAME("device_name");
override fun getString(needsGlobal: Boolean): String = NativeConfig.getString(key, needsGlobal)

View File

@@ -16,6 +16,7 @@ import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.utils.NativeConfig
/**
@@ -90,6 +91,7 @@ abstract class SettingsItem(
const val TYPE_INPUT = 8
const val TYPE_INT_SINGLE_CHOICE = 9
const val TYPE_INPUT_PROFILE = 10
const val TYPE_STRING_INPUT = 11
const val FASTMEM_COMBINED = "fastmem_combined"
@@ -108,6 +110,7 @@ abstract class SettingsItem(
// List of all general
val settingsItems = HashMap<String, SettingsItem>().apply {
put(StringInputSetting(StringSetting.DEVICE_NAME, titleId = R.string.device_name))
put(
SwitchSetting(
BooleanSetting.RENDERER_USE_SPEED_LIMIT,

View File

@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.StringRes
import org.yuzu.yuzu_emu.features.settings.model.AbstractStringSetting
class StringInputSetting(
setting: AbstractStringSetting,
@StringRes titleId: Int = 0,
titleString: String = "",
@StringRes descriptionId: Int = 0,
descriptionString: String = ""
) : SettingsItem(setting, titleId, titleString, descriptionId, descriptionString) {
override val type = TYPE_STRING_INPUT
fun getSelectedValue(needsGlobal: Boolean = false) = setting.getValueAsString(needsGlobal)
fun setSelectedValue(selection: String) =
(setting as AbstractStringSetting).setString(selection)
}

View File

@@ -85,6 +85,10 @@ class SettingsAdapter(
InputProfileViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_STRING_INPUT -> {
StringInputViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
else -> {
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
}
@@ -392,6 +396,15 @@ class SettingsAdapter(
popup.show()
}
fun onStringInputClick(item: StringInputSetting, position: Int) {
SettingsDialogFragment.newInstance(
settingsViewModel,
item,
SettingsItem.TYPE_STRING_INPUT,
position
).show(fragment.childFragmentManager, SettingsDialogFragment.TAG)
}
fun onLongClick(item: SettingsItem, position: Int): Boolean {
SettingsDialogFragment.newInstance(
settingsViewModel,

View File

@@ -14,6 +14,7 @@ import androidx.fragment.app.activityViewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.DialogEditTextBinding
import org.yuzu.yuzu_emu.databinding.DialogSliderBinding
import org.yuzu.yuzu_emu.features.input.NativeInput
import org.yuzu.yuzu_emu.features.input.model.AnalogDirection
@@ -23,6 +24,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.IntSingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringInputSetting
import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting
import org.yuzu.yuzu_emu.utils.ParamPackage
import org.yuzu.yuzu_emu.utils.collect
@@ -37,6 +39,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
private val settingsViewModel: SettingsViewModel by activityViewModels()
private lateinit var sliderBinding: DialogSliderBinding
private lateinit var stringInputBinding: DialogEditTextBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -131,6 +134,18 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
.create()
}
SettingsItem.TYPE_STRING_INPUT -> {
stringInputBinding = DialogEditTextBinding.inflate(layoutInflater)
val item = settingsViewModel.clickedItem as StringInputSetting
stringInputBinding.editText.setText(item.getSelectedValue())
MaterialAlertDialogBuilder(requireContext())
.setTitle(item.title)
.setView(stringInputBinding.root)
.setPositiveButton(android.R.string.ok, this)
.setNegativeButton(android.R.string.cancel, defaultCancelListener)
.create()
}
SettingsItem.TYPE_STRING_SINGLE_CHOICE -> {
val item = settingsViewModel.clickedItem as StringSingleChoiceSetting
MaterialAlertDialogBuilder(requireContext())
@@ -158,6 +173,7 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
): View? {
return when (type) {
SettingsItem.TYPE_SLIDER -> sliderBinding.root
SettingsItem.TYPE_STRING_INPUT -> stringInputBinding.root
else -> super.onCreateView(inflater, container, savedInstanceState)
}
}
@@ -200,6 +216,13 @@ class SettingsDialogFragment : DialogFragment(), DialogInterface.OnClickListener
val sliderSetting = settingsViewModel.clickedItem as SliderSetting
sliderSetting.setSelectedValue(settingsViewModel.sliderProgress.value)
}
is StringInputSetting -> {
val stringInputSetting = settingsViewModel.clickedItem as StringInputSetting
stringInputSetting.setSelectedValue(
(stringInputBinding.editText.text ?: "").toString()
)
}
}
closeDialog()
}

View File

@@ -23,6 +23,7 @@ import org.yuzu.yuzu_emu.features.settings.model.LongSetting
import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.Settings.MenuTag
import org.yuzu.yuzu_emu.features.settings.model.ShortSetting
import org.yuzu.yuzu_emu.features.settings.model.StringSetting
import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.NativeConfig
@@ -153,6 +154,7 @@ class SettingsFragmentPresenter(
private fun addSystemSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(StringSetting.DEVICE_NAME.key)
add(BooleanSetting.RENDERER_USE_SPEED_LIMIT.key)
add(ShortSetting.RENDERER_SPEED_LIMIT.key)
add(BooleanSetting.USE_DOCKED_MODE.key)
@@ -778,7 +780,7 @@ class SettingsFragmentPresenter(
playerIndex: Int,
paramName: String,
stick: NativeAnalog,
defaultValue: Int
defaultValue: Float
): AbstractIntSetting =
object : AbstractIntSetting {
val params get() = NativeInput.getStickParam(playerIndex, stick)
@@ -786,7 +788,7 @@ class SettingsFragmentPresenter(
override val key = ""
override fun getInt(needsGlobal: Boolean): Int =
(params.get(paramName, 0.15f) * 100).toInt()
(params.get(paramName, defaultValue) * 100).toInt()
override fun setInt(value: Int) {
val tempParams = params
@@ -794,12 +796,12 @@ class SettingsFragmentPresenter(
NativeInput.setStickParam(playerIndex, stick, tempParams)
}
override val defaultValue = defaultValue
override val defaultValue = (defaultValue * 100).toInt()
override fun getValueAsString(needsGlobal: Boolean): String =
getInt(needsGlobal).toString()
override fun reset() = setInt(defaultValue)
override fun reset() = setInt(this.defaultValue)
}
private fun getExtraStickSettings(
@@ -809,11 +811,11 @@ class SettingsFragmentPresenter(
val stickIsController =
NativeInput.isController(NativeInput.getStickParam(playerIndex, nativeAnalog))
val modifierRangeSetting =
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 50)
getStickIntSettingFromParam(playerIndex, "modifier_scale", nativeAnalog, 0.5f)
val stickRangeSetting =
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 95)
getStickIntSettingFromParam(playerIndex, "range", nativeAnalog, 0.95f)
val stickDeadzoneSetting =
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 15)
getStickIntSettingFromParam(playerIndex, "deadzone", nativeAnalog, 0.15f)
val out = mutableListOf<SettingsItem>().apply {
if (stickIsController) {

View File

@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.model.view.StringInputSetting
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class StringInputViewHolder(val binding: ListItemSettingBinding, adapter: SettingsAdapter) :
SettingViewHolder(binding.root, adapter) {
private lateinit var setting: StringInputSetting
override fun bind(item: SettingsItem) {
setting = item as StringInputSetting
binding.textSettingName.text = setting.title
binding.textSettingDescription.setVisible(setting.description.isNotEmpty())
binding.textSettingDescription.text = setting.description
binding.textSettingValue.setVisible(true)
binding.textSettingValue.text = setting.getSelectedValue()
binding.buttonClear.setVisible(setting.clearable)
binding.buttonClear.setOnClickListener {
adapter.onClearClick(setting, bindingAdapterPosition)
}
setStyle(setting.isEditable, binding)
}
override fun onClick(clicked: View) {
if (setting.isEditable) {
adapter.onStringInputClick(setting, bindingAdapterPosition)
}
}
override fun onLongClick(clicked: View): Boolean {
if (setting.isEditable) {
return adapter.onLongClick(setting, bindingAdapterPosition)
}
return false
}
}

View File

@@ -292,6 +292,9 @@ void EmulationSession::ShutdownEmulation() {
// Unload user input.
m_system.HIDCore().UnloadInputDevices();
// Enable all controllers
m_system.HIDCore().SetSupportedStyleTag({Core::HID::NpadStyleSet::All});
// Shutdown the main emulated process
if (m_load_result == Core::SystemResultStatus::Success) {
m_system.DetachDebugger();

View File

@@ -102,8 +102,50 @@ void ApplyControllerConfig(size_t player_index,
}
}
std::vector<s32> GetSupportedStyles(int player_index) {
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
const auto npad_style_set = hid_core.GetSupportedStyleTag();
std::vector<s32> supported_indexes;
if (npad_style_set.fullkey == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Fullkey));
}
if (npad_style_set.joycon_dual == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconDual));
}
if (npad_style_set.joycon_left == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconLeft));
}
if (npad_style_set.joycon_right == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::JoyconRight));
}
if (player_index == 0 && npad_style_set.handheld == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::Handheld));
}
if (npad_style_set.gamecube == 1) {
supported_indexes.push_back(static_cast<s32>(Core::HID::NpadStyleIndex::GameCube));
}
return supported_indexes;
}
void ConnectController(size_t player_index, bool connected) {
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
ApplyControllerConfig(player_index, [&](Core::HID::EmulatedController* controller) {
auto supported_styles = GetSupportedStyles(player_index);
auto controller_style = controller->GetNpadStyleIndex(true);
auto style = std::find(supported_styles.begin(), supported_styles.end(),
static_cast<int>(controller_style));
if (style == supported_styles.end() && !supported_styles.empty()) {
controller->SetNpadStyleIndex(
static_cast<Core::HID::NpadStyleIndex>(supported_styles[0]));
}
});
if (player_index == 0) {
auto* handheld = hid_core.GetEmulatedController(Core::HID::NpadIdType::Handheld);
auto* player_one = hid_core.GetEmulatedController(Core::HID::NpadIdType::Player1);
@@ -522,36 +564,10 @@ jint Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getButtonNameImpl(JNIEnv
jintArray Java_org_yuzu_yuzu_1emu_features_input_NativeInput_getSupportedStyleTagsImpl(
JNIEnv* env, jobject j_obj, jint j_player_index) {
auto& hid_core = EmulationSession::GetInstance().System().HIDCore();
const auto npad_style_set = hid_core.GetSupportedStyleTag();
std::vector<s32> supported_indexes;
if (npad_style_set.fullkey == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Fullkey));
}
if (npad_style_set.joycon_dual == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconDual));
}
if (npad_style_set.joycon_left == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconLeft));
}
if (npad_style_set.joycon_right == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::JoyconRight));
}
if (j_player_index == 0 && npad_style_set.handheld == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::Handheld));
}
if (npad_style_set.gamecube == 1) {
supported_indexes.push_back(static_cast<u32>(Core::HID::NpadStyleIndex::GameCube));
}
jintArray j_supported_indexes = env->NewIntArray(supported_indexes.size());
env->SetIntArrayRegion(j_supported_indexes, 0, supported_indexes.size(),
supported_indexes.data());
auto supported_styles = GetSupportedStyles(j_player_index);
jintArray j_supported_indexes = env->NewIntArray(supported_styles.size());
env->SetIntArrayRegion(j_supported_indexes, 0, supported_styles.size(),
supported_styles.data());
return j_supported_indexes;
}

View File

@@ -209,6 +209,7 @@
<string name="value_with_units">%1$s%2$s</string>
<!-- System settings strings -->
<string name="device_name">Device name</string>
<string name="use_docked_mode">Docked Mode</string>
<string name="use_docked_mode_description">Increases resolution, decreasing performance. Handheld Mode is used when disabled, lowering resolution and increasing performance.</string>
<string name="emulated_region">Emulated region</string>

View File

@@ -59,8 +59,12 @@ add_library(core STATIC
file_sys/fs_path.h
file_sys/fs_path_utility.h
file_sys/fs_string_util.h
file_sys/fsa/fs_i_directory.h
file_sys/fsa/fs_i_file.h
file_sys/fsa/fs_i_filesystem.h
file_sys/fsmitm_romfsbuild.cpp
file_sys/fsmitm_romfsbuild.h
file_sys/fssrv/fssrv_sf_path.h
file_sys/fssystem/fs_i_storage.h
file_sys/fssystem/fs_types.h
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp

View File

@@ -23,6 +23,8 @@ enum class OpenDirectoryMode : u64 {
File = (1 << 1),
All = (Directory | File),
NotRequireFileSize = (1ULL << 31),
};
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
@@ -36,4 +38,29 @@ enum class CreateOption : u8 {
BigFile = (1 << 0),
};
struct FileSystemAttribute {
u8 dir_entry_name_length_max_defined;
u8 file_entry_name_length_max_defined;
u8 dir_path_name_length_max_defined;
u8 file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x5);
u8 utf16_dir_entry_name_length_max_defined;
u8 utf16_file_entry_name_length_max_defined;
u8 utf16_dir_path_name_length_max_defined;
u8 utf16_file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x18);
s32 dir_entry_name_length_max;
s32 file_entry_name_length_max;
s32 dir_path_name_length_max;
s32 file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x5);
s32 utf16_dir_entry_name_length_max;
s32 utf16_file_entry_name_length_max;
s32 utf16_dir_path_name_length_max;
s32 utf16_file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x18);
INSERT_PADDING_WORDS_NOINIT(0x1);
};
static_assert(sizeof(FileSystemAttribute) == 0xC0, "FileSystemAttribute has incorrect size");
} // namespace FileSys

View File

@@ -10,7 +10,7 @@ namespace FileSys {
constexpr size_t RequiredAlignment = alignof(u64);
void* AllocateUnsafe(size_t size) {
inline void* AllocateUnsafe(size_t size) {
// Allocate
void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
@@ -21,16 +21,16 @@ void* AllocateUnsafe(size_t size) {
return ptr;
}
void DeallocateUnsafe(void* ptr, size_t size) {
inline void DeallocateUnsafe(void* ptr, size_t size) {
// Deallocate the pointer
::operator delete(ptr, std::align_val_t{RequiredAlignment});
}
void* Allocate(size_t size) {
inline void* Allocate(size_t size) {
return AllocateUnsafe(size);
}
void Deallocate(void* ptr, size_t size) {
inline void Deallocate(void* ptr, size_t size) {
// If the pointer is non-null, deallocate it
if (ptr != nullptr) {
DeallocateUnsafe(ptr, size);

View File

@@ -381,7 +381,7 @@ public:
// Check that it's possible for us to remove a child
auto* p = m_write_buffer.Get();
s32 len = std::strlen(p);
s32 len = static_cast<s32>(std::strlen(p));
R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented);
// Handle a trailing separator

View File

@@ -426,9 +426,10 @@ public:
R_SUCCEED();
}
static Result Normalize(char* dst, size_t* out_len, const char* path, size_t max_out_size,
bool is_windows_path, bool is_drive_relative_path,
bool allow_all_characters = false) {
static constexpr Result Normalize(char* dst, size_t* out_len, const char* path,
size_t max_out_size, bool is_windows_path,
bool is_drive_relative_path,
bool allow_all_characters = false) {
// Use StringTraits names for remainder of scope
using namespace StringTraits;

View File

@@ -19,6 +19,11 @@ constexpr int Strlen(const T* str) {
return length;
}
template <typename T>
constexpr int Strnlen(const T* str, std::size_t count) {
return Strnlen(str, static_cast<int>(count));
}
template <typename T>
constexpr int Strnlen(const T* str, int count) {
ASSERT(str != nullptr);
@@ -32,6 +37,11 @@ constexpr int Strnlen(const T* str, int count) {
return length;
}
template <typename T>
constexpr int Strncmp(const T* lhs, const T* rhs, std::size_t count) {
return Strncmp(lhs, rhs, static_cast<int>(count));
}
template <typename T>
constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
ASSERT(lhs != nullptr);
@@ -51,6 +61,11 @@ constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
return l - r;
}
template <typename T>
static constexpr int Strlcpy(T* dst, const T* src, std::size_t count) {
return Strlcpy<T>(dst, src, static_cast<int>(count));
}
template <typename T>
static constexpr int Strlcpy(T* dst, const T* src, int count) {
ASSERT(dst != nullptr);

View File

@@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/common_types.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/fs_directory.h"
#include "core/file_sys/fs_file.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/result.h"
namespace FileSys::Fsa {
class IDirectory {
public:
explicit IDirectory(VirtualDir backend_, OpenDirectoryMode mode)
: backend(std::move(backend_)) {
// TODO(DarkLordZach): Verify that this is the correct behavior.
// Build entry index now to save time later.
if (True(mode & OpenDirectoryMode::Directory)) {
BuildEntryIndex(backend->GetSubdirectories(), DirectoryEntryType::Directory);
}
if (True(mode & OpenDirectoryMode::File)) {
BuildEntryIndex(backend->GetFiles(), DirectoryEntryType::File);
}
}
virtual ~IDirectory() {}
Result Read(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
R_UNLESS(out_count != nullptr, ResultNullptrArgument);
if (max_entries == 0) {
*out_count = 0;
R_SUCCEED();
}
R_UNLESS(out_entries != nullptr, ResultNullptrArgument);
R_UNLESS(max_entries > 0, ResultInvalidArgument);
R_RETURN(this->DoRead(out_count, out_entries, max_entries));
}
Result GetEntryCount(s64* out) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetEntryCount(out));
}
private:
Result DoRead(s64* out_count, DirectoryEntry* out_entries, s64 max_entries) {
const u64 actual_entries =
std::min(static_cast<u64>(max_entries), entries.size() - next_entry_index);
const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
next_entry_index += actual_entries;
*out_count = actual_entries;
std::memcpy(out_entries, begin, range_size);
R_SUCCEED();
}
Result DoGetEntryCount(s64* out) {
*out = entries.size() - next_entry_index;
R_SUCCEED();
}
// TODO: Remove this when VFS is gone
template <typename T>
void BuildEntryIndex(const std::vector<T>& new_data, DirectoryEntryType type) {
entries.reserve(entries.size() + new_data.size());
for (const auto& new_entry : new_data) {
auto name = new_entry->GetName();
if (type == DirectoryEntryType::File && name == GetSaveDataSizeFileName()) {
continue;
}
entries.emplace_back(name, static_cast<s8>(type),
type == DirectoryEntryType::Directory ? 0 : new_entry->GetSize());
}
}
VirtualDir backend;
std::vector<DirectoryEntry> entries;
u64 next_entry_index = 0;
};
} // namespace FileSys::Fsa

View File

@@ -0,0 +1,167 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/overflow.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/fs_file.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fs_operate_range.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
namespace FileSys::Fsa {
class IFile {
public:
explicit IFile(VirtualFile backend_) : backend(std::move(backend_)) {}
virtual ~IFile() {}
Result Read(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
// Check that we have an output pointer
R_UNLESS(out != nullptr, ResultNullptrArgument);
// If we have nothing to read, just succeed
if (size == 0) {
*out = 0;
R_SUCCEED();
}
// Check that the read is valid
R_UNLESS(buffer != nullptr, ResultNullptrArgument);
R_UNLESS(offset >= 0, ResultOutOfRange);
R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
// Do the read
R_RETURN(this->DoRead(out, offset, buffer, size, option));
}
Result Read(size_t* out, s64 offset, void* buffer, size_t size) {
R_RETURN(this->Read(out, offset, buffer, size, ReadOption::None));
}
Result GetSize(s64* out) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetSize(out));
}
Result Flush() {
R_RETURN(this->DoFlush());
}
Result Write(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
// Handle the zero-size case
if (size == 0) {
if (option.HasFlushFlag()) {
R_TRY(this->Flush());
}
R_SUCCEED();
}
// Check the write is valid
R_UNLESS(buffer != nullptr, ResultNullptrArgument);
R_UNLESS(offset >= 0, ResultOutOfRange);
R_UNLESS(Common::CanAddWithoutOverflow<s64>(offset, size), ResultOutOfRange);
R_RETURN(this->DoWrite(offset, buffer, size, option));
}
Result SetSize(s64 size) {
R_UNLESS(size >= 0, ResultOutOfRange);
R_RETURN(this->DoSetSize(size));
}
Result OperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
const void* src, size_t src_size) {
R_RETURN(this->DoOperateRange(dst, dst_size, op_id, offset, size, src, src_size));
}
Result OperateRange(OperationId op_id, s64 offset, s64 size) {
R_RETURN(this->DoOperateRange(nullptr, 0, op_id, offset, size, nullptr, 0));
}
protected:
Result DryRead(size_t* out, s64 offset, size_t size, const ReadOption& option,
OpenMode open_mode) {
// Check that we can read
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Read) != 0, ResultReadNotPermitted);
// Get the file size, and validate our offset
s64 file_size = 0;
R_TRY(this->DoGetSize(std::addressof(file_size)));
R_UNLESS(offset <= file_size, ResultOutOfRange);
*out = static_cast<size_t>(std::min(file_size - offset, static_cast<s64>(size)));
R_SUCCEED();
}
Result DrySetSize(s64 size, OpenMode open_mode) {
// Check that we can write
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
R_SUCCEED();
}
Result DryWrite(bool* out_append, s64 offset, size_t size, const WriteOption& option,
OpenMode open_mode) {
// Check that we can write
R_UNLESS(static_cast<u32>(open_mode & OpenMode::Write) != 0, ResultWriteNotPermitted);
// Get the file size
s64 file_size = 0;
R_TRY(this->DoGetSize(&file_size));
// Determine if we need to append
*out_append = false;
if (file_size < offset + static_cast<s64>(size)) {
R_UNLESS(static_cast<u32>(open_mode & OpenMode::AllowAppend) != 0,
ResultFileExtensionWithoutOpenModeAllowAppend);
*out_append = true;
}
R_SUCCEED();
}
private:
Result DoRead(size_t* out, s64 offset, void* buffer, size_t size, const ReadOption& option) {
const auto read_size = backend->Read(static_cast<u8*>(buffer), size, offset);
*out = read_size;
R_SUCCEED();
}
Result DoGetSize(s64* out) {
*out = backend->GetSize();
R_SUCCEED();
}
Result DoFlush() {
// Exists for SDK compatibiltity -- No need to flush file.
R_SUCCEED();
}
Result DoWrite(s64 offset, const void* buffer, size_t size, const WriteOption& option) {
const std::size_t written = backend->Write(static_cast<const u8*>(buffer), size, offset);
ASSERT_MSG(written == size,
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", size,
written);
R_SUCCEED();
}
Result DoSetSize(s64 size) {
backend->Resize(size);
R_SUCCEED();
}
Result DoOperateRange(void* dst, size_t dst_size, OperationId op_id, s64 offset, s64 size,
const void* src, size_t src_size) {
R_THROW(ResultNotImplemented);
}
VirtualFile backend;
};
} // namespace FileSys::Fsa

View File

@@ -0,0 +1,206 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/errors.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fs_path.h"
#include "core/file_sys/vfs/vfs_types.h"
#include "core/hle/result.h"
#include "core/hle/service/filesystem/filesystem.h"
namespace FileSys::Fsa {
class IFile;
class IDirectory;
enum class QueryId : u32 {
SetConcatenationFileAttribute = 0,
UpdateMac = 1,
IsSignedSystemPartitionOnSdCardValid = 2,
QueryUnpreparedFileInformation = 3,
};
class IFileSystem {
public:
explicit IFileSystem(VirtualDir backend_) : backend{std::move(backend_)} {}
virtual ~IFileSystem() {}
Result CreateFile(const Path& path, s64 size, CreateOption option) {
R_UNLESS(size >= 0, ResultOutOfRange);
R_RETURN(this->DoCreateFile(path, size, static_cast<int>(option)));
}
Result CreateFile(const Path& path, s64 size) {
R_RETURN(this->CreateFile(path, size, CreateOption::None));
}
Result DeleteFile(const Path& path) {
R_RETURN(this->DoDeleteFile(path));
}
Result CreateDirectory(const Path& path) {
R_RETURN(this->DoCreateDirectory(path));
}
Result DeleteDirectory(const Path& path) {
R_RETURN(this->DoDeleteDirectory(path));
}
Result DeleteDirectoryRecursively(const Path& path) {
R_RETURN(this->DoDeleteDirectoryRecursively(path));
}
Result RenameFile(const Path& old_path, const Path& new_path) {
R_RETURN(this->DoRenameFile(old_path, new_path));
}
Result RenameDirectory(const Path& old_path, const Path& new_path) {
R_RETURN(this->DoRenameDirectory(old_path, new_path));
}
Result GetEntryType(DirectoryEntryType* out, const Path& path) {
R_RETURN(this->DoGetEntryType(out, path));
}
Result OpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
R_UNLESS(out_file != nullptr, ResultNullptrArgument);
R_UNLESS(static_cast<u32>(mode & OpenMode::ReadWrite) != 0, ResultInvalidOpenMode);
R_UNLESS(static_cast<u32>(mode & ~OpenMode::All) == 0, ResultInvalidOpenMode);
R_RETURN(this->DoOpenFile(out_file, path, mode));
}
Result OpenDirectory(VirtualDir* out_dir, const Path& path, OpenDirectoryMode mode) {
R_UNLESS(out_dir != nullptr, ResultNullptrArgument);
R_UNLESS(static_cast<u64>(mode & OpenDirectoryMode::All) != 0, ResultInvalidOpenMode);
R_UNLESS(static_cast<u64>(
mode & ~(OpenDirectoryMode::All | OpenDirectoryMode::NotRequireFileSize)) == 0,
ResultInvalidOpenMode);
R_RETURN(this->DoOpenDirectory(out_dir, path, mode));
}
Result Commit() {
R_RETURN(this->DoCommit());
}
Result GetFreeSpaceSize(s64* out, const Path& path) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetFreeSpaceSize(out, path));
}
Result GetTotalSpaceSize(s64* out, const Path& path) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetTotalSpaceSize(out, path));
}
Result CleanDirectoryRecursively(const Path& path) {
R_RETURN(this->DoCleanDirectoryRecursively(path));
}
Result GetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
R_UNLESS(out != nullptr, ResultNullptrArgument);
R_RETURN(this->DoGetFileTimeStampRaw(out, path));
}
Result QueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
const Path& path) {
R_RETURN(this->DoQueryEntry(dst, dst_size, src, src_size, query, path));
}
// These aren't accessible as commands
Result CommitProvisionally(s64 counter) {
R_RETURN(this->DoCommitProvisionally(counter));
}
Result Rollback() {
R_RETURN(this->DoRollback());
}
Result Flush() {
R_RETURN(this->DoFlush());
}
private:
Result DoCreateFile(const Path& path, s64 size, int flags) {
R_RETURN(backend.CreateFile(path.GetString(), size));
}
Result DoDeleteFile(const Path& path) {
R_RETURN(backend.DeleteFile(path.GetString()));
}
Result DoCreateDirectory(const Path& path) {
R_RETURN(backend.CreateDirectory(path.GetString()));
}
Result DoDeleteDirectory(const Path& path) {
R_RETURN(backend.DeleteDirectory(path.GetString()));
}
Result DoDeleteDirectoryRecursively(const Path& path) {
R_RETURN(backend.DeleteDirectoryRecursively(path.GetString()));
}
Result DoRenameFile(const Path& old_path, const Path& new_path) {
R_RETURN(backend.RenameFile(old_path.GetString(), new_path.GetString()));
}
Result DoRenameDirectory(const Path& old_path, const Path& new_path) {
R_RETURN(backend.RenameDirectory(old_path.GetString(), new_path.GetString()));
}
Result DoGetEntryType(DirectoryEntryType* out, const Path& path) {
R_RETURN(backend.GetEntryType(out, path.GetString()));
}
Result DoOpenFile(VirtualFile* out_file, const Path& path, OpenMode mode) {
R_RETURN(backend.OpenFile(out_file, path.GetString(), mode));
}
Result DoOpenDirectory(VirtualDir* out_directory, const Path& path, OpenDirectoryMode mode) {
R_RETURN(backend.OpenDirectory(out_directory, path.GetString()));
}
Result DoCommit() {
R_THROW(ResultNotImplemented);
}
Result DoGetFreeSpaceSize(s64* out, const Path& path) {
R_THROW(ResultNotImplemented);
}
Result DoGetTotalSpaceSize(s64* out, const Path& path) {
R_THROW(ResultNotImplemented);
}
Result DoCleanDirectoryRecursively(const Path& path) {
R_RETURN(backend.CleanDirectoryRecursively(path.GetString()));
}
Result DoGetFileTimeStampRaw(FileTimeStampRaw* out, const Path& path) {
R_RETURN(backend.GetFileTimeStampRaw(out, path.GetString()));
}
Result DoQueryEntry(char* dst, size_t dst_size, const char* src, size_t src_size, QueryId query,
const Path& path) {
R_THROW(ResultNotImplemented);
}
// These aren't accessible as commands
Result DoCommitProvisionally(s64 counter) {
R_THROW(ResultNotImplemented);
}
Result DoRollback() {
R_THROW(ResultNotImplemented);
}
Result DoFlush() {
R_THROW(ResultNotImplemented);
}
Service::FileSystem::VfsDirectoryServiceWrapper backend;
};
} // namespace FileSys::Fsa

View File

@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "core/file_sys/fs_directory.h"
namespace FileSys::Sf {
struct Path {
char str[EntryNameLengthMax + 1];
static constexpr Path Encode(const char* p) {
Path path = {};
for (size_t i = 0; i < sizeof(path) - 1; i++) {
path.str[i] = p[i];
if (p[i] == '\x00') {
break;
}
}
return path;
}
static constexpr size_t GetPathLength(const Path& path) {
size_t len = 0;
for (size_t i = 0; i < sizeof(path) - 1 && path.str[i] != '\x00'; i++) {
len++;
}
return len;
}
};
static_assert(std::is_trivially_copyable_v<Path>, "Path must be trivially copyable.");
using FspPath = Path;
} // namespace FileSys::Sf

View File

@@ -3,6 +3,7 @@
#pragma once
#include <mutex>
#include <optional>
#include "core/crypto/aes_util.h"

View File

@@ -3,82 +3,34 @@
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/savedata_factory.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::FileSystem {
template <typename T>
static void BuildEntryIndex(std::vector<FileSys::DirectoryEntry>& entries,
const std::vector<T>& new_data, FileSys::DirectoryEntryType type) {
entries.reserve(entries.size() + new_data.size());
for (const auto& new_entry : new_data) {
auto name = new_entry->GetName();
if (type == FileSys::DirectoryEntryType::File &&
name == FileSys::GetSaveDataSizeFileName()) {
continue;
}
entries.emplace_back(name, static_cast<s8>(type),
type == FileSys::DirectoryEntryType::Directory ? 0
: new_entry->GetSize());
}
}
IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
IDirectory::IDirectory(Core::System& system_, FileSys::VirtualDir directory_,
FileSys::OpenDirectoryMode mode)
: ServiceFramework{system_, "IDirectory"}, backend(std::move(backend_)) {
: ServiceFramework{system_, "IDirectory"},
backend(std::make_unique<FileSys::Fsa::IDirectory>(directory_, mode)) {
static const FunctionInfo functions[] = {
{0, &IDirectory::Read, "Read"},
{1, &IDirectory::GetEntryCount, "GetEntryCount"},
{0, D<&IDirectory::Read>, "Read"},
{1, D<&IDirectory::GetEntryCount>, "GetEntryCount"},
};
RegisterHandlers(functions);
// TODO(DarkLordZach): Verify that this is the correct behavior.
// Build entry index now to save time later.
if (True(mode & FileSys::OpenDirectoryMode::Directory)) {
BuildEntryIndex(entries, backend->GetSubdirectories(),
FileSys::DirectoryEntryType::Directory);
}
if (True(mode & FileSys::OpenDirectoryMode::File)) {
BuildEntryIndex(entries, backend->GetFiles(), FileSys::DirectoryEntryType::File);
}
}
void IDirectory::Read(HLERequestContext& ctx) {
Result IDirectory::Read(
Out<s64> out_count,
const OutArray<FileSys::DirectoryEntry, BufferAttr_HipcMapAlias> out_entries) {
LOG_DEBUG(Service_FS, "called.");
// Calculate how many entries we can fit in the output buffer
const u64 count_entries = ctx.GetWriteBufferNumElements<FileSys::DirectoryEntry>();
// Cap at total number of entries.
const u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index);
// Determine data start and end
const auto* begin = reinterpret_cast<u8*>(entries.data() + next_entry_index);
const auto* end = reinterpret_cast<u8*>(entries.data() + next_entry_index + actual_entries);
const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
next_entry_index += actual_entries;
// Write the data to memory
ctx.WriteBuffer(begin, range_size);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(actual_entries);
R_RETURN(backend->Read(out_count, out_entries.data(), out_entries.size()));
}
void IDirectory::GetEntryCount(HLERequestContext& ctx) {
Result IDirectory::GetEntryCount(Out<s64> out_count) {
LOG_DEBUG(Service_FS, "called");
u64 count = entries.size() - next_entry_index;
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(count);
R_RETURN(backend->GetEntryCount(out_count));
}
} // namespace Service::FileSystem

View File

@@ -3,7 +3,9 @@
#pragma once
#include "core/file_sys/fsa/fs_i_directory.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/service.h"
@@ -15,16 +17,15 @@ namespace Service::FileSystem {
class IDirectory final : public ServiceFramework<IDirectory> {
public:
explicit IDirectory(Core::System& system_, FileSys::VirtualDir backend_,
explicit IDirectory(Core::System& system_, FileSys::VirtualDir directory_,
FileSys::OpenDirectoryMode mode);
private:
FileSys::VirtualDir backend;
std::vector<FileSys::DirectoryEntry> entries;
u64 next_entry_index = 0;
std::unique_ptr<FileSys::Fsa::IDirectory> backend;
void Read(HLERequestContext& ctx);
void GetEntryCount(HLERequestContext& ctx);
Result Read(Out<s64> out_count,
const OutArray<FileSys::DirectoryEntry, BufferAttr_HipcMapAlias> out_entries);
Result GetEntryCount(Out<s64> out_count);
};
} // namespace Service::FileSystem

View File

@@ -2,126 +2,64 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "core/file_sys/errors.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/fsp/fs_i_file.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::FileSystem {
IFile::IFile(Core::System& system_, FileSys::VirtualFile backend_)
: ServiceFramework{system_, "IFile"}, backend(std::move(backend_)) {
IFile::IFile(Core::System& system_, FileSys::VirtualFile file_)
: ServiceFramework{system_, "IFile"}, backend{std::make_unique<FileSys::Fsa::IFile>(file_)} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"},
{1, &IFile::Write, "Write"},
{2, &IFile::Flush, "Flush"},
{3, &IFile::SetSize, "SetSize"},
{4, &IFile::GetSize, "GetSize"},
{0, D<&IFile::Read>, "Read"},
{1, D<&IFile::Write>, "Write"},
{2, D<&IFile::Flush>, "Flush"},
{3, D<&IFile::SetSize>, "SetSize"},
{4, D<&IFile::GetSize>, "GetSize"},
{5, nullptr, "OperateRange"},
{6, nullptr, "OperateRangeWithBuffer"},
};
// clang-format on
RegisterHandlers(functions);
}
void IFile::Read(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 option = rp.Pop<u64>();
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
// Error checking
if (length < 0) {
LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidSize);
return;
}
if (offset < 0) {
LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidOffset);
return;
}
Result IFile::Read(
FileSys::ReadOption option, Out<s64> out_size, s64 offset,
const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer,
s64 size) {
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
size);
// Read the data from the Storage backend
std::vector<u8> output = backend->ReadBytes(length, offset);
// Write the data to memory
ctx.WriteBuffer(output);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(static_cast<u64>(output.size()));
R_RETURN(
backend->Read(reinterpret_cast<size_t*>(out_size.Get()), offset, out_buffer.data(), size));
}
void IFile::Write(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 option = rp.Pop<u64>();
const s64 offset = rp.Pop<s64>();
const s64 length = rp.Pop<s64>();
Result IFile::Write(
const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
FileSys::WriteOption option, s64 offset, s64 size) {
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option.value, offset,
size);
LOG_DEBUG(Service_FS, "called, option={}, offset=0x{:X}, length={}", option, offset, length);
// Error checking
if (length < 0) {
LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidSize);
return;
}
if (offset < 0) {
LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(FileSys::ResultInvalidOffset);
return;
}
const auto data = ctx.ReadBuffer();
ASSERT_MSG(static_cast<s64>(data.size()) <= length,
"Attempting to write more data than requested (requested={:016X}, actual={:016X}).",
length, data.size());
// Write the data to the Storage backend
const auto write_size =
static_cast<std::size_t>(std::distance(data.begin(), data.begin() + length));
const std::size_t written = backend->Write(data.data(), write_size, offset);
ASSERT_MSG(static_cast<s64>(written) == length,
"Could not write all bytes to file (requested={:016X}, actual={:016X}).", length,
written);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
R_RETURN(backend->Write(offset, buffer.data(), size, option));
}
void IFile::Flush(HLERequestContext& ctx) {
Result IFile::Flush() {
LOG_DEBUG(Service_FS, "called");
// Exists for SDK compatibiltity -- No need to flush file.
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
R_RETURN(backend->Flush());
}
void IFile::SetSize(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 size = rp.Pop<u64>();
Result IFile::SetSize(s64 size) {
LOG_DEBUG(Service_FS, "called, size={}", size);
backend->Resize(size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
R_RETURN(backend->SetSize(size));
}
void IFile::GetSize(HLERequestContext& ctx) {
const u64 size = backend->GetSize();
LOG_DEBUG(Service_FS, "called, size={}", size);
Result IFile::GetSize(Out<s64> out_size) {
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push<u64>(size);
R_RETURN(backend->GetSize(out_size));
}
} // namespace Service::FileSystem

View File

@@ -3,6 +3,8 @@
#pragma once
#include "core/file_sys/fsa/fs_i_file.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/service.h"
@@ -10,16 +12,21 @@ namespace Service::FileSystem {
class IFile final : public ServiceFramework<IFile> {
public:
explicit IFile(Core::System& system_, FileSys::VirtualFile backend_);
explicit IFile(Core::System& system_, FileSys::VirtualFile file_);
private:
FileSys::VirtualFile backend;
std::unique_ptr<FileSys::Fsa::IFile> backend;
void Read(HLERequestContext& ctx);
void Write(HLERequestContext& ctx);
void Flush(HLERequestContext& ctx);
void SetSize(HLERequestContext& ctx);
void GetSize(HLERequestContext& ctx);
Result Read(FileSys::ReadOption option, Out<s64> out_size, s64 offset,
const OutBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure>
out_buffer,
s64 size);
Result Write(
const InBuffer<BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> buffer,
FileSys::WriteOption option, s64 offset, s64 size);
Result Flush();
Result SetSize(s64 size);
Result GetSize(Out<s64> out_size);
};
} // namespace Service::FileSystem

View File

@@ -2,261 +2,172 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/string_util.h"
#include "core/file_sys/fssrv/fssrv_sf_path.h"
#include "core/hle/service/cmif_serialization.h"
#include "core/hle/service/filesystem/fsp/fs_i_directory.h"
#include "core/hle/service/filesystem/fsp/fs_i_file.h"
#include "core/hle/service/filesystem/fsp/fs_i_filesystem.h"
#include "core/hle/service/ipc_helpers.h"
namespace Service::FileSystem {
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_)
: ServiceFramework{system_, "IFileSystem"}, backend{std::move(backend_)}, size{std::move(
size_)} {
IFileSystem::IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_)
: ServiceFramework{system_, "IFileSystem"}, backend{std::make_unique<FileSys::Fsa::IFileSystem>(
dir_)},
size_getter{std::move(size_getter_)} {
static const FunctionInfo functions[] = {
{0, &IFileSystem::CreateFile, "CreateFile"},
{1, &IFileSystem::DeleteFile, "DeleteFile"},
{2, &IFileSystem::CreateDirectory, "CreateDirectory"},
{3, &IFileSystem::DeleteDirectory, "DeleteDirectory"},
{4, &IFileSystem::DeleteDirectoryRecursively, "DeleteDirectoryRecursively"},
{5, &IFileSystem::RenameFile, "RenameFile"},
{0, D<&IFileSystem::CreateFile>, "CreateFile"},
{1, D<&IFileSystem::DeleteFile>, "DeleteFile"},
{2, D<&IFileSystem::CreateDirectory>, "CreateDirectory"},
{3, D<&IFileSystem::DeleteDirectory>, "DeleteDirectory"},
{4, D<&IFileSystem::DeleteDirectoryRecursively>, "DeleteDirectoryRecursively"},
{5, D<&IFileSystem::RenameFile>, "RenameFile"},
{6, nullptr, "RenameDirectory"},
{7, &IFileSystem::GetEntryType, "GetEntryType"},
{8, &IFileSystem::OpenFile, "OpenFile"},
{9, &IFileSystem::OpenDirectory, "OpenDirectory"},
{10, &IFileSystem::Commit, "Commit"},
{11, &IFileSystem::GetFreeSpaceSize, "GetFreeSpaceSize"},
{12, &IFileSystem::GetTotalSpaceSize, "GetTotalSpaceSize"},
{13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
{14, &IFileSystem::GetFileTimeStampRaw, "GetFileTimeStampRaw"},
{7, D<&IFileSystem::GetEntryType>, "GetEntryType"},
{8, D<&IFileSystem::OpenFile>, "OpenFile"},
{9, D<&IFileSystem::OpenDirectory>, "OpenDirectory"},
{10, D<&IFileSystem::Commit>, "Commit"},
{11, D<&IFileSystem::GetFreeSpaceSize>, "GetFreeSpaceSize"},
{12, D<&IFileSystem::GetTotalSpaceSize>, "GetTotalSpaceSize"},
{13, D<&IFileSystem::CleanDirectoryRecursively>, "CleanDirectoryRecursively"},
{14, D<&IFileSystem::GetFileTimeStampRaw>, "GetFileTimeStampRaw"},
{15, nullptr, "QueryEntry"},
{16, &IFileSystem::GetFileSystemAttribute, "GetFileSystemAttribute"},
{16, D<&IFileSystem::GetFileSystemAttribute>, "GetFileSystemAttribute"},
};
RegisterHandlers(functions);
}
void IFileSystem::CreateFile(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
Result IFileSystem::CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
s32 option, s64 size) {
LOG_DEBUG(Service_FS, "called. file={}, option=0x{:X}, size=0x{:08X}", path->str, option, size);
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
const u64 file_mode = rp.Pop<u64>();
const u32 file_size = rp.Pop<u32>();
LOG_DEBUG(Service_FS, "called. file={}, mode=0x{:X}, size=0x{:08X}", name, file_mode,
file_size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.CreateFile(name, file_size));
R_RETURN(backend->CreateFile(FileSys::Path(path->str), size));
}
void IFileSystem::DeleteFile(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
Result IFileSystem::DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_DEBUG(Service_FS, "called. file={}", path->str);
LOG_DEBUG(Service_FS, "called. file={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteFile(name));
R_RETURN(backend->DeleteFile(FileSys::Path(path->str)));
}
void IFileSystem::CreateDirectory(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
Result IFileSystem::CreateDirectory(
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_DEBUG(Service_FS, "called. directory={}", path->str);
LOG_DEBUG(Service_FS, "called. directory={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.CreateDirectory(name));
R_RETURN(backend->CreateDirectory(FileSys::Path(path->str)));
}
void IFileSystem::DeleteDirectory(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
Result IFileSystem::DeleteDirectory(
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_DEBUG(Service_FS, "called. directory={}", path->str);
LOG_DEBUG(Service_FS, "called. directory={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteDirectory(name));
R_RETURN(backend->DeleteDirectory(FileSys::Path(path->str)));
}
void IFileSystem::DeleteDirectoryRecursively(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
Result IFileSystem::DeleteDirectoryRecursively(
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_DEBUG(Service_FS, "called. directory={}", path->str);
LOG_DEBUG(Service_FS, "called. directory={}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.DeleteDirectoryRecursively(name));
R_RETURN(backend->DeleteDirectoryRecursively(FileSys::Path(path->str)));
}
void IFileSystem::CleanDirectoryRecursively(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
Result IFileSystem::CleanDirectoryRecursively(
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_DEBUG(Service_FS, "called. Directory: {}", path->str);
LOG_DEBUG(Service_FS, "called. Directory: {}", name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.CleanDirectoryRecursively(name));
R_RETURN(backend->CleanDirectoryRecursively(FileSys::Path(path->str)));
}
void IFileSystem::RenameFile(HLERequestContext& ctx) {
const std::string src_name = Common::StringFromBuffer(ctx.ReadBuffer(0));
const std::string dst_name = Common::StringFromBuffer(ctx.ReadBuffer(1));
Result IFileSystem::RenameFile(
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path) {
LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", old_path->str, new_path->str);
LOG_DEBUG(Service_FS, "called. file '{}' to file '{}'", src_name, dst_name);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(backend.RenameFile(src_name, dst_name));
R_RETURN(backend->RenameFile(FileSys::Path(old_path->str), FileSys::Path(new_path->str)));
}
void IFileSystem::OpenFile(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
const auto mode = static_cast<FileSys::OpenMode>(rp.Pop<u32>());
LOG_DEBUG(Service_FS, "called. file={}, mode={}", name, mode);
Result IFileSystem::OpenFile(OutInterface<IFile> out_interface,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
u32 mode) {
LOG_DEBUG(Service_FS, "called. file={}, mode={}", path->str, mode);
FileSys::VirtualFile vfs_file{};
auto result = backend.OpenFile(&vfs_file, name, mode);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
R_TRY(backend->OpenFile(&vfs_file, FileSys::Path(path->str),
static_cast<FileSys::OpenMode>(mode)));
auto file = std::make_shared<IFile>(system, vfs_file);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IFile>(std::move(file));
*out_interface = std::make_shared<IFile>(system, vfs_file);
R_SUCCEED();
}
void IFileSystem::OpenDirectory(HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
const auto mode = rp.PopRaw<FileSys::OpenDirectoryMode>();
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", name, mode);
Result IFileSystem::OpenDirectory(OutInterface<IDirectory> out_interface,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
u32 mode) {
LOG_DEBUG(Service_FS, "called. directory={}, mode={}", path->str, mode);
FileSys::VirtualDir vfs_dir{};
auto result = backend.OpenDirectory(&vfs_dir, name);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
R_TRY(backend->OpenDirectory(&vfs_dir, FileSys::Path(path->str),
static_cast<FileSys::OpenDirectoryMode>(mode)));
auto directory = std::make_shared<IDirectory>(system, vfs_dir, mode);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess);
rb.PushIpcInterface<IDirectory>(std::move(directory));
*out_interface = std::make_shared<IDirectory>(system, vfs_dir,
static_cast<FileSys::OpenDirectoryMode>(mode));
R_SUCCEED();
}
void IFileSystem::GetEntryType(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_DEBUG(Service_FS, "called. file={}", name);
Result IFileSystem::GetEntryType(
Out<u32> out_type, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_DEBUG(Service_FS, "called. file={}", path->str);
FileSys::DirectoryEntryType vfs_entry_type{};
auto result = backend.GetEntryType(&vfs_entry_type, name);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
R_TRY(backend->GetEntryType(&vfs_entry_type, FileSys::Path(path->str)));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(ResultSuccess);
rb.Push<u32>(static_cast<u32>(vfs_entry_type));
*out_type = static_cast<u32>(vfs_entry_type);
R_SUCCEED();
}
void IFileSystem::Commit(HLERequestContext& ctx) {
Result IFileSystem::Commit() {
LOG_WARNING(Service_FS, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
R_SUCCEED();
}
void IFileSystem::GetFreeSpaceSize(HLERequestContext& ctx) {
Result IFileSystem::GetFreeSpaceSize(
Out<s64> out_size, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(size.get_free_size());
*out_size = size_getter.get_free_size();
R_SUCCEED();
}
void IFileSystem::GetTotalSpaceSize(HLERequestContext& ctx) {
Result IFileSystem::GetTotalSpaceSize(
Out<s64> out_size, const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_DEBUG(Service_FS, "called");
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(ResultSuccess);
rb.Push(size.get_total_size());
*out_size = size_getter.get_total_size();
R_SUCCEED();
}
void IFileSystem::GetFileTimeStampRaw(HLERequestContext& ctx) {
const auto file_buffer = ctx.ReadBuffer();
const std::string name = Common::StringFromBuffer(file_buffer);
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", name);
Result IFileSystem::GetFileTimeStampRaw(
Out<FileSys::FileTimeStampRaw> out_timestamp,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path) {
LOG_WARNING(Service_FS, "(Partial Implementation) called. file={}", path->str);
FileSys::FileTimeStampRaw vfs_timestamp{};
auto result = backend.GetFileTimeStampRaw(&vfs_timestamp, name);
if (result != ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
return;
}
R_TRY(backend->GetFileTimeStampRaw(&vfs_timestamp, FileSys::Path(path->str)));
IPC::ResponseBuilder rb{ctx, 10};
rb.Push(ResultSuccess);
rb.PushRaw(vfs_timestamp);
*out_timestamp = vfs_timestamp;
R_SUCCEED();
}
void IFileSystem::GetFileSystemAttribute(HLERequestContext& ctx) {
Result IFileSystem::GetFileSystemAttribute(Out<FileSys::FileSystemAttribute> out_attribute) {
LOG_WARNING(Service_FS, "(STUBBED) called");
struct FileSystemAttribute {
u8 dir_entry_name_length_max_defined;
u8 file_entry_name_length_max_defined;
u8 dir_path_name_length_max_defined;
u8 file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x5);
u8 utf16_dir_entry_name_length_max_defined;
u8 utf16_file_entry_name_length_max_defined;
u8 utf16_dir_path_name_length_max_defined;
u8 utf16_file_path_name_length_max_defined;
INSERT_PADDING_BYTES_NOINIT(0x18);
s32 dir_entry_name_length_max;
s32 file_entry_name_length_max;
s32 dir_path_name_length_max;
s32 file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x5);
s32 utf16_dir_entry_name_length_max;
s32 utf16_file_entry_name_length_max;
s32 utf16_dir_path_name_length_max;
s32 utf16_file_path_name_length_max;
INSERT_PADDING_WORDS_NOINIT(0x18);
INSERT_PADDING_WORDS_NOINIT(0x1);
};
static_assert(sizeof(FileSystemAttribute) == 0xc0, "FileSystemAttribute has incorrect size");
FileSystemAttribute savedata_attribute{};
FileSys::FileSystemAttribute savedata_attribute{};
savedata_attribute.dir_entry_name_length_max_defined = true;
savedata_attribute.file_entry_name_length_max_defined = true;
savedata_attribute.dir_entry_name_length_max = 0x40;
savedata_attribute.file_entry_name_length_max = 0x40;
IPC::ResponseBuilder rb{ctx, 50};
rb.Push(ResultSuccess);
rb.PushRaw(savedata_attribute);
*out_attribute = savedata_attribute;
R_SUCCEED();
}
} // namespace Service::FileSystem

View File

@@ -3,36 +3,58 @@
#pragma once
#include "common/common_funcs.h"
#include "core/file_sys/fs_filesystem.h"
#include "core/file_sys/fsa/fs_i_filesystem.h"
#include "core/file_sys/vfs/vfs.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp/fsp_util.h"
#include "core/hle/service/service.h"
namespace FileSys::Sf {
struct Path;
}
namespace Service::FileSystem {
class IFile;
class IDirectory;
class IFileSystem final : public ServiceFramework<IFileSystem> {
public:
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir backend_, SizeGetter size_);
explicit IFileSystem(Core::System& system_, FileSys::VirtualDir dir_, SizeGetter size_getter_);
void CreateFile(HLERequestContext& ctx);
void DeleteFile(HLERequestContext& ctx);
void CreateDirectory(HLERequestContext& ctx);
void DeleteDirectory(HLERequestContext& ctx);
void DeleteDirectoryRecursively(HLERequestContext& ctx);
void CleanDirectoryRecursively(HLERequestContext& ctx);
void RenameFile(HLERequestContext& ctx);
void OpenFile(HLERequestContext& ctx);
void OpenDirectory(HLERequestContext& ctx);
void GetEntryType(HLERequestContext& ctx);
void Commit(HLERequestContext& ctx);
void GetFreeSpaceSize(HLERequestContext& ctx);
void GetTotalSpaceSize(HLERequestContext& ctx);
void GetFileTimeStampRaw(HLERequestContext& ctx);
void GetFileSystemAttribute(HLERequestContext& ctx);
Result CreateFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, s32 option,
s64 size);
Result DeleteFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result CreateDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result DeleteDirectory(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result DeleteDirectoryRecursively(
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result CleanDirectoryRecursively(
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result RenameFile(const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> old_path,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> new_path);
Result OpenFile(OutInterface<IFile> out_interface,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path, u32 mode);
Result OpenDirectory(OutInterface<IDirectory> out_interface,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path,
u32 mode);
Result GetEntryType(Out<u32> out_type,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result Commit();
Result GetFreeSpaceSize(Out<s64> out_size,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result GetTotalSpaceSize(Out<s64> out_size,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result GetFileTimeStampRaw(Out<FileSys::FileTimeStampRaw> out_timestamp,
const InLargeData<FileSys::Sf::Path, BufferAttr_HipcPointer> path);
Result GetFileSystemAttribute(Out<FileSys::FileSystemAttribute> out_attribute);
private:
VfsDirectoryServiceWrapper backend;
SizeGetter size;
std::unique_ptr<FileSys::Fsa::IFileSystem> backend;
SizeGetter size_getter;
};
} // namespace Service::FileSystem

View File

@@ -436,14 +436,14 @@ Result IApplicationManagerInterface::GetApplicationViewWithPromotionInfo(
Result IApplicationManagerInterface::GetApplicationRightsOnClient(
OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
Common::UUID account_id, u32 flags, u64 application_id) {
u32 flags, u64 application_id, Uid account_id) {
LOG_WARNING(Service_NS, "(STUBBED) called, flags={}, application_id={:016X}, account_id={}",
flags, application_id, account_id.FormattedString());
flags, application_id, account_id.uuid.FormattedString());
if (!out_rights.empty()) {
ApplicationRightsOnClient rights{};
rights.application_id = application_id;
rights.uid = account_id;
rights.uid = account_id.uuid;
rights.flags = 0;
rights.flags2 = 0;

View File

@@ -37,7 +37,7 @@ public:
InArray<u64, BufferAttr_HipcMapAlias> application_ids);
Result GetApplicationRightsOnClient(
OutArray<ApplicationRightsOnClient, BufferAttr_HipcMapAlias> out_rights, Out<u32> out_count,
Common::UUID account_id, u32 flags, u64 application_id);
u32 flags, u64 application_id, Uid account_id);
Result CheckSdCardMountStatus();
Result GetSdCardMountStatusChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetFreeSpaceSize(Out<s64> out_free_space_size, FileSys::StorageId storage_id);

View File

@@ -108,4 +108,9 @@ struct ContentPath {
};
static_assert(sizeof(ContentPath) == 0x10, "ContentPath has incorrect size.");
struct Uid {
alignas(8) Common::UUID uuid;
};
static_assert(sizeof(Uid) == 0x10, "Uid has incorrect size.");
} // namespace Service::NS

View File

@@ -41,8 +41,7 @@ IQueryService::IQueryService(Core::System& system_) : ServiceFramework{system_,
IQueryService::~IQueryService() = default;
Result IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId(
Out<PlayStatistics> out_play_statistics, bool unknown, Common::UUID account_id,
u64 application_id) {
Out<PlayStatistics> out_play_statistics, bool unknown, u64 application_id, Uid account_id) {
// TODO(German77): Read statistics of the game
*out_play_statistics = {
.application_id = application_id,
@@ -50,7 +49,7 @@ Result IQueryService::QueryPlayStatisticsByApplicationIdAndUserAccountId(
};
LOG_WARNING(Service_NS, "(STUBBED) called. unknown={}. application_id={:016X}, account_id={}",
unknown, application_id, account_id.FormattedString());
unknown, application_id, account_id.uuid.FormattedString());
R_SUCCEED();
}

View File

@@ -5,6 +5,7 @@
#include "common/uuid.h"
#include "core/hle/service/cmif_types.h"
#include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/service.h"
namespace Service::NS {
@@ -29,8 +30,7 @@ public:
private:
Result QueryPlayStatisticsByApplicationIdAndUserAccountId(
Out<PlayStatistics> out_play_statistics, bool unknown, Common::UUID account_id,
u64 application_id);
Out<PlayStatistics> out_play_statistics, bool unknown, u64 application_id, Uid account_id);
};
} // namespace Service::NS

View File

@@ -285,7 +285,7 @@ void SharedBufferManager::DestroySession(Kernel::KProcess* owner_process) {
auto& session = it->second;
// Destroy the layer.
R_ASSERT(m_container.DestroyStrayLayer(session.layer_id));
m_container.DestroyStrayLayer(session.layer_id);
// Close nvmap handle.
FreeHandle(session.buffer_nvmap_handle, *m_nvdrv, session.nvmap_fd);

View File

@@ -72,12 +72,19 @@ TextureCache<P>::TextureCache(Runtime& runtime_, Tegra::MaxwellDeviceMemoryManag
template <class P>
void TextureCache<P>::RunGarbageCollector() {
bool high_priority_mode = total_used_memory >= expected_memory;
bool aggressive_mode = total_used_memory >= critical_memory;
const u64 ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
size_t num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
const auto clean_up = [this, &num_iterations, &high_priority_mode,
&aggressive_mode](ImageId image_id) {
bool high_priority_mode = false;
bool aggressive_mode = false;
u64 ticks_to_destroy = 0;
size_t num_iterations = 0;
const auto Configure = [&](bool allow_aggressive) {
high_priority_mode = total_used_memory >= expected_memory;
aggressive_mode = allow_aggressive && total_used_memory >= critical_memory;
ticks_to_destroy = aggressive_mode ? 10ULL : high_priority_mode ? 25ULL : 50ULL;
num_iterations = aggressive_mode ? 40 : (high_priority_mode ? 20 : 10);
};
const auto Cleanup = [this, &num_iterations, &high_priority_mode,
&aggressive_mode](ImageId image_id) {
if (num_iterations == 0) {
return true;
}
@@ -123,7 +130,16 @@ void TextureCache<P>::RunGarbageCollector() {
}
return false;
};
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, clean_up);
// Try to remove anything old enough and not high priority.
Configure(false);
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
// If pressure is still too high, prune aggressively.
if (total_used_memory >= critical_memory) {
Configure(true);
lru_cache.ForEachItemBelow(frame_tick - ticks_to_destroy, Cleanup);
}
}
template <class P>