mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-11-04 07:38:47 +00:00 
			
		
		
		
	[WIP] NCCHContainer: support for partitions if container is NCSD (#5345)
* GetProgramLaunchInfo: improve to for 3ds files * NCSD: allow to load other partitions * fix typo * Update src/core/hle/service/fs/fs_user.cpp Co-authored-by: Valentin Vanelslande <vvanelslandedev@gmail.com> * Update src/core/hle/service/fs/fs_user.cpp Co-authored-by: Valentin Vanelslande <vvanelslandedev@gmail.com> Co-authored-by: Marshall Mohror <mohror64@gmail.com> Co-authored-by: Valentin Vanelslande <vvanelslandedev@gmail.com>
This commit is contained in:
		
							parent
							
								
									897e473da4
								
							
						
					
					
						commit
						1722701c07
					
				
					 10 changed files with 226 additions and 34 deletions
				
			
		| 
						 | 
				
			
			@ -31,6 +31,7 @@
 | 
			
		|||
#include "core/hle/service/am/am_sys.h"
 | 
			
		||||
#include "core/hle/service/am/am_u.h"
 | 
			
		||||
#include "core/hle/service/fs/archive.h"
 | 
			
		||||
#include "core/hle/service/fs/fs_user.h"
 | 
			
		||||
#include "core/loader/loader.h"
 | 
			
		||||
#include "core/loader/smdh.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -465,16 +466,19 @@ std::string GetTitleMetadataPath(Service::FS::MediaType media_type, u64 tid, boo
 | 
			
		|||
    return content_path + fmt::format("{:08x}.tmd", (update ? update_id : base_id));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetTitleContentPath(FS::MediaType media_type, u64 tid, std::size_t index, bool update) {
 | 
			
		||||
    std::string content_path = GetTitlePath(media_type, tid) + "content/";
 | 
			
		||||
std::string GetTitleContentPath(Service::FS::MediaType media_type, u64 tid, std::size_t index,
 | 
			
		||||
                                bool update) {
 | 
			
		||||
 | 
			
		||||
    if (media_type == FS::MediaType::GameCard) {
 | 
			
		||||
        // TODO(shinyquagsire23): get current app file if TID matches?
 | 
			
		||||
        LOG_ERROR(Service_AM, "Request for gamecard partition {} content path unimplemented!",
 | 
			
		||||
                  static_cast<u32>(index));
 | 
			
		||||
        return "";
 | 
			
		||||
    if (media_type == Service::FS::MediaType::GameCard) {
 | 
			
		||||
        // TODO(B3N30): check if TID matches
 | 
			
		||||
        auto fs_user =
 | 
			
		||||
            Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>(
 | 
			
		||||
                "fs:USER");
 | 
			
		||||
        return fs_user->GetCurrentGamecardPath();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string content_path = GetTitlePath(media_type, tid) + "content/";
 | 
			
		||||
 | 
			
		||||
    std::string tmd_path = GetTitleMetadataPath(media_type, tid, update);
 | 
			
		||||
 | 
			
		||||
    u32 content_id = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -509,9 +513,11 @@ std::string GetTitlePath(Service::FS::MediaType media_type, u64 tid) {
 | 
			
		|||
        return fmt::format("{}{:08x}/{:08x}/", GetMediaTitlePath(media_type), high, low);
 | 
			
		||||
 | 
			
		||||
    if (media_type == Service::FS::MediaType::GameCard) {
 | 
			
		||||
        // TODO(shinyquagsire23): get current app path if TID matches?
 | 
			
		||||
        LOG_ERROR(Service_AM, "Request for gamecard title path unimplemented!");
 | 
			
		||||
        return "";
 | 
			
		||||
        // TODO(B3N30): check if TID matches
 | 
			
		||||
        auto fs_user =
 | 
			
		||||
            Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>(
 | 
			
		||||
                "fs:USER");
 | 
			
		||||
        return fs_user->GetCurrentGamecardPath();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return "";
 | 
			
		||||
| 
						 | 
				
			
			@ -528,9 +534,11 @@ std::string GetMediaTitlePath(Service::FS::MediaType media_type) {
 | 
			
		|||
                           SDCARD_ID);
 | 
			
		||||
 | 
			
		||||
    if (media_type == Service::FS::MediaType::GameCard) {
 | 
			
		||||
        // TODO(shinyquagsire23): get current app parent folder if TID matches?
 | 
			
		||||
        LOG_ERROR(Service_AM, "Request for gamecard parent path unimplemented!");
 | 
			
		||||
        return "";
 | 
			
		||||
        // TODO(B3N30): check if TID matchess
 | 
			
		||||
        auto fs_user =
 | 
			
		||||
            Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>(
 | 
			
		||||
                "fs:USER");
 | 
			
		||||
        return fs_user->GetCurrentGamecardPath();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return "";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,16 @@
 | 
			
		|||
 | 
			
		||||
namespace Service::FS {
 | 
			
		||||
 | 
			
		||||
MediaType GetMediaTypeFromPath(std::string_view path) {
 | 
			
		||||
    if (path.rfind(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), 0) == 0) {
 | 
			
		||||
        return MediaType::NAND;
 | 
			
		||||
    }
 | 
			
		||||
    if (path.rfind(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), 0) == 0) {
 | 
			
		||||
        return MediaType::SDMC;
 | 
			
		||||
    }
 | 
			
		||||
    return MediaType::GameCard;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ArchiveBackend* ArchiveManager::GetArchive(ArchiveHandle handle) {
 | 
			
		||||
    auto itr = handle_map.find(handle);
 | 
			
		||||
    return (itr == handle_map.end()) ? nullptr : itr->second.get();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,6 +48,14 @@ enum class ArchiveIdCode : u32 {
 | 
			
		|||
/// Media types for the archives
 | 
			
		||||
enum class MediaType : u32 { NAND = 0, SDMC = 1, GameCard = 2 };
 | 
			
		||||
 | 
			
		||||
MediaType GetMediaTypeFromPath(std::string_view path);
 | 
			
		||||
 | 
			
		||||
enum class SpecialContentType : u8 {
 | 
			
		||||
    Update = 1,
 | 
			
		||||
    Manual = 2,
 | 
			
		||||
    DLPChild = 3,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef u64 ArchiveHandle;
 | 
			
		||||
 | 
			
		||||
struct ArchiveResource {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,6 +12,7 @@
 | 
			
		|||
#include "common/string_util.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/file_sys/errors.h"
 | 
			
		||||
#include "core/file_sys/ncch_container.h"
 | 
			
		||||
#include "core/file_sys/seed_db.h"
 | 
			
		||||
#include "core/hle/ipc.h"
 | 
			
		||||
#include "core/hle/ipc_helpers.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -682,13 +683,11 @@ void FS_USER::GetProgramLaunchInfo(Kernel::HLERequestContext& ctx) {
 | 
			
		|||
 | 
			
		||||
    LOG_DEBUG(Service_FS, "process_id={}", process_id);
 | 
			
		||||
 | 
			
		||||
    // TODO(Subv): The real FS service manages its own process list and only checks the processes
 | 
			
		||||
    // that were registered with the 'fs:REG' service.
 | 
			
		||||
    auto process = system.Kernel().GetProcessById(process_id);
 | 
			
		||||
    auto program_info = program_info_map.find(process_id);
 | 
			
		||||
 | 
			
		||||
    IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
 | 
			
		||||
 | 
			
		||||
    if (process == nullptr) {
 | 
			
		||||
    if (program_info == program_info_map.end()) {
 | 
			
		||||
        // Note: In this case, the rest of the parameters are not changed but the command header
 | 
			
		||||
        // remains the same.
 | 
			
		||||
        rb.Push(ResultCode(FileSys::ErrCodes::ArchiveNotMounted, ErrorModule::FS,
 | 
			
		||||
| 
						 | 
				
			
			@ -697,13 +696,9 @@ void FS_USER::GetProgramLaunchInfo(Kernel::HLERequestContext& ctx) {
 | 
			
		|||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u64 program_id = process->codeset->program_id;
 | 
			
		||||
 | 
			
		||||
    auto media_type = Service::AM::GetTitleMediaType(program_id);
 | 
			
		||||
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
    rb.Push(program_id);
 | 
			
		||||
    rb.Push(static_cast<u8>(media_type));
 | 
			
		||||
    rb.Push(program_info->second.program_id);
 | 
			
		||||
    rb.Push(static_cast<u8>(program_info->second.media_type));
 | 
			
		||||
 | 
			
		||||
    // TODO(Subv): Find out what this value means.
 | 
			
		||||
    rb.Push<u32>(0);
 | 
			
		||||
| 
						 | 
				
			
			@ -752,6 +747,32 @@ void FS_USER::ObsoletedDeleteExtSaveData(Kernel::HLERequestContext& ctx) {
 | 
			
		|||
              static_cast<u32>(media_type));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FS_USER::GetSpecialContentIndex(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    IPC::RequestParser rp(ctx, 0x83A, 4, 0);
 | 
			
		||||
    const MediaType media_type = static_cast<MediaType>(rp.Pop<u8>());
 | 
			
		||||
    const u64 title_id = rp.Pop<u64>();
 | 
			
		||||
    const auto type = rp.PopEnum<SpecialContentType>();
 | 
			
		||||
 | 
			
		||||
    LOG_DEBUG(Service_FS, "called, media_type={:08X} type={:08X}, title_id={:016X}",
 | 
			
		||||
              static_cast<u32>(media_type), static_cast<u32>(type), title_id);
 | 
			
		||||
 | 
			
		||||
    ResultVal<u16> index;
 | 
			
		||||
    if (media_type == MediaType::GameCard) {
 | 
			
		||||
        index = GetSpecialContentIndexFromGameCard(title_id, type);
 | 
			
		||||
    } else {
 | 
			
		||||
        index = GetSpecialContentIndexFromTMD(media_type, title_id, type);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (index.Succeeded()) {
 | 
			
		||||
        IPC::RequestBuilder rb = rp.MakeBuilder(2, 0);
 | 
			
		||||
        rb.Push(RESULT_SUCCESS);
 | 
			
		||||
        rb.Push(index.Unwrap());
 | 
			
		||||
    } else {
 | 
			
		||||
        IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
 | 
			
		||||
        rb.Push(index.Code());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FS_USER::GetNumSeeds(Kernel::HLERequestContext& ctx) {
 | 
			
		||||
    IPC::RequestBuilder rb{ctx, 0x87D, 2, 0};
 | 
			
		||||
    rb.Push(RESULT_SUCCESS);
 | 
			
		||||
| 
						 | 
				
			
			@ -808,6 +829,66 @@ void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
 | 
			
		|||
    rb.Push<u64>(0);      // the secure value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void FS_USER::Register(u32 process_id, u64 program_id, const std::string& filepath) {
 | 
			
		||||
    const MediaType media_type = GetMediaTypeFromPath(filepath);
 | 
			
		||||
    program_info_map.insert_or_assign(process_id, ProgramInfo{program_id, media_type});
 | 
			
		||||
    if (media_type == MediaType::GameCard) {
 | 
			
		||||
        current_gamecard_path = filepath;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string FS_USER::GetCurrentGamecardPath() const {
 | 
			
		||||
    return current_gamecard_path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultVal<u16> FS_USER::GetSpecialContentIndexFromGameCard(u64 title_id, SpecialContentType type) {
 | 
			
		||||
    // TODO(B3N30) check if on real 3DS NCSD is checked if partition exists
 | 
			
		||||
 | 
			
		||||
    if (type > SpecialContentType::DLPChild) {
 | 
			
		||||
        // Maybe type 4 is New 3DS update/partition 6 but this needs more research
 | 
			
		||||
        // TODO(B3N30): Find correct result code
 | 
			
		||||
        return ResultCode(-1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    switch (type) {
 | 
			
		||||
    case SpecialContentType::Update:
 | 
			
		||||
        return MakeResult(static_cast<u16>(NCSDContentIndex::Update));
 | 
			
		||||
    case SpecialContentType::Manual:
 | 
			
		||||
        return MakeResult(static_cast<u16>(NCSDContentIndex::Manual));
 | 
			
		||||
    case SpecialContentType::DLPChild:
 | 
			
		||||
        return MakeResult(static_cast<u16>(NCSDContentIndex::DLP));
 | 
			
		||||
    default:
 | 
			
		||||
        ASSERT(false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultVal<u16> FS_USER::GetSpecialContentIndexFromTMD(MediaType media_type, u64 title_id,
 | 
			
		||||
                                                      SpecialContentType type) {
 | 
			
		||||
    if (type > SpecialContentType::DLPChild) {
 | 
			
		||||
        // TODO(B3N30): Find correct result code
 | 
			
		||||
        return ResultCode(-1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string tmd_path = AM::GetTitleMetadataPath(media_type, title_id);
 | 
			
		||||
 | 
			
		||||
    FileSys::TitleMetadata tmd;
 | 
			
		||||
    if (tmd.Load(tmd_path) != Loader::ResultStatus::Success || type == SpecialContentType::Update) {
 | 
			
		||||
        // TODO(B3N30): Find correct result code
 | 
			
		||||
        return ResultCode(-1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // TODO(B3N30): Does real 3DS check if content exists in TMD?
 | 
			
		||||
 | 
			
		||||
    switch (type) {
 | 
			
		||||
    case SpecialContentType::Manual:
 | 
			
		||||
        return MakeResult(static_cast<u16>(FileSys::TMDContentIndex::Manual));
 | 
			
		||||
    case SpecialContentType::DLPChild:
 | 
			
		||||
        return MakeResult(static_cast<u16>(FileSys::TMDContentIndex::DLP));
 | 
			
		||||
    default:
 | 
			
		||||
        ASSERT(false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
FS_USER::FS_USER(Core::System& system)
 | 
			
		||||
    : ServiceFramework("fs:USER", 30), system(system), archives(system.ArchiveManager()) {
 | 
			
		||||
    static const FunctionInfo functions[] = {
 | 
			
		||||
| 
						 | 
				
			
			@ -870,7 +951,7 @@ FS_USER::FS_USER(Core::System& system)
 | 
			
		|||
        {0x08370040, nullptr, "SetCardSpiBaudRate"},
 | 
			
		||||
        {0x08380040, nullptr, "SetCardSpiBusMode"},
 | 
			
		||||
        {0x08390000, nullptr, "SendInitializeInfoTo9"},
 | 
			
		||||
        {0x083A0100, nullptr, "GetSpecialContentIndex"},
 | 
			
		||||
        {0x083A0100, &FS_USER::GetSpecialContentIndex, "GetSpecialContentIndex"},
 | 
			
		||||
        {0x083B00C2, nullptr, "GetLegacyRomHeader"},
 | 
			
		||||
        {0x083C00C2, nullptr, "GetLegacyBannerData"},
 | 
			
		||||
        {0x083D0100, nullptr, "CheckAuthorityToAccessExtSaveData"},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@
 | 
			
		|||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <unordered_map>
 | 
			
		||||
#include <boost/serialization/base_object.hpp>
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "core/hle/service/service.h"
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +39,12 @@ class FS_USER final : public ServiceFramework<FS_USER, ClientSlot> {
 | 
			
		|||
public:
 | 
			
		||||
    explicit FS_USER(Core::System& system);
 | 
			
		||||
 | 
			
		||||
    // On real HW this is part of FS:Reg. But since that module is only used by loader and pm, which
 | 
			
		||||
    // we HLEed, we can just directly use it here
 | 
			
		||||
    void Register(u32 process_id, u64 program_id, const std::string& filepath);
 | 
			
		||||
 | 
			
		||||
    std::string GetCurrentGamecardPath() const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void Initialize(Kernel::HLERequestContext& ctx);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -525,6 +532,20 @@ private:
 | 
			
		|||
     */
 | 
			
		||||
    void ObsoletedDeleteExtSaveData(Kernel::HLERequestContext& ctx);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * FS_User::GetSpecialContentIndex service function.
 | 
			
		||||
     *  Inputs:
 | 
			
		||||
     *      0 : 0x083A0100
 | 
			
		||||
     *      1 : Media type
 | 
			
		||||
     *    2-3 : Program ID
 | 
			
		||||
     *      4 : Special content type
 | 
			
		||||
     *  Outputs:
 | 
			
		||||
     *      0 : 0x083A0080
 | 
			
		||||
     *      1 : Result of function, 0 on success, otherwise error code
 | 
			
		||||
     *      2 : Special content index
 | 
			
		||||
     */
 | 
			
		||||
    void GetSpecialContentIndex(Kernel::HLERequestContext& ctx);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * FS_User::GetNumSeeds service function.
 | 
			
		||||
     *  Inputs:
 | 
			
		||||
| 
						 | 
				
			
			@ -577,6 +598,18 @@ private:
 | 
			
		|||
     */
 | 
			
		||||
    void GetSaveDataSecureValue(Kernel::HLERequestContext& ctx);
 | 
			
		||||
 | 
			
		||||
    static ResultVal<u16> GetSpecialContentIndexFromGameCard(u64 title_id, SpecialContentType type);
 | 
			
		||||
    static ResultVal<u16> GetSpecialContentIndexFromTMD(MediaType media_type, u64 title_id,
 | 
			
		||||
                                                        SpecialContentType type);
 | 
			
		||||
 | 
			
		||||
    struct ProgramInfo {
 | 
			
		||||
        u64 program_id;
 | 
			
		||||
        MediaType media_type;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    std::unordered_map<u32, ProgramInfo> program_info_map;
 | 
			
		||||
    std::string current_gamecard_path;
 | 
			
		||||
 | 
			
		||||
    u32 priority = -1; ///< For SetPriority and GetPriority service functions
 | 
			
		||||
 | 
			
		||||
    Core::System& system;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue