<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://try-as400.pocnet.net/index.php?action=history&amp;feed=atom&amp;title=File_Formats%3ASAVF</id>
	<title>File Formats:SAVF - Revision history</title>
	<link rel="self" type="application/atom+xml" href="http://try-as400.pocnet.net/index.php?action=history&amp;feed=atom&amp;title=File_Formats%3ASAVF"/>
	<link rel="alternate" type="text/html" href="http://try-as400.pocnet.net/index.php?title=File_Formats:SAVF&amp;action=history"/>
	<updated>2026-08-15T02:55:53Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.9</generator>
	<entry>
		<id>http://try-as400.pocnet.net/index.php?title=File_Formats:SAVF&amp;diff=1781&amp;oldid=prev</id>
		<title>Friedkiwi: Document the SAVF container: chunk layer and checksum, item descriptors, sections, SRDS catalogue (via create-page on MediaWiki MCP Server)</title>
		<link rel="alternate" type="text/html" href="http://try-as400.pocnet.net/index.php?title=File_Formats:SAVF&amp;diff=1781&amp;oldid=prev"/>
		<updated>2026-08-09T11:53:42Z</updated>

		<summary type="html">&lt;p&gt;Document the SAVF container: chunk layer and checksum, item descriptors, sections, SRDS catalogue (via create-page on MediaWiki MCP Server)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;A &amp;#039;&amp;#039;&amp;#039;SAVF&amp;#039;&amp;#039;&amp;#039; (&amp;#039;&amp;#039;save file&amp;#039;&amp;#039;) is the container OS/400 and IBM i write with &amp;lt;code&amp;gt;SAVOBJ&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SAVLIB&amp;lt;/code&amp;gt;, and read back with &amp;lt;code&amp;gt;RSTOBJ&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;RSTLIB&amp;lt;/code&amp;gt;. It holds Machine Interface objects — programs, files, data areas, libraries — in the form they have on disk, together with a catalogue describing what was saved and from which machine.&lt;br /&gt;
