diff options
Diffstat (limited to 'src/core')
45 files changed, 1773 insertions, 488 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4a9c6fd2f..299f1f261 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -17,13 +17,16 @@ set(SRCS core_timing.cpp file_sys/archive_backend.cpp file_sys/archive_extsavedata.cpp + file_sys/archive_ncch.cpp file_sys/archive_romfs.cpp file_sys/archive_savedata.cpp - file_sys/archive_savedatacheck.cpp file_sys/archive_sdmc.cpp + file_sys/archive_sdmcwriteonly.cpp file_sys/archive_systemsavedata.cpp file_sys/disk_archive.cpp file_sys/ivfc_archive.cpp + file_sys/path_parser.cpp + file_sys/savedata_archive.cpp gdbstub/gdbstub.cpp hle/config_mem.cpp hle/hle.cpp @@ -159,15 +162,18 @@ set(HEADERS core_timing.h file_sys/archive_backend.h file_sys/archive_extsavedata.h + file_sys/archive_ncch.h file_sys/archive_romfs.h file_sys/archive_savedata.h - file_sys/archive_savedatacheck.h file_sys/archive_sdmc.h + file_sys/archive_sdmcwriteonly.h file_sys/archive_systemsavedata.h file_sys/directory_backend.h file_sys/disk_archive.h file_sys/file_backend.h file_sys/ivfc_archive.h + file_sys/path_parser.h + file_sys/savedata_archive.h gdbstub/gdbstub.h hle/config_mem.h hle/function_wrappers.h diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index b4444c869..ca8a94ee9 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -52,6 +52,7 @@ static Dynarmic::UserCallbacks GetUserCallbacks(ARMul_State* interpeter_state) { user_callbacks.MemoryWrite16 = &Memory::Write16; user_callbacks.MemoryWrite32 = &Memory::Write32; user_callbacks.MemoryWrite64 = &Memory::Write64; + user_callbacks.page_table = Memory::GetCurrentPageTablePointers(); return user_callbacks; } @@ -130,9 +131,9 @@ MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { MICROPROFILE_SCOPE(ARM_Jit); - jit->Run(static_cast<unsigned>(num_instructions)); + unsigned ticks_executed = jit->Run(static_cast<unsigned>(num_instructions)); - AddTicks(num_instructions); + AddTicks(ticks_executed); } void ARM_Dynarmic::SaveContext(Core::ThreadContext& ctx) { diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h index 06b8f2ed7..58f6c150c 100644 --- a/src/core/file_sys/archive_backend.h +++ b/src/core/file_sys/archive_backend.h @@ -87,7 +87,7 @@ public: * @return Opened file, or error code */ virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, - const Mode mode) const = 0; + const Mode& mode) const = 0; /** * Delete a file specified by its path @@ -100,53 +100,53 @@ public: * Rename a File specified by its path * @param src_path Source path relative to the archive * @param dest_path Destination path relative to the archive - * @return Whether rename succeeded + * @return Result of the operation */ - virtual bool RenameFile(const Path& src_path, const Path& dest_path) const = 0; + virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0; /** * Delete a directory specified by its path * @param path Path relative to the archive - * @return Whether the directory could be deleted + * @return Result of the operation */ - virtual bool DeleteDirectory(const Path& path) const = 0; + virtual ResultCode DeleteDirectory(const Path& path) const = 0; /** * Delete a directory specified by its path and anything under it * @param path Path relative to the archive - * @return Whether the directory could be deleted + * @return Result of the operation */ - virtual bool DeleteDirectoryRecursively(const Path& path) const = 0; + virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0; /** * Create a file specified by its path * @param path Path relative to the Archive * @param size The size of the new file, filled with zeroes - * @return File creation result code + * @return Result of the operation */ virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; /** * Create a directory specified by its path * @param path Path relative to the archive - * @return Whether the directory could be created + * @return Result of the operation */ - virtual bool CreateDirectory(const Path& path) const = 0; + virtual ResultCode CreateDirectory(const Path& path) const = 0; /** * Rename a Directory specified by its path * @param src_path Source path relative to the archive * @param dest_path Destination path relative to the archive - * @return Whether rename succeeded + * @return Result of the operation */ - virtual bool RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; + virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; /** * Open a directory specified by its path * @param path Path relative to the archive - * @return Opened directory, or nullptr + * @return Opened directory, or error code */ - virtual std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const = 0; + virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const = 0; /** * Get the free space diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index e1d29efd3..e1c4931ec 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -11,6 +11,9 @@ #include "common/string_util.h" #include "core/file_sys/archive_extsavedata.h" #include "core/file_sys/disk_archive.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/path_parser.h" +#include "core/file_sys/savedata_archive.h" #include "core/hle/service/fs/archive.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -18,6 +21,116 @@ namespace FileSys { +/** + * A modified version of DiskFile for fixed-size file used by ExtSaveData + * The file size can't be changed by SetSize or Write. + */ +class FixSizeDiskFile : public DiskFile { +public: + FixSizeDiskFile(FileUtil::IOFile&& file, const Mode& mode) : DiskFile(std::move(file), mode) { + size = GetSize(); + } + + bool SetSize(u64 size) const override { + return false; + } + + ResultVal<size_t> Write(u64 offset, size_t length, bool flush, + const u8* buffer) const override { + if (offset > size) { + return ResultCode(ErrorDescription::FS_WriteBeyondEnd, ErrorModule::FS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); + } else if (offset == size) { + return MakeResult<size_t>(0); + } + + if (offset + length > size) { + length = size - offset; + } + + return DiskFile::Write(offset, length, flush, buffer); + } + +private: + u64 size{}; +}; + +/** + * Archive backend for general extsave data archive type. + * The behaviour of ExtSaveDataArchive is almost the same as SaveDataArchive, except for + * - file size can't be changed once created (thus creating zero-size file and openning with create + * flag are prohibited); + * - always open a file with read+write permission. + */ +class ExtSaveDataArchive : public SaveDataArchive { +public: + ExtSaveDataArchive(const std::string& mount_point) : SaveDataArchive(mount_point) {} + + std::string GetName() const override { + return "ExtSaveDataArchive: " + mount_point; + } + + ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, + const Mode& mode) const override { + LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); + + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + if (mode.hex == 0) { + LOG_ERROR(Service_FS, "Empty open mode"); + return ERROR_UNSUPPORTED_OPEN_FLAGS; + } + + if (mode.create_flag) { + LOG_ERROR(Service_FS, "Create flag is not supported"); + return ERROR_UNSUPPORTED_OPEN_FLAGS; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_FILE_NOT_FOUND; + case PathParser::PathNotFound: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_PATH_NOT_FOUND; + case PathParser::FileInPath: + case PathParser::DirectoryFound: + LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; + case PathParser::NotFound: + LOG_ERROR(Service_FS, "%s not found", full_path.c_str()); + return ERROR_FILE_NOT_FOUND; + } + + FileUtil::IOFile file(full_path, "r+b"); + if (!file.IsOpen()) { + LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str()); + return ERROR_FILE_NOT_FOUND; + } + + Mode rwmode; + rwmode.write_flag.Assign(1); + rwmode.read_flag.Assign(1); + auto disk_file = std::make_unique<FixSizeDiskFile>(std::move(file), rwmode); + return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file)); + } + + ResultCode CreateFile(const Path& path, u64 size) const override { + if (size == 0) { + LOG_ERROR(Service_FS, "Zero-size file is not supported"); + return ERROR_UNSUPPORTED_OPEN_FLAGS; + } + return SaveDataArchive::CreateFile(path, size); + } +}; + std::string GetExtSaveDataPath(const std::string& mount_point, const Path& path) { std::vector<u8> vec_data = path.AsBinary(); const u32* data = reinterpret_cast<const u32*>(vec_data.data()); @@ -84,7 +197,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons ErrorSummary::InvalidState, ErrorLevel::Status); } } - auto archive = std::make_unique<DiskArchive>(fullpath); + auto archive = std::make_unique<ExtSaveDataArchive>(fullpath); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_savedatacheck.cpp b/src/core/file_sys/archive_ncch.cpp index 6c4542b7d..6f1aadfc3 100644 --- a/src/core/file_sys/archive_savedatacheck.cpp +++ b/src/core/file_sys/archive_ncch.cpp @@ -9,7 +9,7 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/string_util.h" -#include "core/file_sys/archive_savedatacheck.h" +#include "core/file_sys/archive_ncch.h" #include "core/file_sys/ivfc_archive.h" #include "core/hle/service/fs/archive.h" @@ -18,22 +18,22 @@ namespace FileSys { -static std::string GetSaveDataCheckContainerPath(const std::string& nand_directory) { +static std::string GetNCCHContainerPath(const std::string& nand_directory) { return Common::StringFromFormat("%s%s/title/", nand_directory.c_str(), SYSTEM_ID.c_str()); } -static std::string GetSaveDataCheckPath(const std::string& mount_point, u32 high, u32 low) { +static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) { return Common::StringFromFormat("%s%08x/%08x/content/00000000.app.romfs", mount_point.c_str(), high, low); } -ArchiveFactory_SaveDataCheck::ArchiveFactory_SaveDataCheck(const std::string& nand_directory) - : mount_point(GetSaveDataCheckContainerPath(nand_directory)) {} +ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory) + : mount_point(GetNCCHContainerPath(nand_directory)) {} -ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(const Path& path) { +ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& path) { auto vec = path.AsBinary(); const u32* data = reinterpret_cast<u32*>(vec.data()); - std::string file_path = GetSaveDataCheckPath(mount_point, data[1], data[0]); + std::string file_path = GetNCCHPath(mount_point, data[1], data[0]); auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb"); if (!file->IsOpen()) { @@ -45,15 +45,15 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveDataCheck::Open(co return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } -ResultCode ArchiveFactory_SaveDataCheck::Format(const Path& path, - const FileSys::ArchiveFormatInfo& format_info) { - LOG_ERROR(Service_FS, "Attempted to format a SaveDataCheck archive."); +ResultCode ArchiveFactory_NCCH::Format(const Path& path, + const FileSys::ArchiveFormatInfo& format_info) { + LOG_ERROR(Service_FS, "Attempted to format a NCCH archive."); // TODO: Verify error code return ResultCode(ErrorDescription::NotAuthorized, ErrorModule::FS, ErrorSummary::NotSupported, ErrorLevel::Permanent); } -ResultVal<ArchiveFormatInfo> ArchiveFactory_SaveDataCheck::GetFormatInfo(const Path& path) const { +ResultVal<ArchiveFormatInfo> ArchiveFactory_NCCH::GetFormatInfo(const Path& path) const { // TODO(Subv): Implement LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str()); return ResultCode(-1); diff --git a/src/core/file_sys/archive_savedatacheck.h b/src/core/file_sys/archive_ncch.h index e9cafbed9..66b8ce75d 100644 --- a/src/core/file_sys/archive_savedatacheck.h +++ b/src/core/file_sys/archive_ncch.h @@ -14,13 +14,13 @@ namespace FileSys { -/// File system interface to the SaveDataCheck archive -class ArchiveFactory_SaveDataCheck final : public ArchiveFactory { +/// File system interface to the NCCH archive +class ArchiveFactory_NCCH final : public ArchiveFactory { public: - ArchiveFactory_SaveDataCheck(const std::string& mount_point); + ArchiveFactory_NCCH(const std::string& mount_point); std::string GetName() const override { - return "SaveDataCheck"; + return "NCCH"; } ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; diff --git a/src/core/file_sys/archive_savedata.cpp b/src/core/file_sys/archive_savedata.cpp index 6711035ec..ecb44a215 100644 --- a/src/core/file_sys/archive_savedata.cpp +++ b/src/core/file_sys/archive_savedata.cpp @@ -9,7 +9,7 @@ #include "common/logging/log.h" #include "common/string_util.h" #include "core/file_sys/archive_savedata.h" -#include "core/file_sys/disk_archive.h" +#include "core/file_sys/savedata_archive.h" #include "core/hle/kernel/process.h" #include "core/hle/service/fs/archive.h" @@ -54,7 +54,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SaveData::Open(const P ErrorSummary::InvalidState, ErrorLevel::Status); } - auto archive = std::make_unique<DiskArchive>(std::move(concrete_mount_point)); + auto archive = std::make_unique<SaveDataArchive>(std::move(concrete_mount_point)); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index bcb03ed36..333dfb92e 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -8,6 +8,8 @@ #include "common/logging/log.h" #include "core/file_sys/archive_sdmc.h" #include "core/file_sys/disk_archive.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/path_parser.h" #include "core/settings.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -15,6 +17,281 @@ namespace FileSys { +ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path, + const Mode& mode) const { + Mode modified_mode; + modified_mode.hex = mode.hex; + + // SDMC archive always opens a file with at least read permission + modified_mode.read_flag.Assign(1); + + return OpenFileBase(path, modified_mode); +} + +ResultVal<std::unique_ptr<FileBackend>> SDMCArchive::OpenFileBase(const Path& path, + const Mode& mode) const { + LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); + + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + if (mode.hex == 0) { + LOG_ERROR(Service_FS, "Empty open mode"); + return ERROR_INVALID_OPEN_FLAGS; + } + + if (mode.create_flag && !mode.write_flag) { + LOG_ERROR(Service_FS, "Create flag set but write flag not set"); + return ERROR_INVALID_OPEN_FLAGS; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_NOT_FOUND; + case PathParser::PathNotFound: + case PathParser::FileInPath: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_NOT_FOUND; + case PathParser::DirectoryFound: + LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC; + case PathParser::NotFound: + if (!mode.create_flag) { + LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", + full_path.c_str()); + return ERROR_NOT_FOUND; + } else { + // Create the file + FileUtil::CreateEmptyFile(full_path); + } + break; + } + + FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb"); + if (!file.IsOpen()) { + LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str()); + return ERROR_NOT_FOUND; + } + + auto disk_file = std::make_unique<DiskFile>(std::move(file), mode); + return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file)); +} + +ResultCode SDMCArchive::DeleteFile(const Path& path) const { + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_NOT_FOUND; + case PathParser::PathNotFound: + case PathParser::FileInPath: + case PathParser::NotFound: + LOG_ERROR(Service_FS, "%s not found", full_path.c_str()); + return ERROR_NOT_FOUND; + case PathParser::DirectoryFound: + LOG_ERROR(Service_FS, "%s is not a file", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC; + } + + if (FileUtil::Delete(full_path)) { + return RESULT_SUCCESS; + } + + LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str()); + return ERROR_NOT_FOUND; +} + +ResultCode SDMCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { + if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) { + return RESULT_SUCCESS; + } + + // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't + // exist or similar. Verify. + return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description + ErrorSummary::NothingHappened, ErrorLevel::Status); +} + +template <typename T> +static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point, + T deleter) { + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + if (path_parser.IsRootDirectory()) + return ERROR_NOT_FOUND; + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_NOT_FOUND; + case PathParser::PathNotFound: + case PathParser::NotFound: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_NOT_FOUND; + case PathParser::FileInPath: + case PathParser::FileFound: + LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC; + } + + if (deleter(full_path)) { + return RESULT_SUCCESS; + } + + LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC; +} + +ResultCode SDMCArchive::DeleteDirectory(const Path& path) const { + return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir); +} + +ResultCode SDMCArchive::DeleteDirectoryRecursively(const Path& path) const { + return DeleteDirectoryHelper( + path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); }); +} + +ResultCode SDMCArchive::CreateFile(const FileSys::Path& path, u64 size) const { + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_NOT_FOUND; + case PathParser::PathNotFound: + case PathParser::FileInPath: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_NOT_FOUND; + case PathParser::DirectoryFound: + LOG_ERROR(Service_FS, "%s already exists", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC; + case PathParser::FileFound: + LOG_ERROR(Service_FS, "%s already exists", full_path.c_str()); + return ERROR_ALREADY_EXISTS; + } + + if (size == 0) { + FileUtil::CreateEmptyFile(full_path); + return RESULT_SUCCESS; + } + + FileUtil::IOFile file(full_path, "wb"); + // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) + // We do this by seeking to the right size, then writing a single null byte. + if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) { + return RESULT_SUCCESS; + } + + LOG_ERROR(Service_FS, "Too large file"); + return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource, + ErrorLevel::Info); +} + +ResultCode SDMCArchive::CreateDirectory(const Path& path) const { + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_NOT_FOUND; + case PathParser::PathNotFound: + case PathParser::FileInPath: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_NOT_FOUND; + case PathParser::DirectoryFound: + case PathParser::FileFound: + LOG_ERROR(Service_FS, "%s already exists", full_path.c_str()); + return ERROR_ALREADY_EXISTS; + } + + if (FileUtil::CreateDir(mount_point + path.AsString())) { + return RESULT_SUCCESS; + } + + LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str()); + return ResultCode(ErrorDescription::NoData, ErrorModule::FS, ErrorSummary::Canceled, + ErrorLevel::Status); +} + +ResultCode SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { + if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) + return RESULT_SUCCESS; + + // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't + // exist or similar. Verify. + return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description + ErrorSummary::NothingHappened, ErrorLevel::Status); +} + +ResultVal<std::unique_ptr<DirectoryBackend>> SDMCArchive::OpenDirectory(const Path& path) const { + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_NOT_FOUND; + case PathParser::PathNotFound: + case PathParser::NotFound: + case PathParser::FileFound: + LOG_ERROR(Service_FS, "%s not found", full_path.c_str()); + return ERROR_NOT_FOUND; + case PathParser::FileInPath: + LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC; + } + + auto directory = std::make_unique<DiskDirectory>(full_path); + return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory)); +} + +u64 SDMCArchive::GetFreeBytes() const { + // TODO: Stubbed to return 1GiB + return 1024 * 1024 * 1024; +} + ArchiveFactory_SDMC::ArchiveFactory_SDMC(const std::string& sdmc_directory) : sdmc_directory(sdmc_directory) { LOG_INFO(Service_FS, "Directory %s set as SDMC.", sdmc_directory.c_str()); @@ -35,7 +312,7 @@ bool ArchiveFactory_SDMC::Initialize() { } ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path) { - auto archive = std::make_unique<DiskArchive>(sdmc_directory); + auto archive = std::make_unique<SDMCArchive>(sdmc_directory); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 88e855351..9d99b110c 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h @@ -14,6 +14,32 @@ namespace FileSys { +/// Archive backend for SDMC archive +class SDMCArchive : public ArchiveBackend { +public: + SDMCArchive(const std::string& mount_point_) : mount_point(mount_point_) {} + + std::string GetName() const override { + return "SDMCArchive: " + mount_point; + } + + ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, + const Mode& mode) const override; + ResultCode DeleteFile(const Path& path) const override; + ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; + ResultCode DeleteDirectory(const Path& path) const override; + ResultCode DeleteDirectoryRecursively(const Path& path) const override; + ResultCode CreateFile(const Path& path, u64 size) const override; + ResultCode CreateDirectory(const Path& path) const override; + ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; + ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; + u64 GetFreeBytes() const override; + +protected: + ResultVal<std::unique_ptr<FileBackend>> OpenFileBase(const Path& path, const Mode& mode) const; + std::string mount_point; +}; + /// File system interface to the SDMC archive class ArchiveFactory_SDMC final : public ArchiveFactory { public: diff --git a/src/core/file_sys/archive_sdmcwriteonly.cpp b/src/core/file_sys/archive_sdmcwriteonly.cpp new file mode 100644 index 000000000..2aafc9b1d --- /dev/null +++ b/src/core/file_sys/archive_sdmcwriteonly.cpp @@ -0,0 +1,70 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <memory> +#include "common/file_util.h" +#include "core/file_sys/archive_sdmcwriteonly.h" +#include "core/file_sys/directory_backend.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/file_backend.h" +#include "core/settings.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +ResultVal<std::unique_ptr<FileBackend>> SDMCWriteOnlyArchive::OpenFile(const Path& path, + const Mode& mode) const { + if (mode.read_flag) { + LOG_ERROR(Service_FS, "Read flag is not supported"); + return ERROR_INVALID_READ_FLAG; + } + return SDMCArchive::OpenFileBase(path, mode); +} + +ResultVal<std::unique_ptr<DirectoryBackend>> SDMCWriteOnlyArchive::OpenDirectory( + const Path& path) const { + LOG_ERROR(Service_FS, "Not supported"); + return ERROR_UNSUPPORTED_OPEN_FLAGS; +} + +ArchiveFactory_SDMCWriteOnly::ArchiveFactory_SDMCWriteOnly(const std::string& mount_point) + : sdmc_directory(mount_point) { + LOG_INFO(Service_FS, "Directory %s set as SDMCWriteOnly.", sdmc_directory.c_str()); +} + +bool ArchiveFactory_SDMCWriteOnly::Initialize() { + if (!Settings::values.use_virtual_sd) { + LOG_WARNING(Service_FS, "SDMC disabled by config."); + return false; + } + + if (!FileUtil::CreateFullPath(sdmc_directory)) { + LOG_ERROR(Service_FS, "Unable to create SDMC path."); + return false; + } + + return true; +} + +ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMCWriteOnly::Open(const Path& path) { + auto archive = std::make_unique<SDMCWriteOnlyArchive>(sdmc_directory); + return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); +} + +ResultCode ArchiveFactory_SDMCWriteOnly::Format(const Path& path, + const FileSys::ArchiveFormatInfo& format_info) { + // TODO(wwylele): hwtest this + LOG_ERROR(Service_FS, "Attempted to format a SDMC write-only archive."); + return ResultCode(-1); +} + +ResultVal<ArchiveFormatInfo> ArchiveFactory_SDMCWriteOnly::GetFormatInfo(const Path& path) const { + // TODO(Subv): Implement + LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str()); + return ResultCode(-1); +} + +} // namespace FileSys diff --git a/src/core/file_sys/archive_sdmcwriteonly.h b/src/core/file_sys/archive_sdmcwriteonly.h new file mode 100644 index 000000000..ed977485a --- /dev/null +++ b/src/core/file_sys/archive_sdmcwriteonly.h @@ -0,0 +1,57 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/file_sys/archive_sdmc.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +/** + * Archive backend for SDMC write-only archive. + * The behaviour of SDMCWriteOnlyArchive is almost the same as SDMCArchive, except for + * - OpenDirectory is unsupported; + * - OpenFile with read flag is unsupported. + */ +class SDMCWriteOnlyArchive : public SDMCArchive { +public: + SDMCWriteOnlyArchive(const std::string& mount_point) : SDMCArchive(mount_point) {} + + std::string GetName() const override { + return "SDMCWriteOnlyArchive: " + mount_point; + } + + ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, + const Mode& mode) const override; + + ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; +}; + +/// File system interface to the SDMC write-only archive +class ArchiveFactory_SDMCWriteOnly final : public ArchiveFactory { +public: + ArchiveFactory_SDMCWriteOnly(const std::string& mount_point); + + /** + * Initialize the archive. + * @return true if it initialized successfully + */ + bool Initialize(); + + std::string GetName() const override { + return "SDMCWriteOnly"; + } + + ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) override; + ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; + ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; + +private: + std::string sdmc_directory; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/archive_systemsavedata.cpp b/src/core/file_sys/archive_systemsavedata.cpp index 48ebc0ed4..54e7793e0 100644 --- a/src/core/file_sys/archive_systemsavedata.cpp +++ b/src/core/file_sys/archive_systemsavedata.cpp @@ -9,7 +9,7 @@ #include "common/file_util.h" #include "common/string_util.h" #include "core/file_sys/archive_systemsavedata.h" -#include "core/file_sys/disk_archive.h" +#include "core/file_sys/savedata_archive.h" #include "core/hle/service/fs/archive.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -56,7 +56,7 @@ ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SystemSaveData::Open(c return ResultCode(ErrorDescription::FS_NotFormatted, ErrorModule::FS, ErrorSummary::InvalidState, ErrorLevel::Status); } - auto archive = std::make_unique<DiskArchive>(fullpath); + auto archive = std::make_unique<SaveDataArchive>(fullpath); return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); } diff --git a/src/core/file_sys/directory_backend.h b/src/core/file_sys/directory_backend.h index b55e382ef..0c93f2074 100644 --- a/src/core/file_sys/directory_backend.h +++ b/src/core/file_sys/directory_backend.h @@ -41,12 +41,6 @@ public: virtual ~DirectoryBackend() {} /** - * Open the directory - * @return true if the directory opened correctly - */ - virtual bool Open() = 0; - - /** * List files contained in the directory * @param count Number of entries to return at once in entries * @param entries Buffer to read data into diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp index 2f05af361..a243d9a13 100644 --- a/src/core/file_sys/disk_archive.cpp +++ b/src/core/file_sys/disk_archive.cpp @@ -15,144 +15,8 @@ namespace FileSys { -ResultVal<std::unique_ptr<FileBackend>> DiskArchive::OpenFile(const Path& path, - const Mode mode) const { - LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); - auto file = std::make_unique<DiskFile>(*this, path, mode); - ResultCode result = file->Open(); - if (result.IsError()) - return result; - return MakeResult<std::unique_ptr<FileBackend>>(std::move(file)); -} - -ResultCode DiskArchive::DeleteFile(const Path& path) const { - std::string file_path = mount_point + path.AsString(); - - if (FileUtil::IsDirectory(file_path)) - return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, - ErrorLevel::Status); - - if (!FileUtil::Exists(file_path)) - return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, - ErrorLevel::Status); - - if (FileUtil::Delete(file_path)) - return RESULT_SUCCESS; - - return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, - ErrorLevel::Status); -} - -bool DiskArchive::RenameFile(const Path& src_path, const Path& dest_path) const { - return FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString()); -} - -bool DiskArchive::DeleteDirectory(const Path& path) const { - return FileUtil::DeleteDir(mount_point + path.AsString()); -} - -bool DiskArchive::DeleteDirectoryRecursively(const Path& path) const { - return FileUtil::DeleteDirRecursively(mount_point + path.AsString()); -} - -ResultCode DiskArchive::CreateFile(const FileSys::Path& path, u64 size) const { - std::string full_path = mount_point + path.AsString(); - - if (FileUtil::IsDirectory(full_path)) - return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, - ErrorLevel::Status); - - if (FileUtil::Exists(full_path)) - return ResultCode(ErrorDescription::FS_AlreadyExists, ErrorModule::FS, - ErrorSummary::NothingHappened, ErrorLevel::Status); - - if (size == 0) { - FileUtil::CreateEmptyFile(full_path); - return RESULT_SUCCESS; - } - - FileUtil::IOFile file(full_path, "wb"); - // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) - // We do this by seeking to the right size, then writing a single null byte. - if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) - return RESULT_SUCCESS; - - return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource, - ErrorLevel::Info); -} - -bool DiskArchive::CreateDirectory(const Path& path) const { - return FileUtil::CreateDir(mount_point + path.AsString()); -} - -bool DiskArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { - return FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString()); -} - -std::unique_ptr<DirectoryBackend> DiskArchive::OpenDirectory(const Path& path) const { - LOG_DEBUG(Service_FS, "called path=%s", path.DebugStr().c_str()); - auto directory = std::make_unique<DiskDirectory>(*this, path); - if (!directory->Open()) - return nullptr; - return std::move(directory); -} - -u64 DiskArchive::GetFreeBytes() const { - // TODO: Stubbed to return 1GiB - return 1024 * 1024 * 1024; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -DiskFile::DiskFile(const DiskArchive& archive, const Path& path, const Mode mode) { - // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass - // the root directory we set while opening the archive. - // For example, opening /../../etc/passwd can give the emulated program your users list. - this->path = archive.mount_point + path.AsString(); - this->mode.hex = mode.hex; -} - -ResultCode DiskFile::Open() { - if (FileUtil::IsDirectory(path)) - return ResultCode(ErrorDescription::FS_NotAFile, ErrorModule::FS, ErrorSummary::Canceled, - ErrorLevel::Status); - - // Specifying only the Create flag is invalid - if (mode.create_flag && !mode.read_flag && !mode.write_flag) { - return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, - ErrorSummary::Canceled, ErrorLevel::Status); - } - - if (!FileUtil::Exists(path)) { - if (!mode.create_flag) { - LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", - path.c_str()); - return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, - ErrorSummary::NotFound, ErrorLevel::Status); - } else { - // Create the file - FileUtil::CreateEmptyFile(path); - } - } - - std::string mode_string = ""; - if (mode.write_flag) - mode_string += "r+"; // Files opened with Write access can be read from - else if (mode.read_flag) - mode_string += "r"; - - // Open the file in binary mode, to avoid problems with CR/LF on Windows systems - mode_string += "b"; - - file = std::make_unique<FileUtil::IOFile>(path, mode_string.c_str()); - if (file->IsOpen()) - return RESULT_SUCCESS; - return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, - ErrorLevel::Status); -} - ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { - if (!mode.read_flag && !mode.write_flag) + if (!mode.read_flag) return ResultCode(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, ErrorSummary::Canceled, ErrorLevel::Status); @@ -189,21 +53,11 @@ bool DiskFile::Close() const { //////////////////////////////////////////////////////////////////////////////////////////////////// -DiskDirectory::DiskDirectory(const DiskArchive& archive, const Path& path) : directory() { - // TODO(Link Mauve): normalize path into an absolute path without "..", it can currently bypass - // the root directory we set while opening the archive. - // For example, opening /../../usr/bin can give the emulated program your installed programs. - this->path = archive.mount_point + path.AsString(); -} - -bool DiskDirectory::Open() { - if (!FileUtil::IsDirectory(path)) - return false; +DiskDirectory::DiskDirectory(const std::string& path) : directory() { unsigned size = FileUtil::ScanDirectoryTree(path, directory); directory.size = size; directory.isDirectory = true; children_iterator = directory.children.begin(); - return true; } u32 DiskDirectory::Read(const u32 count, Entry* entries) { diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h index 59ebb2002..eb9166df6 100644 --- a/src/core/file_sys/disk_archive.h +++ b/src/core/file_sys/disk_archive.h @@ -20,43 +20,13 @@ namespace FileSys { -/** - * Helper which implements a backend accessing the host machine's filesystem. - * This should be subclassed by concrete archive types, which will provide the - * base directory on the host filesystem and override any required functionality. - */ -class DiskArchive : public ArchiveBackend { -public: - DiskArchive(const std::string& mount_point_) : mount_point(mount_point_) {} - - virtual std::string GetName() const override { - return "DiskArchive: " + mount_point; - } - - ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, - const Mode mode) const override; - ResultCode DeleteFile(const Path& path) const override; - bool RenameFile(const Path& src_path, const Path& dest_path) const override; - bool DeleteDirectory(const Path& path) const override; - bool DeleteDirectoryRecursively(const Path& path) const override; - ResultCode CreateFile(const Path& path, u64 size) const override; - bool CreateDirectory(const Path& path) const override; - bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; - std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; - u64 GetFreeBytes() const override; - -protected: - friend class DiskFile; - friend class DiskDirectory; - - std::string mount_point; -}; - class DiskFile : public FileBackend { public: - DiskFile(const DiskArchive& archive, const Path& path, const Mode mode); + DiskFile(FileUtil::IOFile&& file_, const Mode& mode_) + : file(new FileUtil::IOFile(std::move(file_))) { + mode.hex = mode_.hex; + } - ResultCode Open() override; ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; u64 GetSize() const override; @@ -68,20 +38,18 @@ public: } protected: - std::string path; Mode mode; std::unique_ptr<FileUtil::IOFile> file; }; class DiskDirectory : public DirectoryBackend { public: - DiskDirectory(const DiskArchive& archive, const Path& path); + DiskDirectory(const std::string& path); ~DiskDirectory() override { Close(); } - bool Open() override; u32 Read(const u32 count, Entry* entries) override; bool Close() const override { @@ -89,7 +57,6 @@ public: } protected: - std::string path; u32 total_entries_in_directory; FileUtil::FSTEntry directory; diff --git a/src/core/file_sys/errors.h b/src/core/file_sys/errors.h new file mode 100644 index 000000000..fd1b07df0 --- /dev/null +++ b/src/core/file_sys/errors.h @@ -0,0 +1,40 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/result.h" + +namespace FileSys { + +const ResultCode ERROR_INVALID_PATH(ErrorDescription::FS_InvalidPath, ErrorModule::FS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); +const ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ErrorDescription::FS_UnsupportedOpenFlags, + ErrorModule::FS, ErrorSummary::NotSupported, + ErrorLevel::Usage); +const ResultCode ERROR_INVALID_OPEN_FLAGS(ErrorDescription::FS_InvalidOpenFlags, ErrorModule::FS, + ErrorSummary::Canceled, ErrorLevel::Status); +const ResultCode ERROR_INVALID_READ_FLAG(ErrorDescription::FS_InvalidReadFlag, ErrorModule::FS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); +const ResultCode ERROR_FILE_NOT_FOUND(ErrorDescription::FS_FileNotFound, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); +const ResultCode ERROR_PATH_NOT_FOUND(ErrorDescription::FS_PathNotFound, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); +const ResultCode ERROR_NOT_FOUND(ErrorDescription::FS_NotFound, ErrorModule::FS, + ErrorSummary::NotFound, ErrorLevel::Status); +const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ErrorDescription::FS_UnexpectedFileOrDirectory, + ErrorModule::FS, ErrorSummary::NotSupported, + ErrorLevel::Usage); +const ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY_SDMC(ErrorDescription::FS_NotAFile, + ErrorModule::FS, ErrorSummary::Canceled, + ErrorLevel::Status); +const ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ErrorDescription::FS_DirectoryAlreadyExists, + ErrorModule::FS, ErrorSummary::NothingHappened, + ErrorLevel::Status); +const ResultCode ERROR_FILE_ALREADY_EXISTS(ErrorDescription::FS_FileAlreadyExists, ErrorModule::FS, + ErrorSummary::NothingHappened, ErrorLevel::Status); +const ResultCode ERROR_ALREADY_EXISTS(ErrorDescription::FS_AlreadyExists, ErrorModule::FS, + ErrorSummary::NothingHappened, ErrorLevel::Status); +const ResultCode ERROR_DIRECTORY_NOT_EMPTY(ErrorDescription::FS_DirectoryNotEmpty, ErrorModule::FS, + ErrorSummary::Canceled, ErrorLevel::Status); + +} // namespace FileSys diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h index ed997537f..5e7c2bab4 100644 --- a/src/core/file_sys/file_backend.h +++ b/src/core/file_sys/file_backend.h @@ -19,12 +19,6 @@ public: virtual ~FileBackend() {} /** - * Open the file - * @return Result of the file operation - */ - virtual ResultCode Open() = 0; - - /** * Read data from the file * @param offset Offset in bytes to start reading data from * @param length Length in bytes of data to read from file diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp index af59d296d..2735d2e3c 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/ivfc_archive.cpp @@ -18,7 +18,7 @@ std::string IVFCArchive::GetName() const { } ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, - const Mode mode) const { + const Mode& mode) const { return MakeResult<std::unique_ptr<FileBackend>>( std::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); } @@ -31,22 +31,25 @@ ResultCode IVFCArchive::DeleteFile(const Path& path) const { ErrorLevel::Status); } -bool IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { +ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", GetName().c_str()); - return false; + // TODO(wwylele): Use correct error code + return ResultCode(-1); } -bool IVFCArchive::DeleteDirectory(const Path& path) const { +ResultCode IVFCArchive::DeleteDirectory(const Path& path) const { LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", GetName().c_str()); - return false; + // TODO(wwylele): Use correct error code + return ResultCode(-1); } -bool IVFCArchive::DeleteDirectoryRecursively(const Path& path) const { +ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const { LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", GetName().c_str()); - return false; + // TODO(wwylele): Use correct error code + return ResultCode(-1); } ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { @@ -57,20 +60,22 @@ ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { ErrorLevel::Permanent); } -bool IVFCArchive::CreateDirectory(const Path& path) const { +ResultCode IVFCArchive::CreateDirectory(const Path& path) const { LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).", GetName().c_str()); - return false; + // TODO(wwylele): Use correct error code + return ResultCode(-1); } -bool IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { +ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", GetName().c_str()); - return false; + // TODO(wwylele): Use correct error code + return ResultCode(-1); } -std::unique_ptr<DirectoryBackend> IVFCArchive::OpenDirectory(const Path& path) const { - return std::make_unique<IVFCDirectory>(); +ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const { + return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>()); } u64 IVFCArchive::GetFreeBytes() const { diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h index 2fbb3a568..e6fbdfb1f 100644 --- a/src/core/file_sys/ivfc_archive.h +++ b/src/core/file_sys/ivfc_archive.h @@ -33,15 +33,15 @@ public: std::string GetName() const override; ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, - const Mode mode) const override; + const Mode& mode) const override; ResultCode DeleteFile(const Path& path) const override; - bool RenameFile(const Path& src_path, const Path& dest_path) const override; - bool DeleteDirectory(const Path& path) const override; - bool DeleteDirectoryRecursively(const Path& path) const override; + ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; + ResultCode DeleteDirectory(const Path& path) const override; + ResultCode DeleteDirectoryRecursively(const Path& path) const override; ResultCode CreateFile(const Path& path, u64 size) const override; - bool CreateDirectory(const Path& path) const override; - bool RenameDirectory(const Path& src_path, const Path& dest_path) const override; - std::unique_ptr<DirectoryBackend> OpenDirectory(const Path& path) const override; + ResultCode CreateDirectory(const Path& path) const override; + ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; + ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; u64 GetFreeBytes() const override; protected: @@ -55,9 +55,6 @@ public: IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) : romfs_file(file), data_offset(offset), data_size(size) {} - ResultCode Open() override { - return RESULT_SUCCESS; - } ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; u64 GetSize() const override; @@ -75,9 +72,6 @@ private: class IVFCDirectory : public DirectoryBackend { public: - bool Open() override { - return false; - } u32 Read(const u32 count, Entry* entries) override { return 0; } diff --git a/src/core/file_sys/path_parser.cpp b/src/core/file_sys/path_parser.cpp new file mode 100644 index 000000000..5a89b02b8 --- /dev/null +++ b/src/core/file_sys/path_parser.cpp @@ -0,0 +1,98 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <set> +#include "common/file_util.h" +#include "common/string_util.h" +#include "core/file_sys/path_parser.h" + +namespace FileSys { + +PathParser::PathParser(const Path& path) { + if (path.GetType() != LowPathType::Char && path.GetType() != LowPathType::Wchar) { + is_valid = false; + return; + } + + auto path_string = path.AsString(); + if (path_string.size() == 0 || path_string[0] != '/') { + is_valid = false; + return; + } + + // Filter out invalid characters for the host system. + // Although some of these characters are valid on 3DS, they are unlikely to be used by games. + if (std::find_if(path_string.begin(), path_string.end(), [](char c) { + static const std::set<char> invalid_chars{'<', '>', '\\', '|', ':', '\"', '*', '?'}; + return invalid_chars.find(c) != invalid_chars.end(); + }) != path_string.end()) { + is_valid = false; + return; + } + + Common::SplitString(path_string, '/', path_sequence); + + auto begin = path_sequence.begin(); + auto end = path_sequence.end(); + end = std::remove_if(begin, end, [](std::string& str) { return str == "" || str == "."; }); + path_sequence = std::vector<std::string>(begin, end); + + // checks if the path is out of bounds. + int level = 0; + for (auto& node : path_sequence) { + if (node == "..") { + --level; + if (level < 0) { + is_valid = false; + return; + } + } else { + ++level; + } + } + + is_valid = true; + is_root = level == 0; +} + +PathParser::HostStatus PathParser::GetHostStatus(const std::string& mount_point) const { + auto path = mount_point; + if (!FileUtil::IsDirectory(path)) + return InvalidMountPoint; + if (path_sequence.empty()) { + return DirectoryFound; + } + + for (auto iter = path_sequence.begin(); iter != path_sequence.end() - 1; iter++) { + if (path.back() != '/') + path += '/'; + path += *iter; + + if (!FileUtil::Exists(path)) + return PathNotFound; + if (FileUtil::IsDirectory(path)) + continue; + return FileInPath; + } + + path += "/" + path_sequence.back(); + if (!FileUtil::Exists(path)) + return NotFound; + if (FileUtil::IsDirectory(path)) + return DirectoryFound; + return FileFound; +} + +std::string PathParser::BuildHostPath(const std::string& mount_point) const { + std::string path = mount_point; + for (auto& node : path_sequence) { + if (path.back() != '/') + path += '/'; + path += node; + } + return path; +} + +} // namespace FileSys diff --git a/src/core/file_sys/path_parser.h b/src/core/file_sys/path_parser.h new file mode 100644 index 000000000..990802579 --- /dev/null +++ b/src/core/file_sys/path_parser.h @@ -0,0 +1,61 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <vector> +#include "core/file_sys/archive_backend.h" + +namespace FileSys { + +/** + * A helper class parsing and verifying a string-type Path. + * Every archives with a sub file system should use this class to parse the path argument and check + * the status of the file / directory in question on the host file system. + */ +class PathParser { +public: + PathParser(const Path& path); + + /** + * Checks if the Path is valid. + * This function should be called once a PathParser is constructed. + * A Path is valid if: + * - it is a string path (with type LowPathType::Char or LowPathType::Wchar), + * - it starts with "/" (this seems a hard requirement in real 3DS), + * - it doesn't contain invalid characters, and + * - it doesn't go out of the root directory using "..". + */ + bool IsValid() const { + return is_valid; + } + + /// Checks if the Path represents the root directory. + bool IsRootDirectory() const { + return is_root; + } + + enum HostStatus { + InvalidMountPoint, + PathNotFound, // "/a/b/c" when "a" doesn't exist + FileInPath, // "/a/b/c" when "a" is a file + FileFound, // "/a/b/c" when "c" is a file + DirectoryFound, // "/a/b/c" when "c" is a directory + NotFound // "/a/b/c" when "a/b/" exists but "c" doesn't exist + }; + + /// Checks the status of the specified file / directory by the Path on the host file system. + HostStatus GetHostStatus(const std::string& mount_point) const; + + /// Builds a full path on the host file system. + std::string BuildHostPath(const std::string& mount_point) const; + +private: + std::vector<std::string> path_sequence; + bool is_valid{}; + bool is_root{}; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/savedata_archive.cpp b/src/core/file_sys/savedata_archive.cpp new file mode 100644 index 000000000..f2e6a06bc --- /dev/null +++ b/src/core/file_sys/savedata_archive.cpp @@ -0,0 +1,283 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/file_util.h" +#include "core/file_sys/disk_archive.h" +#include "core/file_sys/errors.h" +#include "core/file_sys/path_parser.h" +#include "core/file_sys/savedata_archive.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path, + const Mode& mode) const { + LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); + + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + if (mode.hex == 0) { + LOG_ERROR(Service_FS, "Empty open mode"); + return ERROR_UNSUPPORTED_OPEN_FLAGS; + } + + if (mode.create_flag && !mode.write_flag) { + LOG_ERROR(Service_FS, "Create flag set but write flag not set"); + return ERROR_UNSUPPORTED_OPEN_FLAGS; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_FILE_NOT_FOUND; + case PathParser::PathNotFound: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_PATH_NOT_FOUND; + case PathParser::FileInPath: + case PathParser::DirectoryFound: + LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; + case PathParser::NotFound: + if (!mode.create_flag) { + LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", + full_path.c_str()); + return ERROR_FILE_NOT_FOUND; + } else { + // Create the file + FileUtil::CreateEmptyFile(full_path); + } + break; + } + + FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb"); + if (!file.IsOpen()) { + LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str()); + return ERROR_FILE_NOT_FOUND; + } + + auto disk_file = std::make_unique<DiskFile>(std::move(file), mode); + return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file)); +} + +ResultCode SaveDataArchive::DeleteFile(const Path& path) const { + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_FILE_NOT_FOUND; + case PathParser::PathNotFound: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_PATH_NOT_FOUND; + case PathParser::FileInPath: + case PathParser::DirectoryFound: + case PathParser::NotFound: + LOG_ERROR(Service_FS, "File not found %s", full_path.c_str()); + return ERROR_FILE_NOT_FOUND; + } + + if (FileUtil::Delete(full_path)) { + return RESULT_SUCCESS; + } + + LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str()); + return ERROR_FILE_NOT_FOUND; +} + +ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const { + if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) { + return RESULT_SUCCESS; + } + + // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't + // exist or similar. Verify. + return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description + ErrorSummary::NothingHappened, ErrorLevel::Status); +} + +template <typename T> +static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point, + T deleter) { + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + if (path_parser.IsRootDirectory()) + return ERROR_DIRECTORY_NOT_EMPTY; + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_PATH_NOT_FOUND; + case PathParser::PathNotFound: + case PathParser::NotFound: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_PATH_NOT_FOUND; + case PathParser::FileInPath: + case PathParser::FileFound: + LOG_ERROR(Service_FS, "Unexpected file or directory %s", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; + } + + if (deleter(full_path)) { + return RESULT_SUCCESS; + } + + LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str()); + return ERROR_DIRECTORY_NOT_EMPTY; +} + +ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const { + return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir); +} + +ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const { + return DeleteDirectoryHelper( + path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); }); +} + +ResultCode SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const { + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_FILE_NOT_FOUND; + case PathParser::PathNotFound: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_PATH_NOT_FOUND; + case PathParser::FileInPath: + LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; + case PathParser::DirectoryFound: + case PathParser::FileFound: + LOG_ERROR(Service_FS, "%s already exists", full_path.c_str()); + return ERROR_FILE_ALREADY_EXISTS; + } + + if (size == 0) { + FileUtil::CreateEmptyFile(full_path); + return RESULT_SUCCESS; + } + + FileUtil::IOFile file(full_path, "wb"); + // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) + // We do this by seeking to the right size, then writing a single null byte. + if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) { + return RESULT_SUCCESS; + } + + LOG_ERROR(Service_FS, "Too large file"); + return ResultCode(ErrorDescription::TooLarge, ErrorModule::FS, ErrorSummary::OutOfResource, + ErrorLevel::Info); +} + +ResultCode SaveDataArchive::CreateDirectory(const Path& path) const { + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_FILE_NOT_FOUND; + case PathParser::PathNotFound: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_PATH_NOT_FOUND; + case PathParser::FileInPath: + LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; + case PathParser::DirectoryFound: + case PathParser::FileFound: + LOG_ERROR(Service_FS, "%s already exists", full_path.c_str()); + return ERROR_DIRECTORY_ALREADY_EXISTS; + } + + if (FileUtil::CreateDir(mount_point + path.AsString())) { + return RESULT_SUCCESS; + } + + LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str()); + return ResultCode(ErrorDescription::NoData, ErrorModule::FS, ErrorSummary::Canceled, + ErrorLevel::Status); +} + +ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { + if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) + return RESULT_SUCCESS; + + // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't + // exist or similar. Verify. + return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description + ErrorSummary::NothingHappened, ErrorLevel::Status); +} + +ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory( + const Path& path) const { + const PathParser path_parser(path); + + if (!path_parser.IsValid()) { + LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto full_path = path_parser.BuildHostPath(mount_point); + + switch (path_parser.GetHostStatus(mount_point)) { + case PathParser::InvalidMountPoint: + LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); + return ERROR_FILE_NOT_FOUND; + case PathParser::PathNotFound: + case PathParser::NotFound: + LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); + return ERROR_PATH_NOT_FOUND; + case PathParser::FileInPath: + case PathParser::FileFound: + LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); + return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; + } + + auto directory = std::make_unique<DiskDirectory>(full_path); + return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory)); +} + +u64 SaveDataArchive::GetFreeBytes() const { + // TODO: Stubbed to return 1GiB + return 1024 * 1024 * 1024; +} + +} // namespace FileSys diff --git a/src/core/file_sys/savedata_archive.h b/src/core/file_sys/savedata_archive.h new file mode 100644 index 000000000..2fb6c452a --- /dev/null +++ b/src/core/file_sys/savedata_archive.h @@ -0,0 +1,43 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include "core/file_sys/archive_backend.h" +#include "core/file_sys/directory_backend.h" +#include "core/file_sys/file_backend.h" +#include "core/hle/result.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +/// Archive backend for general save data archive type (SaveData and SystemSaveData) +class SaveDataArchive : public ArchiveBackend { +public: + SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {} + + std::string GetName() const override { + return "SaveDataArchive: " + mount_point; + } + + ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, + const Mode& mode) const override; + ResultCode DeleteFile(const Path& path) const override; + ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; + ResultCode DeleteDirectory(const Path& path) const override; + ResultCode DeleteDirectoryRecursively(const Path& path) const override; + ResultCode CreateFile(const Path& path, u64 size) const override; + ResultCode CreateDirectory(const Path& path) const override; + ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; + ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; + u64 GetFreeBytes() const override; + +protected: + std::string mount_point; +}; + +} // namespace FileSys diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp index 14964427b..e1379ac4d 100644 --- a/src/core/hle/applets/erreula.cpp +++ b/src/core/hle/applets/erreula.cpp @@ -10,7 +10,7 @@ namespace HLE { namespace Applets { ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& parameter) { - if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) { LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); UNIMPLEMENTED(); // TODO(Subv): Find the right error code @@ -36,7 +36,7 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; - result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); + result.signal = static_cast<u32>(Service::APT::SignalType::Response); result.buffer.clear(); result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); result.sender_id = static_cast<u32>(id); @@ -57,7 +57,7 @@ ResultCode ErrEula::StartImpl(const Service::APT::AppletStartupParameter& parame Service::APT::MessageParameter message; message.buffer.resize(parameter.buffer.size()); std::fill(message.buffer.begin(), message.buffer.end(), 0); - message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); + message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit); message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); message.sender_id = static_cast<u32>(id); Service::APT::SendParameter(message); diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index 53a8683a4..3455b9201 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp @@ -19,7 +19,7 @@ namespace HLE { namespace Applets { ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& parameter) { - if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) { LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); UNIMPLEMENTED(); // TODO(Subv): Find the right error code @@ -44,7 +44,7 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; - result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); + result.signal = static_cast<u32>(Service::APT::SignalType::Response); result.buffer.clear(); result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); result.sender_id = static_cast<u32>(id); @@ -73,7 +73,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa Service::APT::MessageParameter message; message.buffer.resize(sizeof(MiiResult)); std::memcpy(message.buffer.data(), &result, message.buffer.size()); - message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); + message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit); message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); message.sender_id = static_cast<u32>(id); Service::APT::SendParameter(message); diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index 06ddf538b..1e21337f5 100644 --- a/src/core/hle/applets/swkbd.cpp +++ b/src/core/hle/applets/swkbd.cpp @@ -22,7 +22,7 @@ namespace HLE { namespace Applets { ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter const& parameter) { - if (parameter.signal != static_cast<u32>(Service::APT::SignalType::LibAppJustStarted)) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) { LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); UNIMPLEMENTED(); // TODO(Subv): Find the right error code @@ -47,7 +47,7 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; - result.signal = static_cast<u32>(Service::APT::SignalType::LibAppFinished); + result.signal = static_cast<u32>(Service::APT::SignalType::Response); result.buffer.clear(); result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); result.sender_id = static_cast<u32>(id); @@ -108,7 +108,7 @@ void SoftwareKeyboard::Finalize() { Service::APT::MessageParameter message; message.buffer.resize(sizeof(SoftwareKeyboardConfig)); std::memcpy(message.buffer.data(), &config, message.buffer.size()); - message.signal = static_cast<u32>(Service::APT::SignalType::LibAppClosed); + message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit); message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); message.sender_id = static_cast<u32>(id); Service::APT::SendParameter(message); diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 1489c7002..3e116e3df 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -22,6 +22,11 @@ SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) { evt->reset_type = reset_type; evt->name = std::move(name); + if (reset_type == ResetType::Pulse) { + LOG_ERROR(Kernel, "Unimplemented event reset type Pulse"); + UNIMPLEMENTED(); + } + return evt; } diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index a9f98223c..eac181f4e 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -31,6 +31,11 @@ SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) { timer->interval_delay = 0; timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); + if (reset_type == ResetType::Pulse) { + LOG_ERROR(Kernel, "Unimplemented timer reset type Pulse"); + UNIMPLEMENTED(); + } + return timer; } diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 7f8d8e00d..f7356f9d8 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -20,15 +20,24 @@ enum class ErrorDescription : u32 { OS_InvalidBufferDescriptor = 48, WrongAddress = 53, FS_ArchiveNotMounted = 101, + FS_FileNotFound = 112, + FS_PathNotFound = 113, FS_NotFound = 120, + FS_FileAlreadyExists = 180, + FS_DirectoryAlreadyExists = 185, FS_AlreadyExists = 190, FS_InvalidOpenFlags = 230, + FS_DirectoryNotEmpty = 240, FS_NotAFile = 250, FS_NotFormatted = 340, ///< This is used by the FS service when creating a SaveData archive OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage GPU_FirstInitialization = 519, + FS_InvalidReadFlag = 700, FS_InvalidPath = 702, + FS_WriteBeyondEnd = 705, + FS_UnsupportedOpenFlags = 760, + FS_UnexpectedFileOrDirectory = 770, InvalidSection = 1000, TooLarge = 1001, NotAuthorized = 1002, diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index c4bd65986..31e5e07b2 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -396,6 +396,15 @@ void StartLibraryApplet(Service::Interface* self) { cmd_buff[1] = applet->Start(parameter).raw; } +void CancelLibraryApplet(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 exiting = cmd_buff[1] & 0xFF; + + cmd_buff[1] = 1; // TODO: Find the return code meaning + + LOG_WARNING(Service_APT, "(STUBBED) called exiting=%u", exiting); +} + void SetScreenCapPostPermission(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -523,7 +532,7 @@ void Init() { notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); - next_parameter.signal = static_cast<u32>(SignalType::AppJustStarted); + next_parameter.signal = static_cast<u32>(SignalType::Wakeup); next_parameter.destination_id = 0x300; } diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index a118cda1f..44dbd8757 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -46,12 +46,23 @@ static_assert(sizeof(CaptureBufferInfo) == 0x20, "CaptureBufferInfo struct has i /// Signals used by APT functions enum class SignalType : u32 { None = 0x0, - AppJustStarted = 0x1, - LibAppJustStarted = 0x2, - LibAppFinished = 0x3, - LibAppClosed = 0xA, - ReturningToApp = 0xB, - ExitingApp = 0xC, + Wakeup = 0x1, + Request = 0x2, + Response = 0x3, + Exit = 0x4, + Message = 0x5, + HomeButtonSingle = 0x6, + HomeButtonDouble = 0x7, + DspSleep = 0x8, + DspWakeup = 0x9, + WakeupByExit = 0xA, + WakeupByPause = 0xB, + WakeupByCancel = 0xC, + WakeupByCancelAll = 0xD, + WakeupByPowerButtonClick = 0xE, + WakeupToJumpHome = 0xF, + RequestForSysApplet = 0x10, + WakeupToLaunchApplication = 0x11, }; /// App Id's used by APT functions @@ -381,6 +392,17 @@ void PreloadLibraryApplet(Service::Interface* self); void StartLibraryApplet(Service::Interface* self); /** + * APT::CancelLibraryApplet service function + * Inputs: + * 0 : Command header [0x003B0040] + * 1 : u8, Application exiting (0 = not exiting, 1 = exiting) + * Outputs: + * 0 : Header code + * 1 : Result code + */ +void CancelLibraryApplet(Service::Interface* self); + +/** * APT::GetStartupArgument service function * Inputs: * 1 : Parameter Size (capped to 0x300) diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index f27ad91b7..a7a0c8a41 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -25,7 +25,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00160040, PreloadLibraryApplet, "PreloadLibraryApplet"}, {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, - {0x003B0040, nullptr, "CancelLibraryApplet?"}, + {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"}, {0x003E0080, nullptr, "ReplySleepQuery"}, {0x00430040, NotifyToWait, "NotifyToWait?"}, {0x00440000, GetSharedFont, "GetSharedFont?"}, diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index d6ad42e21..a731c39f6 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -67,7 +67,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00380040, nullptr, "PreloadResidentApplet"}, {0x00390040, nullptr, "PrepareToStartResidentApplet"}, {0x003A0044, nullptr, "StartResidentApplet"}, - {0x003B0040, nullptr, "CancelLibraryApplet"}, + {0x003B0040, CancelLibraryApplet, "CancelLibraryApplet"}, {0x003C0042, nullptr, "SendDspSleep"}, {0x003D0042, nullptr, "SendDspWakeUp"}, {0x003E0080, nullptr, "ReplySleepQuery"}, diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 24eee6903..d554c3f54 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -45,7 +45,8 @@ static_assert(sizeof(SaveFileConfig) == 0x455C, enum ConfigBlockID { StereoCameraSettingsBlockID = 0x00050005, SoundOutputModeBlockID = 0x00070001, - ConsoleUniqueIDBlockID = 0x00090001, + ConsoleUniqueID1BlockID = 0x00090000, + ConsoleUniqueID2BlockID = 0x00090001, UsernameBlockID = 0x000A0000, BirthdayBlockID = 0x000A0001, LanguageBlockID = 0x000A0002, @@ -359,7 +360,7 @@ ResultCode CreateConfigInfoBlk(u32 block_id, u16 size, u16 flags, const void* da } ResultCode DeleteConfigNANDSaveFile() { - FileSys::Path path("config"); + FileSys::Path path("/config"); return Service::FS::DeleteFileFromArchive(cfg_system_save_data_archive, path); } @@ -368,7 +369,7 @@ ResultCode UpdateConfigNANDSavegame() { mode.write_flag.Assign(1); mode.create_flag.Assign(1); - FileSys::Path path("config"); + FileSys::Path path("/config"); auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode); ASSERT_MSG(config_result.Succeeded(), "could not open file"); @@ -382,8 +383,9 @@ ResultCode UpdateConfigNANDSavegame() { ResultCode FormatConfig() { ResultCode res = DeleteConfigNANDSaveFile(); // The delete command fails if the file doesn't exist, so we have to check that too - if (!res.IsSuccess() && res.description != ErrorDescription::FS_NotFound) + if (!res.IsSuccess() && res.description != ErrorDescription::FS_FileNotFound) { return res; + } // Delete the old data cfg_config_file_buffer.fill(0); // Create the header @@ -409,7 +411,12 @@ ResultCode FormatConfig() { if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(ConsoleUniqueIDBlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, + res = CreateConfigInfoBlk(ConsoleUniqueID1BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, + &CONSOLE_UNIQUE_ID); + if (!res.IsSuccess()) + return res; + + res = CreateConfigInfoBlk(ConsoleUniqueID2BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, &CONSOLE_UNIQUE_ID); if (!res.IsSuccess()) return res; @@ -504,7 +511,7 @@ ResultCode LoadConfigNANDSaveFile() { cfg_system_save_data_archive = *archive_result; - FileSys::Path config_path("config"); + FileSys::Path config_path("/config"); FileSys::Mode open_mode = {}; open_mode.read_flag.Assign(1); diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 3ca4f98de..9905757c7 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp @@ -2,9 +2,15 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> +#include <chrono> +#include <iomanip> +#include <sstream> + #include "common/bit_field.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/result.h" #include "core/hle/service/err_f.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -12,12 +18,45 @@ namespace ERR_F { -enum { - ErrSpecifier0 = 0, - ErrSpecifier1 = 1, - ErrSpecifier3 = 3, - ErrSpecifier4 = 4, +enum class FatalErrType : u32 { + Generic = 0, + Corrupted = 1, + CardRemoved = 2, + Exception = 3, + ResultFailure = 4, + Logged = 5, +}; + +enum class ExceptionType : u32 { + PrefetchAbort = 0, + DataAbort = 1, + Undefined = 2, + VectorFP = 3, +}; + +struct ExceptionInfo { + u8 exception_type; + INSERT_PADDING_BYTES(3); + u32 sr; + u32 ar; + u32 fpexc; + u32 fpinst; + u32 fpinst2; +}; +static_assert(sizeof(ExceptionInfo) == 0x18, "ExceptionInfo struct has incorrect size"); + +struct ExceptionContext final { + std::array<u32, 16> arm_regs; + u32 cpsr; }; +static_assert(sizeof(ExceptionContext) == 0x44, "ExceptionContext struct has incorrect size"); + +struct ExceptionData { + ExceptionInfo exception_info; + ExceptionContext exception_context; + INSERT_PADDING_WORDS(1); +}; +static_assert(sizeof(ExceptionData) == 0x60, "ExceptionData struct has incorrect size"); // This is used instead of ResultCode from result.h // because we can't have non-trivial data members in unions. @@ -30,150 +69,191 @@ union RSL { BitField<27, 5, u32> level; }; -union ErrInfo { - u8 specifier; - - struct { - u8 specifier; // 0x0 - u8 rev_high; // 0x1 - u16 rev_low; // 0x2 - RSL result_code; // 0x4 - u32 address; // 0x8 - INSERT_PADDING_BYTES(4); // 0xC - u32 pid_low; // 0x10 - u32 pid_high; // 0x14 - u32 aid_low; // 0x18 - u32 aid_high; // 0x1C - } errtype1; - - struct { - u8 specifier; // 0x0 - u8 rev_high; // 0x1 - u16 rev_low; // 0x2 - INSERT_PADDING_BYTES(0xC); // 0x4 - u32 pid_low; // 0x10 - u32 pid_high; // 0x14 - u32 aid_low; // 0x18 - u32 aid_high; // 0x1C - u8 error_type; // 0x20 - INSERT_PADDING_BYTES(3); // 0x21 - u32 fault_status_reg; // 0x24 - u32 fault_addr; // 0x28 - u32 fpexc; // 0x2C - u32 finst; // 0x30 - u32 finst2; // 0x34 - INSERT_PADDING_BYTES(0x34); // 0x38 - u32 sp; // 0x6C - u32 pc; // 0x70 - u32 lr; // 0x74 - u32 cpsr; // 0x78 - } errtype3; - - struct { - u8 specifier; // 0x0 - u8 rev_high; // 0x1 - u16 rev_low; // 0x2 - RSL result_code; // 0x4 - INSERT_PADDING_BYTES(8); // 0x8 - u32 pid_low; // 0x10 - u32 pid_high; // 0x14 - u32 aid_low; // 0x18 - u32 aid_high; // 0x1C - char debug_string1[0x2E]; // 0x20 - char debug_string2[0x2E]; // 0x4E - } errtype4; +struct ErrInfo { + struct ErrInfoCommon { + u8 specifier; // 0x0 + u8 rev_high; // 0x1 + u16 rev_low; // 0x2 + RSL result_code; // 0x4 + u32 pc_address; // 0x8 + u32 pid; // 0xC + u32 title_id_low; // 0x10 + u32 title_id_high; // 0x14 + u32 app_title_id_low; // 0x18 + u32 app_title_id_high; // 0x1C + } errinfo_common; + static_assert(sizeof(ErrInfoCommon) == 0x20, "ErrInfoCommon struct has incorrect size"); + + union { + struct { + char data[0x60]; // 0x20 + } generic; + + struct { + ExceptionData exception_data; // 0x20 + } exception; + + struct { + char message[0x60]; // 0x20 + } result_failure; + }; }; -enum { PrefetchAbort = 0, DataAbort = 1, UndefInstr = 2, VectorFP = 3 }; +static std::string GetErrType(u8 type_code) { + switch (static_cast<FatalErrType>(type_code)) { + case FatalErrType::Generic: + return "Generic"; + case FatalErrType::Corrupted: + return "Corrupted"; + case FatalErrType::CardRemoved: + return "CardRemoved"; + case FatalErrType::Exception: + return "Exception"; + case FatalErrType::ResultFailure: + return "ResultFailure"; + case FatalErrType::Logged: + return "Logged"; + default: + return "Unknown Error Type"; + } +} -static std::string GetErrInfo3Type(u8 type_code) { - switch (type_code) { - case PrefetchAbort: +static std::string GetExceptionType(u8 type_code) { + switch (static_cast<ExceptionType>(type_code)) { + case ExceptionType::PrefetchAbort: return "Prefetch Abort"; - case DataAbort: + case ExceptionType::DataAbort: return "Data Abort"; - case UndefInstr: - return "Undefined Instruction"; - case VectorFP: - return "Vector Floating Point"; + case ExceptionType::Undefined: + return "Undefined Exception"; + case ExceptionType::VectorFP: + return "Vector Floating Point Exception"; default: - return "unknown"; + return "Unknown Exception Type"; } } +static std::string GetCurrentSystemTime() { + auto now = std::chrono::system_clock::now(); + auto time = std::chrono::system_clock::to_time_t(now); + + std::stringstream time_stream; + time_stream << std::put_time(std::localtime(&time), "%Y/%m/%d %H:%M:%S"); + return time_stream.str(); +} + +static void LogGenericInfo(const ErrInfo::ErrInfoCommon& errinfo_common) { + LOG_CRITICAL(Service_ERR, "PID: 0x%08X", errinfo_common.pid); + LOG_CRITICAL(Service_ERR, "REV: 0x%08X_0x%08X", errinfo_common.rev_high, + errinfo_common.rev_low); + LOG_CRITICAL(Service_ERR, "TID: 0x%08X_0x%08X", errinfo_common.title_id_high, + errinfo_common.title_id_low); + LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errinfo_common.app_title_id_high, + errinfo_common.app_title_id_low); + LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errinfo_common.pc_address); + + LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errinfo_common.result_code.raw); + LOG_CRITICAL(Service_ERR, " Level: %u", errinfo_common.result_code.level.Value()); + LOG_CRITICAL(Service_ERR, " Summary: %u", errinfo_common.result_code.summary.Value()); + LOG_CRITICAL(Service_ERR, " Module: %u", errinfo_common.result_code.module.Value()); + LOG_CRITICAL(Service_ERR, " Desc: %u", errinfo_common.result_code.description.Value()); +} + +/* ThrowFatalError function + * Inputs: + * 0 : Header code [0x00010800] + * 1-32 : FatalErrInfo + * Outputs: + * 0 : Header code + * 1 : Result code + */ static void ThrowFatalError(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - LOG_CRITICAL(Service_ERR, "Fatal error!"); + LOG_CRITICAL(Service_ERR, "Fatal error"); const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]); + LOG_CRITICAL(Service_ERR, "Fatal error type: %s", + GetErrType(errinfo->errinfo_common.specifier).c_str()); - switch (errinfo->specifier) { - case ErrSpecifier0: - case ErrSpecifier1: { - const auto& errtype = errinfo->errtype1; - LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); - LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); - LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); - LOG_CRITICAL(Service_ERR, "ADR: 0x%08X", errtype.address); - - LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw); - LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value()); - LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value()); - LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value()); - LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value()); + // Generic Info + LogGenericInfo(errinfo->errinfo_common); + + switch (static_cast<FatalErrType>(errinfo->errinfo_common.specifier)) { + case FatalErrType::Generic: + case FatalErrType::Corrupted: + case FatalErrType::CardRemoved: + case FatalErrType::Logged: { + LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str()); break; } + case FatalErrType::Exception: { + const auto& errtype = errinfo->exception; + + // Register Info + LOG_CRITICAL(Service_ERR, "ARM Registers:"); + for (u32 index = 0; index < errtype.exception_data.exception_context.arm_regs.size(); + ++index) { + if (index < 13) { + LOG_DEBUG(Service_ERR, "r%u=0x%08X", index, + errtype.exception_data.exception_context.arm_regs.at(index)); + } else if (index == 13) { + LOG_CRITICAL(Service_ERR, "SP=0x%08X", + errtype.exception_data.exception_context.arm_regs.at(index)); + } else if (index == 14) { + LOG_CRITICAL(Service_ERR, "LR=0x%08X", + errtype.exception_data.exception_context.arm_regs.at(index)); + } else if (index == 15) { + LOG_CRITICAL(Service_ERR, "PC=0x%08X", + errtype.exception_data.exception_context.arm_regs.at(index)); + } + } + LOG_CRITICAL(Service_ERR, "CPSR=0x%08X", errtype.exception_data.exception_context.cpsr); - case ErrSpecifier3: { - const auto& errtype = errinfo->errtype3; - LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); - LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); - LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); - LOG_CRITICAL(Service_ERR, "TYPE: %s", GetErrInfo3Type(errtype.error_type).c_str()); - - LOG_CRITICAL(Service_ERR, "PC: 0x%08X", errtype.pc); - LOG_CRITICAL(Service_ERR, "LR: 0x%08X", errtype.lr); - LOG_CRITICAL(Service_ERR, "SP: 0x%08X", errtype.sp); - LOG_CRITICAL(Service_ERR, "CPSR: 0x%08X", errtype.cpsr); - - switch (errtype.error_type) { - case PrefetchAbort: - case DataAbort: - LOG_CRITICAL(Service_ERR, "Fault Address: 0x%08X", errtype.fault_addr); - LOG_CRITICAL(Service_ERR, "Fault Status Register: 0x%08X", errtype.fault_status_reg); + // Exception Info + LOG_CRITICAL( + Service_ERR, "EXCEPTION TYPE: %s", + GetExceptionType(errtype.exception_data.exception_info.exception_type).c_str()); + switch (static_cast<ExceptionType>(errtype.exception_data.exception_info.exception_type)) { + case ExceptionType::PrefetchAbort: + LOG_CRITICAL(Service_ERR, "IFSR: 0x%08X", errtype.exception_data.exception_info.sr); + LOG_CRITICAL(Service_ERR, "r15: 0x%08X", errtype.exception_data.exception_info.ar); + case ExceptionType::DataAbort: + LOG_CRITICAL(Service_ERR, "DFSR: 0x%08X", errtype.exception_data.exception_info.sr); + LOG_CRITICAL(Service_ERR, "DFAR: 0x%08X", errtype.exception_data.exception_info.ar); break; - case VectorFP: - LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X", errtype.fpexc); - LOG_CRITICAL(Service_ERR, "FINST: 0x%08X", errtype.finst); - LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X", errtype.finst2); + case ExceptionType::VectorFP: + LOG_CRITICAL(Service_ERR, "FPEXC: 0x%08X", + errtype.exception_data.exception_info.fpinst); + LOG_CRITICAL(Service_ERR, "FINST: 0x%08X", + errtype.exception_data.exception_info.fpinst); + LOG_CRITICAL(Service_ERR, "FINST2: 0x%08X", + errtype.exception_data.exception_info.fpinst2); break; } + LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str()); break; } - case ErrSpecifier4: { - const auto& errtype = errinfo->errtype4; - LOG_CRITICAL(Service_ERR, "PID: 0x%08X_0x%08X", errtype.pid_low, errtype.pid_high); - LOG_CRITICAL(Service_ERR, "REV: %d", errtype.rev_low | (errtype.rev_high << 16)); - LOG_CRITICAL(Service_ERR, "AID: 0x%08X_0x%08X", errtype.aid_low, errtype.aid_high); + case FatalErrType::ResultFailure: { + const auto& errtype = errinfo->result_failure; - LOG_CRITICAL(Service_ERR, "RSL: 0x%08X", errtype.result_code.raw); - LOG_CRITICAL(Service_ERR, " Level: %u", errtype.result_code.level.Value()); - LOG_CRITICAL(Service_ERR, " Summary: %u", errtype.result_code.summary.Value()); - LOG_CRITICAL(Service_ERR, " Module: %u", errtype.result_code.module.Value()); - LOG_CRITICAL(Service_ERR, " Desc: %u", errtype.result_code.description.Value()); - - LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string1); - LOG_CRITICAL(Service_ERR, "%s", errtype.debug_string2); + // Failure Message + LOG_CRITICAL(Service_ERR, "Failure Message: %s", errtype.message); + LOG_CRITICAL(Service_ERR, "Datetime: %s", GetCurrentSystemTime().c_str()); break; } - } - cmd_buff[1] = 0; // No error + } // switch FatalErrType + + cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error } const Interface::FunctionInfo FunctionTable[] = { + // clang-format off {0x00010800, ThrowFatalError, "ThrowFatalError"}, + {0x00020042, nullptr, "SetUserString"}, + // clang-format on }; //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 7f9696bfb..4c29784e8 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -15,9 +15,10 @@ #include "common/logging/log.h" #include "core/file_sys/archive_backend.h" #include "core/file_sys/archive_extsavedata.h" +#include "core/file_sys/archive_ncch.h" #include "core/file_sys/archive_savedata.h" -#include "core/file_sys/archive_savedatacheck.h" #include "core/file_sys/archive_sdmc.h" +#include "core/file_sys/archive_sdmcwriteonly.h" #include "core/file_sys/archive_systemsavedata.h" #include "core/file_sys/directory_backend.h" #include "core/file_sys/file_backend.h" @@ -338,17 +339,11 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, return ERR_INVALID_ARCHIVE_HANDLE; if (src_archive == dest_archive) { - if (src_archive->RenameFile(src_path, dest_path)) - return RESULT_SUCCESS; + return src_archive->RenameFile(src_path, dest_path); } else { // TODO: Implement renaming across archives return UnimplementedFunction(ErrorModule::FS); } - - // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't - // exist or similar. Verify. - return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description - ErrorSummary::NothingHappened, ErrorLevel::Status); } ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { @@ -356,10 +351,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy if (archive == nullptr) return ERR_INVALID_ARCHIVE_HANDLE; - if (archive->DeleteDirectory(path)) - return RESULT_SUCCESS; - return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description - ErrorSummary::Canceled, ErrorLevel::Status); + return archive->DeleteDirectory(path); } ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle, @@ -368,10 +360,7 @@ ResultCode DeleteDirectoryRecursivelyFromArchive(ArchiveHandle archive_handle, if (archive == nullptr) return ERR_INVALID_ARCHIVE_HANDLE; - if (archive->DeleteDirectoryRecursively(path)) - return RESULT_SUCCESS; - return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description - ErrorSummary::Canceled, ErrorLevel::Status); + return archive->DeleteDirectoryRecursively(path); } ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, @@ -388,10 +377,7 @@ ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy if (archive == nullptr) return ERR_INVALID_ARCHIVE_HANDLE; - if (archive->CreateDirectory(path)) - return RESULT_SUCCESS; - return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description - ErrorSummary::Canceled, ErrorLevel::Status); + return archive->CreateDirectory(path); } ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, @@ -404,17 +390,11 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, return ERR_INVALID_ARCHIVE_HANDLE; if (src_archive == dest_archive) { - if (src_archive->RenameDirectory(src_path, dest_path)) - return RESULT_SUCCESS; + return src_archive->RenameDirectory(src_path, dest_path); } else { // TODO: Implement renaming across archives return UnimplementedFunction(ErrorModule::FS); } - - // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't - // exist or similar. Verify. - return ResultCode(ErrorDescription::NoData, ErrorModule::FS, // TODO: verify description - ErrorSummary::NothingHappened, ErrorLevel::Status); } ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, @@ -423,13 +403,11 @@ ResultVal<Kernel::SharedPtr<Directory>> OpenDirectoryFromArchive(ArchiveHandle a if (archive == nullptr) return ERR_INVALID_ARCHIVE_HANDLE; - std::unique_ptr<FileSys::DirectoryBackend> backend = archive->OpenDirectory(path); - if (backend == nullptr) { - return ResultCode(ErrorDescription::FS_NotFound, ErrorModule::FS, ErrorSummary::NotFound, - ErrorLevel::Permanent); - } + auto backend = archive->OpenDirectory(path); + if (backend.Failed()) + return backend.Code(); - auto directory = Kernel::SharedPtr<Directory>(new Directory(std::move(backend), path)); + auto directory = Kernel::SharedPtr<Directory>(new Directory(backend.MoveFrom(), path)); return MakeResult<Kernel::SharedPtr<Directory>>(std::move(directory)); } @@ -549,6 +527,13 @@ void RegisterArchiveTypes() { LOG_ERROR(Service_FS, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); + auto sdmcwo_factory = std::make_unique<FileSys::ArchiveFactory_SDMCWriteOnly>(sdmc_directory); + if (sdmcwo_factory->Initialize()) + RegisterArchiveType(std::move(sdmcwo_factory), ArchiveIdCode::SDMCWriteOnly); + else + LOG_ERROR(Service_FS, "Can't instantiate SDMCWriteOnly archive with path %s", + sdmc_directory.c_str()); + // Create the SaveData archive auto savedata_factory = std::make_unique<FileSys::ArchiveFactory_SaveData>(sdmc_directory); RegisterArchiveType(std::move(savedata_factory), ArchiveIdCode::SaveData); @@ -569,10 +554,9 @@ void RegisterArchiveTypes() { LOG_ERROR(Service_FS, "Can't instantiate SharedExtSaveData archive with path %s", sharedextsavedata_factory->GetMountPoint().c_str()); - // Create the SaveDataCheck archive, basically a small variation of the RomFS archive - auto savedatacheck_factory = - std::make_unique<FileSys::ArchiveFactory_SaveDataCheck>(nand_directory); - RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::SaveDataCheck); + // Create the NCCH archive, basically a small variation of the RomFS archive + auto savedatacheck_factory = std::make_unique<FileSys::ArchiveFactory_NCCH>(nand_directory); + RegisterArchiveType(std::move(savedatacheck_factory), ArchiveIdCode::NCCH); auto systemsavedata_factory = std::make_unique<FileSys::ArchiveFactory_SystemSaveData>(nand_directory); diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 41a76285c..21ed9717b 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -33,7 +33,7 @@ enum class ArchiveIdCode : u32 { SystemSaveData = 0x00000008, SDMC = 0x00000009, SDMCWriteOnly = 0x0000000A, - SaveDataCheck = 0x2345678A, + NCCH = 0x2345678A, }; /// Media types for the archives diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index edd1ea97b..563341504 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -2,6 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/logging/log.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/shared_memory.h" #include "core/hle/service/mic_u.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -9,23 +12,298 @@ namespace MIC_U { +enum class Encoding : u8 { + PCM8 = 0, + PCM16 = 1, + PCM8Signed = 2, + PCM16Signed = 3, +}; + +enum class SampleRate : u8 { + SampleRate32730 = 0, + SampleRate16360 = 1, + SampleRate10910 = 2, + SampleRate8180 = 3 +}; + +static Kernel::SharedPtr<Kernel::Event> buffer_full_event; +static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; +static u8 mic_gain = 0; +static bool mic_power = false; +static bool is_sampling = false; +static bool allow_shell_closed; +static bool clamp = false; +static Encoding encoding; +static SampleRate sample_rate; +static s32 audio_buffer_offset; +static u32 audio_buffer_size; +static bool audio_buffer_loop; + +/** + * MIC::MapSharedMem service function + * Inputs: + * 0 : Header Code[0x00010042] + * 1 : Shared-mem size + * 2 : CopyHandleDesc + * 3 : Shared-mem handle + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void MapSharedMem(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 size = cmd_buff[1]; + Handle mem_handle = cmd_buff[3]; + shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(mem_handle); + if (shared_memory) { + shared_memory->name = "MIC_U:shared_memory"; + } + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "called, size=0x%X, mem_handle=0x%08X", size, mem_handle); +} + +/** + * MIC::UnmapSharedMem service function + * Inputs: + * 0 : Header Code[0x00020000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void UnmapSharedMem(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "called"); +} + +/** + * MIC::StartSampling service function + * Inputs: + * 0 : Header Code[0x00030140] + * 1 : Encoding + * 2 : SampleRate + * 3 : Base offset for audio data in sharedmem + * 4 : Size of the audio data in sharedmem + * 5 : Loop at end of buffer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void StartSampling(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + encoding = static_cast<Encoding>(cmd_buff[1] & 0xFF); + sample_rate = static_cast<SampleRate>(cmd_buff[2] & 0xFF); + audio_buffer_offset = cmd_buff[3]; + audio_buffer_size = cmd_buff[4]; + audio_buffer_loop = static_cast<bool>(cmd_buff[5] & 0xFF); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + is_sampling = true; + LOG_WARNING(Service_MIC, "(STUBBED) called, encoding=%u, sample_rate=%u, " + "audio_buffer_offset=%d, audio_buffer_size=%u, audio_buffer_loop=%u", + encoding, sample_rate, audio_buffer_offset, audio_buffer_size, audio_buffer_loop); +} + +/** + * MIC::AdjustSampling service function + * Inputs: + * 0 : Header Code[0x00040040] + * 1 : SampleRate + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void AdjustSampling(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + sample_rate = static_cast<SampleRate>(cmd_buff[1] & 0xFF); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, sample_rate=%u", sample_rate); +} + +/** + * MIC::StopSampling service function + * Inputs: + * 0 : Header Code[0x00050000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void StopSampling(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + is_sampling = false; + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::IsSampling service function + * Inputs: + * 0 : Header Code[0x00060000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : 0 = sampling, non-zero = sampling + */ +static void IsSampling(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = is_sampling; + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::GetBufferFullEvent service function + * Inputs: + * 0 : Header Code[0x00070000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 3 : Event handle + */ +static void GetBufferFullEvent(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).MoveFrom(); + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::SetGain service function + * Inputs: + * 0 : Header Code[0x00080040] + * 1 : Gain + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetGain(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + mic_gain = cmd_buff[1] & 0xFF; + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, mic_gain=%u", mic_gain); +} + +/** + * MIC::GetGain service function + * Inputs: + * 0 : Header Code[0x00090000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Gain + */ +static void GetGain(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = mic_gain; + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::SetPower service function + * Inputs: + * 0 : Header Code[0x000A0040] + * 1 : Power (0 = off, 1 = on) + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetPower(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + mic_power = static_cast<bool>(cmd_buff[1] & 0xFF); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, mic_power=%u", mic_power); +} + +/** + * MIC::GetPower service function + * Inputs: + * 0 : Header Code[0x000B0000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Power + */ +static void GetPower(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = mic_power; + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::SetIirFilterMic service function + * Inputs: + * 0 : Header Code[0x000C0042] + * 1 : Size + * 2 : (Size << 4) | 0xA + * 3 : Pointer to IIR Filter Data + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetIirFilterMic(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 size = cmd_buff[1]; + VAddr buffer = cmd_buff[3]; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, size=0x%X, buffer=0x%08X", size, buffer); +} + +/** + * MIC::SetClamp service function + * Inputs: + * 0 : Header Code[0x000D0040] + * 1 : Clamp (0 = don't clamp, non-zero = clamp) + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetClamp(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + clamp = static_cast<bool>(cmd_buff[1] & 0xFF); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, clamp=%u", clamp); +} + +/** + * MIC::GetClamp service function + * Inputs: + * 0 : Header Code[0x000E0000] + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Clamp (0 = don't clamp, non-zero = clamp) + */ +static void GetClamp(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = clamp; + LOG_WARNING(Service_MIC, "(STUBBED) called"); +} + +/** + * MIC::SetAllowShellClosed service function + * Inputs: + * 0 : Header Code[0x000D0040] + * 1 : Sampling allowed while shell closed (0 = disallow, non-zero = allow) + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SetAllowShellClosed(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + allow_shell_closed = static_cast<bool>(cmd_buff[1] & 0xFF); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_MIC, "(STUBBED) called, allow_shell_closed=%u", allow_shell_closed); +} + const Interface::FunctionInfo FunctionTable[] = { - {0x00010042, nullptr, "MapSharedMem"}, - {0x00020000, nullptr, "UnmapSharedMem"}, - {0x00030140, nullptr, "Initialize"}, - {0x00040040, nullptr, "AdjustSampling"}, - {0x00050000, nullptr, "StopSampling"}, - {0x00060000, nullptr, "IsSampling"}, - {0x00070000, nullptr, "GetEventHandle"}, - {0x00080040, nullptr, "SetGain"}, - {0x00090000, nullptr, "GetGain"}, - {0x000A0040, nullptr, "SetPower"}, - {0x000B0000, nullptr, "GetPower"}, - {0x000C0042, nullptr, "size"}, - {0x000D0040, nullptr, "SetClamp"}, - {0x000E0000, nullptr, "GetClamp"}, - {0x000F0040, nullptr, "SetAllowShellClosed"}, - {0x00100040, nullptr, "unknown_input2"}, + {0x00010042, MapSharedMem, "MapSharedMem"}, + {0x00020000, UnmapSharedMem, "UnmapSharedMem"}, + {0x00030140, StartSampling, "StartSampling"}, + {0x00040040, AdjustSampling, "AdjustSampling"}, + {0x00050000, StopSampling, "StopSampling"}, + {0x00060000, IsSampling, "IsSampling"}, + {0x00070000, GetBufferFullEvent, "GetBufferFullEvent"}, + {0x00080040, SetGain, "SetGain"}, + {0x00090000, GetGain, "GetGain"}, + {0x000A0040, SetPower, "SetPower"}, + {0x000B0000, GetPower, "GetPower"}, + {0x000C0042, SetIirFilterMic, "SetIirFilterMic"}, + {0x000D0040, SetClamp, "SetClamp"}, + {0x000E0000, GetClamp, "GetClamp"}, + {0x000F0040, SetAllowShellClosed, "SetAllowShellClosed"}, + {0x00100040, nullptr, "SetClientSDKVersion"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -33,6 +311,18 @@ const Interface::FunctionInfo FunctionTable[] = { Interface::Interface() { Register(FunctionTable); + shared_memory = nullptr; + buffer_full_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "MIC_U::buffer_full_event"); + mic_gain = 0; + mic_power = false; + is_sampling = false; + clamp = false; +} + +Interface::~Interface() { + shared_memory = nullptr; + buffer_full_event = nullptr; } } // namespace diff --git a/src/core/hle/service/mic_u.h b/src/core/hle/service/mic_u.h index dc795d14c..1cff7390e 100644 --- a/src/core/hle/service/mic_u.h +++ b/src/core/hle/service/mic_u.h @@ -16,6 +16,7 @@ namespace MIC_U { class Interface : public Service::Interface { public: Interface(); + ~Interface(); std::string GetPortName() const override { return "mic:u"; diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp index 6e6b63329..cc859c14c 100644 --- a/src/core/hle/service/ptm/ptm.cpp +++ b/src/core/hle/service/ptm/ptm.cpp @@ -128,7 +128,7 @@ void Init() { Service::FS::OpenArchive(Service::FS::ArchiveIdCode::SharedExtSaveData, archive_path); ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!"); - FileSys::Path gamecoin_path("gamecoin.dat"); + FileSys::Path gamecoin_path("/gamecoin.dat"); FileSys::Mode open_mode = {}; open_mode.write_flag.Assign(1); open_mode.create_flag.Assign(1); diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 28cb97d8e..45dedea68 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -29,16 +29,12 @@ namespace GPU { Regs g_regs; -/// True if the current frame was skipped -bool g_skip_frame; /// 268MHz CPU clocks / 60Hz frames per second const u64 frame_ticks = 268123480ull / 60; /// Event id for CoreTiming static int vblank_event; /// Total number of frames drawn static u64 frame_count; -/// True if the last frame was skipped -static bool last_skip_frame; template <typename T> inline void Read(T& var, const u32 raw_addr) { @@ -519,20 +515,7 @@ template void Write<u8>(u32 addr, const u8 data); /// Update hardware static void VBlankCallback(u64 userdata, int cycles_late) { frame_count++; - last_skip_frame = g_skip_frame; - g_skip_frame = (frame_count & Settings::values.frame_skip) != 0; - - // Swap buffers based on the frameskip mode, which is a little bit tricky. When - // a frame is being skipped, nothing is being rendered to the internal framebuffer(s). - // So, we should only swap frames if the last frame was rendered. The rules are: - // - If frameskip == 0 (disabled), always swap buffers - // - If frameskip == 1, swap buffers every other frame (starting from the first frame) - // - If frameskip > 1, swap buffers every frameskip^n frames (starting from the second frame) - if ((((Settings::values.frame_skip != 1) ^ last_skip_frame) && - last_skip_frame != g_skip_frame) || - Settings::values.frame_skip == 0) { - VideoCore::g_renderer->SwapBuffers(); - } + VideoCore::g_renderer->SwapBuffers(); // Signal to GSP that GPU interrupt has occurred // TODO(yuriks): hwtest to determine if PDC0 is for the Top screen and PDC1 for the Sub @@ -579,8 +562,6 @@ void Init() { framebuffer_sub.color_format.Assign(Regs::PixelFormat::RGB8); framebuffer_sub.active_fb = 0; - last_skip_frame = false; - g_skip_frame = false; frame_count = 0; vblank_event = CoreTiming::RegisterEvent("GPU::VBlankCallback", VBlankCallback); diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index 32ddc5697..d53381216 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -316,7 +316,6 @@ ASSERT_REG_POSITION(command_processor_config, 0x00638); static_assert(sizeof(Regs) == 0x1000 * sizeof(u32), "Invalid total size of register set"); extern Regs g_regs; -extern bool g_skip_frame; template <typename T> void Read(T& var, const u32 addr); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 64c388374..65e4bba85 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -45,13 +45,11 @@ struct SpecialRegion { * requires an indexed fetch and a check for NULL. */ struct PageTable { - static const size_t NUM_ENTRIES = 1 << (32 - PAGE_BITS); - /** * Array of memory pointers backing each page. An entry can only be non-null if the * corresponding entry in the `attributes` array is of type `Memory`. */ - std::array<u8*, NUM_ENTRIES> pointers; + std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers; /** * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of @@ -63,13 +61,13 @@ struct PageTable { * Array of fine grained page attributes. If it is set to any value other than `Memory`, then * the corresponding entry in `pointers` MUST be set to null. */ - std::array<PageType, NUM_ENTRIES> attributes; + std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; /** * Indicates the number of externally cached resources touching a page that should be * flushed before the memory is accessed */ - std::array<u8, NUM_ENTRIES> cached_res_count; + std::array<u8, PAGE_TABLE_NUM_ENTRIES> cached_res_count; }; /// Singular page table used for the singleton process @@ -77,6 +75,10 @@ static PageTable main_page_table; /// Currently active page table static PageTable* current_page_table = &main_page_table; +std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers() { + return ¤t_page_table->pointers; +} + static void MapPages(u32 base, u32 size, u8* memory, PageType type) { LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, (base + size) * PAGE_SIZE); @@ -84,7 +86,7 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { u32 end = base + size; while (base != end) { - ASSERT_MSG(base < PageTable::NUM_ENTRIES, "out of range mapping at %08X", base); + ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); // Since pages are unmapped on shutdown after video core is shutdown, the renderer may be // null here diff --git a/src/core/memory.h b/src/core/memory.h index 8fd3080ff..903b58a22 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -4,6 +4,7 @@ #pragma once +#include <array> #include <cstddef> #include <string> #include "common/common_types.h" @@ -17,6 +18,7 @@ namespace Memory { const u32 PAGE_SIZE = 0x1000; const u32 PAGE_MASK = PAGE_SIZE - 1; const int PAGE_BITS = 12; +const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); /// Physical memory regions as seen from the ARM11 enum : PAddr { @@ -166,4 +168,11 @@ void RasterizerFlushRegion(PAddr start, u32 size); * Flushes and invalidates any externally cached rasterizer resources touching the given region. */ void RasterizerFlushAndInvalidateRegion(PAddr start, u32 size); + +/** + * Dynarmic has an optimization to memory accesses when the pointer to the page exists that + * can be used by setting up the current page table as a callback. This function is used to + * retrieve the current page table for that purpose. + */ +std::array<u8*, PAGE_TABLE_NUM_ENTRIES>* GetCurrentPageTablePointers(); } diff --git a/src/core/settings.h b/src/core/settings.h index e931953d7..7470fdbeb 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -78,7 +78,6 @@ struct Values { // Core bool use_cpu_jit; - int frame_skip; // Data Storage bool use_virtual_sd; |