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