early-access version 4088
This commit is contained in:
parent
06b2c0cf0c
commit
ef251c7289
160 changed files with 7677 additions and 839 deletions
|
@ -1,7 +1,7 @@
|
||||||
yuzu emulator early access
|
yuzu emulator early access
|
||||||
=============
|
=============
|
||||||
|
|
||||||
This is the source code for early-access 4087.
|
This is the source code for early-access 4088.
|
||||||
|
|
||||||
## Legal Notice
|
## Legal Notice
|
||||||
|
|
||||||
|
|
|
@ -303,6 +303,11 @@ object NativeLibrary {
|
||||||
*/
|
*/
|
||||||
external fun getCpuBackend(): String
|
external fun getCpuBackend(): String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current GPU Driver.
|
||||||
|
*/
|
||||||
|
external fun getGpuDriver(): String
|
||||||
|
|
||||||
external fun applySettings()
|
external fun applySettings()
|
||||||
|
|
||||||
external fun logSettings()
|
external fun logSettings()
|
||||||
|
@ -614,6 +619,11 @@ object NativeLibrary {
|
||||||
*/
|
*/
|
||||||
external fun clearFilesystemProvider()
|
external fun clearFilesystemProvider()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if all necessary keys are present for decryption
|
||||||
|
*/
|
||||||
|
external fun areKeysPresent(): Boolean
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Button type for use in onTouchEvent
|
* Button type for use in onTouchEvent
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -14,16 +14,21 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
* Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
|
* Generic adapter that implements an [AsyncDifferConfig] and covers some of the basic boilerplate
|
||||||
* code used in every [RecyclerView].
|
* code used in every [RecyclerView].
|
||||||
* Type assigned to [Model] must inherit from [Object] in order to be compared properly.
|
* Type assigned to [Model] must inherit from [Object] in order to be compared properly.
|
||||||
|
* @param exact Decides whether each item will be compared by reference or by their contents
|
||||||
*/
|
*/
|
||||||
abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>> :
|
abstract class AbstractDiffAdapter<Model : Any, Holder : AbstractViewHolder<Model>>(
|
||||||
ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>()).build()) {
|
exact: Boolean = true
|
||||||
|
) : ListAdapter<Model, Holder>(AsyncDifferConfig.Builder(DiffCallback<Model>(exact)).build()) {
|
||||||
override fun onBindViewHolder(holder: Holder, position: Int) =
|
override fun onBindViewHolder(holder: Holder, position: Int) =
|
||||||
holder.bind(currentList[position])
|
holder.bind(currentList[position])
|
||||||
|
|
||||||
private class DiffCallback<Model> : DiffUtil.ItemCallback<Model>() {
|
private class DiffCallback<Model>(val exact: Boolean) : DiffUtil.ItemCallback<Model>() {
|
||||||
override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
|
override fun areItemsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
|
||||||
|
if (exact) {
|
||||||
return oldItem === newItem
|
return oldItem === newItem
|
||||||
}
|
}
|
||||||
|
return oldItem == newItem
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("DiffUtilEquals")
|
@SuppressLint("DiffUtilEquals")
|
||||||
override fun areContentsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
|
override fun areContentsTheSame(oldItem: Model & Any, newItem: Model & Any): Boolean {
|
||||||
|
|
|
@ -30,7 +30,7 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
|
||||||
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
import org.yuzu.yuzu_emu.viewholder.AbstractViewHolder
|
||||||
|
|
||||||
class GameAdapter(private val activity: AppCompatActivity) :
|
class GameAdapter(private val activity: AppCompatActivity) :
|
||||||
AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>() {
|
AbstractDiffAdapter<Game, GameAdapter.GameViewHolder>(exact = false) {
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameViewHolder {
|
||||||
CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
CardGameBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
.also { return GameViewHolder(it) }
|
.also { return GameViewHolder(it) }
|
||||||
|
|
|
@ -38,7 +38,6 @@ import androidx.window.layout.WindowLayoutInfo
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.google.android.material.slider.Slider
|
import com.google.android.material.slider.Slider
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
import org.yuzu.yuzu_emu.HomeNavigationDirections
|
||||||
|
@ -141,7 +140,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
|
|
||||||
// So this fragment doesn't restart on configuration changes; i.e. rotation.
|
// So this fragment doesn't restart on configuration changes; i.e. rotation.
|
||||||
retainInstance = true
|
retainInstance = true
|
||||||
emulationState = EmulationState(game.path)
|
emulationState = EmulationState(game.path) {
|
||||||
|
return@EmulationState driverViewModel.isInteractionAllowed.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -370,6 +371,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
||||||
|
driverViewModel.isInteractionAllowed.collect {
|
||||||
|
if (it) {
|
||||||
|
startEmulation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
launch {
|
launch {
|
||||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
emulationViewModel.emulationStarted.collectLatest {
|
emulationViewModel.emulationStarted.collectLatest {
|
||||||
|
@ -398,19 +408,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
launch {
|
|
||||||
repeatOnLifecycle(Lifecycle.State.RESUMED) {
|
|
||||||
driverViewModel.isInteractionAllowed.collect {
|
|
||||||
if (it) {
|
|
||||||
onEmulationStart()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onEmulationStart() {
|
private fun startEmulation() {
|
||||||
if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
|
if (!NativeLibrary.isRunning() && !NativeLibrary.isPaused()) {
|
||||||
if (!DirectoryInitialization.areDirectoriesReady) {
|
if (!DirectoryInitialization.areDirectoriesReady) {
|
||||||
DirectoryInitialization.start()
|
DirectoryInitialization.start()
|
||||||
|
@ -485,12 +486,15 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
val FRAMETIME = 2
|
val FRAMETIME = 2
|
||||||
val SPEED = 3
|
val SPEED = 3
|
||||||
perfStatsUpdater = {
|
perfStatsUpdater = {
|
||||||
if (emulationViewModel.emulationStarted.value) {
|
if (emulationViewModel.emulationStarted.value &&
|
||||||
|
!emulationViewModel.isEmulationStopping.value
|
||||||
|
) {
|
||||||
val perfStats = NativeLibrary.getPerfStats()
|
val perfStats = NativeLibrary.getPerfStats()
|
||||||
val cpuBackend = NativeLibrary.getCpuBackend()
|
val cpuBackend = NativeLibrary.getCpuBackend()
|
||||||
|
val gpuDriver = NativeLibrary.getGpuDriver()
|
||||||
if (_binding != null) {
|
if (_binding != null) {
|
||||||
binding.showFpsText.text =
|
binding.showFpsText.text =
|
||||||
String.format("FPS: %.1f\n%s", perfStats[FPS], cpuBackend)
|
String.format("FPS: %.1f\n%s/%s", perfStats[FPS], cpuBackend, gpuDriver)
|
||||||
}
|
}
|
||||||
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
|
perfStatsUpdateHandler.postDelayed(perfStatsUpdater!!, 800)
|
||||||
}
|
}
|
||||||
|
@ -807,7 +811,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class EmulationState(private val gamePath: String) {
|
private class EmulationState(
|
||||||
|
private val gamePath: String,
|
||||||
|
private val emulationCanStart: () -> Boolean
|
||||||
|
) {
|
||||||
private var state: State
|
private var state: State
|
||||||
private var surface: Surface? = null
|
private var surface: Surface? = null
|
||||||
|
|
||||||
|
@ -901,6 +908,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
State.PAUSED -> Log.warning(
|
State.PAUSED -> Log.warning(
|
||||||
"[EmulationFragment] Surface cleared while emulation paused."
|
"[EmulationFragment] Surface cleared while emulation paused."
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> Log.warning(
|
else -> Log.warning(
|
||||||
"[EmulationFragment] Surface cleared while emulation stopped."
|
"[EmulationFragment] Surface cleared while emulation stopped."
|
||||||
)
|
)
|
||||||
|
@ -910,6 +918,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
|
||||||
|
|
||||||
private fun runWithValidSurface() {
|
private fun runWithValidSurface() {
|
||||||
NativeLibrary.surfaceChanged(surface)
|
NativeLibrary.surfaceChanged(surface)
|
||||||
|
if (!emulationCanStart.invoke()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
when (state) {
|
when (state) {
|
||||||
State.STOPPED -> {
|
State.STOPPED -> {
|
||||||
val emulationThread = Thread({
|
val emulationThread = Thread({
|
||||||
|
|
|
@ -26,9 +26,15 @@ class MessageDialogFragment : DialogFragment() {
|
||||||
val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
|
val descriptionId = requireArguments().getInt(DESCRIPTION_ID)
|
||||||
val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!!
|
val descriptionString = requireArguments().getString(DESCRIPTION_STRING)!!
|
||||||
val helpLinkId = requireArguments().getInt(HELP_LINK)
|
val helpLinkId = requireArguments().getInt(HELP_LINK)
|
||||||
|
val dismissible = requireArguments().getBoolean(DISMISSIBLE)
|
||||||
|
val clearPositiveAction = requireArguments().getBoolean(CLEAR_POSITIVE_ACTION)
|
||||||
|
|
||||||
val builder = MaterialAlertDialogBuilder(requireContext())
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
|
||||||
|
if (clearPositiveAction) {
|
||||||
|
messageDialogViewModel.positiveAction = null
|
||||||
|
}
|
||||||
|
|
||||||
if (messageDialogViewModel.positiveAction == null) {
|
if (messageDialogViewModel.positiveAction == null) {
|
||||||
builder.setPositiveButton(R.string.close, null)
|
builder.setPositiveButton(R.string.close, null)
|
||||||
} else {
|
} else {
|
||||||
|
@ -51,6 +57,8 @@ class MessageDialogFragment : DialogFragment() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isCancelable = dismissible
|
||||||
|
|
||||||
return builder.show()
|
return builder.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,6 +75,8 @@ class MessageDialogFragment : DialogFragment() {
|
||||||
private const val DESCRIPTION_ID = "DescriptionId"
|
private const val DESCRIPTION_ID = "DescriptionId"
|
||||||
private const val DESCRIPTION_STRING = "DescriptionString"
|
private const val DESCRIPTION_STRING = "DescriptionString"
|
||||||
private const val HELP_LINK = "Link"
|
private const val HELP_LINK = "Link"
|
||||||
|
private const val DISMISSIBLE = "Dismissible"
|
||||||
|
private const val CLEAR_POSITIVE_ACTION = "ClearPositiveAction"
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
activity: FragmentActivity? = null,
|
activity: FragmentActivity? = null,
|
||||||
|
@ -75,22 +85,28 @@ class MessageDialogFragment : DialogFragment() {
|
||||||
descriptionId: Int = 0,
|
descriptionId: Int = 0,
|
||||||
descriptionString: String = "",
|
descriptionString: String = "",
|
||||||
helpLinkId: Int = 0,
|
helpLinkId: Int = 0,
|
||||||
|
dismissible: Boolean = true,
|
||||||
positiveAction: (() -> Unit)? = null
|
positiveAction: (() -> Unit)? = null
|
||||||
): MessageDialogFragment {
|
): MessageDialogFragment {
|
||||||
val dialog = MessageDialogFragment()
|
var clearPositiveAction = false
|
||||||
val bundle = Bundle()
|
|
||||||
bundle.apply {
|
|
||||||
putInt(TITLE_ID, titleId)
|
|
||||||
putString(TITLE_STRING, titleString)
|
|
||||||
putInt(DESCRIPTION_ID, descriptionId)
|
|
||||||
putString(DESCRIPTION_STRING, descriptionString)
|
|
||||||
putInt(HELP_LINK, helpLinkId)
|
|
||||||
}
|
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
|
ViewModelProvider(activity)[MessageDialogViewModel::class.java].apply {
|
||||||
clear()
|
clear()
|
||||||
this.positiveAction = positiveAction
|
this.positiveAction = positiveAction
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
clearPositiveAction = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val dialog = MessageDialogFragment()
|
||||||
|
val bundle = Bundle().apply {
|
||||||
|
putInt(TITLE_ID, titleId)
|
||||||
|
putString(TITLE_STRING, titleString)
|
||||||
|
putInt(DESCRIPTION_ID, descriptionId)
|
||||||
|
putString(DESCRIPTION_STRING, descriptionString)
|
||||||
|
putInt(HELP_LINK, helpLinkId)
|
||||||
|
putBoolean(DISMISSIBLE, dismissible)
|
||||||
|
putBoolean(CLEAR_POSITIVE_ACTION, clearPositiveAction)
|
||||||
}
|
}
|
||||||
dialog.arguments = bundle
|
dialog.arguments = bundle
|
||||||
return dialog
|
return dialog
|
||||||
|
|
|
@ -31,6 +31,7 @@ import androidx.preference.PreferenceManager
|
||||||
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
|
||||||
import com.google.android.material.transition.MaterialFadeThrough
|
import com.google.android.material.transition.MaterialFadeThrough
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.yuzu.yuzu_emu.NativeLibrary
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import org.yuzu.yuzu_emu.R
|
import org.yuzu.yuzu_emu.R
|
||||||
import org.yuzu.yuzu_emu.YuzuApplication
|
import org.yuzu.yuzu_emu.YuzuApplication
|
||||||
|
@ -162,7 +163,7 @@ class SetupFragment : Fragment() {
|
||||||
R.string.install_prod_keys_warning_help,
|
R.string.install_prod_keys_warning_help,
|
||||||
{
|
{
|
||||||
val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
|
val file = File(DirectoryInitialization.userDirectory + "/keys/prod.keys")
|
||||||
if (file.exists()) {
|
if (file.exists() && NativeLibrary.areKeysPresent()) {
|
||||||
StepState.COMPLETE
|
StepState.COMPLETE
|
||||||
} else {
|
} else {
|
||||||
StepState.INCOMPLETE
|
StepState.INCOMPLETE
|
||||||
|
@ -347,7 +348,8 @@ class SetupFragment : Fragment() {
|
||||||
val getProdKey =
|
val getProdKey =
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
registerForActivityResult(ActivityResultContracts.OpenDocument()) { result ->
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
if (mainActivity.processKey(result)) {
|
mainActivity.processKey(result)
|
||||||
|
if (NativeLibrary.areKeysPresent()) {
|
||||||
keyCallback.onStepCompleted()
|
keyCallback.onStepCompleted()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,6 +144,7 @@ class DriverViewModel : ViewModel() {
|
||||||
val selectedDriverFile = File(StringSetting.DRIVER_PATH.getString())
|
val selectedDriverFile = File(StringSetting.DRIVER_PATH.getString())
|
||||||
val selectedDriverMetadata = GpuDriverHelper.customDriverSettingData
|
val selectedDriverMetadata = GpuDriverHelper.customDriverSettingData
|
||||||
if (GpuDriverHelper.installedCustomDriverData == selectedDriverMetadata) {
|
if (GpuDriverHelper.installedCustomDriverData == selectedDriverMetadata) {
|
||||||
|
setDriverReady()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,11 +70,19 @@ class Game(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
if (other !is Game) {
|
if (this === other) return true
|
||||||
return false
|
if (javaClass != other?.javaClass) return false
|
||||||
}
|
|
||||||
|
|
||||||
return hashCode() == other.hashCode()
|
other as Game
|
||||||
|
|
||||||
|
if (title != other.title) return false
|
||||||
|
if (path != other.path) return false
|
||||||
|
if (programId != other.programId) return false
|
||||||
|
if (developer != other.developer) return false
|
||||||
|
if (version != other.version) return false
|
||||||
|
if (isHomebrew != other.isHomebrew) return false
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
|
|
|
@ -31,6 +31,9 @@ class HomeViewModel : ViewModel() {
|
||||||
private val _reloadPropertiesList = MutableStateFlow(false)
|
private val _reloadPropertiesList = MutableStateFlow(false)
|
||||||
val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
|
val reloadPropertiesList get() = _reloadPropertiesList.asStateFlow()
|
||||||
|
|
||||||
|
private val _checkKeys = MutableStateFlow(false)
|
||||||
|
val checkKeys = _checkKeys.asStateFlow()
|
||||||
|
|
||||||
var navigatedToSetup = false
|
var navigatedToSetup = false
|
||||||
|
|
||||||
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
|
fun setNavigationVisibility(visible: Boolean, animated: Boolean) {
|
||||||
|
@ -66,4 +69,8 @@ class HomeViewModel : ViewModel() {
|
||||||
fun reloadPropertiesList(reload: Boolean) {
|
fun reloadPropertiesList(reload: Boolean) {
|
||||||
_reloadPropertiesList.value = reload
|
_reloadPropertiesList.value = reload
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setCheckKeys(value: Boolean) {
|
||||||
|
_checkKeys.value = value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,9 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
|
|
||||||
override var themeId: Int = 0
|
override var themeId: Int = 0
|
||||||
|
|
||||||
|
private val CHECKED_DECRYPTION = "CheckedDecryption"
|
||||||
|
private var checkedDecryption = false
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
val splashScreen = installSplashScreen()
|
val splashScreen = installSplashScreen()
|
||||||
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
|
splashScreen.setKeepOnScreenCondition { !DirectoryInitialization.areDirectoriesReady }
|
||||||
|
@ -75,6 +78,18 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
checkedDecryption = savedInstanceState.getBoolean(CHECKED_DECRYPTION)
|
||||||
|
}
|
||||||
|
if (!checkedDecryption) {
|
||||||
|
val firstTimeSetup = PreferenceManager.getDefaultSharedPreferences(applicationContext)
|
||||||
|
.getBoolean(Settings.PREF_FIRST_APP_LAUNCH, true)
|
||||||
|
if (!firstTimeSetup) {
|
||||||
|
checkKeys()
|
||||||
|
}
|
||||||
|
checkedDecryption = true
|
||||||
|
}
|
||||||
|
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
|
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING)
|
||||||
|
|
||||||
|
@ -150,6 +165,16 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
launch {
|
||||||
|
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||||
|
homeViewModel.checkKeys.collect {
|
||||||
|
if (it) {
|
||||||
|
checkKeys()
|
||||||
|
homeViewModel.setCheckKeys(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dismiss previous notifications (should not happen unless a crash occurred)
|
// Dismiss previous notifications (should not happen unless a crash occurred)
|
||||||
|
@ -158,6 +183,21 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
setInsets()
|
setInsets()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun checkKeys() {
|
||||||
|
if (!NativeLibrary.areKeysPresent()) {
|
||||||
|
MessageDialogFragment.newInstance(
|
||||||
|
titleId = R.string.keys_missing,
|
||||||
|
descriptionId = R.string.keys_missing_description,
|
||||||
|
helpLinkId = R.string.keys_missing_help
|
||||||
|
).show(supportFragmentManager, MessageDialogFragment.TAG)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putBoolean(CHECKED_DECRYPTION, checkedDecryption)
|
||||||
|
}
|
||||||
|
|
||||||
fun finishSetup(navController: NavController) {
|
fun finishSetup(navController: NavController) {
|
||||||
navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment)
|
navController.navigate(R.id.action_firstTimeSetupFragment_to_gamesFragment)
|
||||||
(binding.navigationView as NavigationBarView).setupWithNavController(navController)
|
(binding.navigationView as NavigationBarView).setupWithNavController(navController)
|
||||||
|
@ -349,6 +389,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
R.string.install_keys_success,
|
R.string.install_keys_success,
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
|
homeViewModel.setCheckKeys(true)
|
||||||
gamesViewModel.reloadGames(true)
|
gamesViewModel.reloadGames(true)
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
|
@ -399,6 +440,7 @@ class MainActivity : AppCompatActivity(), ThemeProvider {
|
||||||
firmwarePath.deleteRecursively()
|
firmwarePath.deleteRecursively()
|
||||||
cacheFirmwareDir.copyRecursively(firmwarePath, true)
|
cacheFirmwareDir.copyRecursively(firmwarePath, true)
|
||||||
NativeLibrary.initializeSystem(true)
|
NativeLibrary.initializeSystem(true)
|
||||||
|
homeViewModel.setCheckKeys(true)
|
||||||
getString(R.string.save_file_imported_success)
|
getString(R.string.save_file_imported_success)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <core/core.h>
|
#include "core/core.h"
|
||||||
#include <core/file_sys/mode.h>
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
#include <core/file_sys/patch_manager.h>
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include <core/loader/nro.h>
|
|
||||||
#include <jni.h>
|
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
#include "core/loader/nro.h"
|
||||||
|
#include "jni.h"
|
||||||
#include "jni/android_common/android_common.h"
|
#include "jni/android_common/android_common.h"
|
||||||
#include "native.h"
|
#include "native.h"
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ extern "C" {
|
||||||
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
|
jboolean Java_org_yuzu_yuzu_1emu_utils_GameMetadata_getIsValid(JNIEnv* env, jobject obj,
|
||||||
jstring jpath) {
|
jstring jpath) {
|
||||||
const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
const auto file = EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
||||||
GetJString(env, jpath), FileSys::Mode::Read);
|
GetJString(env, jpath), FileSys::OpenMode::Read);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,9 +35,10 @@
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
#include "core/file_sys/card_image.h"
|
#include "core/file_sys/card_image.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
#include "core/file_sys/submission_package.h"
|
#include "core/file_sys/submission_package.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/file_sys/vfs_real.h"
|
#include "core/file_sys/vfs/vfs_real.h"
|
||||||
#include "core/frontend/applets/cabinet.h"
|
#include "core/frontend/applets/cabinet.h"
|
||||||
#include "core/frontend/applets/controller.h"
|
#include "core/frontend/applets/controller.h"
|
||||||
#include "core/frontend/applets/error.h"
|
#include "core/frontend/applets/error.h"
|
||||||
|
@ -154,7 +155,7 @@ void EmulationSession::SurfaceChanged() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) {
|
void EmulationSession::ConfigureFilesystemProvider(const std::string& filepath) {
|
||||||
const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::Mode::Read);
|
const auto file = m_system.GetFilesystem()->OpenFile(filepath, FileSys::OpenMode::Read);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -247,6 +248,7 @@ Core::SystemResultStatus EmulationSession::InitializeEmulation(const std::string
|
||||||
m_system.GetCpuManager().OnGpuReady();
|
m_system.GetCpuManager().OnGpuReady();
|
||||||
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
m_system.RegisterExitCallback([&] { HaltEmulation(); });
|
||||||
|
|
||||||
|
OnEmulationStarted();
|
||||||
return Core::SystemResultStatus::Success;
|
return Core::SystemResultStatus::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,8 +465,8 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
|
||||||
};
|
};
|
||||||
|
|
||||||
return static_cast<int>(
|
return static_cast<int>(
|
||||||
ContentManager::InstallNSP(&EmulationSession::GetInstance().System(),
|
ContentManager::InstallNSP(EmulationSession::GetInstance().System(),
|
||||||
EmulationSession::GetInstance().System().GetFilesystem().get(),
|
*EmulationSession::GetInstance().System().GetFilesystem(),
|
||||||
GetJString(env, j_file), callback));
|
GetJString(env, j_file), callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -474,8 +476,8 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
|
||||||
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
|
u64 program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
std::string updatePath = GetJString(env, jupdatePath);
|
std::string updatePath = GetJString(env, jupdatePath);
|
||||||
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
|
||||||
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(updatePath,
|
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
|
||||||
FileSys::Mode::Read));
|
updatePath, FileSys::OpenMode::Read));
|
||||||
for (const auto& item : nsp->GetNCAs()) {
|
for (const auto& item : nsp->GetNCAs()) {
|
||||||
for (const auto& nca_details : item.second) {
|
for (const auto& nca_details : item.second) {
|
||||||
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
|
||||||
|
@ -674,6 +676,11 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getCpuBackend(JNIEnv* env, jclass
|
||||||
return ToJString(env, "JIT");
|
return ToJString(env, "JIT");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getGpuDriver(JNIEnv* env, jobject jobj) {
|
||||||
|
return ToJString(env,
|
||||||
|
EmulationSession::GetInstance().System().GPU().Renderer().GetDeviceVendor());
|
||||||
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_applySettings(JNIEnv* env, jobject jobj) {
|
||||||
EmulationSession::GetInstance().System().ApplySettings();
|
EmulationSession::GetInstance().System().ApplySettings();
|
||||||
}
|
}
|
||||||
|
@ -713,7 +720,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_initializeEmptyUserDirectory(JNIEnv*
|
||||||
jobject instance) {
|
jobject instance) {
|
||||||
const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
|
const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
|
||||||
auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory(
|
auto vfs_nand_dir = EmulationSession::GetInstance().System().GetFilesystem()->OpenDirectory(
|
||||||
Common::FS::PathToUTF8String(nand_dir), FileSys::Mode::Read);
|
Common::FS::PathToUTF8String(nand_dir), FileSys::OpenMode::Read);
|
||||||
|
|
||||||
const auto user_id = EmulationSession::GetInstance().System().GetProfileManager().GetUser(
|
const auto user_id = EmulationSession::GetInstance().System().GetProfileManager().GetUser(
|
||||||
static_cast<std::size_t>(0));
|
static_cast<std::size_t>(0));
|
||||||
|
@ -819,7 +826,7 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeUpdate(JNIEnv* env, jobject job
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeDLC(JNIEnv* env, jobject jobj,
|
||||||
jstring jprogramId) {
|
jstring jprogramId) {
|
||||||
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
auto program_id = EmulationSession::GetProgramId(env, jprogramId);
|
||||||
ContentManager::RemoveAllDLC(&EmulationSession::GetInstance().System(), program_id);
|
ContentManager::RemoveAllDLC(EmulationSession::GetInstance().System(), program_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
|
void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj, jstring jprogramId,
|
||||||
|
@ -829,7 +836,8 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_removeMod(JNIEnv* env, jobject jobj,
|
||||||
program_id, GetJString(env, jname));
|
program_id, GetJString(env, jname));
|
||||||
}
|
}
|
||||||
|
|
||||||
jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env, jobject jobj,
|
jobjectArray Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* env,
|
||||||
|
jobject jobj,
|
||||||
jobject jcallback) {
|
jobject jcallback) {
|
||||||
auto jlambdaClass = env->GetObjectClass(jcallback);
|
auto jlambdaClass = env->GetObjectClass(jcallback);
|
||||||
auto jlambdaInvokeMethod = env->GetMethodID(
|
auto jlambdaInvokeMethod = env->GetMethodID(
|
||||||
|
@ -842,7 +850,7 @@ jobject Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyInstalledContents(JNIEnv* en
|
||||||
|
|
||||||
auto& session = EmulationSession::GetInstance();
|
auto& session = EmulationSession::GetInstance();
|
||||||
std::vector<std::string> result = ContentManager::VerifyInstalledContents(
|
std::vector<std::string> result = ContentManager::VerifyInstalledContents(
|
||||||
&session.System(), session.GetContentProvider(), callback);
|
session.System(), *session.GetContentProvider(), callback);
|
||||||
jobjectArray jresult =
|
jobjectArray jresult =
|
||||||
env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, ""));
|
env->NewObjectArray(result.size(), IDCache::GetStringClass(), ToJString(env, ""));
|
||||||
for (size_t i = 0; i < result.size(); ++i) {
|
for (size_t i = 0; i < result.size(); ++i) {
|
||||||
|
@ -863,7 +871,7 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_verifyGameContents(JNIEnv* env, jobje
|
||||||
};
|
};
|
||||||
auto& session = EmulationSession::GetInstance();
|
auto& session = EmulationSession::GetInstance();
|
||||||
return static_cast<jint>(
|
return static_cast<jint>(
|
||||||
ContentManager::VerifyGameContents(&session.System(), GetJString(env, jpath), callback));
|
ContentManager::VerifyGameContents(session.System(), GetJString(env, jpath), callback));
|
||||||
}
|
}
|
||||||
|
|
||||||
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject jobj,
|
||||||
|
@ -882,7 +890,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getSavePath(JNIEnv* env, jobject j
|
||||||
|
|
||||||
const auto nandDir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
|
const auto nandDir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir);
|
||||||
auto vfsNandDir = system.GetFilesystem()->OpenDirectory(Common::FS::PathToUTF8String(nandDir),
|
auto vfsNandDir = system.GetFilesystem()->OpenDirectory(Common::FS::PathToUTF8String(nandDir),
|
||||||
FileSys::Mode::Read);
|
FileSys::OpenMode::Read);
|
||||||
|
|
||||||
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath(
|
||||||
{}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
|
{}, vfsNandDir, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData,
|
||||||
|
@ -912,4 +920,10 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_clearFilesystemProvider(JNIEnv* env,
|
||||||
EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
|
EmulationSession::GetInstance().GetContentProvider()->ClearAllEntries();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_areKeysPresent(JNIEnv* env, jobject jobj) {
|
||||||
|
auto& system = EmulationSession::GetInstance().System();
|
||||||
|
system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
|
||||||
|
return ContentManager::AreKeysPresent();
|
||||||
|
}
|
||||||
|
|
||||||
} // extern "C"
|
} // extern "C"
|
||||||
|
|
|
@ -144,6 +144,9 @@
|
||||||
<string name="no_save_data_found">No save data found</string>
|
<string name="no_save_data_found">No save data found</string>
|
||||||
<string name="verify_installed_content">Verify installed content</string>
|
<string name="verify_installed_content">Verify installed content</string>
|
||||||
<string name="verify_installed_content_description">Checks all installed content for corruption</string>
|
<string name="verify_installed_content_description">Checks all installed content for corruption</string>
|
||||||
|
<string name="keys_missing">Encryption keys are missing</string>
|
||||||
|
<string name="keys_missing_description">Firmware and retail games cannot be decrypted</string>
|
||||||
|
<string name="keys_missing_help">https://yuzu-emu.org/help/quickstart/#dumping-decryption-keys</string>
|
||||||
|
|
||||||
<!-- Applet launcher strings -->
|
<!-- Applet launcher strings -->
|
||||||
<string name="applets">Applet launcher</string>
|
<string name="applets">Applet launcher</string>
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include "bit_cast.h"
|
#include "bit_cast.h"
|
||||||
|
|
||||||
|
@ -19,4 +20,21 @@ inline T WrappingAdd(T lhs, T rhs) {
|
||||||
return BitCast<T>(lhs_u + rhs_u);
|
return BitCast<T>(lhs_u + rhs_u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
requires(std::is_integral_v<T> && std::is_signed_v<T>)
|
||||||
|
inline bool CanAddWithoutOverflow(T lhs, T rhs) {
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
if (lhs >= 0 && rhs >= 0) {
|
||||||
|
return WrappingAdd(lhs, rhs) >= std::max(lhs, rhs);
|
||||||
|
} else if (lhs < 0 && rhs < 0) {
|
||||||
|
return WrappingAdd(lhs, rhs) <= std::min(lhs, rhs);
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
T res;
|
||||||
|
return !__builtin_add_overflow(lhs, rhs, &res);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Common
|
} // namespace Common
|
||||||
|
|
|
@ -20,28 +20,49 @@ add_library(core STATIC
|
||||||
cpu_manager.h
|
cpu_manager.h
|
||||||
crypto/aes_util.cpp
|
crypto/aes_util.cpp
|
||||||
crypto/aes_util.h
|
crypto/aes_util.h
|
||||||
|
crypto/ctr_encryption_layer.cpp
|
||||||
|
crypto/ctr_encryption_layer.h
|
||||||
crypto/encryption_layer.cpp
|
crypto/encryption_layer.cpp
|
||||||
crypto/encryption_layer.h
|
crypto/encryption_layer.h
|
||||||
crypto/key_manager.cpp
|
crypto/key_manager.cpp
|
||||||
crypto/key_manager.h
|
crypto/key_manager.h
|
||||||
crypto/partition_data_manager.cpp
|
crypto/partition_data_manager.cpp
|
||||||
crypto/partition_data_manager.h
|
crypto/partition_data_manager.h
|
||||||
crypto/ctr_encryption_layer.cpp
|
|
||||||
crypto/ctr_encryption_layer.h
|
|
||||||
crypto/xts_encryption_layer.cpp
|
crypto/xts_encryption_layer.cpp
|
||||||
crypto/xts_encryption_layer.h
|
crypto/xts_encryption_layer.h
|
||||||
debugger/debugger_interface.h
|
|
||||||
debugger/debugger.cpp
|
debugger/debugger.cpp
|
||||||
debugger/debugger.h
|
debugger/debugger.h
|
||||||
debugger/gdbstub_arch.cpp
|
debugger/debugger_interface.h
|
||||||
debugger/gdbstub_arch.h
|
|
||||||
debugger/gdbstub.cpp
|
debugger/gdbstub.cpp
|
||||||
debugger/gdbstub.h
|
debugger/gdbstub.h
|
||||||
|
debugger/gdbstub_arch.cpp
|
||||||
|
debugger/gdbstub_arch.h
|
||||||
device_memory_manager.h
|
device_memory_manager.h
|
||||||
device_memory_manager.inc
|
device_memory_manager.inc
|
||||||
device_memory.cpp
|
device_memory.cpp
|
||||||
device_memory.h
|
device_memory.h
|
||||||
|
file_sys/bis_factory.cpp
|
||||||
|
file_sys/bis_factory.h
|
||||||
|
file_sys/card_image.cpp
|
||||||
|
file_sys/card_image.h
|
||||||
|
file_sys/common_funcs.h
|
||||||
|
file_sys/content_archive.cpp
|
||||||
|
file_sys/content_archive.h
|
||||||
|
file_sys/control_metadata.cpp
|
||||||
|
file_sys/control_metadata.h
|
||||||
|
file_sys/errors.h
|
||||||
|
file_sys/fs_directory.h
|
||||||
|
file_sys/fs_file.h
|
||||||
|
file_sys/fs_filesystem.h
|
||||||
|
file_sys/fs_memory_management.h
|
||||||
|
file_sys/fs_operate_range.h
|
||||||
|
file_sys/fs_path.h
|
||||||
|
file_sys/fs_path_utility.h
|
||||||
|
file_sys/fs_string_util.h
|
||||||
|
file_sys/fsmitm_romfsbuild.cpp
|
||||||
|
file_sys/fsmitm_romfsbuild.h
|
||||||
file_sys/fssystem/fs_i_storage.h
|
file_sys/fssystem/fs_i_storage.h
|
||||||
|
file_sys/fssystem/fs_types.h
|
||||||
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
|
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.cpp
|
||||||
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h
|
file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h
|
||||||
file_sys/fssystem/fssystem_aes_ctr_storage.cpp
|
file_sys/fssystem/fssystem_aes_ctr_storage.cpp
|
||||||
|
@ -83,25 +104,10 @@ add_library(core STATIC
|
||||||
file_sys/fssystem/fssystem_switch_storage.h
|
file_sys/fssystem/fssystem_switch_storage.h
|
||||||
file_sys/fssystem/fssystem_utility.cpp
|
file_sys/fssystem/fssystem_utility.cpp
|
||||||
file_sys/fssystem/fssystem_utility.h
|
file_sys/fssystem/fssystem_utility.h
|
||||||
file_sys/fssystem/fs_types.h
|
|
||||||
file_sys/bis_factory.cpp
|
|
||||||
file_sys/bis_factory.h
|
|
||||||
file_sys/card_image.cpp
|
|
||||||
file_sys/card_image.h
|
|
||||||
file_sys/common_funcs.h
|
|
||||||
file_sys/content_archive.cpp
|
|
||||||
file_sys/content_archive.h
|
|
||||||
file_sys/control_metadata.cpp
|
|
||||||
file_sys/control_metadata.h
|
|
||||||
file_sys/directory.h
|
|
||||||
file_sys/errors.h
|
|
||||||
file_sys/fsmitm_romfsbuild.cpp
|
|
||||||
file_sys/fsmitm_romfsbuild.h
|
|
||||||
file_sys/ips_layer.cpp
|
file_sys/ips_layer.cpp
|
||||||
file_sys/ips_layer.h
|
file_sys/ips_layer.h
|
||||||
file_sys/kernel_executable.cpp
|
file_sys/kernel_executable.cpp
|
||||||
file_sys/kernel_executable.h
|
file_sys/kernel_executable.h
|
||||||
file_sys/mode.h
|
|
||||||
file_sys/nca_metadata.cpp
|
file_sys/nca_metadata.cpp
|
||||||
file_sys/nca_metadata.h
|
file_sys/nca_metadata.h
|
||||||
file_sys/partition_filesystem.cpp
|
file_sys/partition_filesystem.cpp
|
||||||
|
@ -146,22 +152,22 @@ add_library(core STATIC
|
||||||
file_sys/system_archive/system_version.h
|
file_sys/system_archive/system_version.h
|
||||||
file_sys/system_archive/time_zone_binary.cpp
|
file_sys/system_archive/time_zone_binary.cpp
|
||||||
file_sys/system_archive/time_zone_binary.h
|
file_sys/system_archive/time_zone_binary.h
|
||||||
file_sys/vfs.cpp
|
file_sys/vfs/vfs.cpp
|
||||||
file_sys/vfs.h
|
file_sys/vfs/vfs.h
|
||||||
file_sys/vfs_cached.cpp
|
file_sys/vfs/vfs_cached.cpp
|
||||||
file_sys/vfs_cached.h
|
file_sys/vfs/vfs_cached.h
|
||||||
file_sys/vfs_concat.cpp
|
file_sys/vfs/vfs_concat.cpp
|
||||||
file_sys/vfs_concat.h
|
file_sys/vfs/vfs_concat.h
|
||||||
file_sys/vfs_layered.cpp
|
file_sys/vfs/vfs_layered.cpp
|
||||||
file_sys/vfs_layered.h
|
file_sys/vfs/vfs_layered.h
|
||||||
file_sys/vfs_offset.cpp
|
file_sys/vfs/vfs_offset.cpp
|
||||||
file_sys/vfs_offset.h
|
file_sys/vfs/vfs_offset.h
|
||||||
file_sys/vfs_real.cpp
|
file_sys/vfs/vfs_real.cpp
|
||||||
file_sys/vfs_real.h
|
file_sys/vfs/vfs_real.h
|
||||||
file_sys/vfs_static.h
|
file_sys/vfs/vfs_static.h
|
||||||
file_sys/vfs_types.h
|
file_sys/vfs/vfs_types.h
|
||||||
file_sys/vfs_vector.cpp
|
file_sys/vfs/vfs_vector.cpp
|
||||||
file_sys/vfs_vector.h
|
file_sys/vfs/vfs_vector.h
|
||||||
file_sys/xts_archive.cpp
|
file_sys/xts_archive.cpp
|
||||||
file_sys/xts_archive.h
|
file_sys/xts_archive.h
|
||||||
frontend/applets/cabinet.cpp
|
frontend/applets/cabinet.cpp
|
||||||
|
@ -194,7 +200,6 @@ add_library(core STATIC
|
||||||
hle/kernel/board/nintendo/nx/secure_monitor.h
|
hle/kernel/board/nintendo/nx/secure_monitor.h
|
||||||
hle/kernel/code_set.cpp
|
hle/kernel/code_set.cpp
|
||||||
hle/kernel/code_set.h
|
hle/kernel/code_set.h
|
||||||
hle/kernel/svc_results.h
|
|
||||||
hle/kernel/global_scheduler_context.cpp
|
hle/kernel/global_scheduler_context.cpp
|
||||||
hle/kernel/global_scheduler_context.h
|
hle/kernel/global_scheduler_context.h
|
||||||
hle/kernel/init/init_slab_setup.cpp
|
hle/kernel/init/init_slab_setup.cpp
|
||||||
|
@ -204,11 +209,11 @@ add_library(core STATIC
|
||||||
hle/kernel/k_address_arbiter.h
|
hle/kernel/k_address_arbiter.h
|
||||||
hle/kernel/k_address_space_info.cpp
|
hle/kernel/k_address_space_info.cpp
|
||||||
hle/kernel/k_address_space_info.h
|
hle/kernel/k_address_space_info.h
|
||||||
|
hle/kernel/k_affinity_mask.h
|
||||||
hle/kernel/k_auto_object.cpp
|
hle/kernel/k_auto_object.cpp
|
||||||
hle/kernel/k_auto_object.h
|
hle/kernel/k_auto_object.h
|
||||||
hle/kernel/k_auto_object_container.cpp
|
hle/kernel/k_auto_object_container.cpp
|
||||||
hle/kernel/k_auto_object_container.h
|
hle/kernel/k_auto_object_container.h
|
||||||
hle/kernel/k_affinity_mask.h
|
|
||||||
hle/kernel/k_capabilities.cpp
|
hle/kernel/k_capabilities.cpp
|
||||||
hle/kernel/k_capabilities.h
|
hle/kernel/k_capabilities.h
|
||||||
hle/kernel/k_class_token.cpp
|
hle/kernel/k_class_token.cpp
|
||||||
|
@ -232,9 +237,9 @@ add_library(core STATIC
|
||||||
hle/kernel/k_event_info.h
|
hle/kernel/k_event_info.h
|
||||||
hle/kernel/k_handle_table.cpp
|
hle/kernel/k_handle_table.cpp
|
||||||
hle/kernel/k_handle_table.h
|
hle/kernel/k_handle_table.h
|
||||||
hle/kernel/k_hardware_timer_base.h
|
|
||||||
hle/kernel/k_hardware_timer.cpp
|
hle/kernel/k_hardware_timer.cpp
|
||||||
hle/kernel/k_hardware_timer.h
|
hle/kernel/k_hardware_timer.h
|
||||||
|
hle/kernel/k_hardware_timer_base.h
|
||||||
hle/kernel/k_interrupt_manager.cpp
|
hle/kernel/k_interrupt_manager.cpp
|
||||||
hle/kernel/k_interrupt_manager.h
|
hle/kernel/k_interrupt_manager.h
|
||||||
hle/kernel/k_light_client_session.cpp
|
hle/kernel/k_light_client_session.cpp
|
||||||
|
@ -261,10 +266,10 @@ add_library(core STATIC
|
||||||
hle/kernel/k_page_bitmap.h
|
hle/kernel/k_page_bitmap.h
|
||||||
hle/kernel/k_page_buffer.cpp
|
hle/kernel/k_page_buffer.cpp
|
||||||
hle/kernel/k_page_buffer.h
|
hle/kernel/k_page_buffer.h
|
||||||
hle/kernel/k_page_heap.cpp
|
|
||||||
hle/kernel/k_page_heap.h
|
|
||||||
hle/kernel/k_page_group.cpp
|
hle/kernel/k_page_group.cpp
|
||||||
hle/kernel/k_page_group.h
|
hle/kernel/k_page_group.h
|
||||||
|
hle/kernel/k_page_heap.cpp
|
||||||
|
hle/kernel/k_page_heap.h
|
||||||
hle/kernel/k_page_table.h
|
hle/kernel/k_page_table.h
|
||||||
hle/kernel/k_page_table_base.cpp
|
hle/kernel/k_page_table_base.cpp
|
||||||
hle/kernel/k_page_table_base.h
|
hle/kernel/k_page_table_base.h
|
||||||
|
@ -329,8 +334,6 @@ add_library(core STATIC
|
||||||
hle/kernel/slab_helpers.h
|
hle/kernel/slab_helpers.h
|
||||||
hle/kernel/svc.cpp
|
hle/kernel/svc.cpp
|
||||||
hle/kernel/svc.h
|
hle/kernel/svc.h
|
||||||
hle/kernel/svc_common.h
|
|
||||||
hle/kernel/svc_types.h
|
|
||||||
hle/kernel/svc/svc_activity.cpp
|
hle/kernel/svc/svc_activity.cpp
|
||||||
hle/kernel/svc/svc_address_arbiter.cpp
|
hle/kernel/svc/svc_address_arbiter.cpp
|
||||||
hle/kernel/svc/svc_address_translation.cpp
|
hle/kernel/svc/svc_address_translation.cpp
|
||||||
|
@ -368,6 +371,9 @@ add_library(core STATIC
|
||||||
hle/kernel/svc/svc_thread_profiler.cpp
|
hle/kernel/svc/svc_thread_profiler.cpp
|
||||||
hle/kernel/svc/svc_tick.cpp
|
hle/kernel/svc/svc_tick.cpp
|
||||||
hle/kernel/svc/svc_transfer_memory.cpp
|
hle/kernel/svc/svc_transfer_memory.cpp
|
||||||
|
hle/kernel/svc_common.h
|
||||||
|
hle/kernel/svc_results.h
|
||||||
|
hle/kernel/svc_types.h
|
||||||
hle/result.h
|
hle/result.h
|
||||||
hle/service/acc/acc.cpp
|
hle/service/acc/acc.cpp
|
||||||
hle/service/acc/acc.h
|
hle/service/acc/acc.h
|
||||||
|
@ -472,6 +478,8 @@ add_library(core STATIC
|
||||||
hle/service/caps/caps_types.h
|
hle/service/caps/caps_types.h
|
||||||
hle/service/caps/caps_u.cpp
|
hle/service/caps/caps_u.cpp
|
||||||
hle/service/caps/caps_u.h
|
hle/service/caps/caps_u.h
|
||||||
|
hle/service/cmif_serialization.h
|
||||||
|
hle/service/cmif_types.h
|
||||||
hle/service/erpt/erpt.cpp
|
hle/service/erpt/erpt.cpp
|
||||||
hle/service/erpt/erpt.h
|
hle/service/erpt/erpt.h
|
||||||
hle/service/es/es.cpp
|
hle/service/es/es.cpp
|
||||||
|
@ -484,14 +492,25 @@ add_library(core STATIC
|
||||||
hle/service/fatal/fatal_p.h
|
hle/service/fatal/fatal_p.h
|
||||||
hle/service/fatal/fatal_u.cpp
|
hle/service/fatal/fatal_u.cpp
|
||||||
hle/service/fatal/fatal_u.h
|
hle/service/fatal/fatal_u.h
|
||||||
|
hle/service/fgm/fgm.cpp
|
||||||
|
hle/service/fgm/fgm.h
|
||||||
hle/service/filesystem/filesystem.cpp
|
hle/service/filesystem/filesystem.cpp
|
||||||
hle/service/filesystem/filesystem.h
|
hle/service/filesystem/filesystem.h
|
||||||
hle/service/filesystem/fsp_ldr.cpp
|
hle/service/filesystem/fsp/fs_i_directory.cpp
|
||||||
hle/service/filesystem/fsp_ldr.h
|
hle/service/filesystem/fsp/fs_i_directory.h
|
||||||
hle/service/filesystem/fsp_pr.cpp
|
hle/service/filesystem/fsp/fs_i_file.cpp
|
||||||
hle/service/filesystem/fsp_pr.h
|
hle/service/filesystem/fsp/fs_i_file.h
|
||||||
hle/service/filesystem/fsp_srv.cpp
|
hle/service/filesystem/fsp/fs_i_filesystem.cpp
|
||||||
hle/service/filesystem/fsp_srv.h
|
hle/service/filesystem/fsp/fs_i_filesystem.h
|
||||||
|
hle/service/filesystem/fsp/fs_i_storage.cpp
|
||||||
|
hle/service/filesystem/fsp/fs_i_storage.h
|
||||||
|
hle/service/filesystem/fsp/fsp_ldr.cpp
|
||||||
|
hle/service/filesystem/fsp/fsp_ldr.h
|
||||||
|
hle/service/filesystem/fsp/fsp_pr.cpp
|
||||||
|
hle/service/filesystem/fsp/fsp_pr.h
|
||||||
|
hle/service/filesystem/fsp/fsp_srv.cpp
|
||||||
|
hle/service/filesystem/fsp/fsp_srv.h
|
||||||
|
hle/service/filesystem/fsp/fsp_util.h
|
||||||
hle/service/filesystem/romfs_controller.cpp
|
hle/service/filesystem/romfs_controller.cpp
|
||||||
hle/service/filesystem/romfs_controller.h
|
hle/service/filesystem/romfs_controller.h
|
||||||
hle/service/filesystem/save_data_controller.cpp
|
hle/service/filesystem/save_data_controller.cpp
|
||||||
|
@ -549,13 +568,18 @@ add_library(core STATIC
|
||||||
hle/service/hid/irs.h
|
hle/service/hid/irs.h
|
||||||
hle/service/hid/xcd.cpp
|
hle/service/hid/xcd.cpp
|
||||||
hle/service/hid/xcd.h
|
hle/service/hid/xcd.h
|
||||||
|
hle/service/hle_ipc.cpp
|
||||||
|
hle/service/hle_ipc.h
|
||||||
|
hle/service/ipc_helpers.h
|
||||||
|
hle/service/kernel_helpers.cpp
|
||||||
|
hle/service/kernel_helpers.h
|
||||||
hle/service/lbl/lbl.cpp
|
hle/service/lbl/lbl.cpp
|
||||||
hle/service/lbl/lbl.h
|
hle/service/lbl/lbl.h
|
||||||
hle/service/ldn/lan_discovery.cpp
|
hle/service/ldn/lan_discovery.cpp
|
||||||
hle/service/ldn/lan_discovery.h
|
hle/service/ldn/lan_discovery.h
|
||||||
hle/service/ldn/ldn_results.h
|
|
||||||
hle/service/ldn/ldn.cpp
|
hle/service/ldn/ldn.cpp
|
||||||
hle/service/ldn/ldn.h
|
hle/service/ldn/ldn.h
|
||||||
|
hle/service/ldn/ldn_results.h
|
||||||
hle/service/ldn/ldn_types.h
|
hle/service/ldn/ldn_types.h
|
||||||
hle/service/ldr/ldr.cpp
|
hle/service/ldr/ldr.cpp
|
||||||
hle/service/ldr/ldr.h
|
hle/service/ldr/ldr.h
|
||||||
|
@ -563,16 +587,6 @@ add_library(core STATIC
|
||||||
hle/service/lm/lm.h
|
hle/service/lm/lm.h
|
||||||
hle/service/mig/mig.cpp
|
hle/service/mig/mig.cpp
|
||||||
hle/service/mig/mig.h
|
hle/service/mig/mig.h
|
||||||
hle/service/mii/types/char_info.cpp
|
|
||||||
hle/service/mii/types/char_info.h
|
|
||||||
hle/service/mii/types/core_data.cpp
|
|
||||||
hle/service/mii/types/core_data.h
|
|
||||||
hle/service/mii/types/raw_data.cpp
|
|
||||||
hle/service/mii/types/raw_data.h
|
|
||||||
hle/service/mii/types/store_data.cpp
|
|
||||||
hle/service/mii/types/store_data.h
|
|
||||||
hle/service/mii/types/ver3_store_data.cpp
|
|
||||||
hle/service/mii/types/ver3_store_data.h
|
|
||||||
hle/service/mii/mii.cpp
|
hle/service/mii/mii.cpp
|
||||||
hle/service/mii/mii.h
|
hle/service/mii/mii.h
|
||||||
hle/service/mii/mii_database.cpp
|
hle/service/mii/mii_database.cpp
|
||||||
|
@ -584,10 +598,22 @@ add_library(core STATIC
|
||||||
hle/service/mii/mii_result.h
|
hle/service/mii/mii_result.h
|
||||||
hle/service/mii/mii_types.h
|
hle/service/mii/mii_types.h
|
||||||
hle/service/mii/mii_util.h
|
hle/service/mii/mii_util.h
|
||||||
|
hle/service/mii/types/char_info.cpp
|
||||||
|
hle/service/mii/types/char_info.h
|
||||||
|
hle/service/mii/types/core_data.cpp
|
||||||
|
hle/service/mii/types/core_data.h
|
||||||
|
hle/service/mii/types/raw_data.cpp
|
||||||
|
hle/service/mii/types/raw_data.h
|
||||||
|
hle/service/mii/types/store_data.cpp
|
||||||
|
hle/service/mii/types/store_data.h
|
||||||
|
hle/service/mii/types/ver3_store_data.cpp
|
||||||
|
hle/service/mii/types/ver3_store_data.h
|
||||||
hle/service/mm/mm_u.cpp
|
hle/service/mm/mm_u.cpp
|
||||||
hle/service/mm/mm_u.h
|
hle/service/mm/mm_u.h
|
||||||
hle/service/mnpp/mnpp_app.cpp
|
hle/service/mnpp/mnpp_app.cpp
|
||||||
hle/service/mnpp/mnpp_app.h
|
hle/service/mnpp/mnpp_app.h
|
||||||
|
hle/service/mutex.cpp
|
||||||
|
hle/service/mutex.h
|
||||||
hle/service/ncm/ncm.cpp
|
hle/service/ncm/ncm.cpp
|
||||||
hle/service/ncm/ncm.h
|
hle/service/ncm/ncm.h
|
||||||
hle/service/nfc/common/amiibo_crypto.cpp
|
hle/service/nfc/common/amiibo_crypto.cpp
|
||||||
|
@ -757,19 +783,12 @@ add_library(core STATIC
|
||||||
hle/service/ptm/ptm.h
|
hle/service/ptm/ptm.h
|
||||||
hle/service/ptm/ts.cpp
|
hle/service/ptm/ts.cpp
|
||||||
hle/service/ptm/ts.h
|
hle/service/ptm/ts.h
|
||||||
hle/service/hle_ipc.cpp
|
hle/service/ro/ro.cpp
|
||||||
hle/service/hle_ipc.h
|
hle/service/ro/ro.h
|
||||||
hle/service/ipc_helpers.h
|
|
||||||
hle/service/kernel_helpers.cpp
|
|
||||||
hle/service/kernel_helpers.h
|
|
||||||
hle/service/mutex.cpp
|
|
||||||
hle/service/mutex.h
|
|
||||||
hle/service/ro/ro_nro_utils.cpp
|
hle/service/ro/ro_nro_utils.cpp
|
||||||
hle/service/ro/ro_nro_utils.h
|
hle/service/ro/ro_nro_utils.h
|
||||||
hle/service/ro/ro_results.h
|
hle/service/ro/ro_results.h
|
||||||
hle/service/ro/ro_types.h
|
hle/service/ro/ro_types.h
|
||||||
hle/service/ro/ro.cpp
|
|
||||||
hle/service/ro/ro.h
|
|
||||||
hle/service/server_manager.cpp
|
hle/service/server_manager.cpp
|
||||||
hle/service/server_manager.h
|
hle/service/server_manager.h
|
||||||
hle/service/service.cpp
|
hle/service/service.cpp
|
||||||
|
@ -836,9 +855,9 @@ add_library(core STATIC
|
||||||
internal_network/network.h
|
internal_network/network.h
|
||||||
internal_network/network_interface.cpp
|
internal_network/network_interface.cpp
|
||||||
internal_network/network_interface.h
|
internal_network/network_interface.h
|
||||||
internal_network/sockets.h
|
|
||||||
internal_network/socket_proxy.cpp
|
internal_network/socket_proxy.cpp
|
||||||
internal_network/socket_proxy.h
|
internal_network/socket_proxy.h
|
||||||
|
internal_network/sockets.h
|
||||||
loader/deconstructed_rom_directory.cpp
|
loader/deconstructed_rom_directory.cpp
|
||||||
loader/deconstructed_rom_directory.h
|
loader/deconstructed_rom_directory.h
|
||||||
loader/kip.cpp
|
loader/kip.cpp
|
||||||
|
@ -857,13 +876,13 @@ add_library(core STATIC
|
||||||
loader/nsp.h
|
loader/nsp.h
|
||||||
loader/xci.cpp
|
loader/xci.cpp
|
||||||
loader/xci.h
|
loader/xci.h
|
||||||
|
memory.cpp
|
||||||
|
memory.h
|
||||||
memory/cheat_engine.cpp
|
memory/cheat_engine.cpp
|
||||||
memory/cheat_engine.h
|
memory/cheat_engine.h
|
||||||
memory/dmnt_cheat_types.h
|
memory/dmnt_cheat_types.h
|
||||||
memory/dmnt_cheat_vm.cpp
|
memory/dmnt_cheat_vm.cpp
|
||||||
memory/dmnt_cheat_vm.h
|
memory/dmnt_cheat_vm.h
|
||||||
memory.cpp
|
|
||||||
memory.h
|
|
||||||
perf_stats.cpp
|
perf_stats.cpp
|
||||||
perf_stats.h
|
perf_stats.h
|
||||||
precompiled_headers.h
|
precompiled_headers.h
|
||||||
|
|
|
@ -21,13 +21,13 @@
|
||||||
#include "core/debugger/debugger.h"
|
#include "core/debugger/debugger.h"
|
||||||
#include "core/device_memory.h"
|
#include "core/device_memory.h"
|
||||||
#include "core/file_sys/bis_factory.h"
|
#include "core/file_sys/bis_factory.h"
|
||||||
#include "core/file_sys/mode.h"
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
#include "core/file_sys/romfs_factory.h"
|
#include "core/file_sys/romfs_factory.h"
|
||||||
#include "core/file_sys/savedata_factory.h"
|
#include "core/file_sys/savedata_factory.h"
|
||||||
#include "core/file_sys/vfs_concat.h"
|
#include "core/file_sys/vfs/vfs_concat.h"
|
||||||
#include "core/file_sys/vfs_real.h"
|
#include "core/file_sys/vfs/vfs_real.h"
|
||||||
#include "core/gpu_dirty_memory_manager.h"
|
#include "core/gpu_dirty_memory_manager.h"
|
||||||
#include "core/hle/kernel/k_memory_manager.h"
|
#include "core/hle/kernel/k_memory_manager.h"
|
||||||
#include "core/hle/kernel/k_process.h"
|
#include "core/hle/kernel/k_process.h"
|
||||||
|
@ -102,7 +102,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||||
Common::SplitPath(path, &dir_name, &filename, nullptr);
|
Common::SplitPath(path, &dir_name, &filename, nullptr);
|
||||||
|
|
||||||
if (filename == "00") {
|
if (filename == "00") {
|
||||||
const auto dir = vfs->OpenDirectory(dir_name, FileSys::Mode::Read);
|
const auto dir = vfs->OpenDirectory(dir_name, FileSys::OpenMode::Read);
|
||||||
std::vector<FileSys::VirtualFile> concat;
|
std::vector<FileSys::VirtualFile> concat;
|
||||||
|
|
||||||
for (u32 i = 0; i < 0x10; ++i) {
|
for (u32 i = 0; i < 0x10; ++i) {
|
||||||
|
@ -127,10 +127,10 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Common::FS::IsDir(path)) {
|
if (Common::FS::IsDir(path)) {
|
||||||
return vfs->OpenFile(path + "/main", FileSys::Mode::Read);
|
return vfs->OpenFile(path + "/main", FileSys::OpenMode::Read);
|
||||||
}
|
}
|
||||||
|
|
||||||
return vfs->OpenFile(path, FileSys::Mode::Read);
|
return vfs->OpenFile(path, FileSys::OpenMode::Read);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct System::Impl {
|
struct System::Impl {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace Core::Frontend {
|
namespace Core::Frontend {
|
||||||
class EmuWindow;
|
class EmuWindow;
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace Core::Crypto {
|
namespace Core::Crypto {
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace Core::Crypto {
|
namespace Core::Crypto {
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
#include "core/crypto/partition_data_manager.h"
|
#include "core/crypto/partition_data_manager.h"
|
||||||
#include "core/crypto/xts_encryption_layer.h"
|
#include "core/crypto/xts_encryption_layer.h"
|
||||||
#include "core/file_sys/kernel_executable.h"
|
#include "core/file_sys/kernel_executable.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
using Common::AsArray;
|
using Common::AsArray;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace Core::Crypto {
|
namespace Core::Crypto {
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,8 @@
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include "common/fs/path_util.h"
|
#include "common/fs/path_util.h"
|
||||||
#include "core/file_sys/bis_factory.h"
|
#include "core/file_sys/bis_factory.h"
|
||||||
#include "core/file_sys/mode.h"
|
|
||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
@ -84,7 +83,7 @@ VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id,
|
||||||
VirtualFilesystem file_system) const {
|
VirtualFilesystem file_system) const {
|
||||||
auto& keys = Core::Crypto::KeyManager::Instance();
|
auto& keys = Core::Crypto::KeyManager::Instance();
|
||||||
Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory(
|
Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory(
|
||||||
Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), Mode::Read)};
|
Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), OpenMode::Read)};
|
||||||
keys.PopulateFromPartitionData(pdm);
|
keys.PopulateFromPartitionData(pdm);
|
||||||
|
|
||||||
switch (id) {
|
switch (id) {
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -13,8 +13,8 @@
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
#include "core/file_sys/partition_filesystem.h"
|
#include "core/file_sys/partition_filesystem.h"
|
||||||
#include "core/file_sys/submission_package.h"
|
#include "core/file_sys/submission_package.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace Core::Crypto {
|
namespace Core::Crypto {
|
||||||
class KeyManager;
|
class KeyManager;
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
#include "core/file_sys/content_archive.h"
|
#include "core/file_sys/content_archive.h"
|
||||||
#include "core/file_sys/partition_filesystem.h"
|
#include "core/file_sys/partition_filesystem.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
#include "core/file_sys/fssystem/fssystem_compression_configuration.h"
|
#include "core/file_sys/fssystem/fssystem_compression_configuration.h"
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace Loader {
|
namespace Loader {
|
||||||
enum class ResultStatus : u16;
|
enum class ResultStatus : u16;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/control_metadata.h"
|
#include "core/file_sys/control_metadata.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -7,18 +7,13 @@
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
constexpr Result ERROR_PATH_NOT_FOUND{ErrorModule::FS, 1};
|
constexpr Result ResultPathNotFound{ErrorModule::FS, 1};
|
||||||
constexpr Result ERROR_PATH_ALREADY_EXISTS{ErrorModule::FS, 2};
|
constexpr Result ResultPathAlreadyExists{ErrorModule::FS, 2};
|
||||||
constexpr Result ERROR_ENTITY_NOT_FOUND{ErrorModule::FS, 1002};
|
|
||||||
constexpr Result ERROR_SD_CARD_NOT_FOUND{ErrorModule::FS, 2001};
|
|
||||||
constexpr Result ERROR_OUT_OF_BOUNDS{ErrorModule::FS, 3005};
|
|
||||||
constexpr Result ERROR_FAILED_MOUNT_ARCHIVE{ErrorModule::FS, 3223};
|
|
||||||
constexpr Result ERROR_INVALID_ARGUMENT{ErrorModule::FS, 6001};
|
|
||||||
constexpr Result ERROR_INVALID_OFFSET{ErrorModule::FS, 6061};
|
|
||||||
constexpr Result ERROR_INVALID_SIZE{ErrorModule::FS, 6062};
|
|
||||||
|
|
||||||
constexpr Result ResultUnsupportedSdkVersion{ErrorModule::FS, 50};
|
constexpr Result ResultUnsupportedSdkVersion{ErrorModule::FS, 50};
|
||||||
constexpr Result ResultPartitionNotFound{ErrorModule::FS, 1001};
|
constexpr Result ResultPartitionNotFound{ErrorModule::FS, 1001};
|
||||||
|
constexpr Result ResultTargetNotFound{ErrorModule::FS, 1002};
|
||||||
|
constexpr Result ResultPortSdCardNoDevice{ErrorModule::FS, 2001};
|
||||||
|
constexpr Result ResultNotImplemented{ErrorModule::FS, 3001};
|
||||||
constexpr Result ResultUnsupportedVersion{ErrorModule::FS, 3002};
|
constexpr Result ResultUnsupportedVersion{ErrorModule::FS, 3002};
|
||||||
constexpr Result ResultOutOfRange{ErrorModule::FS, 3005};
|
constexpr Result ResultOutOfRange{ErrorModule::FS, 3005};
|
||||||
constexpr Result ResultAllocationMemoryFailedInFileSystemBuddyHeapA{ErrorModule::FS, 3294};
|
constexpr Result ResultAllocationMemoryFailedInFileSystemBuddyHeapA{ErrorModule::FS, 3294};
|
||||||
|
@ -78,10 +73,21 @@ constexpr Result ResultUnexpectedInCompressedStorageA{ErrorModule::FS, 5324};
|
||||||
constexpr Result ResultUnexpectedInCompressedStorageB{ErrorModule::FS, 5325};
|
constexpr Result ResultUnexpectedInCompressedStorageB{ErrorModule::FS, 5325};
|
||||||
constexpr Result ResultUnexpectedInCompressedStorageC{ErrorModule::FS, 5326};
|
constexpr Result ResultUnexpectedInCompressedStorageC{ErrorModule::FS, 5326};
|
||||||
constexpr Result ResultUnexpectedInCompressedStorageD{ErrorModule::FS, 5327};
|
constexpr Result ResultUnexpectedInCompressedStorageD{ErrorModule::FS, 5327};
|
||||||
|
constexpr Result ResultUnexpectedInPathA{ErrorModule::FS, 5328};
|
||||||
constexpr Result ResultInvalidArgument{ErrorModule::FS, 6001};
|
constexpr Result ResultInvalidArgument{ErrorModule::FS, 6001};
|
||||||
|
constexpr Result ResultInvalidPath{ErrorModule::FS, 6002};
|
||||||
|
constexpr Result ResultTooLongPath{ErrorModule::FS, 6003};
|
||||||
|
constexpr Result ResultInvalidCharacter{ErrorModule::FS, 6004};
|
||||||
|
constexpr Result ResultInvalidPathFormat{ErrorModule::FS, 6005};
|
||||||
|
constexpr Result ResultDirectoryUnobtainable{ErrorModule::FS, 6006};
|
||||||
|
constexpr Result ResultNotNormalized{ErrorModule::FS, 6007};
|
||||||
constexpr Result ResultInvalidOffset{ErrorModule::FS, 6061};
|
constexpr Result ResultInvalidOffset{ErrorModule::FS, 6061};
|
||||||
constexpr Result ResultInvalidSize{ErrorModule::FS, 6062};
|
constexpr Result ResultInvalidSize{ErrorModule::FS, 6062};
|
||||||
constexpr Result ResultNullptrArgument{ErrorModule::FS, 6063};
|
constexpr Result ResultNullptrArgument{ErrorModule::FS, 6063};
|
||||||
|
constexpr Result ResultInvalidOpenMode{ErrorModule::FS, 6072};
|
||||||
|
constexpr Result ResultFileExtensionWithoutOpenModeAllowAppend{ErrorModule::FS, 6201};
|
||||||
|
constexpr Result ResultReadNotPermitted{ErrorModule::FS, 6202};
|
||||||
|
constexpr Result ResultWriteNotPermitted{ErrorModule::FS, 6203};
|
||||||
constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325};
|
constexpr Result ResultUnsupportedSetSizeForIndirectStorage{ErrorModule::FS, 6325};
|
||||||
constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387};
|
constexpr Result ResultUnsupportedWriteForCompressedStorage{ErrorModule::FS, 6387};
|
||||||
constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388};
|
constexpr Result ResultUnsupportedOperateRangeForCompressedStorage{ErrorModule::FS, 6388};
|
||||||
|
|
33
src/core/file_sys/fs_directory.h
Executable file
33
src/core/file_sys/fs_directory.h
Executable file
|
@ -0,0 +1,33 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
constexpr inline size_t EntryNameLengthMax = 0x300;
|
||||||
|
|
||||||
|
struct DirectoryEntry {
|
||||||
|
DirectoryEntry(std::string_view view, s8 entry_type, u64 entry_size)
|
||||||
|
: type{entry_type}, file_size{static_cast<s64>(entry_size)} {
|
||||||
|
const std::size_t copy_size = view.copy(name, std::size(name) - 1);
|
||||||
|
name[copy_size] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
char name[EntryNameLengthMax + 1];
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
s8 type;
|
||||||
|
INSERT_PADDING_BYTES(3);
|
||||||
|
s64 file_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(DirectoryEntry) == 0x310,
|
||||||
|
"Directory Entry struct isn't exactly 0x310 bytes long!");
|
||||||
|
static_assert(offsetof(DirectoryEntry, type) == 0x304, "Wrong offset for type in Entry.");
|
||||||
|
static_assert(offsetof(DirectoryEntry, file_size) == 0x308, "Wrong offset for file_size in Entry.");
|
||||||
|
|
||||||
|
struct DirectoryHandle {
|
||||||
|
void* handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
65
src/core/file_sys/fs_file.h
Executable file
65
src/core/file_sys/fs_file.h
Executable file
|
@ -0,0 +1,65 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
struct ReadOption {
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
static const ReadOption None;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ReadOptionFlag : u32 {
|
||||||
|
ReadOptionFlag_None = (0 << 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr const ReadOption ReadOption::None = {ReadOptionFlag_None};
|
||||||
|
|
||||||
|
inline constexpr bool operator==(const ReadOption& lhs, const ReadOption& rhs) {
|
||||||
|
return lhs.value == rhs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr bool operator!=(const ReadOption& lhs, const ReadOption& rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(sizeof(ReadOption) == sizeof(u32));
|
||||||
|
|
||||||
|
enum WriteOptionFlag : u32 {
|
||||||
|
WriteOptionFlag_None = (0 << 0),
|
||||||
|
WriteOptionFlag_Flush = (1 << 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WriteOption {
|
||||||
|
u32 value;
|
||||||
|
|
||||||
|
constexpr inline bool HasFlushFlag() const {
|
||||||
|
return value & WriteOptionFlag_Flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const WriteOption None;
|
||||||
|
static const WriteOption Flush;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline constexpr const WriteOption WriteOption::None = {WriteOptionFlag_None};
|
||||||
|
inline constexpr const WriteOption WriteOption::Flush = {WriteOptionFlag_Flush};
|
||||||
|
|
||||||
|
inline constexpr bool operator==(const WriteOption& lhs, const WriteOption& rhs) {
|
||||||
|
return lhs.value == rhs.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline constexpr bool operator!=(const WriteOption& lhs, const WriteOption& rhs) {
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(sizeof(WriteOption) == sizeof(u32));
|
||||||
|
|
||||||
|
struct FileHandle {
|
||||||
|
void* handle;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
39
src/core/file_sys/fs_filesystem.h
Executable file
39
src/core/file_sys/fs_filesystem.h
Executable file
|
@ -0,0 +1,39 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
enum class OpenMode : u32 {
|
||||||
|
Read = (1 << 0),
|
||||||
|
Write = (1 << 1),
|
||||||
|
AllowAppend = (1 << 2),
|
||||||
|
|
||||||
|
ReadWrite = (Read | Write),
|
||||||
|
All = (ReadWrite | AllowAppend),
|
||||||
|
};
|
||||||
|
DECLARE_ENUM_FLAG_OPERATORS(OpenMode)
|
||||||
|
|
||||||
|
enum class OpenDirectoryMode : u64 {
|
||||||
|
Directory = (1 << 0),
|
||||||
|
File = (1 << 1),
|
||||||
|
|
||||||
|
All = (Directory | File),
|
||||||
|
};
|
||||||
|
DECLARE_ENUM_FLAG_OPERATORS(OpenDirectoryMode)
|
||||||
|
|
||||||
|
enum class DirectoryEntryType : u8 {
|
||||||
|
Directory = 0,
|
||||||
|
File = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CreateOption : u8 {
|
||||||
|
None = (0 << 0),
|
||||||
|
BigFile = (1 << 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
40
src/core/file_sys/fs_memory_management.h
Executable file
40
src/core/file_sys/fs_memory_management.h
Executable file
|
@ -0,0 +1,40 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include "common/alignment.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
constexpr size_t RequiredAlignment = alignof(u64);
|
||||||
|
|
||||||
|
void* AllocateUnsafe(size_t size) {
|
||||||
|
// Allocate
|
||||||
|
void* const ptr = ::operator new(size, std::align_val_t{RequiredAlignment});
|
||||||
|
|
||||||
|
// Check alignment
|
||||||
|
ASSERT(Common::IsAligned(reinterpret_cast<uintptr_t>(ptr), RequiredAlignment));
|
||||||
|
|
||||||
|
// Return allocated pointer
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeallocateUnsafe(void* ptr, size_t size) {
|
||||||
|
// Deallocate the pointer
|
||||||
|
::operator delete(ptr, std::align_val_t{RequiredAlignment});
|
||||||
|
}
|
||||||
|
|
||||||
|
void* Allocate(size_t size) {
|
||||||
|
return AllocateUnsafe(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Deallocate(void* ptr, size_t size) {
|
||||||
|
// If the pointer is non-null, deallocate it
|
||||||
|
if (ptr != nullptr) {
|
||||||
|
DeallocateUnsafe(ptr, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileSys
|
22
src/core/file_sys/fs_operate_range.h
Executable file
22
src/core/file_sys/fs_operate_range.h
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
enum class OperationId : s64 {
|
||||||
|
FillZero = 0,
|
||||||
|
DestroySignature = 1,
|
||||||
|
Invalidate = 2,
|
||||||
|
QueryRange = 3,
|
||||||
|
QueryUnpreparedRange = 4,
|
||||||
|
QueryLazyLoadCompletionRate = 5,
|
||||||
|
SetLazyLoadPriority = 6,
|
||||||
|
|
||||||
|
ReadLazyLoadFileForciblyForDebug = 10001,
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
566
src/core/file_sys/fs_path.h
Executable file
566
src/core/file_sys/fs_path.h
Executable file
|
@ -0,0 +1,566 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/alignment.h"
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
#include "core/file_sys/errors.h"
|
||||||
|
#include "core/file_sys/fs_memory_management.h"
|
||||||
|
#include "core/file_sys/fs_path_utility.h"
|
||||||
|
#include "core/file_sys/fs_string_util.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
class DirectoryPathParser;
|
||||||
|
|
||||||
|
class Path {
|
||||||
|
YUZU_NON_COPYABLE(Path);
|
||||||
|
YUZU_NON_MOVEABLE(Path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr const char* EmptyPath = "";
|
||||||
|
static constexpr size_t WriteBufferAlignmentLength = 8;
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class DirectoryPathParser;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class WriteBuffer {
|
||||||
|
YUZU_NON_COPYABLE(WriteBuffer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
char* m_buffer;
|
||||||
|
size_t m_length_and_is_normalized;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr WriteBuffer() : m_buffer(nullptr), m_length_and_is_normalized(0) {}
|
||||||
|
|
||||||
|
constexpr ~WriteBuffer() {
|
||||||
|
if (m_buffer != nullptr) {
|
||||||
|
Deallocate(m_buffer, this->GetLength());
|
||||||
|
this->ResetBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr WriteBuffer(WriteBuffer&& rhs)
|
||||||
|
: m_buffer(rhs.m_buffer), m_length_and_is_normalized(rhs.m_length_and_is_normalized) {
|
||||||
|
rhs.ResetBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr WriteBuffer& operator=(WriteBuffer&& rhs) {
|
||||||
|
if (m_buffer != nullptr) {
|
||||||
|
Deallocate(m_buffer, this->GetLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buffer = rhs.m_buffer;
|
||||||
|
m_length_and_is_normalized = rhs.m_length_and_is_normalized;
|
||||||
|
|
||||||
|
rhs.ResetBuffer();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void ResetBuffer() {
|
||||||
|
m_buffer = nullptr;
|
||||||
|
this->SetLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr char* Get() const {
|
||||||
|
return m_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t GetLength() const {
|
||||||
|
return m_length_and_is_normalized >> 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsNormalized() const {
|
||||||
|
return static_cast<bool>(m_length_and_is_normalized & 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SetNormalized() {
|
||||||
|
m_length_and_is_normalized |= static_cast<size_t>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SetNotNormalized() {
|
||||||
|
m_length_and_is_normalized &= ~static_cast<size_t>(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr WriteBuffer(char* buffer, size_t length)
|
||||||
|
: m_buffer(buffer), m_length_and_is_normalized(0) {
|
||||||
|
this->SetLength(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static WriteBuffer Make(size_t length) {
|
||||||
|
if (void* alloc = Allocate(length); alloc != nullptr) {
|
||||||
|
return WriteBuffer(static_cast<char*>(alloc), length);
|
||||||
|
} else {
|
||||||
|
return WriteBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr void SetLength(size_t size) {
|
||||||
|
m_length_and_is_normalized = (m_length_and_is_normalized & 1) | (size << 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* m_str;
|
||||||
|
WriteBuffer m_write_buffer;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr Path() : m_str(EmptyPath), m_write_buffer() {}
|
||||||
|
|
||||||
|
constexpr Path(const char* s) : m_str(s), m_write_buffer() {
|
||||||
|
m_write_buffer.SetNormalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr ~Path() = default;
|
||||||
|
|
||||||
|
constexpr Result SetShallowBuffer(const char* buffer) {
|
||||||
|
// Check pre-conditions
|
||||||
|
ASSERT(m_write_buffer.GetLength() == 0);
|
||||||
|
|
||||||
|
// Check the buffer is valid
|
||||||
|
R_UNLESS(buffer != nullptr, ResultNullptrArgument);
|
||||||
|
|
||||||
|
// Set buffer
|
||||||
|
this->SetReadOnlyBuffer(buffer);
|
||||||
|
|
||||||
|
// Note that we're normalized
|
||||||
|
this->SetNormalized();
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const char* GetString() const {
|
||||||
|
// Check pre-conditions
|
||||||
|
ASSERT(this->IsNormalized());
|
||||||
|
|
||||||
|
return m_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t GetLength() const {
|
||||||
|
if (std::is_constant_evaluated()) {
|
||||||
|
return Strlen(this->GetString());
|
||||||
|
} else {
|
||||||
|
return std::strlen(this->GetString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsEmpty() const {
|
||||||
|
return *m_str == '\x00';
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsMatchHead(const char* p, size_t len) const {
|
||||||
|
return Strncmp(this->GetString(), p, len) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Initialize(const Path& rhs) {
|
||||||
|
// Check the other path is normalized
|
||||||
|
const bool normalized = rhs.IsNormalized();
|
||||||
|
R_UNLESS(normalized, ResultNotNormalized);
|
||||||
|
|
||||||
|
// Allocate buffer for our path
|
||||||
|
const auto len = rhs.GetLength();
|
||||||
|
R_TRY(this->Preallocate(len + 1));
|
||||||
|
|
||||||
|
// Copy the path
|
||||||
|
const size_t copied = Strlcpy<char>(m_write_buffer.Get(), rhs.GetString(), len + 1);
|
||||||
|
R_UNLESS(copied == len, ResultUnexpectedInPathA);
|
||||||
|
|
||||||
|
// Set normalized
|
||||||
|
this->SetNormalized();
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Initialize(const char* path, size_t len) {
|
||||||
|
// Check the path is valid
|
||||||
|
R_UNLESS(path != nullptr, ResultNullptrArgument);
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
R_TRY(this->InitializeImpl(path, len));
|
||||||
|
|
||||||
|
// Set not normalized
|
||||||
|
this->SetNotNormalized();
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Initialize(const char* path) {
|
||||||
|
// Check the path is valid
|
||||||
|
R_UNLESS(path != nullptr, ResultNullptrArgument);
|
||||||
|
|
||||||
|
R_RETURN(this->Initialize(path, std::strlen(path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeWithReplaceBackslash(const char* path) {
|
||||||
|
// Check the path is valid
|
||||||
|
R_UNLESS(path != nullptr, ResultNullptrArgument);
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
R_TRY(this->InitializeImpl(path, std::strlen(path)));
|
||||||
|
|
||||||
|
// Replace slashes as desired
|
||||||
|
if (const auto write_buffer_length = m_write_buffer.GetLength(); write_buffer_length > 1) {
|
||||||
|
Replace(m_write_buffer.Get(), write_buffer_length - 1, '\\', '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set not normalized
|
||||||
|
this->SetNotNormalized();
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeWithReplaceForwardSlashes(const char* path) {
|
||||||
|
// Check the path is valid
|
||||||
|
R_UNLESS(path != nullptr, ResultNullptrArgument);
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
R_TRY(this->InitializeImpl(path, std::strlen(path)));
|
||||||
|
|
||||||
|
// Replace slashes as desired
|
||||||
|
if (m_write_buffer.GetLength() > 1) {
|
||||||
|
if (auto* p = m_write_buffer.Get(); p[0] == '/' && p[1] == '/') {
|
||||||
|
p[0] = '\\';
|
||||||
|
p[1] = '\\';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set not normalized
|
||||||
|
this->SetNotNormalized();
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeWithNormalization(const char* path, size_t size) {
|
||||||
|
// Check the path is valid
|
||||||
|
R_UNLESS(path != nullptr, ResultNullptrArgument);
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
R_TRY(this->InitializeImpl(path, size));
|
||||||
|
|
||||||
|
// Set not normalized
|
||||||
|
this->SetNotNormalized();
|
||||||
|
|
||||||
|
// Perform normalization
|
||||||
|
PathFlags path_flags;
|
||||||
|
if (IsPathRelative(m_str)) {
|
||||||
|
path_flags.AllowRelativePath();
|
||||||
|
} else if (IsWindowsPath(m_str, true)) {
|
||||||
|
path_flags.AllowWindowsPath();
|
||||||
|
} else {
|
||||||
|
/* NOTE: In this case, Nintendo checks is normalized, then sets is normalized, then
|
||||||
|
* returns success. */
|
||||||
|
/* This seems like a bug. */
|
||||||
|
size_t dummy;
|
||||||
|
bool normalized;
|
||||||
|
R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy),
|
||||||
|
m_str));
|
||||||
|
|
||||||
|
this->SetNormalized();
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize
|
||||||
|
R_TRY(this->Normalize(path_flags));
|
||||||
|
|
||||||
|
this->SetNormalized();
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeWithNormalization(const char* path) {
|
||||||
|
// Check the path is valid
|
||||||
|
R_UNLESS(path != nullptr, ResultNullptrArgument);
|
||||||
|
|
||||||
|
R_RETURN(this->InitializeWithNormalization(path, std::strlen(path)));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeAsEmpty() {
|
||||||
|
// Clear our buffer
|
||||||
|
this->ClearBuffer();
|
||||||
|
|
||||||
|
// Set normalized
|
||||||
|
this->SetNormalized();
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AppendChild(const char* child) {
|
||||||
|
// Check the path is valid
|
||||||
|
R_UNLESS(child != nullptr, ResultNullptrArgument);
|
||||||
|
|
||||||
|
// Basic checks. If we have a path and the child is empty, we have nothing to do
|
||||||
|
const char* c = child;
|
||||||
|
if (m_str[0]) {
|
||||||
|
// Skip an early separator
|
||||||
|
if (*c == '/') {
|
||||||
|
++c;
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED_IF(*c == '\x00');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't have a string, we can just initialize
|
||||||
|
auto cur_len = std::strlen(m_str);
|
||||||
|
if (cur_len == 0) {
|
||||||
|
R_RETURN(this->Initialize(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a trailing separator
|
||||||
|
if (m_str[cur_len - 1] == '/' || m_str[cur_len - 1] == '\\') {
|
||||||
|
--cur_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the child path's length
|
||||||
|
auto child_len = std::strlen(c);
|
||||||
|
|
||||||
|
// Reset our write buffer
|
||||||
|
WriteBuffer old_write_buffer;
|
||||||
|
if (m_write_buffer.Get() != nullptr) {
|
||||||
|
old_write_buffer = std::move(m_write_buffer);
|
||||||
|
this->ClearBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-allocate the new buffer
|
||||||
|
R_TRY(this->Preallocate(cur_len + 1 + child_len + 1));
|
||||||
|
|
||||||
|
// Get our write buffer
|
||||||
|
auto* dst = m_write_buffer.Get();
|
||||||
|
if (old_write_buffer.Get() != nullptr && cur_len > 0) {
|
||||||
|
Strlcpy<char>(dst, old_write_buffer.Get(), cur_len + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add separator
|
||||||
|
dst[cur_len] = '/';
|
||||||
|
|
||||||
|
// Copy the child path
|
||||||
|
const size_t copied = Strlcpy<char>(dst + cur_len + 1, c, child_len + 1);
|
||||||
|
R_UNLESS(copied == child_len, ResultUnexpectedInPathA);
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result AppendChild(const Path& rhs) {
|
||||||
|
R_RETURN(this->AppendChild(rhs.GetString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Combine(const Path& parent, const Path& child) {
|
||||||
|
// Get the lengths
|
||||||
|
const auto p_len = parent.GetLength();
|
||||||
|
const auto c_len = child.GetLength();
|
||||||
|
|
||||||
|
// Allocate our buffer
|
||||||
|
R_TRY(this->Preallocate(p_len + c_len + 1));
|
||||||
|
|
||||||
|
// Initialize as parent
|
||||||
|
R_TRY(this->Initialize(parent));
|
||||||
|
|
||||||
|
// If we're empty, we can just initialize as child
|
||||||
|
if (this->IsEmpty()) {
|
||||||
|
R_TRY(this->Initialize(child));
|
||||||
|
} else {
|
||||||
|
// Otherwise, we should append the child
|
||||||
|
R_TRY(this->AppendChild(child));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result RemoveChild() {
|
||||||
|
// If we don't have a write-buffer, ensure that we have one
|
||||||
|
if (m_write_buffer.Get() == nullptr) {
|
||||||
|
if (const auto len = std::strlen(m_str); len > 0) {
|
||||||
|
R_TRY(this->Preallocate(len));
|
||||||
|
Strlcpy<char>(m_write_buffer.Get(), m_str, len + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that it's possible for us to remove a child
|
||||||
|
auto* p = m_write_buffer.Get();
|
||||||
|
s32 len = std::strlen(p);
|
||||||
|
R_UNLESS(len != 1 || (p[0] != '/' && p[0] != '.'), ResultNotImplemented);
|
||||||
|
|
||||||
|
// Handle a trailing separator
|
||||||
|
if (len > 0 && (p[len - 1] == '\\' || p[len - 1] == '/')) {
|
||||||
|
--len;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the child path segment
|
||||||
|
while ((--len) >= 0 && p[len]) {
|
||||||
|
if (p[len] == '/' || p[len] == '\\') {
|
||||||
|
if (len > 0) {
|
||||||
|
p[len] = 0;
|
||||||
|
} else {
|
||||||
|
p[1] = 0;
|
||||||
|
len = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that length remains > 0
|
||||||
|
R_UNLESS(len > 0, ResultNotImplemented);
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Normalize(const PathFlags& flags) {
|
||||||
|
// If we're already normalized, nothing to do
|
||||||
|
R_SUCCEED_IF(this->IsNormalized());
|
||||||
|
|
||||||
|
// Check if we're normalized
|
||||||
|
bool normalized;
|
||||||
|
size_t dummy;
|
||||||
|
R_TRY(PathFormatter::IsNormalized(std::addressof(normalized), std::addressof(dummy), m_str,
|
||||||
|
flags));
|
||||||
|
|
||||||
|
// If we're not normalized, normalize
|
||||||
|
if (!normalized) {
|
||||||
|
// Determine necessary buffer length
|
||||||
|
auto len = m_write_buffer.GetLength();
|
||||||
|
if (flags.IsRelativePathAllowed() && IsPathRelative(m_str)) {
|
||||||
|
len += 2;
|
||||||
|
}
|
||||||
|
if (flags.IsWindowsPathAllowed() && IsWindowsPath(m_str, true)) {
|
||||||
|
len += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a new buffer
|
||||||
|
const size_t size = Common::AlignUp(len, WriteBufferAlignmentLength);
|
||||||
|
auto buf = WriteBuffer::Make(size);
|
||||||
|
R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique);
|
||||||
|
|
||||||
|
// Normalize into it
|
||||||
|
R_TRY(PathFormatter::Normalize(buf.Get(), size, m_write_buffer.Get(),
|
||||||
|
m_write_buffer.GetLength(), flags));
|
||||||
|
|
||||||
|
// Set the normalized buffer as our buffer
|
||||||
|
this->SetModifiableBuffer(std::move(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set normalized
|
||||||
|
this->SetNormalized();
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void ClearBuffer() {
|
||||||
|
m_write_buffer.ResetBuffer();
|
||||||
|
m_str = EmptyPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetModifiableBuffer(WriteBuffer&& buffer) {
|
||||||
|
// Check pre-conditions
|
||||||
|
ASSERT(buffer.Get() != nullptr);
|
||||||
|
ASSERT(buffer.GetLength() > 0);
|
||||||
|
ASSERT(Common::IsAligned(buffer.GetLength(), WriteBufferAlignmentLength));
|
||||||
|
|
||||||
|
// Get whether we're normalized
|
||||||
|
if (m_write_buffer.IsNormalized()) {
|
||||||
|
buffer.SetNormalized();
|
||||||
|
} else {
|
||||||
|
buffer.SetNotNormalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set write buffer
|
||||||
|
m_write_buffer = std::move(buffer);
|
||||||
|
m_str = m_write_buffer.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SetReadOnlyBuffer(const char* buffer) {
|
||||||
|
m_str = buffer;
|
||||||
|
m_write_buffer.ResetBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result Preallocate(size_t length) {
|
||||||
|
// Allocate additional space, if needed
|
||||||
|
if (length > m_write_buffer.GetLength()) {
|
||||||
|
// Allocate buffer
|
||||||
|
const size_t size = Common::AlignUp(length, WriteBufferAlignmentLength);
|
||||||
|
auto buf = WriteBuffer::Make(size);
|
||||||
|
R_UNLESS(buf.Get() != nullptr, ResultAllocationMemoryFailedMakeUnique);
|
||||||
|
|
||||||
|
// Set write buffer
|
||||||
|
this->SetModifiableBuffer(std::move(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result InitializeImpl(const char* path, size_t size) {
|
||||||
|
if (size > 0 && path[0]) {
|
||||||
|
// Pre allocate a buffer for the path
|
||||||
|
R_TRY(this->Preallocate(size + 1));
|
||||||
|
|
||||||
|
// Copy the path
|
||||||
|
const size_t copied = Strlcpy<char>(m_write_buffer.Get(), path, size + 1);
|
||||||
|
R_UNLESS(copied >= size, ResultUnexpectedInPathA);
|
||||||
|
} else {
|
||||||
|
// We can just clear the buffer
|
||||||
|
this->ClearBuffer();
|
||||||
|
}
|
||||||
|
|
||||||
|
R_SUCCEED();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr char* GetWriteBuffer() {
|
||||||
|
ASSERT(m_write_buffer.Get() != nullptr);
|
||||||
|
return m_write_buffer.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr size_t GetWriteBufferLength() const {
|
||||||
|
return m_write_buffer.GetLength();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool IsNormalized() const {
|
||||||
|
return m_write_buffer.IsNormalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SetNormalized() {
|
||||||
|
m_write_buffer.SetNormalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void SetNotNormalized() {
|
||||||
|
m_write_buffer.SetNotNormalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool operator==(const FileSys::Path& rhs) const {
|
||||||
|
return std::strcmp(this->GetString(), rhs.GetString()) == 0;
|
||||||
|
}
|
||||||
|
bool operator!=(const FileSys::Path& rhs) const {
|
||||||
|
return !(*this == rhs);
|
||||||
|
}
|
||||||
|
bool operator==(const char* p) const {
|
||||||
|
return std::strcmp(this->GetString(), p) == 0;
|
||||||
|
}
|
||||||
|
bool operator!=(const char* p) const {
|
||||||
|
return !(*this == p);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline Result SetUpFixedPath(FileSys::Path* out, const char* s) {
|
||||||
|
// Verify the path is normalized
|
||||||
|
bool normalized;
|
||||||
|
size_t dummy;
|
||||||
|
R_TRY(PathNormalizer::IsNormalized(std::addressof(normalized), std::addressof(dummy), s));
|
||||||
|
|
||||||
|
R_UNLESS(normalized, ResultInvalidPathFormat);
|
||||||
|
|
||||||
|
// Set the fixed path
|
||||||
|
R_RETURN(out->SetShallowBuffer(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline bool IsWindowsDriveRootPath(const FileSys::Path& path) {
|
||||||
|
const char* const str = path.GetString();
|
||||||
|
return IsWindowsDrive(str) &&
|
||||||
|
(str[2] == StringTraits::DirectorySeparator ||
|
||||||
|
str[2] == StringTraits::AlternateDirectorySeparator) &&
|
||||||
|
str[3] == StringTraits::NullTerminator;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileSys
|
1239
src/core/file_sys/fs_path_utility.h
Executable file
1239
src/core/file_sys/fs_path_utility.h
Executable file
File diff suppressed because it is too large
Load diff
226
src/core/file_sys/fs_string_util.h
Executable file
226
src/core/file_sys/fs_string_util.h
Executable file
|
@ -0,0 +1,226 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr int Strlen(const T* str) {
|
||||||
|
ASSERT(str != nullptr);
|
||||||
|
|
||||||
|
int length = 0;
|
||||||
|
while (*str++) {
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr int Strnlen(const T* str, int count) {
|
||||||
|
ASSERT(str != nullptr);
|
||||||
|
ASSERT(count >= 0);
|
||||||
|
|
||||||
|
int length = 0;
|
||||||
|
while (count-- && *str++) {
|
||||||
|
++length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr int Strncmp(const T* lhs, const T* rhs, int count) {
|
||||||
|
ASSERT(lhs != nullptr);
|
||||||
|
ASSERT(rhs != nullptr);
|
||||||
|
ASSERT(count >= 0);
|
||||||
|
|
||||||
|
if (count == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T l, r;
|
||||||
|
do {
|
||||||
|
l = *(lhs++);
|
||||||
|
r = *(rhs++);
|
||||||
|
} while (l && (l == r) && (--count));
|
||||||
|
|
||||||
|
return l - r;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static constexpr int Strlcpy(T* dst, const T* src, int count) {
|
||||||
|
ASSERT(dst != nullptr);
|
||||||
|
ASSERT(src != nullptr);
|
||||||
|
|
||||||
|
const T* cur = src;
|
||||||
|
if (count > 0) {
|
||||||
|
while ((--count) && *cur) {
|
||||||
|
*(dst++) = *(cur++);
|
||||||
|
}
|
||||||
|
*dst = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (*cur) {
|
||||||
|
cur++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<int>(cur - src);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum CharacterEncodingResult {
|
||||||
|
CharacterEncodingResult_Success = 0,
|
||||||
|
CharacterEncodingResult_InsufficientLength = 1,
|
||||||
|
CharacterEncodingResult_InvalidFormat = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace impl {
|
||||||
|
|
||||||
|
class CharacterEncodingHelper {
|
||||||
|
public:
|
||||||
|
static constexpr int8_t Utf8NBytesInnerTable[0x100 + 1] = {
|
||||||
|
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3,
|
||||||
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 8,
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr char GetUtf8NBytes(size_t i) {
|
||||||
|
return static_cast<char>(Utf8NBytesInnerTable[1 + i]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace impl
|
||||||
|
|
||||||
|
constexpr inline CharacterEncodingResult ConvertCharacterUtf8ToUtf32(u32* dst, const char* src) {
|
||||||
|
// Check pre-conditions
|
||||||
|
ASSERT(dst != nullptr);
|
||||||
|
ASSERT(src != nullptr);
|
||||||
|
|
||||||
|
// Perform the conversion
|
||||||
|
const auto* p = src;
|
||||||
|
switch (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[0]))) {
|
||||||
|
case 1:
|
||||||
|
*dst = static_cast<u32>(p[0]);
|
||||||
|
return CharacterEncodingResult_Success;
|
||||||
|
case 2:
|
||||||
|
if ((static_cast<u32>(p[0]) & 0x1E) != 0) {
|
||||||
|
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) ==
|
||||||
|
0) {
|
||||||
|
*dst = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0);
|
||||||
|
return CharacterEncodingResult_Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
|
||||||
|
impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) {
|
||||||
|
const u32 c = (static_cast<u32>(p[0] & 0xF) << 12) |
|
||||||
|
(static_cast<u32>(p[1] & 0x3F) << 6) |
|
||||||
|
(static_cast<u32>(p[2] & 0x3F) << 0);
|
||||||
|
if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) {
|
||||||
|
*dst = c;
|
||||||
|
return CharacterEncodingResult_Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CharacterEncodingResult_InvalidFormat;
|
||||||
|
case 4:
|
||||||
|
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
|
||||||
|
impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 &&
|
||||||
|
impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) {
|
||||||
|
const u32 c =
|
||||||
|
(static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) |
|
||||||
|
(static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0);
|
||||||
|
if (c >= 0x10000 && c < 0x110000) {
|
||||||
|
*dst = c;
|
||||||
|
return CharacterEncodingResult_Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CharacterEncodingResult_InvalidFormat;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We failed to convert
|
||||||
|
return CharacterEncodingResult_InvalidFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr inline CharacterEncodingResult PickOutCharacterFromUtf8String(char* dst,
|
||||||
|
const char** str) {
|
||||||
|
// Check pre-conditions
|
||||||
|
ASSERT(dst != nullptr);
|
||||||
|
ASSERT(str != nullptr);
|
||||||
|
ASSERT(*str != nullptr);
|
||||||
|
|
||||||
|
// Clear the output
|
||||||
|
dst[0] = 0;
|
||||||
|
dst[1] = 0;
|
||||||
|
dst[2] = 0;
|
||||||
|
dst[3] = 0;
|
||||||
|
|
||||||
|
// Perform the conversion
|
||||||
|
const auto* p = *str;
|
||||||
|
u32 c = static_cast<u32>(*p);
|
||||||
|
switch (impl::CharacterEncodingHelper::GetUtf8NBytes(c)) {
|
||||||
|
case 1:
|
||||||
|
dst[0] = (*str)[0];
|
||||||
|
++(*str);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if ((p[0] & 0x1E) != 0) {
|
||||||
|
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) ==
|
||||||
|
0) {
|
||||||
|
c = (static_cast<u32>(p[0] & 0x1F) << 6) | (static_cast<u32>(p[1] & 0x3F) << 0);
|
||||||
|
dst[0] = (*str)[0];
|
||||||
|
dst[1] = (*str)[1];
|
||||||
|
(*str) += 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CharacterEncodingResult_InvalidFormat;
|
||||||
|
case 3:
|
||||||
|
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
|
||||||
|
impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0) {
|
||||||
|
c = (static_cast<u32>(p[0] & 0xF) << 12) | (static_cast<u32>(p[1] & 0x3F) << 6) |
|
||||||
|
(static_cast<u32>(p[2] & 0x3F) << 0);
|
||||||
|
if ((c & 0xF800) != 0 && (c & 0xF800) != 0xD800) {
|
||||||
|
dst[0] = (*str)[0];
|
||||||
|
dst[1] = (*str)[1];
|
||||||
|
dst[2] = (*str)[2];
|
||||||
|
(*str) += 3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CharacterEncodingResult_InvalidFormat;
|
||||||
|
case 4:
|
||||||
|
if (impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[1])) == 0 &&
|
||||||
|
impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[2])) == 0 &&
|
||||||
|
impl::CharacterEncodingHelper::GetUtf8NBytes(static_cast<unsigned char>(p[3])) == 0) {
|
||||||
|
c = (static_cast<u32>(p[0] & 0x7) << 18) | (static_cast<u32>(p[1] & 0x3F) << 12) |
|
||||||
|
(static_cast<u32>(p[2] & 0x3F) << 6) | (static_cast<u32>(p[3] & 0x3F) << 0);
|
||||||
|
if (c >= 0x10000 && c < 0x110000) {
|
||||||
|
dst[0] = (*str)[0];
|
||||||
|
dst[1] = (*str)[1];
|
||||||
|
dst[2] = (*str)[2];
|
||||||
|
dst[3] = (*str)[3];
|
||||||
|
(*str) += 4;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return CharacterEncodingResult_InvalidFormat;
|
||||||
|
default:
|
||||||
|
return CharacterEncodingResult_InvalidFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CharacterEncodingResult_Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileSys
|
|
@ -8,8 +8,8 @@
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "core/file_sys/fsmitm_romfsbuild.h"
|
#include "core/file_sys/fsmitm_romfsbuild.h"
|
||||||
#include "core/file_sys/ips_layer.h"
|
#include "core/file_sys/ips_layer.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "common/overflow.h"
|
#include "common/overflow.h"
|
||||||
#include "core/file_sys/errors.h"
|
#include "core/file_sys/errors.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include "core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h"
|
#include "core/file_sys/fssystem/fssystem_aes_ctr_counter_extended_storage.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_aes_ctr_storage.h"
|
#include "core/file_sys/fssystem/fssystem_aes_ctr_storage.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_nca_header.h"
|
#include "core/file_sys/fssystem/fssystem_nca_header.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
#include "core/file_sys/errors.h"
|
#include "core/file_sys/errors.h"
|
||||||
#include "core/file_sys/fssystem/fs_i_storage.h"
|
#include "core/file_sys/fssystem/fs_i_storage.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/literals.h"
|
#include "common/literals.h"
|
||||||
|
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "core/file_sys/fssystem/fssystem_bucket_tree.h"
|
#include "core/file_sys/fssystem/fssystem_bucket_tree.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_compression_common.h"
|
#include "core/file_sys/fssystem/fssystem_compression_common.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_pooled_buffer.h"
|
#include "core/file_sys/fssystem/fssystem_pooled_buffer.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h"
|
#include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "core/file_sys/fssystem/fs_types.h"
|
#include "core/file_sys/fssystem/fs_types.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_alignment_matching_storage.h"
|
#include "core/file_sys/fssystem/fssystem_alignment_matching_storage.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_integrity_verification_storage.h"
|
#include "core/file_sys/fssystem/fssystem_integrity_verification_storage.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#include "core/file_sys/errors.h"
|
#include "core/file_sys/errors.h"
|
||||||
#include "core/file_sys/fssystem/fs_i_storage.h"
|
#include "core/file_sys/fssystem/fs_i_storage.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
#include "core/file_sys/fssystem/fs_i_storage.h"
|
#include "core/file_sys/fssystem/fs_i_storage.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_bucket_tree.h"
|
#include "core/file_sys/fssystem/fssystem_bucket_tree.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_bucket_tree_template_impl.h"
|
#include "core/file_sys/fssystem/fssystem_bucket_tree_template_impl.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h"
|
#include "core/file_sys/fssystem/fssystem_hierarchical_integrity_verification_storage.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_nca_header.h"
|
#include "core/file_sys/fssystem/fssystem_nca_header.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
|
#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_sparse_storage.h"
|
#include "core/file_sys/fssystem/fssystem_sparse_storage.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_switch_storage.h"
|
#include "core/file_sys/fssystem/fssystem_switch_storage.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "core/file_sys/fssystem/fssystem_compression_common.h"
|
#include "core/file_sys/fssystem/fssystem_compression_common.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_nca_header.h"
|
#include "core/file_sys/fssystem/fssystem_nca_header.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "core/file_sys/fssystem/fssystem_aes_xts_storage.h"
|
#include "core/file_sys/fssystem/fssystem_aes_xts_storage.h"
|
||||||
#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
|
#include "core/file_sys/fssystem/fssystem_nca_file_system_driver.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/ips_layer.h"
|
#include "core/file_sys/ips_layer.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
#include "core/file_sys/kernel_executable.h"
|
#include "core/file_sys/kernel_executable.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace Loader {
|
namespace Loader {
|
||||||
enum class ResultStatus : u16;
|
enum class ResultStatus : u16;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
class CNMT;
|
class CNMT;
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/file_sys/partition_filesystem.h"
|
#include "core/file_sys/partition_filesystem.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace Loader {
|
namespace Loader {
|
||||||
enum class ResultStatus : u16;
|
enum class ResultStatus : u16;
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
#include "core/file_sys/patch_manager.h"
|
#include "core/file_sys/patch_manager.h"
|
||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
#include "core/file_sys/romfs.h"
|
#include "core/file_sys/romfs.h"
|
||||||
#include "core/file_sys/vfs_cached.h"
|
#include "core/file_sys/vfs/vfs_cached.h"
|
||||||
#include "core/file_sys/vfs_layered.h"
|
#include "core/file_sys/vfs/vfs_layered.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
#include "core/hle/service/filesystem/filesystem.h"
|
#include "core/hle/service/filesystem/filesystem.h"
|
||||||
#include "core/hle/service/ns/language.h"
|
#include "core/hle/service/ns/language.h"
|
||||||
#include "core/hle/service/set/settings_server.h"
|
#include "core/hle/service/set/settings_server.h"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
#include "core/memory/dmnt_cheat_types.h"
|
#include "core/memory/dmnt_cheat_types.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "common/scope_exit.h"
|
#include "common/scope_exit.h"
|
||||||
#include "core/file_sys/program_metadata.h"
|
#include "core/file_sys/program_metadata.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace Loader {
|
namespace Loader {
|
||||||
enum class ResultStatus : u16;
|
enum class ResultStatus : u16;
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
#include "core/file_sys/submission_package.h"
|
#include "core/file_sys/submission_package.h"
|
||||||
#include "core/file_sys/vfs_concat.h"
|
#include "core/file_sys/vfs/vfs_concat.h"
|
||||||
#include "core/loader/loader.h"
|
#include "core/loader/loader.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#include <boost/container/flat_map.hpp>
|
#include <boost/container/flat_map.hpp>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/crypto/key_manager.h"
|
#include "core/crypto/key_manager.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
class CNMT;
|
class CNMT;
|
||||||
|
|
|
@ -9,11 +9,11 @@
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/fsmitm_romfsbuild.h"
|
#include "core/file_sys/fsmitm_romfsbuild.h"
|
||||||
#include "core/file_sys/romfs.h"
|
#include "core/file_sys/romfs.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/file_sys/vfs_cached.h"
|
#include "core/file_sys/vfs/vfs_cached.h"
|
||||||
#include "core/file_sys/vfs_concat.h"
|
#include "core/file_sys/vfs/vfs_concat.h"
|
||||||
#include "core/file_sys/vfs_offset.h"
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
namespace Loader {
|
namespace Loader {
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "common/uuid.h"
|
#include "common/uuid.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "core/file_sys/savedata_factory.h"
|
#include "core/file_sys/savedata_factory.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "core/file_sys/registered_cache.h"
|
#include "core/file_sys/registered_cache.h"
|
||||||
#include "core/file_sys/sdmc_factory.h"
|
#include "core/file_sys/sdmc_factory.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
#include "core/file_sys/xts_archive.h"
|
#include "core/file_sys/xts_archive.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
#include "core/hle/result.h"
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
namespace Core::Crypto {
|
namespace Core::Crypto {
|
||||||
class KeyManager;
|
class KeyManager;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "core/file_sys/system_archive/mii_model.h"
|
#include "core/file_sys/system_archive/mii_model.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
|
|
||||||
namespace FileSys::SystemArchive {
|
namespace FileSys::SystemArchive {
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace FileSys::SystemArchive {
|
namespace FileSys::SystemArchive {
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/system_archive/ng_word.h"
|
#include "core/file_sys/system_archive/ng_word.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
|
|
||||||
namespace FileSys::SystemArchive {
|
namespace FileSys::SystemArchive {
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace FileSys::SystemArchive {
|
namespace FileSys::SystemArchive {
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
#include "core/file_sys/system_archive/data/font_nintendo_extended.h"
|
#include "core/file_sys/system_archive/data/font_nintendo_extended.h"
|
||||||
#include "core/file_sys/system_archive/data/font_standard.h"
|
#include "core/file_sys/system_archive/data/font_standard.h"
|
||||||
#include "core/file_sys/system_archive/shared_font.h"
|
#include "core/file_sys/system_archive/shared_font.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
#include "core/hle/service/ns/iplatform_service_manager.h"
|
#include "core/hle/service/ns/iplatform_service_manager.h"
|
||||||
|
|
||||||
namespace FileSys::SystemArchive {
|
namespace FileSys::SystemArchive {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace FileSys::SystemArchive {
|
namespace FileSys::SystemArchive {
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace FileSys::SystemArchive {
|
namespace FileSys::SystemArchive {
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "common/logging/log.h"
|
#include "common/logging/log.h"
|
||||||
#include "core/file_sys/system_archive/system_version.h"
|
#include "core/file_sys/system_archive/system_version.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
#include "core/hle/api_version.h"
|
#include "core/hle/api_version.h"
|
||||||
|
|
||||||
namespace FileSys::SystemArchive {
|
namespace FileSys::SystemArchive {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace FileSys::SystemArchive {
|
namespace FileSys::SystemArchive {
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "core/file_sys/system_archive/time_zone_binary.h"
|
#include "core/file_sys/system_archive/time_zone_binary.h"
|
||||||
#include "core/file_sys/vfs_vector.h"
|
#include "core/file_sys/vfs/vfs_vector.h"
|
||||||
|
|
||||||
#include "nx_tzdb.h"
|
#include "nx_tzdb.h"
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "core/file_sys/vfs_types.h"
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
namespace FileSys::SystemArchive {
|
namespace FileSys::SystemArchive {
|
||||||
|
|
||||||
|
|
551
src/core/file_sys/vfs/vfs.cpp
Executable file
551
src/core/file_sys/vfs/vfs.cpp
Executable file
|
@ -0,0 +1,551 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <numeric>
|
||||||
|
#include <string>
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
VfsFilesystem::VfsFilesystem(VirtualDir root_) : root(std::move(root_)) {}
|
||||||
|
|
||||||
|
VfsFilesystem::~VfsFilesystem() = default;
|
||||||
|
|
||||||
|
std::string VfsFilesystem::GetName() const {
|
||||||
|
return root->GetName();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VfsFilesystem::IsReadable() const {
|
||||||
|
return root->IsReadable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VfsFilesystem::IsWritable() const {
|
||||||
|
return root->IsWritable();
|
||||||
|
}
|
||||||
|
|
||||||
|
VfsEntryType VfsFilesystem::GetEntryType(std::string_view path_) const {
|
||||||
|
const auto path = Common::FS::SanitizePath(path_);
|
||||||
|
if (root->GetFileRelative(path) != nullptr)
|
||||||
|
return VfsEntryType::File;
|
||||||
|
if (root->GetDirectoryRelative(path) != nullptr)
|
||||||
|
return VfsEntryType::Directory;
|
||||||
|
|
||||||
|
return VfsEntryType::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile VfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) {
|
||||||
|
const auto path = Common::FS::SanitizePath(path_);
|
||||||
|
return root->GetFileRelative(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile VfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) {
|
||||||
|
const auto path = Common::FS::SanitizePath(path_);
|
||||||
|
return root->CreateFileRelative(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile VfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
|
||||||
|
const auto old_path = Common::FS::SanitizePath(old_path_);
|
||||||
|
const auto new_path = Common::FS::SanitizePath(new_path_);
|
||||||
|
|
||||||
|
// VfsDirectory impls are only required to implement copy across the current directory.
|
||||||
|
if (Common::FS::GetParentPath(old_path) == Common::FS::GetParentPath(new_path)) {
|
||||||
|
if (!root->Copy(Common::FS::GetFilename(old_path), Common::FS::GetFilename(new_path)))
|
||||||
|
return nullptr;
|
||||||
|
return OpenFile(new_path, OpenMode::ReadWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do it using RawCopy. Non-default impls are encouraged to optimize this.
|
||||||
|
const auto old_file = OpenFile(old_path, OpenMode::Read);
|
||||||
|
if (old_file == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
auto new_file = OpenFile(new_path, OpenMode::Read);
|
||||||
|
if (new_file != nullptr)
|
||||||
|
return nullptr;
|
||||||
|
new_file = CreateFile(new_path, OpenMode::Write);
|
||||||
|
if (new_file == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
if (!VfsRawCopy(old_file, new_file))
|
||||||
|
return nullptr;
|
||||||
|
return new_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile VfsFilesystem::MoveFile(std::string_view old_path, std::string_view new_path) {
|
||||||
|
const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
|
||||||
|
const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
|
||||||
|
|
||||||
|
// Again, non-default impls are highly encouraged to provide a more optimized version of this.
|
||||||
|
auto out = CopyFile(sanitized_old_path, sanitized_new_path);
|
||||||
|
if (out == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
if (DeleteFile(sanitized_old_path))
|
||||||
|
return out;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VfsFilesystem::DeleteFile(std::string_view path_) {
|
||||||
|
const auto path = Common::FS::SanitizePath(path_);
|
||||||
|
auto parent = OpenDirectory(Common::FS::GetParentPath(path), OpenMode::Write);
|
||||||
|
if (parent == nullptr)
|
||||||
|
return false;
|
||||||
|
return parent->DeleteFile(Common::FS::GetFilename(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir VfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
|
||||||
|
const auto path = Common::FS::SanitizePath(path_);
|
||||||
|
return root->GetDirectoryRelative(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir VfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
|
||||||
|
const auto path = Common::FS::SanitizePath(path_);
|
||||||
|
return root->CreateDirectoryRelative(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_view new_path_) {
|
||||||
|
const auto old_path = Common::FS::SanitizePath(old_path_);
|
||||||
|
const auto new_path = Common::FS::SanitizePath(new_path_);
|
||||||
|
|
||||||
|
// Non-default impls are highly encouraged to provide a more optimized version of this.
|
||||||
|
auto old_dir = OpenDirectory(old_path, OpenMode::Read);
|
||||||
|
if (old_dir == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
auto new_dir = OpenDirectory(new_path, OpenMode::Read);
|
||||||
|
if (new_dir != nullptr)
|
||||||
|
return nullptr;
|
||||||
|
new_dir = CreateDirectory(new_path, OpenMode::Write);
|
||||||
|
if (new_dir == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
for (const auto& file : old_dir->GetFiles()) {
|
||||||
|
const auto x = CopyFile(old_path + '/' + file->GetName(), new_path + '/' + file->GetName());
|
||||||
|
if (x == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& dir : old_dir->GetSubdirectories()) {
|
||||||
|
const auto x =
|
||||||
|
CopyDirectory(old_path + '/' + dir->GetName(), new_path + '/' + dir->GetName());
|
||||||
|
if (x == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir VfsFilesystem::MoveDirectory(std::string_view old_path, std::string_view new_path) {
|
||||||
|
const auto sanitized_old_path = Common::FS::SanitizePath(old_path);
|
||||||
|
const auto sanitized_new_path = Common::FS::SanitizePath(new_path);
|
||||||
|
|
||||||
|
// Non-default impls are highly encouraged to provide a more optimized version of this.
|
||||||
|
auto out = CopyDirectory(sanitized_old_path, sanitized_new_path);
|
||||||
|
if (out == nullptr)
|
||||||
|
return nullptr;
|
||||||
|
if (DeleteDirectory(sanitized_old_path))
|
||||||
|
return out;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VfsFilesystem::DeleteDirectory(std::string_view path_) {
|
||||||
|
const auto path = Common::FS::SanitizePath(path_);
|
||||||
|
auto parent = OpenDirectory(Common::FS::GetParentPath(path), OpenMode::Write);
|
||||||
|
if (parent == nullptr)
|
||||||
|
return false;
|
||||||
|
return parent->DeleteSubdirectoryRecursive(Common::FS::GetFilename(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
VfsFile::~VfsFile() = default;
|
||||||
|
|
||||||
|
std::string VfsFile::GetExtension() const {
|
||||||
|
return std::string(Common::FS::GetExtensionFromFilename(GetName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
VfsDirectory::~VfsDirectory() = default;
|
||||||
|
|
||||||
|
std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
|
||||||
|
u8 out{};
|
||||||
|
const std::size_t size = Read(&out, sizeof(u8), offset);
|
||||||
|
if (size == 1) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
|
||||||
|
std::vector<u8> out(size);
|
||||||
|
std::size_t read_size = Read(out.data(), size, offset);
|
||||||
|
out.resize(read_size);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> VfsFile::ReadAllBytes() const {
|
||||||
|
return ReadBytes(GetSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VfsFile::WriteByte(u8 data, std::size_t offset) {
|
||||||
|
return Write(&data, 1, offset) == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset) {
|
||||||
|
return Write(data.data(), data.size(), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VfsFile::GetFullPath() const {
|
||||||
|
if (GetContainingDirectory() == nullptr)
|
||||||
|
return '/' + GetName();
|
||||||
|
|
||||||
|
return GetContainingDirectory()->GetFullPath() + '/' + GetName();
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile VfsDirectory::GetFileRelative(std::string_view path) const {
|
||||||
|
auto vec = Common::FS::SplitPathComponents(path);
|
||||||
|
if (vec.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vec.size() == 1) {
|
||||||
|
return GetFile(vec[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dir = GetSubdirectory(vec[0]);
|
||||||
|
for (std::size_t component = 1; component < vec.size() - 1; ++component) {
|
||||||
|
if (dir == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = dir->GetSubdirectory(vec[component]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dir == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir->GetFile(vec.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile VfsDirectory::GetFileAbsolute(std::string_view path) const {
|
||||||
|
if (IsRoot()) {
|
||||||
|
return GetFileRelative(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetParentDirectory()->GetFileAbsolute(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir VfsDirectory::GetDirectoryRelative(std::string_view path) const {
|
||||||
|
auto vec = Common::FS::SplitPathComponents(path);
|
||||||
|
if (vec.empty()) {
|
||||||
|
// TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently
|
||||||
|
// because of const-ness
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dir = GetSubdirectory(vec[0]);
|
||||||
|
for (std::size_t component = 1; component < vec.size(); ++component) {
|
||||||
|
if (dir == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir = dir->GetSubdirectory(vec[component]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir VfsDirectory::GetDirectoryAbsolute(std::string_view path) const {
|
||||||
|
if (IsRoot()) {
|
||||||
|
return GetDirectoryRelative(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetParentDirectory()->GetDirectoryAbsolute(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile VfsDirectory::GetFile(std::string_view name) const {
|
||||||
|
const auto& files = GetFiles();
|
||||||
|
const auto iter = std::find_if(files.begin(), files.end(),
|
||||||
|
[&name](const auto& file1) { return name == file1->GetName(); });
|
||||||
|
return iter == files.end() ? nullptr : *iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileTimeStampRaw VfsDirectory::GetFileTimeStamp([[maybe_unused]] std::string_view path) const {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir VfsDirectory::GetSubdirectory(std::string_view name) const {
|
||||||
|
const auto& subs = GetSubdirectories();
|
||||||
|
const auto iter = std::find_if(subs.begin(), subs.end(),
|
||||||
|
[&name](const auto& file1) { return name == file1->GetName(); });
|
||||||
|
return iter == subs.end() ? nullptr : *iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VfsDirectory::IsRoot() const {
|
||||||
|
return GetParentDirectory() == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t VfsDirectory::GetSize() const {
|
||||||
|
const auto& files = GetFiles();
|
||||||
|
const auto sum_sizes = [](const auto& range) {
|
||||||
|
return std::accumulate(range.begin(), range.end(), 0ULL,
|
||||||
|
[](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); });
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto file_total = sum_sizes(files);
|
||||||
|
const auto& sub_dir = GetSubdirectories();
|
||||||
|
const auto subdir_total = sum_sizes(sub_dir);
|
||||||
|
|
||||||
|
return file_total + subdir_total;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile VfsDirectory::CreateFileRelative(std::string_view path) {
|
||||||
|
auto vec = Common::FS::SplitPathComponents(path);
|
||||||
|
if (vec.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vec.size() == 1) {
|
||||||
|
return CreateFile(vec[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dir = GetSubdirectory(vec[0]);
|
||||||
|
if (dir == nullptr) {
|
||||||
|
dir = CreateSubdirectory(vec[0]);
|
||||||
|
if (dir == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir->CreateFileRelative(Common::FS::GetPathWithoutTop(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile VfsDirectory::CreateFileAbsolute(std::string_view path) {
|
||||||
|
if (IsRoot()) {
|
||||||
|
return CreateFileRelative(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetParentDirectory()->CreateFileAbsolute(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir VfsDirectory::CreateDirectoryRelative(std::string_view path) {
|
||||||
|
auto vec = Common::FS::SplitPathComponents(path);
|
||||||
|
if (vec.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vec.size() == 1) {
|
||||||
|
return CreateSubdirectory(vec[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto dir = GetSubdirectory(vec[0]);
|
||||||
|
if (dir == nullptr) {
|
||||||
|
dir = CreateSubdirectory(vec[0]);
|
||||||
|
if (dir == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dir->CreateDirectoryRelative(Common::FS::GetPathWithoutTop(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir VfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
|
||||||
|
if (IsRoot()) {
|
||||||
|
return CreateDirectoryRelative(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetParentDirectory()->CreateDirectoryAbsolute(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
|
||||||
|
auto dir = GetSubdirectory(name);
|
||||||
|
if (dir == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
for (const auto& file : dir->GetFiles()) {
|
||||||
|
if (!DeleteFile(file->GetName())) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& sdir : dir->GetSubdirectories()) {
|
||||||
|
if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VfsDirectory::CleanSubdirectoryRecursive(std::string_view name) {
|
||||||
|
auto dir = GetSubdirectory(name);
|
||||||
|
if (dir == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = true;
|
||||||
|
for (const auto& file : dir->GetFiles()) {
|
||||||
|
if (!dir->DeleteFile(file->GetName())) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& sdir : dir->GetSubdirectories()) {
|
||||||
|
if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) {
|
||||||
|
success = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
|
||||||
|
const auto f1 = GetFile(src);
|
||||||
|
auto f2 = CreateFile(dest);
|
||||||
|
if (f1 == nullptr || f2 == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!f2->Resize(f1->GetSize())) {
|
||||||
|
DeleteFile(dest);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const {
|
||||||
|
std::map<std::string, VfsEntryType, std::less<>> out;
|
||||||
|
for (const auto& dir : GetSubdirectories())
|
||||||
|
out.emplace(dir->GetName(), VfsEntryType::Directory);
|
||||||
|
for (const auto& file : GetFiles())
|
||||||
|
out.emplace(file->GetName(), VfsEntryType::File);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string VfsDirectory::GetFullPath() const {
|
||||||
|
if (IsRoot())
|
||||||
|
return GetName();
|
||||||
|
|
||||||
|
return GetParentDirectory()->GetFullPath() + '/' + GetName();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadOnlyVfsDirectory::IsWritable() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadOnlyVfsDirectory::IsReadable() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir ReadOnlyVfsDirectory::CreateSubdirectory(std::string_view name) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile ReadOnlyVfsDirectory::CreateFile(std::string_view name) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile ReadOnlyVfsDirectory::CreateFileAbsolute(std::string_view path) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile ReadOnlyVfsDirectory::CreateFileRelative(std::string_view path) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir ReadOnlyVfsDirectory::CreateDirectoryAbsolute(std::string_view path) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir ReadOnlyVfsDirectory::CreateDirectoryRelative(std::string_view path) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadOnlyVfsDirectory::DeleteSubdirectory(std::string_view name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadOnlyVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadOnlyVfsDirectory::CleanSubdirectoryRecursive(std::string_view name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadOnlyVfsDirectory::DeleteFile(std::string_view name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) {
|
||||||
|
if (file1->GetSize() != file2->GetSize())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<u8> f1_v(block_size);
|
||||||
|
std::vector<u8> f2_v(block_size);
|
||||||
|
for (std::size_t i = 0; i < file1->GetSize(); i += block_size) {
|
||||||
|
auto f1_vs = file1->Read(f1_v.data(), block_size, i);
|
||||||
|
auto f2_vs = file2->Read(f2_v.data(), block_size, i);
|
||||||
|
|
||||||
|
if (f1_vs != f2_vs)
|
||||||
|
return false;
|
||||||
|
auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end());
|
||||||
|
if (iters.first != f1_v.end() && iters.second != f2_v.end())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) {
|
||||||
|
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
||||||
|
return false;
|
||||||
|
if (!dest->Resize(src->GetSize()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::vector<u8> temp(std::min(block_size, src->GetSize()));
|
||||||
|
for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
|
||||||
|
const auto read = std::min(block_size, src->GetSize() - i);
|
||||||
|
|
||||||
|
if (src->Read(temp.data(), read, i) != read) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dest->Write(temp.data(), read, i) != read) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) {
|
||||||
|
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (const auto& file : src->GetFiles()) {
|
||||||
|
const auto out = dest->CreateFile(file->GetName());
|
||||||
|
if (!VfsRawCopy(file, out, block_size))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& dir : src->GetSubdirectories()) {
|
||||||
|
const auto out = dest->CreateSubdirectory(dir->GetName());
|
||||||
|
if (!VfsRawCopyD(dir, out, block_size))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) {
|
||||||
|
const auto res = rel->GetDirectoryRelative(path);
|
||||||
|
if (res == nullptr)
|
||||||
|
return rel->CreateDirectoryRelative(path);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} // namespace FileSys
|
326
src/core/file_sys/vfs/vfs.h
Executable file
326
src/core/file_sys/vfs/vfs.h
Executable file
|
@ -0,0 +1,326 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "common/common_funcs.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
// An enumeration representing what can be at the end of a path in a VfsFilesystem
|
||||||
|
enum class VfsEntryType {
|
||||||
|
None,
|
||||||
|
File,
|
||||||
|
Directory,
|
||||||
|
};
|
||||||
|
|
||||||
|
// A class representing an abstract filesystem. A default implementation given the root VirtualDir
|
||||||
|
// is provided for convenience, but if the Vfs implementation has any additional state or
|
||||||
|
// functionality, they will need to override.
|
||||||
|
class VfsFilesystem {
|
||||||
|
public:
|
||||||
|
YUZU_NON_COPYABLE(VfsFilesystem);
|
||||||
|
YUZU_NON_MOVEABLE(VfsFilesystem);
|
||||||
|
|
||||||
|
explicit VfsFilesystem(VirtualDir root);
|
||||||
|
virtual ~VfsFilesystem();
|
||||||
|
|
||||||
|
// Gets the friendly name for the filesystem.
|
||||||
|
virtual std::string GetName() const;
|
||||||
|
|
||||||
|
// Return whether or not the user has read permissions on this filesystem.
|
||||||
|
virtual bool IsReadable() const;
|
||||||
|
// Return whether or not the user has write permission on this filesystem.
|
||||||
|
virtual bool IsWritable() const;
|
||||||
|
|
||||||
|
// Determine if the entry at path is non-existent, a file, or a directory.
|
||||||
|
virtual VfsEntryType GetEntryType(std::string_view path) const;
|
||||||
|
|
||||||
|
// Opens the file with path relative to root. If it doesn't exist, returns nullptr.
|
||||||
|
virtual VirtualFile OpenFile(std::string_view path, OpenMode perms);
|
||||||
|
// Creates a new, empty file at path
|
||||||
|
virtual VirtualFile CreateFile(std::string_view path, OpenMode perms);
|
||||||
|
// Copies the file from old_path to new_path, returning the new file on success and nullptr on
|
||||||
|
// failure.
|
||||||
|
virtual VirtualFile CopyFile(std::string_view old_path, std::string_view new_path);
|
||||||
|
// Moves the file from old_path to new_path, returning the moved file on success and nullptr on
|
||||||
|
// failure.
|
||||||
|
virtual VirtualFile MoveFile(std::string_view old_path, std::string_view new_path);
|
||||||
|
// Deletes the file with path relative to root, returning true on success.
|
||||||
|
virtual bool DeleteFile(std::string_view path);
|
||||||
|
|
||||||
|
// Opens the directory with path relative to root. If it doesn't exist, returns nullptr.
|
||||||
|
virtual VirtualDir OpenDirectory(std::string_view path, OpenMode perms);
|
||||||
|
// Creates a new, empty directory at path
|
||||||
|
virtual VirtualDir CreateDirectory(std::string_view path, OpenMode perms);
|
||||||
|
// Copies the directory from old_path to new_path, returning the new directory on success and
|
||||||
|
// nullptr on failure.
|
||||||
|
virtual VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path);
|
||||||
|
// Moves the directory from old_path to new_path, returning the moved directory on success and
|
||||||
|
// nullptr on failure.
|
||||||
|
virtual VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path);
|
||||||
|
// Deletes the directory with path relative to root, returning true on success.
|
||||||
|
virtual bool DeleteDirectory(std::string_view path);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Root directory in default implementation.
|
||||||
|
VirtualDir root;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A class representing a file in an abstract filesystem.
|
||||||
|
class VfsFile {
|
||||||
|
public:
|
||||||
|
YUZU_NON_COPYABLE(VfsFile);
|
||||||
|
YUZU_NON_MOVEABLE(VfsFile);
|
||||||
|
|
||||||
|
VfsFile() = default;
|
||||||
|
virtual ~VfsFile();
|
||||||
|
|
||||||
|
// Retrieves the file name.
|
||||||
|
virtual std::string GetName() const = 0;
|
||||||
|
// Retrieves the extension of the file name.
|
||||||
|
virtual std::string GetExtension() const;
|
||||||
|
// Retrieves the size of the file.
|
||||||
|
virtual std::size_t GetSize() const = 0;
|
||||||
|
// Resizes the file to new_size. Returns whether or not the operation was successful.
|
||||||
|
virtual bool Resize(std::size_t new_size) = 0;
|
||||||
|
// Gets a pointer to the directory containing this file, returning nullptr if there is none.
|
||||||
|
virtual VirtualDir GetContainingDirectory() const = 0;
|
||||||
|
|
||||||
|
// Returns whether or not the file can be written to.
|
||||||
|
virtual bool IsWritable() const = 0;
|
||||||
|
// Returns whether or not the file can be read from.
|
||||||
|
virtual bool IsReadable() const = 0;
|
||||||
|
|
||||||
|
// The primary method of reading from the file. Reads length bytes into data starting at offset
|
||||||
|
// into file. Returns number of bytes successfully read.
|
||||||
|
virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0;
|
||||||
|
// The primary method of writing to the file. Writes length bytes from data starting at offset
|
||||||
|
// into file. Returns number of bytes successfully written.
|
||||||
|
virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;
|
||||||
|
|
||||||
|
// Reads exactly one byte at the offset provided, returning std::nullopt on error.
|
||||||
|
virtual std::optional<u8> ReadByte(std::size_t offset = 0) const;
|
||||||
|
// Reads size bytes starting at offset in file into a vector.
|
||||||
|
virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
|
||||||
|
// Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
|
||||||
|
// 0)'
|
||||||
|
virtual std::vector<u8> ReadAllBytes() const;
|
||||||
|
|
||||||
|
// Reads an array of type T, size number_elements starting at offset.
|
||||||
|
// Returns the number of bytes (sizeof(T)*number_elements) read successfully.
|
||||||
|
template <typename T>
|
||||||
|
std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const {
|
||||||
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||||
|
|
||||||
|
return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads size bytes into the memory starting at data starting at offset into the file.
|
||||||
|
// Returns the number of bytes read successfully.
|
||||||
|
template <typename T>
|
||||||
|
std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const {
|
||||||
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||||
|
return Read(reinterpret_cast<u8*>(data), size, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads one object of type T starting at offset in file.
|
||||||
|
// Returns the number of bytes read successfully (sizeof(T)).
|
||||||
|
template <typename T>
|
||||||
|
std::size_t ReadObject(T* data, std::size_t offset = 0) const {
|
||||||
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||||
|
return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes exactly one byte to offset in file and returns whether or not the byte was written
|
||||||
|
// successfully.
|
||||||
|
virtual bool WriteByte(u8 data, std::size_t offset = 0);
|
||||||
|
// Writes a vector of bytes to offset in file and returns the number of bytes successfully
|
||||||
|
// written.
|
||||||
|
virtual std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset = 0);
|
||||||
|
|
||||||
|
// Writes an array of type T, size number_elements to offset in file.
|
||||||
|
// Returns the number of bytes (sizeof(T)*number_elements) written successfully.
|
||||||
|
template <typename T>
|
||||||
|
std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
|
||||||
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||||
|
return Write(reinterpret_cast<const u8*>(data), number_elements * sizeof(T), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes size bytes starting at memory location data to offset in file.
|
||||||
|
// Returns the number of bytes written successfully.
|
||||||
|
template <typename T>
|
||||||
|
std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) {
|
||||||
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||||
|
return Write(reinterpret_cast<const u8*>(data), size, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writes one object of type T to offset in file.
|
||||||
|
// Returns the number of bytes written successfully (sizeof(T)).
|
||||||
|
template <typename T>
|
||||||
|
std::size_t WriteObject(const T& data, std::size_t offset = 0) {
|
||||||
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
||||||
|
return Write(reinterpret_cast<const u8*>(&data), sizeof(T), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Renames the file to name. Returns whether or not the operation was successful.
|
||||||
|
virtual bool Rename(std::string_view name) = 0;
|
||||||
|
|
||||||
|
// Returns the full path of this file as a string, recursively
|
||||||
|
virtual std::string GetFullPath() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A class representing a directory in an abstract filesystem.
|
||||||
|
class VfsDirectory {
|
||||||
|
public:
|
||||||
|
YUZU_NON_COPYABLE(VfsDirectory);
|
||||||
|
YUZU_NON_MOVEABLE(VfsDirectory);
|
||||||
|
|
||||||
|
VfsDirectory() = default;
|
||||||
|
virtual ~VfsDirectory();
|
||||||
|
|
||||||
|
// Retrieves the file located at path as if the current directory was root. Returns nullptr if
|
||||||
|
// not found.
|
||||||
|
virtual VirtualFile GetFileRelative(std::string_view path) const;
|
||||||
|
// Calls GetFileRelative(path) on the root of the current directory.
|
||||||
|
virtual VirtualFile GetFileAbsolute(std::string_view path) const;
|
||||||
|
|
||||||
|
// Retrieves the directory located at path as if the current directory was root. Returns nullptr
|
||||||
|
// if not found.
|
||||||
|
virtual VirtualDir GetDirectoryRelative(std::string_view path) const;
|
||||||
|
// Calls GetDirectoryRelative(path) on the root of the current directory.
|
||||||
|
virtual VirtualDir GetDirectoryAbsolute(std::string_view path) const;
|
||||||
|
|
||||||
|
// Returns a vector containing all of the files in this directory.
|
||||||
|
virtual std::vector<VirtualFile> GetFiles() const = 0;
|
||||||
|
// Returns the file with filename matching name. Returns nullptr if directory doesn't have a
|
||||||
|
// file with name.
|
||||||
|
virtual VirtualFile GetFile(std::string_view name) const;
|
||||||
|
|
||||||
|
// Returns a struct containing the file's timestamp.
|
||||||
|
virtual FileTimeStampRaw GetFileTimeStamp(std::string_view path) const;
|
||||||
|
|
||||||
|
// Returns a vector containing all of the subdirectories in this directory.
|
||||||
|
virtual std::vector<VirtualDir> GetSubdirectories() const = 0;
|
||||||
|
// Returns the directory with name matching name. Returns nullptr if directory doesn't have a
|
||||||
|
// directory with name.
|
||||||
|
virtual VirtualDir GetSubdirectory(std::string_view name) const;
|
||||||
|
|
||||||
|
// Returns whether or not the directory can be written to.
|
||||||
|
virtual bool IsWritable() const = 0;
|
||||||
|
// Returns whether of not the directory can be read from.
|
||||||
|
virtual bool IsReadable() const = 0;
|
||||||
|
|
||||||
|
// Returns whether or not the directory is the root of the current file tree.
|
||||||
|
virtual bool IsRoot() const;
|
||||||
|
|
||||||
|
// Returns the name of the directory.
|
||||||
|
virtual std::string GetName() const = 0;
|
||||||
|
// Returns the total size of all files and subdirectories in this directory.
|
||||||
|
virtual std::size_t GetSize() const;
|
||||||
|
// Returns the parent directory of this directory. Returns nullptr if this directory is root or
|
||||||
|
// has no parent.
|
||||||
|
virtual VirtualDir GetParentDirectory() const = 0;
|
||||||
|
|
||||||
|
// Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr
|
||||||
|
// if the operation failed.
|
||||||
|
virtual VirtualDir CreateSubdirectory(std::string_view name) = 0;
|
||||||
|
// Creates a new file with name name. Returns a pointer to the new file or nullptr if the
|
||||||
|
// operation failed.
|
||||||
|
virtual VirtualFile CreateFile(std::string_view name) = 0;
|
||||||
|
|
||||||
|
// Creates a new file at the path relative to this directory. Also creates directories if
|
||||||
|
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
|
||||||
|
virtual VirtualFile CreateFileRelative(std::string_view path);
|
||||||
|
|
||||||
|
// Creates a new file at the path relative to root of this directory. Also creates directories
|
||||||
|
// if they do not exist and is supported by this implementation. Returns nullptr on any failure.
|
||||||
|
virtual VirtualFile CreateFileAbsolute(std::string_view path);
|
||||||
|
|
||||||
|
// Creates a new directory at the path relative to this directory. Also creates directories if
|
||||||
|
// they do not exist and is supported by this implementation. Returns nullptr on any failure.
|
||||||
|
virtual VirtualDir CreateDirectoryRelative(std::string_view path);
|
||||||
|
|
||||||
|
// Creates a new directory at the path relative to root of this directory. Also creates
|
||||||
|
// directories if they do not exist and is supported by this implementation. Returns nullptr on
|
||||||
|
// any failure.
|
||||||
|
virtual VirtualDir CreateDirectoryAbsolute(std::string_view path);
|
||||||
|
|
||||||
|
// Deletes the subdirectory with the given name and returns true on success.
|
||||||
|
virtual bool DeleteSubdirectory(std::string_view name) = 0;
|
||||||
|
|
||||||
|
// Deletes all subdirectories and files within the provided directory and then deletes
|
||||||
|
// the directory itself. Returns true on success.
|
||||||
|
virtual bool DeleteSubdirectoryRecursive(std::string_view name);
|
||||||
|
|
||||||
|
// Deletes all subdirectories and files within the provided directory.
|
||||||
|
// Unlike DeleteSubdirectoryRecursive, this does not delete the provided directory.
|
||||||
|
virtual bool CleanSubdirectoryRecursive(std::string_view name);
|
||||||
|
|
||||||
|
// Returns whether or not the file with name name was deleted successfully.
|
||||||
|
virtual bool DeleteFile(std::string_view name) = 0;
|
||||||
|
|
||||||
|
// Returns whether or not this directory was renamed to name.
|
||||||
|
virtual bool Rename(std::string_view name) = 0;
|
||||||
|
|
||||||
|
// Returns whether or not the file with name src was successfully copied to a new file with name
|
||||||
|
// dest.
|
||||||
|
virtual bool Copy(std::string_view src, std::string_view dest);
|
||||||
|
|
||||||
|
// Gets all of the entries directly in the directory (files and dirs), returning a map between
|
||||||
|
// item name -> type.
|
||||||
|
virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
|
||||||
|
|
||||||
|
// Returns the full path of this directory as a string, recursively
|
||||||
|
virtual std::string GetFullPath() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A convenience partial-implementation of VfsDirectory that stubs out methods that should only work
|
||||||
|
// if writable. This is to avoid redundant empty methods everywhere.
|
||||||
|
class ReadOnlyVfsDirectory : public VfsDirectory {
|
||||||
|
public:
|
||||||
|
bool IsWritable() const override;
|
||||||
|
bool IsReadable() const override;
|
||||||
|
VirtualDir CreateSubdirectory(std::string_view name) override;
|
||||||
|
VirtualFile CreateFile(std::string_view name) override;
|
||||||
|
VirtualFile CreateFileAbsolute(std::string_view path) override;
|
||||||
|
VirtualFile CreateFileRelative(std::string_view path) override;
|
||||||
|
VirtualDir CreateDirectoryAbsolute(std::string_view path) override;
|
||||||
|
VirtualDir CreateDirectoryRelative(std::string_view path) override;
|
||||||
|
bool DeleteSubdirectory(std::string_view name) override;
|
||||||
|
bool DeleteSubdirectoryRecursive(std::string_view name) override;
|
||||||
|
bool CleanSubdirectoryRecursive(std::string_view name) override;
|
||||||
|
bool DeleteFile(std::string_view name) override;
|
||||||
|
bool Rename(std::string_view name) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Compare the two files, byte-for-byte, in increments specified by block_size
|
||||||
|
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2,
|
||||||
|
std::size_t block_size = 0x1000);
|
||||||
|
|
||||||
|
// A method that copies the raw data between two different implementations of VirtualFile. If you
|
||||||
|
// are using the same implementation, it is probably better to use the Copy method in the parent
|
||||||
|
// directory of src/dest.
|
||||||
|
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000);
|
||||||
|
|
||||||
|
// A method that performs a similar function to VfsRawCopy above, but instead copies entire
|
||||||
|
// directories. It suffers the same performance penalties as above and an implementation-specific
|
||||||
|
// Copy should always be preferred.
|
||||||
|
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000);
|
||||||
|
|
||||||
|
// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
|
||||||
|
// it attempts to create it and returns the new dir or nullptr on failure.
|
||||||
|
VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path);
|
||||||
|
|
||||||
|
} // namespace FileSys
|
63
src/core/file_sys/vfs/vfs_cached.cpp
Executable file
63
src/core/file_sys/vfs/vfs_cached.cpp
Executable file
|
@ -0,0 +1,63 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include "core/file_sys/vfs/vfs_cached.h"
|
||||||
|
#include "core/file_sys/vfs/vfs_types.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
CachedVfsDirectory::CachedVfsDirectory(VirtualDir&& source_dir)
|
||||||
|
: name(source_dir->GetName()), parent(source_dir->GetParentDirectory()) {
|
||||||
|
for (auto& dir : source_dir->GetSubdirectories()) {
|
||||||
|
dirs.emplace(dir->GetName(), std::make_shared<CachedVfsDirectory>(std::move(dir)));
|
||||||
|
}
|
||||||
|
for (auto& file : source_dir->GetFiles()) {
|
||||||
|
files.emplace(file->GetName(), std::move(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CachedVfsDirectory::~CachedVfsDirectory() = default;
|
||||||
|
|
||||||
|
VirtualFile CachedVfsDirectory::GetFile(std::string_view file_name) const {
|
||||||
|
auto it = files.find(file_name);
|
||||||
|
if (it != files.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir CachedVfsDirectory::GetSubdirectory(std::string_view dir_name) const {
|
||||||
|
auto it = dirs.find(dir_name);
|
||||||
|
if (it != dirs.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualFile> CachedVfsDirectory::GetFiles() const {
|
||||||
|
std::vector<VirtualFile> out;
|
||||||
|
for (auto& [file_name, file] : files) {
|
||||||
|
out.push_back(file);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualDir> CachedVfsDirectory::GetSubdirectories() const {
|
||||||
|
std::vector<VirtualDir> out;
|
||||||
|
for (auto& [dir_name, dir] : dirs) {
|
||||||
|
out.push_back(dir);
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CachedVfsDirectory::GetName() const {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir CachedVfsDirectory::GetParentDirectory() const {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileSys
|
31
src/core/file_sys/vfs/vfs_cached.h
Executable file
31
src/core/file_sys/vfs/vfs_cached.h
Executable file
|
@ -0,0 +1,31 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
class CachedVfsDirectory : public ReadOnlyVfsDirectory {
|
||||||
|
public:
|
||||||
|
CachedVfsDirectory(VirtualDir&& source_directory);
|
||||||
|
|
||||||
|
~CachedVfsDirectory() override;
|
||||||
|
VirtualFile GetFile(std::string_view file_name) const override;
|
||||||
|
VirtualDir GetSubdirectory(std::string_view dir_name) const override;
|
||||||
|
std::vector<VirtualFile> GetFiles() const override;
|
||||||
|
std::vector<VirtualDir> GetSubdirectories() const override;
|
||||||
|
std::string GetName() const override;
|
||||||
|
VirtualDir GetParentDirectory() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string name;
|
||||||
|
VirtualDir parent;
|
||||||
|
std::map<std::string, VirtualDir, std::less<>> dirs;
|
||||||
|
std::map<std::string, VirtualFile, std::less<>> files;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
192
src/core/file_sys/vfs/vfs_concat.cpp
Executable file
192
src/core/file_sys/vfs/vfs_concat.cpp
Executable file
|
@ -0,0 +1,192 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "core/file_sys/vfs/vfs_concat.h"
|
||||||
|
#include "core/file_sys/vfs/vfs_static.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
ConcatenatedVfsFile::ConcatenatedVfsFile(std::string&& name_, ConcatenationMap&& concatenation_map_)
|
||||||
|
: concatenation_map(std::move(concatenation_map_)), name(std::move(name_)) {
|
||||||
|
DEBUG_ASSERT(this->VerifyContinuity());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConcatenatedVfsFile::VerifyContinuity() const {
|
||||||
|
u64 last_offset = 0;
|
||||||
|
for (auto& entry : concatenation_map) {
|
||||||
|
if (entry.offset != last_offset) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
last_offset = entry.offset + entry.file->GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
|
||||||
|
|
||||||
|
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::string&& name,
|
||||||
|
std::vector<VirtualFile>&& files) {
|
||||||
|
// Fold trivial cases.
|
||||||
|
if (files.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (files.size() == 1) {
|
||||||
|
return files.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the concatenation map from the input.
|
||||||
|
std::vector<ConcatenationEntry> concatenation_map;
|
||||||
|
concatenation_map.reserve(files.size());
|
||||||
|
u64 last_offset = 0;
|
||||||
|
|
||||||
|
for (auto& file : files) {
|
||||||
|
const auto size = file->GetSize();
|
||||||
|
|
||||||
|
concatenation_map.emplace_back(ConcatenationEntry{
|
||||||
|
.offset = last_offset,
|
||||||
|
.file = std::move(file),
|
||||||
|
});
|
||||||
|
|
||||||
|
last_offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(
|
||||||
|
u8 filler_byte, std::string&& name, std::vector<std::pair<u64, VirtualFile>>&& files) {
|
||||||
|
// Fold trivial cases.
|
||||||
|
if (files.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (files.size() == 1) {
|
||||||
|
return files.begin()->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the concatenation map from the input.
|
||||||
|
std::vector<ConcatenationEntry> concatenation_map;
|
||||||
|
|
||||||
|
concatenation_map.reserve(files.size());
|
||||||
|
u64 last_offset = 0;
|
||||||
|
|
||||||
|
// Iteration of a multimap is ordered, so offset will be strictly non-decreasing.
|
||||||
|
for (auto& [offset, file] : files) {
|
||||||
|
const auto size = file->GetSize();
|
||||||
|
|
||||||
|
if (offset > last_offset) {
|
||||||
|
concatenation_map.emplace_back(ConcatenationEntry{
|
||||||
|
.offset = last_offset,
|
||||||
|
.file = std::make_shared<StaticVfsFile>(filler_byte, offset - last_offset),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
concatenation_map.emplace_back(ConcatenationEntry{
|
||||||
|
.offset = offset,
|
||||||
|
.file = std::move(file),
|
||||||
|
});
|
||||||
|
|
||||||
|
last_offset = offset + size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return VirtualFile(new ConcatenatedVfsFile(std::move(name), std::move(concatenation_map)));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ConcatenatedVfsFile::GetName() const {
|
||||||
|
if (concatenation_map.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (!name.empty()) {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
return concatenation_map.front().file->GetName();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t ConcatenatedVfsFile::GetSize() const {
|
||||||
|
if (concatenation_map.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return concatenation_map.back().offset + concatenation_map.back().file->GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConcatenatedVfsFile::Resize(std::size_t new_size) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir ConcatenatedVfsFile::GetContainingDirectory() const {
|
||||||
|
if (concatenation_map.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return concatenation_map.front().file->GetContainingDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConcatenatedVfsFile::IsWritable() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConcatenatedVfsFile::IsReadable() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
|
||||||
|
const ConcatenationEntry key{
|
||||||
|
.offset = offset,
|
||||||
|
.file = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Read nothing if the map is empty.
|
||||||
|
if (concatenation_map.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Binary search to find the iterator to the first position we can check.
|
||||||
|
// It must exist, since we are not empty and are comparing unsigned integers.
|
||||||
|
auto it = std::prev(std::upper_bound(concatenation_map.begin(), concatenation_map.end(), key));
|
||||||
|
u64 cur_length = length;
|
||||||
|
u64 cur_offset = offset;
|
||||||
|
|
||||||
|
while (cur_length > 0 && it != concatenation_map.end()) {
|
||||||
|
// Check if we can read the file at this position.
|
||||||
|
const auto& file = it->file;
|
||||||
|
const u64 map_offset = it->offset;
|
||||||
|
const u64 file_size = file->GetSize();
|
||||||
|
|
||||||
|
if (cur_offset > map_offset + file_size) {
|
||||||
|
// Entirely out of bounds read.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the file at this position.
|
||||||
|
const u64 file_seek = cur_offset - map_offset;
|
||||||
|
const u64 intended_read_size = std::min<u64>(cur_length, file_size - file_seek);
|
||||||
|
const u64 actual_read_size =
|
||||||
|
file->Read(data + (cur_offset - offset), intended_read_size, file_seek);
|
||||||
|
|
||||||
|
// Update tracking.
|
||||||
|
cur_offset += actual_read_size;
|
||||||
|
cur_length -= actual_read_size;
|
||||||
|
it++;
|
||||||
|
|
||||||
|
// If we encountered a short read, we're done.
|
||||||
|
if (actual_read_size < intended_read_size) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cur_offset - offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConcatenatedVfsFile::Rename(std::string_view new_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileSys
|
57
src/core/file_sys/vfs/vfs_concat.h
Executable file
57
src/core/file_sys/vfs/vfs_concat.h
Executable file
|
@ -0,0 +1,57 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <compare>
|
||||||
|
#include <map>
|
||||||
|
#include <memory>
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
|
||||||
|
// read-only.
|
||||||
|
class ConcatenatedVfsFile : public VfsFile {
|
||||||
|
private:
|
||||||
|
struct ConcatenationEntry {
|
||||||
|
u64 offset;
|
||||||
|
VirtualFile file;
|
||||||
|
|
||||||
|
auto operator<=>(const ConcatenationEntry& other) const {
|
||||||
|
return this->offset <=> other.offset;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
using ConcatenationMap = std::vector<ConcatenationEntry>;
|
||||||
|
|
||||||
|
explicit ConcatenatedVfsFile(std::string&& name,
|
||||||
|
std::vector<ConcatenationEntry>&& concatenation_map);
|
||||||
|
bool VerifyContinuity() const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~ConcatenatedVfsFile() override;
|
||||||
|
|
||||||
|
/// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
|
||||||
|
static VirtualFile MakeConcatenatedFile(std::string&& name, std::vector<VirtualFile>&& files);
|
||||||
|
|
||||||
|
/// Convenience function that turns a map of offsets to files into a concatenated file, filling
|
||||||
|
/// gaps with a given filler byte.
|
||||||
|
static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::string&& name,
|
||||||
|
std::vector<std::pair<u64, VirtualFile>>&& files);
|
||||||
|
|
||||||
|
std::string GetName() const override;
|
||||||
|
std::size_t GetSize() const override;
|
||||||
|
bool Resize(std::size_t new_size) override;
|
||||||
|
VirtualDir GetContainingDirectory() const override;
|
||||||
|
bool IsWritable() const override;
|
||||||
|
bool IsReadable() const override;
|
||||||
|
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
|
||||||
|
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
|
||||||
|
bool Rename(std::string_view new_name) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ConcatenationMap concatenation_map;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
132
src/core/file_sys/vfs/vfs_layered.cpp
Executable file
132
src/core/file_sys/vfs/vfs_layered.cpp
Executable file
|
@ -0,0 +1,132 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <set>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <utility>
|
||||||
|
#include "core/file_sys/vfs/vfs_layered.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs_, std::string name_)
|
||||||
|
: dirs(std::move(dirs_)), name(std::move(name_)) {}
|
||||||
|
|
||||||
|
LayeredVfsDirectory::~LayeredVfsDirectory() = default;
|
||||||
|
|
||||||
|
VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dirs,
|
||||||
|
std::string name) {
|
||||||
|
if (dirs.empty())
|
||||||
|
return nullptr;
|
||||||
|
if (dirs.size() == 1)
|
||||||
|
return dirs[0];
|
||||||
|
|
||||||
|
return VirtualDir(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
|
||||||
|
for (const auto& layer : dirs) {
|
||||||
|
const auto file = layer->GetFileRelative(path);
|
||||||
|
if (file != nullptr)
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir LayeredVfsDirectory::GetDirectoryRelative(std::string_view path) const {
|
||||||
|
std::vector<VirtualDir> out;
|
||||||
|
for (const auto& layer : dirs) {
|
||||||
|
auto dir = layer->GetDirectoryRelative(path);
|
||||||
|
if (dir != nullptr) {
|
||||||
|
out.emplace_back(std::move(dir));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeLayeredDirectory(std::move(out));
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile LayeredVfsDirectory::GetFile(std::string_view file_name) const {
|
||||||
|
return GetFileRelative(file_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir LayeredVfsDirectory::GetSubdirectory(std::string_view subdir_name) const {
|
||||||
|
return GetDirectoryRelative(subdir_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LayeredVfsDirectory::GetFullPath() const {
|
||||||
|
return dirs[0]->GetFullPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualFile> LayeredVfsDirectory::GetFiles() const {
|
||||||
|
std::vector<VirtualFile> out;
|
||||||
|
std::unordered_set<std::string> out_names;
|
||||||
|
|
||||||
|
for (const auto& layer : dirs) {
|
||||||
|
for (auto& file : layer->GetFiles()) {
|
||||||
|
const auto [it, is_new] = out_names.emplace(file->GetName());
|
||||||
|
if (is_new) {
|
||||||
|
out.emplace_back(std::move(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualDir> LayeredVfsDirectory::GetSubdirectories() const {
|
||||||
|
std::vector<VirtualDir> out;
|
||||||
|
std::unordered_set<std::string> out_names;
|
||||||
|
|
||||||
|
for (const auto& layer : dirs) {
|
||||||
|
for (const auto& sd : layer->GetSubdirectories()) {
|
||||||
|
out_names.emplace(sd->GetName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out.reserve(out_names.size());
|
||||||
|
for (const auto& subdir : out_names) {
|
||||||
|
out.emplace_back(GetSubdirectory(subdir));
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LayeredVfsDirectory::IsWritable() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LayeredVfsDirectory::IsReadable() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string LayeredVfsDirectory::GetName() const {
|
||||||
|
return name.empty() ? dirs[0]->GetName() : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir LayeredVfsDirectory::GetParentDirectory() const {
|
||||||
|
return dirs[0]->GetParentDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir LayeredVfsDirectory::CreateSubdirectory(std::string_view subdir_name) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile LayeredVfsDirectory::CreateFile(std::string_view file_name) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view subdir_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LayeredVfsDirectory::DeleteFile(std::string_view file_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LayeredVfsDirectory::Rename(std::string_view new_name) {
|
||||||
|
name = new_name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileSys
|
46
src/core/file_sys/vfs/vfs_layered.h
Executable file
46
src/core/file_sys/vfs/vfs_layered.h
Executable file
|
@ -0,0 +1,46 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first
|
||||||
|
// one and falling back to the one after. The highest priority directory (overwrites all others)
|
||||||
|
// should be element 0 in the dirs vector.
|
||||||
|
class LayeredVfsDirectory : public VfsDirectory {
|
||||||
|
explicit LayeredVfsDirectory(std::vector<VirtualDir> dirs_, std::string name_);
|
||||||
|
|
||||||
|
public:
|
||||||
|
~LayeredVfsDirectory() override;
|
||||||
|
|
||||||
|
/// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
|
||||||
|
static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = "");
|
||||||
|
|
||||||
|
VirtualFile GetFileRelative(std::string_view path) const override;
|
||||||
|
VirtualDir GetDirectoryRelative(std::string_view path) const override;
|
||||||
|
VirtualFile GetFile(std::string_view file_name) const override;
|
||||||
|
VirtualDir GetSubdirectory(std::string_view subdir_name) const override;
|
||||||
|
std::string GetFullPath() const override;
|
||||||
|
|
||||||
|
std::vector<VirtualFile> GetFiles() const override;
|
||||||
|
std::vector<VirtualDir> GetSubdirectories() const override;
|
||||||
|
bool IsWritable() const override;
|
||||||
|
bool IsReadable() const override;
|
||||||
|
std::string GetName() const override;
|
||||||
|
VirtualDir GetParentDirectory() const override;
|
||||||
|
VirtualDir CreateSubdirectory(std::string_view subdir_name) override;
|
||||||
|
VirtualFile CreateFile(std::string_view file_name) override;
|
||||||
|
bool DeleteSubdirectory(std::string_view subdir_name) override;
|
||||||
|
bool DeleteFile(std::string_view file_name) override;
|
||||||
|
bool Rename(std::string_view new_name) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<VirtualDir> dirs;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
98
src/core/file_sys/vfs/vfs_offset.cpp
Executable file
98
src/core/file_sys/vfs/vfs_offset.cpp
Executable file
|
@ -0,0 +1,98 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "core/file_sys/vfs/vfs_offset.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
OffsetVfsFile::OffsetVfsFile(VirtualFile file_, std::size_t size_, std::size_t offset_,
|
||||||
|
std::string name_, VirtualDir parent_)
|
||||||
|
: file(file_), offset(offset_), size(size_), name(std::move(name_)),
|
||||||
|
parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {}
|
||||||
|
|
||||||
|
OffsetVfsFile::~OffsetVfsFile() = default;
|
||||||
|
|
||||||
|
std::string OffsetVfsFile::GetName() const {
|
||||||
|
return name.empty() ? file->GetName() : name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t OffsetVfsFile::GetSize() const {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OffsetVfsFile::Resize(std::size_t new_size) {
|
||||||
|
if (offset + new_size < file->GetSize()) {
|
||||||
|
size = new_size;
|
||||||
|
} else {
|
||||||
|
auto res = file->Resize(offset + new_size);
|
||||||
|
if (!res)
|
||||||
|
return false;
|
||||||
|
size = new_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir OffsetVfsFile::GetContainingDirectory() const {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OffsetVfsFile::IsWritable() const {
|
||||||
|
return file->IsWritable();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OffsetVfsFile::IsReadable() const {
|
||||||
|
return file->IsReadable();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t OffsetVfsFile::Read(u8* data, std::size_t length, std::size_t r_offset) const {
|
||||||
|
return file->Read(data, TrimToFit(length, r_offset), offset + r_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t r_offset) {
|
||||||
|
return file->Write(data, TrimToFit(length, r_offset), offset + r_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
|
||||||
|
if (r_offset >= size) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return file->ReadByte(offset + r_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
|
||||||
|
return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> OffsetVfsFile::ReadAllBytes() const {
|
||||||
|
return file->ReadBytes(size, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OffsetVfsFile::WriteByte(u8 data, std::size_t r_offset) {
|
||||||
|
if (r_offset < size)
|
||||||
|
return file->WriteByte(data, offset + r_offset);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, std::size_t r_offset) {
|
||||||
|
return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OffsetVfsFile::Rename(std::string_view new_name) {
|
||||||
|
return file->Rename(new_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t OffsetVfsFile::GetOffset() const {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t OffsetVfsFile::TrimToFit(std::size_t r_size, std::size_t r_offset) const {
|
||||||
|
return std::clamp(r_size, std::size_t{0}, size - r_offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileSys
|
50
src/core/file_sys/vfs/vfs_offset.h
Executable file
50
src/core/file_sys/vfs/vfs_offset.h
Executable file
|
@ -0,0 +1,50 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
// An implementation of VfsFile that wraps around another VfsFile at a certain offset.
|
||||||
|
// Similar to seeking to an offset.
|
||||||
|
// If the file is writable, operations that would write past the end of the offset file will expand
|
||||||
|
// the size of this wrapper.
|
||||||
|
class OffsetVfsFile : public VfsFile {
|
||||||
|
public:
|
||||||
|
OffsetVfsFile(VirtualFile file, std::size_t size, std::size_t offset = 0,
|
||||||
|
std::string new_name = "", VirtualDir new_parent = nullptr);
|
||||||
|
~OffsetVfsFile() override;
|
||||||
|
|
||||||
|
std::string GetName() const override;
|
||||||
|
std::size_t GetSize() const override;
|
||||||
|
bool Resize(std::size_t new_size) override;
|
||||||
|
VirtualDir GetContainingDirectory() const override;
|
||||||
|
bool IsWritable() const override;
|
||||||
|
bool IsReadable() const override;
|
||||||
|
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
|
||||||
|
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
|
||||||
|
std::optional<u8> ReadByte(std::size_t offset) const override;
|
||||||
|
std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override;
|
||||||
|
std::vector<u8> ReadAllBytes() const override;
|
||||||
|
bool WriteByte(u8 data, std::size_t offset) override;
|
||||||
|
std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset) override;
|
||||||
|
|
||||||
|
bool Rename(std::string_view new_name) override;
|
||||||
|
|
||||||
|
std::size_t GetOffset() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::size_t TrimToFit(std::size_t r_size, std::size_t r_offset) const;
|
||||||
|
|
||||||
|
VirtualFile file;
|
||||||
|
std::size_t offset;
|
||||||
|
std::size_t size;
|
||||||
|
std::string name;
|
||||||
|
VirtualDir parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
527
src/core/file_sys/vfs/vfs_real.cpp
Executable file
527
src/core/file_sys/vfs/vfs_real.cpp
Executable file
|
@ -0,0 +1,527 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/fs/file.h"
|
||||||
|
#include "common/fs/fs.h"
|
||||||
|
#include "common/fs/path_util.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
#include "core/file_sys/vfs/vfs_real.h"
|
||||||
|
|
||||||
|
// For FileTimeStampRaw
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define stat _stat64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
namespace FS = Common::FS;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr size_t MaxOpenFiles = 512;
|
||||||
|
|
||||||
|
constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(OpenMode mode) {
|
||||||
|
switch (mode) {
|
||||||
|
case OpenMode::Read:
|
||||||
|
return FS::FileAccessMode::Read;
|
||||||
|
case OpenMode::Write:
|
||||||
|
case OpenMode::ReadWrite:
|
||||||
|
case OpenMode::AllowAppend:
|
||||||
|
case OpenMode::All:
|
||||||
|
return FS::FileAccessMode::ReadWrite;
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {}
|
||||||
|
RealVfsFilesystem::~RealVfsFilesystem() = default;
|
||||||
|
|
||||||
|
std::string RealVfsFilesystem::GetName() const {
|
||||||
|
return "Real";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsFilesystem::IsReadable() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsFilesystem::IsWritable() const {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const {
|
||||||
|
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
|
if (!FS::Exists(path)) {
|
||||||
|
return VfsEntryType::None;
|
||||||
|
}
|
||||||
|
if (FS::IsDir(path)) {
|
||||||
|
return VfsEntryType::Directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
return VfsEntryType::File;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile RealVfsFilesystem::OpenFileFromEntry(std::string_view path_, std::optional<u64> size,
|
||||||
|
OpenMode perms) {
|
||||||
|
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
|
std::scoped_lock lk{list_lock};
|
||||||
|
|
||||||
|
if (auto it = cache.find(path); it != cache.end()) {
|
||||||
|
if (auto file = it->second.lock(); file) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!size && !FS::IsFile(path)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reference = std::make_unique<FileReference>();
|
||||||
|
this->InsertReferenceIntoListLocked(*reference);
|
||||||
|
|
||||||
|
auto file = std::shared_ptr<RealVfsFile>(
|
||||||
|
new RealVfsFile(*this, std::move(reference), path, perms, size));
|
||||||
|
cache[path] = file;
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, OpenMode perms) {
|
||||||
|
return OpenFileFromEntry(path_, {}, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, OpenMode perms) {
|
||||||
|
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{list_lock};
|
||||||
|
cache.erase(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Current usages of CreateFile expect to delete the contents of an existing file.
|
||||||
|
if (FS::IsFile(path)) {
|
||||||
|
FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile};
|
||||||
|
|
||||||
|
if (!temp.IsOpen()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp.Close();
|
||||||
|
|
||||||
|
return OpenFile(path, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FS::NewFile(path)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OpenFile(path, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) {
|
||||||
|
// Unused
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) {
|
||||||
|
const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
|
const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{list_lock};
|
||||||
|
cache.erase(old_path);
|
||||||
|
cache.erase(new_path);
|
||||||
|
}
|
||||||
|
if (!FS::RenameFile(old_path, new_path)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return OpenFile(new_path, OpenMode::ReadWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsFilesystem::DeleteFile(std::string_view path_) {
|
||||||
|
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
|
{
|
||||||
|
std::scoped_lock lk{list_lock};
|
||||||
|
cache.erase(path);
|
||||||
|
}
|
||||||
|
return FS::RemoveFile(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, OpenMode perms) {
|
||||||
|
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
|
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, OpenMode perms) {
|
||||||
|
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
|
if (!FS::CreateDirs(path)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_,
|
||||||
|
std::string_view new_path_) {
|
||||||
|
// Unused
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_,
|
||||||
|
std::string_view new_path_) {
|
||||||
|
const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
|
const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
|
|
||||||
|
if (!FS::RenameDir(old_path, new_path)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return OpenDirectory(new_path, OpenMode::ReadWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) {
|
||||||
|
const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault);
|
||||||
|
return FS::RemoveDirRecursively(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_lock<std::mutex> RealVfsFilesystem::RefreshReference(const std::string& path,
|
||||||
|
OpenMode perms,
|
||||||
|
FileReference& reference) {
|
||||||
|
std::unique_lock lk{list_lock};
|
||||||
|
|
||||||
|
// Temporarily remove from list.
|
||||||
|
this->RemoveReferenceFromListLocked(reference);
|
||||||
|
|
||||||
|
// Restore file if needed.
|
||||||
|
if (!reference.file) {
|
||||||
|
this->EvictSingleReferenceLocked();
|
||||||
|
|
||||||
|
reference.file =
|
||||||
|
FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile);
|
||||||
|
if (reference.file) {
|
||||||
|
num_open_files++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinsert into list.
|
||||||
|
this->InsertReferenceIntoListLocked(reference);
|
||||||
|
|
||||||
|
return lk;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RealVfsFilesystem::DropReference(std::unique_ptr<FileReference>&& reference) {
|
||||||
|
std::scoped_lock lk{list_lock};
|
||||||
|
|
||||||
|
// Remove from list.
|
||||||
|
this->RemoveReferenceFromListLocked(*reference);
|
||||||
|
|
||||||
|
// Close the file.
|
||||||
|
if (reference->file) {
|
||||||
|
reference->file.reset();
|
||||||
|
num_open_files--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RealVfsFilesystem::EvictSingleReferenceLocked() {
|
||||||
|
if (num_open_files < MaxOpenFiles || open_references.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get and remove from list.
|
||||||
|
auto& reference = open_references.back();
|
||||||
|
this->RemoveReferenceFromListLocked(reference);
|
||||||
|
|
||||||
|
// Close the file.
|
||||||
|
if (reference.file) {
|
||||||
|
reference.file.reset();
|
||||||
|
num_open_files--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinsert into closed list.
|
||||||
|
this->InsertReferenceIntoListLocked(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RealVfsFilesystem::InsertReferenceIntoListLocked(FileReference& reference) {
|
||||||
|
if (reference.file) {
|
||||||
|
open_references.push_front(reference);
|
||||||
|
} else {
|
||||||
|
closed_references.push_front(reference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RealVfsFilesystem::RemoveReferenceFromListLocked(FileReference& reference) {
|
||||||
|
if (reference.file) {
|
||||||
|
open_references.erase(open_references.iterator_to(reference));
|
||||||
|
} else {
|
||||||
|
closed_references.erase(closed_references.iterator_to(reference));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::unique_ptr<FileReference> reference_,
|
||||||
|
const std::string& path_, OpenMode perms_, std::optional<u64> size_)
|
||||||
|
: base(base_), reference(std::move(reference_)), path(path_),
|
||||||
|
parent_path(FS::GetParentPath(path_)), path_components(FS::SplitPathComponentsCopy(path_)),
|
||||||
|
size(size_), perms(perms_) {}
|
||||||
|
|
||||||
|
RealVfsFile::~RealVfsFile() {
|
||||||
|
base.DropReference(std::move(reference));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RealVfsFile::GetName() const {
|
||||||
|
return path_components.empty() ? "" : std::string(path_components.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t RealVfsFile::GetSize() const {
|
||||||
|
if (size) {
|
||||||
|
return *size;
|
||||||
|
}
|
||||||
|
auto lk = base.RefreshReference(path, perms, *reference);
|
||||||
|
return reference->file ? reference->file->GetSize() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsFile::Resize(std::size_t new_size) {
|
||||||
|
size.reset();
|
||||||
|
auto lk = base.RefreshReference(path, perms, *reference);
|
||||||
|
return reference->file ? reference->file->SetSize(new_size) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir RealVfsFile::GetContainingDirectory() const {
|
||||||
|
return base.OpenDirectory(parent_path, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsFile::IsWritable() const {
|
||||||
|
return True(perms & OpenMode::Write);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsFile::IsReadable() const {
|
||||||
|
return True(perms & OpenMode::Read);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
|
||||||
|
auto lk = base.RefreshReference(path, perms, *reference);
|
||||||
|
if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return reference->file->ReadSpan(std::span{data, length});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
|
||||||
|
size.reset();
|
||||||
|
auto lk = base.RefreshReference(path, perms, *reference);
|
||||||
|
if (!reference->file || !reference->file->Seek(static_cast<s64>(offset))) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return reference->file->WriteSpan(std::span{data, length});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsFile::Rename(std::string_view name) {
|
||||||
|
return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if
|
||||||
|
// constexpr' because there is a compile error in the branch not used.
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>() const {
|
||||||
|
if (perms == OpenMode::AllowAppend) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualFile> out;
|
||||||
|
|
||||||
|
const FS::DirEntryCallable callback = [this,
|
||||||
|
&out](const std::filesystem::directory_entry& entry) {
|
||||||
|
const auto full_path_string = FS::PathToUTF8String(entry.path());
|
||||||
|
|
||||||
|
out.emplace_back(base.OpenFileFromEntry(full_path_string, entry.file_size(), perms));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
FS::IterateDirEntries(path, callback, FS::DirEntryFilter::File);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDirectory>() const {
|
||||||
|
if (perms == OpenMode::AllowAppend) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualDir> out;
|
||||||
|
|
||||||
|
const FS::DirEntryCallable callback = [this,
|
||||||
|
&out](const std::filesystem::directory_entry& entry) {
|
||||||
|
const auto full_path_string = FS::PathToUTF8String(entry.path());
|
||||||
|
|
||||||
|
out.emplace_back(base.OpenDirectory(full_path_string, perms));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
FS::IterateDirEntries(path, callback, FS::DirEntryFilter::Directory);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_,
|
||||||
|
OpenMode perms_)
|
||||||
|
: base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)),
|
||||||
|
path_components(FS::SplitPathComponentsCopy(path)), perms(perms_) {
|
||||||
|
if (!FS::Exists(path) && True(perms & OpenMode::Write)) {
|
||||||
|
void(FS::CreateDirs(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RealVfsDirectory::~RealVfsDirectory() = default;
|
||||||
|
|
||||||
|
VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const {
|
||||||
|
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
|
||||||
|
if (!FS::Exists(full_path) || FS::IsDir(full_path)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return base.OpenFile(full_path, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const {
|
||||||
|
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
|
||||||
|
if (!FS::Exists(full_path) || !FS::IsDir(full_path)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return base.OpenDirectory(full_path, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile RealVfsDirectory::GetFile(std::string_view name) const {
|
||||||
|
return GetFileRelative(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const {
|
||||||
|
return GetDirectoryRelative(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) {
|
||||||
|
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
|
||||||
|
if (!FS::CreateParentDirs(full_path)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return base.CreateFile(full_path, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_path) {
|
||||||
|
const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path));
|
||||||
|
return base.CreateDirectory(full_path, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) {
|
||||||
|
const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name));
|
||||||
|
return base.DeleteDirectory(full_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualFile> RealVfsDirectory::GetFiles() const {
|
||||||
|
return IterateEntries<RealVfsFile, VfsFile>();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileTimeStampRaw RealVfsDirectory::GetFileTimeStamp(std::string_view path_) const {
|
||||||
|
const auto full_path = FS::SanitizePath(path + '/' + std::string(path_));
|
||||||
|
const auto fs_path = std::filesystem::path{FS::ToU8String(full_path)};
|
||||||
|
struct stat file_status;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
const auto stat_result = _wstat64(fs_path.c_str(), &file_status);
|
||||||
|
#else
|
||||||
|
const auto stat_result = stat(fs_path.c_str(), &file_status);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (stat_result != 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
.created{static_cast<u64>(file_status.st_ctime)},
|
||||||
|
.accessed{static_cast<u64>(file_status.st_atime)},
|
||||||
|
.modified{static_cast<u64>(file_status.st_mtime)},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const {
|
||||||
|
return IterateEntries<RealVfsDirectory, VfsDirectory>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsDirectory::IsWritable() const {
|
||||||
|
return True(perms & OpenMode::Write);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsDirectory::IsReadable() const {
|
||||||
|
return True(perms & OpenMode::Read);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RealVfsDirectory::GetName() const {
|
||||||
|
return path_components.empty() ? "" : std::string(path_components.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir RealVfsDirectory::GetParentDirectory() const {
|
||||||
|
if (path_components.size() <= 1) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.OpenDirectory(parent_path, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir RealVfsDirectory::CreateSubdirectory(std::string_view name) {
|
||||||
|
const std::string subdir_path = (path + '/').append(name);
|
||||||
|
return base.CreateDirectory(subdir_path, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualFile RealVfsDirectory::CreateFile(std::string_view name) {
|
||||||
|
const std::string file_path = (path + '/').append(name);
|
||||||
|
return base.CreateFile(file_path, perms);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) {
|
||||||
|
const std::string subdir_path = (path + '/').append(name);
|
||||||
|
return base.DeleteDirectory(subdir_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsDirectory::DeleteFile(std::string_view name) {
|
||||||
|
const std::string file_path = (path + '/').append(name);
|
||||||
|
return base.DeleteFile(file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RealVfsDirectory::Rename(std::string_view name) {
|
||||||
|
const std::string new_name = (parent_path + '/').append(name);
|
||||||
|
return base.MoveFile(path, new_name) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string RealVfsDirectory::GetFullPath() const {
|
||||||
|
auto out = path;
|
||||||
|
std::replace(out.begin(), out.end(), '\\', '/');
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
|
||||||
|
if (perms == OpenMode::AllowAppend) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<std::string, VfsEntryType, std::less<>> out;
|
||||||
|
|
||||||
|
const FS::DirEntryCallable callback = [&out](const std::filesystem::directory_entry& entry) {
|
||||||
|
const auto filename = FS::PathToUTF8String(entry.path().filename());
|
||||||
|
out.insert_or_assign(filename,
|
||||||
|
entry.is_directory() ? VfsEntryType::Directory : VfsEntryType::File);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
FS::IterateDirEntries(path, callback);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace FileSys
|
148
src/core/file_sys/vfs/vfs_real.h
Executable file
148
src/core/file_sys/vfs/vfs_real.h
Executable file
|
@ -0,0 +1,148 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
|
#include <string_view>
|
||||||
|
#include "common/intrusive_list.h"
|
||||||
|
#include "core/file_sys/fs_filesystem.h"
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
|
namespace Common::FS {
|
||||||
|
class IOFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
struct FileReference : public Common::IntrusiveListBaseNode<FileReference> {
|
||||||
|
std::shared_ptr<Common::FS::IOFile> file{};
|
||||||
|
};
|
||||||
|
|
||||||
|
class RealVfsFile;
|
||||||
|
class RealVfsDirectory;
|
||||||
|
|
||||||
|
class RealVfsFilesystem : public VfsFilesystem {
|
||||||
|
public:
|
||||||
|
RealVfsFilesystem();
|
||||||
|
~RealVfsFilesystem() override;
|
||||||
|
|
||||||
|
std::string GetName() const override;
|
||||||
|
bool IsReadable() const override;
|
||||||
|
bool IsWritable() const override;
|
||||||
|
VfsEntryType GetEntryType(std::string_view path) const override;
|
||||||
|
VirtualFile OpenFile(std::string_view path, OpenMode perms = OpenMode::Read) override;
|
||||||
|
VirtualFile CreateFile(std::string_view path, OpenMode perms = OpenMode::ReadWrite) override;
|
||||||
|
VirtualFile CopyFile(std::string_view old_path, std::string_view new_path) override;
|
||||||
|
VirtualFile MoveFile(std::string_view old_path, std::string_view new_path) override;
|
||||||
|
bool DeleteFile(std::string_view path) override;
|
||||||
|
VirtualDir OpenDirectory(std::string_view path, OpenMode perms = OpenMode::Read) override;
|
||||||
|
VirtualDir CreateDirectory(std::string_view path,
|
||||||
|
OpenMode perms = OpenMode::ReadWrite) override;
|
||||||
|
VirtualDir CopyDirectory(std::string_view old_path, std::string_view new_path) override;
|
||||||
|
VirtualDir MoveDirectory(std::string_view old_path, std::string_view new_path) override;
|
||||||
|
bool DeleteDirectory(std::string_view path) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
using ReferenceListType = Common::IntrusiveListBaseTraits<FileReference>::ListType;
|
||||||
|
std::map<std::string, std::weak_ptr<VfsFile>, std::less<>> cache;
|
||||||
|
ReferenceListType open_references;
|
||||||
|
ReferenceListType closed_references;
|
||||||
|
std::mutex list_lock;
|
||||||
|
size_t num_open_files{};
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class RealVfsFile;
|
||||||
|
std::unique_lock<std::mutex> RefreshReference(const std::string& path, OpenMode perms,
|
||||||
|
FileReference& reference);
|
||||||
|
void DropReference(std::unique_ptr<FileReference>&& reference);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class RealVfsDirectory;
|
||||||
|
VirtualFile OpenFileFromEntry(std::string_view path, std::optional<u64> size,
|
||||||
|
OpenMode perms = OpenMode::Read);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void EvictSingleReferenceLocked();
|
||||||
|
void InsertReferenceIntoListLocked(FileReference& reference);
|
||||||
|
void RemoveReferenceFromListLocked(FileReference& reference);
|
||||||
|
};
|
||||||
|
|
||||||
|
// An implementation of VfsFile that represents a file on the user's computer.
|
||||||
|
class RealVfsFile : public VfsFile {
|
||||||
|
friend class RealVfsDirectory;
|
||||||
|
friend class RealVfsFilesystem;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~RealVfsFile() override;
|
||||||
|
|
||||||
|
std::string GetName() const override;
|
||||||
|
std::size_t GetSize() const override;
|
||||||
|
bool Resize(std::size_t new_size) override;
|
||||||
|
VirtualDir GetContainingDirectory() const override;
|
||||||
|
bool IsWritable() const override;
|
||||||
|
bool IsReadable() const override;
|
||||||
|
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
|
||||||
|
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
|
||||||
|
bool Rename(std::string_view name) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
RealVfsFile(RealVfsFilesystem& base, std::unique_ptr<FileReference> reference,
|
||||||
|
const std::string& path, OpenMode perms = OpenMode::Read,
|
||||||
|
std::optional<u64> size = {});
|
||||||
|
|
||||||
|
RealVfsFilesystem& base;
|
||||||
|
std::unique_ptr<FileReference> reference;
|
||||||
|
std::string path;
|
||||||
|
std::string parent_path;
|
||||||
|
std::vector<std::string> path_components;
|
||||||
|
std::optional<u64> size;
|
||||||
|
OpenMode perms;
|
||||||
|
};
|
||||||
|
|
||||||
|
// An implementation of VfsDirectory that represents a directory on the user's computer.
|
||||||
|
class RealVfsDirectory : public VfsDirectory {
|
||||||
|
friend class RealVfsFilesystem;
|
||||||
|
|
||||||
|
public:
|
||||||
|
~RealVfsDirectory() override;
|
||||||
|
|
||||||
|
VirtualFile GetFileRelative(std::string_view relative_path) const override;
|
||||||
|
VirtualDir GetDirectoryRelative(std::string_view relative_path) const override;
|
||||||
|
VirtualFile GetFile(std::string_view name) const override;
|
||||||
|
VirtualDir GetSubdirectory(std::string_view name) const override;
|
||||||
|
VirtualFile CreateFileRelative(std::string_view relative_path) override;
|
||||||
|
VirtualDir CreateDirectoryRelative(std::string_view relative_path) override;
|
||||||
|
bool DeleteSubdirectoryRecursive(std::string_view name) override;
|
||||||
|
std::vector<VirtualFile> GetFiles() const override;
|
||||||
|
FileTimeStampRaw GetFileTimeStamp(std::string_view path) const override;
|
||||||
|
std::vector<VirtualDir> GetSubdirectories() const override;
|
||||||
|
bool IsWritable() const override;
|
||||||
|
bool IsReadable() const override;
|
||||||
|
std::string GetName() const override;
|
||||||
|
VirtualDir GetParentDirectory() const override;
|
||||||
|
VirtualDir CreateSubdirectory(std::string_view name) override;
|
||||||
|
VirtualFile CreateFile(std::string_view name) override;
|
||||||
|
bool DeleteSubdirectory(std::string_view name) override;
|
||||||
|
bool DeleteFile(std::string_view name) override;
|
||||||
|
bool Rename(std::string_view name) override;
|
||||||
|
std::string GetFullPath() const override;
|
||||||
|
std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
RealVfsDirectory(RealVfsFilesystem& base, const std::string& path,
|
||||||
|
OpenMode perms = OpenMode::Read);
|
||||||
|
|
||||||
|
template <typename T, typename R>
|
||||||
|
std::vector<std::shared_ptr<R>> IterateEntries() const;
|
||||||
|
|
||||||
|
RealVfsFilesystem& base;
|
||||||
|
std::string path;
|
||||||
|
std::string parent_path;
|
||||||
|
std::vector<std::string> path_components;
|
||||||
|
OpenMode perms;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
80
src/core/file_sys/vfs/vfs_static.h
Executable file
80
src/core/file_sys/vfs/vfs_static.h
Executable file
|
@ -0,0 +1,80 @@
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "core/file_sys/vfs/vfs.h"
|
||||||
|
|
||||||
|
namespace FileSys {
|
||||||
|
|
||||||
|
class StaticVfsFile : public VfsFile {
|
||||||
|
public:
|
||||||
|
explicit StaticVfsFile(u8 value_, std::size_t size_ = 0, std::string name_ = "",
|
||||||
|
VirtualDir parent_ = nullptr)
|
||||||
|
: value{value_}, size{size_}, name{std::move(name_)}, parent{std::move(parent_)} {}
|
||||||
|
|
||||||
|
std::string GetName() const override {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t GetSize() const override {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Resize(std::size_t new_size) override {
|
||||||
|
size = new_size;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
VirtualDir GetContainingDirectory() const override {
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsWritable() const override {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsReadable() const override {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override {
|
||||||
|
const auto read = std::min(length, size - offset);
|
||||||
|
std::fill(data, data + read, value);
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<u8> ReadByte(std::size_t offset) const override {
|
||||||
|
if (offset >= size) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
|
||||||
|
const auto read = std::min(length, size - offset);
|
||||||
|
return std::vector<u8>(read, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Rename(std::string_view new_name) override {
|
||||||
|
name = new_name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
u8 value;
|
||||||
|
std::size_t size;
|
||||||
|
std::string name;
|
||||||
|
VirtualDir parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace FileSys
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue