[#144] Read storage block offsets from the archive BlocksInfo (format version 9) - #128
Conversation
There was a problem hiding this comment.
Pull request overview
This PR adds proactive support for Unity Archive format v9’s BlockPaddingBetweenChunks flag by updating the C# archive parser to correctly skip 16-byte alignment padding between non-streamed blocks, and extends CLI reporting + test data/tests to validate the new layout.
Changes:
- Update archive block offset accumulation to align after each non-streamed block when
BlockPaddingBetweenChunksis set, and consolidate header flag constants into a sharedArchiveFlagsdefinition. - Improve reporting: recognize the new flag in
archive headeroutput and report total block padding size inarchive info. - Add LeadingEdge LZ4 (chunk-based) AssetBundle build output and new tests that validate padding bytes and alignment behavior.
Reviewed changes
Copilot reviewed 14 out of 22 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| UnityBinaryFormat/ArchiveDetector.cs | Adds v9 padding-aware block offset accumulation and introduces shared ArchiveFlags. |
| Archive/ArchiveTool.cs | Reports BlockPaddingBetweenChunks as a known flag and adds blockPaddingSize reporting in archive info. |
| UnityDataTool.Tests/ArchiveTests.cs | Adds tests for v9 flag reporting, block alignment/padding verification, and native extraction on padded archives. |
| UnityProjects/LeadingEdge/Assets/Editor/BuildAssetBundles.cs | Adds a second build entry point to generate LZ4 chunk-based bundles into a separate folder. |
| UnityProjects/LeadingEdge/AGENTS.md | Documents the new LZ4 build entry point and its purpose. |
| TestCommon/Data/LeadingEdgeBuilds/AGENTS.md | Documents the new AssetBundlesLz4/ test-data folder and why it exists. |
| TestCommon/Data/LeadingEdgeBuilds/AssetBundlesLz4/AssetBundlesLz4.manifest | Adds new LeadingEdge LZ4 bundle manifest test data. |
| TestCommon/Data/LeadingEdgeBuilds/AssetBundlesLz4/a.manifest | Adds new LeadingEdge LZ4 bundle manifest test data. |
| TestCommon/Data/LeadingEdgeBuilds/AssetBundlesLz4/6.manifest | Adds new LeadingEdge LZ4 bundle manifest test data. |
| TestCommon/Data/LeadingEdgeBuilds/AssetBundlesLz4/assetbundleroot.manifest | Adds new LeadingEdge LZ4 bundle manifest test data. |
| TestCommon/Data/LeadingEdgeBuilds/AssetBundlesLz4/directaudioclipreference.manifest | Adds new LeadingEdge LZ4 bundle manifest test data. |
| TestCommon/Data/LeadingEdgeBuilds/AssetBundlesLz4/serializationdemo.manifest | Adds new LeadingEdge LZ4 bundle manifest test data. |
| TestCommon/Data/LeadingEdgeBuilds/AssetBundlesLz4/scenes.manifest | Adds new LeadingEdge LZ4 bundle manifest test data. |
| TestCommon/Data/LeadingEdgeBuilds/AssetBundlesLz4/singleaudioclipdirectreference.manifest | Adds new LeadingEdge LZ4 bundle manifest test data. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
The new format is going to be part of in 6.7 b2 and it should already be in the 7.0 alpha release. |
A change in Unity 6.7 bumps the Unity Archive format to version 9 and introduces padding - each chunk-based (non-streamed) storage block is followed by zero padding up to the next 16-byte boundary of the data section, so that a size change in one chunk no longer shifts the position of every chunk after it (this aids binary patching). each StorageBlock now serializes a UInt64 offset giving where its stored bytes start, relative to the start of the data section. The writer is then free to change its padding policy without another format bump, and the reader no longer needs the "streamed blocks are not padded, chunk blocks are" special case. Parse that offset as the fourth field of each block record, after Flags, when the signature is UnityFS and the version is 9 or later. Version 8 and earlier store their blocks contiguously, so their offsets are still accumulated from the compressed sizes. A v9 archive whose blocks are out of order or overlapping is rejected, matching the native reader.
21471a6 to
c384ca8
Compare
|
This will land in unity6.7support branch where we are accumulating fixes. |
Summary
Proactive support for an upcoming Unity Archive format change, so we find out whether it
causes UnityDataTools any trouble before the format change lands. The archive work is on
unity/unity#119906.
The design of that change was revised, and this PR now reflects the revision. The original
approach bumped the format to version 9 and added a header flag,
kArchiveBlockPaddingBetweenChunks, telling readers to round each block's start offset up tothe next 16-byte boundary. The current approach instead has each
StorageBlockrecord its ownoffset, and the flag is gone.
That is the better trade for a reader. Block positions are no longer reconstructed from a
padding rule that the reader has to know and the writer has to keep, so the writer can change
its alignment policy later (4 KB for DirectStorage, none at all) with no further format bump,
and the "streamed blocks are not padded, chunk blocks are" special case disappears.
The offset is relative to the start of the data section, not the file. It has to be: in the
BlocksInfo-at-the-start layout that AssetBundle builds use, the absolute data offset depends on
compressedBlocksInfoSize, which is not known until every block has been written — and puttingabsolute offsets in the blocks would change the bytes that get compressed, which changes that
size, which changes the offsets. Readers add
GetDataOffset(header).Changes
Parsing —
UnityBinaryFormat/ArchiveDetector.csStorageBlock.Offset(UInt64, big-endian) is parsed as the fourth field of each blockrecord, after
Flags. Note the serialized order differs from the C++ struct layout, whereoffsetis declared first.Signature == "UnityFS" && Version >= 9, mirroringHeader::HasStorageBlockOffsets().Version 8 and earlier store blocks contiguously, so those offsets are still accumulated from
the compressed sizes — the pre-existing behaviour, now the explicit legacy path.
kErrorin
ReadBlocksInfo. These offsets are corruption-controlled input rather than derived fromsizes, so this is the one new failure mode the format change introduces.
HasBlockPaddingBetweenChunksand the0x400flag constant; addedHasStorageBlockOffsets.Reporting —
Archive/ArchiveTool.csBlockPaddingBetweenChunksremoved from the flag name table, since the bit is no longer partof the format.
archive infokeeps theBlock Padding Size/blockPaddingSizefield but now derives itfrom the stored offsets rather than an assumed alignment rule. Without it,
Data Sizesilentlystops accounting for the whole data section.
Test data
UnityProjects/LeadingEdge/Assets/Editor/BuildAssetBundles.cshas aBuildLz4entry point(
ChunkBasedCompression) writing the same bundle layout to a separate folder. The existingLZMA reference bundles are deliberately untouched — both the v8 contiguous layout and the v9
offset layout are wanted as reference data.
TestCommon/Data/LeadingEdgeBuilds/AssetBundlesLz4/regenerated in the new format. Thescenesbundle is the interesting one: 11 blocks, 80 bytes of padding, withBuildPlayer-Scene1.sharedAssetsspanning the first eight..metafiles gain explicitAudioImportersettings (GUIDs unchanged), which theLeadingEdge project needs in order to build with a 6.7 editor at all.
Testing
dotnet test— 790 passed, 10 skipped, 0 failed.New coverage in
UnityDataTool.Tests/ArchiveTests.cs:ArchiveBlocks_Version9_OffsetsComeFromTheBlockList— checks the parsed offsets against theactual file: blocks in order, every byte of every gap between them zero, and a non-zero total
gap. That last assertion matters: without it the test would still pass on a contiguous archive
even if the stored offsets were being ignored entirely.
ArchiveBlocks_LegacyVersion8_BlocksAreContiguous— the accumulate fallback, againstPlayerDataCompressed/data.unity3d, a real v8 8-block Lz4 archive.ArchiveHeader_Version9_AllFlagsRecognized— version 9, and no flag bit reported as raw hex(which would mean a flag we don't know about).
ArchiveInfo_Version9_ReportsPaddingSize,ArchiveExtract_Version9_FilesExtractedSuccessfully.Validating that the writer is correct
This branch is also meant to be evidence that the v9 writer on the Unity side is right, so the
checks were aimed at the produced bytes rather than at self-consistency. Against a debug build
of
buildpipeline/archive_alignmentat44a19c46:GetBlocksInfoSizetherefore agrees with the bytes written, which the writer asserts.compressedSizeBlockPaddingBetweenChunksabsent from every regenerated header.StoreStreampath (LZMA / uncompressed), not justStoreChunkUFS_CreateArchive.Two writer paths were exercised: the Editor's AssetBundle build, and
UFS_CreateArchivevia thenative library. Both use the BlocksInfo-at-the-start layout.
Residual gaps, neither reachable from here: multiple streamed blocks in one archive (needs