- /* Calculate how many chunks the resource conists of in its entirety. */
- u64 num_chunks = (resource_uncompressed_size + WIM_CHUNK_SIZE - 1) /
- WIM_CHUNK_SIZE;
- /* As mentioned, the first chunk has no entry in the chunk table. */
- u64 num_chunk_entries = num_chunks - 1;
-
-
- /* The index of the chunk that the read starts at. */
- u64 start_chunk = offset / WIM_CHUNK_SIZE;
- /* The byte offset at which the read starts, within the start chunk. */
- u64 start_chunk_offset = offset % WIM_CHUNK_SIZE;
-
- /* The index of the chunk that contains the last byte of the read. */
- u64 end_chunk = (offset + len - 1) / WIM_CHUNK_SIZE;
- /* The byte offset of the last byte of the read, within the end chunk */
- u64 end_chunk_offset = (offset + len - 1) % WIM_CHUNK_SIZE;
-
- /* Number of chunks that are actually needed to read the requested part
- * of the file. */
- u64 num_needed_chunks = end_chunk - start_chunk + 1;
-
- /* If the end chunk is not the last chunk, an extra chunk entry is
- * needed because we need to know the offset of the chunk after the last
- * chunk read to figure out the size of the last read chunk. */
- if (end_chunk != num_chunks - 1)
- num_needed_chunks++;
-
- /* Declare the chunk table. It will only contain offsets for the chunks
- * that are actually needed for this read. */
- u64 chunk_offsets[num_needed_chunks];
-
- /* 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;
+ 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 {