+/* Given a nonempty run of consecutive lookup table entries with the SOLID flag
+ * set, count how many specify resources (as opposed to streams within those
+ * resources).
+ *
+ * Returns the resulting count. */
+static size_t
+count_solid_resources(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_SOLID)) {
+ /* Run was terminated by a stand-alone stream entry. */
+ break;
+ }
+
+ if (reshdr.uncompressed_size == SOLID_RESOURCE_MAGIC_NUMBER) {
+ /* This is a resource entry. */
+ count++;
+ }
+ } while (--max);
+ return count;
+}
+
+/*
+ * Given a run of consecutive lookup table entries with the SOLID flag set and
+ * having @num_rspecs resource entries, load resource information from them into
+ * the resource specifications in the @rspecs array.
+ *
+ * Returns 0 on success, or a nonzero error code on failure.
+ */
+static int
+do_load_solid_info(WIMStruct *wim, struct wim_resource_spec **rspecs,
+ size_t num_rspecs,
+ const struct wim_lookup_table_entry_disk *entries)
+{
+ for (size_t i = 0; i < num_rspecs; 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 != SOLID_RESOURCE_MAGIC_NUMBER);
+
+ rspec = rspecs[i];
+
+ wim_res_hdr_to_spec(&reshdr, wim, rspec);
+
+ /* For solid 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 solid 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("Solid resource %zu/%zu: %"PRIu64" => %"PRIu64" "
+ "(%"TS"/%"PRIu32") @ +%"PRIu64"",
+ i + 1, num_rspecs,
+ 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 SOLID 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 *rspecs_ret and *num_rspecs_ret.
+ */
+static int
+load_solid_info(WIMStruct *wim,
+ const struct wim_lookup_table_entry_disk *entries,
+ size_t num_remaining_entries,
+ struct wim_resource_spec ***rspecs_ret,
+ size_t *num_rspecs_ret)
+{
+ size_t num_rspecs;
+ struct wim_resource_spec **rspecs;
+ size_t i;
+ int ret;
+
+ num_rspecs = count_solid_resources(entries, num_remaining_entries);
+ rspecs = CALLOC(num_rspecs, sizeof(rspecs[0]));
+ if (!rspecs)
+ return WIMLIB_ERR_NOMEM;
+
+ for (i = 0; i < num_rspecs; i++) {
+ rspecs[i] = MALLOC(sizeof(struct wim_resource_spec));
+ if (!rspecs[i]) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto out_free_rspecs;
+ }
+ }
+
+ ret = do_load_solid_info(wim, rspecs, num_rspecs, entries);
+ if (ret)
+ goto out_free_rspecs;
+
+ *rspecs_ret = rspecs;
+ *num_rspecs_ret = num_rspecs;
+ return 0;
+
+out_free_rspecs:
+ for (i = 0; i < num_rspecs; i++)
+ FREE(rspecs[i]);
+ FREE(rspecs);
+ return ret;
+}
+
+/* Given a 'struct wim_lookup_table_entry' allocated for a stream entry with the
+ * SOLID flag set, try to bind it to resource in the current solid run. */
+static int
+bind_stream_to_solid_resource(const struct wim_reshdr *reshdr,
+ struct wim_lookup_table_entry *stream,
+ struct wim_resource_spec **rspecs,
+ size_t num_rspecs)
+{
+ u64 offset = reshdr->offset_in_wim;
+
+ /* XXX: This linear search will be slow in the degenerate case where the
+ * number of solid resources in the run is huge. */
+ stream->size = reshdr->size_in_wim;
+ stream->flags = reshdr->flags;
+ for (size_t i = 0; i < num_rspecs; i++) {
+ if (offset + stream->size <= rspecs[i]->uncompressed_size) {
+ stream->offset_in_res = offset;
+ lte_bind_wim_resource_spec(stream, rspecs[i]);
+ return 0;
+ }
+ offset -= rspecs[i]->uncompressed_size;
+ }
+ ERROR("Stream could not be assigned to a solid resource");
+ return WIMLIB_ERR_INVALID_LOOKUP_TABLE_ENTRY;
+}
+
+static void
+free_solid_rspecs(struct wim_resource_spec **rspecs, size_t num_rspecs)
+{
+ if (rspecs) {
+ for (size_t i = 0; i < num_rspecs; i++)
+ if (list_empty(&rspecs[i]->stream_list))
+ FREE(rspecs[i]);
+ FREE(rspecs);
+ }
+}
+