Skip to content

Commit

Permalink
Vfs part 1 Update and DLCs (#62)
Browse files Browse the repository at this point in the history
Co-authored-by: Dzmitry Dubrova <[email protected]>
  • Loading branch information
Ishan09811 and dima-xd authored Jan 24, 2025
1 parent 184ee1a commit d165984
Show file tree
Hide file tree
Showing 40 changed files with 1,642 additions and 355 deletions.
3 changes: 3 additions & 0 deletions app/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,9 @@ add_library(skyline SHARED
${source_DIR}/skyline/vfs/npdm.cpp
${source_DIR}/skyline/vfs/nca.cpp
${source_DIR}/skyline/vfs/ticket.cpp
${source_DIR}/skyline/vfs/cnmt.cpp
${source_DIR}/skyline/vfs/bktr.cpp
${source_DIR}/skyline/vfs/patch_manager.cpp
${source_DIR}/skyline/services/serviceman.cpp
${source_DIR}/skyline/services/base_service.cpp
${source_DIR}/skyline/services/sm/IUserInterface.cpp
Expand Down
17 changes: 16 additions & 1 deletion app/src/main/cpp/emu_jni.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(
jstring romUriJstring,
jint romType,
jint romFd,
jintArray dlcFds,
jint updateFd,
jobject settingsInstance,
jstring publicAppFilesPathJstring,
jstring privateAppFilesPathJstring,
Expand All @@ -87,6 +89,12 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(

auto jvmManager{std::make_shared<skyline::JvmManager>(env, instance)};

jsize dlcArrSize = dlcFds != nullptr ? env->GetArrayLength(dlcFds) : 0;
std::vector<int> dlcFdsVector(dlcArrSize);

if (dlcArrSize > 0)
env->GetIntArrayRegion(dlcFds, 0, dlcArrSize, &dlcFdsVector[0]);

std::shared_ptr<skyline::Settings> settings{std::make_shared<skyline::AndroidSettings>(env, settingsInstance)};

skyline::JniString publicAppFilesPath(env, publicAppFilesPathJstring);
Expand Down Expand Up @@ -126,7 +134,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(

LOGDNF("Launching ROM {}", skyline::JniString(env, romUriJstring));

os->Execute(romFd, static_cast<skyline::loader::RomFormat>(romType));
os->Execute(romFd, dlcFdsVector, updateFd, static_cast<skyline::loader::RomFormat>(romType));
} catch (std::exception &e) {
LOGENF("An uncaught exception has occurred: {}", e.what());
} catch (const skyline::signal::SignalException &e) {
Expand All @@ -144,6 +152,13 @@ extern "C" JNIEXPORT void Java_emu_skyline_EmulationActivity_executeApplication(

skyline::AsyncLogger::Finalize(true);
close(romFd);

close(updateFd);

if (dlcArrSize > 0)
for (int i = 0; i < dlcArrSize; i++)
close(env->GetIntArrayElements(dlcFds, nullptr)[i]);

}

extern "C" JNIEXPORT jboolean Java_emu_skyline_EmulationActivity_stopEmulation(JNIEnv *, jobject, jboolean join) {
Expand Down
17 changes: 15 additions & 2 deletions app/src/main/cpp/loader_jni.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "skyline/vfs/nca.h"
#include "skyline/vfs/os_backing.h"
#include "skyline/vfs/os_filesystem.h"
#include "skyline/vfs/cnmt.h"
#include "skyline/loader/nro.h"
#include "skyline/loader/nso.h"
#include "skyline/loader/nca.h"
Expand Down Expand Up @@ -50,9 +51,12 @@ extern "C" JNIEXPORT jint JNICALL Java_emu_skyline_loader_RomFile_populate(JNIEn
jclass clazz{env->GetObjectClass(thiz)};
jfieldID applicationNameField{env->GetFieldID(clazz, "applicationName", "Ljava/lang/String;")};
jfieldID applicationTitleIdField{env->GetFieldID(clazz, "applicationTitleId", "Ljava/lang/String;")};
jfieldID addOnContentBaseIdField{env->GetFieldID(clazz, "addOnContentBaseId", "Ljava/lang/String;")};
jfieldID applicationAuthorField{env->GetFieldID(clazz, "applicationAuthor", "Ljava/lang/String;")};
jfieldID rawIconField{env->GetFieldID(clazz, "rawIcon", "[B")};
jfieldID applicationVersionField{env->GetFieldID(clazz, "applicationVersion", "Ljava/lang/String;")};
jfieldID romType{env->GetFieldID(clazz, "romTypeInt", "I")};
jfieldID parentTitleId{env->GetFieldID(clazz, "parentTitleId", "Ljava/lang/String;")};

if (loader->nacp) {
auto language{skyline::language::GetApplicationLanguage(static_cast<skyline::language::SystemLanguage>(systemLanguage))};
Expand All @@ -62,6 +66,7 @@ extern "C" JNIEXPORT jint JNICALL Java_emu_skyline_loader_RomFile_populate(JNIEn
env->SetObjectField(thiz, applicationNameField, env->NewStringUTF(loader->nacp->GetApplicationName(language).c_str()));
env->SetObjectField(thiz, applicationVersionField, env->NewStringUTF(loader->nacp->GetApplicationVersion().c_str()));
env->SetObjectField(thiz, applicationTitleIdField, env->NewStringUTF(loader->nacp->GetSaveDataOwnerId().c_str()));
env->SetObjectField(thiz, addOnContentBaseIdField, env->NewStringUTF(loader->nacp->GetAddOnContentBaseId().c_str()));
env->SetObjectField(thiz, applicationAuthorField, env->NewStringUTF(loader->nacp->GetApplicationPublisher(language).c_str()));

auto icon{loader->GetIcon(language)};
Expand All @@ -70,6 +75,14 @@ extern "C" JNIEXPORT jint JNICALL Java_emu_skyline_loader_RomFile_populate(JNIEn
env->SetObjectField(thiz, rawIconField, iconByteArray);
}

if (loader->cnmt) {
auto contentMetaType{loader->cnmt->GetContentMetaType()};
env->SetIntField(thiz, romType, static_cast<skyline::u8>(contentMetaType));

if (contentMetaType != skyline::vfs::ContentMetaType::Application)
env->SetObjectField(thiz, parentTitleId, env->NewStringUTF(loader->cnmt->GetParentTitleId().c_str()));
}

return static_cast<jint>(skyline::loader::LoaderResult::Success);
}

Expand Down Expand Up @@ -98,7 +111,7 @@ extern "C" JNIEXPORT jstring Java_emu_skyline_preference_FirmwareImportPreferenc
std::shared_ptr<skyline::vfs::Backing> backing{systemArchivesFileSystem->OpenFile(entry.name)};
auto nca{skyline::vfs::NCA(backing, keyStore)};

if (nca.header.programId == systemVersionProgramId && nca.romFs != nullptr) {
if (nca.header.titleId == systemVersionProgramId && nca.romFs != nullptr) {
auto controlRomFs{std::make_shared<skyline::vfs::RomFileSystem>(nca.romFs)};
auto file{controlRomFs->OpenFile("file")};
SystemVersion systemVersion;
Expand Down Expand Up @@ -165,7 +178,7 @@ extern "C" JNIEXPORT void Java_emu_skyline_preference_FirmwareImportPreference_e
std::shared_ptr<skyline::vfs::Backing> backing{systemArchivesFileSystem->OpenFile(entry.name)};
auto nca{skyline::vfs::NCA(backing, keyStore)};

if (nca.header.programId >= firstFontProgramId && nca.header.programId <= lastFontProgramId && nca.romFs != nullptr) {
if (nca.header.titleId >= firstFontProgramId && nca.header.titleId <= lastFontProgramId && nca.romFs != nullptr) {
auto controlRomFs{std::make_shared<skyline::vfs::RomFileSystem>(nca.romFs)};

for (auto fileEntry = controlRomFs->fileMap.begin(); fileEntry != controlRomFs->fileMap.end(); fileEntry++) {
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/cpp/skyline/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ namespace skyline {
std::shared_ptr<JvmManager> jvm;
std::shared_ptr<Settings> settings;
std::shared_ptr<loader::Loader> loader;
std::vector<std::shared_ptr<loader::Loader>> dlcLoaders;
std::shared_ptr<loader::Loader> updateLoader;
std::shared_ptr<nce::NCE> nce;
std::shared_ptr<jit::Jit32> jit32;
std::shared_ptr<kernel::type::KProcess> process{};
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/cpp/skyline/loader/loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#include <linux/elf.h>
#include <vfs/nacp.h>
#include <vfs/cnmt.h>
#include <vfs/nca.h>
#include <common/signal.h>
#include "executable.h"

Expand Down Expand Up @@ -39,6 +41,9 @@ namespace skyline::loader {
MissingTitleKey,
MissingTitleKek,
MissingKeyArea,

ErrorSparseNCA,
ErrorCompressedNCA,
};

/**
Expand Down Expand Up @@ -92,6 +97,10 @@ namespace skyline::loader {
ExecutableLoadInfo LoadExecutable(const std::shared_ptr<kernel::type::KProcess> &process, const DeviceState &state, Executable &executable, size_t offset = 0, const std::string &name = {}, bool dynamicallyLinked = false);

std::optional<vfs::NACP> nacp;
std::optional<vfs::CNMT> cnmt;
std::optional<vfs::NCA> programNca; //!< The main program NCA within the NSP
std::optional<vfs::NCA> controlNca; //!< The main control NCA within the NSP
std::optional<vfs::NCA> publicNca;
std::shared_ptr<vfs::Backing> romFs;

virtual ~Loader() = default;
Expand Down
28 changes: 21 additions & 7 deletions app/src/main/cpp/skyline/loader/nsp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <vfs/ticket.h>
#include "nca.h"
#include "nsp.h"
#include "vfs/patch_manager.h"

namespace skyline::loader {
static void ExtractTickets(const std::shared_ptr<vfs::PartitionFileSystem>& dir, const std::shared_ptr<crypto::KeyStore> &keyStore) {
Expand Down Expand Up @@ -33,26 +34,39 @@ namespace skyline::loader {
try {
auto nca{vfs::NCA(nsp->OpenFile(entry.name), keyStore)};

if (nca.contentType == vfs::NcaContentType::Program && nca.romFs != nullptr && nca.exeFs != nullptr)
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)
else if (nca.contentType == vfs::NCAContentType::Control && nca.romFs != nullptr)
controlNca = std::move(nca);
else if (nca.contentType == vfs::NCAContentType::Meta)
metaNca = std::move(nca);
else if (nca.contentType == vfs::NCAContentType::PublicData)
publicNca = std::move(nca);
} catch (const loader_exception &e) {
throw loader_exception(e.error);
} catch (const std::exception &e) {
continue;
}
}

if (!programNca || !controlNca)
throw exception("Incomplete NSP file");
if (programNca)
romFs = programNca->romFs;

romFs = programNca->romFs;
controlRomFs = std::make_shared<vfs::RomFileSystem>(controlNca->romFs);
nacp.emplace(controlRomFs->OpenFile("control.nacp"));
if (controlNca) {
controlRomFs = std::make_shared<vfs::RomFileSystem>(controlNca->romFs);
nacp.emplace(controlRomFs->OpenFile("control.nacp"));
}

if (metaNca)
cnmt = vfs::CNMT(metaNca->cnmt);
}

void *NspLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> &process, const DeviceState &state) {
if (state.updateLoader) {
auto patchManager{std::make_shared<vfs::PatchManager>()};
programNca->exeFs = patchManager->PatchExeFS(state, programNca->exeFs);
}

process->npdm = vfs::NPDM(programNca->exeFs->OpenFile("main.npdm"));
return NcaLoader::LoadExeFs(this, programNca->exeFs, process, state);
}
Expand Down
3 changes: 1 addition & 2 deletions app/src/main/cpp/skyline/loader/nsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ namespace skyline::loader {
private:
std::shared_ptr<vfs::PartitionFileSystem> nsp; //!< A shared pointer to the NSP's PFS0
std::shared_ptr<vfs::RomFileSystem> controlRomFs; //!< A shared 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
std::optional<vfs::NCA> metaNca; //!< The main meta NCA within the NSP

public:
NspLoader(const std::shared_ptr<vfs::Backing> &backing, const std::shared_ptr<crypto::KeyStore> &keyStore);
Expand Down
20 changes: 13 additions & 7 deletions app/src/main/cpp/skyline/loader/xci.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,12 @@ namespace skyline::loader {
try {
auto nca{vfs::NCA(secure->OpenFile(entry.name), keyStore, true)};

if (nca.contentType == vfs::NcaContentType::Program && nca.romFs != nullptr && nca.exeFs != nullptr)
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)
else if (nca.contentType == vfs::NCAContentType::Control && nca.romFs != nullptr)
controlNca = std::move(nca);
else if (nca.contentType == vfs::NCAContentType::Meta)
metaNca = std::move(nca);
} catch (const loader_exception &e) {
throw loader_exception(e.error);
} catch (const std::exception &e) {
Expand All @@ -52,12 +54,16 @@ namespace skyline::loader {
throw exception("Corrupted secure partition");
}

if (!programNca || !controlNca)
throw exception("Incomplete XCI file");
if (programNca)
romFs = programNca->romFs;

romFs = programNca->romFs;
controlRomFs = std::make_shared<vfs::RomFileSystem>(controlNca->romFs);
nacp.emplace(controlRomFs->OpenFile("control.nacp"));
if (controlNca) {
controlRomFs = std::make_shared<vfs::RomFileSystem>(controlNca->romFs);
nacp.emplace(controlRomFs->OpenFile("control.nacp"));
}

if (metaNca)
cnmt = vfs::CNMT(metaNca->cnmt);
}

void *XciLoader::LoadProcessData(const std::shared_ptr<kernel::type::KProcess> &process, const DeviceState &state) {
Expand Down
1 change: 1 addition & 0 deletions app/src/main/cpp/skyline/loader/xci.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ namespace skyline::loader {
std::shared_ptr<vfs::RomFileSystem> controlRomFs; //!< A shared pointer to the control NCA's RomFS
std::optional<vfs::NCA> programNca; //!< The main program NCA within the secure partition
std::optional<vfs::NCA> controlNca; //!< The main control NCA within the secure partition
std::optional<vfs::NCA> metaNca; //!< The main meta NCA within the secure partition

public:
XciLoader(const std::shared_ptr<vfs::Backing> &backing, const std::shared_ptr<crypto::KeyStore> &keyStore);
Expand Down
56 changes: 37 additions & 19 deletions app/src/main/cpp/skyline/os.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,26 +34,18 @@ namespace skyline::kernel {

bool isJitEnabled = false;

void OS::Execute(int romFd, loader::RomFormat romType) {
void OS::Execute(int romFd, std::vector<int> dlcFds, int updateFd, loader::RomFormat romType) {
auto romFile{std::make_shared<vfs::OsBacking>(romFd)};
auto keyStore{std::make_shared<crypto::KeyStore>(privateAppFilesPath + "keys/")};
keyStore = std::make_shared<crypto::KeyStore>(privateAppFilesPath + "keys/");

state.loader = [&]() -> std::shared_ptr<loader::Loader> {
switch (romType) {
case loader::RomFormat::NRO:
return std::make_shared<loader::NroLoader>(std::move(romFile));
case loader::RomFormat::NSO:
return std::make_shared<loader::NsoLoader>(std::move(romFile));
case loader::RomFormat::NCA:
return std::make_shared<loader::NcaLoader>(std::move(romFile), std::move(keyStore));
case loader::RomFormat::NSP:
return std::make_shared<loader::NspLoader>(romFile, keyStore);
case loader::RomFormat::XCI:
return std::make_shared<loader::XciLoader>(romFile, keyStore);
default:
throw exception("Unsupported ROM extension.");
}
}();
state.loader = GetLoader(romFd, keyStore, romType);

if (updateFd > 0)
state.updateLoader = GetLoader(updateFd, keyStore, romType);

if (dlcFds.size() > 0)
for (int fd : dlcFds)
state.dlcLoaders.push_back(GetLoader(fd, keyStore, romType));

state.gpu->Initialise();

Expand All @@ -68,7 +60,15 @@ namespace skyline::kernel {
name = nacp->GetApplicationName(nacp->GetFirstSupportedTitleLanguage());
if (publisher.empty())
publisher = nacp->GetApplicationPublisher(nacp->GetFirstSupportedTitleLanguage());
LOGINF(R"(Starting "{}" ({}) v{} by "{}")", name, nacp->GetSaveDataOwnerId(), nacp->GetApplicationVersion(), publisher);

if (state.updateLoader)
LOGINF("Applied update v{}", state.updateLoader->nacp->GetApplicationVersion());

if (state.dlcLoaders.size() > 0)
for (auto &loader : state.dlcLoaders)
LOGINF("Applied DLC {}", loader->cnmt->GetTitleId());

LOGINF(R"(Starting "{}" ({}) v{} by "{}")", name, nacp->GetSaveDataOwnerId(), state.updateLoader ? state.updateLoader->nacp->GetApplicationVersion() : nacp->GetApplicationVersion(), publisher);
}

// Scheduler retrieves information from the NPDM of the process so it needs to be initialized after the process is created
Expand All @@ -89,4 +89,22 @@ namespace skyline::kernel {
process->Kill(true, true, true);
}
}

std::shared_ptr<loader::Loader> OS::GetLoader(int fd, std::shared_ptr<crypto::KeyStore> keyStore, loader::RomFormat romType) {
auto file{std::make_shared<vfs::OsBacking>(fd)};
switch (romType) {
case loader::RomFormat::NRO:
return std::make_shared<loader::NroLoader>(std::move(file));
case loader::RomFormat::NSO:
return std::make_shared<loader::NsoLoader>(std::move(file));
case loader::RomFormat::NCA:
return std::make_shared<loader::NcaLoader>(std::move(file), std::move(keyStore));
case loader::RomFormat::NSP:
return std::make_shared<loader::NspLoader>(file, keyStore);
case loader::RomFormat::XCI:
return std::make_shared<loader::XciLoader>(file, keyStore);
default:
throw exception("Unsupported ROM extension.");
}
}
}
8 changes: 7 additions & 1 deletion app/src/main/cpp/skyline/os.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#pragma once

#include <crypto/key_store.h>
#include <common/language.h>
#include "vfs/filesystem.h"
#include "loader/loader.h"
Expand All @@ -21,6 +22,7 @@ namespace skyline::kernel {
std::string privateAppFilesPath; //!< The full path to the app's private files directory
std::string deviceTimeZone; //!< The timezone name (e.g. Europe/London)
std::shared_ptr<vfs::FileSystem> assetFileSystem; //!< A filesystem to be used for accessing emulator assets (like tzdata)
std::shared_ptr<crypto::KeyStore> keyStore;
DeviceState state;
service::ServiceManager serviceManager;

Expand All @@ -41,8 +43,12 @@ namespace skyline::kernel {
/**
* @brief Execute a particular ROM file
* @param romFd A FD to the ROM file to execute
* @param dlcFds An array of FD to the DLC files
* @param updateFd A FD to the Update file
* @param romType The type of the ROM file
*/
void Execute(int romFd, loader::RomFormat romType);
void Execute(int romFd, std::vector<int> dlcFds, int updateFd, loader::RomFormat romType);

std::shared_ptr<loader::Loader> GetLoader(int fd, std::shared_ptr<crypto::KeyStore> keyStore, loader::RomFormat romType);
};
}
Loading

0 comments on commit d165984

Please sign in to comment.