summaryrefslogtreecommitdiffstats
path: root/src/core/loader/deconstructed_rom_directory.cpp
blob: b01b2caf63d1426bd528fced359459d98f002e4b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.

#include <cinttypes>
#include "common/common_funcs.h"
#include "common/common_paths.h"
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/romfs_factory.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/nso.h"
#include "core/memory.h"

namespace Loader {

static std::string FindRomFS(const std::string& directory) {
    std::string filepath_romfs;
    const auto callback = [&filepath_romfs](unsigned*, const std::string& directory,
                                            const std::string& virtual_name) -> bool {
        const std::string physical_name = directory + virtual_name;
        if (FileUtil::IsDirectory(physical_name)) {
            // Skip directories
            return true;
        }

        // Verify extension
        const std::string extension = physical_name.substr(physical_name.find_last_of(".") + 1);
        if (Common::ToLower(extension) != "romfs") {
            return true;
        }

        // Found it - we are done
        filepath_romfs = std::move(physical_name);
        return false;
    };

    // Search the specified directory recursively, looking for the first .romfs file, which will
    // be used for the RomFS
    FileUtil::ForeachDirectoryEntry(nullptr, directory, callback);

    return filepath_romfs;
}

AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file,
                                                                         std::string filepath)
    : AppLoader(std::move(file)), filepath(std::move(filepath)) {}

FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& file,
                                                           const std::string& filepath) {
    bool is_main_found{};
    bool is_npdm_found{};
    bool is_rtld_found{};
    bool is_sdk_found{};

    const auto callback = [&](unsigned* num_entries_out, const std::string& directory,
                              const std::string& virtual_name) -> bool {
        // Skip directories
        std::string physical_name = directory + virtual_name;
        if (FileUtil::IsDirectory(physical_name)) {
            return true;
        }

        // Verify filename
        if (Common::ToLower(virtual_name) == "main") {
            is_main_found = true;
        } else if (Common::ToLower(virtual_name) == "main.npdm") {
            is_npdm_found = true;
            return true;
        } else if (Common::ToLower(virtual_name) == "rtld") {
            is_rtld_found = true;
        } else if (Common::ToLower(virtual_name) == "sdk") {
            is_sdk_found = true;
        } else {
            // Continue searching
            return true;
        }

        // Verify file is an NSO
        FileUtil::IOFile file(physical_name, "rb");
        if (AppLoader_NSO::IdentifyType(file, physical_name) != FileType::NSO) {
            return false;
        }

        // We are done if we've found and verified all required NSOs
        return !(is_main_found && is_npdm_found && is_rtld_found && is_sdk_found);
    };

    // Search the directory recursively, looking for the required modules
    const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
    FileUtil::ForeachDirectoryEntry(nullptr, directory, callback);

    if (is_main_found && is_npdm_found && is_rtld_found && is_sdk_found) {
        return FileType::DeconstructedRomDirectory;
    }

    return FileType::Error;
}

ResultStatus AppLoader_DeconstructedRomDirectory::Load(
    Kernel::SharedPtr<Kernel::Process>& process) {
    if (is_loaded) {
        return ResultStatus::ErrorAlreadyLoaded;
    }
    if (!file.IsOpen()) {
        return ResultStatus::Error;
    }

    const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP;
    const std::string npdm_path = directory + DIR_SEP + "main.npdm";

    ResultStatus result = metadata.Load(npdm_path);
    if (result != ResultStatus::Success) {
        return result;
    }
    metadata.Print();

    const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
    if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) {
        return ResultStatus::ErrorUnsupportedArch;
    }

    // Load NSO modules
    VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
    for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
                               "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
        const std::string path = directory + DIR_SEP + module;
        const VAddr load_addr = next_load_addr;
        next_load_addr = AppLoader_NSO::LoadModule(path, load_addr);
        if (next_load_addr) {
            NGLOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
        } else {
            next_load_addr = load_addr;
        }
    }

    process->program_id = metadata.GetTitleID();
    process->svc_access_mask.set();
    process->address_mappings = default_address_mappings;
    process->resource_limit =
        Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
    process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(),
                 metadata.GetMainThreadStackSize());

    // Find the RomFS by searching for a ".romfs" file in this directory
    filepath_romfs = FindRomFS(directory);

    // Register the RomFS if a ".romfs" file was found
    if (!filepath_romfs.empty()) {
        Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this),
                                                Service::FileSystem::Type::RomFS);
    }

    is_loaded = true;
    return ResultStatus::Success;
}

ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(
    std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) {

    if (filepath_romfs.empty()) {
        NGLOG_DEBUG(Loader, "No RomFS available");
        return ResultStatus::ErrorNotUsed;
    }

    // We reopen the file, to allow its position to be independent
    romfs_file = std::make_shared<FileUtil::IOFile>(filepath_romfs, "rb");
    if (!romfs_file->IsOpen()) {
        return ResultStatus::Error;
    }

    offset = 0;
    size = romfs_file->GetSize();

    NGLOG_DEBUG(Loader, "RomFS offset:           0x{:016X}", offset);
    NGLOG_DEBUG(Loader, "RomFS size:             0x{:016X}", size);

    // Reset read pointer
    file.Seek(0, SEEK_SET);

    return ResultStatus::Success;
}

} // namespace Loader