libmspack 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +5 -0
  4. data/.yardopts +1 -0
  5. data/Gemfile +4 -0
  6. data/README.md +75 -0
  7. data/Rakefile +22 -0
  8. data/UNLICENSE +24 -0
  9. data/ext/Rakefile +16 -0
  10. data/ext/i386-windows/libmspack.dll +0 -0
  11. data/ext/libmspack/AUTHORS +12 -0
  12. data/ext/libmspack/COPYING.LIB +504 -0
  13. data/ext/libmspack/ChangeLog +491 -0
  14. data/ext/libmspack/Makefile.am +100 -0
  15. data/ext/libmspack/NEWS +0 -0
  16. data/ext/libmspack/README +130 -0
  17. data/ext/libmspack/TODO +8 -0
  18. data/ext/libmspack/cleanup.sh +9 -0
  19. data/ext/libmspack/configure.ac +50 -0
  20. data/ext/libmspack/debian/changelog +6 -0
  21. data/ext/libmspack/debian/control +14 -0
  22. data/ext/libmspack/debian/rules +101 -0
  23. data/ext/libmspack/doc/Doxyfile.in +22 -0
  24. data/ext/libmspack/doc/Makefile.in +14 -0
  25. data/ext/libmspack/doc/szdd_kwaj_format.html +331 -0
  26. data/ext/libmspack/libmspack.pc.in +10 -0
  27. data/ext/libmspack/mspack/cab.h +127 -0
  28. data/ext/libmspack/mspack/cabc.c +24 -0
  29. data/ext/libmspack/mspack/cabd.c +1444 -0
  30. data/ext/libmspack/mspack/chm.h +122 -0
  31. data/ext/libmspack/mspack/chmc.c +24 -0
  32. data/ext/libmspack/mspack/chmd.c +1392 -0
  33. data/ext/libmspack/mspack/crc32.c +95 -0
  34. data/ext/libmspack/mspack/crc32.h +17 -0
  35. data/ext/libmspack/mspack/des.h +15 -0
  36. data/ext/libmspack/mspack/hlp.h +33 -0
  37. data/ext/libmspack/mspack/hlpc.c +24 -0
  38. data/ext/libmspack/mspack/hlpd.c +24 -0
  39. data/ext/libmspack/mspack/kwaj.h +118 -0
  40. data/ext/libmspack/mspack/kwajc.c +24 -0
  41. data/ext/libmspack/mspack/kwajd.c +561 -0
  42. data/ext/libmspack/mspack/lit.h +35 -0
  43. data/ext/libmspack/mspack/litc.c +24 -0
  44. data/ext/libmspack/mspack/litd.c +24 -0
  45. data/ext/libmspack/mspack/lzss.h +66 -0
  46. data/ext/libmspack/mspack/lzssd.c +93 -0
  47. data/ext/libmspack/mspack/lzx.h +221 -0
  48. data/ext/libmspack/mspack/lzxc.c +18 -0
  49. data/ext/libmspack/mspack/lzxd.c +895 -0
  50. data/ext/libmspack/mspack/mspack.def +28 -0
  51. data/ext/libmspack/mspack/mspack.h +2353 -0
  52. data/ext/libmspack/mspack/mszip.h +126 -0
  53. data/ext/libmspack/mspack/mszipc.c +18 -0
  54. data/ext/libmspack/mspack/mszipd.c +514 -0
  55. data/ext/libmspack/mspack/oab.h +60 -0
  56. data/ext/libmspack/mspack/oabc.c +24 -0
  57. data/ext/libmspack/mspack/oabd.c +408 -0
  58. data/ext/libmspack/mspack/qtm.h +128 -0
  59. data/ext/libmspack/mspack/qtmc.c +18 -0
  60. data/ext/libmspack/mspack/qtmd.c +489 -0
  61. data/ext/libmspack/mspack/readbits.h +207 -0
  62. data/ext/libmspack/mspack/readhuff.h +173 -0
  63. data/ext/libmspack/mspack/sha.h +15 -0
  64. data/ext/libmspack/mspack/system.c +239 -0
  65. data/ext/libmspack/mspack/system.h +124 -0
  66. data/ext/libmspack/mspack/szdd.h +39 -0
  67. data/ext/libmspack/mspack/szddc.c +24 -0
  68. data/ext/libmspack/mspack/szddd.c +247 -0
  69. data/ext/libmspack/rebuild.sh +8 -0
  70. data/ext/libmspack/test/cabd_c10 +19 -0
  71. data/ext/libmspack/test/cabd_compare +34 -0
  72. data/ext/libmspack/test/cabd_md5.c +161 -0
  73. data/ext/libmspack/test/cabd_memory.c +179 -0
  74. data/ext/libmspack/test/cabd_test.c +386 -0
  75. data/ext/libmspack/test/cabrip.c +81 -0
  76. data/ext/libmspack/test/chmd_compare +38 -0
  77. data/ext/libmspack/test/chmd_find.c +95 -0
  78. data/ext/libmspack/test/chmd_md5.c +67 -0
  79. data/ext/libmspack/test/chmd_order.c +144 -0
  80. data/ext/libmspack/test/chminfo.c +284 -0
  81. data/ext/libmspack/test/chmx.c +216 -0
  82. data/ext/libmspack/test/error.h +22 -0
  83. data/ext/libmspack/test/expand.c +79 -0
  84. data/ext/libmspack/test/md5.c +457 -0
  85. data/ext/libmspack/test/md5.h +165 -0
  86. data/ext/libmspack/test/md5_fh.h +123 -0
  87. data/ext/libmspack/test/msdecompile_md5 +24 -0
  88. data/ext/libmspack/test/msexpand_md5 +39 -0
  89. data/ext/libmspack/test/multifh.c +435 -0
  90. data/ext/libmspack/test/oabx.c +41 -0
  91. data/ext/libmspack/test/test_files/cabd/1.pl +84 -0
  92. data/ext/libmspack/test/test_files/cabd/2.pl +75 -0
  93. data/ext/libmspack/test/test_files/cabd/bad_folderindex.cab +0 -0
  94. data/ext/libmspack/test/test_files/cabd/bad_nofiles.cab +0 -0
  95. data/ext/libmspack/test/test_files/cabd/bad_nofolders.cab +0 -0
  96. data/ext/libmspack/test/test_files/cabd/bad_signature.cab +0 -0
  97. data/ext/libmspack/test/test_files/cabd/multi_basic_pt1.cab +0 -0
  98. data/ext/libmspack/test/test_files/cabd/multi_basic_pt2.cab +0 -0
  99. data/ext/libmspack/test/test_files/cabd/multi_basic_pt3.cab +0 -0
  100. data/ext/libmspack/test/test_files/cabd/multi_basic_pt4.cab +0 -0
  101. data/ext/libmspack/test/test_files/cabd/multi_basic_pt5.cab +0 -0
  102. data/ext/libmspack/test/test_files/cabd/normal_255c_filename.cab +0 -0
  103. data/ext/libmspack/test/test_files/cabd/normal_2files_1folder.cab +0 -0
  104. data/ext/libmspack/test/test_files/cabd/partial_nodata.cab +0 -0
  105. data/ext/libmspack/test/test_files/cabd/partial_nofiles.cab +0 -0
  106. data/ext/libmspack/test/test_files/cabd/partial_nofolder.cab +0 -0
  107. data/ext/libmspack/test/test_files/cabd/partial_shortextheader.cab +0 -0
  108. data/ext/libmspack/test/test_files/cabd/partial_shortfile1.cab +0 -0
  109. data/ext/libmspack/test/test_files/cabd/partial_shortfile2.cab +0 -0
  110. data/ext/libmspack/test/test_files/cabd/partial_shortfolder.cab +0 -0
  111. data/ext/libmspack/test/test_files/cabd/partial_shortheader.cab +0 -0
  112. data/ext/libmspack/test/test_files/cabd/partial_str_nofname.cab +0 -0
  113. data/ext/libmspack/test/test_files/cabd/partial_str_noninfo.cab +0 -0
  114. data/ext/libmspack/test/test_files/cabd/partial_str_nonname.cab +0 -0
  115. data/ext/libmspack/test/test_files/cabd/partial_str_nopinfo.cab +0 -0
  116. data/ext/libmspack/test/test_files/cabd/partial_str_nopname.cab +0 -0
  117. data/ext/libmspack/test/test_files/cabd/partial_str_shortfname.cab +0 -0
  118. data/ext/libmspack/test/test_files/cabd/partial_str_shortninfo.cab +0 -0
  119. data/ext/libmspack/test/test_files/cabd/partial_str_shortnname.cab +0 -0
  120. data/ext/libmspack/test/test_files/cabd/partial_str_shortpinfo.cab +0 -0
  121. data/ext/libmspack/test/test_files/cabd/partial_str_shortpname.cab +0 -0
  122. data/ext/libmspack/test/test_files/cabd/reserve_---.cab +0 -0
  123. data/ext/libmspack/test/test_files/cabd/reserve_--D.cab +0 -0
  124. data/ext/libmspack/test/test_files/cabd/reserve_-F-.cab +0 -0
  125. data/ext/libmspack/test/test_files/cabd/reserve_-FD.cab +0 -0
  126. data/ext/libmspack/test/test_files/cabd/reserve_H--.cab +0 -0
  127. data/ext/libmspack/test/test_files/cabd/reserve_H-D.cab +0 -0
  128. data/ext/libmspack/test/test_files/cabd/reserve_HF-.cab +0 -0
  129. data/ext/libmspack/test/test_files/cabd/reserve_HFD.cab +0 -0
  130. data/ext/libmspack/test/test_files/cabd/search_basic.cab +0 -0
  131. data/ext/libmspack/test/test_files/cabd/search_tricky1.cab +0 -0
  132. data/ext/libmspack/winbuild.sh +26 -0
  133. data/ext/x86_64-windows/libmspack.dll +0 -0
  134. data/lib/libmspack/constants.rb +9 -0
  135. data/lib/libmspack/exceptions.rb +12 -0
  136. data/lib/libmspack/mscab.rb +722 -0
  137. data/lib/libmspack/mschm.rb +301 -0
  138. data/lib/libmspack/mshlp.rb +15 -0
  139. data/lib/libmspack/mskwaj.rb +124 -0
  140. data/lib/libmspack/mslit.rb +18 -0
  141. data/lib/libmspack/msoab.rb +36 -0
  142. data/lib/libmspack/mspack.rb +208 -0
  143. data/lib/libmspack/msszdd.rb +81 -0
  144. data/lib/libmspack/system.rb +84 -0
  145. data/lib/libmspack/version.rb +4 -0
  146. data/lib/libmspack.rb +121 -0
  147. data/libmspack.gemspec +33 -0
  148. data/spec/libmspack_spec.rb +26 -0
  149. data/spec/spec_helper.rb +5 -0
  150. metadata +309 -0
@@ -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,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
+ }