diff options
author | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2021-12-15 21:04:28 +0100 |
---|---|---|
committer | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2021-12-24 18:05:53 +0100 |
commit | c876d3bbefe0dc00c27ca0c12d29da5874946962 (patch) | |
tree | 69f468a185a369b01998314bc3ecc19b70f4fcaa /utils/rbutilqt/mspack/cabd.c | |
parent | 6c6f0757d7a902feb293be165d1490c42bc8e7ad (diff) | |
download | rockbox-c876d3bbefe0dc00c27ca0c12d29da5874946962.tar.gz rockbox-c876d3bbefe0dc00c27ca0c12d29da5874946962.zip |
rbutil: Merge rbutil with utils folder.
rbutil uses several components from the utils folder, and can be
considered part of utils too. Having it in a separate folder is an
arbitrary split that doesn't help anymore these days, so merge them.
This also allows other utils to easily use libtools.make without the
need to navigate to a different folder.
Change-Id: I3fc2f4de19e3e776553efb5dea5f779dfec0dc21
Diffstat (limited to 'utils/rbutilqt/mspack/cabd.c')
-rw-r--r-- | utils/rbutilqt/mspack/cabd.c | 1508 |
1 files changed, 1508 insertions, 0 deletions
diff --git a/utils/rbutilqt/mspack/cabd.c b/utils/rbutilqt/mspack/cabd.c new file mode 100644 index 0000000000..ae66769b24 --- /dev/null +++ b/utils/rbutilqt/mspack/cabd.c | |||
@@ -0,0 +1,1508 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2018 Stuart Caie. | ||
3 | * | ||
4 | * libmspack is free software; you can redistribute it and/or modify it under | ||
5 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
6 | * | ||
7 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
8 | */ | ||
9 | |||
10 | /* Cabinet (.CAB) files are a form of file archive. Each cabinet contains | ||
11 | * "folders", which are compressed spans of data. Each cabinet has | ||
12 | * "files", whose metadata is in the cabinet header, but whose actual data | ||
13 | * is stored compressed in one of the "folders". Cabinets can span more | ||
14 | * than one physical file on disk, in which case they are a "cabinet set", | ||
15 | * and usually the last folder of each cabinet extends into the next | ||
16 | * cabinet. | ||
17 | * | ||
18 | * For a complete description of the format, see the MSDN site: | ||
19 | * http://msdn.microsoft.com/en-us/library/bb267310.aspx | ||
20 | */ | ||
21 | |||
22 | /* CAB decompression implementation */ | ||
23 | |||
24 | #include "system-mspack.h" | ||
25 | #include "cab.h" | ||
26 | #include "mszip.h" | ||
27 | #include "lzx.h" | ||
28 | #include "qtm.h" | ||
29 | |||
30 | /* Notes on compliance with cabinet specification: | ||
31 | * | ||
32 | * One of the main changes between cabextract 0.6 and libmspack's cab | ||
33 | * decompressor is the move from block-oriented decompression to | ||
34 | * stream-oriented decompression. | ||
35 | * | ||
36 | * cabextract would read one data block from disk, decompress it with the | ||
37 | * appropriate method, then write the decompressed data. The CAB | ||
38 | * specification is specifically designed to work like this, as it ensures | ||
39 | * compression matches do not span the maximum decompressed block size | ||
40 | * limit of 32kb. | ||
41 | * | ||
42 | * However, the compression algorithms used are stream oriented, with | ||
43 | * specific hacks added to them to enforce the "individual 32kb blocks" | ||
44 | * rule in CABs. In other file formats, they do not have this limitation. | ||
45 | * | ||
46 | * In order to make more generalised decompressors, libmspack's CAB | ||
47 | * decompressor has moved from being block-oriented to more stream | ||
48 | * oriented. This also makes decompression slightly faster. | ||
49 | * | ||
50 | * However, this leads to incompliance with the CAB specification. The | ||
51 | * CAB controller can no longer ensure each block of input given to the | ||
52 | * decompressors is matched with their output. The "decompressed size" of | ||
53 | * each individual block is thrown away. | ||
54 | * | ||
55 | * Each CAB block is supposed to be seen as individually compressed. This | ||
56 | * means each consecutive data block can have completely different | ||
57 | * "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in | ||
58 | * reality, all data blocks in a folder decompress to exactly 32768 bytes, | ||
59 | * excepting the final block. | ||
60 | * | ||
61 | * Given this situation, the decompression algorithms are designed to | ||
62 | * realign their input bitstreams on 32768 output-byte boundaries, and | ||
63 | * various other special cases have been made. libmspack will not | ||
64 | * correctly decompress LZX or Quantum compressed folders where the blocks | ||
65 | * do not follow this "32768 bytes until last block" pattern. It could be | ||
66 | * implemented if needed, but hopefully this is not necessary -- it has | ||
67 | * not been seen in over 3Gb of CAB archives. | ||
68 | */ | ||
69 | |||
70 | /* prototypes */ | ||
71 | static struct mscabd_cabinet * cabd_open( | ||
72 | struct mscab_decompressor *base, const char *filename); | ||
73 | static void cabd_close( | ||
74 | struct mscab_decompressor *base, struct mscabd_cabinet *origcab); | ||
75 | static int cabd_read_headers( | ||
76 | struct mspack_system *sys, struct mspack_file *fh, | ||
77 | struct mscabd_cabinet_p *cab, off_t offset, int salvage, int quiet); | ||
78 | static char *cabd_read_string( | ||
79 | struct mspack_system *sys, struct mspack_file *fh, int *error); | ||
80 | |||
81 | static struct mscabd_cabinet *cabd_search( | ||
82 | struct mscab_decompressor *base, const char *filename); | ||
83 | static int cabd_find( | ||
84 | struct mscab_decompressor_p *self, unsigned char *buf, | ||
85 | struct mspack_file *fh, const char *filename, off_t flen, | ||
86 | off_t *firstlen, struct mscabd_cabinet_p **firstcab); | ||
87 | |||
88 | static int cabd_prepend( | ||
89 | struct mscab_decompressor *base, struct mscabd_cabinet *cab, | ||
90 | struct mscabd_cabinet *prevcab); | ||
91 | static int cabd_append( | ||
92 | struct mscab_decompressor *base, struct mscabd_cabinet *cab, | ||
93 | struct mscabd_cabinet *nextcab); | ||
94 | static int cabd_merge( | ||
95 | struct mscab_decompressor *base, struct mscabd_cabinet *lcab, | ||
96 | struct mscabd_cabinet *rcab); | ||
97 | static int cabd_can_merge_folders( | ||
98 | struct mspack_system *sys, struct mscabd_folder_p *lfol, | ||
99 | struct mscabd_folder_p *rfol); | ||
100 | |||
101 | static int cabd_extract( | ||
102 | struct mscab_decompressor *base, struct mscabd_file *file, | ||
103 | const char *filename); | ||
104 | static int cabd_init_decomp( | ||
105 | struct mscab_decompressor_p *self, unsigned int ct); | ||
106 | static void cabd_free_decomp( | ||
107 | struct mscab_decompressor_p *self); | ||
108 | static int cabd_sys_read( | ||
109 | struct mspack_file *file, void *buffer, int bytes); | ||
110 | static int cabd_sys_write( | ||
111 | struct mspack_file *file, void *buffer, int bytes); | ||
112 | static int cabd_sys_read_block( | ||
113 | struct mspack_system *sys, struct mscabd_decompress_state *d, int *out, | ||
114 | int ignore_cksum, int ignore_blocksize); | ||
115 | static unsigned int cabd_checksum( | ||
116 | unsigned char *data, unsigned int bytes, unsigned int cksum); | ||
117 | static struct noned_state *noned_init( | ||
118 | struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out, | ||
119 | int bufsize); | ||
120 | |||
121 | static int noned_decompress( | ||
122 | struct noned_state *s, off_t bytes); | ||
123 | static void noned_free( | ||
124 | struct noned_state *state); | ||
125 | |||
126 | static int cabd_param( | ||
127 | struct mscab_decompressor *base, int param, int value); | ||
128 | |||
129 | static int cabd_error( | ||
130 | struct mscab_decompressor *base); | ||
131 | |||
132 | |||
133 | /*************************************** | ||
134 | * MSPACK_CREATE_CAB_DECOMPRESSOR | ||
135 | *************************************** | ||
136 | * constructor | ||
137 | */ | ||
138 | struct mscab_decompressor * | ||
139 | mspack_create_cab_decompressor(struct mspack_system *sys) | ||
140 | { | ||
141 | struct mscab_decompressor_p *self = NULL; | ||
142 | |||
143 | if (!sys) sys = mspack_default_system; | ||
144 | if (!mspack_valid_system(sys)) return NULL; | ||
145 | |||
146 | if ((self = (struct mscab_decompressor_p *) sys->alloc(sys, sizeof(struct mscab_decompressor_p)))) { | ||
147 | self->base.open = &cabd_open; | ||
148 | self->base.close = &cabd_close; | ||
149 | self->base.search = &cabd_search; | ||
150 | self->base.extract = &cabd_extract; | ||
151 | self->base.prepend = &cabd_prepend; | ||
152 | self->base.append = &cabd_append; | ||
153 | self->base.set_param = &cabd_param; | ||
154 | self->base.last_error = &cabd_error; | ||
155 | self->system = sys; | ||
156 | self->d = NULL; | ||
157 | self->error = MSPACK_ERR_OK; | ||
158 | |||
159 | self->searchbuf_size = 32768; | ||
160 | self->fix_mszip = 0; | ||
161 | self->buf_size = 4096; | ||
162 | self->salvage = 0; | ||
163 | } | ||
164 | return (struct mscab_decompressor *) self; | ||
165 | } | ||
166 | |||
167 | /*************************************** | ||
168 | * MSPACK_DESTROY_CAB_DECOMPRESSOR | ||
169 | *************************************** | ||
170 | * destructor | ||
171 | */ | ||
172 | void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) { | ||
173 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
174 | if (self) { | ||
175 | struct mspack_system *sys = self->system; | ||
176 | if (self->d) { | ||
177 | if (self->d->infh) sys->close(self->d->infh); | ||
178 | cabd_free_decomp(self); | ||
179 | sys->free(self->d); | ||
180 | } | ||
181 | sys->free(self); | ||
182 | } | ||
183 | } | ||
184 | |||
185 | |||
186 | /*************************************** | ||
187 | * CABD_OPEN | ||
188 | *************************************** | ||
189 | * opens a file and tries to read it as a cabinet file | ||
190 | */ | ||
191 | static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, | ||
192 | const char *filename) | ||
193 | { | ||
194 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
195 | struct mscabd_cabinet_p *cab = NULL; | ||
196 | struct mspack_system *sys; | ||
197 | struct mspack_file *fh; | ||
198 | int error; | ||
199 | |||
200 | if (!base) return NULL; | ||
201 | sys = self->system; | ||
202 | |||
203 | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { | ||
204 | if ((cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { | ||
205 | cab->base.filename = filename; | ||
206 | error = cabd_read_headers(sys, fh, cab, (off_t) 0, self->salvage, 0); | ||
207 | if (error) { | ||
208 | cabd_close(base, (struct mscabd_cabinet *) cab); | ||
209 | cab = NULL; | ||
210 | } | ||
211 | self->error = error; | ||
212 | } | ||
213 | else { | ||
214 | self->error = MSPACK_ERR_NOMEMORY; | ||
215 | } | ||
216 | sys->close(fh); | ||
217 | } | ||
218 | else { | ||
219 | self->error = MSPACK_ERR_OPEN; | ||
220 | } | ||
221 | return (struct mscabd_cabinet *) cab; | ||
222 | } | ||
223 | |||
224 | /*************************************** | ||
225 | * CABD_CLOSE | ||
226 | *************************************** | ||
227 | * frees all memory associated with a given mscabd_cabinet. | ||
228 | */ | ||
229 | static void cabd_close(struct mscab_decompressor *base, | ||
230 | struct mscabd_cabinet *origcab) | ||
231 | { | ||
232 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
233 | struct mscabd_folder_data *dat, *ndat; | ||
234 | struct mscabd_cabinet *cab, *ncab; | ||
235 | struct mscabd_folder *fol, *nfol; | ||
236 | struct mscabd_file *fi, *nfi; | ||
237 | struct mspack_system *sys; | ||
238 | |||
239 | if (!base) return; | ||
240 | sys = self->system; | ||
241 | |||
242 | self->error = MSPACK_ERR_OK; | ||
243 | |||
244 | while (origcab) { | ||
245 | /* free files */ | ||
246 | for (fi = origcab->files; fi; fi = nfi) { | ||
247 | nfi = fi->next; | ||
248 | sys->free(fi->filename); | ||
249 | sys->free(fi); | ||
250 | } | ||
251 | |||
252 | /* free folders */ | ||
253 | for (fol = origcab->folders; fol; fol = nfol) { | ||
254 | nfol = fol->next; | ||
255 | |||
256 | /* free folder decompression state if it has been decompressed */ | ||
257 | if (self->d && (self->d->folder == (struct mscabd_folder_p *) fol)) { | ||
258 | if (self->d->infh) sys->close(self->d->infh); | ||
259 | cabd_free_decomp(self); | ||
260 | sys->free(self->d); | ||
261 | self->d = NULL; | ||
262 | } | ||
263 | |||
264 | /* free folder data segments */ | ||
265 | for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) { | ||
266 | ndat = dat->next; | ||
267 | sys->free(dat); | ||
268 | } | ||
269 | sys->free(fol); | ||
270 | } | ||
271 | |||
272 | /* free predecessor cabinets (and the original cabinet's strings) */ | ||
273 | for (cab = origcab; cab; cab = ncab) { | ||
274 | ncab = cab->prevcab; | ||
275 | sys->free(cab->prevname); | ||
276 | sys->free(cab->nextname); | ||
277 | sys->free(cab->previnfo); | ||
278 | sys->free(cab->nextinfo); | ||
279 | if (cab != origcab) sys->free(cab); | ||
280 | } | ||
281 | |||
282 | /* free successor cabinets */ | ||
283 | for (cab = origcab->nextcab; cab; cab = ncab) { | ||
284 | ncab = cab->nextcab; | ||
285 | sys->free(cab->prevname); | ||
286 | sys->free(cab->nextname); | ||
287 | sys->free(cab->previnfo); | ||
288 | sys->free(cab->nextinfo); | ||
289 | sys->free(cab); | ||
290 | } | ||
291 | |||
292 | /* free actual cabinet structure */ | ||
293 | cab = origcab->next; | ||
294 | sys->free(origcab); | ||
295 | |||
296 | /* repeat full procedure again with the cab->next pointer (if set) */ | ||
297 | origcab = cab; | ||
298 | } | ||
299 | } | ||
300 | |||
301 | /*************************************** | ||
302 | * CABD_READ_HEADERS | ||
303 | *************************************** | ||
304 | * reads the cabinet file header, folder list and file list. | ||
305 | * fills out a pre-existing mscabd_cabinet structure, allocates memory | ||
306 | * for folders and files as necessary | ||
307 | */ | ||
308 | static int cabd_read_headers(struct mspack_system *sys, | ||
309 | struct mspack_file *fh, | ||
310 | struct mscabd_cabinet_p *cab, | ||
311 | off_t offset, int salvage, int quiet) | ||
312 | { | ||
313 | int num_folders, num_files, folder_resv, i, x, err, fidx; | ||
314 | struct mscabd_folder_p *fol, *linkfol = NULL; | ||
315 | struct mscabd_file *file, *linkfile = NULL; | ||
316 | unsigned char buf[64]; | ||
317 | |||
318 | /* initialise pointers */ | ||
319 | cab->base.next = NULL; | ||
320 | cab->base.files = NULL; | ||
321 | cab->base.folders = NULL; | ||
322 | cab->base.prevcab = cab->base.nextcab = NULL; | ||
323 | cab->base.prevname = cab->base.nextname = NULL; | ||
324 | cab->base.previnfo = cab->base.nextinfo = NULL; | ||
325 | |||
326 | cab->base.base_offset = offset; | ||
327 | |||
328 | /* seek to CFHEADER */ | ||
329 | if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { | ||
330 | return MSPACK_ERR_SEEK; | ||
331 | } | ||
332 | |||
333 | /* read in the CFHEADER */ | ||
334 | if (sys->read(fh, &buf[0], cfhead_SIZEOF) != cfhead_SIZEOF) { | ||
335 | return MSPACK_ERR_READ; | ||
336 | } | ||
337 | |||
338 | /* check for "MSCF" signature */ | ||
339 | if (EndGetI32(&buf[cfhead_Signature]) != 0x4643534D) { | ||
340 | return MSPACK_ERR_SIGNATURE; | ||
341 | } | ||
342 | |||
343 | /* some basic header fields */ | ||
344 | cab->base.length = EndGetI32(&buf[cfhead_CabinetSize]); | ||
345 | cab->base.set_id = EndGetI16(&buf[cfhead_SetID]); | ||
346 | cab->base.set_index = EndGetI16(&buf[cfhead_CabinetIndex]); | ||
347 | |||
348 | /* get the number of folders */ | ||
349 | num_folders = EndGetI16(&buf[cfhead_NumFolders]); | ||
350 | if (num_folders == 0) { | ||
351 | if (!quiet) sys->message(fh, "no folders in cabinet."); | ||
352 | return MSPACK_ERR_DATAFORMAT; | ||
353 | } | ||
354 | |||
355 | /* get the number of files */ | ||
356 | num_files = EndGetI16(&buf[cfhead_NumFiles]); | ||
357 | if (num_files == 0) { | ||
358 | if (!quiet) sys->message(fh, "no files in cabinet."); | ||
359 | return MSPACK_ERR_DATAFORMAT; | ||
360 | } | ||
361 | |||
362 | /* check cabinet version */ | ||
363 | if ((buf[cfhead_MajorVersion] != 1) && (buf[cfhead_MinorVersion] != 3)) { | ||
364 | if (!quiet) sys->message(fh, "WARNING; cabinet version is not 1.3"); | ||
365 | } | ||
366 | |||
367 | /* read the reserved-sizes part of header, if present */ | ||
368 | cab->base.flags = EndGetI16(&buf[cfhead_Flags]); | ||
369 | |||
370 | if (cab->base.flags & cfheadRESERVE_PRESENT) { | ||
371 | if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) { | ||
372 | return MSPACK_ERR_READ; | ||
373 | } | ||
374 | cab->base.header_resv = EndGetI16(&buf[cfheadext_HeaderReserved]); | ||
375 | folder_resv = buf[cfheadext_FolderReserved]; | ||
376 | cab->block_resv = buf[cfheadext_DataReserved]; | ||
377 | |||
378 | if (cab->base.header_resv > 60000) { | ||
379 | if (!quiet) sys->message(fh, "WARNING; reserved header > 60000."); | ||
380 | } | ||
381 | |||
382 | /* skip the reserved header */ | ||
383 | if (cab->base.header_resv) { | ||
384 | if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) { | ||
385 | return MSPACK_ERR_SEEK; | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | else { | ||
390 | cab->base.header_resv = 0; | ||
391 | folder_resv = 0; | ||
392 | cab->block_resv = 0; | ||
393 | } | ||
394 | |||
395 | /* read name and info of preceeding cabinet in set, if present */ | ||
396 | if (cab->base.flags & cfheadPREV_CABINET) { | ||
397 | cab->base.prevname = cabd_read_string(sys, fh, &err); | ||
398 | if (err) return err; | ||
399 | cab->base.previnfo = cabd_read_string(sys, fh, &err); | ||
400 | if (err) return err; | ||
401 | } | ||
402 | |||
403 | /* read name and info of next cabinet in set, if present */ | ||
404 | if (cab->base.flags & cfheadNEXT_CABINET) { | ||
405 | cab->base.nextname = cabd_read_string(sys, fh, &err); | ||
406 | if (err) return err; | ||
407 | cab->base.nextinfo = cabd_read_string(sys, fh, &err); | ||
408 | if (err) return err; | ||
409 | } | ||
410 | |||
411 | /* read folders */ | ||
412 | for (i = 0; i < num_folders; i++) { | ||
413 | if (sys->read(fh, &buf[0], cffold_SIZEOF) != cffold_SIZEOF) { | ||
414 | return MSPACK_ERR_READ; | ||
415 | } | ||
416 | if (folder_resv) { | ||
417 | if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) { | ||
418 | return MSPACK_ERR_SEEK; | ||
419 | } | ||
420 | } | ||
421 | |||
422 | if (!(fol = (struct mscabd_folder_p *) sys->alloc(sys, sizeof(struct mscabd_folder_p)))) { | ||
423 | return MSPACK_ERR_NOMEMORY; | ||
424 | } | ||
425 | fol->base.next = NULL; | ||
426 | fol->base.comp_type = EndGetI16(&buf[cffold_CompType]); | ||
427 | fol->base.num_blocks = EndGetI16(&buf[cffold_NumBlocks]); | ||
428 | fol->data.next = NULL; | ||
429 | fol->data.cab = (struct mscabd_cabinet_p *) cab; | ||
430 | fol->data.offset = offset + (off_t) | ||
431 | ( (unsigned int) EndGetI32(&buf[cffold_DataOffset]) ); | ||
432 | fol->merge_prev = NULL; | ||
433 | fol->merge_next = NULL; | ||
434 | |||
435 | /* link folder into list of folders */ | ||
436 | if (!linkfol) cab->base.folders = (struct mscabd_folder *) fol; | ||
437 | else linkfol->base.next = (struct mscabd_folder *) fol; | ||
438 | linkfol = fol; | ||
439 | } | ||
440 | |||
441 | /* read files */ | ||
442 | for (i = 0; i < num_files; i++) { | ||
443 | if (sys->read(fh, &buf[0], cffile_SIZEOF) != cffile_SIZEOF) { | ||
444 | return MSPACK_ERR_READ; | ||
445 | } | ||
446 | |||
447 | if (!(file = (struct mscabd_file *) sys->alloc(sys, sizeof(struct mscabd_file)))) { | ||
448 | return MSPACK_ERR_NOMEMORY; | ||
449 | } | ||
450 | |||
451 | file->next = NULL; | ||
452 | file->length = EndGetI32(&buf[cffile_UncompressedSize]); | ||
453 | file->attribs = EndGetI16(&buf[cffile_Attribs]); | ||
454 | file->offset = EndGetI32(&buf[cffile_FolderOffset]); | ||
455 | |||
456 | /* set folder pointer */ | ||
457 | fidx = EndGetI16(&buf[cffile_FolderIndex]); | ||
458 | if (fidx < cffileCONTINUED_FROM_PREV) { | ||
459 | /* normal folder index; count up to the correct folder */ | ||
460 | if (fidx < num_folders) { | ||
461 | struct mscabd_folder *ifol = cab->base.folders; | ||
462 | while (fidx--) if (ifol) ifol = ifol->next; | ||
463 | file->folder = ifol; | ||
464 | } | ||
465 | else { | ||
466 | D(("invalid folder index")) | ||
467 | file->folder = NULL; | ||
468 | } | ||
469 | } | ||
470 | else { | ||
471 | /* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or | ||
472 | * CONTINUED_PREV_AND_NEXT */ | ||
473 | if ((fidx == cffileCONTINUED_TO_NEXT) || | ||
474 | (fidx == cffileCONTINUED_PREV_AND_NEXT)) | ||
475 | { | ||
476 | /* get last folder */ | ||
477 | struct mscabd_folder *ifol = cab->base.folders; | ||
478 | while (ifol->next) ifol = ifol->next; | ||
479 | file->folder = ifol; | ||
480 | |||
481 | /* set "merge next" pointer */ | ||
482 | fol = (struct mscabd_folder_p *) ifol; | ||
483 | if (!fol->merge_next) fol->merge_next = file; | ||
484 | } | ||
485 | |||
486 | if ((fidx == cffileCONTINUED_FROM_PREV) || | ||
487 | (fidx == cffileCONTINUED_PREV_AND_NEXT)) | ||
488 | { | ||
489 | /* get first folder */ | ||
490 | file->folder = cab->base.folders; | ||
491 | |||
492 | /* set "merge prev" pointer */ | ||
493 | fol = (struct mscabd_folder_p *) file->folder; | ||
494 | if (!fol->merge_prev) fol->merge_prev = file; | ||
495 | } | ||
496 | } | ||
497 | |||
498 | /* get time */ | ||
499 | x = EndGetI16(&buf[cffile_Time]); | ||
500 | file->time_h = x >> 11; | ||
501 | file->time_m = (x >> 5) & 0x3F; | ||
502 | file->time_s = (x << 1) & 0x3E; | ||
503 | |||
504 | /* get date */ | ||
505 | x = EndGetI16(&buf[cffile_Date]); | ||
506 | file->date_d = x & 0x1F; | ||
507 | file->date_m = (x >> 5) & 0xF; | ||
508 | file->date_y = (x >> 9) + 1980; | ||
509 | |||
510 | /* get filename */ | ||
511 | file->filename = cabd_read_string(sys, fh, &err); | ||
512 | |||
513 | /* if folder index or filename are bad, either skip it or fail */ | ||
514 | if (err || !file->folder) { | ||
515 | sys->free(file->filename); | ||
516 | sys->free(file); | ||
517 | if (salvage) continue; | ||
518 | return err ? err : MSPACK_ERR_DATAFORMAT; | ||
519 | } | ||
520 | |||
521 | /* link file entry into file list */ | ||
522 | if (!linkfile) cab->base.files = file; | ||
523 | else linkfile->next = file; | ||
524 | linkfile = file; | ||
525 | } | ||
526 | |||
527 | if (cab->base.files == NULL) { | ||
528 | /* We never actually added any files to the file list. Something went wrong. | ||
529 | * The file header may have been invalid */ | ||
530 | D(("No files found, even though header claimed to have %d files", num_files)) | ||
531 | return MSPACK_ERR_DATAFORMAT; | ||
532 | } | ||
533 | |||
534 | return MSPACK_ERR_OK; | ||
535 | } | ||
536 | |||
537 | static char *cabd_read_string(struct mspack_system *sys, | ||
538 | struct mspack_file *fh, int *error) | ||
539 | { | ||
540 | off_t base = sys->tell(fh); | ||
541 | char buf[256], *str; | ||
542 | int len, i, ok; | ||
543 | |||
544 | /* read up to 256 bytes */ | ||
545 | if ((len = sys->read(fh, &buf[0], 256)) <= 0) { | ||
546 | *error = MSPACK_ERR_READ; | ||
547 | return NULL; | ||
548 | } | ||
549 | |||
550 | /* search for a null terminator in the buffer */ | ||
551 | for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; } | ||
552 | /* reject empty strings */ | ||
553 | if (i == 0) ok = 0; | ||
554 | |||
555 | if (!ok) { | ||
556 | *error = MSPACK_ERR_DATAFORMAT; | ||
557 | return NULL; | ||
558 | } | ||
559 | |||
560 | len = i + 1; | ||
561 | |||
562 | /* set the data stream to just after the string and return */ | ||
563 | if (sys->seek(fh, base + (off_t)len, MSPACK_SYS_SEEK_START)) { | ||
564 | *error = MSPACK_ERR_SEEK; | ||
565 | return NULL; | ||
566 | } | ||
567 | |||
568 | if (!(str = (char *) sys->alloc(sys, len))) { | ||
569 | *error = MSPACK_ERR_NOMEMORY; | ||
570 | return NULL; | ||
571 | } | ||
572 | |||
573 | sys->copy(&buf[0], str, len); | ||
574 | *error = MSPACK_ERR_OK; | ||
575 | return str; | ||
576 | } | ||
577 | |||
578 | /*************************************** | ||
579 | * CABD_SEARCH, CABD_FIND | ||
580 | *************************************** | ||
581 | * cabd_search opens a file, finds its extent, allocates a search buffer, | ||
582 | * then reads through the whole file looking for possible cabinet headers. | ||
583 | * if it finds any, it tries to read them as real cabinets. returns a linked | ||
584 | * list of results | ||
585 | * | ||
586 | * cabd_find is the inner loop of cabd_search, to make it easier to | ||
587 | * break out of the loop and be sure that all resources are freed | ||
588 | */ | ||
589 | static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, | ||
590 | const char *filename) | ||
591 | { | ||
592 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
593 | struct mscabd_cabinet_p *cab = NULL; | ||
594 | struct mspack_system *sys; | ||
595 | unsigned char *search_buf; | ||
596 | struct mspack_file *fh; | ||
597 | off_t filelen, firstlen = 0; | ||
598 | |||
599 | if (!base) return NULL; | ||
600 | sys = self->system; | ||
601 | |||
602 | /* allocate a search buffer */ | ||
603 | search_buf = (unsigned char *) sys->alloc(sys, (size_t) self->searchbuf_size); | ||
604 | if (!search_buf) { | ||
605 | self->error = MSPACK_ERR_NOMEMORY; | ||
606 | return NULL; | ||
607 | } | ||
608 | |||
609 | /* open file and get its full file length */ | ||
610 | if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { | ||
611 | if (!(self->error = mspack_sys_filelen(sys, fh, &filelen))) { | ||
612 | self->error = cabd_find(self, search_buf, fh, filename, | ||
613 | filelen, &firstlen, &cab); | ||
614 | } | ||
615 | |||
616 | /* truncated / extraneous data warning: */ | ||
617 | if (firstlen && (firstlen != filelen) && | ||
618 | (!cab || (cab->base.base_offset == 0))) | ||
619 | { | ||
620 | if (firstlen < filelen) { | ||
621 | sys->message(fh, "WARNING; possible %" LD | ||
622 | " extra bytes at end of file.", | ||
623 | filelen - firstlen); | ||
624 | } | ||
625 | else { | ||
626 | sys->message(fh, "WARNING; file possibly truncated by %" LD " bytes.", | ||
627 | firstlen - filelen); | ||
628 | } | ||
629 | } | ||
630 | |||
631 | sys->close(fh); | ||
632 | } | ||
633 | else { | ||
634 | self->error = MSPACK_ERR_OPEN; | ||
635 | } | ||
636 | |||
637 | /* free the search buffer */ | ||
638 | sys->free(search_buf); | ||
639 | |||
640 | return (struct mscabd_cabinet *) cab; | ||
641 | } | ||
642 | |||
643 | static int cabd_find(struct mscab_decompressor_p *self, unsigned char *buf, | ||
644 | struct mspack_file *fh, const char *filename, off_t flen, | ||
645 | off_t *firstlen, struct mscabd_cabinet_p **firstcab) | ||
646 | { | ||
647 | struct mscabd_cabinet_p *cab, *link = NULL; | ||
648 | off_t caboff, offset, length; | ||
649 | struct mspack_system *sys = self->system; | ||
650 | unsigned char *p, *pend, state = 0; | ||
651 | unsigned int cablen_u32 = 0, foffset_u32 = 0; | ||
652 | int false_cabs = 0; | ||
653 | |||
654 | #if !LARGEFILE_SUPPORT | ||
655 | /* detect 32-bit off_t overflow */ | ||
656 | if (flen < 0) { | ||
657 | sys->message(fh, largefile_msg); | ||
658 | return MSPACK_ERR_OK; | ||
659 | } | ||
660 | #endif | ||
661 | |||
662 | /* search through the full file length */ | ||
663 | for (offset = 0; offset < flen; offset += length) { | ||
664 | /* search length is either the full length of the search buffer, or the | ||
665 | * amount of data remaining to the end of the file, whichever is less. */ | ||
666 | length = flen - offset; | ||
667 | if (length > self->searchbuf_size) { | ||
668 | length = self->searchbuf_size; | ||
669 | } | ||
670 | |||
671 | /* fill the search buffer with data from disk */ | ||
672 | if (sys->read(fh, &buf[0], (int) length) != (int) length) { | ||
673 | return MSPACK_ERR_READ; | ||
674 | } | ||
675 | |||
676 | /* FAQ avoidance strategy */ | ||
677 | if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) { | ||
678 | sys->message(fh, "WARNING; found InstallShield header. Use unshield " | ||
679 | "(https://github.com/twogood/unshield) to unpack this file"); | ||
680 | } | ||
681 | |||
682 | /* read through the entire buffer. */ | ||
683 | for (p = &buf[0], pend = &buf[length]; p < pend; ) { | ||
684 | switch (state) { | ||
685 | /* starting state */ | ||
686 | case 0: | ||
687 | /* we spend most of our time in this while loop, looking for | ||
688 | * a leading 'M' of the 'MSCF' signature */ | ||
689 | while (p < pend && *p != 0x4D) p++; | ||
690 | /* if we found tht 'M', advance state */ | ||
691 | if (p++ < pend) state = 1; | ||
692 | break; | ||
693 | |||
694 | /* verify that the next 3 bytes are 'S', 'C' and 'F' */ | ||
695 | case 1: state = (*p++ == 0x53) ? 2 : 0; break; | ||
696 | case 2: state = (*p++ == 0x43) ? 3 : 0; break; | ||
697 | case 3: state = (*p++ == 0x46) ? 4 : 0; break; | ||
698 | |||
699 | /* we don't care about bytes 4-7 (see default: for action) */ | ||
700 | |||
701 | /* bytes 8-11 are the overall length of the cabinet */ | ||
702 | case 8: cablen_u32 = *p++; state++; break; | ||
703 | case 9: cablen_u32 |= *p++ << 8; state++; break; | ||
704 | case 10: cablen_u32 |= *p++ << 16; state++; break; | ||
705 | case 11: cablen_u32 |= *p++ << 24; state++; break; | ||
706 | |||
707 | /* we don't care about bytes 12-15 (see default: for action) */ | ||
708 | |||
709 | /* bytes 16-19 are the offset within the cabinet of the filedata */ | ||
710 | case 16: foffset_u32 = *p++; state++; break; | ||
711 | case 17: foffset_u32 |= *p++ << 8; state++; break; | ||
712 | case 18: foffset_u32 |= *p++ << 16; state++; break; | ||
713 | case 19: foffset_u32 |= *p++ << 24; | ||
714 | /* now we have recieved 20 bytes of potential cab header. work out | ||
715 | * the offset in the file of this potential cabinet */ | ||
716 | caboff = offset + (p - &buf[0]) - 20; | ||
717 | |||
718 | /* should reading cabinet fail, restart search just after 'MSCF' */ | ||
719 | offset = caboff + 4; | ||
720 | |||
721 | /* capture the "length of cabinet" field if there is a cabinet at | ||
722 | * offset 0 in the file, regardless of whether the cabinet can be | ||
723 | * read correctly or not */ | ||
724 | if (caboff == 0) *firstlen = (off_t) cablen_u32; | ||
725 | |||
726 | /* check that the files offset is less than the alleged length of | ||
727 | * the cabinet, and that the offset + the alleged length are | ||
728 | * 'roughly' within the end of overall file length. In salvage | ||
729 | * mode, don't check the alleged length, allow it to be garbage */ | ||
730 | if ((foffset_u32 < cablen_u32) && | ||
731 | ((caboff + (off_t) foffset_u32) < (flen + 32)) && | ||
732 | (((caboff + (off_t) cablen_u32) < (flen + 32)) || self->salvage)) | ||
733 | { | ||
734 | /* likely cabinet found -- try reading it */ | ||
735 | if (!(cab = (struct mscabd_cabinet_p *) sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { | ||
736 | return MSPACK_ERR_NOMEMORY; | ||
737 | } | ||
738 | cab->base.filename = filename; | ||
739 | if (cabd_read_headers(sys, fh, cab, caboff, self->salvage, 1)) { | ||
740 | /* destroy the failed cabinet */ | ||
741 | cabd_close((struct mscab_decompressor *) self, | ||
742 | (struct mscabd_cabinet *) cab); | ||
743 | false_cabs++; | ||
744 | } | ||
745 | else { | ||
746 | /* cabinet read correctly! */ | ||
747 | |||
748 | /* link the cab into the list */ | ||
749 | if (!link) *firstcab = cab; | ||
750 | else link->base.next = (struct mscabd_cabinet *) cab; | ||
751 | link = cab; | ||
752 | |||
753 | /* cause the search to restart after this cab's data. */ | ||
754 | offset = caboff + (off_t) cablen_u32; | ||
755 | |||
756 | #if !LARGEFILE_SUPPORT | ||
757 | /* detect 32-bit off_t overflow */ | ||
758 | if (offset < caboff) { | ||
759 | sys->message(fh, largefile_msg); | ||
760 | return MSPACK_ERR_OK; | ||
761 | } | ||
762 | #endif | ||
763 | } | ||
764 | } | ||
765 | |||
766 | /* restart search */ | ||
767 | if (offset >= flen) return MSPACK_ERR_OK; | ||
768 | if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { | ||
769 | return MSPACK_ERR_SEEK; | ||
770 | } | ||
771 | length = 0; | ||
772 | p = pend; | ||
773 | state = 0; | ||
774 | break; | ||
775 | |||
776 | /* for bytes 4-7 and 12-15, just advance state/pointer */ | ||
777 | default: | ||
778 | p++, state++; | ||
779 | } /* switch(state) */ | ||
780 | } /* for (... p < pend ...) */ | ||
781 | } /* for (... offset < length ...) */ | ||
782 | |||
783 | if (false_cabs) { | ||
784 | D(("%d false cabinets found", false_cabs)) | ||
785 | } | ||
786 | |||
787 | return MSPACK_ERR_OK; | ||
788 | } | ||
789 | |||
790 | /*************************************** | ||
791 | * CABD_MERGE, CABD_PREPEND, CABD_APPEND | ||
792 | *************************************** | ||
793 | * joins cabinets together, also merges split folders between these two | ||
794 | * cabinets only. This includes freeing the duplicate folder and file(s) | ||
795 | * and allocating a further mscabd_folder_data structure to append to the | ||
796 | * merged folder's data parts list. | ||
797 | */ | ||
798 | static int cabd_prepend(struct mscab_decompressor *base, | ||
799 | struct mscabd_cabinet *cab, | ||
800 | struct mscabd_cabinet *prevcab) | ||
801 | { | ||
802 | return cabd_merge(base, prevcab, cab); | ||
803 | } | ||
804 | |||
805 | static int cabd_append(struct mscab_decompressor *base, | ||
806 | struct mscabd_cabinet *cab, | ||
807 | struct mscabd_cabinet *nextcab) | ||
808 | { | ||
809 | return cabd_merge(base, cab, nextcab); | ||
810 | } | ||
811 | |||
812 | static int cabd_merge(struct mscab_decompressor *base, | ||
813 | struct mscabd_cabinet *lcab, | ||
814 | struct mscabd_cabinet *rcab) | ||
815 | { | ||
816 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
817 | struct mscabd_folder_data *data, *ndata; | ||
818 | struct mscabd_folder_p *lfol, *rfol; | ||
819 | struct mscabd_file *fi, *rfi, *lfi; | ||
820 | struct mscabd_cabinet *cab; | ||
821 | struct mspack_system *sys; | ||
822 | |||
823 | if (!self) return MSPACK_ERR_ARGS; | ||
824 | sys = self->system; | ||
825 | |||
826 | /* basic args check */ | ||
827 | if (!lcab || !rcab || (lcab == rcab)) { | ||
828 | D(("lcab NULL, rcab NULL or lcab = rcab")) | ||
829 | return self->error = MSPACK_ERR_ARGS; | ||
830 | } | ||
831 | |||
832 | /* check there's not already a cabinet attached */ | ||
833 | if (lcab->nextcab || rcab->prevcab) { | ||
834 | D(("cabs already joined")) | ||
835 | return self->error = MSPACK_ERR_ARGS; | ||
836 | } | ||
837 | |||
838 | /* do not create circular cabinet chains */ | ||
839 | for (cab = lcab->prevcab; cab; cab = cab->prevcab) { | ||
840 | if (cab == rcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} | ||
841 | } | ||
842 | for (cab = rcab->nextcab; cab; cab = cab->nextcab) { | ||
843 | if (cab == lcab) {D(("circular!")) return self->error = MSPACK_ERR_ARGS;} | ||
844 | } | ||
845 | |||
846 | /* warn about odd set IDs or indices */ | ||
847 | if (lcab->set_id != rcab->set_id) { | ||
848 | sys->message(NULL, "WARNING; merged cabinets with differing Set IDs."); | ||
849 | } | ||
850 | |||
851 | if (lcab->set_index > rcab->set_index) { | ||
852 | sys->message(NULL, "WARNING; merged cabinets with odd order."); | ||
853 | } | ||
854 | |||
855 | /* merging the last folder in lcab with the first folder in rcab */ | ||
856 | lfol = (struct mscabd_folder_p *) lcab->folders; | ||
857 | rfol = (struct mscabd_folder_p *) rcab->folders; | ||
858 | while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; | ||
859 | |||
860 | /* do we need to merge folders? */ | ||
861 | if (!lfol->merge_next && !rfol->merge_prev) { | ||
862 | /* no, at least one of the folders is not for merging */ | ||
863 | |||
864 | /* attach cabs */ | ||
865 | lcab->nextcab = rcab; | ||
866 | rcab->prevcab = lcab; | ||
867 | |||
868 | /* attach folders */ | ||
869 | lfol->base.next = (struct mscabd_folder *) rfol; | ||
870 | |||
871 | /* attach files */ | ||
872 | fi = lcab->files; | ||
873 | while (fi->next) fi = fi->next; | ||
874 | fi->next = rcab->files; | ||
875 | } | ||
876 | else { | ||
877 | /* folder merge required - do the files match? */ | ||
878 | if (! cabd_can_merge_folders(sys, lfol, rfol)) { | ||
879 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
880 | } | ||
881 | |||
882 | /* allocate a new folder data structure */ | ||
883 | if (!(data = (struct mscabd_folder_data *) sys->alloc(sys, sizeof(struct mscabd_folder_data)))) { | ||
884 | return self->error = MSPACK_ERR_NOMEMORY; | ||
885 | } | ||
886 | |||
887 | /* attach cabs */ | ||
888 | lcab->nextcab = rcab; | ||
889 | rcab->prevcab = lcab; | ||
890 | |||
891 | /* append rfol's data to lfol */ | ||
892 | ndata = &lfol->data; | ||
893 | while (ndata->next) ndata = ndata->next; | ||
894 | ndata->next = data; | ||
895 | *data = rfol->data; | ||
896 | rfol->data.next = NULL; | ||
897 | |||
898 | /* lfol becomes rfol. | ||
899 | * NOTE: special case, don't merge if rfol is merge prev and next, | ||
900 | * rfol->merge_next is going to be deleted, so keep lfol's version | ||
901 | * instead */ | ||
902 | lfol->base.num_blocks += rfol->base.num_blocks - 1; | ||
903 | if ((rfol->merge_next == NULL) || | ||
904 | (rfol->merge_next->folder != (struct mscabd_folder *) rfol)) | ||
905 | { | ||
906 | lfol->merge_next = rfol->merge_next; | ||
907 | } | ||
908 | |||
909 | /* attach the rfol's folder (except the merge folder) */ | ||
910 | while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; | ||
911 | lfol->base.next = rfol->base.next; | ||
912 | |||
913 | /* free disused merge folder */ | ||
914 | sys->free(rfol); | ||
915 | |||
916 | /* attach rfol's files */ | ||
917 | fi = lcab->files; | ||
918 | while (fi->next) fi = fi->next; | ||
919 | fi->next = rcab->files; | ||
920 | |||
921 | /* delete all files from rfol's merge folder */ | ||
922 | lfi = NULL; | ||
923 | for (fi = lcab->files; fi ; fi = rfi) { | ||
924 | rfi = fi->next; | ||
925 | /* if file's folder matches the merge folder, unlink and free it */ | ||
926 | if (fi->folder == (struct mscabd_folder *) rfol) { | ||
927 | if (lfi) lfi->next = rfi; else lcab->files = rfi; | ||
928 | sys->free(fi->filename); | ||
929 | sys->free(fi); | ||
930 | } | ||
931 | else lfi = fi; | ||
932 | } | ||
933 | } | ||
934 | |||
935 | /* all done! fix files and folders pointers in all cabs so they all | ||
936 | * point to the same list */ | ||
937 | for (cab = lcab->prevcab; cab; cab = cab->prevcab) { | ||
938 | cab->files = lcab->files; | ||
939 | cab->folders = lcab->folders; | ||
940 | } | ||
941 | |||
942 | for (cab = lcab->nextcab; cab; cab = cab->nextcab) { | ||
943 | cab->files = lcab->files; | ||
944 | cab->folders = lcab->folders; | ||
945 | } | ||
946 | |||
947 | return self->error = MSPACK_ERR_OK; | ||
948 | } | ||
949 | |||
950 | /* decides if two folders are OK to merge */ | ||
951 | static int cabd_can_merge_folders(struct mspack_system *sys, | ||
952 | struct mscabd_folder_p *lfol, | ||
953 | struct mscabd_folder_p *rfol) | ||
954 | { | ||
955 | struct mscabd_file *lfi, *rfi, *l, *r; | ||
956 | int matching = 1; | ||
957 | |||
958 | /* check that both folders use the same compression method/settings */ | ||
959 | if (lfol->base.comp_type != rfol->base.comp_type) { | ||
960 | D(("folder merge: compression type mismatch")) | ||
961 | return 0; | ||
962 | } | ||
963 | |||
964 | /* check there are not too many data blocks after merging */ | ||
965 | if ((lfol->base.num_blocks + rfol->base.num_blocks) > CAB_FOLDERMAX) { | ||
966 | D(("folder merge: too many data blocks in merged folders")) | ||
967 | return 0; | ||
968 | } | ||
969 | |||
970 | if (!(lfi = lfol->merge_next) || !(rfi = rfol->merge_prev)) { | ||
971 | D(("folder merge: one cabinet has no files to merge")) | ||
972 | return 0; | ||
973 | } | ||
974 | |||
975 | /* for all files in lfol (which is the last folder in whichever cab and | ||
976 | * only has files to merge), compare them to the files from rfol. They | ||
977 | * should be identical in number and order. to verify this, check the | ||
978 | * offset and length of each file. */ | ||
979 | for (l=lfi, r=rfi; l; l=l->next, r=r->next) { | ||
980 | if (!r || (l->offset != r->offset) || (l->length != r->length)) { | ||
981 | matching = 0; | ||
982 | break; | ||
983 | } | ||
984 | } | ||
985 | |||
986 | if (matching) return 1; | ||
987 | |||
988 | /* if rfol does not begin with an identical copy of the files in lfol, make | ||
989 | * make a judgement call; if at least ONE file from lfol is in rfol, allow | ||
990 | * the merge with a warning about missing files. */ | ||
991 | matching = 0; | ||
992 | for (l = lfi; l; l = l->next) { | ||
993 | for (r = rfi; r; r = r->next) { | ||
994 | if (l->offset == r->offset && l->length == r->length) break; | ||
995 | } | ||
996 | if (r) matching = 1; else sys->message(NULL, | ||
997 | "WARNING; merged file %s not listed in both cabinets", l->filename); | ||
998 | } | ||
999 | return matching; | ||
1000 | } | ||
1001 | |||
1002 | |||
1003 | /*************************************** | ||
1004 | * CABD_EXTRACT | ||
1005 | *************************************** | ||
1006 | * extracts a file from a cabinet | ||
1007 | */ | ||
1008 | static int cabd_extract(struct mscab_decompressor *base, | ||
1009 | struct mscabd_file *file, const char *filename) | ||
1010 | { | ||
1011 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
1012 | struct mscabd_folder_p *fol; | ||
1013 | struct mspack_system *sys; | ||
1014 | struct mspack_file *fh; | ||
1015 | off_t filelen; | ||
1016 | |||
1017 | if (!self) return MSPACK_ERR_ARGS; | ||
1018 | if (!file) return self->error = MSPACK_ERR_ARGS; | ||
1019 | |||
1020 | sys = self->system; | ||
1021 | fol = (struct mscabd_folder_p *) file->folder; | ||
1022 | |||
1023 | /* if offset is beyond 2GB, nothing can be extracted */ | ||
1024 | if (file->offset > CAB_LENGTHMAX) { | ||
1025 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1026 | } | ||
1027 | |||
1028 | /* if file claims to go beyond 2GB either error out, | ||
1029 | * or in salvage mode reduce file length so it fits 2GB limit | ||
1030 | */ | ||
1031 | filelen = file->length; | ||
1032 | if (filelen > CAB_LENGTHMAX || (file->offset + filelen) > CAB_LENGTHMAX) { | ||
1033 | if (self->salvage) { | ||
1034 | filelen = CAB_LENGTHMAX - file->offset; | ||
1035 | } | ||
1036 | else { | ||
1037 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1038 | } | ||
1039 | } | ||
1040 | |||
1041 | /* extraction impossible if no folder, or folder needs predecessor */ | ||
1042 | if (!fol || fol->merge_prev) { | ||
1043 | sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " | ||
1044 | "cabinet set is incomplete", file->filename); | ||
1045 | return self->error = MSPACK_ERR_DECRUNCH; | ||
1046 | } | ||
1047 | |||
1048 | /* if file goes beyond what can be decoded, given an error. | ||
1049 | * In salvage mode, don't assume block sizes, just try decoding | ||
1050 | */ | ||
1051 | if (!self->salvage) { | ||
1052 | off_t maxlen = fol->base.num_blocks * CAB_BLOCKMAX; | ||
1053 | if ((file->offset + filelen) > maxlen) { | ||
1054 | sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " | ||
1055 | "cabinet set is incomplete", file->filename); | ||
1056 | return self->error = MSPACK_ERR_DECRUNCH; | ||
1057 | } | ||
1058 | } | ||
1059 | |||
1060 | /* allocate generic decompression state */ | ||
1061 | if (!self->d) { | ||
1062 | self->d = (struct mscabd_decompress_state *) sys->alloc(sys, sizeof(struct mscabd_decompress_state)); | ||
1063 | if (!self->d) return self->error = MSPACK_ERR_NOMEMORY; | ||
1064 | self->d->folder = NULL; | ||
1065 | self->d->data = NULL; | ||
1066 | self->d->sys = *sys; | ||
1067 | self->d->sys.read = &cabd_sys_read; | ||
1068 | self->d->sys.write = &cabd_sys_write; | ||
1069 | self->d->state = NULL; | ||
1070 | self->d->infh = NULL; | ||
1071 | self->d->incab = NULL; | ||
1072 | } | ||
1073 | |||
1074 | /* do we need to change folder or reset the current folder? */ | ||
1075 | if ((self->d->folder != fol) || (self->d->offset > file->offset) || | ||
1076 | !self->d->state) | ||
1077 | { | ||
1078 | /* free any existing decompressor */ | ||
1079 | cabd_free_decomp(self); | ||
1080 | |||
1081 | /* do we need to open a new cab file? */ | ||
1082 | if (!self->d->infh || (fol->data.cab != self->d->incab)) { | ||
1083 | /* close previous file handle if from a different cab */ | ||
1084 | if (self->d->infh) sys->close(self->d->infh); | ||
1085 | self->d->incab = fol->data.cab; | ||
1086 | self->d->infh = sys->open(sys, fol->data.cab->base.filename, | ||
1087 | MSPACK_SYS_OPEN_READ); | ||
1088 | if (!self->d->infh) return self->error = MSPACK_ERR_OPEN; | ||
1089 | } | ||
1090 | /* seek to start of data blocks */ | ||
1091 | if (sys->seek(self->d->infh, fol->data.offset, MSPACK_SYS_SEEK_START)) { | ||
1092 | return self->error = MSPACK_ERR_SEEK; | ||
1093 | } | ||
1094 | |||
1095 | /* set up decompressor */ | ||
1096 | if (cabd_init_decomp(self, (unsigned int) fol->base.comp_type)) { | ||
1097 | return self->error; | ||
1098 | } | ||
1099 | |||
1100 | /* initialise new folder state */ | ||
1101 | self->d->folder = fol; | ||
1102 | self->d->data = &fol->data; | ||
1103 | self->d->offset = 0; | ||
1104 | self->d->block = 0; | ||
1105 | self->d->outlen = 0; | ||
1106 | self->d->i_ptr = self->d->i_end = &self->d->input[0]; | ||
1107 | |||
1108 | /* read_error lasts for the lifetime of a decompressor */ | ||
1109 | self->read_error = MSPACK_ERR_OK; | ||
1110 | } | ||
1111 | |||
1112 | /* open file for output */ | ||
1113 | if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { | ||
1114 | return self->error = MSPACK_ERR_OPEN; | ||
1115 | } | ||
1116 | |||
1117 | self->error = MSPACK_ERR_OK; | ||
1118 | |||
1119 | /* if file has more than 0 bytes */ | ||
1120 | if (filelen) { | ||
1121 | off_t bytes; | ||
1122 | int error; | ||
1123 | /* get to correct offset. | ||
1124 | * - use NULL fh to say 'no writing' to cabd_sys_write() | ||
1125 | * - if cabd_sys_read() has an error, it will set self->read_error | ||
1126 | * and pass back MSPACK_ERR_READ | ||
1127 | */ | ||
1128 | self->d->outfh = NULL; | ||
1129 | if ((bytes = file->offset - self->d->offset)) { | ||
1130 | error = self->d->decompress(self->d->state, bytes); | ||
1131 | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; | ||
1132 | } | ||
1133 | |||
1134 | /* if getting to the correct offset was error free, unpack file */ | ||
1135 | if (!self->error) { | ||
1136 | self->d->outfh = fh; | ||
1137 | error = self->d->decompress(self->d->state, filelen); | ||
1138 | self->error = (error == MSPACK_ERR_READ) ? self->read_error : error; | ||
1139 | } | ||
1140 | } | ||
1141 | |||
1142 | /* close output file */ | ||
1143 | sys->close(fh); | ||
1144 | self->d->outfh = NULL; | ||
1145 | |||
1146 | return self->error; | ||
1147 | } | ||
1148 | |||
1149 | /*************************************** | ||
1150 | * CABD_INIT_DECOMP, CABD_FREE_DECOMP | ||
1151 | *************************************** | ||
1152 | * cabd_init_decomp initialises decompression state, according to which | ||
1153 | * decompression method was used. relies on self->d->folder being the same | ||
1154 | * as when initialised. | ||
1155 | * | ||
1156 | * cabd_free_decomp frees decompression state, according to which method | ||
1157 | * was used. | ||
1158 | */ | ||
1159 | static int cabd_init_decomp(struct mscab_decompressor_p *self, unsigned int ct) | ||
1160 | { | ||
1161 | struct mspack_file *fh = (struct mspack_file *) self; | ||
1162 | |||
1163 | self->d->comp_type = ct; | ||
1164 | |||
1165 | switch (ct & cffoldCOMPTYPE_MASK) { | ||
1166 | case cffoldCOMPTYPE_NONE: | ||
1167 | self->d->decompress = (int (*)(void *, off_t)) &noned_decompress; | ||
1168 | self->d->state = noned_init(&self->d->sys, fh, fh, self->buf_size); | ||
1169 | break; | ||
1170 | case cffoldCOMPTYPE_MSZIP: | ||
1171 | self->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress; | ||
1172 | self->d->state = mszipd_init(&self->d->sys, fh, fh, self->buf_size, | ||
1173 | self->fix_mszip); | ||
1174 | break; | ||
1175 | case cffoldCOMPTYPE_QUANTUM: | ||
1176 | self->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress; | ||
1177 | self->d->state = qtmd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, | ||
1178 | self->buf_size); | ||
1179 | break; | ||
1180 | case cffoldCOMPTYPE_LZX: | ||
1181 | self->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress; | ||
1182 | self->d->state = lzxd_init(&self->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0, | ||
1183 | self->buf_size, (off_t)0,0); | ||
1184 | break; | ||
1185 | default: | ||
1186 | return self->error = MSPACK_ERR_DATAFORMAT; | ||
1187 | } | ||
1188 | return self->error = (self->d->state) ? MSPACK_ERR_OK : MSPACK_ERR_NOMEMORY; | ||
1189 | } | ||
1190 | |||
1191 | static void cabd_free_decomp(struct mscab_decompressor_p *self) { | ||
1192 | if (!self || !self->d || !self->d->state) return; | ||
1193 | |||
1194 | switch (self->d->comp_type & cffoldCOMPTYPE_MASK) { | ||
1195 | case cffoldCOMPTYPE_NONE: noned_free((struct noned_state *) self->d->state); break; | ||
1196 | case cffoldCOMPTYPE_MSZIP: mszipd_free((struct mszipd_stream *) self->d->state); break; | ||
1197 | case cffoldCOMPTYPE_QUANTUM: qtmd_free((struct qtmd_stream *) self->d->state); break; | ||
1198 | case cffoldCOMPTYPE_LZX: lzxd_free((struct lzxd_stream *) self->d->state); break; | ||
1199 | } | ||
1200 | self->d->decompress = NULL; | ||
1201 | self->d->state = NULL; | ||
1202 | } | ||
1203 | |||
1204 | /*************************************** | ||
1205 | * CABD_SYS_READ, CABD_SYS_WRITE | ||
1206 | *************************************** | ||
1207 | * cabd_sys_read is the internal reader function which the decompressors | ||
1208 | * use. will read data blocks (and merge split blocks) from the cabinet | ||
1209 | * and serve the read bytes to the decompressors | ||
1210 | * | ||
1211 | * cabd_sys_write is the internal writer function which the decompressors | ||
1212 | * use. it either writes data to disk (self->d->outfh) with the real | ||
1213 | * sys->write() function, or does nothing with the data when | ||
1214 | * self->d->outfh == NULL. advances self->d->offset | ||
1215 | */ | ||
1216 | static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { | ||
1217 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; | ||
1218 | unsigned char *buf = (unsigned char *) buffer; | ||
1219 | struct mspack_system *sys = self->system; | ||
1220 | int avail, todo, outlen, ignore_cksum, ignore_blocksize; | ||
1221 | |||
1222 | ignore_cksum = self->salvage || | ||
1223 | (self->fix_mszip && | ||
1224 | ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP)); | ||
1225 | ignore_blocksize = self->salvage; | ||
1226 | |||
1227 | todo = bytes; | ||
1228 | while (todo > 0) { | ||
1229 | avail = self->d->i_end - self->d->i_ptr; | ||
1230 | |||
1231 | /* if out of input data, read a new block */ | ||
1232 | if (avail) { | ||
1233 | /* copy as many input bytes available as possible */ | ||
1234 | if (avail > todo) avail = todo; | ||
1235 | sys->copy(self->d->i_ptr, buf, (size_t) avail); | ||
1236 | self->d->i_ptr += avail; | ||
1237 | buf += avail; | ||
1238 | todo -= avail; | ||
1239 | } | ||
1240 | else { | ||
1241 | /* out of data, read a new block */ | ||
1242 | |||
1243 | /* check if we're out of input blocks, advance block counter */ | ||
1244 | if (self->d->block++ >= self->d->folder->base.num_blocks) { | ||
1245 | if (!self->salvage) { | ||
1246 | self->read_error = MSPACK_ERR_DATAFORMAT; | ||
1247 | } | ||
1248 | else { | ||
1249 | D(("Ran out of CAB input blocks prematurely")) | ||
1250 | } | ||
1251 | break; | ||
1252 | } | ||
1253 | |||
1254 | /* read a block */ | ||
1255 | self->read_error = cabd_sys_read_block(sys, self->d, &outlen, | ||
1256 | ignore_cksum, ignore_blocksize); | ||
1257 | if (self->read_error) return -1; | ||
1258 | self->d->outlen += outlen; | ||
1259 | |||
1260 | /* special Quantum hack -- trailer byte to allow the decompressor | ||
1261 | * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have | ||
1262 | * anything from 0 to 4 trailing null bytes. */ | ||
1263 | if ((self->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) { | ||
1264 | *self->d->i_end++ = 0xFF; | ||
1265 | } | ||
1266 | |||
1267 | /* is this the last block? */ | ||
1268 | if (self->d->block >= self->d->folder->base.num_blocks) { | ||
1269 | if ((self->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) { | ||
1270 | /* special LZX hack -- on the last block, inform LZX of the | ||
1271 | * size of the output data stream. */ | ||
1272 | lzxd_set_output_length((struct lzxd_stream *) self->d->state, self->d->outlen); | ||
1273 | } | ||
1274 | } | ||
1275 | } /* if (avail) */ | ||
1276 | } /* while (todo > 0) */ | ||
1277 | return bytes - todo; | ||
1278 | } | ||
1279 | |||
1280 | static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) { | ||
1281 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) file; | ||
1282 | self->d->offset += bytes; | ||
1283 | if (self->d->outfh) { | ||
1284 | return self->system->write(self->d->outfh, buffer, bytes); | ||
1285 | } | ||
1286 | return bytes; | ||
1287 | } | ||
1288 | |||
1289 | /*************************************** | ||
1290 | * CABD_SYS_READ_BLOCK | ||
1291 | *************************************** | ||
1292 | * reads a whole data block from a cab file. the block may span more than | ||
1293 | * one cab file, if it does then the fragments will be reassembled | ||
1294 | */ | ||
1295 | static int cabd_sys_read_block(struct mspack_system *sys, | ||
1296 | struct mscabd_decompress_state *d, | ||
1297 | int *out, int ignore_cksum, | ||
1298 | int ignore_blocksize) | ||
1299 | { | ||
1300 | unsigned char hdr[cfdata_SIZEOF]; | ||
1301 | unsigned int cksum; | ||
1302 | int len, full_len; | ||
1303 | |||
1304 | /* reset the input block pointer and end of block pointer */ | ||
1305 | d->i_ptr = d->i_end = &d->input[0]; | ||
1306 | |||
1307 | do { | ||
1308 | /* read the block header */ | ||
1309 | if (sys->read(d->infh, &hdr[0], cfdata_SIZEOF) != cfdata_SIZEOF) { | ||
1310 | return MSPACK_ERR_READ; | ||
1311 | } | ||
1312 | |||
1313 | /* skip any reserved block headers */ | ||
1314 | if (d->data->cab->block_resv && | ||
1315 | sys->seek(d->infh, (off_t) d->data->cab->block_resv, | ||
1316 | MSPACK_SYS_SEEK_CUR)) | ||
1317 | { | ||
1318 | return MSPACK_ERR_SEEK; | ||
1319 | } | ||
1320 | |||
1321 | /* blocks must not be over CAB_INPUTMAX in size */ | ||
1322 | len = EndGetI16(&hdr[cfdata_CompressedSize]); | ||
1323 | full_len = (d->i_end - d->i_ptr) + len; /* include cab-spanning blocks */ | ||
1324 | if (full_len > CAB_INPUTMAX) { | ||
1325 | D(("block size %d > CAB_INPUTMAX", full_len)); | ||
1326 | /* in salvage mode, blocks can be 65535 bytes but no more than that */ | ||
1327 | if (!ignore_blocksize || full_len > CAB_INPUTMAX_SALVAGE) { | ||
1328 | return MSPACK_ERR_DATAFORMAT; | ||
1329 | } | ||
1330 | } | ||
1331 | |||
1332 | /* blocks must not expand to more than CAB_BLOCKMAX */ | ||
1333 | if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) { | ||
1334 | D(("block size > CAB_BLOCKMAX")) | ||
1335 | if (!ignore_blocksize) return MSPACK_ERR_DATAFORMAT; | ||
1336 | } | ||
1337 | |||
1338 | /* read the block data */ | ||
1339 | if (sys->read(d->infh, d->i_end, len) != len) { | ||
1340 | return MSPACK_ERR_READ; | ||
1341 | } | ||
1342 | |||
1343 | /* perform checksum test on the block (if one is stored) */ | ||
1344 | if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) { | ||
1345 | unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0); | ||
1346 | if (cabd_checksum(&hdr[4], 4, sum2) != cksum) { | ||
1347 | if (!ignore_cksum) return MSPACK_ERR_CHECKSUM; | ||
1348 | sys->message(d->infh, "WARNING; bad block checksum found"); | ||
1349 | } | ||
1350 | } | ||
1351 | |||
1352 | /* advance end of block pointer to include newly read data */ | ||
1353 | d->i_end += len; | ||
1354 | |||
1355 | /* uncompressed size == 0 means this block was part of a split block | ||
1356 | * and it continues as the first block of the next cabinet in the set. | ||
1357 | * otherwise, this is the last part of the block, and no more block | ||
1358 | * reading needs to be done. | ||
1359 | */ | ||
1360 | /* EXIT POINT OF LOOP -- uncompressed size != 0 */ | ||
1361 | if ((*out = EndGetI16(&hdr[cfdata_UncompressedSize]))) { | ||
1362 | return MSPACK_ERR_OK; | ||
1363 | } | ||
1364 | |||
1365 | /* otherwise, advance to next cabinet */ | ||
1366 | |||
1367 | /* close current file handle */ | ||
1368 | sys->close(d->infh); | ||
1369 | d->infh = NULL; | ||
1370 | |||
1371 | /* advance to next member in the cabinet set */ | ||
1372 | if (!(d->data = d->data->next)) { | ||
1373 | sys->message(d->infh, "WARNING; ran out of cabinets in set. Are any missing?"); | ||
1374 | return MSPACK_ERR_DATAFORMAT; | ||
1375 | } | ||
1376 | |||
1377 | /* open next cab file */ | ||
1378 | d->incab = d->data->cab; | ||
1379 | if (!(d->infh = sys->open(sys, d->incab->base.filename, | ||
1380 | MSPACK_SYS_OPEN_READ))) | ||
1381 | { | ||
1382 | return MSPACK_ERR_OPEN; | ||
1383 | } | ||
1384 | |||
1385 | /* seek to start of data blocks */ | ||
1386 | if (sys->seek(d->infh, d->data->offset, MSPACK_SYS_SEEK_START)) { | ||
1387 | return MSPACK_ERR_SEEK; | ||
1388 | } | ||
1389 | } while (1); | ||
1390 | |||
1391 | /* not reached */ | ||
1392 | return MSPACK_ERR_OK; | ||
1393 | } | ||
1394 | |||
1395 | static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, | ||
1396 | unsigned int cksum) | ||
1397 | { | ||
1398 | unsigned int len, ul = 0; | ||
1399 | |||
1400 | for (len = bytes >> 2; len--; data += 4) { | ||
1401 | cksum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24)); | ||
1402 | } | ||
1403 | |||
1404 | switch (bytes & 3) { | ||
1405 | case 3: ul |= *data++ << 16; /*@fallthrough@*/ | ||
1406 | case 2: ul |= *data++ << 8; /*@fallthrough@*/ | ||
1407 | case 1: ul |= *data; | ||
1408 | } | ||
1409 | cksum ^= ul; | ||
1410 | |||
1411 | return cksum; | ||
1412 | } | ||
1413 | |||
1414 | /*************************************** | ||
1415 | * NONED_INIT, NONED_DECOMPRESS, NONED_FREE | ||
1416 | *************************************** | ||
1417 | * the "not compressed" method decompressor | ||
1418 | */ | ||
1419 | struct noned_state { | ||
1420 | struct mspack_system *sys; | ||
1421 | struct mspack_file *i; | ||
1422 | struct mspack_file *o; | ||
1423 | unsigned char *buf; | ||
1424 | int bufsize; | ||
1425 | }; | ||
1426 | |||
1427 | static struct noned_state *noned_init(struct mspack_system *sys, | ||
1428 | struct mspack_file *in, | ||
1429 | struct mspack_file *out, | ||
1430 | int bufsize) | ||
1431 | { | ||
1432 | struct noned_state *state = (struct noned_state *) sys->alloc(sys, sizeof(struct noned_state)); | ||
1433 | unsigned char *buf = (unsigned char *) sys->alloc(sys, (size_t) bufsize); | ||
1434 | if (state && buf) { | ||
1435 | state->sys = sys; | ||
1436 | state->i = in; | ||
1437 | state->o = out; | ||
1438 | state->buf = buf; | ||
1439 | state->bufsize = bufsize; | ||
1440 | } | ||
1441 | else { | ||
1442 | sys->free(buf); | ||
1443 | sys->free(state); | ||
1444 | state = NULL; | ||
1445 | } | ||
1446 | return state; | ||
1447 | } | ||
1448 | |||
1449 | static int noned_decompress(struct noned_state *s, off_t bytes) { | ||
1450 | int run; | ||
1451 | while (bytes > 0) { | ||
1452 | run = (bytes > s->bufsize) ? s->bufsize : (int) bytes; | ||
1453 | if (s->sys->read(s->i, &s->buf[0], run) != run) return MSPACK_ERR_READ; | ||
1454 | if (s->sys->write(s->o, &s->buf[0], run) != run) return MSPACK_ERR_WRITE; | ||
1455 | bytes -= run; | ||
1456 | } | ||
1457 | return MSPACK_ERR_OK; | ||
1458 | } | ||
1459 | |||
1460 | static void noned_free(struct noned_state *state) { | ||
1461 | struct mspack_system *sys; | ||
1462 | if (state) { | ||
1463 | sys = state->sys; | ||
1464 | sys->free(state->buf); | ||
1465 | sys->free(state); | ||
1466 | } | ||
1467 | } | ||
1468 | |||
1469 | |||
1470 | /*************************************** | ||
1471 | * CABD_PARAM | ||
1472 | *************************************** | ||
1473 | * allows a parameter to be set | ||
1474 | */ | ||
1475 | static int cabd_param(struct mscab_decompressor *base, int param, int value) { | ||
1476 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
1477 | if (!self) return MSPACK_ERR_ARGS; | ||
1478 | |||
1479 | switch (param) { | ||
1480 | case MSCABD_PARAM_SEARCHBUF: | ||
1481 | if (value < 4) return MSPACK_ERR_ARGS; | ||
1482 | self->searchbuf_size = value; | ||
1483 | break; | ||
1484 | case MSCABD_PARAM_FIXMSZIP: | ||
1485 | self->fix_mszip = value; | ||
1486 | break; | ||
1487 | case MSCABD_PARAM_DECOMPBUF: | ||
1488 | if (value < 4) return MSPACK_ERR_ARGS; | ||
1489 | self->buf_size = value; | ||
1490 | break; | ||
1491 | case MSCABD_PARAM_SALVAGE: | ||
1492 | self->salvage = value; | ||
1493 | break; | ||
1494 | default: | ||
1495 | return MSPACK_ERR_ARGS; | ||
1496 | } | ||
1497 | return MSPACK_ERR_OK; | ||
1498 | } | ||
1499 | |||
1500 | /*************************************** | ||
1501 | * CABD_ERROR | ||
1502 | *************************************** | ||
1503 | * returns the last error that occurred | ||
1504 | */ | ||
1505 | static int cabd_error(struct mscab_decompressor *base) { | ||
1506 | struct mscab_decompressor_p *self = (struct mscab_decompressor_p *) base; | ||
1507 | return (self) ? self->error : MSPACK_ERR_ARGS; | ||
1508 | } | ||