mirror of
https://github.com/citra-emu/citra-canary.git
synced 2024-12-22 18:35:27 +00:00
Add missing FS:USER functions (#7051)
This commit is contained in:
parent
b231a22ea5
commit
597a2e8ead
|
@ -221,6 +221,13 @@ public:
|
|||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the client thread that made the service request.
|
||||
*/
|
||||
std::shared_ptr<Thread> ClientThread() const {
|
||||
return thread;
|
||||
}
|
||||
|
||||
class WakeupCallback {
|
||||
public:
|
||||
virtual ~WakeupCallback() = default;
|
||||
|
|
|
@ -670,6 +670,26 @@ void FS_USER::GetFormatInfo(Kernel::HLERequestContext& ctx) {
|
|||
rb.Push<bool>(format_info->duplicate_data != 0);
|
||||
}
|
||||
|
||||
void FS_USER::GetProductInfo(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
u32 process_id = rp.Pop<u32>();
|
||||
|
||||
LOG_DEBUG(Service_FS, "called, process_id={}", process_id);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(6, 0);
|
||||
|
||||
const auto product_info = GetProductInfo(process_id);
|
||||
if (!product_info.has_value()) {
|
||||
rb.Push(ResultCode(FileSys::ErrCodes::ArchiveNotMounted, ErrorModule::FS,
|
||||
ErrorSummary::NotFound, ErrorLevel::Status));
|
||||
return;
|
||||
}
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw<ProductInfo>(product_info.value());
|
||||
}
|
||||
|
||||
void FS_USER::GetProgramLaunchInfo(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const auto process_id = rp.Pop<u32>();
|
||||
|
@ -687,8 +707,20 @@ void FS_USER::GetProgramLaunchInfo(Kernel::HLERequestContext& ctx) {
|
|||
return;
|
||||
}
|
||||
|
||||
ProgramInfo program_info = program_info_result.Unwrap();
|
||||
|
||||
// Always report the launched program mediatype is SD if the friends module is requesting this
|
||||
// information and the media type is game card. Otherwise, friends will append a "romid" field
|
||||
// to the NASC request with a cartridge unique identifier. Using a dump of a game card and the
|
||||
// game card itself at the same time online is known to have caused issues in the past.
|
||||
auto process = ctx.ClientThread()->owner_process.lock();
|
||||
if (process && process->codeset->name == "friends" &&
|
||||
program_info.media_type == MediaType::GameCard) {
|
||||
program_info.media_type = MediaType::SDMC;
|
||||
}
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushRaw(program_info_result.Unwrap());
|
||||
rb.PushRaw<ProgramInfo>(program_info);
|
||||
}
|
||||
|
||||
void FS_USER::ObsoletedCreateExtSaveData(Kernel::HLERequestContext& ctx) {
|
||||
|
@ -775,12 +807,12 @@ void FS_USER::AddSeed(Kernel::HLERequestContext& ctx) {
|
|||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void FS_USER::SetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
void FS_USER::ObsoletedSetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
u64 value = rp.Pop<u64>();
|
||||
u32 secure_value_slot = rp.Pop<u32>();
|
||||
u32 unique_id = rp.Pop<u32>();
|
||||
u8 title_variation = rp.Pop<u8>();
|
||||
const u64 value = rp.Pop<u64>();
|
||||
const u32 secure_value_slot = rp.Pop<u32>();
|
||||
const u32 unique_id = rp.Pop<u32>();
|
||||
const u8 title_variation = rp.Pop<u8>();
|
||||
|
||||
// TODO: Generate and Save the Secure Value
|
||||
|
||||
|
@ -794,12 +826,11 @@ void FS_USER::SetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
|||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
void FS_USER::ObsoletedGetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
|
||||
u32 secure_value_slot = rp.Pop<u32>();
|
||||
u32 unique_id = rp.Pop<u32>();
|
||||
u8 title_variation = rp.Pop<u8>();
|
||||
const u32 secure_value_slot = rp.Pop<u32>();
|
||||
const u32 unique_id = rp.Pop<u32>();
|
||||
const u8 title_variation = rp.Pop<u8>();
|
||||
|
||||
LOG_WARNING(
|
||||
Service_FS,
|
||||
|
@ -816,7 +847,77 @@ 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) {
|
||||
void FS_USER::SetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const u32 secure_value_slot = rp.Pop<u32>();
|
||||
const u64 value = rp.Pop<u64>();
|
||||
|
||||
// TODO: Generate and Save the Secure Value
|
||||
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called, value=0x{:016x} secure_value_slot=0x{:08X}", value,
|
||||
secure_value_slot);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void FS_USER::GetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const u32 secure_value_slot = rp.Pop<u32>();
|
||||
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called secure_value_slot=0x{:08X}", secure_value_slot);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
// TODO: Implement Secure Value Lookup & Generation
|
||||
|
||||
rb.Push<bool>(false); // indicates that the secure value doesn't exist
|
||||
rb.Push<bool>(false); // looks like a boolean value, purpose unknown
|
||||
rb.Push<u64>(0); // the secure value
|
||||
}
|
||||
|
||||
void FS_USER::SetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const auto archive_handle = rp.PopRaw<ArchiveHandle>();
|
||||
const u32 secure_value_slot = rp.Pop<u32>();
|
||||
const u64 value = rp.Pop<u64>();
|
||||
const bool flush = rp.Pop<bool>();
|
||||
|
||||
// TODO: Generate and Save the Secure Value
|
||||
|
||||
LOG_WARNING(Service_FS,
|
||||
"(STUBBED) called, value=0x{:016x} secure_value_slot=0x{:04X} "
|
||||
"archive_handle=0x{:08X} flush={}",
|
||||
value, secure_value_slot, archive_handle, flush);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void FS_USER::GetSaveDataSecureValue(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp(ctx);
|
||||
const auto archive_handle = rp.PopRaw<ArchiveHandle>();
|
||||
const u32 secure_value_slot = rp.Pop<u32>();
|
||||
|
||||
LOG_WARNING(Service_FS, "(STUBBED) called secure_value_slot=0x{:08X} archive_handle=0x{:08X}",
|
||||
secure_value_slot, archive_handle);
|
||||
|
||||
IPC::RequestBuilder rb = rp.MakeBuilder(5, 0);
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
// TODO: Implement Secure Value Lookup & Generation
|
||||
|
||||
rb.Push<bool>(false); // indicates that the secure value doesn't exist
|
||||
rb.Push<bool>(false); // looks like a boolean value, purpose unknown
|
||||
rb.Push<u64>(0); // the secure value
|
||||
}
|
||||
|
||||
void FS_USER::RegisterProgramInfo(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) {
|
||||
|
@ -828,6 +929,19 @@ std::string FS_USER::GetCurrentGamecardPath() const {
|
|||
return current_gamecard_path;
|
||||
}
|
||||
|
||||
void FS_USER::RegisterProductInfo(u32 process_id, const ProductInfo& product_info) {
|
||||
product_info_map.insert_or_assign(process_id, product_info);
|
||||
}
|
||||
|
||||
std::optional<FS_USER::ProductInfo> FS_USER::GetProductInfo(u32 process_id) {
|
||||
auto it = product_info_map.find(process_id);
|
||||
if (it != product_info_map.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
ResultVal<u16> FS_USER::GetSpecialContentIndexFromGameCard(u64 title_id, SpecialContentType type) {
|
||||
// TODO(B3N30) check if on real 3DS NCSD is checked if partition exists
|
||||
|
||||
|
@ -929,7 +1043,7 @@ FS_USER::FS_USER(Core::System& system)
|
|||
{0x082B, nullptr, "CardNorDirectRead_4xIO"},
|
||||
{0x082C, nullptr, "CardNorDirectCpuWriteWithoutVerify"},
|
||||
{0x082D, nullptr, "CardNorDirectSectorEraseWithoutVerify"},
|
||||
{0x082E, nullptr, "GetProductInfo"},
|
||||
{0x082E, &FS_USER::GetProductInfo, "GetProductInfo"},
|
||||
{0x082F, &FS_USER::GetProgramLaunchInfo, "GetProgramLaunchInfo"},
|
||||
{0x0830, &FS_USER::ObsoletedCreateExtSaveData, "Obsoleted_3_0_CreateExtSaveData"},
|
||||
{0x0831, nullptr, "CreateSharedExtSaveData"},
|
||||
|
@ -984,12 +1098,16 @@ FS_USER::FS_USER(Core::System& system)
|
|||
{0x0862, &FS_USER::SetPriority, "SetPriority"},
|
||||
{0x0863, &FS_USER::GetPriority, "GetPriority"},
|
||||
{0x0864, nullptr, "GetNandInfo"},
|
||||
{0x0865, &FS_USER::SetSaveDataSecureValue, "SetSaveDataSecureValue"},
|
||||
{0x0866, &FS_USER::GetSaveDataSecureValue, "GetSaveDataSecureValue"},
|
||||
{0x0865, &FS_USER::ObsoletedSetSaveDataSecureValue, "SetSaveDataSecureValue"},
|
||||
{0x0866, &FS_USER::ObsoletedGetSaveDataSecureValue, "GetSaveDataSecureValue"},
|
||||
{0x0867, nullptr, "ControlSecureSave"},
|
||||
{0x0868, nullptr, "GetMediaType"},
|
||||
{0x0869, nullptr, "GetNandEraseCount"},
|
||||
{0x086A, nullptr, "ReadNandReport"},
|
||||
{0x086E, &FS_USER::SetThisSaveDataSecureValue, "SetThisSaveDataSecureValue" },
|
||||
{0x086F, &FS_USER::GetThisSaveDataSecureValue, "GetThisSaveDataSecureValue" },
|
||||
{0x0875, &FS_USER::SetSaveDataSecureValue, "SetSaveDataSecureValue" },
|
||||
{0x0876, &FS_USER::GetSaveDataSecureValue, "GetSaveDataSecureValue" },
|
||||
{0x087A, &FS_USER::AddSeed, "AddSeed"},
|
||||
{0x087D, &FS_USER::GetNumSeeds, "GetNumSeeds"},
|
||||
{0x0886, nullptr, "CheckUpdatedDat"},
|
||||
|
|
|
@ -4,10 +4,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <boost/serialization/base_object.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/file_sys/errors.h"
|
||||
#include "core/hle/service/fs/archive.h"
|
||||
#include "core/hle/service/service.h"
|
||||
|
||||
namespace Core {
|
||||
|
@ -47,12 +49,23 @@ 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);
|
||||
// On real HW this is part of FSReg (FSReg:Register). But since that module is only used by
|
||||
// loader and pm, which we HLEed, we can just directly use it here
|
||||
void RegisterProgramInfo(u32 process_id, u64 program_id, const std::string& filepath);
|
||||
|
||||
std::string GetCurrentGamecardPath() const;
|
||||
|
||||
struct ProductInfo {
|
||||
std::array<u8, 0x10> product_code;
|
||||
u16_le maker_code;
|
||||
u16_le remaster_version;
|
||||
};
|
||||
static_assert(sizeof(ProductInfo) == 0x14);
|
||||
|
||||
void RegisterProductInfo(u32 process_id, const ProductInfo& product_info);
|
||||
|
||||
std::optional<ProductInfo> GetProductInfo(u32 process_id);
|
||||
|
||||
/// Gets the registered program info of a process.
|
||||
ResultVal<ProgramInfo> GetProgramLaunchInfo(u32 process_id) const {
|
||||
auto info = program_info_map.find(process_id);
|
||||
|
@ -509,6 +522,17 @@ private:
|
|||
*/
|
||||
void GetFormatInfo(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::GetProductInfo service function.
|
||||
* Inputs:
|
||||
* 0 : 0x082E0040
|
||||
* 1 : Process ID
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2-6 : Product info
|
||||
*/
|
||||
void GetProductInfo(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::GetProgramLaunchInfo service function.
|
||||
* Inputs:
|
||||
|
@ -600,7 +624,7 @@ private:
|
|||
* 0 : 0x08650140
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void SetSaveDataSecureValue(Kernel::HLERequestContext& ctx);
|
||||
void ObsoletedSetSaveDataSecureValue(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::GetSaveDataSecureValue service function.
|
||||
|
@ -615,6 +639,57 @@ private:
|
|||
* 2 : If Secure Value doesn't exist, 0, if it exists, 1
|
||||
* 3-4 : Secure Value
|
||||
*/
|
||||
void ObsoletedGetSaveDataSecureValue(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::SetThisSaveDataSecureValue service function.
|
||||
* Inputs:
|
||||
* 1 : Secure Value Slot
|
||||
* 2-3 : Secure Value
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void SetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::GetSaveDataSecureValue service function.
|
||||
* Inputs:
|
||||
* 1 : Secure Value Slot
|
||||
* Outputs:
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : If Secure Value doesn't exist, 0, if it exists, 1
|
||||
* 3 : Unknown
|
||||
* 4-5 : Secure Value
|
||||
*/
|
||||
void GetThisSaveDataSecureValue(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::SetSaveDataSecureValue service function.
|
||||
* Inputs:
|
||||
* 0 : 0x08750180
|
||||
* 1-2 : Archive
|
||||
* 3 : Secure Value Slot
|
||||
* 4 : value
|
||||
* 5 : flush
|
||||
* Outputs:
|
||||
* 0 : header
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
*/
|
||||
void SetSaveDataSecureValue(Kernel::HLERequestContext& ctx);
|
||||
|
||||
/**
|
||||
* FS_User::GetSaveDataSecureValue service function.
|
||||
* Inputs:
|
||||
* 0 : 0x087600C0
|
||||
* 1-2 : Archive
|
||||
* 2 : Secure Value slot
|
||||
* Outputs:
|
||||
* 0 : Header
|
||||
* 1 : Result of function, 0 on success, otherwise error code
|
||||
* 2 : If Secure Value doesn't exist, 0, if it exists, 1
|
||||
* 3 : unknown
|
||||
* 4-5 : Secure Value
|
||||
*/
|
||||
void GetSaveDataSecureValue(Kernel::HLERequestContext& ctx);
|
||||
|
||||
static ResultVal<u16> GetSpecialContentIndexFromGameCard(u64 title_id, SpecialContentType type);
|
||||
|
@ -624,6 +699,8 @@ private:
|
|||
std::unordered_map<u32, ProgramInfo> program_info_map;
|
||||
std::string current_gamecard_path;
|
||||
|
||||
std::unordered_map<u32, ProductInfo> product_info_map;
|
||||
|
||||
u32 priority = -1; ///< For SetPriority and GetPriority service functions
|
||||
|
||||
Core::System& system;
|
||||
|
|
|
@ -282,7 +282,7 @@ ResultStatus AppLoader_THREEDSX::Load(std::shared_ptr<Kernel::Process>& process)
|
|||
// On real HW this is done with FS:Reg, but we can be lazy
|
||||
auto fs_user =
|
||||
Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>("fs:USER");
|
||||
fs_user->Register(process->GetObjectId(), process->codeset->program_id, filepath);
|
||||
fs_user->RegisterProgramInfo(process->GetObjectId(), process->codeset->program_id, filepath);
|
||||
|
||||
process->Run(48, Kernel::DEFAULT_STACK_SIZE);
|
||||
|
||||
|
|
|
@ -174,7 +174,16 @@ ResultStatus AppLoader_NCCH::LoadExec(std::shared_ptr<Kernel::Process>& process)
|
|||
auto fs_user =
|
||||
Core::System::GetInstance().ServiceManager().GetService<Service::FS::FS_USER>(
|
||||
"fs:USER");
|
||||
fs_user->Register(process->process_id, process->codeset->program_id, filepath);
|
||||
fs_user->RegisterProgramInfo(process->process_id, process->codeset->program_id, filepath);
|
||||
|
||||
Service::FS::FS_USER::ProductInfo product_info{};
|
||||
std::memcpy(product_info.product_code.data(), overlay_ncch->ncch_header.product_code,
|
||||
product_info.product_code.size());
|
||||
std::memcpy(&product_info.remaster_version,
|
||||
overlay_ncch->exheader_header.codeset_info.flags.remaster_version,
|
||||
sizeof(product_info.remaster_version));
|
||||
product_info.maker_code = overlay_ncch->ncch_header.maker_code;
|
||||
fs_user->RegisterProductInfo(process->process_id, product_info);
|
||||
|
||||
process->Run(priority, stack_size);
|
||||
return ResultStatus::Success;
|
||||
|
|
Loading…
Reference in a new issue