Three bugs in the async version of UnpWriteBuf (used only in the async
decompression path) were causing data corruption when decompressing RAR5
archives containing executable filters (E8/E8E9/ARM) whose blocks spanned
the 4MB write boundary (UNPACK_MAX_WRITE):
1. WrittenFileSize was only incremented inside `if (OutMem != null)`, but
should always be incremented after ApplyFilter (matching sync behavior).
2. UnpSomeRead = true was missing from the filter processing path.
3. In the NotAllFiltersProcessed else branch (filter intersects write border):
- WrPtr was not updated to WrittenBorder, corrupting the window pointer
used to compute WriteBorder on the next flush cycle
- Remaining filters had NextWindow set to true (inverted logic), when
sync correctly sets them to false so they are processed next time
All three changes align the async path with the sync UnpWriteBuf logic.
Two releases since the generator was adopted here, both fixing things this
repository ran into.
2.0.41 emits the using directives of the source file into the generated one.
Documentation is copied across verbatim and a cref in it resolves against the
file it lands in, so crefs which relied on a using went unresolved and the
compiler reported CS1574 for each.
2.0.42 spaces a rewritten argument list the way it was written. The generated
extension calls here were coming out as
IArchiveEntryExtensions.WriteTo(archiveEntry, streamToWriteTo,
Constants.BufferSize,
progress: progress) ;
and now come out as
IArchiveEntryExtensions.WriteTo(archiveEntry, streamToWriteTo,
Constants.BufferSize,
progress: progress);
No change to what is generated, only to how it is spaced and what its
documentation can refer to. Full test suite passes unchanged.
The five synchronous WriteTo/WriteToDirectory/WriteToFile overloads were
maintained by hand alongside their async counterparts, which had drifted:
the async ones report progress through ExtractionOptions.BufferSize while
the sync ones did not, and each fix has had to be made twice.
Zomp.SyncMethodGenerator produces the synchronous copy from the async
source at compile time. The async methods are the only ones written out;
the sync versions are emitted into the same partial class, keeping the
public API identical - same names, same overloads, same signatures.
The generator is a build-time only dependency (PrivateAssets="all"), so
it adds nothing to the shipped package and no runtime reference.
Documentation summaries lose the word "asynchronously" because a summary
is now shared by both copies of the method.
The Zip64 branch of the streaming header reader assumed a data descriptor always follows a >=4GB entry. When the entry instead has back-patched sizes (no descriptor), the following header was parsed as descriptor fields, overwriting the entry's correct size/CRC with central-directory 0xFFFFFFFF sentinels and, on non-seekable async streams, leaving the reader misaligned so the next entry threw.
Now it detects a header signature after the entry data and leaves the already-correct metadata untouched, parsing that header normally. The fix has been applied to both the sync and async readers.