+/* On-disk format of a WIM lookup table entry (stream entry). */
+struct wim_lookup_table_entry_disk {
+ /* Size, offset, and flags of the stream. */
+ struct wim_reshdr_disk reshdr;
+
+ /* Which part of the split WIM this stream is in; indexed from 1. */
+ le16 part_number;
+
+ /* Reference count of this stream over all WIM images. */
+ le32 refcnt;
+
+ /* SHA1 message digest of the uncompressed data of this stream, or
+ * optionally all zeroes if this stream is of zero length. */
+ u8 hash[SHA1_HASH_SIZE];
+} _packed_attribute;
+
+#define WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE 50
+
+/* Given a nonempty run of consecutive lookup table entries with the
+ * PACKED_STREAMS flag set, count how many specify resources (as opposed to
+ * streams within those resources).
+ *
+ * Returns the resulting count. */
+static size_t
+count_subpacks(const struct wim_lookup_table_entry_disk *entries, size_t max)
+{
+ size_t count = 0;
+ do {
+ struct wim_reshdr reshdr;
+
+ get_wim_reshdr(&(entries++)->reshdr, &reshdr);
+
+ if (!(reshdr.flags & WIM_RESHDR_FLAG_PACKED_STREAMS)) {
+ /* Run was terminated by a stand-alone stream entry. */
+ break;
+ }
+
+ if (reshdr.uncompressed_size == WIM_PACK_MAGIC_NUMBER) {
+ /* This is a resource entry. */
+ count++;
+ }
+ } while (--max);
+ return count;
+}
+
+/* Given a run of consecutive lookup table entries with the PACKED_STREAMS flag
+ * set and having @num_subpacks resource entries, load resource information from
+ * them into the resource specifications in the @subpacks array.
+ *
+ * Returns 0 on success, or a nonzero error code on failure. */
+static int
+do_load_subpack_info(WIMStruct *wim, struct wim_resource_spec **subpacks,
+ size_t num_subpacks,
+ const struct wim_lookup_table_entry_disk *entries)
+{
+ for (size_t i = 0; i < num_subpacks; i++) {
+ struct wim_reshdr reshdr;
+ struct alt_chunk_table_header_disk hdr;
+ struct wim_resource_spec *rspec;
+ int ret;
+
+ /* Advance to next resource entry. */
+
+ do {
+ get_wim_reshdr(&(entries++)->reshdr, &reshdr);
+ } while (reshdr.uncompressed_size != WIM_PACK_MAGIC_NUMBER);
+
+ rspec = subpacks[i];
+
+ wim_res_hdr_to_spec(&reshdr, wim, rspec);
+
+ /* For packed resources, the uncompressed size, compression
+ * type, and chunk size are stored in the resource itself, not
+ * in the lookup table. */
+
+ ret = full_pread(&wim->in_fd, &hdr,
+ sizeof(hdr), reshdr.offset_in_wim);
+ if (ret) {
+ ERROR("Failed to read header of packed resource "
+ "(offset_in_wim=%"PRIu64")",
+ reshdr.offset_in_wim);
+ return ret;
+ }
+
+ rspec->uncompressed_size = le64_to_cpu(hdr.res_usize);
+
+ /* Compression format numbers must be the same as in
+ * WIMGAPI to be compatible here. */
+ BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_NONE != 0);
+ BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_XPRESS != 1);
+ BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZX != 2);
+ BUILD_BUG_ON(WIMLIB_COMPRESSION_TYPE_LZMS != 3);
+ rspec->compression_type = le32_to_cpu(hdr.compression_format);
+
+ rspec->chunk_size = le32_to_cpu(hdr.chunk_size);
+
+ DEBUG("Subpack %zu/%zu: %"PRIu64" => %"PRIu64" "
+ "(%"TS"/%"PRIu32") @ +%"PRIu64"",
+ i + 1, num_subpacks,
+ rspec->uncompressed_size,
+ rspec->size_in_wim,
+ wimlib_get_compression_type_string(rspec->compression_type),
+ rspec->chunk_size,
+ rspec->offset_in_wim);
+
+ }
+ return 0;
+}
+
+/* Given a nonempty run of consecutive lookup table entries with the
+ * PACKED_STREAMS flag set, allocate a 'struct wim_resource_spec' for each
+ * resource within that run.