]> wimlib.net Git - wimlib/blob - examples/decompressfile.c
mount_image.c: add fallback definitions of RENAME_* constants
[wimlib] / examples / decompressfile.c
1 /*
2  * decompressfile.c - decompression API example
3  *
4  * Copyright 2022 Eric Biggers
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation
8  * files (the "Software"), to deal in the Software without
9  * restriction, including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following
13  * conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  */
27
28 /*
29  * This an example of using wimlib's compression API to decompress a file
30  * compressed with the compressfile.c program.
31  *
32  * This program does *not* have anything to do with WIM files other than the
33  * fact that this makes use of compression formats that are used in WIM files.
34  * This is purely an example of using the compression API.
35  *
36  * Compile with:
37  *
38  *    $ gcc decompressfile.c -o decompressfile -lwim
39  *
40  * Run with:
41  *
42  *    $ ./decompressfile INFILE OUTFILE
43  *
44  * For example:
45  *
46  *    $ ./compressfile book.txt book.txt.lzms LZMS 1048576
47  *    $ rm -f book.txt
48  *    $ ./decompressfile book.txt.lzms book.txt
49  *
50  * The compressed file format created here is simply a series of compressed
51  * chunks.  A real format would need to have checksums and other metadata.
52  */
53
54 #define _FILE_OFFSET_BITS 64
55
56 #if defined(_MSC_VER) && _MSC_VER < 1800 /* VS pre-2013? */
57 #  define PRIu64 "I64u"
58 #  define PRIu32 "u"
59 #else
60 #  define __STDC_FORMAT_MACROS 1
61 #  include <inttypes.h>
62 #endif
63
64 #include <wimlib.h>
65
66 #include <errno.h>
67 #include <fcntl.h>
68 #include <stdarg.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #ifdef _WIN32
72 #  include <io.h>
73 #else
74 #  include <unistd.h>
75 #endif
76
77 /*
78  * Windows compatibility defines for string encoding.  Applications using wimlib
79  * that need to run on both UNIX and Windows will need to do something similar
80  * to this, whereas applications that only need to run on one or the other can
81  * just use their platform's convention directly.
82  */
83 #ifdef _WIN32
84 #  define main          wmain
85    typedef wchar_t      tchar;
86 #  define TS            "ls"
87 #  define topen         _wopen
88 #else
89    typedef char         tchar;
90 #  define TS            "s"
91 #  define topen         open
92 #  define O_BINARY      0
93 #endif
94
95 static void
96 fatal_error(int err, const char *format, ...)
97 {
98         va_list va;
99
100         va_start(va, format);
101         vfprintf(stderr, format, va);
102         if (err != 0)
103                 fprintf(stderr, ": %s\n", strerror(err));
104         else
105                 fputc('\n', stderr);
106         va_end(va);
107         exit(1);
108 }
109
110 static void
111 do_decompress(int in_fd, const tchar *in_filename,
112               int out_fd, const tchar *out_filename,
113               uint32_t chunk_size, struct wimlib_decompressor *decompressor)
114 {
115         uint64_t chunk_num;
116
117         char *ubuf = (char *)malloc(chunk_size);
118         char *cbuf = (char *)malloc(chunk_size - 1);
119
120         for (chunk_num = 1; ; chunk_num++) {
121                 int32_t bytes_read;
122                 uint32_t usize;
123                 uint32_t csize;
124
125                 /* Read chunk uncompressed and compressed sizes.  */
126                 bytes_read = read(in_fd, &usize, sizeof(uint32_t));
127                 if (bytes_read == 0)
128                         break;
129
130                 if (bytes_read != sizeof(uint32_t) ||
131                     read(in_fd, &csize, sizeof(uint32_t)) != sizeof(uint32_t))
132                 {
133                         fatal_error(errno, "Error reading \"%" TS"\"",
134                                     in_filename);
135                 }
136
137                 if (csize > usize || usize > chunk_size)
138                         fatal_error(0, "The data is invalid!");
139
140                 if (usize == csize) {
141                         if (read(in_fd, ubuf, usize) != (int32_t)usize) {
142                                 fatal_error(errno, "Error reading \"%" TS"\"",
143                                             in_filename);
144                         }
145                 } else {
146                         if (read(in_fd, cbuf, csize) != (int32_t)csize) {
147                                 fatal_error(errno, "Error reading \"%" TS"\"",
148                                             in_filename);
149                         }
150
151                         if (wimlib_decompress(cbuf, csize, ubuf, usize,
152                                               decompressor))
153                         {
154                                 fatal_error(0,
155                                             "The compressed data is invalid!");
156                         }
157                 }
158
159                 printf("Chunk %" PRIu64": %" PRIu32" => %" PRIu32" bytes\n",
160                        chunk_num, csize, usize);
161
162                 /* Output the uncompressed chunk size, the compressed chunk
163                  * size, then the chunk data.  Note: a real program would need
164                  * to output the chunk sizes in consistent endianness.  */
165                 if (write(out_fd, ubuf, usize) != (int32_t)usize) {
166                         fatal_error(errno, "Error writing to \"%" TS"\"",
167                                     out_filename);
168                 }
169         }
170         free(ubuf);
171         free(cbuf);
172 }
173
174 int main(int argc, tchar **argv)
175 {
176         const tchar *in_filename;
177         const tchar *out_filename;
178         int in_fd;
179         int out_fd;
180         uint32_t ctype32;
181         enum wimlib_compression_type ctype;
182         uint32_t chunk_size;
183         int ret;
184         struct wimlib_decompressor *decompressor;
185
186         if (argc != 3) {
187                 fprintf(stderr, "Usage: %" TS" INFILE OUTFILE\n", argv[0]);
188                 return 2;
189         }
190
191         in_filename = argv[1];
192         out_filename = argv[2];
193
194         /* Open input file and output file.  */
195         in_fd = topen(in_filename, O_RDONLY | O_BINARY);
196         if (in_fd < 0)
197                 fatal_error(errno, "Failed to open \"%" TS"\"", in_filename);
198         out_fd = topen(out_filename, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY,
199                        0644);
200         if (out_fd < 0)
201                 fatal_error(errno, "Failed to open \"%" TS"\"", out_filename);
202
203         /* Get compression type and chunk size.  */
204         if (read(in_fd, &ctype32, sizeof(uint32_t)) != sizeof(uint32_t) ||
205             read(in_fd, &chunk_size, sizeof(uint32_t)) != sizeof(uint32_t))
206                 fatal_error(errno, "Error reading from \"%" TS"\"", in_filename);
207         ctype = (enum wimlib_compression_type)ctype32;
208
209         /* Create a decompressor for the compression type and chunk size with
210          * the default parameters.  */
211         ret = wimlib_create_decompressor(ctype, chunk_size, &decompressor);
212         if (ret != 0)
213                 fatal_error(0, "Failed to create decompressor: %" TS,
214                             wimlib_get_error_string((enum wimlib_error_code)ret));
215
216         /* Decompress and write the data.  */
217         do_decompress(in_fd, in_filename,
218                       out_fd, out_filename,
219                       chunk_size, decompressor);
220
221         /* Cleanup and return.  */
222         if (close(out_fd))
223                 fatal_error(errno, "Error closing \"%" TS"\"", out_filename);
224         wimlib_free_decompressor(decompressor);
225         return 0;
226 }