* Decode short codes at the end of the inflate input
InflateCodes waited for as many bits as its lookup table uses before
it decoded a literal, length or distance code. When the input ended
with fewer bits than that, the last codes were not decoded, even though
they were complete. An MS-ZIP block is a deflate stream on its own, so
its last match was lost and the block came out short (#1).
When no more input is there, the code is now looked up with the bits
that are left and used if it is not longer than those.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep a full 32 KiB inflate dictionary
SetDictionary kept only the last 32767 bytes of a 32 KiB dictionary,
because the window needs one free byte between its read and write
positions. MS-ZIP sets the previous 32 KiB block as the dictionary,
and a match at the start of a block can copy from 32768 bytes back,
which then read a byte that was not in the window.
A dictionary that fills the window now starts with the read and write
positions at 0, so the first byte written replaces the oldest byte of
the dictionary.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Disposing a ReadOnlyCompositeStream did not dispose the streams it
reads from, so files opened only for the composite stayed open until
their streams were finalized. WiseOverlayHeader.OpenFile in
SabreTools.Serialization opens a Wise installer and all of its parts
this way.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Parse doubles with the invariant culture
ConvertToDouble used double.TryParse with the current culture, so a
value like "59.940000" became 59940000 on a German system and null on a
French one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drop the culture reset from the ConvertToDouble test
A culture set inside a test does not carry over to the next test, so
the reset and the try/finally around it are not needed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* Allow 0-byte reads at the end of a byte array
ReadBytes(ref offset, 0) threw ArgumentOutOfRangeException when offset
was equal to the array length, before the 0-byte case was handled. The
same happened for a 0-byte read from an empty array, and TryReadBytes
with a count of 0 at the end threw instead of returning true. The offset
check now accepts the end of the array for a 0-byte read only; reads past
the end still throw.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Handle 0-byte reads before the offset check
Same order as the stream version of ReadExactlyToBuffer, applied to
both byte array copies (Numerics.Extensions and IO.Extensions).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This is being done to avoid any API-breaking changes in the future. If the values are set explicitly, added values either need to force a major version update or get added as the next-highest values.