Source code for nx.filesystem

import pathlib
import _nx

from . import users


SAVEDATA_BASE_PATH = 'save:/'
ROMFS_BASE_PATH = 'romfs:/'


mounted_romfs = None
mounted_savedata = None


[docs]class FileSystem: """Represents a filesystem. Attributes ---------- base_path: pathlib.Path The base path of the filesystem.""" def __init__(self, base_path: str): self.base_path = pathlib.Path(base_path)
[docs] def open(self, file_path: str, mode='r', buffering=-1, encoding=None, errors=None, newline=None): """Opens a file given a file path and returns a file-like object. Apart from the ``file_path: str`` parameter, this method works the same way as ``pathlib.Path.open``, thus it works pretty much the same way as the ``open`` function. """ return self.base_path.joinpath(file_path).open(mode=mode, buffering=buffering, encoding=encoding, errors=errors, newline=newline)
[docs]class MountableFileSystem(FileSystem): """Represents a filesystem that is able to be mounted. Attributes ---------- base_path: pathlib.Path The base path of the filesystem. """ def __init__(self, base_path): super().__init__(base_path) @property def is_mounted(self): """Whether or not the filesystem is currently mounted.""" raise NotImplementedError
[docs] def open(self, file_path: str, mode='r', buffering=-1, encoding=None, errors=None, newline=None): """Opens a file given a file path and returns a file-like object. Apart from the ``file_path: str`` parameter, this method works the same way as ``pathlib.Path.open``, thus it works pretty much the same way as the ``open`` function. """ if not self.is_mounted: self.mount() return super().open(file_path, mode=mode, buffering=buffering, encoding=encoding, errors=errors, newline=newline)
[docs] def mount(self): """Mounts the filesystem.""" raise NotImplementedError
[docs] def commit(self): """Commits the filesystem.""" raise NotImplementedError
[docs] def unmount(self): """Unmounts the filesystem.""" raise NotImplementedError
def __enter__(self): self.mount() return self def __exit__(self, exc_type, exc_val, exc_tb): self.commit() self.unmount()
[docs]class RomFS(MountableFileSystem): """Represents the data filesystem of a title. Do not instantiate this. Rather, get a RomFS object via ``nx.titles[MY_TITLE_ID].romfs``. Attributes ---------- title: :class:`Title` The title this RomFS belongs to. base_path: pathlib.Path The base path of the RomFS. """ def __init__(self, title): super().__init__(ROMFS_BASE_PATH) self.title = title @property def is_mounted(self): """Whether the RomFS is mounted.""" return self is mounted_romfs
[docs] def mount(self): """Yet to be implemented. Mounts the RomFS.""" raise NotImplementedError # TODO: implement RomFS.mount
[docs] def unmount(self): """Unmounts the mounted RomFS.""" _nx.fsdev_unmount_device('romfs')
def __exit__(self, exc_type, exc_val, exc_tb): self.unmount()
[docs]class Savedata(MountableFileSystem): """Represents the savedata filesystem of a title. Do not instantiate this. Rather, get a Savedata object via ``nx.titles[MY_TITLE_ID].savedata``. Attributes ---------- title: :class:`Title` The title this Savedata belongs to. base_path: pathlib.Path The base path of the savedata filesystem. """ def __init__(self, title, user=None): super().__init__(SAVEDATA_BASE_PATH) self.title = title self.user = user if user is not None else users.active_user @property def is_mounted(self): """Whether the savedata filesystem has been mounted.""" return self is mounted_savedata
[docs] def mount(self): """Mounts the savedata filesystem.""" if self.is_mounted: return if self.user is None: raise RuntimeError("No active user, you need to launch and " "close a game prior to launching HBL.") _nx.fs_mount_savedata('save', self.title.id, self.user.id) global mounted_savedata # TODO: consider not using globals mounted_savedata = self
[docs] def commit(self): """Commits the savedata filesystem.""" _nx.fsdev_commit_device('save')
[docs] def unmount(self): """Unmounts the savedata filesystem.""" _nx.fsdev_unmount_device('save')