&lt;br /&gt;
Understanding the format is useful well beyond restoring a save file on a machine. It is the only practical way to get at the bytes of an MI object &amp;#039;&amp;#039;offline&amp;#039;&amp;#039;: the same byte stream also appears inside optical images and tape images, so a distribution medium, a PTF, or a backup is an extractable archive of real MI objects. What can be done with one once it is extracted is described in [[File Formats:PGM]] for programs.&lt;br /&gt;
&lt;br /&gt;
Everything below is big endian, and every text field is EBCDIC.&lt;br /&gt;
&lt;br /&gt;
== Three layers ==&lt;br /&gt;
It helps to separate three things that are often run together:&lt;br /&gt;
&lt;br /&gt;
* the &amp;#039;&amp;#039;&amp;#039;chunk&amp;#039;&amp;#039;&amp;#039; layer, which wraps the save stream in 528-byte units with a checksum on each;&lt;br /&gt;
* the &amp;#039;&amp;#039;&amp;#039;item&amp;#039;&amp;#039;&amp;#039; layer, which is a chain of saved objects, each a 512-byte descriptor followed by that object&amp;#039;s segments;&lt;br /&gt;
* the &amp;#039;&amp;#039;&amp;#039;catalogue&amp;#039;&amp;#039;&amp;#039;, a single special item per save that lists the library and its objects.&lt;br /&gt;
&lt;br /&gt;
Only the chunk layer is specific to a save file held in &amp;lt;code&amp;gt;QSYS&amp;lt;/code&amp;gt;. When the same save is carried on optical or tape media, the chunk wrapper is absent and the item chain sits in the medium directly. A reader that separates the layers handles all three media with one item parser.&lt;br /&gt;
&lt;br /&gt;
== The chunk layer ==&lt;br /&gt;
A save file is exactly &amp;#039;&amp;#039;chunk count&amp;#039;&amp;#039; × 528 bytes. Chunk &amp;#039;&amp;#039;n&amp;#039;&amp;#039;, counting from zero, begins at file offset &amp;lt;code&amp;gt;n × 528&amp;lt;/code&amp;gt; and consists of 512 bytes of save-stream data followed by a 16-byte checksum. The logical save stream is the concatenation of the data halves, so translating a stream offset to a file offset is&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
file_offset = (stream_offset / 512) * 528 + (stream_offset % 512)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== File header ===&lt;br /&gt;
The first 512 bytes of the stream — which are also the data half of chunk 1 — describe the container.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Offset&lt;br /&gt;
!Size&lt;br /&gt;
!Field&lt;br /&gt;
!Notes&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x00&amp;lt;/code&amp;gt;&lt;br /&gt;
|8&lt;br /&gt;
|MI creation timestamp&lt;br /&gt;
|Also the seed for every chunk checksum.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x08&amp;lt;/code&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|Chunk count&lt;br /&gt;
|32-bit form.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x10&amp;lt;/code&amp;gt;&lt;br /&gt;
|8&lt;br /&gt;
|Machine type and model&lt;br /&gt;
|EBCDIC, model first: &amp;lt;code&amp;gt;42A 8286&amp;lt;/code&amp;gt; is an 8286-42A.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x30&amp;lt;/code&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|Release level&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x34&amp;lt;/code&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|Header length&lt;br /&gt;
|The first item starts here. Zero means 512.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x38&amp;lt;/code&amp;gt;&lt;br /&gt;
|8&lt;br /&gt;
|Chunk count&lt;br /&gt;
|64-bit form, used when the 32-bit field would overflow.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x40&amp;lt;/code&amp;gt;&lt;br /&gt;
|16&lt;br /&gt;
|Global checksum backup&lt;br /&gt;
|See below.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
A valid file satisfies &amp;lt;code&amp;gt;chunk count == file size / 528&amp;lt;/code&amp;gt;; anything else means truncation. Because the header is itself chunk 1, verifying its checksum is the cheapest reliable test of whether a file is a save file at all.&lt;br /&gt;
&lt;br /&gt;
=== The global checksum displaces real data ===&lt;br /&gt;
This is the trap worth knowing about before writing any extractor. The last 16 bytes of the &amp;#039;&amp;#039;logical&amp;#039;&amp;#039; stream, at &amp;lt;code&amp;gt;chunk count × 512 − 16&amp;lt;/code&amp;gt;, are overwritten with a whole-file checksum. The data bytes they replaced are preserved in the file header at &amp;lt;code&amp;gt;+0x40&amp;lt;/code&amp;gt;, and a reader must put them back. A reader that does not will silently corrupt the tail of the last object it extracts, in a way that no chunk checksum will catch, because the chunk checksums are computed over the overwritten bytes.&lt;br /&gt;
&lt;br /&gt;
=== The chunk checksum ===&lt;br /&gt;
Each chunk&amp;#039;s 16-byte trailer is the chunk number, counting from one, as a 4-byte big-endian integer, followed by a 12-byte digest. The digest is two folding passes built from a single primitive:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
chkadd(a, b):          # b is exactly one byte longer than a&lt;br /&gt;
    b[1:] += a &amp;lt;&amp;lt; 1    # big-endian magnitudes, carry propagated&lt;br /&gt;
    b[0]  += carry + (the bit shifted out of the top of a)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The first pass takes the chunk number followed by the 512 data bytes and cuts them into eight parts of 61, 62, 63, 64, 65, 66, 67 and 68 bytes, then folds them in ascending order: &amp;lt;code&amp;gt;chkadd&amp;lt;/code&amp;gt; of the 61-byte part into the 62-byte part, that into the 63-byte part, and so on. The second pass takes the resulting 68 bytes, cuts them into parts of 5, 6, 7, 8, 9, 10, 11 and 12 bytes, and folds them the same way, leaving 12 bytes. The first 8 of those are then XORed with the file header&amp;#039;s MI timestamp.&lt;br /&gt;
