From a53d6266c73a950b4b65c83be7064a2ec73b91d7 Mon Sep 17 00:00:00 2001 From: Billy Laws Date: Mon, 29 Jun 2020 21:19:32 +0100 Subject: [PATCH] Implement a basic NSP loader An NSP (Nintendo Submission Package) is effectively a PFS0 containing NCAs, there are also tickets and a CNMT file which contains metadata about updates. The current implementation is very basic and only support Control and Program NCAs which is enough for loading games. Support for updates and dlc will be added at a later date. --- app/CMakeLists.txt | 1 + app/src/main/cpp/loader_jni.cpp | 3 + app/src/main/cpp/skyline/loader/nsp.cpp | 62 +++++++++++++++++++ app/src/main/cpp/skyline/loader/nsp.h | 30 +++++++++ app/src/main/cpp/skyline/os.cpp | 3 + app/src/main/java/emu/skyline/MainActivity.kt | 1 + 6 files changed, 100 insertions(+) create mode 100644 app/src/main/cpp/skyline/loader/nsp.cpp create mode 100644 app/src/main/cpp/skyline/loader/nsp.h diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 15d65623..b5981259 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -44,6 +44,7 @@ add_library(skyline SHARED ${source_DIR}/skyline/loader/nro.cpp ${source_DIR}/skyline/loader/nso.cpp ${source_DIR}/skyline/loader/nca.cpp + ${source_DIR}/skyline/loader/nsp.cpp ${source_DIR}/skyline/kernel/memory.cpp ${source_DIR}/skyline/kernel/ipc.cpp ${source_DIR}/skyline/kernel/svc.cpp diff --git a/app/src/main/cpp/loader_jni.cpp b/app/src/main/cpp/loader_jni.cpp index c1ce0f6a..6e6bb942 100644 --- a/app/src/main/cpp/loader_jni.cpp +++ b/app/src/main/cpp/loader_jni.cpp @@ -5,6 +5,7 @@ #include "skyline/loader/nro.h" #include "skyline/loader/nso.h" #include "skyline/loader/nca.h" +#include "skyline/loader/nsp.h" #include "skyline/jvm.h" extern "C" JNIEXPORT jlong JNICALL Java_emu_skyline_loader_RomFile_initialize(JNIEnv *env, jobject thiz, jint jformat, jint fd) { @@ -20,6 +21,8 @@ extern "C" JNIEXPORT jlong JNICALL Java_emu_skyline_loader_RomFile_initialize(JN return reinterpret_cast(new skyline::loader::NsoLoader(backing)); case skyline::loader::RomFormat::NCA: return reinterpret_cast(new skyline::loader::NcaLoader(backing)); + case skyline::loader::RomFormat::NSP: + return reinterpret_cast(new skyline::loader::NspLoader(backing)); default: return 0; } diff --git a/app/src/main/cpp/skyline/loader/nsp.cpp b/app/src/main/cpp/skyline/loader/nsp.cpp new file mode 100644 index 00000000..c33715b8 --- /dev/null +++ b/app/src/main/cpp/skyline/loader/nsp.cpp @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include "nca.h" +#include "nsp.h" + +namespace skyline::loader { + NspLoader::NspLoader(const std::shared_ptr &backing) : nsp(std::make_shared(backing)) { + auto root = nsp->OpenDirectory("", {false, true}); + + for (const auto &entry : root->Read()) { + if (entry.name.substr(entry.name.find_last_of(".") + 1) != "nca") + continue; + + try { + auto nca = vfs::NCA(nsp->OpenFile(entry.name)); + + if (nca.contentType == vfs::NcaContentType::Program && nca.romFs != nullptr && nca.exeFs != nullptr) + programNca = std::move(nca); + else if (nca.contentType == vfs::NcaContentType::Control && nca.romFs != nullptr) + controlNca = std::move(nca); + } catch (const std::exception &e) { + continue; + } + } + + if (!programNca.has_value() || !controlNca.has_value()) + throw exception("Incomplete NSP file"); + + romFs = programNca->romFs; + controlRomFs = std::make_shared(controlNca->romFs); + nacp = std::make_shared(controlRomFs->OpenFile("control.nacp")); + } + + void NspLoader::LoadProcessData(const std::shared_ptr process, const DeviceState &state) { + NcaLoader::LoadExeFs(programNca->exeFs, process, state); + } + + std::vector NspLoader::GetIcon() { + if (romFs == nullptr) + return std::vector(); + + auto root = controlRomFs->OpenDirectory("", {false, true}); + std::shared_ptr icon; + + // Use the first icon file available + for (const auto &entry : root->Read()) { + if (entry.name.rfind("icon") == 0) { + icon = controlRomFs->OpenFile(entry.name); + break; + } + } + + if (icon == nullptr) + return std::vector(); + + std::vector buffer(icon->size); + + icon->Read(buffer.data(), 0, icon->size); + return buffer; + } +} diff --git a/app/src/main/cpp/skyline/loader/nsp.h b/app/src/main/cpp/skyline/loader/nsp.h new file mode 100644 index 00000000..afa21744 --- /dev/null +++ b/app/src/main/cpp/skyline/loader/nsp.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#pragma once + +#include +#include +#include +#include +#include "loader.h" + +namespace skyline::loader { + /** + * @brief The NspLoader class consolidates all the data in an NSP providing a simple way to load an application and access its metadata (https://switchbrew.org/wiki/NCA_Format#PFS0) + */ + class NspLoader : public Loader { + private: + std::shared_ptr nsp; //!< A shared pointer to the NSP's PFS0 + std::shared_ptr controlRomFs; //!< A pointer to the control NCA's RomFS + std::optional programNca; //!< The main program NCA within the NSP + std::optional controlNca; //!< The main control NCA within the NSP + + public: + NspLoader(const std::shared_ptr &backing); + + std::vector GetIcon(); + + void LoadProcessData(const std::shared_ptr process, const DeviceState &state); + }; +} diff --git a/app/src/main/cpp/skyline/os.cpp b/app/src/main/cpp/skyline/os.cpp index 6cbb662a..1e7cee29 100644 --- a/app/src/main/cpp/skyline/os.cpp +++ b/app/src/main/cpp/skyline/os.cpp @@ -5,6 +5,7 @@ #include "loader/nro.h" #include "loader/nso.h" #include "loader/nca.h" +#include "loader/nsp.h" #include "nce/guest.h" #include "os.h" @@ -20,6 +21,8 @@ namespace skyline::kernel { state.loader = std::make_shared(romFile); } else if (romType == loader::RomFormat::NCA) { state.loader = std::make_shared(romFile); + } else if (romType == loader::RomFormat::NSP) { + state.loader = std::make_shared(romFile); } else { throw exception("Unsupported ROM extension."); } diff --git a/app/src/main/java/emu/skyline/MainActivity.kt b/app/src/main/java/emu/skyline/MainActivity.kt index 239ce437..73f2ce4c 100644 --- a/app/src/main/java/emu/skyline/MainActivity.kt +++ b/app/src/main/java/emu/skyline/MainActivity.kt @@ -114,6 +114,7 @@ class MainActivity : AppCompatActivity(), View.OnClickListener, View.OnLongClick var foundRoms = addEntries("nro", RomFormat.NRO, DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!) foundRoms = foundRoms or addEntries("nso", RomFormat.NSO, DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!) foundRoms = foundRoms or addEntries("nca", RomFormat.NCA, DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!) + foundRoms = foundRoms or addEntries("nsp", RomFormat.NSP, DocumentFile.fromTreeUri(this, Uri.parse(sharedPreferences.getString("search_location", "")))!!) runOnUiThread { if (!foundRoms)