- /* Set the implicit offset of the first chunk if it is included in the
- * needed chunks.
- *
- * Note: M$'s documentation includes a picture that shows the first
- * chunk starting right after the chunk entry table, labeled as offset
- * 0x10. However, in the actual file format, the offset is measured
- * from the end of the chunk entry table, so the first chunk has an
- * offset of 0. */
- if (start_chunk == 0)
- chunk_offsets[0] = 0;
-
- /* According to M$'s documentation, if the uncompressed size of
- * the file is greater than 4 GB, the chunk entries are 8-byte
- * integers. Otherwise, they are 4-byte integers. */
- u64 chunk_entry_size = (resource_uncompressed_size >= (u64)1 << 32) ?
- 8 : 4;
-
- /* Size of the full chunk table in the WIM file. */
- u64 chunk_table_size = chunk_entry_size * num_chunk_entries;
-
- /* Read the needed chunk offsets from the table in the WIM file. */
-
- /* Index, in the WIM file, of the first needed entry in the
- * chunk table. */
- u64 start_table_idx = (start_chunk == 0) ? 0 : start_chunk - 1;
-
- /* Number of entries we need to actually read from the chunk
- * table (excludes the implicit first chunk). */
- u64 num_needed_chunk_entries = (start_chunk == 0) ?
- num_needed_chunks - 1 : num_needed_chunks;
-
- /* Skip over unneeded chunk table entries. */
- u64 file_offset_of_needed_chunk_entries = resource_offset +
- start_table_idx * chunk_entry_size;
- if (fseeko(fp, file_offset_of_needed_chunk_entries, SEEK_SET))
- goto read_error;
-
- /* Number of bytes we need to read from the chunk table. */
- size_t size = num_needed_chunk_entries * chunk_entry_size;
-
- /* Read the raw data into the end of the chunk_offsets array to
- * avoid allocating another array. */
- void *chunk_tab_buf = (void*)&chunk_offsets[num_needed_chunks] - size;
-
- if (fread(chunk_tab_buf, 1, size, fp) != size)
- goto read_error;
-
- /* Now fill in chunk_offsets from the entries we have read in
- * chunk_tab_buf. */
-
- u64 *chunk_tab_p = chunk_offsets;
- if (start_chunk == 0)
- chunk_tab_p++;
-
- if (chunk_entry_size == 4) {
- u32 *entries = (u32*)chunk_tab_buf;
- while (num_needed_chunk_entries--)
- *chunk_tab_p++ = le32_to_cpu(*entries++);
- } else {
- u64 *entries = (u64*)chunk_tab_buf;
- while (num_needed_chunk_entries--)
- *chunk_tab_p++ = le64_to_cpu(*entries++);
- }
+ const u32 chunk_order = fls32(chunk_size);
+
+ /* Calculate the total number of chunks the resource is divided into. */
+ const u64 num_chunks = (rdesc->uncompressed_size + chunk_size - 1) >> chunk_order;
+
+ /* Calculate the 0-based indices of the first and last chunks containing
+ * data that needs to be passed to the callback. */
+ const u64 first_needed_chunk = first_offset >> chunk_order;
+ const u64 last_needed_chunk = last_offset >> chunk_order;
+
+ /* Calculate the 0-based index of the first chunk that actually needs to
+ * be read. This is normally first_needed_chunk, but for pipe reads we
+ * must always start from the 0th chunk. */
+ const u64 read_start_chunk = (is_pipe_read ? 0 : first_needed_chunk);
+
+ /* Calculate the number of chunk offsets that are needed for the chunks
+ * being read. */
+ const u64 num_needed_chunk_offsets =
+ last_needed_chunk - read_start_chunk + 1 +
+ (last_needed_chunk < num_chunks - 1);
+
+ /* Calculate the number of entries in the chunk table. Normally, it's
+ * one less than the number of chunks, since the first chunk has no
+ * entry. But in the alternate chunk table format, the chunk entries
+ * contain chunk sizes, not offsets, and there is one per chunk. */
+ const u64 num_chunk_entries = (alt_chunk_table ? num_chunks : num_chunks - 1);
+
+ /* Set the size of each chunk table entry based on the resource's
+ * uncompressed size. */
+ const u64 chunk_entry_size = get_chunk_entry_size(rdesc->uncompressed_size,
+ alt_chunk_table);
+
+ /* Calculate the size of the chunk table in bytes. */
+ const u64 chunk_table_size = num_chunk_entries * chunk_entry_size;
+
+ /* Calculate the size of the chunk table in bytes, including the header
+ * in the case of the alternate chunk table format. */
+ const u64 chunk_table_full_size =
+ (alt_chunk_table) ? chunk_table_size + sizeof(struct alt_chunk_table_header_disk)
+ : chunk_table_size;
+
+ if (!is_pipe_read) {
+ /* Read the needed chunk table entries into memory and use them
+ * to initialize the chunk_offsets array. */
+
+ u64 first_chunk_entry_to_read;
+ u64 num_chunk_entries_to_read;
+
+ if (alt_chunk_table) {
+ /* The alternate chunk table contains chunk sizes, not
+ * offsets, so we always must read all preceding entries
+ * in order to determine offsets. */
+ first_chunk_entry_to_read = 0;
+ num_chunk_entries_to_read = last_needed_chunk + 1;
+ } else {
+
+ num_chunk_entries_to_read = last_needed_chunk - read_start_chunk + 1;
+
+ /* The first chunk has no explicit chunk table entry. */
+ if (read_start_chunk == 0) {
+ num_chunk_entries_to_read--;
+ first_chunk_entry_to_read = 0;
+ } else {
+ first_chunk_entry_to_read = read_start_chunk - 1;
+ }
+
+ /* Unless we're reading the final chunk of the resource,
+ * we need the offset of the chunk following the last
+ * needed chunk so that the compressed size of the last
+ * needed chunk can be computed. */
+ if (last_needed_chunk < num_chunks - 1)
+ num_chunk_entries_to_read++;
+ }