&lt;br /&gt;
There is one non-obvious detail. In both passes the parts are &amp;#039;&amp;#039;filled&amp;#039;&amp;#039; from the input in the order 61, 62, 63, 64, 65, 66, &amp;#039;&amp;#039;&amp;#039;68, 67&amp;#039;&amp;#039;&amp;#039; — and 5, 6, 7, 8, 9, 10, &amp;#039;&amp;#039;&amp;#039;12, 11&amp;#039;&amp;#039;&amp;#039; — while being &amp;#039;&amp;#039;folded&amp;#039;&amp;#039; in ascending order. An implementation that fills them in plain ascending order produces a plausible-looking wrong answer.&lt;br /&gt;
&lt;br /&gt;
The construction is a checksum, not a cryptographic hash. It detects media damage. It is not a tamper seal, and it carries no secret beyond a timestamp printed in the same header.&lt;br /&gt;
&lt;br /&gt;
== The item layer ==&lt;br /&gt;
Items are chained: parse the descriptor at the header length, add the item&amp;#039;s computed length, repeat, and stop when the next item would begin past &amp;lt;code&amp;gt;(chunk count − 1) × 512&amp;lt;/code&amp;gt;. Every descriptor begins with &amp;lt;code&amp;gt;FFFFFFFF&amp;lt;/code&amp;gt;, which makes resynchronising after damage feasible.&lt;br /&gt;
&lt;br /&gt;
=== Item descriptor ===&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Offset&lt;br /&gt;
!Size&lt;br /&gt;
!Field&lt;br /&gt;
!Notes&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x00&amp;lt;/code&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|&amp;lt;code&amp;gt;FFFFFFFF&amp;lt;/code&amp;gt;&lt;br /&gt;
|Eyecatcher.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x04&amp;lt;/code&amp;gt;&lt;br /&gt;
|10&lt;br /&gt;
|Object name&lt;br /&gt;
|EBCDIC. Replaced by a file ID for integrated file system objects.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x0E&amp;lt;/code&amp;gt;&lt;br /&gt;
|20&lt;br /&gt;
|Member name&lt;br /&gt;
|Empty unless the object has members.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x22&amp;lt;/code&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|MI object type&lt;br /&gt;
|The same code space used throughout the machine: &amp;lt;code&amp;gt;0x0201&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;*PGM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;0x0401&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;*LIB&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;0x1901&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;*FILE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;0x19DB&amp;lt;/code&amp;gt; &amp;lt;code&amp;gt;*SRDS&amp;lt;/code&amp;gt;.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x24&amp;lt;/code&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|Flags&lt;br /&gt;
|Bit &amp;lt;code&amp;gt;0x40&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;0x80&amp;lt;/code&amp;gt; means the item is an IFS object.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x25&amp;lt;/code&amp;gt;&lt;br /&gt;
|3&lt;br /&gt;
|Catalogue offset&lt;br /&gt;
|Where this object&amp;#039;s entry sits inside the catalogue.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x44&amp;lt;/code&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|Header occupation&lt;br /&gt;
|Bytes of the descriptor page actually used.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x48&amp;lt;/code&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|Stored length&lt;br /&gt;
|In 512-byte units, minus one.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x54&amp;lt;/code&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|Release created for&lt;br /&gt;
|Governs the section-entry layout below.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x56&amp;lt;/code&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|Metadata length&lt;br /&gt;
|For a catalogue item, the size of the catalogue.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x64&amp;lt;/code&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|Section count&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0xC0&amp;lt;/code&amp;gt;&lt;br /&gt;
|8&lt;br /&gt;
|Save timestamp&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0xCC&amp;lt;/code&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|Total length&lt;br /&gt;
|In 512-byte units.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0xD4&amp;lt;/code&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|Data length&lt;br /&gt;
|In 512-byte units. Always the total length less 4096 bytes.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x100&amp;lt;/code&amp;gt;&lt;br /&gt;
|14 × 8&lt;br /&gt;
|Pointer array&lt;br /&gt;
|See below.&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x170&amp;lt;/code&amp;gt;&lt;br /&gt;
|8&lt;br /&gt;
|Packed length&lt;br /&gt;
|Stored as the length less 512.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
The pointer array holds single-level store addresses, but only the low 24 bits matter: they are an offset into the item, biased by 4096, so &amp;lt;code&amp;gt;(pointer &amp;amp;amp; 0xFFFFFF) − 4096&amp;lt;/code&amp;gt; gives an offset within the descriptor page. Index 1 locates the section length table and index 8 the section type table.&lt;br /&gt;
&lt;br /&gt;
=== Sections ===&lt;br /&gt;
A saved MI object is a set of segments, and each segment is one &amp;#039;&amp;#039;section&amp;#039;&amp;#039; of the item. Sections start at the first 4096-byte boundary after the descriptor and run consecutively, which is why the data length is always the total length less one page.&lt;br /&gt;
&lt;br /&gt;
The length table has one entry per section. On any modern release — when the &amp;#039;&amp;#039;release created for&amp;#039;&amp;#039; field exceeds 32 — an entry is 16 bytes: capacity, length, and the segment&amp;#039;s single-level store address. Older saves use a 10-byte entry with a shifted 6-byte address and no separate length.&lt;br /&gt;
&lt;br /&gt;
The type table has one 8-byte entry per section, of which the first halfword is the segment type. This is the &amp;#039;&amp;#039;&amp;#039;same value as the first halfword of the segment&amp;#039;s own segment header&amp;#039;&amp;#039;&amp;#039;, which is a useful consistency check when extracting: the item&amp;#039;s idea of what a section is must agree with the segment&amp;#039;s own.&lt;br /&gt;
&lt;br /&gt;
=== Item length and compression ===&lt;br /&gt;
An uncompressed item occupies its stored length. A packed item records a non-zero packed length, and the writer then realigns the following item on the enclosing 512-byte boundary, so the step from one item to the next is&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
if packed == 512:        step = stored&lt;br /&gt;
elif packed &amp;gt; stored:    step = packed&lt;br /&gt;
else:&lt;br /&gt;
    slack  = stored - packed&lt;br /&gt;
    within = item_offset % 512&lt;br /&gt;
    if within == 0:      step = stored&lt;br /&gt;
    elif within &amp;lt; slack: step = packed + slack - within&lt;br /&gt;
    else:                step = stored + 512 - within&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The compression method is identifiable from the first six bytes of the packed data, which are EBCDIC eyecatchers:&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Bytes&lt;br /&gt;
