diff options
| author | 2019-04-28 18:55:56 -0400 | |
|---|---|---|
| committer | 2019-09-30 17:23:26 -0400 | |
| commit | cb7c96b96a0ad781d5bb4387072d5d0becd74dd7 (patch) | |
| tree | 2397b6633887e61a6885039acecedd8488e3db96 /src | |
| parent | bcat: Implement IDeliveryCacheFileService commands (diff) | |
| download | yuzu-cb7c96b96a0ad781d5bb4387072d5d0becd74dd7.tar.gz yuzu-cb7c96b96a0ad781d5bb4387072d5d0becd74dd7.tar.xz yuzu-cb7c96b96a0ad781d5bb4387072d5d0becd74dd7.zip | |
bcat: Implement IDeliveryCacheProgressService commands
Used to query completion status and events for the current delivery task.
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/bcat/module.cpp | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index 25f68ed63..1459fab11 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp | |||
| @@ -2,13 +2,144 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <cctype> | ||
| 6 | #include <mbedtls/md5.h> | ||
| 7 | #include "backend/boxcat.h" | ||
| 8 | #include "common/hex_util.h" | ||
| 5 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/string_util.h" | ||
| 11 | #include "core/file_sys/vfs.h" | ||
| 6 | #include "core/hle/ipc_helpers.h" | 12 | #include "core/hle/ipc_helpers.h" |
| 13 | #include "core/hle/kernel/process.h" | ||
| 14 | #include "core/hle/kernel/readable_event.h" | ||
| 15 | #include "core/hle/kernel/writable_event.h" | ||
| 16 | #include "core/hle/service/bcat/backend/backend.h" | ||
| 7 | #include "core/hle/service/bcat/bcat.h" | 17 | #include "core/hle/service/bcat/bcat.h" |
| 8 | #include "core/hle/service/bcat/module.h" | 18 | #include "core/hle/service/bcat/module.h" |
| 19 | #include "core/hle/service/filesystem/filesystem.h" | ||
| 20 | #include "core/settings.h" | ||
| 9 | 21 | ||
| 10 | namespace Service::BCAT { | 22 | namespace Service::BCAT { |
| 11 | 23 | ||
| 24 | constexpr ResultCode ERROR_INVALID_ARGUMENT{ErrorModule::BCAT, 1}; | ||
| 25 | constexpr ResultCode ERROR_FAILED_OPEN_ENTITY{ErrorModule::BCAT, 2}; | ||
| 26 | constexpr ResultCode ERROR_ENTITY_ALREADY_OPEN{ErrorModule::BCAT, 6}; | ||
| 27 | constexpr ResultCode ERROR_NO_OPEN_ENTITY{ErrorModule::BCAT, 7}; | ||
| 28 | |||
| 29 | // The command to clear the delivery cache just calls fs IFileSystem DeleteFile on all of the files | ||
| 30 | // and if any of them have a non-zero result it just forwards that result. This is the FS error code | ||
| 31 | // for permission denied, which is the closest approximation of this scenario. | ||
| 32 | constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; | ||
| 33 | |||
| 34 | using BCATDigest = std::array<u8, 0x10>; | ||
| 35 | |||
| 36 | struct DeliveryCacheProgressImpl { | ||
| 37 | enum class Status : u8 { | ||
| 38 | Incomplete = 0x1, | ||
| 39 | Complete = 0x9, | ||
| 40 | }; | ||
| 41 | |||
| 42 | Status status = Status::Incomplete; | ||
| 43 | INSERT_PADDING_BYTES( | ||
| 44 | 0x1FF); ///< TODO(DarkLordZach): RE this structure. It just seems to convey info about the | ||
| 45 | ///< progress of the BCAT sync, but for us just setting completion works. | ||
| 46 | }; | ||
| 47 | static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200, | ||
| 48 | "DeliveryCacheProgressImpl has incorrect size."); | ||
| 49 | |||
| 50 | namespace { | ||
| 51 | |||
| 52 | u64 GetCurrentBuildID() { | ||
| 53 | const auto& id = Core::System::GetInstance().GetCurrentProcessBuildID(); | ||
| 54 | u64 out{}; | ||
| 55 | std::memcpy(&out, id.data(), sizeof(u64)); | ||
| 56 | return out; | ||
| 57 | } | ||
| 58 | |||
| 59 | // The digest is only used to determine if a file is unique compared to others of the same name. | ||
| 60 | // Since the algorithm isn't ever checked in game, MD5 is safe. | ||
| 61 | BCATDigest DigestFile(const FileSys::VirtualFile& file) { | ||
| 62 | BCATDigest out{}; | ||
| 63 | const auto bytes = file->ReadAllBytes(); | ||
| 64 | mbedtls_md5(bytes.data(), bytes.size(), out.data()); | ||
| 65 | return out; | ||
| 66 | } | ||
| 67 | |||
| 68 | // For a name to be valid it must be non-empty, must have a null terminating character as the final | ||
| 69 | // char, can only contain numbers, letters, underscores and a hyphen if directory and a period if | ||
| 70 | // file. | ||
| 71 | bool VerifyNameValidInternal(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name, | ||
| 72 | char match_char) { | ||
| 73 | const auto null_chars = std::count(name.begin(), name.end(), 0); | ||
| 74 | const auto bad_chars = std::count_if(name.begin(), name.end(), [match_char](char c) { | ||
| 75 | return !std::isalnum(static_cast<u8>(c)) && c != '_' && c != match_char && c != '\0'; | ||
| 76 | }); | ||
| 77 | if (null_chars == 0x20 || null_chars == 0 || bad_chars != 0 || name[0x1F] != '\0') { | ||
| 78 | LOG_ERROR(Service_BCAT, "Name passed was invalid!"); | ||
| 79 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 80 | rb.Push(ERROR_INVALID_ARGUMENT); | ||
| 81 | return false; | ||
| 82 | } | ||
| 83 | |||
| 84 | return true; | ||
| 85 | } | ||
| 86 | |||
| 87 | bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name) { | ||
| 88 | return VerifyNameValidInternal(ctx, name, '-'); | ||
| 89 | } | ||
| 90 | |||
| 91 | bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name) { | ||
| 92 | return VerifyNameValidInternal(ctx, name, '.'); | ||
| 93 | } | ||
| 94 | |||
| 95 | } // Anonymous namespace | ||
| 96 | |||
| 97 | using DirectoryName = std::array<char, 0x20>; | ||
| 98 | using FileName = std::array<char, 0x20>; | ||
| 99 | |||
| 100 | struct DeliveryCacheDirectoryEntry { | ||
| 101 | FileName name; | ||
| 102 | u64 size; | ||
| 103 | BCATDigest digest; | ||
| 104 | }; | ||
| 105 | |||
| 106 | class IDeliveryCacheProgressService final : public ServiceFramework<IDeliveryCacheProgressService> { | ||
| 107 | public: | ||
| 108 | IDeliveryCacheProgressService(Kernel::SharedPtr<Kernel::ReadableEvent> event, | ||
| 109 | const DeliveryCacheProgressImpl& impl) | ||
| 110 | : ServiceFramework{"IDeliveryCacheProgressService"}, event(std::move(event)), impl(impl) { | ||
| 111 | // clang-format off | ||
| 112 | static const FunctionInfo functions[] = { | ||
| 113 | {0, &IDeliveryCacheProgressService::GetEvent, "GetEvent"}, | ||
| 114 | {1, &IDeliveryCacheProgressService::GetImpl, "GetImpl"}, | ||
| 115 | }; | ||
| 116 | // clang-format on | ||
| 117 | |||
| 118 | RegisterHandlers(functions); | ||
| 119 | } | ||
| 120 | |||
| 121 | private: | ||
| 122 | void GetEvent(Kernel::HLERequestContext& ctx) { | ||
| 123 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 124 | |||
| 125 | IPC::ResponseBuilder rb{ctx, 2, 1}; | ||
| 126 | rb.Push(RESULT_SUCCESS); | ||
| 127 | rb.PushCopyObjects(event); | ||
| 128 | } | ||
| 129 | |||
| 130 | void GetImpl(Kernel::HLERequestContext& ctx) { | ||
| 131 | LOG_DEBUG(Service_BCAT, "called"); | ||
| 132 | |||
| 133 | ctx.WriteBuffer(&impl, sizeof(DeliveryCacheProgressImpl)); | ||
| 134 | |||
| 135 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 136 | rb.Push(RESULT_SUCCESS); | ||
| 137 | } | ||
| 138 | |||
| 139 | Kernel::SharedPtr<Kernel::ReadableEvent> event; | ||
| 140 | const DeliveryCacheProgressImpl& impl; | ||
| 141 | }; | ||
| 142 | |||
| 12 | class IBcatService final : public ServiceFramework<IBcatService> { | 143 | class IBcatService final : public ServiceFramework<IBcatService> { |
| 13 | public: | 144 | public: |
| 14 | IBcatService(Backend& backend) : ServiceFramework("IBcatService"), backend(backend) { | 145 | IBcatService(Backend& backend) : ServiceFramework("IBcatService"), backend(backend) { |