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)