!EBCDIC&lt;br /&gt;
!&amp;lt;code&amp;gt;DTACPR&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;D3C46DE3D9E2&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;LD_TRS&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;*MEDIUM&amp;lt;/code&amp;gt;, the TERSE algorithm&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;D3C46DD3E9F1&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;LD_LZ1&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;*HIGH&amp;lt;/code&amp;gt;, IBM&amp;#039;s LZ1&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;D3C46DE9D3C2&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;LD_ZLB&amp;lt;/code&amp;gt;&lt;br /&gt;
|&amp;lt;code&amp;gt;*ZLIB&amp;lt;/code&amp;gt;; the deflate stream begins 12 bytes in&lt;br /&gt;
|-&lt;br /&gt;
|none&lt;br /&gt;
|&lt;br /&gt;
|&amp;lt;code&amp;gt;*LOW&amp;lt;/code&amp;gt;, an SNA-style run-length scheme, recognisable only by decoding it&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Note that these are &amp;#039;&amp;#039;not&amp;#039;&amp;#039; the compression used for a compressed LID payload; see [[Data Structures:LID]], where all four of these were tried and rejected.&lt;br /&gt;
&lt;br /&gt;
== The catalogue ==&lt;br /&gt;
Every save writes one save/restore descriptor space item, MI type &amp;lt;code&amp;gt;0x19DB&amp;lt;/code&amp;gt;, named &amp;lt;code&amp;gt;QSRDSSPC.&amp;#039;&amp;#039;n&amp;#039;&amp;#039;&amp;lt;/code&amp;gt;, ahead of the objects it describes. Items named &amp;lt;code&amp;gt;QSR.ADDITIONAL_INFO.*&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;QSRDSSPC.EA&amp;amp;nbsp;SRD*&amp;lt;/code&amp;gt; are continuation blocks belonging to the preceding descriptor, not new saves.&lt;br /&gt;
&lt;br /&gt;
The catalogue itself is the &amp;#039;&amp;#039;&amp;#039;last&amp;#039;&amp;#039;&amp;#039; &amp;#039;&amp;#039;metadata length&amp;#039;&amp;#039; bytes of that item&amp;#039;s data. Its first byte is the save command, and that selects the shape: a library save has the layout below, while a save of the integrated file system produces a different, tree-structured descriptor.&lt;br /&gt;
&lt;br /&gt;
{|class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
!Offset&lt;br /&gt;
!Size&lt;br /&gt;
!Field&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x00&amp;lt;/code&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|Save command&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x01&amp;lt;/code&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|Target release&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x02&amp;lt;/code&amp;gt;&lt;br /&gt;
|30&lt;br /&gt;
|Saved library name&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x20&amp;lt;/code&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|Base object type, &amp;lt;code&amp;gt;0x0401&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x22&amp;lt;/code&amp;gt;&lt;br /&gt;
|8&lt;br /&gt;
|Save timestamp&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x2A&amp;lt;/code&amp;gt;&lt;br /&gt;
|4&lt;br /&gt;
|Object count&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x34&amp;lt;/code&amp;gt;&lt;br /&gt;
|8&lt;br /&gt;
|System serial number&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x3C&amp;lt;/code&amp;gt;&lt;br /&gt;
|2&lt;br /&gt;
|ASP number&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x3E&amp;lt;/code&amp;gt;&lt;br /&gt;
|10&lt;br /&gt;
|Create authority&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x4C&amp;lt;/code&amp;gt;&lt;br /&gt;
|1&lt;br /&gt;
|Save-while-active indicator&lt;br /&gt;
|-&lt;br /&gt;
|&amp;lt;code&amp;gt;+0x50&amp;lt;/code&amp;gt;&lt;br /&gt;
|10&lt;br /&gt;
|ASP name, target release 66 and later only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Object descriptors follow, at &amp;lt;code&amp;gt;+0x50&amp;lt;/code&amp;gt; or at &amp;lt;code&amp;gt;+0x70&amp;lt;/code&amp;gt; when the ASP name is present. Each is 151 bytes on target release 49 and later, and 87 bytes before that. The first fields are the object name at &amp;lt;code&amp;gt;+0x00&amp;lt;/code&amp;gt; for 30 bytes, the MI object type at &amp;lt;code&amp;gt;+0x1E&amp;lt;/code&amp;gt;, and the owning user profile at &amp;lt;code&amp;gt;+0x20&amp;lt;/code&amp;gt; for 10 bytes. The size is a 4-byte field at &amp;lt;code&amp;gt;+0x43&amp;lt;/code&amp;gt;, multiplied by a second 4-byte field at &amp;lt;code&amp;gt;+0x79&amp;lt;/code&amp;gt; when that is non-zero. The remainder of the descriptor holds offsets, into the same buffer, of chains of length-prefixed records carrying the object&amp;#039;s text description, service description, usage information, work-unit references and user information.&lt;br /&gt;
&lt;br /&gt;
Worth noticing for its own sake: the catalogue records the &amp;#039;&amp;#039;&amp;#039;serial number of the machine that produced the save&amp;#039;&amp;#039;&amp;#039;. A save file is not anonymous.&lt;br /&gt;
&lt;br /&gt;
== Practical consequences ==&lt;br /&gt;
* Any MI object can be lifted out of a save file byte for byte, with its segments separated and their segment identifiers intact. That is what makes offline analysis of a program object possible without console access to a machine and without printing a storage dump.&lt;br /&gt;
* Verifying the chunk checksums is a genuine integrity check on an archived save file, and can be done without a machine.&lt;br /&gt;
* The item chain and the catalogue are independent descriptions of the same set of objects. Where they disagree, the file has been damaged or truncated.&lt;br /&gt;
* Because the chunk layer is only present for a save file held in &amp;lt;code&amp;gt;QSYS&amp;lt;/code&amp;gt;, a save carved out of an optical or tape image is parsed by the same code with the chunk translation switched off.&lt;br /&gt;
&lt;br /&gt;
== Scope of this description ==&lt;br /&gt;
The layout was mapped with reference to jSAVF, an independently developed third-party reader, and then verified field by field against a real V7R4 save file. Where an object in that file also had a printed Display/Alter/Dump available, the bytes extracted here matched the printout exactly. The chunk checksum was reimplemented from scratch and verifies every chunk of the test file.&lt;br /&gt;
&lt;br /&gt;
Not established: the meaning of the 4-byte field at &amp;lt;code&amp;gt;+0x0C&amp;lt;/code&amp;gt; of the file header, the layout of the integrated file system catalogue, and the LZ1 and TERSE decompressors.&lt;br /&gt;
&lt;br /&gt;
== Weblinks ==&lt;br /&gt;
* [https://www.anerty.net/software/file/jSAVF/?lang=en jSAVF] — a maintained third-party reader for save files, and the starting point for the layout described here.&lt;br /&gt;
&lt;br /&gt;
[[Category: System Internals]]&lt;/div&gt;</summary>
		<author><name>Friedkiwi</name></author>
	</entry>
</feed>