diff options
| author | 2019-05-13 18:51:02 -0400 | |
|---|---|---|
| committer | 2019-09-30 17:27:23 -0400 | |
| commit | 2d410ddf4d9c0109d64fdf3319efeb9e6cc0bce1 (patch) | |
| tree | 81e11426f742b8e8605782095999dc3a3ec5524b | |
| parent | boxcat: Use Etag header names for file digest (diff) | |
| download | yuzu-2d410ddf4d9c0109d64fdf3319efeb9e6cc0bce1.tar.gz yuzu-2d410ddf4d9c0109d64fdf3319efeb9e6cc0bce1.tar.xz yuzu-2d410ddf4d9c0109d64fdf3319efeb9e6cc0bce1.zip | |
bcat: Implement DeliveryCacheProgressImpl structure
Huge thanks to lioncash for re-ing this for me.
| -rw-r--r-- | src/core/file_sys/vfs_libzip.cpp | 8 | ||||
| -rw-r--r-- | src/core/hle/service/bcat/backend/backend.cpp | 88 | ||||
| -rw-r--r-- | src/core/hle/service/bcat/backend/backend.h | 97 | ||||
| -rw-r--r-- | src/core/hle/service/bcat/backend/boxcat.cpp | 147 | ||||
| -rw-r--r-- | src/core/hle/service/bcat/backend/boxcat.h | 6 | ||||
| -rw-r--r-- | src/core/hle/service/bcat/module.cpp | 56 |
6 files changed, 314 insertions, 88 deletions
diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp index e34474ae0..8bdaa7e4a 100644 --- a/src/core/file_sys/vfs_libzip.cpp +++ b/src/core/file_sys/vfs_libzip.cpp | |||
| @@ -15,13 +15,13 @@ VirtualDir ExtractZIP(VirtualFile file) { | |||
| 15 | zip_error_t error{}; | 15 | zip_error_t error{}; |
| 16 | 16 | ||
| 17 | const auto data = file->ReadAllBytes(); | 17 | const auto data = file->ReadAllBytes(); |
| 18 | std::unique_ptr<zip_source_t, decltype(&zip_source_free)> src{ | 18 | std::unique_ptr<zip_source_t, decltype(&zip_source_close)> src{ |
| 19 | zip_source_buffer_create(data.data(), data.size(), 0, &error), zip_source_free}; | 19 | zip_source_buffer_create(data.data(), data.size(), 0, &error), zip_source_close}; |
| 20 | if (src == nullptr) | 20 | if (src == nullptr) |
| 21 | return nullptr; | 21 | return nullptr; |
| 22 | 22 | ||
| 23 | std::unique_ptr<zip_t, decltype(&zip_discard)> zip{zip_open_from_source(src.get(), 0, &error), | 23 | std::unique_ptr<zip_t, decltype(&zip_close)> zip{zip_open_from_source(src.get(), 0, &error), |
| 24 | zip_discard}; | 24 | zip_close}; |
| 25 | if (zip == nullptr) | 25 | if (zip == nullptr) |
| 26 | return nullptr; | 26 | return nullptr; |
| 27 | 27 | ||
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp index 9a67da2ef..e389ad568 100644 --- a/src/core/hle/service/bcat/backend/backend.cpp +++ b/src/core/hle/service/bcat/backend/backend.cpp | |||
| @@ -4,10 +4,90 @@ | |||
| 4 | 4 | ||
| 5 | #include "common/hex_util.h" | 5 | #include "common/hex_util.h" |
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/core.h" | ||
| 8 | #include "core/hle/lock.h" | ||
| 7 | #include "core/hle/service/bcat/backend/backend.h" | 9 | #include "core/hle/service/bcat/backend/backend.h" |
| 8 | 10 | ||
| 9 | namespace Service::BCAT { | 11 | namespace Service::BCAT { |
| 10 | 12 | ||
| 13 | ProgressServiceBackend::ProgressServiceBackend(std::string event_name) : impl{} { | ||
| 14 | auto& kernel{Core::System::GetInstance().Kernel()}; | ||
| 15 | event = Kernel::WritableEvent::CreateEventPair( | ||
| 16 | kernel, Kernel::ResetType::OneShot, "ProgressServiceBackend:UpdateEvent:" + event_name); | ||
| 17 | } | ||
| 18 | |||
| 19 | Kernel::SharedPtr<Kernel::ReadableEvent> ProgressServiceBackend::GetEvent() { | ||
| 20 | return event.readable; | ||
| 21 | } | ||
| 22 | |||
| 23 | DeliveryCacheProgressImpl& ProgressServiceBackend::GetImpl() { | ||
| 24 | return impl; | ||
| 25 | } | ||
| 26 | |||
| 27 | void ProgressServiceBackend::SetNeedHLELock(bool need) { | ||
| 28 | need_hle_lock = need; | ||
| 29 | } | ||
| 30 | |||
| 31 | void ProgressServiceBackend::SetTotalSize(u64 size) { | ||
| 32 | impl.total_bytes = size; | ||
| 33 | SignalUpdate(); | ||
| 34 | } | ||
| 35 | |||
| 36 | void ProgressServiceBackend::StartConnecting() { | ||
| 37 | impl.status = DeliveryCacheProgressImpl::Status::Connecting; | ||
| 38 | SignalUpdate(); | ||
| 39 | } | ||
| 40 | |||
| 41 | void ProgressServiceBackend::StartProcessingDataList() { | ||
| 42 | impl.status = DeliveryCacheProgressImpl::Status::ProcessingDataList; | ||
| 43 | SignalUpdate(); | ||
| 44 | } | ||
| 45 | |||
| 46 | void ProgressServiceBackend::StartDownloadingFile(std::string_view dir_name, | ||
| 47 | std::string_view file_name, u64 file_size) { | ||
| 48 | impl.status = DeliveryCacheProgressImpl::Status::Downloading; | ||
| 49 | impl.current_downloaded_bytes = 0; | ||
| 50 | impl.current_total_bytes = file_size; | ||
| 51 | std::memcpy(impl.current_directory.data(), dir_name.data(), std::min(dir_name.size(), 0x31ull)); | ||
| 52 | std::memcpy(impl.current_file.data(), file_name.data(), std::min(file_name.size(), 0x31ull)); | ||
| 53 | SignalUpdate(); | ||
| 54 | } | ||
| 55 | |||
| 56 | void ProgressServiceBackend::UpdateFileProgress(u64 downloaded) { | ||
| 57 | impl.current_downloaded_bytes = downloaded; | ||
| 58 | SignalUpdate(); | ||
| 59 | } | ||
| 60 | |||
| 61 | void ProgressServiceBackend::FinishDownloadingFile() { | ||
| 62 | impl.total_downloaded_bytes += impl.current_total_bytes; | ||
| 63 | SignalUpdate(); | ||
| 64 | } | ||
| 65 | |||
| 66 | void ProgressServiceBackend::CommitDirectory(std::string_view dir_name) { | ||
| 67 | impl.status = DeliveryCacheProgressImpl::Status::Committing; | ||
| 68 | impl.current_file.fill(0); | ||
| 69 | impl.current_downloaded_bytes = 0; | ||
| 70 | impl.current_total_bytes = 0; | ||
| 71 | std::memcpy(impl.current_directory.data(), dir_name.data(), std::min(dir_name.size(), 0x31ull)); | ||
| 72 | SignalUpdate(); | ||
| 73 | } | ||
| 74 | |||
| 75 | void ProgressServiceBackend::FinishDownload(ResultCode result) { | ||
| 76 | impl.total_downloaded_bytes = impl.total_bytes; | ||
| 77 | impl.status = DeliveryCacheProgressImpl::Status::Done; | ||
| 78 | impl.result = result; | ||
| 79 | SignalUpdate(); | ||
| 80 | } | ||
| 81 | |||
| 82 | void ProgressServiceBackend::SignalUpdate() const { | ||
| 83 | if (need_hle_lock) { | ||
| 84 | std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock); | ||
| 85 | event.writable->Signal(); | ||
| 86 | } else { | ||
| 87 | event.writable->Signal(); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 11 | Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} | 91 | Backend::Backend(DirectoryGetter getter) : dir_getter(std::move(getter)) {} |
| 12 | 92 | ||
| 13 | Backend::~Backend() = default; | 93 | Backend::~Backend() = default; |
| @@ -16,20 +96,20 @@ NullBackend::NullBackend(const DirectoryGetter& getter) : Backend(std::move(gett | |||
| 16 | 96 | ||
| 17 | NullBackend::~NullBackend() = default; | 97 | NullBackend::~NullBackend() = default; |
| 18 | 98 | ||
| 19 | bool NullBackend::Synchronize(TitleIDVersion title, CompletionCallback callback) { | 99 | bool NullBackend::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { |
| 20 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, | 100 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}", title.title_id, |
| 21 | title.build_id); | 101 | title.build_id); |
| 22 | 102 | ||
| 23 | callback(true); | 103 | progress.FinishDownload(RESULT_SUCCESS); |
| 24 | return true; | 104 | return true; |
| 25 | } | 105 | } |
| 26 | 106 | ||
| 27 | bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, | 107 | bool NullBackend::SynchronizeDirectory(TitleIDVersion title, std::string name, |
| 28 | CompletionCallback callback) { | 108 | ProgressServiceBackend& progress) { |
| 29 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id, | 109 | LOG_DEBUG(Service_BCAT, "called, title_id={:016X}, build_id={:016X}, name={}", title.title_id, |
| 30 | title.build_id, name); | 110 | title.build_id, name); |
| 31 | 111 | ||
| 32 | callback(true); | 112 | progress.FinishDownload(RESULT_SUCCESS); |
| 33 | return true; | 113 | return true; |
| 34 | } | 114 | } |
| 35 | 115 | ||
diff --git a/src/core/hle/service/bcat/backend/backend.h b/src/core/hle/service/bcat/backend/backend.h index 5b4118814..50973a13a 100644 --- a/src/core/hle/service/bcat/backend/backend.h +++ b/src/core/hle/service/bcat/backend/backend.h | |||
| @@ -8,10 +8,14 @@ | |||
| 8 | #include <optional> | 8 | #include <optional> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/file_sys/vfs_types.h" | 10 | #include "core/file_sys/vfs_types.h" |
| 11 | #include "core/hle/kernel/readable_event.h" | ||
| 12 | #include "core/hle/kernel/writable_event.h" | ||
| 13 | #include "core/hle/result.h" | ||
| 11 | 14 | ||
| 12 | namespace Service::BCAT { | 15 | namespace Service::BCAT { |
| 13 | 16 | ||
| 14 | using CompletionCallback = std::function<void(bool)>; | 17 | struct DeliveryCacheProgressImpl; |
| 18 | |||
| 15 | using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>; | 19 | using DirectoryGetter = std::function<FileSys::VirtualDir(u64)>; |
| 16 | using Passphrase = std::array<u8, 0x20>; | 20 | using Passphrase = std::array<u8, 0x20>; |
| 17 | 21 | ||
| @@ -20,33 +24,116 @@ struct TitleIDVersion { | |||
| 20 | u64 build_id; | 24 | u64 build_id; |
| 21 | }; | 25 | }; |
| 22 | 26 | ||
| 27 | using DirectoryName = std::array<char, 0x20>; | ||
| 28 | using FileName = std::array<char, 0x20>; | ||
| 29 | |||
| 30 | struct DeliveryCacheProgressImpl { | ||
| 31 | enum class Status : s32 { | ||
| 32 | None = 0x0, | ||
| 33 | Queued = 0x1, | ||
| 34 | Connecting = 0x2, | ||
| 35 | ProcessingDataList = 0x3, | ||
| 36 | Downloading = 0x4, | ||
| 37 | Committing = 0x5, | ||
| 38 | Done = 0x9, | ||
| 39 | }; | ||
| 40 | |||
| 41 | Status status; | ||
| 42 | ResultCode result = RESULT_SUCCESS; | ||
| 43 | DirectoryName current_directory; | ||
| 44 | FileName current_file; | ||
| 45 | s64 current_downloaded_bytes; ///< Bytes downloaded on current file. | ||
| 46 | s64 current_total_bytes; ///< Bytes total on current file. | ||
| 47 | s64 total_downloaded_bytes; ///< Bytes downloaded on overall download. | ||
| 48 | s64 total_bytes; ///< Bytes total on overall download. | ||
| 49 | INSERT_PADDING_BYTES( | ||
| 50 | 0x198); ///< Appears to be unused in official code, possibly reserved for future use. | ||
| 51 | }; | ||
| 52 | static_assert(sizeof(DeliveryCacheProgressImpl) == 0x200, | ||
| 53 | "DeliveryCacheProgressImpl has incorrect size."); | ||
| 54 | |||
| 55 | // A class to manage the signalling to the game about BCAT download progress. | ||
| 56 | // Some of this class is implemented in module.cpp to avoid exposing the implementation structure. | ||
| 57 | class ProgressServiceBackend { | ||
| 58 | friend class IBcatService; | ||
| 59 | |||
| 60 | ProgressServiceBackend(std::string event_name); | ||
| 61 | |||
| 62 | Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(); | ||
| 63 | DeliveryCacheProgressImpl& GetImpl(); | ||
| 64 | |||
| 65 | public: | ||
| 66 | // Clients should call this with true if any of the functions are going to be called from a | ||
| 67 | // non-HLE thread and this class need to lock the hle mutex. (default is false) | ||
| 68 | void SetNeedHLELock(bool need); | ||
| 69 | |||
| 70 | // Sets the number of bytes total in the entire download. | ||
| 71 | void SetTotalSize(u64 size); | ||
| 72 | |||
| 73 | // Notifies the application that the backend has started connecting to the server. | ||
| 74 | void StartConnecting(); | ||
| 75 | // Notifies the application that the backend has begun accumulating and processing metadata. | ||
| 76 | void StartProcessingDataList(); | ||
| 77 | |||
| 78 | // Notifies the application that a file is starting to be downloaded. | ||
| 79 | void StartDownloadingFile(std::string_view dir_name, std::string_view file_name, u64 file_size); | ||
| 80 | // Updates the progress of the current file to the size passed. | ||
| 81 | void UpdateFileProgress(u64 downloaded); | ||
| 82 | // Notifies the application that the current file has completed download. | ||
| 83 | void FinishDownloadingFile(); | ||
| 84 | |||
| 85 | // Notifies the application that all files in this directory have completed and are being | ||
| 86 | // finalized. | ||
| 87 | void CommitDirectory(std::string_view dir_name); | ||
| 88 | |||
| 89 | // Notifies the application that the operation completed with result code result. | ||
| 90 | void FinishDownload(ResultCode result); | ||
| 91 | |||
| 92 | private: | ||
| 93 | void SignalUpdate() const; | ||
| 94 | |||
| 95 | DeliveryCacheProgressImpl impl; | ||
| 96 | Kernel::EventPair event; | ||
| 97 | bool need_hle_lock = false; | ||
| 98 | }; | ||
| 99 | |||
| 100 | // A class representing an abstract backend for BCAT functionality. | ||
| 23 | class Backend { | 101 | class Backend { |
| 24 | public: | 102 | public: |
| 25 | explicit Backend(DirectoryGetter getter); | 103 | explicit Backend(DirectoryGetter getter); |
| 26 | virtual ~Backend(); | 104 | virtual ~Backend(); |
| 27 | 105 | ||
| 28 | virtual bool Synchronize(TitleIDVersion title, CompletionCallback callback) = 0; | 106 | // Called when the backend is needed to synchronize the data for the game with title ID and |
| 107 | // version in title. A ProgressServiceBackend object is provided to alert the application of | ||
| 108 | // status. | ||
| 109 | virtual bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) = 0; | ||
| 110 | // Very similar to Synchronize, but only for the directory provided. Backends should not alter | ||
| 111 | // the data for any other directories. | ||
| 29 | virtual bool SynchronizeDirectory(TitleIDVersion title, std::string name, | 112 | virtual bool SynchronizeDirectory(TitleIDVersion title, std::string name, |
| 30 | CompletionCallback callback) = 0; | 113 | ProgressServiceBackend& progress) = 0; |
| 31 | 114 | ||
| 115 | // Removes all cached data associated with title id provided. | ||
| 32 | virtual bool Clear(u64 title_id) = 0; | 116 | virtual bool Clear(u64 title_id) = 0; |
| 33 | 117 | ||
| 118 | // Sets the BCAT Passphrase to be used with the associated title ID. | ||
| 34 | virtual void SetPassphrase(u64 title_id, const Passphrase& passphrase) = 0; | 119 | virtual void SetPassphrase(u64 title_id, const Passphrase& passphrase) = 0; |
| 35 | 120 | ||
| 121 | // Gets the launch parameter used by AM associated with the title ID and version provided. | ||
| 36 | virtual std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) = 0; | 122 | virtual std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) = 0; |
| 37 | 123 | ||
| 38 | protected: | 124 | protected: |
| 39 | DirectoryGetter dir_getter; | 125 | DirectoryGetter dir_getter; |
| 40 | }; | 126 | }; |
| 41 | 127 | ||
| 128 | // A backend of BCAT that provides no operation. | ||
| 42 | class NullBackend : public Backend { | 129 | class NullBackend : public Backend { |
| 43 | public: | 130 | public: |
| 44 | explicit NullBackend(const DirectoryGetter& getter); | 131 | explicit NullBackend(const DirectoryGetter& getter); |
| 45 | ~NullBackend() override; | 132 | ~NullBackend() override; |
| 46 | 133 | ||
| 47 | bool Synchronize(TitleIDVersion title, CompletionCallback callback) override; | 134 | bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; |
| 48 | bool SynchronizeDirectory(TitleIDVersion title, std::string name, | 135 | bool SynchronizeDirectory(TitleIDVersion title, std::string name, |
| 49 | CompletionCallback callback) override; | 136 | ProgressServiceBackend& progress) override; |
| 50 | 137 | ||
| 51 | bool Clear(u64 title_id) override; | 138 | bool Clear(u64 title_id) override; |
| 52 | 139 | ||
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index 31d2e045c..3754594df 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp | |||
| @@ -14,13 +14,28 @@ | |||
| 14 | #include "core/file_sys/vfs_libzip.h" | 14 | #include "core/file_sys/vfs_libzip.h" |
| 15 | #include "core/file_sys/vfs_vector.h" | 15 | #include "core/file_sys/vfs_vector.h" |
| 16 | #include "core/frontend/applets/error.h" | 16 | #include "core/frontend/applets/error.h" |
| 17 | #include "core/hle/lock.h" | ||
| 18 | #include "core/hle/service/am/applets/applets.h" | 17 | #include "core/hle/service/am/applets/applets.h" |
| 19 | #include "core/hle/service/bcat/backend/boxcat.h" | 18 | #include "core/hle/service/bcat/backend/boxcat.h" |
| 20 | #include "core/settings.h" | 19 | #include "core/settings.h" |
| 21 | 20 | ||
| 21 | namespace { | ||
| 22 | |||
| 23 | // Prevents conflicts with windows macro called CreateFile | ||
| 24 | FileSys::VirtualFile VfsCreateFileWrap(FileSys::VirtualDir dir, std::string_view name) { | ||
| 25 | return dir->CreateFile(name); | ||
| 26 | } | ||
| 27 | |||
| 28 | // Prevents conflicts with windows macro called DeleteFile | ||
| 29 | bool VfsDeleteFileWrap(FileSys::VirtualDir dir, std::string_view name) { | ||
| 30 | return dir->DeleteFile(name); | ||
| 31 | } | ||
| 32 | |||
| 33 | } // Anonymous namespace | ||
| 34 | |||
| 22 | namespace Service::BCAT { | 35 | namespace Service::BCAT { |
| 23 | 36 | ||
| 37 | constexpr ResultCode ERROR_GENERAL_BCAT_FAILURE{ErrorModule::BCAT, 1}; | ||
| 38 | |||
| 24 | constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org"; | 39 | constexpr char BOXCAT_HOSTNAME[] = "api.yuzu-emu.org"; |
| 25 | 40 | ||
| 26 | // Formatted using fmt with arg[0] = hex title id | 41 | // Formatted using fmt with arg[0] = hex title id |
| @@ -102,7 +117,68 @@ void HandleDownloadDisplayResult(DownloadResult res) { | |||
| 102 | DOWNLOAD_RESULT_LOG_MESSAGES[static_cast<std::size_t>(res)], [] {}); | 117 | DOWNLOAD_RESULT_LOG_MESSAGES[static_cast<std::size_t>(res)], [] {}); |
| 103 | } | 118 | } |
| 104 | 119 | ||
| 105 | } // namespace | 120 | bool VfsRawCopyProgress(FileSys::VirtualFile src, FileSys::VirtualFile dest, |
| 121 | std::string_view dir_name, ProgressServiceBackend& progress, | ||
| 122 | std::size_t block_size = 0x1000) { | ||
| 123 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) | ||
| 124 | return false; | ||
| 125 | if (!dest->Resize(src->GetSize())) | ||
| 126 | return false; | ||
| 127 | |||
| 128 | progress.StartDownloadingFile(dir_name, src->GetName(), src->GetSize()); | ||
| 129 | |||
| 130 | std::vector<u8> temp(std::min(block_size, src->GetSize())); | ||
| 131 | for (std::size_t i = 0; i < src->GetSize(); i += block_size) { | ||
| 132 | const auto read = std::min(block_size, src->GetSize() - i); | ||
| 133 | |||
| 134 | if (src->Read(temp.data(), read, i) != read) { | ||
| 135 | return false; | ||
| 136 | } | ||
| 137 | |||
| 138 | if (dest->Write(temp.data(), read, i) != read) { | ||
| 139 | return false; | ||
| 140 | } | ||
| 141 | |||
| 142 | progress.UpdateFileProgress(i); | ||
| 143 | } | ||
| 144 | |||
| 145 | progress.FinishDownloadingFile(); | ||
| 146 | |||
| 147 | return true; | ||
| 148 | } | ||
| 149 | |||
| 150 | bool VfsRawCopyDProgressSingle(FileSys::VirtualDir src, FileSys::VirtualDir dest, | ||
| 151 | ProgressServiceBackend& progress, std::size_t block_size = 0x1000) { | ||
| 152 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) | ||
| 153 | return false; | ||
| 154 | |||
| 155 | for (const auto& file : src->GetFiles()) { | ||
| 156 | const auto out_file = VfsCreateFileWrap(dest, file->GetName()); | ||
| 157 | if (!VfsRawCopyProgress(file, out_file, src->GetName(), progress, block_size)) { | ||
| 158 | return false; | ||
| 159 | } | ||
| 160 | } | ||
| 161 | progress.CommitDirectory(src->GetName()); | ||
| 162 | |||
| 163 | return true; | ||
| 164 | } | ||
| 165 | |||
| 166 | bool VfsRawCopyDProgress(FileSys::VirtualDir src, FileSys::VirtualDir dest, | ||
| 167 | ProgressServiceBackend& progress, std::size_t block_size = 0x1000) { | ||
| 168 | if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable()) | ||
| 169 | return false; | ||
| 170 | |||
| 171 | for (const auto& dir : src->GetSubdirectories()) { | ||
| 172 | const auto out = dest->CreateSubdirectory(dir->GetName()); | ||
| 173 | if (!VfsRawCopyDProgressSingle(dir, out, progress, block_size)) { | ||
| 174 | return false; | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | return true; | ||
| 179 | } | ||
| 180 | |||
| 181 | } // Anonymous namespace | ||
| 106 | 182 | ||
| 107 | class Boxcat::Client { | 183 | class Boxcat::Client { |
| 108 | public: | 184 | public: |
| @@ -194,24 +270,24 @@ Boxcat::Boxcat(DirectoryGetter getter) : Backend(std::move(getter)) {} | |||
| 194 | Boxcat::~Boxcat() = default; | 270 | Boxcat::~Boxcat() = default; |
| 195 | 271 | ||
| 196 | void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, | 272 | void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, |
| 197 | CompletionCallback callback, std::optional<std::string> dir_name = {}) { | 273 | ProgressServiceBackend& progress, |
| 198 | const auto failure = [&callback] { | 274 | std::optional<std::string> dir_name = {}) { |
| 199 | // Acquire the HLE mutex | 275 | progress.SetNeedHLELock(true); |
| 200 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 201 | callback(false); | ||
| 202 | }; | ||
| 203 | 276 | ||
| 204 | if (Settings::values.bcat_boxcat_local) { | 277 | if (Settings::values.bcat_boxcat_local) { |
| 205 | LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download."); | 278 | LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download."); |
| 206 | // Acquire the HLE mutex | 279 | const auto dir = dir_getter(title.title_id); |
| 207 | std::lock_guard lock{HLE::g_hle_lock}; | 280 | if (dir) |
| 208 | callback(true); | 281 | progress.SetTotalSize(dir->GetSize()); |
| 282 | progress.FinishDownload(RESULT_SUCCESS); | ||
| 209 | return; | 283 | return; |
| 210 | } | 284 | } |
| 211 | 285 | ||
| 212 | const auto zip_path{GetZIPFilePath(title.title_id)}; | 286 | const auto zip_path{GetZIPFilePath(title.title_id)}; |
| 213 | Boxcat::Client client{zip_path, title.title_id, title.build_id}; | 287 | Boxcat::Client client{zip_path, title.title_id, title.build_id}; |
| 214 | 288 | ||
| 289 | progress.StartConnecting(); | ||
| 290 | |||
| 215 | const auto res = client.DownloadDataZip(); | 291 | const auto res = client.DownloadDataZip(); |
| 216 | if (res != DownloadResult::Success) { | 292 | if (res != DownloadResult::Success) { |
| 217 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); | 293 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); |
| @@ -221,68 +297,85 @@ void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, | |||
| 221 | } | 297 | } |
| 222 | 298 | ||
| 223 | HandleDownloadDisplayResult(res); | 299 | HandleDownloadDisplayResult(res); |
| 224 | failure(); | 300 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); |
| 225 | return; | 301 | return; |
| 226 | } | 302 | } |
| 227 | 303 | ||
| 304 | progress.StartProcessingDataList(); | ||
| 305 | |||
| 228 | FileUtil::IOFile zip{zip_path, "rb"}; | 306 | FileUtil::IOFile zip{zip_path, "rb"}; |
| 229 | const auto size = zip.GetSize(); | 307 | const auto size = zip.GetSize(); |
| 230 | std::vector<u8> bytes(size); | 308 | std::vector<u8> bytes(size); |
| 231 | if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { | 309 | if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { |
| 232 | LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", zip_path); | 310 | LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", zip_path); |
| 233 | failure(); | 311 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); |
| 234 | return; | 312 | return; |
| 235 | } | 313 | } |
| 236 | 314 | ||
| 237 | const auto extracted = FileSys::ExtractZIP(std::make_shared<FileSys::VectorVfsFile>(bytes)); | 315 | const auto extracted = FileSys::ExtractZIP(std::make_shared<FileSys::VectorVfsFile>(bytes)); |
| 238 | if (extracted == nullptr) { | 316 | if (extracted == nullptr) { |
| 239 | LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!"); | 317 | LOG_ERROR(Service_BCAT, "Boxcat failed to extract ZIP file!"); |
| 240 | failure(); | 318 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); |
| 241 | return; | 319 | return; |
| 242 | } | 320 | } |
| 243 | 321 | ||
| 244 | if (dir_name == std::nullopt) { | 322 | if (dir_name == std::nullopt) { |
| 323 | progress.SetTotalSize(extracted->GetSize()); | ||
| 324 | |||
| 245 | const auto target_dir = dir_getter(title.title_id); | 325 | const auto target_dir = dir_getter(title.title_id); |
| 246 | if (target_dir == nullptr || | 326 | if (target_dir == nullptr || !VfsRawCopyDProgress(extracted, target_dir, progress)) { |
| 247 | !FileSys::VfsRawCopyD(extracted, target_dir, VFS_COPY_BLOCK_SIZE)) { | ||
| 248 | LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); | 327 | LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); |
| 249 | failure(); | 328 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); |
| 250 | return; | 329 | return; |
| 251 | } | 330 | } |
| 252 | } else { | 331 | } else { |
| 253 | const auto target_dir = dir_getter(title.title_id); | 332 | const auto target_dir = dir_getter(title.title_id); |
| 254 | if (target_dir == nullptr) { | 333 | if (target_dir == nullptr) { |
| 255 | LOG_ERROR(Service_BCAT, "Boxcat failed to get directory for title ID!"); | 334 | LOG_ERROR(Service_BCAT, "Boxcat failed to get directory for title ID!"); |
| 256 | failure(); | 335 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); |
| 257 | return; | 336 | return; |
| 258 | } | 337 | } |
| 259 | 338 | ||
| 260 | const auto target_sub = target_dir->GetSubdirectory(*dir_name); | 339 | const auto target_sub = target_dir->GetSubdirectory(*dir_name); |
| 261 | const auto source_sub = extracted->GetSubdirectory(*dir_name); | 340 | const auto source_sub = extracted->GetSubdirectory(*dir_name); |
| 262 | 341 | ||
| 342 | progress.SetTotalSize(source_sub->GetSize()); | ||
| 343 | |||
| 344 | std::vector<std::string> filenames; | ||
| 345 | { | ||
| 346 | const auto files = target_sub->GetFiles(); | ||
| 347 | std::transform(files.begin(), files.end(), std::back_inserter(filenames), | ||
| 348 | [](const auto& vfile) { return vfile->GetName(); }); | ||
| 349 | } | ||
| 350 | |||
| 351 | for (const auto& filename : filenames) { | ||
| 352 | VfsDeleteFileWrap(target_sub, filename); | ||
| 353 | } | ||
| 354 | |||
| 263 | if (target_sub == nullptr || source_sub == nullptr || | 355 | if (target_sub == nullptr || source_sub == nullptr || |
| 264 | !FileSys::VfsRawCopyD(source_sub, target_sub, VFS_COPY_BLOCK_SIZE)) { | 356 | !VfsRawCopyDProgressSingle(source_sub, target_sub, progress)) { |
| 265 | LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); | 357 | LOG_ERROR(Service_BCAT, "Boxcat failed to copy extracted ZIP to target directory!"); |
| 266 | failure(); | 358 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); |
| 267 | return; | 359 | return; |
| 268 | } | 360 | } |
| 269 | } | 361 | } |
| 270 | 362 | ||
| 271 | // Acquire the HLE mutex | 363 | progress.FinishDownload(RESULT_SUCCESS); |
| 272 | std::lock_guard lock{HLE::g_hle_lock}; | ||
| 273 | callback(true); | ||
| 274 | } | 364 | } |
| 275 | 365 | ||
| 276 | bool Boxcat::Synchronize(TitleIDVersion title, CompletionCallback callback) { | 366 | bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) { |
| 277 | is_syncing.exchange(true); | 367 | is_syncing.exchange(true); |
| 278 | std::thread(&SynchronizeInternal, dir_getter, title, callback, std::nullopt).detach(); | 368 | std::thread([this, title, &progress] { SynchronizeInternal(dir_getter, title, progress); }) |
| 369 | .detach(); | ||
| 279 | return true; | 370 | return true; |
| 280 | } | 371 | } |
| 281 | 372 | ||
| 282 | bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name, | 373 | bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name, |
| 283 | CompletionCallback callback) { | 374 | ProgressServiceBackend& progress) { |
| 284 | is_syncing.exchange(true); | 375 | is_syncing.exchange(true); |
| 285 | std::thread(&SynchronizeInternal, dir_getter, title, callback, name).detach(); | 376 | std::thread( |
| 377 | [this, title, name, &progress] { SynchronizeInternal(dir_getter, title, progress, name); }) | ||
| 378 | .detach(); | ||
| 286 | return true; | 379 | return true; |
| 287 | } | 380 | } |
| 288 | 381 | ||
diff --git a/src/core/hle/service/bcat/backend/boxcat.h b/src/core/hle/service/bcat/backend/boxcat.h index 1148a4eca..601151189 100644 --- a/src/core/hle/service/bcat/backend/boxcat.h +++ b/src/core/hle/service/bcat/backend/boxcat.h | |||
| @@ -21,16 +21,16 @@ struct EventStatus { | |||
| 21 | /// doesn't require a switch or nintendo account. The content is controlled by the yuzu team. | 21 | /// doesn't require a switch or nintendo account. The content is controlled by the yuzu team. |
| 22 | class Boxcat final : public Backend { | 22 | class Boxcat final : public Backend { |
| 23 | friend void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, | 23 | friend void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title, |
| 24 | CompletionCallback callback, | 24 | ProgressServiceBackend& progress, |
| 25 | std::optional<std::string> dir_name); | 25 | std::optional<std::string> dir_name); |
| 26 | 26 | ||
| 27 | public: | 27 | public: |
| 28 | explicit Boxcat(DirectoryGetter getter); | 28 | explicit Boxcat(DirectoryGetter getter); |
| 29 | ~Boxcat() override; | 29 | ~Boxcat() override; |
| 30 | 30 | ||
| 31 | bool Synchronize(TitleIDVersion title, CompletionCallback callback) override; | 31 | bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override; |
| 32 | bool SynchronizeDirectory(TitleIDVersion title, std::string name, | 32 | bool SynchronizeDirectory(TitleIDVersion title, std::string name, |
| 33 | CompletionCallback callback) override; | 33 | ProgressServiceBackend& progress) override; |
| 34 | 34 | ||
| 35 | bool Clear(u64 title_id) override; | 35 | bool Clear(u64 title_id) override; |
| 36 | 36 | ||
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp index a8d545992..d5f9e9d3b 100644 --- a/src/core/hle/service/bcat/module.cpp +++ b/src/core/hle/service/bcat/module.cpp | |||
| @@ -33,20 +33,6 @@ constexpr ResultCode ERROR_FAILED_CLEAR_CACHE{ErrorModule::FS, 6400}; | |||
| 33 | 33 | ||
| 34 | using BCATDigest = std::array<u8, 0x10>; | 34 | using BCATDigest = std::array<u8, 0x10>; |
| 35 | 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 { | 36 | namespace { |
| 51 | 37 | ||
| 52 | u64 GetCurrentBuildID() { | 38 | u64 GetCurrentBuildID() { |
| @@ -84,19 +70,16 @@ bool VerifyNameValidInternal(Kernel::HLERequestContext& ctx, std::array<char, 0x | |||
| 84 | return true; | 70 | return true; |
| 85 | } | 71 | } |
| 86 | 72 | ||
| 87 | bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name) { | 73 | bool VerifyNameValidDir(Kernel::HLERequestContext& ctx, DirectoryName name) { |
| 88 | return VerifyNameValidInternal(ctx, name, '-'); | 74 | return VerifyNameValidInternal(ctx, name, '-'); |
| 89 | } | 75 | } |
| 90 | 76 | ||
| 91 | bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, std::array<char, 0x20> name) { | 77 | bool VerifyNameValidFile(Kernel::HLERequestContext& ctx, FileName name) { |
| 92 | return VerifyNameValidInternal(ctx, name, '.'); | 78 | return VerifyNameValidInternal(ctx, name, '.'); |
| 93 | } | 79 | } |
| 94 | 80 | ||
| 95 | } // Anonymous namespace | 81 | } // Anonymous namespace |
| 96 | 82 | ||
| 97 | using DirectoryName = std::array<char, 0x20>; | ||
| 98 | using FileName = std::array<char, 0x20>; | ||
| 99 | |||
| 100 | struct DeliveryCacheDirectoryEntry { | 83 | struct DeliveryCacheDirectoryEntry { |
| 101 | FileName name; | 84 | FileName name; |
| 102 | u64 size; | 85 | u64 size; |
| @@ -162,15 +145,6 @@ public: | |||
| 162 | }; | 145 | }; |
| 163 | // clang-format on | 146 | // clang-format on |
| 164 | RegisterHandlers(functions); | 147 | RegisterHandlers(functions); |
| 165 | |||
| 166 | auto& kernel{Core::System::GetInstance().Kernel()}; | ||
| 167 | progress.at(static_cast<std::size_t>(SyncType::Normal)).event = | ||
| 168 | Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky, | ||
| 169 | "BCAT::IDeliveryCacheProgressEvent"); | ||
| 170 | progress.at(static_cast<std::size_t>(SyncType::Directory)).event = | ||
| 171 | Kernel::WritableEvent::CreateEventPair( | ||
| 172 | kernel, Kernel::ResetType::OneShot, | ||
| 173 | "BCAT::IDeliveryCacheProgressEvent::DirectoryName"); | ||
| 174 | } | 148 | } |
| 175 | 149 | ||
| 176 | private: | 150 | private: |
| @@ -180,24 +154,17 @@ private: | |||
| 180 | Count, | 154 | Count, |
| 181 | }; | 155 | }; |
| 182 | 156 | ||
| 183 | std::function<void(bool)> CreateCallback(SyncType type) { | ||
| 184 | return [this, type](bool success) { | ||
| 185 | auto& pair{progress.at(static_cast<std::size_t>(type))}; | ||
| 186 | pair.impl.status = DeliveryCacheProgressImpl::Status::Complete; | ||
| 187 | pair.event.writable->Signal(); | ||
| 188 | }; | ||
| 189 | } | ||
| 190 | |||
| 191 | std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) { | 157 | std::shared_ptr<IDeliveryCacheProgressService> CreateProgressService(SyncType type) { |
| 192 | const auto& pair{progress.at(static_cast<std::size_t>(type))}; | 158 | auto& backend{progress.at(static_cast<std::size_t>(type))}; |
| 193 | return std::make_shared<IDeliveryCacheProgressService>(pair.event.readable, pair.impl); | 159 | return std::make_shared<IDeliveryCacheProgressService>(backend.GetEvent(), |
| 160 | backend.GetImpl()); | ||
| 194 | } | 161 | } |
| 195 | 162 | ||
| 196 | void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) { | 163 | void RequestSyncDeliveryCache(Kernel::HLERequestContext& ctx) { |
| 197 | LOG_DEBUG(Service_BCAT, "called"); | 164 | LOG_DEBUG(Service_BCAT, "called"); |
| 198 | 165 | ||
| 199 | backend.Synchronize({Core::CurrentProcess()->GetTitleID(), GetCurrentBuildID()}, | 166 | backend.Synchronize({Core::CurrentProcess()->GetTitleID(), GetCurrentBuildID()}, |
| 200 | CreateCallback(SyncType::Normal)); | 167 | progress.at(static_cast<std::size_t>(SyncType::Normal))); |
| 201 | 168 | ||
| 202 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 169 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 203 | rb.Push(RESULT_SUCCESS); | 170 | rb.Push(RESULT_SUCCESS); |
| @@ -213,7 +180,8 @@ private: | |||
| 213 | LOG_DEBUG(Service_BCAT, "called, name={}", name); | 180 | LOG_DEBUG(Service_BCAT, "called, name={}", name); |
| 214 | 181 | ||
| 215 | backend.SynchronizeDirectory({Core::CurrentProcess()->GetTitleID(), GetCurrentBuildID()}, | 182 | backend.SynchronizeDirectory({Core::CurrentProcess()->GetTitleID(), GetCurrentBuildID()}, |
| 216 | name, CreateCallback(SyncType::Directory)); | 183 | name, |
| 184 | progress.at(static_cast<std::size_t>(SyncType::Directory))); | ||
| 217 | 185 | ||
| 218 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 186 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 219 | rb.Push(RESULT_SUCCESS); | 187 | rb.Push(RESULT_SUCCESS); |
| @@ -278,12 +246,10 @@ private: | |||
| 278 | 246 | ||
| 279 | Backend& backend; | 247 | Backend& backend; |
| 280 | 248 | ||
| 281 | struct ProgressPair { | 249 | std::array<ProgressServiceBackend, static_cast<std::size_t>(SyncType::Count)> progress{ |
| 282 | Kernel::EventPair event; | 250 | ProgressServiceBackend{"Normal"}, |
| 283 | DeliveryCacheProgressImpl impl; | 251 | ProgressServiceBackend{"Directory"}, |
| 284 | }; | 252 | }; |
| 285 | |||
| 286 | std::array<ProgressPair, static_cast<std::size_t>(SyncType::Count)> progress{}; | ||
| 287 | }; | 253 | }; |
| 288 | 254 | ||
| 289 | void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { | 255 | void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) { |