Merge pull request #1608 from DarkLordZach/save-data-reader
[ns|fsp_srv]: Implement various functions to boot Checkpoint
This commit is contained in:
		| @@ -66,4 +66,10 @@ std::string NACP::GetVersionString() const { | ||||
|     return Common::StringFromFixedZeroTerminatedBuffer(raw->version_string.data(), | ||||
|                                                        raw->version_string.size()); | ||||
| } | ||||
|  | ||||
| std::vector<u8> NACP::GetRawBytes() const { | ||||
|     std::vector<u8> out(sizeof(RawNACP)); | ||||
|     std::memcpy(out.data(), raw.get(), sizeof(RawNACP)); | ||||
|     return out; | ||||
| } | ||||
| } // namespace FileSys | ||||
|   | ||||
| @@ -81,6 +81,7 @@ public: | ||||
|     u64 GetTitleId() const; | ||||
|     u64 GetDLCBaseTitleId() const; | ||||
|     std::string GetVersionString() const; | ||||
|     std::vector<u8> GetRawBytes() const; | ||||
|  | ||||
| private: | ||||
|     std::unique_ptr<RawNACP> raw; | ||||
|   | ||||
| @@ -83,6 +83,24 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescr | ||||
|     return MakeResult<VirtualDir>(std::move(out)); | ||||
| } | ||||
|  | ||||
| VirtualDir SaveDataFactory::GetSaveDataSpaceDirectory(SaveDataSpaceId space) const { | ||||
|     return dir->GetDirectoryRelative(GetSaveDataSpaceIdPath(space)); | ||||
| } | ||||
|  | ||||
| std::string SaveDataFactory::GetSaveDataSpaceIdPath(SaveDataSpaceId space) { | ||||
|     switch (space) { | ||||
|     case SaveDataSpaceId::NandSystem: | ||||
|         return "/system/"; | ||||
|     case SaveDataSpaceId::NandUser: | ||||
|         return "/user/"; | ||||
|     case SaveDataSpaceId::TemporaryStorage: | ||||
|         return "/temp/"; | ||||
|     default: | ||||
|         ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); | ||||
|         return "/unrecognized/"; ///< To prevent corruption when ignoring asserts. | ||||
|     } | ||||
| } | ||||
|  | ||||
| std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, | ||||
|                                          u128 user_id, u64 save_id) { | ||||
|     // According to switchbrew, if a save is of type SaveData and the title id field is 0, it should | ||||
| @@ -90,21 +108,7 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ | ||||
|     if (type == SaveDataType::SaveData && title_id == 0) | ||||
|         title_id = Core::CurrentProcess()->GetTitleID(); | ||||
|  | ||||
|     std::string out; | ||||
|  | ||||
|     switch (space) { | ||||
|     case SaveDataSpaceId::NandSystem: | ||||
|         out = "/system/"; | ||||
|         break; | ||||
|     case SaveDataSpaceId::NandUser: | ||||
|         out = "/user/"; | ||||
|         break; | ||||
|     case SaveDataSpaceId::TemporaryStorage: | ||||
|         out = "/temp/"; | ||||
|         break; | ||||
|     default: | ||||
|         ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); | ||||
|     } | ||||
|     std::string out = GetSaveDataSpaceIdPath(space); | ||||
|  | ||||
|     switch (type) { | ||||
|     case SaveDataType::SystemSaveData: | ||||
|   | ||||
| @@ -52,6 +52,9 @@ public: | ||||
|  | ||||
|     ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); | ||||
|  | ||||
|     VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; | ||||
|  | ||||
|     static std::string GetSaveDataSpaceIdPath(SaveDataSpaceId space); | ||||
|     static std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, | ||||
|                                    u128 user_id, u64 save_id); | ||||
|  | ||||
|   | ||||
| @@ -309,6 +309,16 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | ||||
|     return save_data_factory->Open(space, save_struct); | ||||
| } | ||||
|  | ||||
| ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { | ||||
|     LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space)); | ||||
|  | ||||
|     if (save_data_factory == nullptr) { | ||||
|         return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound); | ||||
|     } | ||||
|  | ||||
|     return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); | ||||
| } | ||||
|  | ||||
| ResultVal<FileSys::VirtualDir> OpenSDMC() { | ||||
|     LOG_TRACE(Service_FS, "Opening SDMC"); | ||||
|  | ||||
|   | ||||
| @@ -45,6 +45,7 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora | ||||
|                                           FileSys::ContentRecordType type); | ||||
| ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, | ||||
|                                             FileSys::SaveDataDescriptor save_struct); | ||||
| ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space); | ||||
| ResultVal<FileSys::VirtualDir> OpenSDMC(); | ||||
|  | ||||
| std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents(); | ||||
|   | ||||
| @@ -11,6 +11,7 @@ | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "common/common_types.h" | ||||
| #include "common/hex_util.h" | ||||
| #include "common/logging/log.h" | ||||
| #include "common/string_util.h" | ||||
| #include "core/file_sys/directory.h" | ||||
| @@ -451,7 +452,147 @@ private: | ||||
|     VfsDirectoryServiceWrapper backend; | ||||
| }; | ||||
|  | ||||
| class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { | ||||
| public: | ||||
|     explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space) | ||||
|         : ServiceFramework("ISaveDataInfoReader") { | ||||
|         static const FunctionInfo functions[] = { | ||||
|             {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, | ||||
|         }; | ||||
|         RegisterHandlers(functions); | ||||
|  | ||||
|         FindAllSaves(space); | ||||
|     } | ||||
|  | ||||
|     void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) { | ||||
|         // Calculate how many entries we can fit in the output buffer | ||||
|         const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo); | ||||
|  | ||||
|         // Cap at total number of entries. | ||||
|         const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index); | ||||
|  | ||||
|         // Determine data start and end | ||||
|         const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index); | ||||
|         const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries); | ||||
|         const auto range_size = static_cast<std::size_t>(std::distance(begin, end)); | ||||
|  | ||||
|         next_entry_index += actual_entries; | ||||
|  | ||||
|         // Write the data to memory | ||||
|         ctx.WriteBuffer(begin, range_size); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|         rb.Push<u32>(static_cast<u32>(actual_entries)); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     static u64 stoull_be(std::string_view str) { | ||||
|         if (str.size() != 16) | ||||
|             return 0; | ||||
|  | ||||
|         const auto bytes = Common::HexStringToArray<0x8>(str); | ||||
|         u64 out{}; | ||||
|         std::memcpy(&out, bytes.data(), sizeof(u64)); | ||||
|  | ||||
|         return Common::swap64(out); | ||||
|     } | ||||
|  | ||||
|     void FindAllSaves(FileSys::SaveDataSpaceId space) { | ||||
|         const auto save_root = OpenSaveDataSpace(space); | ||||
|         ASSERT(save_root.Succeeded()); | ||||
|  | ||||
|         for (const auto& type : (*save_root)->GetSubdirectories()) { | ||||
|             if (type->GetName() == "save") { | ||||
|                 for (const auto& save_id : type->GetSubdirectories()) { | ||||
|                     for (const auto& user_id : save_id->GetSubdirectories()) { | ||||
|                         const auto save_id_numeric = stoull_be(save_id->GetName()); | ||||
|                         auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName()); | ||||
|                         std::reverse(user_id_numeric.begin(), user_id_numeric.end()); | ||||
|  | ||||
|                         if (save_id_numeric != 0) { | ||||
|                             // System Save Data | ||||
|                             info.emplace_back(SaveDataInfo{ | ||||
|                                 0, | ||||
|                                 space, | ||||
|                                 FileSys::SaveDataType::SystemSaveData, | ||||
|                                 {}, | ||||
|                                 user_id_numeric, | ||||
|                                 save_id_numeric, | ||||
|                                 0, | ||||
|                                 user_id->GetSize(), | ||||
|                                 {}, | ||||
|                             }); | ||||
|  | ||||
|                             continue; | ||||
|                         } | ||||
|  | ||||
|                         for (const auto& title_id : user_id->GetSubdirectories()) { | ||||
|                             const auto device = | ||||
|                                 std::all_of(user_id_numeric.begin(), user_id_numeric.end(), | ||||
|                                             [](u8 val) { return val == 0; }); | ||||
|                             info.emplace_back(SaveDataInfo{ | ||||
|                                 0, | ||||
|                                 space, | ||||
|                                 device ? FileSys::SaveDataType::DeviceSaveData | ||||
|                                        : FileSys::SaveDataType::SaveData, | ||||
|                                 {}, | ||||
|                                 user_id_numeric, | ||||
|                                 save_id_numeric, | ||||
|                                 stoull_be(title_id->GetName()), | ||||
|                                 title_id->GetSize(), | ||||
|                                 {}, | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) { | ||||
|                 // Temporary Storage | ||||
|                 for (const auto& user_id : type->GetSubdirectories()) { | ||||
|                     for (const auto& title_id : user_id->GetSubdirectories()) { | ||||
|                         if (!title_id->GetFiles().empty() || | ||||
|                             !title_id->GetSubdirectories().empty()) { | ||||
|                             auto user_id_numeric = | ||||
|                                 Common::HexStringToArray<0x10>(user_id->GetName()); | ||||
|                             std::reverse(user_id_numeric.begin(), user_id_numeric.end()); | ||||
|  | ||||
|                             info.emplace_back(SaveDataInfo{ | ||||
|                                 0, | ||||
|                                 space, | ||||
|                                 FileSys::SaveDataType::TemporaryStorage, | ||||
|                                 {}, | ||||
|                                 user_id_numeric, | ||||
|                                 stoull_be(type->GetName()), | ||||
|                                 stoull_be(title_id->GetName()), | ||||
|                                 title_id->GetSize(), | ||||
|                                 {}, | ||||
|                             }); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     struct SaveDataInfo { | ||||
|         u64_le save_id_unknown; | ||||
|         FileSys::SaveDataSpaceId space; | ||||
|         FileSys::SaveDataType type; | ||||
|         INSERT_PADDING_BYTES(0x6); | ||||
|         std::array<u8, 0x10> user_id; | ||||
|         u64_le save_id; | ||||
|         u64_le title_id; | ||||
|         u64_le save_image_size; | ||||
|         INSERT_PADDING_BYTES(0x28); | ||||
|     }; | ||||
|     static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); | ||||
|  | ||||
|     std::vector<SaveDataInfo> info; | ||||
|     u64 next_entry_index = 0; | ||||
| }; | ||||
|  | ||||
| FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | ||||
|     // clang-format off | ||||
|     static const FunctionInfo functions[] = { | ||||
|         {0, nullptr, "MountContent"}, | ||||
|         {1, &FSP_SRV::Initialize, "Initialize"}, | ||||
| @@ -485,7 +626,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | ||||
|         {58, nullptr, "ReadSaveDataFileSystemExtraData"}, | ||||
|         {59, nullptr, "WriteSaveDataFileSystemExtraData"}, | ||||
|         {60, nullptr, "OpenSaveDataInfoReader"}, | ||||
|         {61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, | ||||
|         {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"}, | ||||
|         {62, nullptr, "OpenCacheStorageList"}, | ||||
|         {64, nullptr, "OpenSaveDataInternalStorageFileSystem"}, | ||||
|         {65, nullptr, "UpdateSaveDataMacForDebug"}, | ||||
| @@ -544,6 +685,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | ||||
|         {1009, nullptr, "GetAndClearMemoryReportInfo"}, | ||||
|         {1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"}, | ||||
|     }; | ||||
|     // clang-format on | ||||
|     RegisterHandlers(functions); | ||||
| } | ||||
|  | ||||
| @@ -618,6 +760,15 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) { | ||||
|     MountSaveData(ctx); | ||||
| } | ||||
|  | ||||
| void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) { | ||||
|     IPC::RequestParser rp{ctx}; | ||||
|     const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>(); | ||||
|  | ||||
|     IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||||
|     rb.Push(RESULT_SUCCESS); | ||||
|     rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space)); | ||||
| } | ||||
|  | ||||
| void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | ||||
|     LOG_WARNING(Service_FS, "(STUBBED) called"); | ||||
|  | ||||
|   | ||||
| @@ -25,6 +25,7 @@ private: | ||||
|     void CreateSaveData(Kernel::HLERequestContext& ctx); | ||||
|     void MountSaveData(Kernel::HLERequestContext& ctx); | ||||
|     void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx); | ||||
|     void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx); | ||||
|     void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); | ||||
|     void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); | ||||
|     void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx); | ||||
|   | ||||
| @@ -2,6 +2,9 @@ | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include "common/logging/log.h" | ||||
| #include "core/file_sys/control_metadata.h" | ||||
| #include "core/file_sys/patch_manager.h" | ||||
| #include "core/hle/ipc_helpers.h" | ||||
| #include "core/hle/kernel/hle_ipc.h" | ||||
| #include "core/hle/service/ns/ns.h" | ||||
| @@ -118,7 +121,7 @@ public: | ||||
|             {305, nullptr, "TerminateSystemApplet"}, | ||||
|             {306, nullptr, "LaunchOverlayApplet"}, | ||||
|             {307, nullptr, "TerminateOverlayApplet"}, | ||||
|             {400, nullptr, "GetApplicationControlData"}, | ||||
|             {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"}, | ||||
|             {401, nullptr, "InvalidateAllApplicationControlCache"}, | ||||
|             {402, nullptr, "RequestDownloadApplicationControlData"}, | ||||
|             {403, nullptr, "GetMaxApplicationControlCacheCount"}, | ||||
| @@ -243,6 +246,65 @@ public: | ||||
|  | ||||
|         RegisterHandlers(functions); | ||||
|     } | ||||
|  | ||||
|     void GetApplicationControlData(Kernel::HLERequestContext& ctx) { | ||||
|         IPC::RequestParser rp{ctx}; | ||||
|         const auto flag = rp.PopRaw<u64>(); | ||||
|         LOG_DEBUG(Service_NS, "called with flag={:016X}", flag); | ||||
|  | ||||
|         const auto title_id = rp.PopRaw<u64>(); | ||||
|  | ||||
|         const auto size = ctx.GetWriteBufferSize(); | ||||
|  | ||||
|         const FileSys::PatchManager pm{title_id}; | ||||
|         const auto control = pm.GetControlMetadata(); | ||||
|  | ||||
|         std::vector<u8> out; | ||||
|  | ||||
|         if (control.first != nullptr) { | ||||
|             if (size < 0x4000) { | ||||
|                 LOG_ERROR(Service_NS, | ||||
|                           "output buffer is too small! (actual={:016X}, expected_min=0x4000)", | ||||
|                           size); | ||||
|                 IPC::ResponseBuilder rb{ctx, 2}; | ||||
|                 // TODO(DarkLordZach): Find a better error code for this. | ||||
|                 rb.Push(ResultCode(-1)); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             out.resize(0x4000); | ||||
|             const auto bytes = control.first->GetRawBytes(); | ||||
|             std::memcpy(out.data(), bytes.data(), bytes.size()); | ||||
|         } else { | ||||
|             LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.", | ||||
|                         title_id); | ||||
|             out.resize(std::min<u64>(0x4000, size)); | ||||
|         } | ||||
|  | ||||
|         if (control.second != nullptr) { | ||||
|             if (size < 0x4000 + control.second->GetSize()) { | ||||
|                 LOG_ERROR(Service_NS, | ||||
|                           "output buffer is too small! (actual={:016X}, expected_min={:016X})", | ||||
|                           size, 0x4000 + control.second->GetSize()); | ||||
|                 IPC::ResponseBuilder rb{ctx, 2}; | ||||
|                 // TODO(DarkLordZach): Find a better error code for this. | ||||
|                 rb.Push(ResultCode(-1)); | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             out.resize(0x4000 + control.second->GetSize()); | ||||
|             control.second->Read(out.data() + 0x4000, control.second->GetSize()); | ||||
|         } else { | ||||
|             LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.", | ||||
|                         title_id); | ||||
|         } | ||||
|  | ||||
|         ctx.WriteBuffer(out); | ||||
|  | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(RESULT_SUCCESS); | ||||
|         rb.Push<u32>(static_cast<u32>(out.size())); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> { | ||||
|   | ||||
| @@ -12,10 +12,12 @@ | ||||
| #include "common/swap.h" | ||||
| #include "core/core.h" | ||||
| #include "core/file_sys/control_metadata.h" | ||||
| #include "core/file_sys/romfs_factory.h" | ||||
| #include "core/file_sys/vfs_offset.h" | ||||
| #include "core/gdbstub/gdbstub.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/vm_manager.h" | ||||
| #include "core/hle/service/filesystem/filesystem.h" | ||||
| #include "core/loader/nro.h" | ||||
| #include "core/loader/nso.h" | ||||
| #include "core/memory.h" | ||||
| @@ -208,6 +210,9 @@ ResultStatus AppLoader_NRO::Load(Kernel::Process& process) { | ||||
|         return ResultStatus::ErrorLoadingNRO; | ||||
|     } | ||||
|  | ||||
|     if (romfs != nullptr) | ||||
|         Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); | ||||
|  | ||||
|     process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); | ||||
|  | ||||
|     is_loaded = true; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bunnei
					bunnei