From: Eric Biggers Date: Thu, 26 Dec 2013 23:28:58 +0000 (-0600) Subject: Add wimlib_set_output_{pack_chunk_size,compression_type}() X-Git-Tag: v1.6.0~89 X-Git-Url: https://wimlib.net/git/?p=wimlib;a=commitdiff_plain;h=2057dd2fcc73340526a2c5a93d16c296fc20e064 Add wimlib_set_output_{pack_chunk_size,compression_type}() --- diff --git a/include/wimlib.h b/include/wimlib.h index ab1b78b2..00caf4e3 100644 --- a/include/wimlib.h +++ b/include/wimlib.h @@ -417,8 +417,7 @@ enum wimlib_compression_type { /** Compressed resources in the WIM use XPRESS compression. */ WIMLIB_COMPRESSION_TYPE_XPRESS = 2, - /** Compressed resources in the WIM use LZMS compression. Currently, - * wimlib has a decompressor for this format but not a compressor. LZMS + /** Compressed resources in the WIM use LZMS compression. Note: LZMS * compression is only compatible with wimlib v1.6.0 and later and with * WIMGAPI Windows 8 and later (and some restrictions apply on the * latter). */ @@ -3216,7 +3215,9 @@ wimlib_set_image_descripton(WIMStruct *wim, int image, * on the compression format. The XPRESS compression format supports chunk * sizes that are powers of 2 with exponents between 15 and 26 inclusively, * whereas the LZX compression format supports chunk sizes that are powers - * of 2 with exponents between 15 and 21 inclusively. + * of 2 with exponents between 15 and 21 inclusively. As a special case, + * if @p out_chunk_size is specified as 0, the chunk size is set to the + * default for the currently selected output compression type. * * @return 0 on success; nonzero on error. * @@ -3226,6 +3227,15 @@ wimlib_set_image_descripton(WIMStruct *wim, int image, extern int wimlib_set_output_chunk_size(WIMStruct *wim, uint32_t chunk_size); +/** + * @ingroup G_writing_and_overwriting_wims + * + * Similar to wimlib_set_output_chunk_size(), but set the chunk size for writing + * packed streams. + */ +extern int +wimlib_set_output_pack_chunk_size(WIMStruct *wim, uint32_t chunk_size); + /** * @ingroup G_writing_and_overwriting_wims * @@ -3242,12 +3252,21 @@ wimlib_set_output_chunk_size(WIMStruct *wim, uint32_t chunk_size); * * @return 0 on success; nonzero on error. * - * @retval ::WIMLIB_ERR_INVALID_PARAM + * @retval ::WIMLIB_ERR_INVALID_COMPRESSION_TYPE * @p ctype did not specify a valid compression type. */ extern int wimlib_set_output_compression_type(WIMStruct *wim, int ctype); +/** + * @ingroup G_writing_and_overwriting_wims + * + * Similar to wimlib_set_output_compression_type(), but set the compression type + * for writing packed streams. + */ +extern int +wimlib_set_output_pack_compression_type(WIMStruct *wim, int ctype); + /** * @ingroup G_modifying_wims * diff --git a/include/wimlib/header.h b/include/wimlib/header.h index 6f701891..2841b827 100644 --- a/include/wimlib/header.h +++ b/include/wimlib/header.h @@ -17,7 +17,7 @@ /* Version number used for WIMs that allow multiple streams packed into one * resource (WIM_RESHDR_FLAG_PACKED_STREAMS). New as of Windows 8 WIMGAPI; used * for the Windows 8 web downloader, but yet not yet properly documented by - * Microsoft. */ + * Microsoft. This WIM format also allows a new compression format (LZMS). */ #define WIM_VERSION_PACKED_STREAMS 0xe00 /* Note: there is another WIM version from Vista pre-releases, but it is not diff --git a/include/wimlib/wim.h b/include/wimlib/wim.h index 99a37362..2e780343 100644 --- a/include/wimlib/wim.h +++ b/include/wimlib/wim.h @@ -70,13 +70,16 @@ struct WIMStruct { u8 guid_set_explicitly : 1; /* One of WIMLIB_COMPRESSION_TYPE_*, cached from the header flags. */ - u8 compression_type : 2; + u8 compression_type; - /* Overwritten compression type for wimlib_overwrite() or - * wimlib_write(). Can be changed by - * wimlib_set_output_compression_type(); otherwise is the same as - * compression_type. */ - u8 out_compression_type : 2; + /* Overridden compression type for wimlib_overwrite() or wimlib_write(). + * Can be changed by wimlib_set_output_compression_type(); otherwise is + * the same as compression_type. */ + u8 out_compression_type; + + /* Compression type for writing packed streams; can be set with + * wimlib_set_output_pack_compression_type(). */ + u8 out_pack_compression_type; /* Uncompressed size of compressed chunks in this WIM (cached from * header). */ @@ -86,6 +89,10 @@ struct WIMStruct { * be changed by wimlib_set_output_chunk_size(); otherwise is the same * as chunk_size. */ u32 out_chunk_size; + + /* Chunk size for writing packed streams; can be set with + * wimlib_set_output_pack_chunk_size(). */ + u32 out_pack_chunk_size; }; static inline bool wim_is_pipable(const WIMStruct *wim) diff --git a/src/header.c b/src/header.c index 90d40ded..0f955519 100644 --- a/src/header.c +++ b/src/header.c @@ -257,7 +257,11 @@ init_wim_header(struct wim_header *hdr, int ctype, u32 chunk_size) { memset(hdr, 0, sizeof(struct wim_header)); hdr->magic = WIM_MAGIC; - hdr->wim_version = WIM_VERSION_DEFAULT; + + if (ctype == WIMLIB_COMPRESSION_TYPE_LZMS) + hdr->wim_version = WIM_VERSION_PACKED_STREAMS; + else + hdr->wim_version = WIM_VERSION_DEFAULT; if (set_wim_hdr_cflags(ctype, hdr)) { ERROR("Invalid compression type specified (%d)", ctype); return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; diff --git a/src/wim.c b/src/wim.c index d833ec75..7a94bd8a 100644 --- a/src/wim.c +++ b/src/wim.c @@ -127,18 +127,11 @@ wim_chunk_size_valid(u32 chunk_size, int ctype) return order >= 15 && order <= 21; case WIMLIB_COMPRESSION_TYPE_XPRESS: - /* WIMGAPI (Windows 7) didn't seem to support XPRESS chunk size - * below 32768 bytes, but larger power-of-two sizes appear to be - * supported. 67108864 was the largest size that worked. - * (Note, however, that the offsets of XPRESS matches are still - * limited to 65535 bytes even when a much larger chunk size is - * used!) - * - * WIMGAPI (Windows 8) seemed to have removed the support for - * larger XPRESS chunk sizes and will refuse to open such WIMs. - * - * 2^15 = 32768 is the default value used for compatibility, but - * wimlib can actually use up to 2^26. */ + /* WIMGAPI (Windows 7, Windows 8) doesn't seem to support XPRESS + * chunk size below 32768 bytes, but larger power-of-two sizes, + * up ta 67108864 bytes, appear to work. (Note, however, that + * the offsets of XPRESS matches are still limited to 65535 + * bytes even when a much larger chunk size is used!) */ return order >= 15 && order <= 26; case WIMLIB_COMPRESSION_TYPE_LZMS: @@ -155,9 +148,21 @@ wim_default_chunk_size(int ctype) { switch (ctype) { case WIMLIB_COMPRESSION_TYPE_LZMS: - return 131072; + return 1U << 17; /* 131072 */ default: - return 32768; + return 1U << 15; /* 32768 */ + } +} + +static u32 +wim_default_pack_chunk_size(int ctype) { + switch (ctype) { + case WIMLIB_COMPRESSION_TYPE_LZMS: + /* Note: WIMGAPI uses 1 << 26, but lower sizes are compatible. + * */ + return 1U << 25; /* 33554432 */ + default: + return 1U << 15; /* 32768 */ } } @@ -226,8 +231,10 @@ wimlib_create_new_wim(int ctype, WIMStruct **wim_ret) wim->refcnts_ok = 1; wim->compression_type = ctype; wim->out_compression_type = ctype; + wim->out_pack_compression_type = ctype; wim->chunk_size = wim->hdr.chunk_size; wim->out_chunk_size = wim->hdr.chunk_size; + wim->out_pack_chunk_size = wim_default_pack_chunk_size(ctype); *wim_ret = wim; return 0; out_free: @@ -447,9 +454,8 @@ wimlib_set_wim_info(WIMStruct *wim, const struct wimlib_wim_info *info, int whic return 0; } -/* API function documented in wimlib.h */ -WIMLIBAPI int -wimlib_set_output_compression_type(WIMStruct *wim, int ctype) +static int +set_out_ctype(int ctype, u8 *out_ctype_p) { switch (ctype) { case WIMLIB_COMPRESSION_TYPE_INVALID: @@ -458,53 +464,85 @@ wimlib_set_output_compression_type(WIMStruct *wim, int ctype) case WIMLIB_COMPRESSION_TYPE_LZX: case WIMLIB_COMPRESSION_TYPE_XPRESS: case WIMLIB_COMPRESSION_TYPE_LZMS: - wim->out_compression_type = ctype; - - /* Reset the chunk size if it's no longer valid. */ - if (!wim_chunk_size_valid(wim->out_chunk_size, - wim->out_compression_type)) - wim->out_chunk_size = wim_default_chunk_size(wim->out_compression_type); + *out_ctype_p = ctype; return 0; } - return WIMLIB_ERR_INVALID_PARAM; + return WIMLIB_ERR_INVALID_COMPRESSION_TYPE; } /* API function documented in wimlib.h */ WIMLIBAPI int -wimlib_set_output_chunk_size(WIMStruct *wim, uint32_t chunk_size) +wimlib_set_output_compression_type(WIMStruct *wim, int ctype) +{ + int ret = set_out_ctype(ctype, &wim->out_compression_type); + if (ret) + return ret; + + /* Reset the chunk size if it's no longer valid. */ + if (!wim_chunk_size_valid(ctype, wim->out_chunk_size)) + wim->out_chunk_size = wim_default_chunk_size(ctype); + return 0; +} + +/* API function documented in wimlib.h */ +WIMLIBAPI int +wimlib_set_output_pack_compression_type(WIMStruct *wim, int ctype) { - if (!wim_chunk_size_valid(chunk_size, wim->out_compression_type)) { + int ret = set_out_ctype(ctype, &wim->out_pack_compression_type); + if (ret) + return ret; + + /* Reset the chunk size if it's no longer valid. */ + if (!wim_chunk_size_valid(ctype, wim->out_pack_chunk_size)) + wim->out_pack_chunk_size = wim_default_pack_chunk_size(ctype); + return 0; +} + +static int +set_out_chunk_size(u32 chunk_size, int ctype, u32 *out_chunk_size_p) +{ + if (!wim_chunk_size_valid(chunk_size, ctype)) { ERROR("Invalid chunk size (%"PRIu32" bytes) " "for compression type %"TS"!", chunk_size, - wimlib_get_compression_type_string(wim->out_compression_type)); - switch (wim->out_compression_type) { - case WIMLIB_COMPRESSION_TYPE_XPRESS: - ERROR("Valid chunk sizes for XPRESS are " - "32768, 65536, 131072, ..., 67108864."); - break; - case WIMLIB_COMPRESSION_TYPE_LZX: - ERROR("Valid chunk sizes for LZX are " - "32768, 65536, 131072, ..., 2097152."); - break; - case WIMLIB_COMPRESSION_TYPE_LZMS: - ERROR("Valid chunk sizes for LZMS are " - "32768, 65536, 131072, ..., 67108864."); - break; - } + wimlib_get_compression_type_string(ctype)); return WIMLIB_ERR_INVALID_CHUNK_SIZE; } - if (chunk_size != 32768 && - wim->out_compression_type != WIMLIB_COMPRESSION_TYPE_LZMS) - { - WARNING ("Changing the compression chunk size to any value other than\n" - " the default of 32768 bytes eliminates compatibility with\n" - " Microsoft's software!"); - } - wim->out_chunk_size = chunk_size; + + *out_chunk_size_p = chunk_size; return 0; } +/* API function documented in wimlib.h */ +WIMLIBAPI int +wimlib_set_output_chunk_size(WIMStruct *wim, uint32_t chunk_size) +{ + if (chunk_size == 0) { + wim->out_chunk_size = + wim_default_chunk_size(wim->out_compression_type); + return 0; + } + + return set_out_chunk_size(chunk_size, + wim->out_compression_type, + &wim->out_chunk_size); +} + +/* API function documented in wimlib.h */ +WIMLIBAPI int +wimlib_set_output_pack_chunk_size(WIMStruct *wim, uint32_t chunk_size) +{ + if (chunk_size == 0) { + wim->out_pack_chunk_size = + wim_default_pack_chunk_size(wim->out_pack_compression_type); + return 0; + } + + return set_out_chunk_size(chunk_size, + wim->out_pack_compression_type, + &wim->out_pack_chunk_size); +} + static int do_open_wim(const tchar *filename, struct filedes *fd_ret) { @@ -629,9 +667,12 @@ begin_read(WIMStruct *wim, const void *wim_filename_or_fd, wim->compression_type = WIMLIB_COMPRESSION_TYPE_NONE; } wim->out_compression_type = wim->compression_type; + wim->out_pack_compression_type = wim->compression_type; /* Check and cache the chunk size. */ - wim->chunk_size = wim->out_chunk_size = wim->hdr.chunk_size; + wim->chunk_size = wim->hdr.chunk_size; + wim->out_chunk_size = wim->chunk_size; + wim->out_pack_chunk_size = wim_default_pack_chunk_size(wim->out_pack_compression_type); if (!wim_chunk_size_valid(wim->chunk_size, wim->compression_type)) { ERROR("Invalid chunk size (%"PRIu32" bytes) " "for compression type %"TS"!", diff --git a/src/write.c b/src/write.c index 0375373a..d2bc3734 100644 --- a/src/write.c +++ b/src/write.c @@ -1308,9 +1308,6 @@ write_stream_list(struct list_head *stream_list, (WRITE_RESOURCE_FLAG_PACK_STREAMS | WRITE_RESOURCE_FLAG_PIPABLE)); - if (write_resource_flags & WRITE_RESOURCE_FLAG_PACK_STREAMS) - out_chunk_size = 1U << 26; - remove_zero_length_streams(stream_list); if (list_empty(stream_list)) { @@ -1492,6 +1489,39 @@ out_destroy_context: return ret; } +static int +wim_write_stream_list(WIMStruct *wim, + struct list_head *stream_list, + int write_flags, + unsigned num_threads, + struct filter_context *filter_ctx, + wimlib_progress_func_t progress_func) +{ + int out_ctype; + u32 out_chunk_size; + int write_resource_flags; + + write_resource_flags = write_flags_to_resource_flags(write_flags); + + if (write_resource_flags & WRITE_RESOURCE_FLAG_PACK_STREAMS) { + out_chunk_size = wim->out_pack_chunk_size; + out_ctype = wim->out_pack_compression_type; + } else { + out_chunk_size = wim->out_chunk_size; + out_ctype = wim->out_compression_type; + } + + return write_stream_list(stream_list, + &wim->out_fd, + write_resource_flags, + out_ctype, + out_chunk_size, + num_threads, + wim->lookup_table, + filter_ctx, + progress_func); +} + static int write_wim_resource(struct wim_lookup_table_entry *lte, struct filedes *out_fd, @@ -1944,15 +1974,12 @@ write_wim_streams(WIMStruct *wim, int image, int write_flags, } } - return write_stream_list(stream_list, - &wim->out_fd, - write_flags_to_resource_flags(write_flags), - wim->out_compression_type, - wim->out_chunk_size, - num_threads, - wim->lookup_table, - filter_ctx, - progress_func); + return wim_write_stream_list(wim, + stream_list, + write_flags, + num_threads, + filter_ctx, + progress_func); } static int @@ -2608,7 +2635,8 @@ write_wim_part(WIMStruct *wim, wim->hdr.magic = WIM_MAGIC; /* Set appropriate version number. */ - if (write_flags & WIMLIB_WRITE_FLAG_PACK_STREAMS) + if ((write_flags & WIMLIB_WRITE_FLAG_PACK_STREAMS) || + wim->out_compression_type == WIMLIB_COMPRESSION_TYPE_LZMS) wim->hdr.wim_version = WIM_VERSION_PACKED_STREAMS; else wim->hdr.wim_version = WIM_VERSION_DEFAULT; @@ -2996,15 +3024,12 @@ overwrite_wim_inplace(WIMStruct *wim, int write_flags, goto out_restore_physical_hdr; } - ret = write_stream_list(&stream_list, - &wim->out_fd, - write_flags_to_resource_flags(write_flags), - wim->compression_type, - wim->chunk_size, - num_threads, - wim->lookup_table, - &filter_ctx, - progress_func); + ret = wim_write_stream_list(wim, + &stream_list, + write_flags, + num_threads, + &filter_ctx, + progress_func); if (ret) goto out_truncate;