+/* 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.
+ *
+ * Returns 0 on success, or a nonzero error code on failure.
+ * Returns the pointers and count in *subpacks_ret and *num_subpacks_ret.
+ */
+static int
+load_subpack_info(WIMStruct *wim,
+ const struct wim_lookup_table_entry_disk *entries,
+ size_t num_remaining_entries,
+ struct wim_resource_spec ***subpacks_ret,
+ size_t *num_subpacks_ret)
+{
+ size_t num_subpacks;
+ struct wim_resource_spec **subpacks;
+ size_t i;
+ int ret;
+
+ num_subpacks = count_subpacks(entries, num_remaining_entries);
+ subpacks = CALLOC(num_subpacks, sizeof(subpacks[0]));
+ if (!subpacks)
+ return WIMLIB_ERR_NOMEM;
+
+ for (i = 0; i < num_subpacks; i++) {
+ subpacks[i] = MALLOC(sizeof(struct wim_resource_spec));
+ if (!subpacks[i]) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_free_subpacks;
+ }
+ }
+
+ ret = do_load_subpack_info(wim, subpacks, num_subpacks, entries);
+ if (ret)
+ goto out_free_subpacks;
+
+ *subpacks_ret = subpacks;
+ *num_subpacks_ret = num_subpacks;
+ return 0;
+
+out_free_subpacks:
+ for (i = 0; i < num_subpacks; i++)
+ FREE(subpacks[i]);
+ FREE(subpacks);
+ return ret;
+}
+
+/* Given a 'struct wim_lookup_table_entry' allocated for a stream entry with
+ * PACKED_STREAMS set, try to bind it to a subpack of the current PACKED_STREAMS
+ * run. */
+static int
+bind_stream_to_subpack(const struct wim_reshdr *reshdr,
+ struct wim_lookup_table_entry *stream,
+ struct wim_resource_spec **subpacks,
+ size_t num_subpacks)
+{
+ u64 offset = reshdr->offset_in_wim;
+
+ /* XXX: This linear search will be slow in the degenerate case where the
+ * number of subpacks is huge. */
+ stream->size = reshdr->size_in_wim;
+ stream->flags = reshdr->flags;
+ for (size_t i = 0; i < num_subpacks; i++) {
+ if (offset + stream->size <= subpacks[i]->uncompressed_size) {
+ stream->offset_in_res = offset;
+ lte_bind_wim_resource_spec(stream, subpacks[i]);
+ return 0;
+ }
+ offset -= subpacks[i]->uncompressed_size;
+ }
+ ERROR("Packed stream could not be assigned to any resource");
+ return WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
+}
+
+static void
+free_subpack_info(struct wim_resource_spec **subpacks, size_t num_subpacks)
+{
+ if (subpacks) {
+ for (size_t i = 0; i < num_subpacks; i++)
+ if (list_empty(&subpacks[i]->stream_list))
+ FREE(subpacks[i]);
+ FREE(subpacks);
+ }
+}
+
+static int
+cmp_streams_by_offset_in_res(const void *p1, const void *p2)
+{
+ const struct wim_lookup_table_entry *lte1, *lte2;
+
+ lte1 = *(const struct wim_lookup_table_entry**)p1;
+ lte2 = *(const struct wim_lookup_table_entry**)p2;
+
+ return cmp_u64(lte1->offset_in_res, lte2->offset_in_res);
+}
+