diff --git a/Engine/Vfs/mod_merge.cpp b/Engine/Vfs/mod_merge.cpp index 4a96e09..caeadd1 100644 --- a/Engine/Vfs/mod_merge.cpp +++ b/Engine/Vfs/mod_merge.cpp @@ -289,8 +289,8 @@ MergeReport merge_mods(const Catalog& catalog, const MergeObserver& observe, con // Archive 1 stays the patch's own file, never a link to a mod's: // a live apply appends manifests and new mods to it while the // game has it open, and a linked file cannot be swapped for a - // private copy then (Windows keeps it in use). - claimed.insert(manifestArchive); + // private copy then (Windows keeps it in use). Placeholder indexes name no file. + claimed.insert({manifestArchive, CasStore::waiting_archive, CasStore::carried_archive}); if (number == manifestArchive) { if (const auto free = claim(directory); free) { spot.archive = *free; diff --git a/Engine/Vfs/mod_merge_cas_store.cpp b/Engine/Vfs/mod_merge_cas_store.cpp index 2004ccb..bb10d7e 100644 --- a/Engine/Vfs/mod_merge_cas_store.cpp +++ b/Engine/Vfs/mod_merge_cas_store.cpp @@ -84,19 +84,19 @@ CasStore CasStore::waiting() const { copy.held_.clear(); copy.offsets_.clear(); copy.carried_.clear(); - copy.carriedIndex_.clear(); copy.carriedAt_.clear(); return copy; } fb::BundleFileInfo CasStore::carry(std::uint32_t installChunk, std::uint16_t archive, std::span encoded) { if (!waiting_) throw std::logic_error("Only a waiting store carries payloads"); - // An install chunk no package names fails here, for this payload alone, and not when it settles. - (void)archive_path(installChunk, archive); - const std::pair key{installChunk, sha1_of(encoded)}; - const auto [found, fresh] = carriedIndex_.try_emplace(key, carried_.size()); - if (fresh) carried_.push_back({installChunk, archive, key.second, encoded, {}}); - return {{true, installChunk, carried_archive}, static_cast(found->second), + if (placeholder(archive) || (archive_ && *archive_ != archive)) + throw std::logic_error("A waiting store writes to one archive index"); + // An install chunk no package names fails here, for this payload alone. + (void)directory(installChunk); + archive_ = archive; + carried_.push_back({installChunk, sha1_of(encoded), encoded, {}}); + return {{true, installChunk, carried_archive}, static_cast(carried_.size() - 1), static_cast(encoded.size())}; } @@ -106,13 +106,12 @@ void CasStore::settle(CasStore& waiting) { held.at = append(fs::path(path), held.bytes); held.bytes = {}; } - // Then the carried assets, in the order the superbundle first named them. An asset an - // earlier superbundle settled in this install chunk is not written again. + // Then the carried assets, each written once per install chunk. for (auto& carried : waiting.carried_) { const std::pair key{carried.installChunk, carried.sha1}; auto found = carriedAt_.find(key); if (found == carriedAt_.end()) - found = carriedAt_.emplace(key, write(carried.installChunk, carried.archive, carried.bytes)).first; + found = carriedAt_.emplace(key, write(carried.installChunk, *waiting.archive_, carried.bytes)).first; carried.at = found->second; } } diff --git a/Engine/Vfs/mod_merge_combine.cpp b/Engine/Vfs/mod_merge_combine.cpp index 9c82a79..310d48e 100644 --- a/Engine/Vfs/mod_merge_combine.cpp +++ b/Engine/Vfs/mod_merge_combine.cpp @@ -370,8 +370,7 @@ fb::TocDocument combine(const fs::path& baseToc, const fs::path& baseRoot, else if (addition.asset.kind == fb::AssetKind::resource) companions.emplace(std::tuple{bundleName, addition.mod, lower(addition.asset.name)}, &addition); } - // Where an addition's payload already went in this superbundle's patch - // archive, by install chunk: every copy that is given it reads the one. + // The carried place each addition got in this superbundle, by install chunk. std::map, fb::BundleFileInfo> carriedAt; // The names any mod changed, to rule the rest out without a copy of each (name_hash). std::unordered_set changedNames, scriptNames; diff --git a/Engine/Vfs/mod_merge_internal.h b/Engine/Vfs/mod_merge_internal.h index b34f9b5..f96758a 100644 --- a/Engine/Vfs/mod_merge_internal.h +++ b/Engine/Vfs/mod_merge_internal.h @@ -132,8 +132,7 @@ public: // own has been settled, settle() appends what it holds to the real archives, and // settled() then moves a placement that points into it to where that went. static constexpr std::uint16_t waiting_archive = 0xFFFF; - // An asset carried into copies in several superbundles. A waiting store names it by its - // place in the store's carried list, and settle() writes it once per install chunk. + // A carried payload, by its index in the waiting store's list: settle() writes it once per install chunk. static constexpr std::uint16_t carried_archive = 0xFFFE; [[nodiscard]] CasStore waiting() const; [[nodiscard]] bool holding() const noexcept { return !held_.empty() || !carried_.empty(); } @@ -169,16 +168,15 @@ private: bool waiting_{}; std::optional archive_; std::map held_; + // Carried payloads (waiting store) and where each landed (settling store). struct Carried { std::uint32_t installChunk{}; - std::uint16_t archive{}; fb::Sha1 sha1; std::span bytes; - fb::BundleFileInfo at; // where settle() put it + fb::BundleFileInfo at; }; - std::vector carried_; // waiting: first-reference order - std::map, std::size_t> carriedIndex_; // waiting: into carried_ - std::map, fb::BundleFileInfo> carriedAt_; // settled: one per install chunk + std::vector carried_; + std::map, fb::BundleFileInfo> carriedAt_; }; using ArchiveUse = std::set>;