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.
This commit is contained in:
Billy Laws 2020-06-29 21:19:32 +01:00 committed by ◱ PixelyIon
parent a5513bd7e6
commit a53d6266c7
6 changed files with 100 additions and 0 deletions

View File

@ -44,6 +44,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/loader/nro.cpp ${source_DIR}/skyline/loader/nro.cpp
${source_DIR}/skyline/loader/nso.cpp ${source_DIR}/skyline/loader/nso.cpp
${source_DIR}/skyline/loader/nca.cpp ${source_DIR}/skyline/loader/nca.cpp
${source_DIR}/skyline/loader/nsp.cpp
${source_DIR}/skyline/kernel/memory.cpp ${source_DIR}/skyline/kernel/memory.cpp
${source_DIR}/skyline/kernel/ipc.cpp ${source_DIR}/skyline/kernel/ipc.cpp
${source_DIR}/skyline/kernel/svc.cpp ${source_DIR}/skyline/kernel/svc.cpp

View File

@ -5,6 +5,7 @@
#include "skyline/loader/nro.h" #include "skyline/loader/nro.h"
#include "skyline/loader/nso.h" #include "skyline/loader/nso.h"
#include "skyline/loader/nca.h" #include "skyline/loader/nca.h"
#include "skyline/loader/nsp.h"
#include "skyline/jvm.h" #include "skyline/jvm.h"
extern "C" JNIEXPORT jlong JNICALL Java_emu_skyline_loader_RomFile_initialize(JNIEnv *env, jobject thiz, jint jformat, jint fd) { 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<jlong>(new skyline::loader::NsoLoader(backing)); return reinterpret_cast<jlong>(new skyline::loader::NsoLoader(backing));
case skyline::loader::RomFormat::NCA: case skyline::loader::RomFormat::NCA:
return reinterpret_cast<jlong>(new skyline::loader::NcaLoader(backing)); return reinterpret_cast<jlong>(new skyline::loader::NcaLoader(backing));
case skyline::loader::RomFormat::NSP:
return reinterpret_cast<jlong>(new skyline::loader::NspLoader(backing));
default: default:
return 0; return 0;
} }

View File

@ -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<vfs::Backing> &backing) : nsp(std::make_shared<vfs::PartitionFileSystem>(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<vfs::RomFileSystem>(controlNca->romFs);
nacp = std::make_shared<vfs::NACP>(controlRomFs->OpenFile("control.nacp"));
}
void NspLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state) {
NcaLoader::LoadExeFs(programNca->exeFs, process, state);
}
std::vector<u8> NspLoader::GetIcon() {
if (romFs == nullptr)
return std::vector<u8>();
auto root = controlRomFs->OpenDirectory("", {false, true});
std::shared_ptr<vfs::Backing> 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<u8>();
std::vector<u8> buffer(icon->size);
icon->Read(buffer.data(), 0, icon->size);
return buffer;
}
}

View File

@ -0,0 +1,30 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common.h>
#include <vfs/nca.h>
#include <vfs/rom_filesystem.h>
#include <vfs/partition_filesystem.h>
#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<vfs::PartitionFileSystem> nsp; //!< A shared pointer to the NSP's PFS0
std::shared_ptr<vfs::RomFileSystem> controlRomFs; //!< A pointer to the control NCA's RomFS
std::optional<vfs::NCA> programNca; //!< The main program NCA within the NSP
std::optional<vfs::NCA> controlNca; //!< The main control NCA within the NSP
public:
NspLoader(const std::shared_ptr<vfs::Backing> &backing);
std::vector<u8> GetIcon();
void LoadProcessData(const std::shared_ptr<kernel::type::KProcess> process, const DeviceState &state);
};
}

View File

@ -5,6 +5,7 @@
#include "loader/nro.h" #include "loader/nro.h"
#include "loader/nso.h" #include "loader/nso.h"
#include "loader/nca.h" #include "loader/nca.h"
#include "loader/nsp.h"
#include "nce/guest.h" #include "nce/guest.h"
#include "os.h" #include "os.h"
@ -20,6 +21,8 @@ namespace skyline::kernel {
state.loader = std::make_shared<loader::NsoLoader>(romFile); state.loader = std::make_shared<loader::NsoLoader>(romFile);
} else if (romType == loader::RomFormat::NCA) { } else if (romType == loader::RomFormat::NCA) {
state.loader = std::make_shared<loader::NcaLoader>(romFile); state.loader = std::make_shared<loader::NcaLoader>(romFile);
} else if (romType == loader::RomFormat::NSP) {
state.loader = std::make_shared<loader::NspLoader>(romFile);
} else { } else {
throw exception("Unsupported ROM extension."); throw exception("Unsupported ROM extension.");
} }

View File

@ -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", "")))!!) 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("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("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 { runOnUiThread {
if (!foundRoms) if (!foundRoms)