git-svn-id: file:///var/lib/svn/clamav-devel/trunk/clamav-devel@588 77e5149b-7576-45b1-b177-96237e5ba77b
Tomasz Kojm authored on 2004/06/02 09:53:431 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,127 @@ |
0 |
+/* This file is part of libmspack. |
|
1 |
+ * (C) 2003-2004 Stuart Caie. |
|
2 |
+ * |
|
3 |
+ * libmspack is free software; you can redistribute it and/or modify it under |
|
4 |
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
|
5 |
+ * |
|
6 |
+ * For further details, see the file COPYING.LIB distributed with libmspack |
|
7 |
+ */ |
|
8 |
+ |
|
9 |
+#ifndef MSPACK_CAB_H |
|
10 |
+#define MSPACK_CAB_H 1 |
|
11 |
+ |
|
12 |
+#include <mszip.h> |
|
13 |
+#include <qtm.h> |
|
14 |
+#include <lzx.h> |
|
15 |
+ |
|
16 |
+/* generic CAB definitions */ |
|
17 |
+ |
|
18 |
+/* structure offsets */ |
|
19 |
+#define cfhead_Signature (0x00) |
|
20 |
+#define cfhead_CabinetSize (0x08) |
|
21 |
+#define cfhead_FileOffset (0x10) |
|
22 |
+#define cfhead_MinorVersion (0x18) |
|
23 |
+#define cfhead_MajorVersion (0x19) |
|
24 |
+#define cfhead_NumFolders (0x1A) |
|
25 |
+#define cfhead_NumFiles (0x1C) |
|
26 |
+#define cfhead_Flags (0x1E) |
|
27 |
+#define cfhead_SetID (0x20) |
|
28 |
+#define cfhead_CabinetIndex (0x22) |
|
29 |
+#define cfhead_SIZEOF (0x24) |
|
30 |
+#define cfheadext_HeaderReserved (0x00) |
|
31 |
+#define cfheadext_FolderReserved (0x02) |
|
32 |
+#define cfheadext_DataReserved (0x03) |
|
33 |
+#define cfheadext_SIZEOF (0x04) |
|
34 |
+#define cffold_DataOffset (0x00) |
|
35 |
+#define cffold_NumBlocks (0x04) |
|
36 |
+#define cffold_CompType (0x06) |
|
37 |
+#define cffold_SIZEOF (0x08) |
|
38 |
+#define cffile_UncompressedSize (0x00) |
|
39 |
+#define cffile_FolderOffset (0x04) |
|
40 |
+#define cffile_FolderIndex (0x08) |
|
41 |
+#define cffile_Date (0x0A) |
|
42 |
+#define cffile_Time (0x0C) |
|
43 |
+#define cffile_Attribs (0x0E) |
|
44 |
+#define cffile_SIZEOF (0x10) |
|
45 |
+#define cfdata_CheckSum (0x00) |
|
46 |
+#define cfdata_CompressedSize (0x04) |
|
47 |
+#define cfdata_UncompressedSize (0x06) |
|
48 |
+#define cfdata_SIZEOF (0x08) |
|
49 |
+ |
|
50 |
+/* flags */ |
|
51 |
+#define cffoldCOMPTYPE_MASK (0x000f) |
|
52 |
+#define cffoldCOMPTYPE_NONE (0x0000) |
|
53 |
+#define cffoldCOMPTYPE_MSZIP (0x0001) |
|
54 |
+#define cffoldCOMPTYPE_QUANTUM (0x0002) |
|
55 |
+#define cffoldCOMPTYPE_LZX (0x0003) |
|
56 |
+#define cfheadPREV_CABINET (0x0001) |
|
57 |
+#define cfheadNEXT_CABINET (0x0002) |
|
58 |
+#define cfheadRESERVE_PRESENT (0x0004) |
|
59 |
+#define cffileCONTINUED_FROM_PREV (0xFFFD) |
|
60 |
+#define cffileCONTINUED_TO_NEXT (0xFFFE) |
|
61 |
+#define cffileCONTINUED_PREV_AND_NEXT (0xFFFF) |
|
62 |
+ |
|
63 |
+/* CAB data blocks are <= 32768 bytes in uncompressed form. Uncompressed |
|
64 |
+ * blocks have zero growth. MSZIP guarantees that it won't grow above |
|
65 |
+ * uncompressed size by more than 12 bytes. LZX guarantees it won't grow |
|
66 |
+ * more than 6144 bytes. Quantum has no documentation, but the largest |
|
67 |
+ * block seen in the wild is 337 bytes above uncompressed size. |
|
68 |
+ */ |
|
69 |
+#define CAB_BLOCKMAX (32768) |
|
70 |
+#define CAB_INPUTMAX (CAB_BLOCKMAX+6144) |
|
71 |
+ |
|
72 |
+/* CAB compression definitions */ |
|
73 |
+ |
|
74 |
+struct mscab_compressor_p { |
|
75 |
+ struct mscab_compressor base; |
|
76 |
+ struct mspack_system *system; |
|
77 |
+ /* todo */ |
|
78 |
+}; |
|
79 |
+ |
|
80 |
+/* CAB decompression definitions */ |
|
81 |
+ |
|
82 |
+struct mscabd_decompress_state { |
|
83 |
+ struct mscabd_folder_p *folder; /* current folder we're extracting from */ |
|
84 |
+ struct mscabd_folder_data *data; /* current folder split we're in */ |
|
85 |
+ unsigned int offset; /* uncompressed offset within folder */ |
|
86 |
+ unsigned int block; /* which block are we decompressing? */ |
|
87 |
+ struct mspack_system sys; /* special I/O code for decompressor */ |
|
88 |
+ int comp_type; /* type of compression used by folder */ |
|
89 |
+ int (*decompress)(void *, off_t); /* decompressor code */ |
|
90 |
+ void *state; /* decompressor state */ |
|
91 |
+ struct mscabd_cabinet_p *incab; /* cabinet where input data comes from */ |
|
92 |
+ struct mspack_file *infh; /* input file handle */ |
|
93 |
+ struct mspack_file *outfh; /* output file handle */ |
|
94 |
+ unsigned char *i_ptr, *i_end; /* input data consumed, end */ |
|
95 |
+ unsigned char input[CAB_INPUTMAX]; /* one input block of data */ |
|
96 |
+}; |
|
97 |
+ |
|
98 |
+struct mscab_decompressor_p { |
|
99 |
+ struct mscab_decompressor base; |
|
100 |
+ struct mscabd_decompress_state *d; |
|
101 |
+ struct mspack_system *system; |
|
102 |
+ int param[3]; /* !!! MATCH THIS TO NUM OF PARAMS IN MSPACK.H !!! */ |
|
103 |
+ int error; |
|
104 |
+}; |
|
105 |
+ |
|
106 |
+struct mscabd_cabinet_p { |
|
107 |
+ struct mscabd_cabinet base; |
|
108 |
+ off_t blocks_off; /* offset to data blocks */ |
|
109 |
+ int block_resv; /* reserved space in data blocks */ |
|
110 |
+}; |
|
111 |
+ |
|
112 |
+/* there is one of these for every cabinet a folder spans */ |
|
113 |
+struct mscabd_folder_data { |
|
114 |
+ struct mscabd_folder_data *next; |
|
115 |
+ struct mscabd_cabinet_p *cab; /* cabinet file of this folder span */ |
|
116 |
+ off_t offset; /* cabinet offset of first datablock */ |
|
117 |
+}; |
|
118 |
+ |
|
119 |
+struct mscabd_folder_p { |
|
120 |
+ struct mscabd_folder base; |
|
121 |
+ struct mscabd_folder_data data; /* where are the data blocks? */ |
|
122 |
+ struct mscabd_file *merge_prev; /* do we need to merge backwards? */ |
|
123 |
+ struct mscabd_file *merge_next; /* do we need to merge forwards? */ |
|
124 |
+}; |
|
125 |
+ |
|
126 |
+#endif |
0 | 127 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,1519 @@ |
0 |
+/* WARNING: This version also supports dopen for descriptor opening and |
|
1 |
+ * is not compatible with the original version. -- T. Kojm |
|
2 |
+ * |
|
3 |
+ * This file is part of libmspack. |
|
4 |
+ * (C) 2003-2004 Stuart Caie. |
|
5 |
+ * |
|
6 |
+ * libmspack is free software; you can redistribute it and/or modify it under |
|
7 |
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
|
8 |
+ * |
|
9 |
+ * For further details, see the file COPYING.LIB distributed with libmspack |
|
10 |
+ */ |
|
11 |
+ |
|
12 |
+/* Cabinet (.CAB) files are a form of file archive. Each cabinet contains |
|
13 |
+ * "folders", which are compressed spans of data. Each cabinet has |
|
14 |
+ * "files", whose metadata is in the cabinet header, but whose actual data |
|
15 |
+ * is stored compressed in one of the "folders". Cabinets can span more |
|
16 |
+ * than one physical file on disk, in which case they are a "cabinet set", |
|
17 |
+ * and usually the last folder of each cabinet extends into the next |
|
18 |
+ * cabinet. |
|
19 |
+ * |
|
20 |
+ * For a complete description of the format, get the official Microsoft |
|
21 |
+ * CAB SDK. It can be found at the following URL: |
|
22 |
+ * |
|
23 |
+ * http://msdn.microsoft.com/library/en-us/dncabsdk/html/cabdl.asp |
|
24 |
+ * |
|
25 |
+ * It is a self-extracting ZIP file, which can be extracted with the unzip |
|
26 |
+ * command. |
|
27 |
+ */ |
|
28 |
+ |
|
29 |
+/* CAB decompression implementation */ |
|
30 |
+ |
|
31 |
+#if HAVE_CONFIG_H |
|
32 |
+#include "clamav-config.h" |
|
33 |
+#endif |
|
34 |
+ |
|
35 |
+#include <mspack.h> |
|
36 |
+#include <system.h> |
|
37 |
+#include <cab.h> |
|
38 |
+ |
|
39 |
+/* Notes on compliance with cabinet specification: |
|
40 |
+ * |
|
41 |
+ * One of the main changes between cabextract 0.6 and libmspack's cab |
|
42 |
+ * decompressor is the move from block-oriented decompression to |
|
43 |
+ * stream-oriented decompression. |
|
44 |
+ * |
|
45 |
+ * cabextract would read one data block from disk, decompress it with the |
|
46 |
+ * appropriate method, then write the decompressed data. The CAB |
|
47 |
+ * specification is specifically designed to work like this, as it ensures |
|
48 |
+ * compression matches do not span the maximum decompressed block size |
|
49 |
+ * limit of 32kb. |
|
50 |
+ * |
|
51 |
+ * However, the compression algorithms used are stream oriented, with |
|
52 |
+ * specific hacks added to them to enforce the "individual 32kb blocks" |
|
53 |
+ * rule in CABs. In other file formats, they do not have this limitation. |
|
54 |
+ * |
|
55 |
+ * In order to make more generalised decompressors, libmspack's CAB |
|
56 |
+ * decompressor has moved from being block-oriented to more stream |
|
57 |
+ * oriented. This also makes decompression slightly faster. |
|
58 |
+ * |
|
59 |
+ * However, this leads to incompliance with the CAB specification. The |
|
60 |
+ * CAB controller can no longer ensure each block of input given to the |
|
61 |
+ * decompressors is matched with their output. The "decompressed size" of |
|
62 |
+ * each individual block is thrown away. |
|
63 |
+ * |
|
64 |
+ * Each CAB block is supposed to be seen as individually compressed. This |
|
65 |
+ * means each consecutive data block can have completely different |
|
66 |
+ * "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in |
|
67 |
+ * reality, all data blocks in a folder decompress to exactly 32768 bytes, |
|
68 |
+ * excepting the final block. |
|
69 |
+ * |
|
70 |
+ * Given this situation, the decompression algorithms are designed to |
|
71 |
+ * realign their input bitstreams on 32768 output-byte boundaries, and |
|
72 |
+ * various other special cases have been made. libmspack will not |
|
73 |
+ * correctly decompress LZX or Quantum compressed folders where the blocks |
|
74 |
+ * do not follow this "32768 bytes until last block" pattern. It could be |
|
75 |
+ * implemented if needed, but hopefully this is not necessary -- it has |
|
76 |
+ * not been seen in over 3Gb of CAB archives. |
|
77 |
+ */ |
|
78 |
+ |
|
79 |
+/* prototypes */ |
|
80 |
+static struct mscabd_cabinet * cabd_open( |
|
81 |
+ struct mscab_decompressor *base, char *filename); |
|
82 |
+static struct mscabd_cabinet * cabd_dopen( |
|
83 |
+ struct mscab_decompressor *base, int desc); |
|
84 |
+static void cabd_close( |
|
85 |
+ struct mscab_decompressor *base, struct mscabd_cabinet *origcab); |
|
86 |
+static int cabd_read_headers( |
|
87 |
+ struct mspack_system *sys, struct mspack_file *fh, |
|
88 |
+ struct mscabd_cabinet_p *cab, off_t offset, int quiet); |
|
89 |
+static char *cabd_read_string( |
|
90 |
+ struct mspack_system *sys, struct mspack_file *fh, |
|
91 |
+ struct mscabd_cabinet_p *cab, int *error); |
|
92 |
+ |
|
93 |
+static struct mscabd_cabinet *cabd_search( |
|
94 |
+ struct mscab_decompressor *base, char *filename); |
|
95 |
+static struct mscabd_cabinet *cabd_dsearch( |
|
96 |
+ struct mscab_decompressor *base, int desc); |
|
97 |
+static int cabd_find( |
|
98 |
+ struct mscab_decompressor_p *this, unsigned char *buf, |
|
99 |
+ struct mspack_file *fh, char *filename, int desc, off_t flen, |
|
100 |
+ unsigned int *firstlen, struct mscabd_cabinet_p **firstcab); |
|
101 |
+ |
|
102 |
+static int cabd_prepend( |
|
103 |
+ struct mscab_decompressor *base, struct mscabd_cabinet *cab, |
|
104 |
+ struct mscabd_cabinet *prevcab); |
|
105 |
+static int cabd_append( |
|
106 |
+ struct mscab_decompressor *base, struct mscabd_cabinet *cab, |
|
107 |
+ struct mscabd_cabinet *nextcab); |
|
108 |
+static int cabd_merge( |
|
109 |
+ struct mscab_decompressor *base, struct mscabd_cabinet *lcab, |
|
110 |
+ struct mscabd_cabinet *rcab); |
|
111 |
+ |
|
112 |
+static int cabd_extract( |
|
113 |
+ struct mscab_decompressor *base, struct mscabd_file *file, char *filename); |
|
114 |
+static int cabd_init_decomp( |
|
115 |
+ struct mscab_decompressor_p *this, unsigned int ct); |
|
116 |
+static void cabd_free_decomp( |
|
117 |
+ struct mscab_decompressor_p *this); |
|
118 |
+static int cabd_sys_read( |
|
119 |
+ struct mspack_file *file, void *buffer, int bytes); |
|
120 |
+static int cabd_sys_write( |
|
121 |
+ struct mspack_file *file, void *buffer, int bytes); |
|
122 |
+static int cabd_sys_read_block( |
|
123 |
+ struct mspack_system *sys, struct mscabd_decompress_state *d, int *out, |
|
124 |
+ int ignore_cksum); |
|
125 |
+static unsigned int cabd_checksum( |
|
126 |
+ unsigned char *data, unsigned int bytes, unsigned int cksum); |
|
127 |
+static struct noned_state *noned_init( |
|
128 |
+ struct mspack_system *sys, struct mspack_file *in, struct mspack_file *out, |
|
129 |
+ int bufsize); |
|
130 |
+ |
|
131 |
+static int noned_decompress( |
|
132 |
+ struct noned_state *s, off_t bytes); |
|
133 |
+static void noned_free( |
|
134 |
+ struct noned_state *state); |
|
135 |
+ |
|
136 |
+static int cabd_param( |
|
137 |
+ struct mscab_decompressor *base, int param, int value); |
|
138 |
+ |
|
139 |
+static int cabd_error( |
|
140 |
+ struct mscab_decompressor *base); |
|
141 |
+ |
|
142 |
+ |
|
143 |
+/*************************************** |
|
144 |
+ * MSPACK_CREATE_CAB_DECOMPRESSOR |
|
145 |
+ *************************************** |
|
146 |
+ * constructor |
|
147 |
+ */ |
|
148 |
+struct mscab_decompressor * |
|
149 |
+ mspack_create_cab_decompressor(struct mspack_system *sys) |
|
150 |
+{ |
|
151 |
+ struct mscab_decompressor_p *this = NULL; |
|
152 |
+ |
|
153 |
+ if (!sys) sys = mspack_default_system; |
|
154 |
+ if (!mspack_valid_system(sys)) return NULL; |
|
155 |
+ |
|
156 |
+ if ((this = sys->alloc(sys, sizeof(struct mscab_decompressor_p)))) { |
|
157 |
+ this->base.open = &cabd_open; |
|
158 |
+ this->base.dopen = &cabd_dopen; |
|
159 |
+ this->base.close = &cabd_close; |
|
160 |
+ this->base.search = &cabd_search; |
|
161 |
+ this->base.dsearch = &cabd_dsearch; |
|
162 |
+ this->base.extract = &cabd_extract; |
|
163 |
+ this->base.prepend = &cabd_prepend; |
|
164 |
+ this->base.append = &cabd_append; |
|
165 |
+ this->base.set_param = &cabd_param; |
|
166 |
+ this->base.last_error = &cabd_error; |
|
167 |
+ this->system = sys; |
|
168 |
+ this->d = NULL; |
|
169 |
+ this->error = MSPACK_ERR_OK; |
|
170 |
+ |
|
171 |
+ this->param[MSCABD_PARAM_SEARCHBUF] = 32768; |
|
172 |
+ this->param[MSCABD_PARAM_FIXMSZIP] = 0; |
|
173 |
+ this->param[MSCABD_PARAM_DECOMPBUF] = 4096; |
|
174 |
+ } |
|
175 |
+ return (struct mscab_decompressor *) this; |
|
176 |
+} |
|
177 |
+ |
|
178 |
+/*************************************** |
|
179 |
+ * MSPACK_DESTROY_CAB_DECOMPRESSOR |
|
180 |
+ *************************************** |
|
181 |
+ * destructor |
|
182 |
+ */ |
|
183 |
+void mspack_destroy_cab_decompressor(struct mscab_decompressor *base) { |
|
184 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base; |
|
185 |
+ if (this) { |
|
186 |
+ struct mspack_system *sys = this->system; |
|
187 |
+ cabd_free_decomp(this); |
|
188 |
+ if (this->d) { |
|
189 |
+ if (this->d->infh) sys->close(this->d->infh); |
|
190 |
+ sys->free(this->d); |
|
191 |
+ } |
|
192 |
+ sys->free(this); |
|
193 |
+ } |
|
194 |
+} |
|
195 |
+ |
|
196 |
+ |
|
197 |
+/*************************************** |
|
198 |
+ * CABD_OPEN |
|
199 |
+ *************************************** |
|
200 |
+ * opens a file and tries to read it as a cabinet file |
|
201 |
+ */ |
|
202 |
+static struct mscabd_cabinet *cabd_open(struct mscab_decompressor *base, |
|
203 |
+ char *filename) |
|
204 |
+{ |
|
205 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base; |
|
206 |
+ struct mscabd_cabinet_p *cab = NULL; |
|
207 |
+ struct mspack_system *sys; |
|
208 |
+ struct mspack_file *fh; |
|
209 |
+ int error; |
|
210 |
+ |
|
211 |
+ if (!base) return NULL; |
|
212 |
+ sys = this->system; |
|
213 |
+ |
|
214 |
+ if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { |
|
215 |
+ if ((cab = sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { |
|
216 |
+ cab->base.filename = filename; |
|
217 |
+ /* cab->base.desc = 0; */ |
|
218 |
+ error = cabd_read_headers(sys, fh, cab, (off_t) 0, 0); |
|
219 |
+ if (error) { |
|
220 |
+ cabd_close(base, (struct mscabd_cabinet *) cab); |
|
221 |
+ cab = NULL; |
|
222 |
+ } |
|
223 |
+ this->error = error; |
|
224 |
+ } |
|
225 |
+ else { |
|
226 |
+ this->error = MSPACK_ERR_NOMEMORY; |
|
227 |
+ } |
|
228 |
+ sys->close(fh); |
|
229 |
+ } |
|
230 |
+ else { |
|
231 |
+ this->error = MSPACK_ERR_OPEN; |
|
232 |
+ } |
|
233 |
+ return (struct mscabd_cabinet *) cab; |
|
234 |
+} |
|
235 |
+ |
|
236 |
+static struct mscabd_cabinet *cabd_dopen(struct mscab_decompressor *base, |
|
237 |
+ int desc) |
|
238 |
+{ |
|
239 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base; |
|
240 |
+ struct mscabd_cabinet_p *cab = NULL; |
|
241 |
+ struct mspack_system *sys; |
|
242 |
+ struct mspack_file *fh; |
|
243 |
+ int error; |
|
244 |
+ |
|
245 |
+ if (!base) return NULL; |
|
246 |
+ sys = this->system; |
|
247 |
+ |
|
248 |
+ if ((fh = sys->dopen(sys, desc, MSPACK_SYS_OPEN_READ))) { |
|
249 |
+ if ((cab = sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { |
|
250 |
+ cab->base.filename = "descriptor"; |
|
251 |
+ cab->base.desc = desc; |
|
252 |
+ error = cabd_read_headers(sys, fh, cab, (off_t) 0, 0); |
|
253 |
+ if (error) { |
|
254 |
+ cabd_close(base, (struct mscabd_cabinet *) cab); |
|
255 |
+ cab = NULL; |
|
256 |
+ } |
|
257 |
+ this->error = error; |
|
258 |
+ } |
|
259 |
+ else { |
|
260 |
+ this->error = MSPACK_ERR_NOMEMORY; |
|
261 |
+ } |
|
262 |
+ sys->close(fh); |
|
263 |
+ } |
|
264 |
+ else { |
|
265 |
+ this->error = MSPACK_ERR_OPEN; |
|
266 |
+ } |
|
267 |
+ return (struct mscabd_cabinet *) cab; |
|
268 |
+} |
|
269 |
+ |
|
270 |
+/*************************************** |
|
271 |
+ * CABD_CLOSE |
|
272 |
+ *************************************** |
|
273 |
+ * frees all memory associated with a given mscabd_cabinet. |
|
274 |
+ */ |
|
275 |
+static void cabd_close(struct mscab_decompressor *base, |
|
276 |
+ struct mscabd_cabinet *origcab) |
|
277 |
+{ |
|
278 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base; |
|
279 |
+ struct mscabd_folder_data *dat, *ndat; |
|
280 |
+ struct mscabd_cabinet *cab, *ncab; |
|
281 |
+ struct mscabd_folder *fol, *nfol; |
|
282 |
+ struct mscabd_file *fi, *nfi; |
|
283 |
+ struct mspack_system *sys; |
|
284 |
+ |
|
285 |
+ if (!base) return; |
|
286 |
+ sys = this->system; |
|
287 |
+ |
|
288 |
+ this->error = MSPACK_ERR_OK; |
|
289 |
+ |
|
290 |
+ while (origcab) { |
|
291 |
+ /* free files */ |
|
292 |
+ for (fi = origcab->files; fi; fi = nfi) { |
|
293 |
+ nfi = fi->next; |
|
294 |
+ sys->free(fi->filename); |
|
295 |
+ sys->free(fi); |
|
296 |
+ } |
|
297 |
+ |
|
298 |
+ /* free folders */ |
|
299 |
+ for (fol = origcab->folders; fol; fol = nfol) { |
|
300 |
+ nfol = fol->next; |
|
301 |
+ |
|
302 |
+ /* free folder decompression state if it has been decompressed */ |
|
303 |
+ if (this->d && (this->d->folder == (struct mscabd_folder_p *) fol)) { |
|
304 |
+ if (this->d->infh) sys->close(this->d->infh); |
|
305 |
+ cabd_free_decomp(this); |
|
306 |
+ sys->free(this->d); |
|
307 |
+ this->d = NULL; |
|
308 |
+ } |
|
309 |
+ |
|
310 |
+ /* free folder data segments */ |
|
311 |
+ for (dat = ((struct mscabd_folder_p *)fol)->data.next; dat; dat = ndat) { |
|
312 |
+ ndat = dat->next; |
|
313 |
+ sys->free(dat); |
|
314 |
+ } |
|
315 |
+ sys->free(fol); |
|
316 |
+ } |
|
317 |
+ |
|
318 |
+ /* free predecessor cabinets (and the original cabinet's strings) */ |
|
319 |
+ for (cab = origcab; cab; cab = ncab) { |
|
320 |
+ ncab = cab->prevcab; |
|
321 |
+ sys->free(cab->prevname); |
|
322 |
+ sys->free(cab->nextname); |
|
323 |
+ sys->free(cab->previnfo); |
|
324 |
+ sys->free(cab->nextinfo); |
|
325 |
+ if (cab != origcab) sys->free(cab); |
|
326 |
+ } |
|
327 |
+ |
|
328 |
+ /* free successor cabinets */ |
|
329 |
+ for (cab = origcab->nextcab; cab; cab = ncab) { |
|
330 |
+ ncab = cab->nextcab; |
|
331 |
+ sys->free(cab->prevname); |
|
332 |
+ sys->free(cab->nextname); |
|
333 |
+ sys->free(cab->previnfo); |
|
334 |
+ sys->free(cab->nextinfo); |
|
335 |
+ sys->free(cab); |
|
336 |
+ } |
|
337 |
+ |
|
338 |
+ /* free actual cabinet structure */ |
|
339 |
+ cab = origcab->next; |
|
340 |
+ sys->free(origcab); |
|
341 |
+ |
|
342 |
+ /* repeat full procedure again with the cab->next pointer (if set) */ |
|
343 |
+ origcab = cab; |
|
344 |
+ } |
|
345 |
+} |
|
346 |
+ |
|
347 |
+/*************************************** |
|
348 |
+ * CABD_READ_HEADERS |
|
349 |
+ *************************************** |
|
350 |
+ * reads the cabinet file header, folder list and file list. |
|
351 |
+ * fills out a pre-existing mscabd_cabinet structure, allocates memory |
|
352 |
+ * for folders and files as necessary |
|
353 |
+ */ |
|
354 |
+static int cabd_read_headers(struct mspack_system *sys, |
|
355 |
+ struct mspack_file *fh, |
|
356 |
+ struct mscabd_cabinet_p *cab, |
|
357 |
+ off_t offset, int quiet) |
|
358 |
+{ |
|
359 |
+ int num_folders, num_files, folder_resv, i, x; |
|
360 |
+ struct mscabd_folder_p *fol, *linkfol = NULL; |
|
361 |
+ struct mscabd_file *file, *linkfile = NULL; |
|
362 |
+ unsigned char buf[64]; |
|
363 |
+ |
|
364 |
+ /* initialise pointers */ |
|
365 |
+ cab->base.next = NULL; |
|
366 |
+ cab->base.files = NULL; |
|
367 |
+ cab->base.folders = NULL; |
|
368 |
+ cab->base.prevcab = cab->base.nextcab = NULL; |
|
369 |
+ cab->base.prevname = cab->base.nextname = NULL; |
|
370 |
+ cab->base.previnfo = cab->base.nextinfo = NULL; |
|
371 |
+ |
|
372 |
+ cab->base.base_offset = offset; |
|
373 |
+ |
|
374 |
+ /* seek to CFHEADER */ |
|
375 |
+ if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) { |
|
376 |
+ return MSPACK_ERR_SEEK; |
|
377 |
+ } |
|
378 |
+ |
|
379 |
+ /* read in the CFHEADER */ |
|
380 |
+ if (sys->read(fh, &buf[0], cfhead_SIZEOF) != cfhead_SIZEOF) { |
|
381 |
+ return MSPACK_ERR_READ; |
|
382 |
+ } |
|
383 |
+ |
|
384 |
+ /* check for "MSCF" signature */ |
|
385 |
+ if (EndGetI32(&buf[cfhead_Signature]) != 0x4643534D) { |
|
386 |
+ return MSPACK_ERR_SIGNATURE; |
|
387 |
+ } |
|
388 |
+ |
|
389 |
+ /* some basic header fields */ |
|
390 |
+ cab->base.length = EndGetI32(&buf[cfhead_CabinetSize]); |
|
391 |
+ cab->base.set_id = EndGetI16(&buf[cfhead_SetID]); |
|
392 |
+ cab->base.set_index = EndGetI16(&buf[cfhead_CabinetIndex]); |
|
393 |
+ |
|
394 |
+ /* get the number of folders */ |
|
395 |
+ num_folders = EndGetI16(&buf[cfhead_NumFolders]); |
|
396 |
+ if (num_folders == 0) { |
|
397 |
+ if (!quiet) sys->message(fh, "no folders in cabinet."); |
|
398 |
+ return MSPACK_ERR_DATAFORMAT; |
|
399 |
+ } |
|
400 |
+ |
|
401 |
+ /* get the number of files */ |
|
402 |
+ num_files = EndGetI16(&buf[cfhead_NumFiles]); |
|
403 |
+ if (num_files == 0) { |
|
404 |
+ if (!quiet) sys->message(fh, "no files in cabinet."); |
|
405 |
+ return MSPACK_ERR_DATAFORMAT; |
|
406 |
+ } |
|
407 |
+ |
|
408 |
+ /* check cabinet version */ |
|
409 |
+ if ((buf[cfhead_MajorVersion] != 1) && (buf[cfhead_MinorVersion] != 3)) { |
|
410 |
+ if (!quiet) sys->message(fh, "WARNING; cabinet version is not 1.3"); |
|
411 |
+ } |
|
412 |
+ |
|
413 |
+ /* read the reserved-sizes part of header, if present */ |
|
414 |
+ cab->base.flags = EndGetI16(&buf[cfhead_Flags]); |
|
415 |
+ if (cab->base.flags & cfheadRESERVE_PRESENT) { |
|
416 |
+ if (sys->read(fh, &buf[0], cfheadext_SIZEOF) != cfheadext_SIZEOF) { |
|
417 |
+ return MSPACK_ERR_READ; |
|
418 |
+ } |
|
419 |
+ cab->base.header_resv = EndGetI16(&buf[cfheadext_HeaderReserved]); |
|
420 |
+ folder_resv = buf[cfheadext_FolderReserved]; |
|
421 |
+ cab->block_resv = buf[cfheadext_DataReserved]; |
|
422 |
+ |
|
423 |
+ if (cab->base.header_resv > 60000) { |
|
424 |
+ if (!quiet) sys->message(fh, "WARNING; reserved header > 60000."); |
|
425 |
+ } |
|
426 |
+ |
|
427 |
+ /* skip the reserved header */ |
|
428 |
+ if (cab->base.header_resv) { |
|
429 |
+ if (sys->seek(fh, (off_t) cab->base.header_resv, MSPACK_SYS_SEEK_CUR)) { |
|
430 |
+ return MSPACK_ERR_SEEK; |
|
431 |
+ } |
|
432 |
+ } |
|
433 |
+ } |
|
434 |
+ else { |
|
435 |
+ cab->base.header_resv = 0; |
|
436 |
+ folder_resv = 0; |
|
437 |
+ cab->block_resv = 0; |
|
438 |
+ } |
|
439 |
+ |
|
440 |
+ /* read name and info of preceeding cabinet in set, if present */ |
|
441 |
+ if (cab->base.flags & cfheadPREV_CABINET) { |
|
442 |
+ cab->base.prevname = cabd_read_string(sys, fh, cab, &x); if (x) return x; |
|
443 |
+ cab->base.previnfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; |
|
444 |
+ } |
|
445 |
+ |
|
446 |
+ /* read name and info of next cabinet in set, if present */ |
|
447 |
+ if (cab->base.flags & cfheadNEXT_CABINET) { |
|
448 |
+ cab->base.nextname = cabd_read_string(sys, fh, cab, &x); if (x) return x; |
|
449 |
+ cab->base.nextinfo = cabd_read_string(sys, fh, cab, &x); if (x) return x; |
|
450 |
+ } |
|
451 |
+ |
|
452 |
+ /* read folders */ |
|
453 |
+ for (i = 0; i < num_folders; i++) { |
|
454 |
+ if (sys->read(fh, &buf[0], cffold_SIZEOF) != cffold_SIZEOF) { |
|
455 |
+ return MSPACK_ERR_READ; |
|
456 |
+ } |
|
457 |
+ if (folder_resv) { |
|
458 |
+ if (sys->seek(fh, (off_t) folder_resv, MSPACK_SYS_SEEK_CUR)) { |
|
459 |
+ return MSPACK_ERR_SEEK; |
|
460 |
+ } |
|
461 |
+ } |
|
462 |
+ |
|
463 |
+ if (!(fol = sys->alloc(sys, sizeof(struct mscabd_folder_p)))) { |
|
464 |
+ return MSPACK_ERR_NOMEMORY; |
|
465 |
+ } |
|
466 |
+ fol->base.next = NULL; |
|
467 |
+ fol->base.comp_type = EndGetI16(&buf[cffold_CompType]); |
|
468 |
+ fol->base.num_blocks = EndGetI16(&buf[cffold_NumBlocks]); |
|
469 |
+ fol->data.next = NULL; |
|
470 |
+ fol->data.cab = (struct mscabd_cabinet_p *) cab; |
|
471 |
+ fol->data.offset = offset + (off_t) |
|
472 |
+ ( (unsigned int) EndGetI32(&buf[cffold_DataOffset]) ); |
|
473 |
+ fol->merge_prev = NULL; |
|
474 |
+ fol->merge_next = NULL; |
|
475 |
+ |
|
476 |
+ /* link folder into list of folders */ |
|
477 |
+ if (!linkfol) cab->base.folders = (struct mscabd_folder *) fol; |
|
478 |
+ else linkfol->base.next = (struct mscabd_folder *) fol; |
|
479 |
+ linkfol = fol; |
|
480 |
+ } |
|
481 |
+ |
|
482 |
+ /* read files */ |
|
483 |
+ for (i = 0; i < num_files; i++) { |
|
484 |
+ if (sys->read(fh, &buf[0], cffile_SIZEOF) != cffile_SIZEOF) { |
|
485 |
+ return MSPACK_ERR_READ; |
|
486 |
+ } |
|
487 |
+ |
|
488 |
+ if (!(file = sys->alloc(sys, sizeof(struct mscabd_file)))) { |
|
489 |
+ return MSPACK_ERR_NOMEMORY; |
|
490 |
+ } |
|
491 |
+ |
|
492 |
+ file->next = NULL; |
|
493 |
+ file->length = EndGetI32(&buf[cffile_UncompressedSize]); |
|
494 |
+ file->attribs = EndGetI16(&buf[cffile_Attribs]); |
|
495 |
+ file->offset = EndGetI32(&buf[cffile_FolderOffset]); |
|
496 |
+ |
|
497 |
+ /* set folder pointer */ |
|
498 |
+ x = EndGetI16(&buf[cffile_FolderIndex]); |
|
499 |
+ if (x < cffileCONTINUED_FROM_PREV) { |
|
500 |
+ /* normal folder index; count up to the correct folder. the folder |
|
501 |
+ * pointer will be NULL if folder index is invalid */ |
|
502 |
+ struct mscabd_folder *ifol = cab->base.folders; |
|
503 |
+ while (x--) if (ifol) ifol = ifol->next; |
|
504 |
+ file->folder = ifol; |
|
505 |
+ |
|
506 |
+ if (!ifol) { |
|
507 |
+ sys->free(file); |
|
508 |
+ D(("invalid folder index")) |
|
509 |
+ return MSPACK_ERR_DATAFORMAT; |
|
510 |
+ } |
|
511 |
+ } |
|
512 |
+ else { |
|
513 |
+ /* either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or |
|
514 |
+ * CONTINUED_PREV_AND_NEXT */ |
|
515 |
+ if ((x == cffileCONTINUED_TO_NEXT) || |
|
516 |
+ (x == cffileCONTINUED_PREV_AND_NEXT)) |
|
517 |
+ { |
|
518 |
+ /* get last folder */ |
|
519 |
+ struct mscabd_folder *ifol = cab->base.folders; |
|
520 |
+ while (ifol->next) ifol = ifol->next; |
|
521 |
+ file->folder = ifol; |
|
522 |
+ |
|
523 |
+ /* set "merge next" pointer */ |
|
524 |
+ fol = (struct mscabd_folder_p *) ifol; |
|
525 |
+ if (!fol->merge_next) fol->merge_next = file; |
|
526 |
+ } |
|
527 |
+ |
|
528 |
+ if ((x == cffileCONTINUED_FROM_PREV) || |
|
529 |
+ (x == cffileCONTINUED_PREV_AND_NEXT)) |
|
530 |
+ { |
|
531 |
+ /* get first folder */ |
|
532 |
+ file->folder = cab->base.folders; |
|
533 |
+ |
|
534 |
+ /* set "merge prev" pointer */ |
|
535 |
+ fol = (struct mscabd_folder_p *) file->folder; |
|
536 |
+ if (!fol->merge_prev) fol->merge_prev = file; |
|
537 |
+ } |
|
538 |
+ } |
|
539 |
+ |
|
540 |
+ /* get time */ |
|
541 |
+ x = EndGetI16(&buf[cffile_Time]); |
|
542 |
+ file->time_h = x >> 11; |
|
543 |
+ file->time_m = (x >> 5) & 0x3F; |
|
544 |
+ file->time_s = (x << 1) & 0x3E; |
|
545 |
+ |
|
546 |
+ /* get date */ |
|
547 |
+ x = EndGetI16(&buf[cffile_Date]); |
|
548 |
+ file->date_d = x & 0x1F; |
|
549 |
+ file->date_m = (x >> 5) & 0xF; |
|
550 |
+ file->date_y = (x >> 9) + 1980; |
|
551 |
+ |
|
552 |
+ /* get filename */ |
|
553 |
+ file->filename = cabd_read_string(sys, fh, cab, &x); |
|
554 |
+ if (x) { |
|
555 |
+ sys->free(file); |
|
556 |
+ return x; |
|
557 |
+ } |
|
558 |
+ |
|
559 |
+ /* link file entry into file list */ |
|
560 |
+ if (!linkfile) cab->base.files = file; |
|
561 |
+ else linkfile->next = file; |
|
562 |
+ linkfile = file; |
|
563 |
+ } |
|
564 |
+ |
|
565 |
+ return MSPACK_ERR_OK; |
|
566 |
+} |
|
567 |
+ |
|
568 |
+static char *cabd_read_string(struct mspack_system *sys, |
|
569 |
+ struct mspack_file *fh, |
|
570 |
+ struct mscabd_cabinet_p *cab, int *error) |
|
571 |
+{ |
|
572 |
+ off_t base = sys->tell(fh); |
|
573 |
+ char buf[256], *str; |
|
574 |
+ unsigned int len, i, ok; |
|
575 |
+ |
|
576 |
+ /* read up to 256 bytes */ |
|
577 |
+ len = sys->read(fh, &buf[0], 256); |
|
578 |
+ |
|
579 |
+ /* search for a null terminator in the buffer */ |
|
580 |
+ for (i = 0, ok = 0; i < len; i++) if (!buf[i]) { ok = 1; break; } |
|
581 |
+ if (!ok) { |
|
582 |
+ *error = MSPACK_ERR_DATAFORMAT; |
|
583 |
+ return NULL; |
|
584 |
+ } |
|
585 |
+ |
|
586 |
+ len = i + 1; |
|
587 |
+ |
|
588 |
+ /* set the data stream to just after the string and return */ |
|
589 |
+ if (sys->seek(fh, base + (off_t)len, MSPACK_SYS_SEEK_START)) { |
|
590 |
+ *error = MSPACK_ERR_SEEK; |
|
591 |
+ return NULL; |
|
592 |
+ } |
|
593 |
+ |
|
594 |
+ if (!(str = sys->alloc(sys, len))) { |
|
595 |
+ *error = MSPACK_ERR_NOMEMORY; |
|
596 |
+ return NULL; |
|
597 |
+ } |
|
598 |
+ |
|
599 |
+ sys->copy(&buf[0], str, len); |
|
600 |
+ *error = MSPACK_ERR_OK; |
|
601 |
+ return str; |
|
602 |
+} |
|
603 |
+ |
|
604 |
+/*************************************** |
|
605 |
+ * CABD_SEARCH, CABD_FIND |
|
606 |
+ *************************************** |
|
607 |
+ * cabd_search opens a file, finds its extent, allocates a search buffer, |
|
608 |
+ * then reads through the whole file looking for possible cabinet headers. |
|
609 |
+ * if it finds any, it tries to read them as real cabinets. returns a linked |
|
610 |
+ * list of results |
|
611 |
+ * |
|
612 |
+ * cabd_find is the inner loop of cabd_search, to make it easier to |
|
613 |
+ * break out of the loop and be sure that all resources are freed |
|
614 |
+ */ |
|
615 |
+static struct mscabd_cabinet *cabd_search(struct mscab_decompressor *base, |
|
616 |
+ char *filename) |
|
617 |
+{ |
|
618 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base; |
|
619 |
+ struct mscabd_cabinet_p *cab = NULL; |
|
620 |
+ struct mspack_system *sys; |
|
621 |
+ unsigned char *search_buf; |
|
622 |
+ struct mspack_file *fh; |
|
623 |
+ unsigned int firstlen = 0; |
|
624 |
+ off_t filelen; |
|
625 |
+ |
|
626 |
+ if (!base) return NULL; |
|
627 |
+ sys = this->system; |
|
628 |
+ |
|
629 |
+ /* allocate a search buffer */ |
|
630 |
+ search_buf = sys->alloc(sys, (size_t) this->param[MSCABD_PARAM_SEARCHBUF]); |
|
631 |
+ if (!search_buf) { |
|
632 |
+ this->error = MSPACK_ERR_NOMEMORY; |
|
633 |
+ return NULL; |
|
634 |
+ } |
|
635 |
+ |
|
636 |
+ /* open file and get its full file length */ |
|
637 |
+ if ((fh = sys->open(sys, filename, MSPACK_SYS_OPEN_READ))) { |
|
638 |
+ if (!(this->error = mspack_sys_filelen(sys, fh, &filelen))) { |
|
639 |
+ this->error = cabd_find(this, search_buf, fh, filename, 0, |
|
640 |
+ filelen, &firstlen, &cab); |
|
641 |
+ } |
|
642 |
+ |
|
643 |
+ /* truncated / extraneous data warning: */ |
|
644 |
+ if (firstlen && (firstlen != filelen) && |
|
645 |
+ (!cab || (cab->base.base_offset == 0))) |
|
646 |
+ { |
|
647 |
+ if (firstlen < filelen) { |
|
648 |
+ sys->message(fh, "WARNING; possible %u extra bytes at end of file.", |
|
649 |
+ (unsigned int) (filelen - firstlen)); |
|
650 |
+ } |
|
651 |
+ else { |
|
652 |
+ sys->message(fh, "WARNING; file possibly truncated by %u bytes.", |
|
653 |
+ (unsigned int) (firstlen - filelen)); |
|
654 |
+ } |
|
655 |
+ } |
|
656 |
+ |
|
657 |
+ sys->close(fh); |
|
658 |
+ } |
|
659 |
+ else { |
|
660 |
+ this->error = MSPACK_ERR_OPEN; |
|
661 |
+ } |
|
662 |
+ |
|
663 |
+ /* free the search buffer */ |
|
664 |
+ sys->free(search_buf); |
|
665 |
+ |
|
666 |
+ return (struct mscabd_cabinet *) cab; |
|
667 |
+} |
|
668 |
+ |
|
669 |
+static struct mscabd_cabinet *cabd_dsearch(struct mscab_decompressor *base, |
|
670 |
+ int desc) |
|
671 |
+{ |
|
672 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base; |
|
673 |
+ struct mscabd_cabinet_p *cab = NULL; |
|
674 |
+ struct mspack_system *sys; |
|
675 |
+ unsigned char *search_buf; |
|
676 |
+ struct mspack_file *fh; |
|
677 |
+ unsigned int firstlen = 0; |
|
678 |
+ off_t filelen; |
|
679 |
+ char *filename = "descriptor-"; |
|
680 |
+ |
|
681 |
+ if (!base) return NULL; |
|
682 |
+ sys = this->system; |
|
683 |
+ |
|
684 |
+ /* allocate a search buffer */ |
|
685 |
+ search_buf = sys->alloc(sys, (size_t) this->param[MSCABD_PARAM_SEARCHBUF]); |
|
686 |
+ if (!search_buf) { |
|
687 |
+ this->error = MSPACK_ERR_NOMEMORY; |
|
688 |
+ return NULL; |
|
689 |
+ } |
|
690 |
+ |
|
691 |
+ /* open file and get its full file length */ |
|
692 |
+ if ((fh = sys->dopen(sys, desc, MSPACK_SYS_OPEN_READ))) { |
|
693 |
+ if (!(this->error = mspack_sys_filelen(sys, fh, &filelen))) { |
|
694 |
+ this->error = cabd_find(this, search_buf, fh, filename, desc, |
|
695 |
+ filelen, &firstlen, &cab); |
|
696 |
+ } |
|
697 |
+ |
|
698 |
+ /* truncated / extraneous data warning: */ |
|
699 |
+ if (firstlen && (firstlen != filelen) && |
|
700 |
+ (!cab || (cab->base.base_offset == 0))) |
|
701 |
+ { |
|
702 |
+ if (firstlen < filelen) { |
|
703 |
+ sys->message(fh, "WARNING; possible %u extra bytes at end of file.", |
|
704 |
+ (unsigned int) (filelen - firstlen)); |
|
705 |
+ } |
|
706 |
+ else { |
|
707 |
+ sys->message(fh, "WARNING; file possibly truncated by %u bytes.", |
|
708 |
+ (unsigned int) (firstlen - filelen)); |
|
709 |
+ } |
|
710 |
+ } |
|
711 |
+ |
|
712 |
+ /* sys->close(fh); */ |
|
713 |
+ } |
|
714 |
+ else { |
|
715 |
+ this->error = MSPACK_ERR_OPEN; |
|
716 |
+ } |
|
717 |
+ |
|
718 |
+ /* free the search buffer */ |
|
719 |
+ sys->free(search_buf); |
|
720 |
+ |
|
721 |
+ return (struct mscabd_cabinet *) cab; |
|
722 |
+} |
|
723 |
+ |
|
724 |
+ |
|
725 |
+static int cabd_find(struct mscab_decompressor_p *this, unsigned char *buf, |
|
726 |
+ struct mspack_file *fh, char *filename, int desc, off_t flen, |
|
727 |
+ unsigned int *firstlen, |
|
728 |
+ struct mscabd_cabinet_p **firstcab) |
|
729 |
+{ |
|
730 |
+ struct mscabd_cabinet_p *cab, *link = NULL; |
|
731 |
+ off_t caboff, offset, foffset=0, cablen=0; |
|
732 |
+ struct mspack_system *sys = this->system; |
|
733 |
+ unsigned char *p, *pend, state = 0; |
|
734 |
+ int false_cabs = 0, length; |
|
735 |
+ |
|
736 |
+ /* search through the full file length */ |
|
737 |
+ for (offset = 0; offset < flen; offset += length) { |
|
738 |
+ /* search length is either the full length of the search buffer, or the |
|
739 |
+ * amount of data remaining to the end of the file, whichever is less. */ |
|
740 |
+ length = flen - offset; |
|
741 |
+ if (length > this->param[MSCABD_PARAM_SEARCHBUF]) { |
|
742 |
+ length = this->param[MSCABD_PARAM_SEARCHBUF]; |
|
743 |
+ } |
|
744 |
+ |
|
745 |
+ /* fill the search buffer with data from disk */ |
|
746 |
+ if (sys->read(fh, &buf[0], length) != length) { |
|
747 |
+ return MSPACK_ERR_READ; |
|
748 |
+ } |
|
749 |
+ |
|
750 |
+ /* FAQ avoidance strategy */ |
|
751 |
+ if ((offset == 0) && (EndGetI32(&buf[0]) == 0x28635349)) { |
|
752 |
+ sys->message(fh, "WARNING; found InstallShield header. " |
|
753 |
+ "This is probably an InstallShield file. " |
|
754 |
+ "Use UNSHIELD (http://synce.sf.net) to unpack it."); |
|
755 |
+ } |
|
756 |
+ |
|
757 |
+ /* read through the entire buffer. */ |
|
758 |
+ for (p = &buf[0], pend = &buf[length]; p < pend; ) { |
|
759 |
+ switch (state) { |
|
760 |
+ /* starting state */ |
|
761 |
+ case 0: |
|
762 |
+ /* we spend most of our time in this while loop, looking for |
|
763 |
+ * a leading 'M' of the 'MSCF' signature */ |
|
764 |
+ while (p < pend && *p != 0x4D) p++; |
|
765 |
+ /* if we found tht 'M', advance state */ |
|
766 |
+ if (p++ < pend) state = 1; |
|
767 |
+ break; |
|
768 |
+ |
|
769 |
+ /* verify that the next 3 bytes are 'S', 'C' and 'F' */ |
|
770 |
+ case 1: state = (*p++ == 0x53) ? 2 : 0; break; |
|
771 |
+ case 2: state = (*p++ == 0x43) ? 3 : 0; break; |
|
772 |
+ case 3: state = (*p++ == 0x46) ? 4 : 0; break; |
|
773 |
+ |
|
774 |
+ /* we don't care about bytes 4-7 (see default: for action) */ |
|
775 |
+ |
|
776 |
+ /* bytes 8-11 are the overall length of the cabinet */ |
|
777 |
+ case 8: cablen = *p++; state++; break; |
|
778 |
+ case 9: cablen |= *p++ << 8; state++; break; |
|
779 |
+ case 10: cablen |= *p++ << 16; state++; break; |
|
780 |
+ case 11: cablen |= *p++ << 24; state++; break; |
|
781 |
+ |
|
782 |
+ /* we don't care about bytes 12-15 (see default: for action) */ |
|
783 |
+ |
|
784 |
+ /* bytes 16-19 are the offset within the cabinet of the filedata */ |
|
785 |
+ case 16: foffset = *p++; state++; break; |
|
786 |
+ case 17: foffset |= *p++ << 8; state++; break; |
|
787 |
+ case 18: foffset |= *p++ << 16; state++; break; |
|
788 |
+ case 19: foffset |= *p++ << 24; |
|
789 |
+ /* now we have recieved 20 bytes of potential cab header. work out |
|
790 |
+ * the offset in the file of this potential cabinet */ |
|
791 |
+ caboff = offset + (p - &buf[0]) - 20; |
|
792 |
+ |
|
793 |
+ /* should reading cabinet fail, restart search just after 'MSCF' */ |
|
794 |
+ offset = caboff + 4; |
|
795 |
+ |
|
796 |
+ /* capture the "length of cabinet" field if there is a cabinet at |
|
797 |
+ * offset 0 in the file, regardless of whether the cabinet can be |
|
798 |
+ * read correctly or not */ |
|
799 |
+ if (caboff == 0) *firstlen = cablen; |
|
800 |
+ |
|
801 |
+ /* check that the files offset is less than the alleged length of |
|
802 |
+ * the cabinet, and that the offset + the alleged length are |
|
803 |
+ * 'roughly' within the end of overall file length */ |
|
804 |
+ if ((foffset < cablen) && |
|
805 |
+ ((caboff + foffset) < (flen + 32)) && |
|
806 |
+ ((caboff + cablen) < (flen + 32)) ) |
|
807 |
+ { |
|
808 |
+ /* likely cabinet found -- try reading it */ |
|
809 |
+ if (!(cab = sys->alloc(sys, sizeof(struct mscabd_cabinet_p)))) { |
|
810 |
+ return MSPACK_ERR_NOMEMORY; |
|
811 |
+ } |
|
812 |
+ cab->base.filename = filename; |
|
813 |
+ cab->base.desc = desc; |
|
814 |
+ if (cabd_read_headers(sys, fh, cab, caboff, 1)) { |
|
815 |
+ /* destroy the failed cabinet */ |
|
816 |
+ cabd_close((struct mscab_decompressor *) this, |
|
817 |
+ (struct mscabd_cabinet *) cab); |
|
818 |
+ false_cabs++; |
|
819 |
+ } |
|
820 |
+ else { |
|
821 |
+ /* cabinet read correctly! */ |
|
822 |
+ |
|
823 |
+ /* cause the search to restart after this cab's data. */ |
|
824 |
+ offset = caboff + cablen; |
|
825 |
+ |
|
826 |
+ /* link the cab into the list */ |
|
827 |
+ if (!link) *firstcab = cab; |
|
828 |
+ else link->base.next = (struct mscabd_cabinet *) cab; |
|
829 |
+ link = cab; |
|
830 |
+ } |
|
831 |
+ } |
|
832 |
+ |
|
833 |
+ /* restart search */ |
|
834 |
+ if (offset >= flen) return MSPACK_ERR_OK; |
|
835 |
+ if (sys->seek(fh, offset, MSPACK_SYS_SEEK_START)) |
|
836 |
+ return MSPACK_ERR_SEEK; |
|
837 |
+ length = 0; |
|
838 |
+ p = pend; |
|
839 |
+ state = 0; |
|
840 |
+ break; |
|
841 |
+ |
|
842 |
+ /* for bytes 4-7 and 12-15, just advance state/pointer */ |
|
843 |
+ default: |
|
844 |
+ p++, state++; |
|
845 |
+ } /* switch(state) */ |
|
846 |
+ } /* for (... p < pend ...) */ |
|
847 |
+ } /* for (... offset < length ...) */ |
|
848 |
+ |
|
849 |
+ if (false_cabs) { |
|
850 |
+ D(("%d false cabinets found", false_cabs)) |
|
851 |
+ } |
|
852 |
+ |
|
853 |
+ return MSPACK_ERR_OK; |
|
854 |
+} |
|
855 |
+ |
|
856 |
+/*************************************** |
|
857 |
+ * CABD_MERGE, CABD_PREPEND, CABD_APPEND |
|
858 |
+ *************************************** |
|
859 |
+ * joins cabinets together, also merges split folders between these two |
|
860 |
+ * cabinets only. this includes freeing the duplicate folder and file(s) |
|
861 |
+ * and allocating a further mscabd_folder_data structure to append to the |
|
862 |
+ * merged folder's data parts list. |
|
863 |
+ */ |
|
864 |
+static int cabd_prepend(struct mscab_decompressor *base, |
|
865 |
+ struct mscabd_cabinet *cab, |
|
866 |
+ struct mscabd_cabinet *prevcab) |
|
867 |
+{ |
|
868 |
+ return cabd_merge(base, prevcab, cab); |
|
869 |
+} |
|
870 |
+ |
|
871 |
+static int cabd_append(struct mscab_decompressor *base, |
|
872 |
+ struct mscabd_cabinet *cab, |
|
873 |
+ struct mscabd_cabinet *nextcab) |
|
874 |
+{ |
|
875 |
+ return cabd_merge(base, cab, nextcab); |
|
876 |
+} |
|
877 |
+ |
|
878 |
+static int cabd_merge(struct mscab_decompressor *base, |
|
879 |
+ struct mscabd_cabinet *lcab, |
|
880 |
+ struct mscabd_cabinet *rcab) |
|
881 |
+{ |
|
882 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base; |
|
883 |
+ struct mscabd_folder_data *data, *ndata; |
|
884 |
+ struct mscabd_folder_p *lfol, *rfol; |
|
885 |
+ struct mscabd_file *fi, *rfi, *lfi; |
|
886 |
+ struct mscabd_cabinet *cab; |
|
887 |
+ struct mspack_system *sys; |
|
888 |
+ |
|
889 |
+ if (!this) return MSPACK_ERR_ARGS; |
|
890 |
+ sys = this->system; |
|
891 |
+ |
|
892 |
+ /* basic args check */ |
|
893 |
+ if (!lcab || !rcab || (lcab == rcab)) { |
|
894 |
+ D(("lcab NULL, rcab NULL or lcab = rcab")) |
|
895 |
+ return this->error = MSPACK_ERR_ARGS; |
|
896 |
+ } |
|
897 |
+ |
|
898 |
+ /* check there's not already a cabinet attached */ |
|
899 |
+ if (lcab->nextcab || rcab->prevcab) { |
|
900 |
+ D(("cabs already joined")) |
|
901 |
+ return this->error = MSPACK_ERR_ARGS; |
|
902 |
+ } |
|
903 |
+ |
|
904 |
+ /* do not create circular cabinet chains */ |
|
905 |
+ for (cab = lcab->prevcab; cab; cab = cab->prevcab) { |
|
906 |
+ if (cab == rcab) {D(("circular!")) return this->error = MSPACK_ERR_ARGS;} |
|
907 |
+ } |
|
908 |
+ for (cab = rcab->nextcab; cab; cab = cab->nextcab) { |
|
909 |
+ if (cab == lcab) {D(("circular!")) return this->error = MSPACK_ERR_ARGS;} |
|
910 |
+ } |
|
911 |
+ |
|
912 |
+ /* warn about odd set IDs or indices */ |
|
913 |
+ if (lcab->set_id != rcab->set_id) { |
|
914 |
+ sys->message(NULL, "WARNING; merged cabinets with differing Set IDs."); |
|
915 |
+ } |
|
916 |
+ |
|
917 |
+ if (lcab->set_index > rcab->set_index) { |
|
918 |
+ sys->message(NULL, "WARNING; merged cabinets with odd order."); |
|
919 |
+ } |
|
920 |
+ |
|
921 |
+ /* merging the last folder in lcab with the first folder in rcab */ |
|
922 |
+ lfol = (struct mscabd_folder_p *) lcab->folders; |
|
923 |
+ rfol = (struct mscabd_folder_p *) rcab->folders; |
|
924 |
+ while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; |
|
925 |
+ |
|
926 |
+ /* do we need to merge folders? */ |
|
927 |
+ if (!lfol->merge_next && !rfol->merge_prev) { |
|
928 |
+ /* no, at least one of the folders is not for merging */ |
|
929 |
+ |
|
930 |
+ /* attach cabs */ |
|
931 |
+ lcab->nextcab = rcab; |
|
932 |
+ rcab->prevcab = lcab; |
|
933 |
+ |
|
934 |
+ /* attach folders */ |
|
935 |
+ lfol->base.next = (struct mscabd_folder *) rfol; |
|
936 |
+ |
|
937 |
+ /* attach files */ |
|
938 |
+ fi = lcab->files; |
|
939 |
+ while (fi->next) fi = fi->next; |
|
940 |
+ fi->next = rcab->files; |
|
941 |
+ } |
|
942 |
+ else { |
|
943 |
+ /* folder merge required */ |
|
944 |
+ |
|
945 |
+ if (!lfol->merge_next) { |
|
946 |
+ D(("rcab has merge files, lcab doesn't")) |
|
947 |
+ return this->error = MSPACK_ERR_DATAFORMAT; |
|
948 |
+ } |
|
949 |
+ |
|
950 |
+ if (!rfol->merge_prev) { |
|
951 |
+ D(("lcab has merge files, rcab doesn't")) |
|
952 |
+ return this->error = MSPACK_ERR_DATAFORMAT; |
|
953 |
+ } |
|
954 |
+ |
|
955 |
+ /* check that both folders use the same compression method/settings */ |
|
956 |
+ if (lfol->base.comp_type != rfol->base.comp_type) { |
|
957 |
+ D(("compression type mismatch")) |
|
958 |
+ return this->error = MSPACK_ERR_DATAFORMAT; |
|
959 |
+ } |
|
960 |
+ |
|
961 |
+ /* for all files in lfol (which is the last folder in whichever cab), |
|
962 |
+ * compare them to the files from rfol. they should be identical in |
|
963 |
+ * number and order. to verify this, check the OFFSETS of each file. */ |
|
964 |
+ lfi = lfol->merge_next; |
|
965 |
+ rfi = rfol->merge_prev; |
|
966 |
+ while (lfi) { |
|
967 |
+ if (!rfi || (lfi->offset != rfi->offset)) { |
|
968 |
+ D(("folder merge mismatch")) |
|
969 |
+ return this->error = MSPACK_ERR_DATAFORMAT; |
|
970 |
+ } |
|
971 |
+ lfi = lfi->next; |
|
972 |
+ rfi = rfi->next; |
|
973 |
+ } |
|
974 |
+ |
|
975 |
+ /* allocate a new folder data structure */ |
|
976 |
+ if (!(data = sys->alloc(sys, sizeof(struct mscabd_folder_data)))) { |
|
977 |
+ return this->error = MSPACK_ERR_NOMEMORY; |
|
978 |
+ } |
|
979 |
+ |
|
980 |
+ /* attach cabs */ |
|
981 |
+ lcab->nextcab = rcab; |
|
982 |
+ rcab->prevcab = lcab; |
|
983 |
+ |
|
984 |
+ /* append rfol's data to lfol */ |
|
985 |
+ ndata = &lfol->data; |
|
986 |
+ while (ndata->next) ndata = ndata->next; |
|
987 |
+ ndata->next = data; |
|
988 |
+ *data = rfol->data; |
|
989 |
+ rfol->data.next = NULL; |
|
990 |
+ |
|
991 |
+ /* lfol becomes rfol. |
|
992 |
+ * NOTE: special case, don't merge if rfol is merge prev and next, |
|
993 |
+ * rfol->merge_next is going to be deleted, so keep lfol's version |
|
994 |
+ * instead */ |
|
995 |
+ lfol->base.num_blocks += rfol->base.num_blocks - 1; |
|
996 |
+ if ((rfol->merge_next == NULL) || |
|
997 |
+ (rfol->merge_next->folder != (struct mscabd_folder *) rfol)) |
|
998 |
+ { |
|
999 |
+ lfol->merge_next = rfol->merge_next; |
|
1000 |
+ } |
|
1001 |
+ |
|
1002 |
+ /* attach the rfol's folder (except the merge folder) */ |
|
1003 |
+ while (lfol->base.next) lfol = (struct mscabd_folder_p *) lfol->base.next; |
|
1004 |
+ lfol->base.next = rfol->base.next; |
|
1005 |
+ |
|
1006 |
+ /* free disused merge folder */ |
|
1007 |
+ sys->free(rfol); |
|
1008 |
+ |
|
1009 |
+ /* attach rfol's files */ |
|
1010 |
+ fi = lcab->files; |
|
1011 |
+ while (fi->next) fi = fi->next; |
|
1012 |
+ fi->next = rcab->files; |
|
1013 |
+ |
|
1014 |
+ /* delete all files from rfol's merge folder */ |
|
1015 |
+ lfi = NULL; |
|
1016 |
+ for (fi = lcab->files; fi ; fi = rfi) { |
|
1017 |
+ rfi = fi->next; |
|
1018 |
+ /* if file's folder matches the merge folder, unlink and free it */ |
|
1019 |
+ if (fi->folder == (struct mscabd_folder *) rfol) { |
|
1020 |
+ if (lfi) lfi->next = rfi; else lcab->files = rfi; |
|
1021 |
+ sys->free(fi->filename); |
|
1022 |
+ sys->free(fi); |
|
1023 |
+ } |
|
1024 |
+ else lfi = fi; |
|
1025 |
+ } |
|
1026 |
+ } |
|
1027 |
+ |
|
1028 |
+ /* all done! fix files and folders pointers in all cabs so they all |
|
1029 |
+ * point to the same list */ |
|
1030 |
+ for (cab = lcab->prevcab; cab; cab = cab->prevcab) { |
|
1031 |
+ cab->files = lcab->files; |
|
1032 |
+ cab->folders = lcab->folders; |
|
1033 |
+ } |
|
1034 |
+ |
|
1035 |
+ for (cab = lcab->nextcab; cab; cab = cab->nextcab) { |
|
1036 |
+ cab->files = lcab->files; |
|
1037 |
+ cab->folders = lcab->folders; |
|
1038 |
+ } |
|
1039 |
+ |
|
1040 |
+ return this->error = MSPACK_ERR_OK; |
|
1041 |
+} |
|
1042 |
+ |
|
1043 |
+/*************************************** |
|
1044 |
+ * CABD_EXTRACT |
|
1045 |
+ *************************************** |
|
1046 |
+ * extracts a file from a cabinet |
|
1047 |
+ */ |
|
1048 |
+static int cabd_extract(struct mscab_decompressor *base, |
|
1049 |
+ struct mscabd_file *file, char *filename) |
|
1050 |
+{ |
|
1051 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base; |
|
1052 |
+ struct mscabd_folder_p *fol; |
|
1053 |
+ struct mspack_system *sys; |
|
1054 |
+ struct mspack_file *fh; |
|
1055 |
+ |
|
1056 |
+ if (!this) return MSPACK_ERR_ARGS; |
|
1057 |
+ if (!file) return this->error = MSPACK_ERR_ARGS; |
|
1058 |
+ |
|
1059 |
+ sys = this->system; |
|
1060 |
+ fol = (struct mscabd_folder_p *) file->folder; |
|
1061 |
+ |
|
1062 |
+ /* check if file can be extracted */ |
|
1063 |
+ if ((!fol) || (fol->merge_prev) || |
|
1064 |
+ (((file->offset + file->length) / CAB_BLOCKMAX) > fol->base.num_blocks)) |
|
1065 |
+ { |
|
1066 |
+ sys->message(NULL, "ERROR; file \"%s\" cannot be extracted, " |
|
1067 |
+ "cabinet set is incomplete.", file->filename); |
|
1068 |
+ return this->error = MSPACK_ERR_DATAFORMAT; |
|
1069 |
+ } |
|
1070 |
+ |
|
1071 |
+ /* allocate generic decompression state */ |
|
1072 |
+ if (!this->d) { |
|
1073 |
+ this->d = sys->alloc(sys, sizeof(struct mscabd_decompress_state)); |
|
1074 |
+ if (!this->d) return this->error = MSPACK_ERR_NOMEMORY; |
|
1075 |
+ this->d->folder = NULL; |
|
1076 |
+ this->d->data = NULL; |
|
1077 |
+ this->d->sys = *sys; |
|
1078 |
+ this->d->sys.read = &cabd_sys_read; |
|
1079 |
+ this->d->sys.write = &cabd_sys_write; |
|
1080 |
+ this->d->state = NULL; |
|
1081 |
+ this->d->infh = NULL; |
|
1082 |
+ this->d->incab = NULL; |
|
1083 |
+ } |
|
1084 |
+ |
|
1085 |
+ /* do we need to change folder or reset the current folder? */ |
|
1086 |
+ if ((this->d->folder != fol) || (this->d->offset > file->offset)) { |
|
1087 |
+ /* do we need to open a new cab file? */ |
|
1088 |
+ |
|
1089 |
+ if (!this->d->infh || (fol->data.cab != this->d->incab)) { |
|
1090 |
+ if (this->d->infh) sys->close(this->d->infh); |
|
1091 |
+ this->d->incab = fol->data.cab; |
|
1092 |
+ |
|
1093 |
+ if(fol->data.cab->base.desc) { |
|
1094 |
+ this->d->infh = sys->dopen(sys, fol->data.cab->base.desc, |
|
1095 |
+ MSPACK_SYS_OPEN_READ); |
|
1096 |
+ } else { |
|
1097 |
+ this->d->infh = sys->open(sys, fol->data.cab->base.filename, |
|
1098 |
+ MSPACK_SYS_OPEN_READ); |
|
1099 |
+ } |
|
1100 |
+ if (!this->d->infh) return this->error = MSPACK_ERR_OPEN; |
|
1101 |
+ } |
|
1102 |
+ |
|
1103 |
+ /* seek to start of data blocks */ |
|
1104 |
+ if (sys->seek(this->d->infh, fol->data.offset, MSPACK_SYS_SEEK_START)) { |
|
1105 |
+ return this->error = MSPACK_ERR_SEEK; |
|
1106 |
+ } |
|
1107 |
+ |
|
1108 |
+ /* set up decompressor */ |
|
1109 |
+ if (cabd_init_decomp(this, (unsigned int) fol->base.comp_type)) { |
|
1110 |
+ return this->error; |
|
1111 |
+ } |
|
1112 |
+ |
|
1113 |
+ /* initialise new folder state */ |
|
1114 |
+ this->d->folder = fol; |
|
1115 |
+ this->d->data = &fol->data; |
|
1116 |
+ this->d->offset = 0; |
|
1117 |
+ this->d->block = 0; |
|
1118 |
+ this->d->i_ptr = this->d->i_end = &this->d->input[0]; |
|
1119 |
+ } |
|
1120 |
+ |
|
1121 |
+ /* open file for output */ |
|
1122 |
+ if (!(fh = sys->open(sys, filename, MSPACK_SYS_OPEN_WRITE))) { |
|
1123 |
+ return this->error = MSPACK_ERR_OPEN; |
|
1124 |
+ } |
|
1125 |
+ |
|
1126 |
+ this->error = MSPACK_ERR_OK; |
|
1127 |
+ |
|
1128 |
+ /* if file has more than 0 bytes */ |
|
1129 |
+ if (file->length) { |
|
1130 |
+ off_t bytes; |
|
1131 |
+ int error; |
|
1132 |
+ /* get to correct offset. |
|
1133 |
+ * - use NULL fh to say 'no writing' to cabd_sys_write() |
|
1134 |
+ * - MSPACK_ERR_READ returncode indicates error in cabd_sys_read(), |
|
1135 |
+ * the real error will already be stored in this->error |
|
1136 |
+ */ |
|
1137 |
+ this->d->outfh = NULL; |
|
1138 |
+ if ((bytes = file->offset - this->d->offset)) { |
|
1139 |
+ error = this->d->decompress(this->d->state, bytes); |
|
1140 |
+ if (error != MSPACK_ERR_READ) this->error = error; |
|
1141 |
+ } |
|
1142 |
+ |
|
1143 |
+ /* if getting to the correct offset was error free, unpack file */ |
|
1144 |
+ if (!this->error) { |
|
1145 |
+ this->d->outfh = fh; |
|
1146 |
+ error = this->d->decompress(this->d->state, (off_t) file->length); |
|
1147 |
+ if (error != MSPACK_ERR_READ) this->error = error; |
|
1148 |
+ } |
|
1149 |
+ } |
|
1150 |
+ |
|
1151 |
+ /* close output file */ |
|
1152 |
+ sys->close(fh); |
|
1153 |
+ this->d->outfh = NULL; |
|
1154 |
+ |
|
1155 |
+ return this->error; |
|
1156 |
+} |
|
1157 |
+ |
|
1158 |
+/*************************************** |
|
1159 |
+ * CABD_INIT_DECOMP, CABD_FREE_DECOMP |
|
1160 |
+ *************************************** |
|
1161 |
+ * cabd_init_decomp initialises decompression state, according to which |
|
1162 |
+ * decompression method was used. relies on this->d->folder being the same |
|
1163 |
+ * as when initialised. |
|
1164 |
+ * |
|
1165 |
+ * cabd_free_decomp frees decompression state, according to which method |
|
1166 |
+ * was used. |
|
1167 |
+ */ |
|
1168 |
+static int cabd_init_decomp(struct mscab_decompressor_p *this, unsigned int ct) |
|
1169 |
+{ |
|
1170 |
+ struct mspack_file *fh = (struct mspack_file *) this; |
|
1171 |
+ |
|
1172 |
+ if (!this || !this->d) { |
|
1173 |
+ return this->error = MSPACK_ERR_ARGS; |
|
1174 |
+ } |
|
1175 |
+ |
|
1176 |
+ /* free any existing decompressor */ |
|
1177 |
+ cabd_free_decomp(this); |
|
1178 |
+ |
|
1179 |
+ this->d->comp_type = ct; |
|
1180 |
+ |
|
1181 |
+ switch (ct & cffoldCOMPTYPE_MASK) { |
|
1182 |
+ case cffoldCOMPTYPE_NONE: |
|
1183 |
+ this->d->decompress = (int (*)(void *, off_t)) &noned_decompress; |
|
1184 |
+ this->d->state = noned_init(&this->d->sys, fh, fh, |
|
1185 |
+ this->param[MSCABD_PARAM_DECOMPBUF]); |
|
1186 |
+ break; |
|
1187 |
+ case cffoldCOMPTYPE_MSZIP: |
|
1188 |
+ this->d->decompress = (int (*)(void *, off_t)) &mszipd_decompress; |
|
1189 |
+ this->d->state = mszipd_init(&this->d->sys, fh, fh, |
|
1190 |
+ this->param[MSCABD_PARAM_DECOMPBUF], |
|
1191 |
+ this->param[MSCABD_PARAM_FIXMSZIP]); |
|
1192 |
+ break; |
|
1193 |
+ case cffoldCOMPTYPE_QUANTUM: |
|
1194 |
+ this->d->decompress = (int (*)(void *, off_t)) &qtmd_decompress; |
|
1195 |
+ this->d->state = qtmd_init(&this->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, |
|
1196 |
+ this->param[MSCABD_PARAM_DECOMPBUF]); |
|
1197 |
+ break; |
|
1198 |
+ case cffoldCOMPTYPE_LZX: |
|
1199 |
+ this->d->decompress = (int (*)(void *, off_t)) &lzxd_decompress; |
|
1200 |
+ this->d->state = lzxd_init(&this->d->sys, fh, fh, (int) (ct >> 8) & 0x1f, 0, |
|
1201 |
+ this->param[MSCABD_PARAM_DECOMPBUF], (off_t) 0); |
|
1202 |
+ break; |
|
1203 |
+ default: |
|
1204 |
+ return this->error = MSPACK_ERR_DATAFORMAT; |
|
1205 |
+ } |
|
1206 |
+ return this->error = (this->d->state) ? MSPACK_ERR_OK : MSPACK_ERR_NOMEMORY; |
|
1207 |
+} |
|
1208 |
+ |
|
1209 |
+static void cabd_free_decomp(struct mscab_decompressor_p *this) { |
|
1210 |
+ if (!this || !this->d || !this->d->folder || !this->d->state) return; |
|
1211 |
+ |
|
1212 |
+ switch (this->d->comp_type & cffoldCOMPTYPE_MASK) { |
|
1213 |
+ case cffoldCOMPTYPE_NONE: noned_free(this->d->state); break; |
|
1214 |
+ case cffoldCOMPTYPE_MSZIP: mszipd_free(this->d->state); break; |
|
1215 |
+ case cffoldCOMPTYPE_QUANTUM: qtmd_free(this->d->state); break; |
|
1216 |
+ case cffoldCOMPTYPE_LZX: lzxd_free(this->d->state); break; |
|
1217 |
+ } |
|
1218 |
+ this->d->decompress = NULL; |
|
1219 |
+ this->d->state = NULL; |
|
1220 |
+} |
|
1221 |
+ |
|
1222 |
+/*************************************** |
|
1223 |
+ * CABD_SYS_READ, CABD_SYS_WRITE |
|
1224 |
+ *************************************** |
|
1225 |
+ * cabd_sys_read is the internal reader function which the decompressors |
|
1226 |
+ * use. will read data blocks (and merge split blocks) from the cabinet |
|
1227 |
+ * and serve the read bytes to the decompressors |
|
1228 |
+ * |
|
1229 |
+ * cabd_sys_write is the internal writer function which the decompressors |
|
1230 |
+ * use. it either writes data to disk (this->d->outfh) with the real |
|
1231 |
+ * sys->write() function, or does nothing with the data when |
|
1232 |
+ * this->d->outfh == NULL. advances this->d->offset |
|
1233 |
+ */ |
|
1234 |
+static int cabd_sys_read(struct mspack_file *file, void *buffer, int bytes) { |
|
1235 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) file; |
|
1236 |
+ unsigned char *buf = (unsigned char *) buffer; |
|
1237 |
+ struct mspack_system *sys = this->system; |
|
1238 |
+ int avail, todo, outlen, ignore_cksum; |
|
1239 |
+ |
|
1240 |
+ ignore_cksum = this->param[MSCABD_PARAM_FIXMSZIP] && |
|
1241 |
+ ((this->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_MSZIP); |
|
1242 |
+ |
|
1243 |
+ todo = bytes; |
|
1244 |
+ while (todo > 0) { |
|
1245 |
+ avail = this->d->i_end - this->d->i_ptr; |
|
1246 |
+ |
|
1247 |
+ /* if out of input data, read a new block */ |
|
1248 |
+ if (avail) { |
|
1249 |
+ /* copy as many input bytes available as possible */ |
|
1250 |
+ if (avail > todo) avail = todo; |
|
1251 |
+ sys->copy(this->d->i_ptr, buf, (size_t) avail); |
|
1252 |
+ this->d->i_ptr += avail; |
|
1253 |
+ buf += avail; |
|
1254 |
+ todo -= avail; |
|
1255 |
+ } |
|
1256 |
+ else { |
|
1257 |
+ /* out of data, read a new block */ |
|
1258 |
+ |
|
1259 |
+ /* check if we're out of input blocks, advance block counter */ |
|
1260 |
+ if (this->d->block++ >= this->d->folder->base.num_blocks) { |
|
1261 |
+ this->error = MSPACK_ERR_DATAFORMAT; |
|
1262 |
+ break; |
|
1263 |
+ } |
|
1264 |
+ |
|
1265 |
+ /* read a block */ |
|
1266 |
+ this->error = cabd_sys_read_block(sys, this->d, &outlen, ignore_cksum); |
|
1267 |
+ if (this->error) return -1; |
|
1268 |
+ |
|
1269 |
+ /* special Quantum hack -- trailer byte to allow the decompressor |
|
1270 |
+ * to realign itself. CAB Quantum blocks, unlike LZX blocks, can have |
|
1271 |
+ * anything from 0 to 4 trailing null bytes. */ |
|
1272 |
+ if ((this->d->comp_type & cffoldCOMPTYPE_MASK)==cffoldCOMPTYPE_QUANTUM) { |
|
1273 |
+ *this->d->i_end++ = 0xFF; |
|
1274 |
+ } |
|
1275 |
+ |
|
1276 |
+ /* is this the last block? */ |
|
1277 |
+ if (this->d->block >= this->d->folder->base.num_blocks) { |
|
1278 |
+ /* last block */ |
|
1279 |
+ if ((this->d->comp_type & cffoldCOMPTYPE_MASK) == cffoldCOMPTYPE_LZX) { |
|
1280 |
+ /* special LZX hack -- on the last block, inform LZX of the |
|
1281 |
+ * size of the output data stream. */ |
|
1282 |
+ lzxd_set_output_length(this->d->state, (off_t) |
|
1283 |
+ ((this->d->block-1) * CAB_BLOCKMAX + outlen)); |
|
1284 |
+ } |
|
1285 |
+ } |
|
1286 |
+ else { |
|
1287 |
+ /* not the last block */ |
|
1288 |
+ if (outlen != CAB_BLOCKMAX) { |
|
1289 |
+ this->system->message(this->d->infh, |
|
1290 |
+ "WARNING; non-maximal data block"); |
|
1291 |
+ } |
|
1292 |
+ } |
|
1293 |
+ } /* if (avail) */ |
|
1294 |
+ } /* while (todo > 0) */ |
|
1295 |
+ return bytes - todo; |
|
1296 |
+} |
|
1297 |
+ |
|
1298 |
+static int cabd_sys_write(struct mspack_file *file, void *buffer, int bytes) { |
|
1299 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) file; |
|
1300 |
+ this->d->offset += bytes; |
|
1301 |
+ if (this->d->outfh) { |
|
1302 |
+ return this->system->write(this->d->outfh, buffer, bytes); |
|
1303 |
+ } |
|
1304 |
+ return bytes; |
|
1305 |
+} |
|
1306 |
+ |
|
1307 |
+/*************************************** |
|
1308 |
+ * CABD_SYS_READ_BLOCK |
|
1309 |
+ *************************************** |
|
1310 |
+ * reads a whole data block from a cab file. the block may span more than |
|
1311 |
+ * one cab file, if it does then the fragments will be reassembled |
|
1312 |
+ */ |
|
1313 |
+static int cabd_sys_read_block(struct mspack_system *sys, |
|
1314 |
+ struct mscabd_decompress_state *d, |
|
1315 |
+ int *out, int ignore_cksum) |
|
1316 |
+{ |
|
1317 |
+ unsigned char hdr[cfdata_SIZEOF]; |
|
1318 |
+ unsigned int cksum; |
|
1319 |
+ int len; |
|
1320 |
+ |
|
1321 |
+ /* reset the input block pointer and end of block pointer */ |
|
1322 |
+ d->i_ptr = d->i_end = &d->input[0]; |
|
1323 |
+ |
|
1324 |
+ do { |
|
1325 |
+ /* read the block header */ |
|
1326 |
+ if (sys->read(d->infh, &hdr[0], cfdata_SIZEOF) != cfdata_SIZEOF) { |
|
1327 |
+ return MSPACK_ERR_READ; |
|
1328 |
+ } |
|
1329 |
+ |
|
1330 |
+ /* skip any reserved block headers */ |
|
1331 |
+ if (d->data->cab->block_resv && |
|
1332 |
+ sys->seek(d->infh, (off_t) d->data->cab->block_resv, |
|
1333 |
+ MSPACK_SYS_SEEK_CUR)) |
|
1334 |
+ { |
|
1335 |
+ return MSPACK_ERR_SEEK; |
|
1336 |
+ } |
|
1337 |
+ |
|
1338 |
+ /* blocks must not be over CAB_INPUTMAX in size */ |
|
1339 |
+ len = EndGetI16(&hdr[cfdata_CompressedSize]); |
|
1340 |
+ if (((d->i_end - d->i_ptr) + len) > CAB_INPUTMAX) { |
|
1341 |
+ D(("block size > CAB_INPUTMAX (%d + %d)", d->i_end - d->i_ptr, len)) |
|
1342 |
+ return MSPACK_ERR_DATAFORMAT; |
|
1343 |
+ } |
|
1344 |
+ |
|
1345 |
+ /* blocks must not expand to more than CAB_BLOCKMAX */ |
|
1346 |
+ if (EndGetI16(&hdr[cfdata_UncompressedSize]) > CAB_BLOCKMAX) { |
|
1347 |
+ D(("block size > CAB_BLOCKMAX")) |
|
1348 |
+ return MSPACK_ERR_DATAFORMAT; |
|
1349 |
+ } |
|
1350 |
+ |
|
1351 |
+ /* read the block data */ |
|
1352 |
+ if (sys->read(d->infh, d->i_end, len) != len) { |
|
1353 |
+ return MSPACK_ERR_READ; |
|
1354 |
+ } |
|
1355 |
+ |
|
1356 |
+ /* perform checksum test on the block (if one is stored) */ |
|
1357 |
+ if ((cksum = EndGetI32(&hdr[cfdata_CheckSum]))) { |
|
1358 |
+ unsigned int sum2 = cabd_checksum(d->i_end, (unsigned int) len, 0); |
|
1359 |
+ if (cabd_checksum(&hdr[4], 4, sum2) != cksum) { |
|
1360 |
+ if (!ignore_cksum) return MSPACK_ERR_CHECKSUM; |
|
1361 |
+ sys->message(d->infh, "WARNING; bad block checksum found"); |
|
1362 |
+ } |
|
1363 |
+ } |
|
1364 |
+ |
|
1365 |
+ /* advance end of block pointer to include newly read data */ |
|
1366 |
+ d->i_end += len; |
|
1367 |
+ |
|
1368 |
+ /* uncompressed size == 0 means this block was part of a split block |
|
1369 |
+ * and it continues as the first block of the next cabinet in the set. |
|
1370 |
+ * otherwise, this is the last part of the block, and no more block |
|
1371 |
+ * reading needs to be done. |
|
1372 |
+ */ |
|
1373 |
+ /* EXIT POINT OF LOOP -- uncompressed size != 0 */ |
|
1374 |
+ if ((*out = EndGetI16(&hdr[cfdata_UncompressedSize]))) { |
|
1375 |
+ return MSPACK_ERR_OK; |
|
1376 |
+ } |
|
1377 |
+ |
|
1378 |
+ /* otherwise, advance to next cabinet */ |
|
1379 |
+ |
|
1380 |
+ /* close current file handle */ |
|
1381 |
+ sys->close(d->infh); |
|
1382 |
+ d->infh = NULL; |
|
1383 |
+ |
|
1384 |
+ /* advance to next member in the cabinet set */ |
|
1385 |
+ if (!(d->data = d->data->next)) { |
|
1386 |
+ D(("ran out of splits in cabinet set")) |
|
1387 |
+ return MSPACK_ERR_DATAFORMAT; |
|
1388 |
+ } |
|
1389 |
+ |
|
1390 |
+ /* open next cab file */ |
|
1391 |
+ d->incab = d->data->cab; |
|
1392 |
+ if (!(d->infh = sys->open(sys, d->incab->base.filename, |
|
1393 |
+ MSPACK_SYS_OPEN_READ))) |
|
1394 |
+ { |
|
1395 |
+ return MSPACK_ERR_OPEN; |
|
1396 |
+ } |
|
1397 |
+ |
|
1398 |
+ /* seek to start of data blocks */ |
|
1399 |
+ if (sys->seek(d->infh, d->data->offset, MSPACK_SYS_SEEK_START)) { |
|
1400 |
+ return MSPACK_ERR_SEEK; |
|
1401 |
+ } |
|
1402 |
+ } while (1); |
|
1403 |
+ |
|
1404 |
+ /* not reached */ |
|
1405 |
+ return MSPACK_ERR_OK; |
|
1406 |
+} |
|
1407 |
+ |
|
1408 |
+static unsigned int cabd_checksum(unsigned char *data, unsigned int bytes, |
|
1409 |
+ unsigned int cksum) |
|
1410 |
+{ |
|
1411 |
+ unsigned int len, ul = 0; |
|
1412 |
+ |
|
1413 |
+ for (len = bytes >> 2; len--; data += 4) { |
|
1414 |
+ cksum ^= ((data[0]) | (data[1]<<8) | (data[2]<<16) | (data[3]<<24)); |
|
1415 |
+ } |
|
1416 |
+ |
|
1417 |
+ switch (bytes & 3) { |
|
1418 |
+ case 3: ul |= *data++ << 16; |
|
1419 |
+ case 2: ul |= *data++ << 8; |
|
1420 |
+ case 1: ul |= *data; |
|
1421 |
+ } |
|
1422 |
+ cksum ^= ul; |
|
1423 |
+ |
|
1424 |
+ return cksum; |
|
1425 |
+} |
|
1426 |
+ |
|
1427 |
+/*************************************** |
|
1428 |
+ * NONED_INIT, NONED_DECOMPRESS, NONED_FREE |
|
1429 |
+ *************************************** |
|
1430 |
+ * the "not compressed" method decompressor |
|
1431 |
+ */ |
|
1432 |
+struct noned_state { |
|
1433 |
+ struct mspack_system *sys; |
|
1434 |
+ struct mspack_file *i; |
|
1435 |
+ struct mspack_file *o; |
|
1436 |
+ unsigned char *buf; |
|
1437 |
+ int bufsize; |
|
1438 |
+}; |
|
1439 |
+ |
|
1440 |
+static struct noned_state *noned_init(struct mspack_system *sys, |
|
1441 |
+ struct mspack_file *in, |
|
1442 |
+ struct mspack_file *out, |
|
1443 |
+ int bufsize) |
|
1444 |
+{ |
|
1445 |
+ struct noned_state *state = sys->alloc(sys, sizeof(struct noned_state)); |
|
1446 |
+ unsigned char *buf = sys->alloc(sys, (size_t) bufsize); |
|
1447 |
+ if (state && buf) { |
|
1448 |
+ state->sys = sys; |
|
1449 |
+ state->i = in; |
|
1450 |
+ state->o = out; |
|
1451 |
+ state->buf = buf; |
|
1452 |
+ state->bufsize = bufsize; |
|
1453 |
+ } |
|
1454 |
+ else { |
|
1455 |
+ sys->free(buf); |
|
1456 |
+ sys->free(state); |
|
1457 |
+ state = NULL; |
|
1458 |
+ } |
|
1459 |
+ return state; |
|
1460 |
+} |
|
1461 |
+ |
|
1462 |
+static int noned_decompress(struct noned_state *s, off_t bytes) { |
|
1463 |
+ int run; |
|
1464 |
+ while (bytes > 0) { |
|
1465 |
+ run = (bytes > s->bufsize) ? s->bufsize : (int) bytes; |
|
1466 |
+ if (s->sys->read(s->i, &s->buf[0], run) != run) return MSPACK_ERR_READ; |
|
1467 |
+ if (s->sys->write(s->o, &s->buf[0], run) != run) return MSPACK_ERR_WRITE; |
|
1468 |
+ bytes -= run; |
|
1469 |
+ } |
|
1470 |
+ return MSPACK_ERR_OK; |
|
1471 |
+} |
|
1472 |
+ |
|
1473 |
+static void noned_free(struct noned_state *state) { |
|
1474 |
+ struct mspack_system *sys; |
|
1475 |
+ if (state) { |
|
1476 |
+ sys = state->sys; |
|
1477 |
+ sys->free(state->buf); |
|
1478 |
+ sys->free(state); |
|
1479 |
+ } |
|
1480 |
+} |
|
1481 |
+ |
|
1482 |
+ |
|
1483 |
+/*************************************** |
|
1484 |
+ * CABD_PARAM |
|
1485 |
+ *************************************** |
|
1486 |
+ * allows a parameter to be set |
|
1487 |
+ */ |
|
1488 |
+static int cabd_param(struct mscab_decompressor *base, int param, int value) { |
|
1489 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base; |
|
1490 |
+ if (!this) return MSPACK_ERR_ARGS; |
|
1491 |
+ |
|
1492 |
+ switch (param) { |
|
1493 |
+ case MSCABD_PARAM_SEARCHBUF: |
|
1494 |
+ if (value < 4) return MSPACK_ERR_ARGS; |
|
1495 |
+ this->param[MSCABD_PARAM_SEARCHBUF] = value; |
|
1496 |
+ break; |
|
1497 |
+ case MSCABD_PARAM_FIXMSZIP: |
|
1498 |
+ this->param[MSCABD_PARAM_FIXMSZIP] = value; |
|
1499 |
+ break; |
|
1500 |
+ case MSCABD_PARAM_DECOMPBUF: |
|
1501 |
+ if (value < 4) return MSPACK_ERR_ARGS; |
|
1502 |
+ this->param[MSCABD_PARAM_DECOMPBUF] = value; |
|
1503 |
+ break; |
|
1504 |
+ default: |
|
1505 |
+ return MSPACK_ERR_ARGS; |
|
1506 |
+ } |
|
1507 |
+ return MSPACK_ERR_OK; |
|
1508 |
+} |
|
1509 |
+ |
|
1510 |
+/*************************************** |
|
1511 |
+ * CABD_ERROR |
|
1512 |
+ *************************************** |
|
1513 |
+ * returns the last error that occurred |
|
1514 |
+ */ |
|
1515 |
+static int cabd_error(struct mscab_decompressor *base) { |
|
1516 |
+ struct mscab_decompressor_p *this = (struct mscab_decompressor_p *) base; |
|
1517 |
+ return (this) ? this->error : MSPACK_ERR_ARGS; |
|
1518 |
+} |
0 | 1519 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,167 @@ |
0 |
+/* This file is part of libmspack. |
|
1 |
+ * (C) 2003-2004 Stuart Caie. |
|
2 |
+ * |
|
3 |
+ * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted |
|
4 |
+ * by Microsoft Corporation. |
|
5 |
+ * |
|
6 |
+ * libmspack is free software; you can redistribute it and/or modify it under |
|
7 |
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
|
8 |
+ * |
|
9 |
+ * For further details, see the file COPYING.LIB distributed with libmspack |
|
10 |
+ */ |
|
11 |
+ |
|
12 |
+#ifndef MSPACK_LZX_H |
|
13 |
+#define MSPACK_LZX_H 1 |
|
14 |
+ |
|
15 |
+/* LZX compression / decompression definitions */ |
|
16 |
+ |
|
17 |
+/* some constants defined by the LZX specification */ |
|
18 |
+#define LZX_MIN_MATCH (2) |
|
19 |
+#define LZX_MAX_MATCH (257) |
|
20 |
+#define LZX_NUM_CHARS (256) |
|
21 |
+#define LZX_BLOCKTYPE_INVALID (0) /* also blocktypes 4-7 invalid */ |
|
22 |
+#define LZX_BLOCKTYPE_VERBATIM (1) |
|
23 |
+#define LZX_BLOCKTYPE_ALIGNED (2) |
|
24 |
+#define LZX_BLOCKTYPE_UNCOMPRESSED (3) |
|
25 |
+#define LZX_PRETREE_NUM_ELEMENTS (20) |
|
26 |
+#define LZX_ALIGNED_NUM_ELEMENTS (8) /* aligned offset tree #elements */ |
|
27 |
+#define LZX_NUM_PRIMARY_LENGTHS (7) /* this one missing from spec! */ |
|
28 |
+#define LZX_NUM_SECONDARY_LENGTHS (249) /* length tree #elements */ |
|
29 |
+ |
|
30 |
+/* LZX huffman defines: tweak tablebits as desired */ |
|
31 |
+#define LZX_PRETREE_MAXSYMBOLS (LZX_PRETREE_NUM_ELEMENTS) |
|
32 |
+#define LZX_PRETREE_TABLEBITS (6) |
|
33 |
+#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 50*8) |
|
34 |
+#define LZX_MAINTREE_TABLEBITS (12) |
|
35 |
+#define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS+1) |
|
36 |
+#define LZX_LENGTH_TABLEBITS (12) |
|
37 |
+#define LZX_ALIGNED_MAXSYMBOLS (LZX_ALIGNED_NUM_ELEMENTS) |
|
38 |
+#define LZX_ALIGNED_TABLEBITS (7) |
|
39 |
+#define LZX_LENTABLE_SAFETY (64) /* table decoding overruns are allowed */ |
|
40 |
+ |
|
41 |
+#define LZX_FRAME_SIZE (32768) /* the size of a frame in LZX */ |
|
42 |
+ |
|
43 |
+struct lzxd_stream { |
|
44 |
+ struct mspack_system *sys; /* I/O routines */ |
|
45 |
+ struct mspack_file *input; /* input file handle */ |
|
46 |
+ struct mspack_file *output; /* output file handle */ |
|
47 |
+ |
|
48 |
+ off_t offset; /* number of bytes actually output */ |
|
49 |
+ off_t length; /* overall decompressed length of stream */ |
|
50 |
+ |
|
51 |
+ unsigned char *window; /* decoding window */ |
|
52 |
+ unsigned int window_size; /* window size */ |
|
53 |
+ unsigned int window_posn; /* decompression offset within window */ |
|
54 |
+ unsigned int frame_posn; /* current frame offset within in window */ |
|
55 |
+ unsigned int frame; /* the number of 32kb frames processed */ |
|
56 |
+ unsigned int reset_interval; /* which frame do we reset the compressor? */ |
|
57 |
+ |
|
58 |
+ unsigned int R0, R1, R2; /* for the LRU offset system */ |
|
59 |
+ unsigned int block_length; /* uncompressed length of this LZX block */ |
|
60 |
+ unsigned int block_remaining; /* uncompressed bytes still left to decode */ |
|
61 |
+ |
|
62 |
+ signed int intel_filesize; /* magic header value used for transform */ |
|
63 |
+ signed int intel_curpos; /* current offset in transform space */ |
|
64 |
+ |
|
65 |
+ unsigned char intel_started; /* has intel E8 decoding started? */ |
|
66 |
+ unsigned char block_type; /* type of the current block */ |
|
67 |
+ unsigned char header_read; /* have we started decoding at all yet? */ |
|
68 |
+ unsigned char posn_slots; /* how many posn slots in stream? */ |
|
69 |
+ unsigned char input_end; /* have we reached the end of input? */ |
|
70 |
+ |
|
71 |
+ int error; |
|
72 |
+ |
|
73 |
+ /* I/O buffering */ |
|
74 |
+ unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end; |
|
75 |
+ unsigned int bit_buffer, bits_left, inbuf_size; |
|
76 |
+ |
|
77 |
+ /* huffman code lengths */ |
|
78 |
+ unsigned char PRETREE_len [LZX_PRETREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; |
|
79 |
+ unsigned char MAINTREE_len [LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; |
|
80 |
+ unsigned char LENGTH_len [LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; |
|
81 |
+ unsigned char ALIGNED_len [LZX_ALIGNED_MAXSYMBOLS + LZX_LENTABLE_SAFETY]; |
|
82 |
+ |
|
83 |
+ /* huffman decoding tables */ |
|
84 |
+ unsigned short PRETREE_table [(1 << LZX_PRETREE_TABLEBITS) + |
|
85 |
+ (LZX_PRETREE_MAXSYMBOLS * 2)]; |
|
86 |
+ unsigned short MAINTREE_table[(1 << LZX_MAINTREE_TABLEBITS) + |
|
87 |
+ (LZX_MAINTREE_MAXSYMBOLS * 2)]; |
|
88 |
+ unsigned short LENGTH_table [(1 << LZX_LENGTH_TABLEBITS) + |
|
89 |
+ (LZX_LENGTH_MAXSYMBOLS * 2)]; |
|
90 |
+ unsigned short ALIGNED_table [(1 << LZX_ALIGNED_TABLEBITS) + |
|
91 |
+ (LZX_ALIGNED_MAXSYMBOLS * 2)]; |
|
92 |
+ |
|
93 |
+ /* this is used purely for doing the intel E8 transform */ |
|
94 |
+ unsigned char e8_buf[LZX_FRAME_SIZE]; |
|
95 |
+}; |
|
96 |
+ |
|
97 |
+/* allocates LZX decompression state for decoding the given stream. |
|
98 |
+ * |
|
99 |
+ * - returns NULL if window_bits is outwith the range 15 to 21 (inclusive). |
|
100 |
+ * |
|
101 |
+ * - uses system->alloc() to allocate memory |
|
102 |
+ * |
|
103 |
+ * - returns NULL if not enough memory |
|
104 |
+ * |
|
105 |
+ * - window_bits is the size of the LZX window, from 32Kb (15) to 2Mb (21). |
|
106 |
+ * |
|
107 |
+ * - reset_interval is how often the bitstream is reset, measured in |
|
108 |
+ * multiples of 32Kb bytes output. For CAB LZX streams, this is always 0 |
|
109 |
+ * (does not occur). |
|
110 |
+ * |
|
111 |
+ * - input_buffer_size is how many bytes to use as an input bitstream buffer |
|
112 |
+ * |
|
113 |
+ * - output_length is the length in bytes of the entirely decompressed |
|
114 |
+ * output stream, if known in advance. It is used to correctly perform |
|
115 |
+ * the Intel E8 transformation, which must stop 6 bytes before the very |
|
116 |
+ * end of the decompressed stream. It is not otherwise used or adhered |
|
117 |
+ * to. If the full decompressed length is known in advance, set it here. |
|
118 |
+ * If it is NOT known, use the value 0, and call lzxd_set_output_length() |
|
119 |
+ * once it is known. If never set, 4 of the final 6 bytes of the output |
|
120 |
+ * stream may be incorrect. |
|
121 |
+ */ |
|
122 |
+extern struct lzxd_stream *lzxd_init(struct mspack_system *system, |
|
123 |
+ struct mspack_file *input, |
|
124 |
+ struct mspack_file *output, |
|
125 |
+ int window_bits, |
|
126 |
+ int reset_interval, |
|
127 |
+ int input_buffer_size, |
|
128 |
+ off_t output_length); |
|
129 |
+ |
|
130 |
+/* see description of output_length in lzxd_init() */ |
|
131 |
+extern void lzxd_set_output_length(struct lzxd_stream *lzx, |
|
132 |
+ off_t output_length); |
|
133 |
+ |
|
134 |
+/* decompresses, or decompresses more of, an LZX stream. |
|
135 |
+ * |
|
136 |
+ * - out_bytes of data will be decompressed and the function will return |
|
137 |
+ * with an MSPACK_ERR_OK return code. |
|
138 |
+ * |
|
139 |
+ * - decompressing will stop as soon as out_bytes is reached. if the true |
|
140 |
+ * amount of bytes decoded spills over that amount, they will be kept for |
|
141 |
+ * a later invocation of lzxd_decompress(). |
|
142 |
+ * |
|
143 |
+ * - the output bytes will be passed to the system->write() function given in |
|
144 |
+ * lzxd_init(), using the output file handle given in lzxd_init(). More |
|
145 |
+ * than one call may be made to system->write(). |
|
146 |
+ * |
|
147 |
+ * - LZX will read input bytes as necessary using the system->read() function |
|
148 |
+ * given in lzxd_init(), using the input file handle given in lzxd_init(). |
|
149 |
+ * This will continue until system->read() returns 0 bytes, or an error. |
|
150 |
+ * input streams should convey an "end of input stream" by refusing to |
|
151 |
+ * supply all the bytes that LZX asks for when they reach the end of the |
|
152 |
+ * stream, rather than return an error code. |
|
153 |
+ * |
|
154 |
+ * - if an error code other than MSPACK_ERR_OK is returned, the stream should |
|
155 |
+ * be considered unusable and lzxd_decompress() should not be called again |
|
156 |
+ * on this stream. |
|
157 |
+ */ |
|
158 |
+extern int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes); |
|
159 |
+ |
|
160 |
+/* frees all state associated with an LZX data stream |
|
161 |
+ * |
|
162 |
+ * - calls system->free() using the system pointer given in lzxd_init() |
|
163 |
+ */ |
|
164 |
+void lzxd_free(struct lzxd_stream *lzx); |
|
165 |
+ |
|
166 |
+#endif |
0 | 167 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,901 @@ |
0 |
+/* This file is part of libmspack. |
|
1 |
+ * (C) 2003-2004 Stuart Caie. |
|
2 |
+ * |
|
3 |
+ * The LZX method was created by Jonathan Forbes and Tomi Poutanen, adapted |
|
4 |
+ * by Microsoft Corporation. |
|
5 |
+ * |
|
6 |
+ * libmspack is free software; you can redistribute it and/or modify it under |
|
7 |
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
|
8 |
+ * |
|
9 |
+ * For further details, see the file COPYING.LIB distributed with libmspack |
|
10 |
+ */ |
|
11 |
+ |
|
12 |
+/* LZX decompression implementation */ |
|
13 |
+ |
|
14 |
+#if HAVE_CONFIG_H |
|
15 |
+#include "clamav-config.h" |
|
16 |
+#endif |
|
17 |
+ |
|
18 |
+#include <mspack.h> |
|
19 |
+#include <system.h> |
|
20 |
+#include <lzx.h> |
|
21 |
+ |
|
22 |
+/* Microsoft's LZX document and their implementation of the |
|
23 |
+ * com.ms.util.cab Java package do not concur. |
|
24 |
+ * |
|
25 |
+ * In the LZX document, there is a table showing the correlation between |
|
26 |
+ * window size and the number of position slots. It states that the 1MB |
|
27 |
+ * window = 40 slots and the 2MB window = 42 slots. In the implementation, |
|
28 |
+ * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the |
|
29 |
+ * first slot whose position base is equal to or more than the required |
|
30 |
+ * window size'. This would explain why other tables in the document refer |
|
31 |
+ * to 50 slots rather than 42. |
|
32 |
+ * |
|
33 |
+ * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode |
|
34 |
+ * is not defined in the specification. |
|
35 |
+ * |
|
36 |
+ * The LZX document does not state the uncompressed block has an |
|
37 |
+ * uncompressed length field. Where does this length field come from, so |
|
38 |
+ * we can know how large the block is? The implementation has it as the 24 |
|
39 |
+ * bits following after the 3 blocktype bits, before the alignment |
|
40 |
+ * padding. |
|
41 |
+ * |
|
42 |
+ * The LZX document states that aligned offset blocks have their aligned |
|
43 |
+ * offset huffman tree AFTER the main and length trees. The implementation |
|
44 |
+ * suggests that the aligned offset tree is BEFORE the main and length |
|
45 |
+ * trees. |
|
46 |
+ * |
|
47 |
+ * The LZX document decoding algorithm states that, in an aligned offset |
|
48 |
+ * block, if an extra_bits value is 1, 2 or 3, then that number of bits |
|
49 |
+ * should be read and the result added to the match offset. This is |
|
50 |
+ * correct for 1 and 2, but not 3, where just a huffman symbol (using the |
|
51 |
+ * aligned tree) should be read. |
|
52 |
+ * |
|
53 |
+ * Regarding the E8 preprocessing, the LZX document states 'No translation |
|
54 |
+ * may be performed on the last 6 bytes of the input block'. This is |
|
55 |
+ * correct. However, the pseudocode provided checks for the *E8 leader* |
|
56 |
+ * up to the last 6 bytes. If the leader appears between -10 and -7 bytes |
|
57 |
+ * from the end, this would cause the next four bytes to be modified, at |
|
58 |
+ * least one of which would be in the last 6 bytes, which is not allowed |
|
59 |
+ * according to the spec. |
|
60 |
+ * |
|
61 |
+ * The specification states that the huffman trees must always contain at |
|
62 |
+ * least one element. However, many CAB files contain blocks where the |
|
63 |
+ * length tree is completely empty (because there are no matches), and |
|
64 |
+ * this is expected to succeed. |
|
65 |
+ */ |
|
66 |
+ |
|
67 |
+ |
|
68 |
+/* LZX decompressor input macros |
|
69 |
+ * |
|
70 |
+ * STORE_BITS stores bitstream state in lzxd_stream structure |
|
71 |
+ * RESTORE_BITS restores bitstream state from lzxd_stream structure |
|
72 |
+ * READ_BITS(var,n) takes N bits from the buffer and puts them in var |
|
73 |
+ * ENSURE_BITS(n) ensures there are at least N bits in the bit buffer. |
|
74 |
+ * PEEK_BITS(n) extracts without removing N bits from the bit buffer |
|
75 |
+ * REMOVE_BITS(n) removes N bits from the bit buffer |
|
76 |
+ * |
|
77 |
+ * These bit access routines work by using the area beyond the MSB and the |
|
78 |
+ * LSB as a free source of zeroes when shifting. This avoids having to |
|
79 |
+ * mask any bits. So we have to know the bit width of the bit buffer |
|
80 |
+ * variable. |
|
81 |
+ * |
|
82 |
+ * The bit buffer datatype should be at least 32 bits wide: it must be |
|
83 |
+ * possible to ENSURE_BITS(16), so it must be possible to add 16 new bits |
|
84 |
+ * to the bit buffer when the bit buffer already has 1 to 15 bits left. |
|
85 |
+ */ |
|
86 |
+ |
|
87 |
+#if HAVE_LIMITS_H |
|
88 |
+# include <limits.h> |
|
89 |
+#endif |
|
90 |
+#ifndef CHAR_BIT |
|
91 |
+# define CHAR_BIT (8) |
|
92 |
+#endif |
|
93 |
+#define BITBUF_WIDTH (sizeof(bit_buffer) * CHAR_BIT) |
|
94 |
+ |
|
95 |
+#define STORE_BITS do { \ |
|
96 |
+ lzx->i_ptr = i_ptr; \ |
|
97 |
+ lzx->i_end = i_end; \ |
|
98 |
+ lzx->bit_buffer = bit_buffer; \ |
|
99 |
+ lzx->bits_left = bits_left; \ |
|
100 |
+} while (0) |
|
101 |
+ |
|
102 |
+#define RESTORE_BITS do { \ |
|
103 |
+ i_ptr = lzx->i_ptr; \ |
|
104 |
+ i_end = lzx->i_end; \ |
|
105 |
+ bit_buffer = lzx->bit_buffer; \ |
|
106 |
+ bits_left = lzx->bits_left; \ |
|
107 |
+} while (0) |
|
108 |
+ |
|
109 |
+#define ENSURE_BITS(nbits) \ |
|
110 |
+ while (bits_left < (nbits)) { \ |
|
111 |
+ if (i_ptr >= i_end) { \ |
|
112 |
+ if (lzxd_read_input(lzx)) return lzx->error; \ |
|
113 |
+ i_ptr = lzx->i_ptr; \ |
|
114 |
+ i_end = lzx->i_end; \ |
|
115 |
+ } \ |
|
116 |
+ bit_buffer |= ((i_ptr[1] << 8) | i_ptr[0]) \ |
|
117 |
+ << (BITBUF_WIDTH - 16 - bits_left); \ |
|
118 |
+ bits_left += 16; \ |
|
119 |
+ i_ptr += 2; \ |
|
120 |
+ } |
|
121 |
+ |
|
122 |
+#define PEEK_BITS(nbits) (bit_buffer >> (BITBUF_WIDTH - (nbits))) |
|
123 |
+ |
|
124 |
+#define REMOVE_BITS(nbits) ((bit_buffer <<= (nbits)), (bits_left -= (nbits))) |
|
125 |
+ |
|
126 |
+#define READ_BITS(val, nbits) do { \ |
|
127 |
+ ENSURE_BITS(nbits); \ |
|
128 |
+ (val) = PEEK_BITS(nbits); \ |
|
129 |
+ REMOVE_BITS(nbits); \ |
|
130 |
+} while (0) |
|
131 |
+ |
|
132 |
+static int lzxd_read_input(struct lzxd_stream *lzx) { |
|
133 |
+ int read = lzx->sys->read(lzx->input, &lzx->inbuf[0], (int)lzx->inbuf_size); |
|
134 |
+ if (read < 0) return lzx->error = MSPACK_ERR_READ; |
|
135 |
+ |
|
136 |
+ /* huff decode's ENSURE_BYTES(16) might overrun the input stream, even |
|
137 |
+ * if those bits aren't used, so fake 2 more bytes */ |
|
138 |
+ if (read == 0) { |
|
139 |
+ if (lzx->input_end) { |
|
140 |
+ D(("out of input bytes")) |
|
141 |
+ return lzx->error = MSPACK_ERR_READ; |
|
142 |
+ } |
|
143 |
+ else { |
|
144 |
+ read = 2; |
|
145 |
+ lzx->inbuf[0] = lzx->inbuf[1] = 0; |
|
146 |
+ lzx->input_end = 1; |
|
147 |
+ } |
|
148 |
+ } |
|
149 |
+ |
|
150 |
+ lzx->i_ptr = &lzx->inbuf[0]; |
|
151 |
+ lzx->i_end = &lzx->inbuf[read]; |
|
152 |
+ |
|
153 |
+ return MSPACK_ERR_OK; |
|
154 |
+} |
|
155 |
+ |
|
156 |
+/* Huffman decoding macros */ |
|
157 |
+ |
|
158 |
+/* READ_HUFFSYM(tablename, var) decodes one huffman symbol from the |
|
159 |
+ * bitstream using the stated table and puts it in var. |
|
160 |
+ */ |
|
161 |
+#define READ_HUFFSYM(tbl, var) do { \ |
|
162 |
+ /* huffman symbols can be up to 16 bits long */ \ |
|
163 |
+ ENSURE_BITS(16); \ |
|
164 |
+ /* immediate table lookup of [tablebits] bits of the code */ \ |
|
165 |
+ sym = lzx->tbl##_table[PEEK_BITS(LZX_##tbl##_TABLEBITS)]; \ |
|
166 |
+ /* is the symbol is longer than [tablebits] bits? (i=node index) */ \ |
|
167 |
+ if (sym >= LZX_##tbl##_MAXSYMBOLS) { \ |
|
168 |
+ /* decode remaining bits by tree traversal */ \ |
|
169 |
+ i = 1 << (BITBUF_WIDTH - LZX_##tbl##_TABLEBITS); \ |
|
170 |
+ do { \ |
|
171 |
+ /* one less bit. error if we run out of bits before decode */ \ |
|
172 |
+ i >>= 1; \ |
|
173 |
+ if (i == 0) { \ |
|
174 |
+ D(("out of bits in huffman decode")) \ |
|
175 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; \ |
|
176 |
+ } \ |
|
177 |
+ /* double node index and add 0 (left branch) or 1 (right) */ \ |
|
178 |
+ sym <<= 1; sym |= (bit_buffer & i) ? 1 : 0; \ |
|
179 |
+ /* hop to next node index / decoded symbol */ \ |
|
180 |
+ sym = lzx->tbl##_table[sym]; \ |
|
181 |
+ /* while we are still in node indicies, not decoded symbols */ \ |
|
182 |
+ } while (sym >= LZX_##tbl##_MAXSYMBOLS); \ |
|
183 |
+ } \ |
|
184 |
+ /* result */ \ |
|
185 |
+ (var) = sym; \ |
|
186 |
+ /* look up the code length of that symbol and discard those bits */ \ |
|
187 |
+ i = lzx->tbl##_len[sym]; \ |
|
188 |
+ REMOVE_BITS(i); \ |
|
189 |
+} while (0) |
|
190 |
+ |
|
191 |
+/* BUILD_TABLE(tbl) builds a huffman lookup table from code lengths */ |
|
192 |
+#define BUILD_TABLE(tbl) \ |
|
193 |
+ if (make_decode_table(LZX_##tbl##_MAXSYMBOLS, LZX_##tbl##_TABLEBITS, \ |
|
194 |
+ &lzx->tbl##_len[0], &lzx->tbl##_table[0])) \ |
|
195 |
+ { \ |
|
196 |
+ D(("failed to build %s table", #tbl)) \ |
|
197 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; \ |
|
198 |
+ } |
|
199 |
+ |
|
200 |
+/* make_decode_table(nsyms, nbits, length[], table[]) |
|
201 |
+ * |
|
202 |
+ * This function was coded by David Tritscher. It builds a fast huffman |
|
203 |
+ * decoding table from a canonical huffman code lengths table. |
|
204 |
+ * |
|
205 |
+ * nsyms = total number of symbols in this huffman tree. |
|
206 |
+ * nbits = any symbols with a code length of nbits or less can be decoded |
|
207 |
+ * in one lookup of the table. |
|
208 |
+ * length = A table to get code lengths from [0 to syms-1] |
|
209 |
+ * table = The table to fill up with decoded symbols and pointers. |
|
210 |
+ * |
|
211 |
+ * Returns 0 for OK or 1 for error |
|
212 |
+ */ |
|
213 |
+ |
|
214 |
+static int make_decode_table(unsigned int nsyms, unsigned int nbits, |
|
215 |
+ unsigned char *length, unsigned short *table) |
|
216 |
+{ |
|
217 |
+ register unsigned short sym; |
|
218 |
+ register unsigned int leaf, fill; |
|
219 |
+ register unsigned char bit_num; |
|
220 |
+ unsigned int pos = 0; /* the current position in the decode table */ |
|
221 |
+ unsigned int table_mask = 1 << nbits; |
|
222 |
+ unsigned int bit_mask = table_mask >> 1; /* don't do 0 length codes */ |
|
223 |
+ unsigned int next_symbol = bit_mask; /* base of allocation for long codes */ |
|
224 |
+ |
|
225 |
+ /* fill entries for codes short enough for a direct mapping */ |
|
226 |
+ for (bit_num = 1; bit_num <= nbits; bit_num++) { |
|
227 |
+ for (sym = 0; sym < nsyms; sym++) { |
|
228 |
+ if (length[sym] != bit_num) continue; |
|
229 |
+ leaf = pos; |
|
230 |
+ if((pos += bit_mask) > table_mask) return 1; /* table overrun */ |
|
231 |
+ /* fill all possible lookups of this symbol with the symbol itself */ |
|
232 |
+ for (fill = bit_mask; fill-- > 0;) table[leaf++] = sym; |
|
233 |
+ } |
|
234 |
+ bit_mask >>= 1; |
|
235 |
+ } |
|
236 |
+ |
|
237 |
+ /* full table already? */ |
|
238 |
+ if (pos == table_mask) return 0; |
|
239 |
+ |
|
240 |
+ /* clear the remainder of the table */ |
|
241 |
+ for (sym = pos; sym < table_mask; sym++) table[sym] = 0xFFFF; |
|
242 |
+ |
|
243 |
+ /* allow codes to be up to nbits+16 long, instead of nbits */ |
|
244 |
+ pos <<= 16; |
|
245 |
+ table_mask <<= 16; |
|
246 |
+ bit_mask = 1 << 15; |
|
247 |
+ |
|
248 |
+ for (bit_num = nbits+1; bit_num <= 16; bit_num++) { |
|
249 |
+ for (sym = 0; sym < nsyms; sym++) { |
|
250 |
+ if (length[sym] != bit_num) continue; |
|
251 |
+ |
|
252 |
+ leaf = pos >> 16; |
|
253 |
+ for (fill = 0; fill < bit_num - nbits; fill++) { |
|
254 |
+ /* if this path hasn't been taken yet, 'allocate' two entries */ |
|
255 |
+ if (table[leaf] == 0xFFFF) { |
|
256 |
+ table[(next_symbol << 1)] = 0xFFFF; |
|
257 |
+ table[(next_symbol << 1) + 1] = 0xFFFF; |
|
258 |
+ table[leaf] = next_symbol++; |
|
259 |
+ } |
|
260 |
+ /* follow the path and select either left or right for next bit */ |
|
261 |
+ leaf = table[leaf] << 1; |
|
262 |
+ if ((pos >> (15-fill)) & 1) leaf++; |
|
263 |
+ } |
|
264 |
+ table[leaf] = sym; |
|
265 |
+ |
|
266 |
+ if ((pos += bit_mask) > table_mask) return 1; /* table overflow */ |
|
267 |
+ } |
|
268 |
+ bit_mask >>= 1; |
|
269 |
+ } |
|
270 |
+ |
|
271 |
+ /* full table? */ |
|
272 |
+ if (pos == table_mask) return 0; |
|
273 |
+ |
|
274 |
+ /* either erroneous table, or all elements are 0 - let's find out. */ |
|
275 |
+ for (sym = 0; sym < nsyms; sym++) if (length[sym]) return 1; |
|
276 |
+ return 0; |
|
277 |
+} |
|
278 |
+ |
|
279 |
+ |
|
280 |
+/* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols |
|
281 |
+ * first to last in the given table. The code lengths are stored in their |
|
282 |
+ * own special LZX way. |
|
283 |
+ */ |
|
284 |
+#define READ_LENGTHS(tbl, first, last) do { \ |
|
285 |
+ STORE_BITS; \ |
|
286 |
+ if (lzxd_read_lens(lzx, &lzx->tbl##_len[0], (first), \ |
|
287 |
+ (unsigned int)(last))) return lzx->error; \ |
|
288 |
+ RESTORE_BITS; \ |
|
289 |
+} while (0) |
|
290 |
+ |
|
291 |
+static int lzxd_read_lens(struct lzxd_stream *lzx, unsigned char *lens, |
|
292 |
+ unsigned int first, unsigned int last) |
|
293 |
+{ |
|
294 |
+ /* bit buffer and huffman symbol decode variables */ |
|
295 |
+ register unsigned int bit_buffer; |
|
296 |
+ register int bits_left, i; |
|
297 |
+ register unsigned short sym; |
|
298 |
+ unsigned char *i_ptr, *i_end; |
|
299 |
+ |
|
300 |
+ unsigned int x, y; |
|
301 |
+ int z; |
|
302 |
+ |
|
303 |
+ RESTORE_BITS; |
|
304 |
+ |
|
305 |
+ /* read lengths for pretree (20 symbols, lengths stored in fixed 4 bits) */ |
|
306 |
+ for (x = 0; x < 20; x++) { |
|
307 |
+ READ_BITS(y, 4); |
|
308 |
+ lzx->PRETREE_len[x] = y; |
|
309 |
+ } |
|
310 |
+ BUILD_TABLE(PRETREE); |
|
311 |
+ |
|
312 |
+ for (x = first; x < last; ) { |
|
313 |
+ READ_HUFFSYM(PRETREE, z); |
|
314 |
+ if (z == 17) { |
|
315 |
+ /* code = 17, run of ([read 4 bits]+4) zeros */ |
|
316 |
+ READ_BITS(y, 4); y += 4; |
|
317 |
+ while (y--) lens[x++] = 0; |
|
318 |
+ } |
|
319 |
+ else if (z == 18) { |
|
320 |
+ /* code = 18, run of ([read 5 bits]+20) zeros */ |
|
321 |
+ READ_BITS(y, 5); y += 20; |
|
322 |
+ while (y--) lens[x++] = 0; |
|
323 |
+ } |
|
324 |
+ else if (z == 19) { |
|
325 |
+ /* code = 19, run of ([read 1 bit]+4) [read huffman symbol] */ |
|
326 |
+ READ_BITS(y, 1); y += 4; |
|
327 |
+ READ_HUFFSYM(PRETREE, z); |
|
328 |
+ z = lens[x] - z; if (z < 0) z += 17; |
|
329 |
+ while (y--) lens[x++] = z; |
|
330 |
+ } |
|
331 |
+ else { |
|
332 |
+ /* code = 0 to 16, delta current length entry */ |
|
333 |
+ z = lens[x] - z; if (z < 0) z += 17; |
|
334 |
+ lens[x++] = z; |
|
335 |
+ } |
|
336 |
+ } |
|
337 |
+ |
|
338 |
+ STORE_BITS; |
|
339 |
+ |
|
340 |
+ return MSPACK_ERR_OK; |
|
341 |
+} |
|
342 |
+ |
|
343 |
+/* LZX static data tables: |
|
344 |
+ * |
|
345 |
+ * LZX uses 'position slots' to represent match offsets. For every match, |
|
346 |
+ * a small 'position slot' number and a small offset from that slot are |
|
347 |
+ * encoded instead of one large offset. |
|
348 |
+ * |
|
349 |
+ * position_base[] is an index to the position slot bases |
|
350 |
+ * |
|
351 |
+ * extra_bits[] states how many bits of offset-from-base data is needed. |
|
352 |
+ */ |
|
353 |
+static unsigned int position_base[51]; |
|
354 |
+static unsigned char extra_bits[51]; |
|
355 |
+ |
|
356 |
+static void lzxd_static_init() { |
|
357 |
+ int i, j; |
|
358 |
+ |
|
359 |
+ for (i = 0, j = 0; i < 51; i += 2) { |
|
360 |
+ extra_bits[i] = j; /* 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7... */ |
|
361 |
+ extra_bits[i+1] = j; |
|
362 |
+ if ((i != 0) && (j < 17)) j++; /* 0,0,1,2,3,4...15,16,17,17,17,17... */ |
|
363 |
+ } |
|
364 |
+ |
|
365 |
+ for (i = 0, j = 0; i < 51; i++) { |
|
366 |
+ position_base[i] = j; /* 0,1,2,3,4,6,8,12,16,24,32,... */ |
|
367 |
+ j += 1 << extra_bits[i]; /* 1,1,1,1,2,2,4,4,8,8,16,16,32,32,... */ |
|
368 |
+ } |
|
369 |
+} |
|
370 |
+ |
|
371 |
+static void lzxd_reset_state(struct lzxd_stream *lzx) { |
|
372 |
+ int i; |
|
373 |
+ |
|
374 |
+ lzx->R0 = 1; |
|
375 |
+ lzx->R1 = 1; |
|
376 |
+ lzx->R2 = 1; |
|
377 |
+ lzx->header_read = 0; |
|
378 |
+ lzx->block_remaining = 0; |
|
379 |
+ lzx->block_type = LZX_BLOCKTYPE_INVALID; |
|
380 |
+ |
|
381 |
+ /* initialise tables to 0 (because deltas will be applied to them) */ |
|
382 |
+ for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) lzx->MAINTREE_len[i] = 0; |
|
383 |
+ for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) lzx->LENGTH_len[i] = 0; |
|
384 |
+} |
|
385 |
+ |
|
386 |
+/*-------- main LZX code --------*/ |
|
387 |
+ |
|
388 |
+struct lzxd_stream *lzxd_init(struct mspack_system *system, |
|
389 |
+ struct mspack_file *input, |
|
390 |
+ struct mspack_file *output, |
|
391 |
+ int window_bits, |
|
392 |
+ int reset_interval, |
|
393 |
+ int input_buffer_size, |
|
394 |
+ off_t output_length) |
|
395 |
+{ |
|
396 |
+ unsigned int window_size = 1 << window_bits; |
|
397 |
+ struct lzxd_stream *lzx; |
|
398 |
+ |
|
399 |
+ if (!system) return NULL; |
|
400 |
+ |
|
401 |
+ /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */ |
|
402 |
+ if (window_bits < 15 || window_bits > 21) return NULL; |
|
403 |
+ |
|
404 |
+ input_buffer_size = (input_buffer_size + 1) & -2; |
|
405 |
+ if (!input_buffer_size) return NULL; |
|
406 |
+ |
|
407 |
+ /* initialise static data */ |
|
408 |
+ lzxd_static_init(); |
|
409 |
+ |
|
410 |
+ /* allocate decompression state */ |
|
411 |
+ if (!(lzx = system->alloc(system, sizeof(struct lzxd_stream)))) { |
|
412 |
+ return NULL; |
|
413 |
+ } |
|
414 |
+ |
|
415 |
+ /* allocate decompression window and input buffer */ |
|
416 |
+ lzx->window = system->alloc(system, (size_t) window_size); |
|
417 |
+ lzx->inbuf = system->alloc(system, (size_t) input_buffer_size); |
|
418 |
+ if (!lzx->window || !lzx->inbuf) { |
|
419 |
+ system->free(lzx->window); |
|
420 |
+ system->free(lzx->inbuf); |
|
421 |
+ system->free(lzx); |
|
422 |
+ return NULL; |
|
423 |
+ } |
|
424 |
+ |
|
425 |
+ /* initialise decompression state */ |
|
426 |
+ lzx->sys = system; |
|
427 |
+ lzx->input = input; |
|
428 |
+ lzx->output = output; |
|
429 |
+ lzx->offset = 0; |
|
430 |
+ lzx->length = output_length; |
|
431 |
+ |
|
432 |
+ lzx->inbuf_size = input_buffer_size; |
|
433 |
+ lzx->window_size = 1 << window_bits; |
|
434 |
+ lzx->window_posn = 0; |
|
435 |
+ lzx->frame_posn = 0; |
|
436 |
+ lzx->frame = 0; |
|
437 |
+ lzx->reset_interval = reset_interval; |
|
438 |
+ lzx->intel_filesize = 0; |
|
439 |
+ lzx->intel_curpos = 0; |
|
440 |
+ |
|
441 |
+ /* window bits: 15 16 17 18 19 20 21 |
|
442 |
+ * position slots: 30 32 34 36 38 42 50 */ |
|
443 |
+ lzx->posn_slots = ((window_bits == 21) ? 50 : |
|
444 |
+ ((window_bits == 20) ? 42 : (window_bits << 1))); |
|
445 |
+ lzx->intel_started = 0; |
|
446 |
+ lzx->input_end = 0; |
|
447 |
+ |
|
448 |
+ lzx->error = MSPACK_ERR_OK; |
|
449 |
+ |
|
450 |
+ lzx->i_ptr = lzx->i_end = &lzx->inbuf[0]; |
|
451 |
+ lzx->o_ptr = lzx->o_end = &lzx->e8_buf[0]; |
|
452 |
+ lzx->bit_buffer = lzx->bits_left = 0; |
|
453 |
+ |
|
454 |
+ lzxd_reset_state(lzx); |
|
455 |
+ return lzx; |
|
456 |
+} |
|
457 |
+ |
|
458 |
+void lzxd_set_output_length(struct lzxd_stream *lzx, off_t out_bytes) { |
|
459 |
+ if (lzx) lzx->length = out_bytes; |
|
460 |
+} |
|
461 |
+ |
|
462 |
+int lzxd_decompress(struct lzxd_stream *lzx, off_t out_bytes) { |
|
463 |
+ /* bitstream reading and huffman variables */ |
|
464 |
+ register unsigned int bit_buffer; |
|
465 |
+ register int bits_left, i=0; |
|
466 |
+ register unsigned short sym; |
|
467 |
+ unsigned char *i_ptr, *i_end; |
|
468 |
+ |
|
469 |
+ int match_length, length_footer, extra, verbatim_bits, bytes_todo; |
|
470 |
+ int this_run, main_element, aligned_bits, j; |
|
471 |
+ unsigned char *window, *runsrc, *rundest, buf[12]; |
|
472 |
+ unsigned int frame_size=0, end_frame, match_offset, window_posn; |
|
473 |
+ unsigned int R0, R1, R2; |
|
474 |
+ |
|
475 |
+ /* easy answers */ |
|
476 |
+ if (!lzx || (out_bytes < 0)) return MSPACK_ERR_ARGS; |
|
477 |
+ if (lzx->error) return lzx->error; |
|
478 |
+ |
|
479 |
+ /* flush out any stored-up bytes before we begin */ |
|
480 |
+ i = lzx->o_end - lzx->o_ptr; |
|
481 |
+ if ((off_t) i > out_bytes) i = (int) out_bytes; |
|
482 |
+ if (i) { |
|
483 |
+ if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) { |
|
484 |
+ return lzx->error = MSPACK_ERR_WRITE; |
|
485 |
+ } |
|
486 |
+ lzx->o_ptr += i; |
|
487 |
+ lzx->offset += i; |
|
488 |
+ out_bytes -= i; |
|
489 |
+ } |
|
490 |
+ if (out_bytes == 0) return MSPACK_ERR_OK; |
|
491 |
+ |
|
492 |
+ /* restore local state */ |
|
493 |
+ RESTORE_BITS; |
|
494 |
+ window = lzx->window; |
|
495 |
+ window_posn = lzx->window_posn; |
|
496 |
+ R0 = lzx->R0; |
|
497 |
+ R1 = lzx->R1; |
|
498 |
+ R2 = lzx->R2; |
|
499 |
+ |
|
500 |
+ end_frame = (unsigned int)((lzx->offset + out_bytes) / LZX_FRAME_SIZE) + 1; |
|
501 |
+ |
|
502 |
+ while (lzx->frame < end_frame) { |
|
503 |
+ /* have we reached the reset interval? (if there is one?) */ |
|
504 |
+ if (lzx->reset_interval && ((lzx->frame % lzx->reset_interval) == 0)) { |
|
505 |
+ if (lzx->block_remaining) { |
|
506 |
+ D(("%d bytes remaining at reset interval", lzx->block_remaining)) |
|
507 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; |
|
508 |
+ } |
|
509 |
+ |
|
510 |
+ /* re-read the intel header and reset the huffman lengths */ |
|
511 |
+ lzxd_reset_state(lzx); |
|
512 |
+ } |
|
513 |
+ |
|
514 |
+ /* read header if necessary */ |
|
515 |
+ if (!lzx->header_read) { |
|
516 |
+ /* read 1 bit. if bit=0, intel filesize = 0. |
|
517 |
+ * if bit=1, read intel filesize (32 bits) */ |
|
518 |
+ j = 0; READ_BITS(i, 1); if (i) { READ_BITS(i, 16); READ_BITS(j, 16); } |
|
519 |
+ lzx->intel_filesize = (i << 16) | j; |
|
520 |
+ lzx->header_read = 1; |
|
521 |
+ } |
|
522 |
+ |
|
523 |
+ /* calculate size of frame: all frames are 32k except the final frame |
|
524 |
+ * which is 32kb or less. this can only be calculated when lzx->length |
|
525 |
+ * has been filled in. */ |
|
526 |
+ frame_size = LZX_FRAME_SIZE; |
|
527 |
+ if (lzx->length && (lzx->length - lzx->offset) < (off_t)frame_size) { |
|
528 |
+ frame_size = lzx->length - lzx->offset; |
|
529 |
+ } |
|
530 |
+ |
|
531 |
+ /* decode until one more frame is available */ |
|
532 |
+ bytes_todo = lzx->frame_posn + frame_size - window_posn; |
|
533 |
+ while (bytes_todo > 0) { |
|
534 |
+ /* initialise new block, if one is needed */ |
|
535 |
+ if (lzx->block_remaining == 0) { |
|
536 |
+ /* realign if previous block was an odd-sized UNCOMPRESSED block */ |
|
537 |
+ if ((lzx->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) && |
|
538 |
+ (lzx->block_length & 1)) |
|
539 |
+ { |
|
540 |
+ if (i_ptr == i_end) { |
|
541 |
+ if (lzxd_read_input(lzx)) return lzx->error; |
|
542 |
+ i_ptr = lzx->i_ptr; |
|
543 |
+ i_end = lzx->i_end; |
|
544 |
+ } |
|
545 |
+ i_ptr++; |
|
546 |
+ } |
|
547 |
+ |
|
548 |
+ /* read block type (3 bits) and block length (24 bits) */ |
|
549 |
+ READ_BITS(lzx->block_type, 3); |
|
550 |
+ READ_BITS(i, 16); READ_BITS(j, 8); |
|
551 |
+ lzx->block_remaining = lzx->block_length = (i << 8) | j; |
|
552 |
+ /*D(("new block t%d len %u", lzx->block_type, lzx->block_length))*/ |
|
553 |
+ |
|
554 |
+ /* read individual block headers */ |
|
555 |
+ switch (lzx->block_type) { |
|
556 |
+ case LZX_BLOCKTYPE_ALIGNED: |
|
557 |
+ /* read lengths of and build aligned huffman decoding tree */ |
|
558 |
+ for (i = 0; i < 8; i++) { READ_BITS(j, 3); lzx->ALIGNED_len[i] = j; } |
|
559 |
+ BUILD_TABLE(ALIGNED); |
|
560 |
+ /* no break -- rest of aligned header is same as verbatim */ |
|
561 |
+ case LZX_BLOCKTYPE_VERBATIM: |
|
562 |
+ /* read lengths of and build main huffman decoding tree */ |
|
563 |
+ READ_LENGTHS(MAINTREE, 0, 256); |
|
564 |
+ READ_LENGTHS(MAINTREE, 256, LZX_NUM_CHARS + (lzx->posn_slots << 3)); |
|
565 |
+ BUILD_TABLE(MAINTREE); |
|
566 |
+ /* if the literal 0xE8 is anywhere in the block... */ |
|
567 |
+ if (lzx->MAINTREE_len[0xE8] != 0) lzx->intel_started = 1; |
|
568 |
+ /* read lengths of and build lengths huffman decoding tree */ |
|
569 |
+ READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS); |
|
570 |
+ BUILD_TABLE(LENGTH); |
|
571 |
+ break; |
|
572 |
+ |
|
573 |
+ case LZX_BLOCKTYPE_UNCOMPRESSED: |
|
574 |
+ /* because we can't assume otherwise */ |
|
575 |
+ lzx->intel_started = 1; |
|
576 |
+ |
|
577 |
+ /* read 1-16 (not 0-15) bits to align to bytes */ |
|
578 |
+ ENSURE_BITS(16); |
|
579 |
+ if (bits_left > 16) i_ptr -= 2; |
|
580 |
+ bits_left = 0; bit_buffer = 0; |
|
581 |
+ |
|
582 |
+ /* read 12 bytes of stored R0 / R1 / R2 values */ |
|
583 |
+ for (rundest = &buf[0], i = 0; i < 12; i++) { |
|
584 |
+ if (i_ptr == i_end) { |
|
585 |
+ if (lzxd_read_input(lzx)) return lzx->error; |
|
586 |
+ i_ptr = lzx->i_ptr; |
|
587 |
+ i_end = lzx->i_end; |
|
588 |
+ } |
|
589 |
+ *rundest++ = *i_ptr++; |
|
590 |
+ } |
|
591 |
+ R0 = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); |
|
592 |
+ R1 = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); |
|
593 |
+ R2 = buf[8] | (buf[9] << 8) | (buf[10] << 16) | (buf[11] << 24); |
|
594 |
+ break; |
|
595 |
+ |
|
596 |
+ default: |
|
597 |
+ D(("bad block type")) |
|
598 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; |
|
599 |
+ } |
|
600 |
+ } |
|
601 |
+ |
|
602 |
+ /* decode more of the block: |
|
603 |
+ * run = min(what's available, what's needed) */ |
|
604 |
+ this_run = lzx->block_remaining; |
|
605 |
+ if (this_run > bytes_todo) this_run = bytes_todo; |
|
606 |
+ |
|
607 |
+ /* assume we decode exactly this_run bytes, for now */ |
|
608 |
+ bytes_todo -= this_run; |
|
609 |
+ lzx->block_remaining -= this_run; |
|
610 |
+ |
|
611 |
+ /* decode at least this_run bytes */ |
|
612 |
+ switch (lzx->block_type) { |
|
613 |
+ case LZX_BLOCKTYPE_VERBATIM: |
|
614 |
+ while (this_run > 0) { |
|
615 |
+ READ_HUFFSYM(MAINTREE, main_element); |
|
616 |
+ if (main_element < LZX_NUM_CHARS) { |
|
617 |
+ /* literal: 0 to LZX_NUM_CHARS-1 */ |
|
618 |
+ window[window_posn++] = main_element; |
|
619 |
+ this_run--; |
|
620 |
+ } |
|
621 |
+ else { |
|
622 |
+ /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ |
|
623 |
+ main_element -= LZX_NUM_CHARS; |
|
624 |
+ |
|
625 |
+ /* get match length */ |
|
626 |
+ match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; |
|
627 |
+ if (match_length == LZX_NUM_PRIMARY_LENGTHS) { |
|
628 |
+ READ_HUFFSYM(LENGTH, length_footer); |
|
629 |
+ match_length += length_footer; |
|
630 |
+ } |
|
631 |
+ match_length += LZX_MIN_MATCH; |
|
632 |
+ |
|
633 |
+ /* get match offset */ |
|
634 |
+ switch ((match_offset = (main_element >> 3))) { |
|
635 |
+ case 0: match_offset = R0; break; |
|
636 |
+ case 1: match_offset = R1; R1=R0; R0 = match_offset; break; |
|
637 |
+ case 2: match_offset = R2; R2=R0; R0 = match_offset; break; |
|
638 |
+ case 3: match_offset = 1; R2=R1; R1=R0; R0 = match_offset; break; |
|
639 |
+ default: |
|
640 |
+ extra = extra_bits[match_offset]; |
|
641 |
+ READ_BITS(verbatim_bits, extra); |
|
642 |
+ match_offset = position_base[match_offset] - 2 + verbatim_bits; |
|
643 |
+ R2 = R1; R1 = R0; R0 = match_offset; |
|
644 |
+ } |
|
645 |
+ |
|
646 |
+ if ((window_posn + match_length) > lzx->window_size) { |
|
647 |
+ D(("match ran over window wrap")) |
|
648 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; |
|
649 |
+ } |
|
650 |
+ |
|
651 |
+ /* copy match */ |
|
652 |
+ rundest = &window[window_posn]; |
|
653 |
+ i = match_length; |
|
654 |
+ /* does match offset wrap the window? */ |
|
655 |
+ if (match_offset > window_posn) { |
|
656 |
+ /* j = length from match offset to end of window */ |
|
657 |
+ j = match_offset - window_posn; |
|
658 |
+ if (j > (int) lzx->window_size) { |
|
659 |
+ D(("match offset beyond window boundaries")) |
|
660 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; |
|
661 |
+ } |
|
662 |
+ runsrc = &window[lzx->window_size - j]; |
|
663 |
+ if (j < i) { |
|
664 |
+ /* if match goes over the window edge, do two copy runs */ |
|
665 |
+ i -= j; while (j-- > 0) *rundest++ = *runsrc++; |
|
666 |
+ runsrc = window; |
|
667 |
+ } |
|
668 |
+ while (i-- > 0) *rundest++ = *runsrc++; |
|
669 |
+ } |
|
670 |
+ else { |
|
671 |
+ runsrc = rundest - match_offset; |
|
672 |
+ while (i-- > 0) *rundest++ = *runsrc++; |
|
673 |
+ } |
|
674 |
+ |
|
675 |
+ this_run -= match_length; |
|
676 |
+ window_posn += match_length; |
|
677 |
+ } |
|
678 |
+ } /* while (this_run > 0) */ |
|
679 |
+ break; |
|
680 |
+ |
|
681 |
+ case LZX_BLOCKTYPE_ALIGNED: |
|
682 |
+ while (this_run > 0) { |
|
683 |
+ READ_HUFFSYM(MAINTREE, main_element); |
|
684 |
+ if (main_element < LZX_NUM_CHARS) { |
|
685 |
+ /* literal: 0 to LZX_NUM_CHARS-1 */ |
|
686 |
+ window[window_posn++] = main_element; |
|
687 |
+ this_run--; |
|
688 |
+ } |
|
689 |
+ else { |
|
690 |
+ /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ |
|
691 |
+ main_element -= LZX_NUM_CHARS; |
|
692 |
+ |
|
693 |
+ /* get match length */ |
|
694 |
+ match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; |
|
695 |
+ if (match_length == LZX_NUM_PRIMARY_LENGTHS) { |
|
696 |
+ READ_HUFFSYM(LENGTH, length_footer); |
|
697 |
+ match_length += length_footer; |
|
698 |
+ } |
|
699 |
+ match_length += LZX_MIN_MATCH; |
|
700 |
+ |
|
701 |
+ /* get match offset */ |
|
702 |
+ switch ((match_offset = (main_element >> 3))) { |
|
703 |
+ case 0: match_offset = R0; break; |
|
704 |
+ case 1: match_offset = R1; R1 = R0; R0 = match_offset; break; |
|
705 |
+ case 2: match_offset = R2; R2 = R0; R0 = match_offset; break; |
|
706 |
+ default: |
|
707 |
+ extra = extra_bits[match_offset]; |
|
708 |
+ match_offset = position_base[match_offset] - 2; |
|
709 |
+ if (extra > 3) { |
|
710 |
+ /* verbatim and aligned bits */ |
|
711 |
+ extra -= 3; |
|
712 |
+ READ_BITS(verbatim_bits, extra); |
|
713 |
+ match_offset += (verbatim_bits << 3); |
|
714 |
+ READ_HUFFSYM(ALIGNED, aligned_bits); |
|
715 |
+ match_offset += aligned_bits; |
|
716 |
+ } |
|
717 |
+ else if (extra == 3) { |
|
718 |
+ /* aligned bits only */ |
|
719 |
+ READ_HUFFSYM(ALIGNED, aligned_bits); |
|
720 |
+ match_offset += aligned_bits; |
|
721 |
+ } |
|
722 |
+ else if (extra > 0) { /* extra==1, extra==2 */ |
|
723 |
+ /* verbatim bits only */ |
|
724 |
+ READ_BITS(verbatim_bits, extra); |
|
725 |
+ match_offset += verbatim_bits; |
|
726 |
+ } |
|
727 |
+ else /* extra == 0 */ { |
|
728 |
+ /* ??? not defined in LZX specification! */ |
|
729 |
+ match_offset = 1; |
|
730 |
+ } |
|
731 |
+ /* update repeated offset LRU queue */ |
|
732 |
+ R2 = R1; R1 = R0; R0 = match_offset; |
|
733 |
+ } |
|
734 |
+ |
|
735 |
+ if ((window_posn + match_length) > lzx->window_size) { |
|
736 |
+ D(("match ran over window wrap")) |
|
737 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; |
|
738 |
+ } |
|
739 |
+ |
|
740 |
+ /* copy match */ |
|
741 |
+ rundest = &window[window_posn]; |
|
742 |
+ i = match_length; |
|
743 |
+ /* does match offset wrap the window? */ |
|
744 |
+ if (match_offset > window_posn) { |
|
745 |
+ /* j = length from match offset to end of window */ |
|
746 |
+ j = match_offset - window_posn; |
|
747 |
+ if (j > (int) lzx->window_size) { |
|
748 |
+ D(("match offset beyond window boundaries")) |
|
749 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; |
|
750 |
+ } |
|
751 |
+ runsrc = &window[lzx->window_size - j]; |
|
752 |
+ if (j < i) { |
|
753 |
+ /* if match goes over the window edge, do two copy runs */ |
|
754 |
+ i -= j; while (j-- > 0) *rundest++ = *runsrc++; |
|
755 |
+ runsrc = window; |
|
756 |
+ } |
|
757 |
+ while (i-- > 0) *rundest++ = *runsrc++; |
|
758 |
+ } |
|
759 |
+ else { |
|
760 |
+ runsrc = rundest - match_offset; |
|
761 |
+ while (i-- > 0) *rundest++ = *runsrc++; |
|
762 |
+ } |
|
763 |
+ |
|
764 |
+ this_run -= match_length; |
|
765 |
+ window_posn += match_length; |
|
766 |
+ } |
|
767 |
+ } /* while (this_run > 0) */ |
|
768 |
+ break; |
|
769 |
+ |
|
770 |
+ case LZX_BLOCKTYPE_UNCOMPRESSED: |
|
771 |
+ /* as this_run is limited not to wrap a frame, this also means it |
|
772 |
+ * won't wrap the window (as the window is a multiple of 32k) */ |
|
773 |
+ rundest = &window[window_posn]; |
|
774 |
+ window_posn += this_run; |
|
775 |
+ while (this_run > 0) { |
|
776 |
+ if ((i = i_end - i_ptr)) { |
|
777 |
+ if (i > this_run) i = this_run; |
|
778 |
+ lzx->sys->copy(i_ptr, rundest, (size_t) i); |
|
779 |
+ rundest += i; |
|
780 |
+ i_ptr += i; |
|
781 |
+ this_run -= i; |
|
782 |
+ } |
|
783 |
+ else { |
|
784 |
+ if (lzxd_read_input(lzx)) return lzx->error; |
|
785 |
+ i_ptr = lzx->i_ptr; |
|
786 |
+ i_end = lzx->i_end; |
|
787 |
+ } |
|
788 |
+ } |
|
789 |
+ break; |
|
790 |
+ |
|
791 |
+ default: |
|
792 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; /* might as well */ |
|
793 |
+ } |
|
794 |
+ |
|
795 |
+ /* did the final match overrun our desired this_run length? */ |
|
796 |
+ if (this_run < 0) { |
|
797 |
+ if ((unsigned int)(-this_run) > lzx->block_remaining) { |
|
798 |
+ D(("overrun went past end of block by %d (%d remaining)", |
|
799 |
+ -this_run, lzx->block_remaining )) |
|
800 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; |
|
801 |
+ } |
|
802 |
+ lzx->block_remaining -= -this_run; |
|
803 |
+ } |
|
804 |
+ } /* while (bytes_todo > 0) */ |
|
805 |
+ |
|
806 |
+ /* streams don't extend over frame boundaries */ |
|
807 |
+ if ((window_posn - lzx->frame_posn) != frame_size) { |
|
808 |
+ D(("decode beyond output frame limits! %d != %d", |
|
809 |
+ window_posn - lzx->frame_posn, frame_size)) |
|
810 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; |
|
811 |
+ } |
|
812 |
+ |
|
813 |
+ /* re-align input bitstream */ |
|
814 |
+ if (bits_left > 0) ENSURE_BITS(16); |
|
815 |
+ if (bits_left & 15) REMOVE_BITS(bits_left & 15); |
|
816 |
+ |
|
817 |
+ /* check that we've used all of the previous frame first */ |
|
818 |
+ if (lzx->o_ptr != lzx->o_end) { |
|
819 |
+ D(("%d avail bytes, new %d frame", lzx->o_end-lzx->o_ptr, frame_size)) |
|
820 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; |
|
821 |
+ } |
|
822 |
+ |
|
823 |
+ /* does this intel block _really_ need decoding? */ |
|
824 |
+ if (lzx->intel_started && lzx->intel_filesize && |
|
825 |
+ (lzx->frame <= 32768) && (frame_size > 10)) |
|
826 |
+ { |
|
827 |
+ unsigned char *data = &lzx->e8_buf[0]; |
|
828 |
+ unsigned char *dataend = &lzx->e8_buf[frame_size - 10]; |
|
829 |
+ signed int curpos = lzx->intel_curpos; |
|
830 |
+ signed int filesize = lzx->intel_filesize; |
|
831 |
+ signed int abs_off, rel_off; |
|
832 |
+ |
|
833 |
+ /* copy e8 block to the e8 buffer and tweak if needed */ |
|
834 |
+ lzx->o_ptr = data; |
|
835 |
+ lzx->sys->copy(&lzx->window[lzx->frame_posn], data, frame_size); |
|
836 |
+ |
|
837 |
+ while (data < dataend) { |
|
838 |
+ if (*data++ != 0xE8) { curpos++; continue; } |
|
839 |
+ abs_off = data[0] | (data[1]<<8) | (data[2]<<16) | (data[3]<<24); |
|
840 |
+ if ((abs_off >= -curpos) && (abs_off < filesize)) { |
|
841 |
+ rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; |
|
842 |
+ data[0] = (unsigned char) rel_off; |
|
843 |
+ data[1] = (unsigned char) (rel_off >> 8); |
|
844 |
+ data[2] = (unsigned char) (rel_off >> 16); |
|
845 |
+ data[3] = (unsigned char) (rel_off >> 24); |
|
846 |
+ } |
|
847 |
+ data += 4; |
|
848 |
+ curpos += 5; |
|
849 |
+ } |
|
850 |
+ lzx->intel_curpos += frame_size; |
|
851 |
+ } |
|
852 |
+ else { |
|
853 |
+ lzx->o_ptr = &lzx->window[lzx->frame_posn]; |
|
854 |
+ if (lzx->intel_filesize) lzx->intel_curpos += frame_size; |
|
855 |
+ } |
|
856 |
+ lzx->o_end = &lzx->o_ptr[frame_size]; |
|
857 |
+ |
|
858 |
+ /* write a frame */ |
|
859 |
+ i = (out_bytes < (off_t)frame_size) ? (unsigned int)out_bytes : frame_size; |
|
860 |
+ if (lzx->sys->write(lzx->output, lzx->o_ptr, i) != i) { |
|
861 |
+ return lzx->error = MSPACK_ERR_WRITE; |
|
862 |
+ } |
|
863 |
+ lzx->o_ptr += i; |
|
864 |
+ lzx->offset += i; |
|
865 |
+ out_bytes -= i; |
|
866 |
+ |
|
867 |
+ /* advance frame start position */ |
|
868 |
+ lzx->frame_posn += frame_size; |
|
869 |
+ lzx->frame++; |
|
870 |
+ |
|
871 |
+ /* wrap window / frame position pointers */ |
|
872 |
+ if (window_posn == lzx->window_size) window_posn = 0; |
|
873 |
+ if (lzx->frame_posn == lzx->window_size) lzx->frame_posn = 0; |
|
874 |
+ |
|
875 |
+ } /* while (lzx->frame < end_frame) */ |
|
876 |
+ |
|
877 |
+ if (out_bytes) { |
|
878 |
+ D(("bytes left to output")) |
|
879 |
+ return lzx->error = MSPACK_ERR_DECRUNCH; |
|
880 |
+ } |
|
881 |
+ |
|
882 |
+ /* store local state */ |
|
883 |
+ STORE_BITS; |
|
884 |
+ lzx->window_posn = window_posn; |
|
885 |
+ lzx->R0 = R0; |
|
886 |
+ lzx->R1 = R1; |
|
887 |
+ lzx->R2 = R2; |
|
888 |
+ |
|
889 |
+ return MSPACK_ERR_OK; |
|
890 |
+} |
|
891 |
+ |
|
892 |
+void lzxd_free(struct lzxd_stream *lzx) { |
|
893 |
+ struct mspack_system *sys; |
|
894 |
+ if (lzx) { |
|
895 |
+ sys = lzx->sys; |
|
896 |
+ sys->free(lzx->inbuf); |
|
897 |
+ sys->free(lzx->window); |
|
898 |
+ sys->free(lzx); |
|
899 |
+ } |
|
900 |
+} |
0 | 901 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,1494 @@ |
0 |
+/* WARNING: This version also supports dopen for descriptor opening and |
|
1 |
+ * is not compatible with the original version. -- T. Kojm |
|
2 |
+ * |
|
3 |
+ * libmspack -- a library for working with Microsoft compression formats. |
|
4 |
+ * (C) 2003-2004 Stuart Caie <kyzer@4u.net> |
|
5 |
+ * |
|
6 |
+ * libmspack is free software; you can redistribute it and/or modify it under |
|
7 |
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
|
8 |
+ * |
|
9 |
+ * This program is distributed in the hope that it will be useful, |
|
10 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
+ * GNU Lesser General Public License for more details. |
|
13 |
+ * |
|
14 |
+ * You should have received a copy of the GNU Lesser General Public License |
|
15 |
+ * along with this program; if not, write to the Free Software |
|
16 |
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
|
17 |
+ */ |
|
18 |
+ |
|
19 |
+/** \mainpage |
|
20 |
+ * |
|
21 |
+ * \section intro Introduction |
|
22 |
+ * |
|
23 |
+ * libmspack is a library which provides compressors and decompressors, |
|
24 |
+ * archivers and dearchivers for Microsoft compression formats. |
|
25 |
+ * |
|
26 |
+ * \section formats Formats supported |
|
27 |
+ * |
|
28 |
+ * The following file formats are supported: |
|
29 |
+ * - SZDD files, which use LZSS compression |
|
30 |
+ * - KWAJ files, which use LZSS, LZSS+Huffman or deflate compression |
|
31 |
+ * - .HLP (MS Help) files, which use LZSS compression |
|
32 |
+ * - .CAB (MS Cabinet) files, which use deflate, LZX or Quantum compression |
|
33 |
+ * - .CHM (HTML Help) files, which use LZX compression |
|
34 |
+ * - .LIT (MS EBook) files, which use LZX compression and DES encryption |
|
35 |
+ * |
|
36 |
+ * To determine the capabilities of the library, and the binary |
|
37 |
+ * compatibility version of any particular compressor or decompressor, use |
|
38 |
+ * the mspack_version() function. The UNIX library interface version is |
|
39 |
+ * defined as the highest-versioned library component. |
|
40 |
+ * |
|
41 |
+ * \section starting Getting started |
|
42 |
+ * |
|
43 |
+ * The macro MSPACK_SYS_SELFTEST() should be used to ensure the library can |
|
44 |
+ * be used. In particular, it checks if the caller is using 32-bit file I/O |
|
45 |
+ * when the library is compiled for 64-bit file I/O and vice versa. |
|
46 |
+ * |
|
47 |
+ * If compiled normally, the library includes basic file I/O and memory |
|
48 |
+ * management functionality using the standard C library. This can be |
|
49 |
+ * customised and replaced entirely by creating a mspack_system structure. |
|
50 |
+ * |
|
51 |
+ * A compressor or decompressor for the required format must be |
|
52 |
+ * instantiated before it can be used. Each construction function takes |
|
53 |
+ * one parameter, which is either a pointer to a custom mspack_system |
|
54 |
+ * structure, or NULL to use the default. The instantiation returned, if |
|
55 |
+ * not NULL, contains function pointers (methods) to work with the given |
|
56 |
+ * file format. |
|
57 |
+ * |
|
58 |
+ * For compression: |
|
59 |
+ * - mspack_create_cab_compressor() creates a mscab_compressor |
|
60 |
+ * - mspack_create_chm_compressor() creates a mschm_compressor |
|
61 |
+ * - mspack_create_lit_compressor() creates a mslit_compressor |
|
62 |
+ * - mspack_create_hlp_compressor() creates a mshlp_compressor |
|
63 |
+ * - mspack_create_szdd_compressor() creates a msszdd_compressor |
|
64 |
+ * - mspack_create_kwaj_compressor() creates a mskwaj_compressor |
|
65 |
+ * |
|
66 |
+ * For decompression: |
|
67 |
+ * - mspack_create_cab_decompressor() creates a mscab_decompressor |
|
68 |
+ * - mspack_create_chm_decompressor() creates a mschm_decompressor |
|
69 |
+ * - mspack_create_lit_decompressor() creates a mslit_decompressor |
|
70 |
+ * - mspack_create_hlp_decompressor() creates a mshlp_decompressor |
|
71 |
+ * - mspack_create_szdd_decompressor() creates a msszdd_decompressor |
|
72 |
+ * - mspack_create_kwaj_decompressor() creates a mskwaj_decompressor |
|
73 |
+ * |
|
74 |
+ * Once finished working with a format, each kind of |
|
75 |
+ * compressor/decompressor has its own specific destructor: |
|
76 |
+ * - mspack_destroy_cab_compressor() |
|
77 |
+ * - mspack_destroy_cab_decompressor() |
|
78 |
+ * - mspack_destroy_chm_compressor() |
|
79 |
+ * - mspack_destroy_chm_decompressor() |
|
80 |
+ * - mspack_destroy_lit_compressor() |
|
81 |
+ * - mspack_destroy_lit_decompressor() |
|
82 |
+ * - mspack_destroy_hlp_compressor() |
|
83 |
+ * - mspack_destroy_hlp_decompressor() |
|
84 |
+ * - mspack_destroy_szdd_compressor() |
|
85 |
+ * - mspack_destroy_szdd_decompressor() |
|
86 |
+ * - mspack_destroy_kwaj_compressor() |
|
87 |
+ * - mspack_destroy_kwaj_decompressor() |
|
88 |
+ * |
|
89 |
+ * Destroying a compressor or decompressor does not destroy any objects, |
|
90 |
+ * structures or handles that have been created using that compressor or |
|
91 |
+ * decompressor. Ensure that everything created or opened is destroyed or |
|
92 |
+ * closed before compressor/decompressor is itself destroyed. |
|
93 |
+ * |
|
94 |
+ * \section errors Error codes |
|
95 |
+ * |
|
96 |
+ * All compressors and decompressors use the same set of error codes. Most |
|
97 |
+ * methods return an error code directly. For methods which do not |
|
98 |
+ * return error codes directly, the error code can be obtained with the |
|
99 |
+ * last_error() method. |
|
100 |
+ * |
|
101 |
+ * - #MSPACK_ERR_OK is used to indicate success. This error code is defined |
|
102 |
+ * as zero, all other code are non-zero. |
|
103 |
+ * - #MSPACK_ERR_ARGS indicates that a method was called with inappropriate |
|
104 |
+ * arguments. |
|
105 |
+ * - #MSPACK_ERR_OPEN indicates that mspack_system::open() failed. |
|
106 |
+ * - #MSPACK_ERR_READ indicates that mspack_system::read() failed. |
|
107 |
+ * - #MSPACK_ERR_WRITE indicates that mspack_system::write() failed. |
|
108 |
+ * - #MSPACK_ERR_SEEK indicates that mspack_system::seek() failed. |
|
109 |
+ * - #MSPACK_ERR_NOMEMORY indicates that mspack_system::alloc() failed. |
|
110 |
+ * - #MSPACK_ERR_SIGNATURE indicates that the file being read does not |
|
111 |
+ * have the correct "signature". It is probably not a valid file for |
|
112 |
+ * whatever format is being read. |
|
113 |
+ * - #MSPACK_ERR_DATAFORMAT indicates that the file being used or read |
|
114 |
+ * is corrupt. |
|
115 |
+ * - #MSPACK_ERR_CHECKSUM indicates that a data checksum has failed. |
|
116 |
+ * - #MSPACK_ERR_CRUNCH indicates an error occured during compression. |
|
117 |
+ * - #MSPACK_ERR_DECRUNCH indicates an error occured during decompression. |
|
118 |
+ */ |
|
119 |
+ |
|
120 |
+#ifndef LIB_MSPACK_H |
|
121 |
+#define LIB_MSPACK_H 1 |
|
122 |
+ |
|
123 |
+#ifdef __cplusplus |
|
124 |
+extern "C" { |
|
125 |
+#endif |
|
126 |
+ |
|
127 |
+#include <sys/types.h> |
|
128 |
+#include <unistd.h> |
|
129 |
+ |
|
130 |
+/** |
|
131 |
+ * System self-test function, to ensure both library and calling program |
|
132 |
+ * can use one another. |
|
133 |
+ * |
|
134 |
+ * A result of MSPACK_ERR_OK means the library and caller are |
|
135 |
+ * compatible. Any other result indicates that the library and caller are |
|
136 |
+ * not compatible and should not be used. In particular, a value of |
|
137 |
+ * MSPACK_ERR_SEEK means the library and caller use different off_t |
|
138 |
+ * datatypes. |
|
139 |
+ * |
|
140 |
+ * It should be used like so: |
|
141 |
+ * |
|
142 |
+ * @code |
|
143 |
+ * int selftest_result; |
|
144 |
+ * MSPACK_SYS_SELFTEST(selftest_result); |
|
145 |
+ * if (selftest_result != MSPACK_ERR_OK) { |
|
146 |
+ * fprintf(stderr, "incompatible with this build of libmspack\n"); |
|
147 |
+ * exit(0); |
|
148 |
+ * } |
|
149 |
+ * @endcode |
|
150 |
+ * |
|
151 |
+ * @param result an int variable to store the result of the self-test |
|
152 |
+ */ |
|
153 |
+#define MSPACK_SYS_SELFTEST(result) do { \ |
|
154 |
+ (result) = mspack_sys_selftest_internal(sizeof(off_t)); \ |
|
155 |
+} while (0) |
|
156 |
+ |
|
157 |
+/** Part of the MSPACK_SYS_SELFTEST() macro, must not be used directly. */ |
|
158 |
+extern int mspack_sys_selftest_internal(int); |
|
159 |
+ |
|
160 |
+/** |
|
161 |
+ * Enquire about the binary compatibility version of a specific interface in |
|
162 |
+ * the library. Currently, the following interfaces are defined: |
|
163 |
+ * |
|
164 |
+ * - #MSPACK_VER_LIBRARY: the overall library |
|
165 |
+ * - #MSPACK_VER_SYSTEM: the mspack_system interface |
|
166 |
+ * - #MSPACK_VER_MSCABD: the mscab_decompressor interface |
|
167 |
+ * - #MSPACK_VER_MSCABC: the mscab_compressor interface |
|
168 |
+ * - #MSPACK_VER_MSCHMD: the mschm_decompressor interface |
|
169 |
+ * - #MSPACK_VER_MSCHMC: the mschm_compressor interface |
|
170 |
+ * - #MSPACK_VER_MSLITD: the mslit_decompressor interface |
|
171 |
+ * - #MSPACK_VER_MSLITC: the mslit_compressor interface |
|
172 |
+ * - #MSPACK_VER_MSHLPD: the mshlp_decompressor interface |
|
173 |
+ * - #MSPACK_VER_MSHLPC: the mshlp_compressor interface |
|
174 |
+ * - #MSPACK_VER_MSSZDDD: the msszdd_decompressor interface |
|
175 |
+ * - #MSPACK_VER_MSSZDDC: the msszdd_compressor interface |
|
176 |
+ * - #MSPACK_VER_MSKWAJD: the mskwaj_decompressor interface |
|
177 |
+ * - #MSPACK_VER_MSKWAJC: the mskwaj_compressor interface |
|
178 |
+ * |
|
179 |
+ * The result of the function should be interpreted as follows: |
|
180 |
+ * - -1: this interface is completely unknown to the library |
|
181 |
+ * - 0: this interface is known, but non-functioning |
|
182 |
+ * - 1: this interface has all basic functionality |
|
183 |
+ * - 2, 3, ...: this interface has additional functionality, clearly marked |
|
184 |
+ * in the documentation as "version 2", "version 3" and so on. |
|
185 |
+ * |
|
186 |
+ * @param interface the interface to request current version of |
|
187 |
+ * @return the version of the requested interface |
|
188 |
+ */ |
|
189 |
+extern int mspack_version(int interface); |
|
190 |
+ |
|
191 |
+/** Pass to mspack_version() to get the overall library version */ |
|
192 |
+#define MSPACK_VER_LIBRARY (0) |
|
193 |
+/** Pass to mspack_version() to get the mspack_system version */ |
|
194 |
+#define MSPACK_VER_SYSTEM (1) |
|
195 |
+/** Pass to mspack_version() to get the mscab_decompressor version */ |
|
196 |
+#define MSPACK_VER_MSCABD (2) |
|
197 |
+/** Pass to mspack_version() to get the mscab_compressor version */ |
|
198 |
+#define MSPACK_VER_MSCABC (3) |
|
199 |
+/** Pass to mspack_version() to get the mschm_decompressor version */ |
|
200 |
+#define MSPACK_VER_MSCHMD (4) |
|
201 |
+/** Pass to mspack_version() to get the mschm_compressor version */ |
|
202 |
+#define MSPACK_VER_MSCHMC (5) |
|
203 |
+/** Pass to mspack_version() to get the mslit_decompressor version */ |
|
204 |
+#define MSPACK_VER_MSLITD (6) |
|
205 |
+/** Pass to mspack_version() to get the mslit_compressor version */ |
|
206 |
+#define MSPACK_VER_MSLITC (7) |
|
207 |
+/** Pass to mspack_version() to get the mshlp_decompressor version */ |
|
208 |
+#define MSPACK_VER_MSHLPD (8) |
|
209 |
+/** Pass to mspack_version() to get the mshlp_compressor version */ |
|
210 |
+#define MSPACK_VER_MSHLPC (9) |
|
211 |
+/** Pass to mspack_version() to get the msszdd_decompressor version */ |
|
212 |
+#define MSPACK_VER_MSSZDDD (10) |
|
213 |
+/** Pass to mspack_version() to get the msszdd_compressor version */ |
|
214 |
+#define MSPACK_VER_MSSZDDC (11) |
|
215 |
+/** Pass to mspack_version() to get the mskwaj_decompressor version */ |
|
216 |
+#define MSPACK_VER_MSKWAJD (12) |
|
217 |
+/** Pass to mspack_version() to get the mskwaj_compressor version */ |
|
218 |
+#define MSPACK_VER_MSKWAJC (13) |
|
219 |
+ |
|
220 |
+/* --- file I/O abstraction ------------------------------------------------ */ |
|
221 |
+ |
|
222 |
+/** |
|
223 |
+ * A structure which abstracts file I/O and memory management. |
|
224 |
+ * |
|
225 |
+ * The library always uses the mspack_system structure for interaction |
|
226 |
+ * with the file system and to allocate, free and copy all memory. It also |
|
227 |
+ * uses it to send literal messages to the library user. |
|
228 |
+ * |
|
229 |
+ * When the library is compiled normally, passing NULL to a compressor or |
|
230 |
+ * decompressor constructor will result in a default mspack_system being |
|
231 |
+ * used, where all methods are implemented with the standard C library. |
|
232 |
+ * However, all constructors support being given a custom created |
|
233 |
+ * mspack_system structure, with the library user's own methods. This |
|
234 |
+ * allows for more abstract interaction, such as reading and writing files |
|
235 |
+ * directly to memory, or from a network socket or pipe. |
|
236 |
+ * |
|
237 |
+ * Implementors of an mspack_system structure should read all |
|
238 |
+ * documentation entries for every structure member, and write methods |
|
239 |
+ * which conform to those standards. |
|
240 |
+ */ |
|
241 |
+struct mspack_system { |
|
242 |
+ /** |
|
243 |
+ * Opens a file for reading, writing, appending or updating. |
|
244 |
+ * |
|
245 |
+ * @param this a self-referential pointer to the mspack_system |
|
246 |
+ * structure whose open() method is being called. If |
|
247 |
+ * this pointer is required by close(), read(), write(), |
|
248 |
+ * seek() or tell(), it should be stored in the result |
|
249 |
+ * structure at this time. |
|
250 |
+ * @param filename the file to be opened. It is passed directly from the |
|
251 |
+ * library caller without being modified, so it is up to |
|
252 |
+ * the caller what this parameter actually represents. |
|
253 |
+ * @param mode one of #MSPACK_SYS_OPEN_READ (open an existing file |
|
254 |
+ * for reading), #MSPACK_SYS_OPEN_WRITE (open a new file |
|
255 |
+ * for writing), #MSPACK_SYS_OPEN_UPDATE (open an existing |
|
256 |
+ * file for reading/writing from the start of the file) or |
|
257 |
+ * #MSPACK_SYS_OPEN_APPEND (open an existing file for |
|
258 |
+ * reading/writing from the end of the file) |
|
259 |
+ * @return a pointer to a mspack_file structure. This structure officially |
|
260 |
+ * contains no members, its true contents are up to the |
|
261 |
+ * mspack_system implementor. It should contain whatever is needed |
|
262 |
+ * for other mspack_system methods to operate. |
|
263 |
+ * @see close(), read(), write(), seek(), tell(), message() |
|
264 |
+ */ |
|
265 |
+ struct mspack_file * (*open)(struct mspack_system *this, |
|
266 |
+ char *filename, |
|
267 |
+ int mode); |
|
268 |
+ |
|
269 |
+ struct mspack_file * (*dopen)(struct mspack_system *this, |
|
270 |
+ int desc, |
|
271 |
+ int mode); |
|
272 |
+ |
|
273 |
+ /** |
|
274 |
+ * Closes a previously opened file. If any memory was allocated for this |
|
275 |
+ * particular file handle, it should be freed at this time. |
|
276 |
+ * |
|
277 |
+ * @param file the file to close |
|
278 |
+ * @see open() |
|
279 |
+ */ |
|
280 |
+ void (*close)(struct mspack_file *file); |
|
281 |
+ |
|
282 |
+ /** |
|
283 |
+ * Reads a given number of bytes from an open file. |
|
284 |
+ * |
|
285 |
+ * @param file the file to read from |
|
286 |
+ * @param buffer the location where the read bytes should be stored |
|
287 |
+ * @param bytes the number of bytes to read from the file. |
|
288 |
+ * @return the number of bytes successfully read (this can be less than |
|
289 |
+ * the number requested), zero to mark the end of file, or less |
|
290 |
+ * than zero to indicate an error. |
|
291 |
+ * @see open(), write() |
|
292 |
+ */ |
|
293 |
+ int (*read)(struct mspack_file *file, |
|
294 |
+ void *buffer, |
|
295 |
+ int bytes); |
|
296 |
+ |
|
297 |
+ /** |
|
298 |
+ * Writes a given number of bytes to an open file. |
|
299 |
+ * |
|
300 |
+ * @param file the file to write to |
|
301 |
+ * @param buffer the location where the written bytes should be read from |
|
302 |
+ * @param bytes the number of bytes to write to the file. |
|
303 |
+ * @return the number of bytes successfully written, this can be less |
|
304 |
+ * than the number requested. Zero or less can indicate an error |
|
305 |
+ * where no bytes at all could be written. All cases where less |
|
306 |
+ * bytes were written than requested are considered by the library |
|
307 |
+ * to be an error. |
|
308 |
+ * @see open(), read() |
|
309 |
+ */ |
|
310 |
+ int (*write)(struct mspack_file *file, |
|
311 |
+ void *buffer, |
|
312 |
+ int bytes); |
|
313 |
+ |
|
314 |
+ /** |
|
315 |
+ * Seeks to a specific file offset within an open file. |
|
316 |
+ * |
|
317 |
+ * Sometimes the library needs to know the length of a file. It does |
|
318 |
+ * this by seeking to the end of the file with seek(file, 0, |
|
319 |
+ * MSPACK_SYS_SEEK_END), then calling tell(). Implementations may want |
|
320 |
+ * to make a special case for this. |
|
321 |
+ * |
|
322 |
+ * Due to the potentially varying 32/64 bit datatype off_t on some |
|
323 |
+ * architectures, the #MSPACK_SYS_SELFTEST macro MUST be used before |
|
324 |
+ * using the library. If not, the error caused by the library passing an |
|
325 |
+ * inappropriate stackframe to seek() is subtle and hard to trace. |
|
326 |
+ * |
|
327 |
+ * @param file the file to be seeked |
|
328 |
+ * @param offset an offset to seek, measured in bytes |
|
329 |
+ * @param mode one of #MSPACK_SYS_SEEK_START (the offset should be |
|
330 |
+ * measured from the start of the file), #MSPACK_SYS_SEEK_CUR |
|
331 |
+ * (the offset should be measured from the current file offset) |
|
332 |
+ * or #MSPACK_SYS_SEEK_END (the offset should be measured from |
|
333 |
+ * the end of the file) |
|
334 |
+ * @return zero for success, non-zero for an error |
|
335 |
+ * @see open(), tell() |
|
336 |
+ */ |
|
337 |
+ int (*seek)(struct mspack_file *file, |
|
338 |
+ off_t offset, |
|
339 |
+ int mode); |
|
340 |
+ |
|
341 |
+ /** |
|
342 |
+ * Returns the current file position (in bytes) of the given file. |
|
343 |
+ * |
|
344 |
+ * @param file the file whose file position is wanted |
|
345 |
+ * @return the current file position of the file |
|
346 |
+ * @see open(), seek() |
|
347 |
+ */ |
|
348 |
+ off_t (*tell)(struct mspack_file *file); |
|
349 |
+ |
|
350 |
+ /** |
|
351 |
+ * Used to send messages from the library to the user. |
|
352 |
+ * |
|
353 |
+ * Occasionally, the library generates warnings or other messages in |
|
354 |
+ * plain english to inform the human user. These are informational only |
|
355 |
+ * and can be ignored if not wanted. |
|
356 |
+ * |
|
357 |
+ * @param file may be a file handle returned from open() if this message |
|
358 |
+ * pertains to a specific open file, or NULL if not related to |
|
359 |
+ * a specific file. |
|
360 |
+ * @param format a printf() style format string. It does NOT include a |
|
361 |
+ * trailing newline. |
|
362 |
+ * @see open() |
|
363 |
+ */ |
|
364 |
+ void (*message)(struct mspack_file *file, |
|
365 |
+ char *format, |
|
366 |
+ ...); |
|
367 |
+ |
|
368 |
+ /** |
|
369 |
+ * Allocates memory. |
|
370 |
+ * |
|
371 |
+ * @param this a self-referential pointer to the mspack_system |
|
372 |
+ * structure whose alloc() method is being called. |
|
373 |
+ * @param bytes the number of bytes to allocate |
|
374 |
+ * @result a pointer to the requested number of bytes, or NULL if |
|
375 |
+ * not enough memory is available |
|
376 |
+ * @see free() |
|
377 |
+ */ |
|
378 |
+ void * (*alloc)(struct mspack_system *this, |
|
379 |
+ size_t bytes); |
|
380 |
+ |
|
381 |
+ /** |
|
382 |
+ * Frees memory. |
|
383 |
+ * |
|
384 |
+ * @param ptr the memory to be freed. |
|
385 |
+ * @see alloc() |
|
386 |
+ */ |
|
387 |
+ void (*free)(void *ptr); |
|
388 |
+ |
|
389 |
+ /** |
|
390 |
+ * Copies from one region of memory to another. |
|
391 |
+ * |
|
392 |
+ * The regions of memory are guaranteed not to overlap, are usually less |
|
393 |
+ * than 256 bytes, and may not be aligned. Please note that the source |
|
394 |
+ * parameter comes before the destination parameter, unlike the standard |
|
395 |
+ * C function memcpy(). |
|
396 |
+ * |
|
397 |
+ * @param src the region of memory to copy from |
|
398 |
+ * @param dest the region of memory to copy to |
|
399 |
+ * @param bytes the size of the memory region, in bytes |
|
400 |
+ */ |
|
401 |
+ void (*copy)(void *src, |
|
402 |
+ void *dest, |
|
403 |
+ size_t bytes); |
|
404 |
+ |
|
405 |
+ /** |
|
406 |
+ * A null pointer to mark the end of mspack_system. It must equal NULL. |
|
407 |
+ * |
|
408 |
+ * Should the mspack_system structure extend in the future, this NULL |
|
409 |
+ * will be seen, rather than have an invalid method pointer called. |
|
410 |
+ */ |
|
411 |
+ void *null_ptr; |
|
412 |
+}; |
|
413 |
+ |
|
414 |
+/** mspack_system::open() mode: open existing file for reading. */ |
|
415 |
+#define MSPACK_SYS_OPEN_READ (0) |
|
416 |
+/** mspack_system::open() mode: open new file for writing */ |
|
417 |
+#define MSPACK_SYS_OPEN_WRITE (1) |
|
418 |
+/** mspack_system::open() mode: open existing file for writing */ |
|
419 |
+#define MSPACK_SYS_OPEN_UPDATE (2) |
|
420 |
+/** mspack_system::open() mode: open existing file for writing */ |
|
421 |
+#define MSPACK_SYS_OPEN_APPEND (3) |
|
422 |
+ |
|
423 |
+/** mspack_system::seek() mode: seek relative to start of file */ |
|
424 |
+#define MSPACK_SYS_SEEK_START (0) |
|
425 |
+/** mspack_system::seek() mode: seek relative to current offset */ |
|
426 |
+#define MSPACK_SYS_SEEK_CUR (1) |
|
427 |
+/** mspack_system::seek() mode: seek relative to end of file */ |
|
428 |
+#define MSPACK_SYS_SEEK_END (2) |
|
429 |
+ |
|
430 |
+/** |
|
431 |
+ * A structure which represents an open file handle. The contents of this |
|
432 |
+ * structure are determined by the implementation of the |
|
433 |
+ * mspack_system::open() method. |
|
434 |
+ */ |
|
435 |
+struct mspack_file { |
|
436 |
+ int dummy; |
|
437 |
+}; |
|
438 |
+ |
|
439 |
+/* --- error codes --------------------------------------------------------- */ |
|
440 |
+ |
|
441 |
+/** Error code: no error */ |
|
442 |
+#define MSPACK_ERR_OK (0) |
|
443 |
+/** Error code: bad arguments to method */ |
|
444 |
+#define MSPACK_ERR_ARGS (1) |
|
445 |
+/** Error code: error opening file */ |
|
446 |
+#define MSPACK_ERR_OPEN (2) |
|
447 |
+/** Error code: error reading file */ |
|
448 |
+#define MSPACK_ERR_READ (3) |
|
449 |
+/** Error code: error writing file */ |
|
450 |
+#define MSPACK_ERR_WRITE (4) |
|
451 |
+/** Error code: seek error */ |
|
452 |
+#define MSPACK_ERR_SEEK (5) |
|
453 |
+/** Error code: out of memory */ |
|
454 |
+#define MSPACK_ERR_NOMEMORY (6) |
|
455 |
+/** Error code: bad "magic id" in file */ |
|
456 |
+#define MSPACK_ERR_SIGNATURE (7) |
|
457 |
+/** Error code: bad or corrupt file format */ |
|
458 |
+#define MSPACK_ERR_DATAFORMAT (8) |
|
459 |
+/** Error code: bad checksum or CRC */ |
|
460 |
+#define MSPACK_ERR_CHECKSUM (9) |
|
461 |
+/** Error code: error during compression */ |
|
462 |
+#define MSPACK_ERR_CRUNCH (10) |
|
463 |
+/** Error code: error during decompression */ |
|
464 |
+#define MSPACK_ERR_DECRUNCH (11) |
|
465 |
+ |
|
466 |
+/* --- functions available in library -------------------------------------- */ |
|
467 |
+ |
|
468 |
+/** Creates a new CAB compressor. |
|
469 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
470 |
+ * @return a #mscab_compressor or NULL |
|
471 |
+ */ |
|
472 |
+extern struct mscab_compressor * |
|
473 |
+ mspack_create_cab_compressor(struct mspack_system *sys); |
|
474 |
+ |
|
475 |
+/** Creates a new CAB decompressor. |
|
476 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
477 |
+ * @return a #mscab_decompressor or NULL |
|
478 |
+ */ |
|
479 |
+extern struct mscab_decompressor * |
|
480 |
+ mspack_create_cab_decompressor(struct mspack_system *sys); |
|
481 |
+ |
|
482 |
+/** Destroys an existing CAB compressor. |
|
483 |
+ * @param this the #mscab_compressor to destroy |
|
484 |
+ */ |
|
485 |
+extern void mspack_destroy_cab_compressor(struct mscab_compressor *this); |
|
486 |
+ |
|
487 |
+/** Destroys an existing CAB decompressor. |
|
488 |
+ * @param this the #mscab_decompressor to destroy |
|
489 |
+ */ |
|
490 |
+extern void mspack_destroy_cab_decompressor(struct mscab_decompressor *this); |
|
491 |
+ |
|
492 |
+ |
|
493 |
+/** Creates a new CHM compressor. |
|
494 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
495 |
+ * @return a #mschm_compressor or NULL |
|
496 |
+ */ |
|
497 |
+extern struct mschm_compressor * |
|
498 |
+ mspack_create_chm_compressor(struct mspack_system *sys); |
|
499 |
+ |
|
500 |
+/** Creates a new CHM decompressor. |
|
501 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
502 |
+ * @return a #mschm_decompressor or NULL |
|
503 |
+ */ |
|
504 |
+extern struct mschm_decompressor * |
|
505 |
+ mspack_create_chm_decompressor(struct mspack_system *sys); |
|
506 |
+ |
|
507 |
+/** Destroys an existing CHM compressor. |
|
508 |
+ * @param this the #mschm_compressor to destroy |
|
509 |
+ */ |
|
510 |
+extern void mspack_destroy_chm_compressor(struct mschm_compressor *this); |
|
511 |
+ |
|
512 |
+/** Destroys an existing CHM decompressor. |
|
513 |
+ * @param this the #mschm_decompressor to destroy |
|
514 |
+ */ |
|
515 |
+extern void mspack_destroy_chm_decompressor(struct mschm_decompressor *this); |
|
516 |
+ |
|
517 |
+ |
|
518 |
+/** Creates a new LIT compressor. |
|
519 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
520 |
+ * @return a #mslit_compressor or NULL |
|
521 |
+ */ |
|
522 |
+extern struct mslit_compressor * |
|
523 |
+ mspack_create_lit_compressor(struct mspack_system *sys); |
|
524 |
+ |
|
525 |
+/** Creates a new LIT decompressor. |
|
526 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
527 |
+ * @return a #mslit_decompressor or NULL |
|
528 |
+ */ |
|
529 |
+extern struct mslit_decompressor * |
|
530 |
+ mspack_create_lit_decompressor(struct mspack_system *sys); |
|
531 |
+ |
|
532 |
+/** Destroys an existing LIT compressor. |
|
533 |
+ * @param this the #mslit_compressor to destroy |
|
534 |
+ */ |
|
535 |
+extern void mspack_destroy_lit_compressor(struct mslit_compressor *this); |
|
536 |
+ |
|
537 |
+/** Destroys an existing LIT decompressor. |
|
538 |
+ * @param this the #mslit_decompressor to destroy |
|
539 |
+ */ |
|
540 |
+extern void mspack_destroy_lit_decompressor(struct mslit_decompressor *this); |
|
541 |
+ |
|
542 |
+ |
|
543 |
+/** Creates a new HLP compressor. |
|
544 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
545 |
+ * @return a #mshlp_compressor or NULL |
|
546 |
+ */ |
|
547 |
+extern struct mshlp_compressor * |
|
548 |
+ mspack_create_hlp_compressor(struct mspack_system *sys); |
|
549 |
+ |
|
550 |
+/** Creates a new HLP decompressor. |
|
551 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
552 |
+ * @return a #mshlp_decompressor or NULL |
|
553 |
+ */ |
|
554 |
+extern struct mshlp_decompressor * |
|
555 |
+ mspack_create_hlp_decompressor(struct mspack_system *sys); |
|
556 |
+ |
|
557 |
+/** Destroys an existing hlp compressor. |
|
558 |
+ * @param this the #mshlp_compressor to destroy |
|
559 |
+ */ |
|
560 |
+extern void mspack_destroy_hlp_compressor(struct mshlp_compressor *this); |
|
561 |
+ |
|
562 |
+/** Destroys an existing hlp decompressor. |
|
563 |
+ * @param this the #mshlp_decompressor to destroy |
|
564 |
+ */ |
|
565 |
+extern void mspack_destroy_hlp_decompressor(struct mshlp_decompressor *this); |
|
566 |
+ |
|
567 |
+ |
|
568 |
+/** Creates a new SZDD compressor. |
|
569 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
570 |
+ * @return a #msszdd_compressor or NULL |
|
571 |
+ */ |
|
572 |
+extern struct msszdd_compressor * |
|
573 |
+ mspack_create_szdd_compressor(struct mspack_system *sys); |
|
574 |
+ |
|
575 |
+/** Creates a new SZDD decompressor. |
|
576 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
577 |
+ * @return a #msszdd_decompressor or NULL |
|
578 |
+ */ |
|
579 |
+extern struct msszdd_decompressor * |
|
580 |
+ mspack_create_szdd_decompressor(struct mspack_system *sys); |
|
581 |
+ |
|
582 |
+/** Destroys an existing SZDD compressor. |
|
583 |
+ * @param this the #msszdd_compressor to destroy |
|
584 |
+ */ |
|
585 |
+extern void mspack_destroy_szdd_compressor(struct msszdd_compressor *this); |
|
586 |
+ |
|
587 |
+/** Destroys an existing SZDD decompressor. |
|
588 |
+ * @param this the #msszdd_decompressor to destroy |
|
589 |
+ */ |
|
590 |
+extern void mspack_destroy_szdd_decompressor(struct msszdd_decompressor *this); |
|
591 |
+ |
|
592 |
+ |
|
593 |
+/** Creates a new KWAJ compressor. |
|
594 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
595 |
+ * @return a #mskwaj_compressor or NULL |
|
596 |
+ */ |
|
597 |
+extern struct mskwaj_compressor * |
|
598 |
+ mspack_create_kwaj_compressor(struct mspack_system *sys); |
|
599 |
+ |
|
600 |
+/** Creates a new KWAJ decompressor. |
|
601 |
+ * @param sys a custom mspack_system structure, or NULL to use the default |
|
602 |
+ * @return a #mskwaj_decompressor or NULL |
|
603 |
+ */ |
|
604 |
+extern struct mskwaj_decompressor * |
|
605 |
+ mspack_create_kwaj_decompressor(struct mspack_system *sys); |
|
606 |
+ |
|
607 |
+/** Destroys an existing KWAJ compressor. |
|
608 |
+ * @param this the #mskwaj_compressor to destroy |
|
609 |
+ */ |
|
610 |
+extern void mspack_destroy_kwaj_compressor(struct mskwaj_compressor *this); |
|
611 |
+ |
|
612 |
+/** Destroys an existing KWAJ decompressor. |
|
613 |
+ * @param this the #mskwaj_decompressor to destroy |
|
614 |
+ */ |
|
615 |
+extern void mspack_destroy_kwaj_decompressor(struct mskwaj_decompressor *this); |
|
616 |
+ |
|
617 |
+ |
|
618 |
+/* --- support for .CAB (MS Cabinet) file format --------------------------- */ |
|
619 |
+ |
|
620 |
+/** |
|
621 |
+ * A structure which represents a single cabinet file. |
|
622 |
+ * |
|
623 |
+ * All fields are READ ONLY. |
|
624 |
+ * |
|
625 |
+ * If this cabinet is part of a merged cabinet set, the #files and #folders |
|
626 |
+ * fields are common to all cabinets in the set, and will be identical. |
|
627 |
+ * |
|
628 |
+ * @see mscab_decompressor::open(), mscab_decompressor::close(), |
|
629 |
+ * mscab_decompressor::search() |
|
630 |
+ */ |
|
631 |
+struct mscabd_cabinet { |
|
632 |
+ /** |
|
633 |
+ * The next cabinet in a chained list, if this cabinet was opened with |
|
634 |
+ * mscab_decompressor::search(). May be NULL to mark the end of the |
|
635 |
+ * list. |
|
636 |
+ */ |
|
637 |
+ struct mscabd_cabinet *next; |
|
638 |
+ |
|
639 |
+ /** |
|
640 |
+ * The filename of the cabinet. More correctly, the filename of the |
|
641 |
+ * physical file that the cabinet resides in. This is given by the |
|
642 |
+ * library user and may be in any format. |
|
643 |
+ */ |
|
644 |
+ char *filename; |
|
645 |
+ |
|
646 |
+ int desc; |
|
647 |
+ |
|
648 |
+ |
|
649 |
+ /** The file offset of cabinet within the physical file it resides in. */ |
|
650 |
+ off_t base_offset; |
|
651 |
+ |
|
652 |
+ /** The length of the cabinet file in bytes. */ |
|
653 |
+ unsigned int length; |
|
654 |
+ |
|
655 |
+ /** The previous cabinet in a cabinet set, or NULL. */ |
|
656 |
+ struct mscabd_cabinet *prevcab; |
|
657 |
+ |
|
658 |
+ /** The next cabinet in a cabinet set, or NULL. */ |
|
659 |
+ struct mscabd_cabinet *nextcab; |
|
660 |
+ |
|
661 |
+ /** The filename of the previous cabinet in a cabinet set, or NULL. */ |
|
662 |
+ char *prevname; |
|
663 |
+ |
|
664 |
+ /** The filename of the next cabinet in a cabinet set, or NULL. */ |
|
665 |
+ char *nextname; |
|
666 |
+ |
|
667 |
+ /** The name of the disk containing the previous cabinet in a cabinet |
|
668 |
+ * set, or NULL. |
|
669 |
+ */ |
|
670 |
+ char *previnfo; |
|
671 |
+ |
|
672 |
+ /** The name of the disk containing the next cabinet in a cabinet set, |
|
673 |
+ * or NULL. |
|
674 |
+ */ |
|
675 |
+ char *nextinfo; |
|
676 |
+ |
|
677 |
+ /** A list of all files in the cabinet or cabinet set. */ |
|
678 |
+ struct mscabd_file *files; |
|
679 |
+ |
|
680 |
+ /** A list of all folders in the cabinet or cabinet set. */ |
|
681 |
+ struct mscabd_folder *folders; |
|
682 |
+ |
|
683 |
+ /** |
|
684 |
+ * The set ID of the cabinet. All cabinets in the same set should have |
|
685 |
+ * the same set ID. |
|
686 |
+ */ |
|
687 |
+ unsigned short set_id; |
|
688 |
+ |
|
689 |
+ /** |
|
690 |
+ * The index number of the cabinet within the set. Numbering should |
|
691 |
+ * start from 0 for the first cabinet in the set, and increment by 1 for |
|
692 |
+ * each following cabinet. |
|
693 |
+ */ |
|
694 |
+ unsigned short set_index; |
|
695 |
+ |
|
696 |
+ /** |
|
697 |
+ * The number of bytes reserved in the header area of the cabinet. |
|
698 |
+ * |
|
699 |
+ * If this is non-zero and flags has MSCAB_HDR_RESV set, this data can |
|
700 |
+ * be read by the calling application. It is of the given length, |
|
701 |
+ * located at offset (base_offset + MSCAB_HDR_RESV_OFFSET) in the |
|
702 |
+ * cabinet file. |
|
703 |
+ * |
|
704 |
+ * @see flags |
|
705 |
+ */ |
|
706 |
+ unsigned short header_resv; |
|
707 |
+ |
|
708 |
+ /** |
|
709 |
+ * Header flags. |
|
710 |
+ * |
|
711 |
+ * - MSCAB_HDR_PREVCAB indicates the cabinet is part of a cabinet set, and |
|
712 |
+ * has a predecessor cabinet. |
|
713 |
+ * - MSCAB_HDR_NEXTCAB indicates the cabinet is part of a cabinet set, and |
|
714 |
+ * has a successor cabinet. |
|
715 |
+ * - MSCAB_HDR_RESV indicates the cabinet has reserved header space. |
|
716 |
+ * |
|
717 |
+ * @see prevname, previnfo, nextname, nextinfo, header_resv |
|
718 |
+ */ |
|
719 |
+ int flags; |
|
720 |
+}; |
|
721 |
+ |
|
722 |
+/** Offset from start of cabinet to the reserved header data (if present). */ |
|
723 |
+#define MSCAB_HDR_RESV_OFFSET (0x28) |
|
724 |
+ |
|
725 |
+/** Cabinet header flag: cabinet has a predecessor */ |
|
726 |
+#define MSCAB_HDR_PREVCAB (0x01) |
|
727 |
+/** Cabinet header flag: cabinet has a successor */ |
|
728 |
+#define MSCAB_HDR_NEXTCAB (0x02) |
|
729 |
+/** Cabinet header flag: cabinet has reserved header space */ |
|
730 |
+#define MSCAB_HDR_RESV (0x04) |
|
731 |
+ |
|
732 |
+/** |
|
733 |
+ * A structure which represents a single folder in a cabinet or cabinet set. |
|
734 |
+ * |
|
735 |
+ * All fields are READ ONLY. |
|
736 |
+ * |
|
737 |
+ * A folder is a single compressed stream of data. When uncompressed, it |
|
738 |
+ * holds the data of one or more files. A folder may be split across more |
|
739 |
+ * than one cabinet. |
|
740 |
+ */ |
|
741 |
+struct mscabd_folder { |
|
742 |
+ /** |
|
743 |
+ * A pointer to the next folder in this cabinet or cabinet set, or NULL |
|
744 |
+ * if this is the final folder. |
|
745 |
+ */ |
|
746 |
+ struct mscabd_folder *next; |
|
747 |
+ |
|
748 |
+ /** |
|
749 |
+ * The compression format used by this folder. |
|
750 |
+ * |
|
751 |
+ * The macro MSCABD_COMP_METHOD() should be used on this field to get |
|
752 |
+ * the algorithm used. The macro MSCABD_COMP_LEVEL() should be used to get |
|
753 |
+ * the "compression level". |
|
754 |
+ * |
|
755 |
+ * @see MSCABD_COMP_METHOD(), MSCABD_COMP_LEVEL() |
|
756 |
+ */ |
|
757 |
+ int comp_type; |
|
758 |
+ |
|
759 |
+ /** |
|
760 |
+ * The total number of data blocks used by this folder. This includes |
|
761 |
+ * data blocks present in other files, if this folder spans more than |
|
762 |
+ * one cabinet. |
|
763 |
+ */ |
|
764 |
+ unsigned int num_blocks; |
|
765 |
+}; |
|
766 |
+ |
|
767 |
+/** |
|
768 |
+ * Returns the compression method used by a folder. |
|
769 |
+ * |
|
770 |
+ * @param comp_type a mscabd_folder::comp_type value |
|
771 |
+ * @return one of #MSCAB_COMP_NONE, #MSCAB_COMP_MSZIP, #MSCAB_COMP_QUANTUM |
|
772 |
+ * or #MSCAB_COMP_LZX |
|
773 |
+ */ |
|
774 |
+#define MSCABD_COMP_METHOD(comp_type) ((comp_type) & 0x0F) |
|
775 |
+/** |
|
776 |
+ * Returns the compression level used by a folder. |
|
777 |
+ * |
|
778 |
+ * @param comp_type a mscabd_folder::comp_type value |
|
779 |
+ * @return the compression level. This is only defined by LZX and Quantum |
|
780 |
+ * compression |
|
781 |
+ */ |
|
782 |
+#define MSCABD_COMP_LEVEL(comp_type) (((comp_type) >> 8) & 0x1F) |
|
783 |
+ |
|
784 |
+/** Compression mode: no compression. */ |
|
785 |
+#define MSCAB_COMP_NONE (0) |
|
786 |
+/** Compression mode: MSZIP (deflate) compression. */ |
|
787 |
+#define MSCAB_COMP_MSZIP (1) |
|
788 |
+/** Compression mode: Quantum compression */ |
|
789 |
+#define MSCAB_COMP_QUANTUM (2) |
|
790 |
+/** Compression mode: LZX compression */ |
|
791 |
+#define MSCAB_COMP_LZX (3) |
|
792 |
+ |
|
793 |
+/** |
|
794 |
+ * A structure which represents a single file in a cabinet or cabinet set. |
|
795 |
+ * |
|
796 |
+ * All fields are READ ONLY. |
|
797 |
+ */ |
|
798 |
+struct mscabd_file { |
|
799 |
+ /** |
|
800 |
+ * The next file in the cabinet or cabinet set, or NULL if this is the |
|
801 |
+ * final file. |
|
802 |
+ */ |
|
803 |
+ struct mscabd_file *next; |
|
804 |
+ |
|
805 |
+ /** |
|
806 |
+ * The filename of the file. |
|
807 |
+ * |
|
808 |
+ * A null terminated string of up to 255 bytes in length, it may be in |
|
809 |
+ * either ISO-8859-1 or UTF8 format, depending on the file attributes. |
|
810 |
+ * |
|
811 |
+ * @see attribs |
|
812 |
+ */ |
|
813 |
+ char *filename; |
|
814 |
+ |
|
815 |
+ /** The uncompressed length of the file, in bytes. */ |
|
816 |
+ unsigned int length; |
|
817 |
+ |
|
818 |
+ /** |
|
819 |
+ * File attributes. |
|
820 |
+ * |
|
821 |
+ * The following attributes are defined: |
|
822 |
+ * - #MSCAB_ATTRIB_RDONLY indicates the file is write protected. |
|
823 |
+ * - #MSCAB_ATTRIB_HIDDEN indicates the file is hidden. |
|
824 |
+ * - #MSCAB_ATTRIB_SYSTEM indicates the file is a operating system file. |
|
825 |
+ * - #MSCAB_ATTRIB_ARCH indicates the file is "archived". |
|
826 |
+ * - #MSCAB_ATTRIB_EXEC indicates the file is an executable program. |
|
827 |
+ * - #MSCAB_ATTRIB_UTF_NAME indicates the filename is in UTF8 format rather |
|
828 |
+ * than ISO-8859-1. |
|
829 |
+ */ |
|
830 |
+ int attribs; |
|
831 |
+ |
|
832 |
+ /** File's last modified time, hour field. */ |
|
833 |
+ char time_h; |
|
834 |
+ /** File's last modified time, minute field. */ |
|
835 |
+ char time_m; |
|
836 |
+ /** File's last modified time, second field. */ |
|
837 |
+ char time_s; |
|
838 |
+ |
|
839 |
+ /** File's last modified date, day field. */ |
|
840 |
+ char date_d; |
|
841 |
+ /** File's last modified date, month field. */ |
|
842 |
+ char date_m; |
|
843 |
+ /** File's last modified date, year field. */ |
|
844 |
+ int date_y; |
|
845 |
+ |
|
846 |
+ /** A pointer to the folder that contains this file. */ |
|
847 |
+ struct mscabd_folder *folder; |
|
848 |
+ |
|
849 |
+ /** The uncompressed offset of this file in its folder. */ |
|
850 |
+ unsigned int offset; |
|
851 |
+}; |
|
852 |
+ |
|
853 |
+/** mscabd_file::attribs attribute: file is read-only. */ |
|
854 |
+#define MSCAB_ATTRIB_RDONLY (0x01) |
|
855 |
+/** mscabd_file::attribs attribute: file is hidden. */ |
|
856 |
+#define MSCAB_ATTRIB_HIDDEN (0x02) |
|
857 |
+/** mscabd_file::attribs attribute: file is an operating system file. */ |
|
858 |
+#define MSCAB_ATTRIB_SYSTEM (0x04) |
|
859 |
+/** mscabd_file::attribs attribute: file is "archived". */ |
|
860 |
+#define MSCAB_ATTRIB_ARCH (0x20) |
|
861 |
+/** mscabd_file::attribs attribute: file is an executable program. */ |
|
862 |
+#define MSCAB_ATTRIB_EXEC (0x40) |
|
863 |
+/** mscabd_file::attribs attribute: filename is UTF8, not ISO-8859-1. */ |
|
864 |
+#define MSCAB_ATTRIB_UTF_NAME (0x80) |
|
865 |
+ |
|
866 |
+/** mscab_decompressor::set_param() parameter: search buffer size. */ |
|
867 |
+#define MSCABD_PARAM_SEARCHBUF (0) |
|
868 |
+/** mscab_decompressor::set_param() parameter: repair MS-ZIP streams? */ |
|
869 |
+#define MSCABD_PARAM_FIXMSZIP (1) |
|
870 |
+/** mscab_decompressor::set_param() parameter: size of decompression buffer */ |
|
871 |
+#define MSCABD_PARAM_DECOMPBUF (2) |
|
872 |
+ |
|
873 |
+/** TODO */ |
|
874 |
+struct mscab_compressor { |
|
875 |
+ int dummy; |
|
876 |
+}; |
|
877 |
+ |
|
878 |
+/** |
|
879 |
+ * A decompressor for .CAB (Microsoft Cabinet) files |
|
880 |
+ * |
|
881 |
+ * All fields are READ ONLY. |
|
882 |
+ * |
|
883 |
+ * @see mspack_create_cab_decompressor(), mspack_destroy_cab_decompressor() |
|
884 |
+ */ |
|
885 |
+struct mscab_decompressor { |
|
886 |
+ /** |
|
887 |
+ * Opens a cabinet file and reads its contents. |
|
888 |
+ * |
|
889 |
+ * If the file opened is a valid cabinet file, all headers will be read |
|
890 |
+ * and a mscabd_cabinet structure will be returned, with a full list of |
|
891 |
+ * folders and files. |
|
892 |
+ * |
|
893 |
+ * In the case of an error occuring, NULL is returned and the error code |
|
894 |
+ * is available from last_error(). |
|
895 |
+ * |
|
896 |
+ * The filename pointer should be considered "in use" until close() is |
|
897 |
+ * called on the cabinet. |
|
898 |
+ * |
|
899 |
+ * @param this a self-referential pointer to the mscab_decompressor |
|
900 |
+ * instance being called |
|
901 |
+ * @param filename the filename of the cabinet file. This is passed |
|
902 |
+ * directly to mspack_system::open(). |
|
903 |
+ * @return a pointer to a mscabd_cabinet structure, or NULL on failure |
|
904 |
+ * @see close(), search(), last_error() |
|
905 |
+ */ |
|
906 |
+ struct mscabd_cabinet * (*open) (struct mscab_decompressor *this, |
|
907 |
+ char *filename); |
|
908 |
+ |
|
909 |
+ struct mscabd_cabinet * (*dopen) (struct mscab_decompressor *this, |
|
910 |
+ int desc); |
|
911 |
+ /** |
|
912 |
+ * Closes a previously opened cabinet or cabinet set. |
|
913 |
+ * |
|
914 |
+ * This closes a cabinet, all cabinets associated with it via the |
|
915 |
+ * mscabd_cabinet::next, mscabd_cabinet::prevcab and |
|
916 |
+ * mscabd_cabinet::nextcab pointers, and all folders and files. All |
|
917 |
+ * memory used by these entities is freed. |
|
918 |
+ * |
|
919 |
+ * The cabinet pointer is now invalid and cannot be used again. All |
|
920 |
+ * mscabd_folder and mscabd_file pointers from that cabinet or cabinet |
|
921 |
+ * set are also now invalid, and cannot be used again. |
|
922 |
+ * |
|
923 |
+ * If the cabinet pointer given was created using search(), it MUST be |
|
924 |
+ * the cabinet pointer returned by search() and not one of the later |
|
925 |
+ * cabinet pointers further along the mscabd_cabinet::next chain. |
|
926 |
+ |
|
927 |
+ * If extra cabinets have been added using append() or prepend(), these |
|
928 |
+ * will all be freed, even if the cabinet pointer given is not the first |
|
929 |
+ * cabinet in the set. Do NOT close() more than one cabinet in the set. |
|
930 |
+ * |
|
931 |
+ * The mscabd_cabinet::filename is not freed by the library, as it is |
|
932 |
+ * not allocated by the library. The caller should free this itself if |
|
933 |
+ * necessary, before it is lost forever. |
|
934 |
+ * |
|
935 |
+ * @param this a self-referential pointer to the mscab_decompressor |
|
936 |
+ * instance being called |
|
937 |
+ * @param cab the cabinet to close |
|
938 |
+ * @see open(), search(), append(), prepend() |
|
939 |
+ */ |
|
940 |
+ void (*close)(struct mscab_decompressor *this, |
|
941 |
+ struct mscabd_cabinet *cab); |
|
942 |
+ |
|
943 |
+ /** |
|
944 |
+ * Searches a regular file for embedded cabinets. |
|
945 |
+ * |
|
946 |
+ * This opens a normal file with the given filename and will search the |
|
947 |
+ * entire file for embedded cabinet files |
|
948 |
+ * |
|
949 |
+ * If any cabinets are found, the equivalent of open() is called on each |
|
950 |
+ * potential cabinet file at the offset it was found. All successfully |
|
951 |
+ * open()ed cabinets are kept in a list. |
|
952 |
+ * |
|
953 |
+ * The first cabinet found will be returned directly as the result of |
|
954 |
+ * this method. Any further cabinets found will be chained in a list |
|
955 |
+ * using the mscabd_cabinet::next field. |
|
956 |
+ * |
|
957 |
+ * In the case of an error occuring anywhere other than the simulated |
|
958 |
+ * open(), NULL is returned and the error code is available from |
|
959 |
+ * last_error(). |
|
960 |
+ * |
|
961 |
+ * If no error occurs, but no cabinets can be found in the file, NULL is |
|
962 |
+ * returned and last_error() returns MSPACK_ERR_OK. |
|
963 |
+ * |
|
964 |
+ * The filename pointer should be considered in use until close() is |
|
965 |
+ * called on the cabinet. |
|
966 |
+ * |
|
967 |
+ * close() should only be called on the result of search(), not on any |
|
968 |
+ * subsequent cabinets in the mscabd_cabinet::next chain. |
|
969 |
+ * |
|
970 |
+ * @param this a self-referential pointer to the mscab_decompressor |
|
971 |
+ * instance being called |
|
972 |
+ * @param filename the filename of the file to search for cabinets. This |
|
973 |
+ * is passed directly to mspack_system::open(). |
|
974 |
+ * @return a pointer to a mscabd_cabinet structure, or NULL |
|
975 |
+ * @see close(), open(), last_error() |
|
976 |
+ */ |
|
977 |
+ struct mscabd_cabinet * (*search) (struct mscab_decompressor *this, |
|
978 |
+ char *filename); |
|
979 |
+ |
|
980 |
+ struct mscabd_cabinet * (*dsearch) (struct mscab_decompressor *this, |
|
981 |
+ int desc); |
|
982 |
+ |
|
983 |
+ /** |
|
984 |
+ * Appends one mscabd_cabinet to another, forming or extending a cabinet |
|
985 |
+ * set. |
|
986 |
+ * |
|
987 |
+ * This will attempt to append one cabinet to another such that |
|
988 |
+ * <tt>(cab->nextcab == nextcab) && (nextcab->prevcab == cab)</tt> and |
|
989 |
+ * any folders split between the two cabinets are merged. |
|
990 |
+ * |
|
991 |
+ * The cabinets MUST be part of a cabinet set -- a cabinet set is a |
|
992 |
+ * cabinet that spans more than one physical cabinet file on disk -- and |
|
993 |
+ * must be appropriately matched. |
|
994 |
+ * |
|
995 |
+ * It can be determined if a cabinet has further parts to load by |
|
996 |
+ * examining the mscabd_cabinet::flags field: |
|
997 |
+ * |
|
998 |
+ * - if <tt>(flags & MSCAB_HDR_PREVCAB)</tt> is non-zero, there is a |
|
999 |
+ * predecessor cabinet to open() and prepend(). Its MS-DOS |
|
1000 |
+ * case-insensitive filename is mscabd_cabinet::prevname |
|
1001 |
+ * - if <tt>(flags & MSCAB_HDR_NEXTCAB)</tt> is non-zero, there is a |
|
1002 |
+ * successor cabinet to open() and append(). Its MS-DOS case-insensitive |
|
1003 |
+ * filename is mscabd_cabinet::nextname |
|
1004 |
+ * |
|
1005 |
+ * If the cabinets do not match, an error code will be returned. Neither |
|
1006 |
+ * cabinet has been altered, and both should be closed seperately. |
|
1007 |
+ * |
|
1008 |
+ * Files and folders in a cabinet set are a single entity. All cabinets |
|
1009 |
+ * in a set use the same file list, which is updated as cabinets in the |
|
1010 |
+ * set are added. All pointers to mscabd_folder and mscabd_file |
|
1011 |
+ * structures in either cabinet must be discarded and re-obtained after |
|
1012 |
+ * merging. |
|
1013 |
+ * |
|
1014 |
+ * @param this a self-referential pointer to the mscab_decompressor |
|
1015 |
+ * instance being called |
|
1016 |
+ * @param cab the cabinet which will be appended to, |
|
1017 |
+ * predecessor of nextcab |
|
1018 |
+ * @param nextcab the cabinet which will be appended, |
|
1019 |
+ * successor of cab |
|
1020 |
+ * @return an error code, or MSPACK_ERR_OK if successful |
|
1021 |
+ * @see prepend(), open(), close() |
|
1022 |
+ */ |
|
1023 |
+ int (*append) (struct mscab_decompressor *this, |
|
1024 |
+ struct mscabd_cabinet *cab, |
|
1025 |
+ struct mscabd_cabinet *nextcab); |
|
1026 |
+ |
|
1027 |
+ /** |
|
1028 |
+ * Prepends one mscabd_cabinet to another, forming or extending a |
|
1029 |
+ * cabinet set. |
|
1030 |
+ * |
|
1031 |
+ * This will attempt to prepend one cabinet to another, such that |
|
1032 |
+ * <tt>(cab->prevcab == prevcab) && (prevcab->nextcab == cab)</tt>. In |
|
1033 |
+ * all other respects, it is identical to append(). See append() for the |
|
1034 |
+ * full documentation. |
|
1035 |
+ * |
|
1036 |
+ * @param this a self-referential pointer to the mscab_decompressor |
|
1037 |
+ * instance being called |
|
1038 |
+ * @param cab the cabinet which will be prepended to, |
|
1039 |
+ * successor of prevcab |
|
1040 |
+ * @param prevcab the cabinet which will be prepended, |
|
1041 |
+ * predecessor of cab |
|
1042 |
+ * @return an error code, or MSPACK_ERR_OK if successful |
|
1043 |
+ * @see append(), open(), close() |
|
1044 |
+ */ |
|
1045 |
+ int (*prepend) (struct mscab_decompressor *this, |
|
1046 |
+ struct mscabd_cabinet *cab, |
|
1047 |
+ struct mscabd_cabinet *prevcab); |
|
1048 |
+ |
|
1049 |
+ /** |
|
1050 |
+ * Extracts a file from a cabinet or cabinet set. |
|
1051 |
+ * |
|
1052 |
+ * This extracts a compressed file in a cabinet and writes it to the given |
|
1053 |
+ * filename. |
|
1054 |
+ * |
|
1055 |
+ * The MS-DOS filename of the file, mscabd_file::filename, is NOT USED |
|
1056 |
+ * by extract(). The caller must examine this MS-DOS filename, copy and |
|
1057 |
+ * change it as necessary, create directories as necessary, and provide |
|
1058 |
+ * the correct filename as a parameter, which will be passed unchanged |
|
1059 |
+ * to the decompressor's mspack_system::open() |
|
1060 |
+ * |
|
1061 |
+ * If the file belongs to a split folder in a multi-part cabinet set, |
|
1062 |
+ * and not enough parts of the cabinet set have been loaded and appended |
|
1063 |
+ * or prepended, an error will be returned immediately. |
|
1064 |
+ * |
|
1065 |
+ * @param this a self-referential pointer to the mscab_decompressor |
|
1066 |
+ * instance being called |
|
1067 |
+ * @param file the file to be decompressed |
|
1068 |
+ * @param filename the filename of the file being written to |
|
1069 |
+ * @return an error code, or MSPACK_ERR_OK if successful |
|
1070 |
+ */ |
|
1071 |
+ int (*extract)(struct mscab_decompressor *this, |
|
1072 |
+ struct mscabd_file *file, |
|
1073 |
+ char *filename); |
|
1074 |
+ |
|
1075 |
+ /** |
|
1076 |
+ * Sets a CAB decompression engine parameter. |
|
1077 |
+ * |
|
1078 |
+ * The following parameters are defined: |
|
1079 |
+ * - #MSCABD_PARAM_SEARCHBUF: How many bytes should be allocated as a |
|
1080 |
+ * buffer when using search()? The minimum value is 4. The default |
|
1081 |
+ * value is 32768. |
|
1082 |
+ * - #MSCABD_PARAM_FIXMSZIP: If non-zero, extract() will ignore bad |
|
1083 |
+ * checksums and recover from decompression errors in MS-ZIP |
|
1084 |
+ * compressed folders. The default value is 0 (don't recover). |
|
1085 |
+ * - #MSCABD_PARAM_DECOMPBUF: How many bytes should be used as an input |
|
1086 |
+ * bit buffer by decompressors? The minimum value is 4. The default |
|
1087 |
+ * value is 4096. |
|
1088 |
+ * |
|
1089 |
+ * @param this a self-referential pointer to the mscab_decompressor |
|
1090 |
+ * instance being called |
|
1091 |
+ * @param param the parameter to set |
|
1092 |
+ * @param value the value to set the parameter to |
|
1093 |
+ * @return MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there |
|
1094 |
+ * is a problem with either parameter or value. |
|
1095 |
+ * @see search(), extract() |
|
1096 |
+ */ |
|
1097 |
+ int (*set_param)(struct mscab_decompressor *this, |
|
1098 |
+ int param, |
|
1099 |
+ int value); |
|
1100 |
+ |
|
1101 |
+ /** |
|
1102 |
+ * Returns the error code set by the most recently called method. |
|
1103 |
+ * |
|
1104 |
+ * This is useful for open() and search(), which do not return an error |
|
1105 |
+ * code directly. |
|
1106 |
+ * |
|
1107 |
+ * @param this a self-referential pointer to the mscab_decompressor |
|
1108 |
+ * instance being called |
|
1109 |
+ * @return the most recent error code |
|
1110 |
+ * @see open(), search() |
|
1111 |
+ */ |
|
1112 |
+ int (*last_error)(struct mscab_decompressor *); |
|
1113 |
+}; |
|
1114 |
+ |
|
1115 |
+/* --- support for .CHM (HTMLHelp) file format ----------------------------- */ |
|
1116 |
+ |
|
1117 |
+/** |
|
1118 |
+ * A structure which represents a section of a CHM helpfile. |
|
1119 |
+ * |
|
1120 |
+ * All fields are READ ONLY. |
|
1121 |
+ * |
|
1122 |
+ * Not used directly, but used as a generic base type for |
|
1123 |
+ * mschmd_sec_uncompressed and mschmd_sec_mscompressed. |
|
1124 |
+ */ |
|
1125 |
+struct mschmd_section { |
|
1126 |
+ /** A pointer to the CHM helpfile that contains this section. */ |
|
1127 |
+ struct mschmd_header *chm; |
|
1128 |
+ |
|
1129 |
+ /** |
|
1130 |
+ * The section ID. Either 0 for the uncompressed section |
|
1131 |
+ * mschmd_sec_uncompressed, or 1 for the LZX compressed section |
|
1132 |
+ * mschmd_sec_mscompressed. No other section IDs are known. |
|
1133 |
+ */ |
|
1134 |
+ unsigned int id; |
|
1135 |
+}; |
|
1136 |
+ |
|
1137 |
+/** |
|
1138 |
+ * A structure which represents the uncompressed section of a CHM helpfile. |
|
1139 |
+ * |
|
1140 |
+ * All fields are READ ONLY. |
|
1141 |
+ */ |
|
1142 |
+struct mschmd_sec_uncompressed { |
|
1143 |
+ /** Generic section data. */ |
|
1144 |
+ struct mschmd_section base; |
|
1145 |
+ |
|
1146 |
+ /** The file offset of where this section begins in the CHM helpfile. */ |
|
1147 |
+ off_t offset; |
|
1148 |
+}; |
|
1149 |
+ |
|
1150 |
+/** |
|
1151 |
+ * A structure which represents the compressed section of a CHM helpfile. |
|
1152 |
+ * |
|
1153 |
+ * All fields are READ ONLY. |
|
1154 |
+ */ |
|
1155 |
+struct mschmd_sec_mscompressed { |
|
1156 |
+ /** Generic section data. */ |
|
1157 |
+ struct mschmd_section base; |
|
1158 |
+ |
|
1159 |
+ /** A pointer to the meta-file which represents all LZX compressed data. */ |
|
1160 |
+ struct mschmd_file *content; |
|
1161 |
+ |
|
1162 |
+ /** A pointer to the file which contains the LZX control data. */ |
|
1163 |
+ struct mschmd_file *control; |
|
1164 |
+ |
|
1165 |
+ /** A pointer to the file which contains the LZX reset table. */ |
|
1166 |
+ struct mschmd_file *rtable; |
|
1167 |
+}; |
|
1168 |
+ |
|
1169 |
+/** |
|
1170 |
+ * A structure which represents a CHM helpfile. |
|
1171 |
+ * |
|
1172 |
+ * All fields are READ ONLY. |
|
1173 |
+ */ |
|
1174 |
+struct mschmd_header { |
|
1175 |
+ /** The version of the CHM file format used in this file. */ |
|
1176 |
+ unsigned int version; |
|
1177 |
+ |
|
1178 |
+ /** |
|
1179 |
+ * The "timestamp" of the CHM helpfile. |
|
1180 |
+ * |
|
1181 |
+ * It is the lower 32 bits of a 64-bit value representing the number of |
|
1182 |
+ * centiseconds since 1601-01-01 00:00:00 UTC, plus 42. It is not useful |
|
1183 |
+ * as a timestamp, but it is useful as a semi-unique ID. |
|
1184 |
+ */ |
|
1185 |
+ unsigned int timestamp; |
|
1186 |
+ |
|
1187 |
+ |
|
1188 |
+ /** |
|
1189 |
+ * The default Language and Country ID (LCID) of the user who ran the |
|
1190 |
+ * HTMLHelp Compiler. This is not the language of the CHM file itself. |
|
1191 |
+ */ |
|
1192 |
+ unsigned int language; |
|
1193 |
+ |
|
1194 |
+ /** |
|
1195 |
+ * The filename of the CHM helpfile. This is given by the library user |
|
1196 |
+ * and may be in any format. |
|
1197 |
+ */ |
|
1198 |
+ char *filename; |
|
1199 |
+ |
|
1200 |
+ /** The length of the CHM helpfile, in bytes. */ |
|
1201 |
+ off_t length; |
|
1202 |
+ |
|
1203 |
+ /** A list of all non-system files in the CHM helpfile. */ |
|
1204 |
+ struct mschmd_file *files; |
|
1205 |
+ |
|
1206 |
+ /** |
|
1207 |
+ * A list of all system files in the CHM helpfile. |
|
1208 |
+ * |
|
1209 |
+ * System files are files which begin with "::". They are meta-files |
|
1210 |
+ * generated by the CHM creation process. |
|
1211 |
+ */ |
|
1212 |
+ struct mschmd_file *sysfiles; |
|
1213 |
+ |
|
1214 |
+ /** The section 0 (uncompressed) data in this CHM helpfile. */ |
|
1215 |
+ struct mschmd_sec_uncompressed sec0; |
|
1216 |
+ |
|
1217 |
+ /** The section 1 (MSCompressed) data in this CHM helpfile. */ |
|
1218 |
+ struct mschmd_sec_mscompressed sec1; |
|
1219 |
+ |
|
1220 |
+ /** The file offset of the first PMGL/PMGI directory chunk. */ |
|
1221 |
+ off_t dir_offset; |
|
1222 |
+ |
|
1223 |
+ /** The number of PMGL/PMGI directory chunks in this CHM helpfile. */ |
|
1224 |
+ unsigned int num_chunks; |
|
1225 |
+ |
|
1226 |
+ /** The size of each PMGL/PMGI chunk, in bytes. */ |
|
1227 |
+ unsigned int chunk_size; |
|
1228 |
+ |
|
1229 |
+ /** The "density" of the quick-reference section in PMGL/PMGI chunks. */ |
|
1230 |
+ unsigned int density; |
|
1231 |
+ |
|
1232 |
+ /** The depth of the index tree. |
|
1233 |
+ * |
|
1234 |
+ * - if 1, there are no PMGI chunks, only PMGL chunks. |
|
1235 |
+ * - if 2, there is 1 PMGI chunk. All chunk indices point to PMGL chunks. |
|
1236 |
+ * - if 3, the root PMGI chunk points to secondary PMGI chunks, which in |
|
1237 |
+ * turn point to PMGL chunks. |
|
1238 |
+ * - and so on... |
|
1239 |
+ */ |
|
1240 |
+ unsigned int depth; |
|
1241 |
+ |
|
1242 |
+ /** |
|
1243 |
+ * The number of the root PGMI chunk. |
|
1244 |
+ * |
|
1245 |
+ * If there is no index in the CHM helpfile, this will be 0xFFFFFFFF. |
|
1246 |
+ */ |
|
1247 |
+ unsigned int index_root; |
|
1248 |
+}; |
|
1249 |
+ |
|
1250 |
+/** |
|
1251 |
+ * A structure which represents a file stored in a CHM helpfile. |
|
1252 |
+ * |
|
1253 |
+ * All fields are READ ONLY. |
|
1254 |
+ */ |
|
1255 |
+struct mschmd_file { |
|
1256 |
+ /** |
|
1257 |
+ * A pointer to the next file in the list, or NULL if this is the final |
|
1258 |
+ * file. |
|
1259 |
+ */ |
|
1260 |
+ struct mschmd_file *next; |
|
1261 |
+ |
|
1262 |
+ /** |
|
1263 |
+ * A pointer to the section that this file is located in. Indirectly, |
|
1264 |
+ * it also points to the CHM helpfile the file is located in. |
|
1265 |
+ */ |
|
1266 |
+ struct mschmd_section *section; |
|
1267 |
+ |
|
1268 |
+ /** The offset within the section data that this file is located at. */ |
|
1269 |
+ off_t offset; |
|
1270 |
+ |
|
1271 |
+ /** The length of this file, in bytes */ |
|
1272 |
+ off_t length; |
|
1273 |
+ |
|
1274 |
+ /** The filename of this file -- a null terminated string in UTF8. */ |
|
1275 |
+ char *filename; |
|
1276 |
+}; |
|
1277 |
+ |
|
1278 |
+/** TODO */ |
|
1279 |
+struct mschm_compressor { |
|
1280 |
+ int dummy; |
|
1281 |
+}; |
|
1282 |
+ |
|
1283 |
+/** |
|
1284 |
+ * A decompressor for .CHM (Microsoft HTMLHelp) files |
|
1285 |
+ * |
|
1286 |
+ * All fields are READ ONLY. |
|
1287 |
+ * |
|
1288 |
+ * @see mspack_create_chm_decompressor(), mspack_destroy_chm_decompressor() |
|
1289 |
+ */ |
|
1290 |
+struct mschm_decompressor { |
|
1291 |
+ /** |
|
1292 |
+ * Opens a CHM helpfile and reads its contents. |
|
1293 |
+ * |
|
1294 |
+ * If the file opened is a valid CHM helpfile, all headers will be read |
|
1295 |
+ * and a mschmd_header structure will be returned, with a full list of |
|
1296 |
+ * files. |
|
1297 |
+ * |
|
1298 |
+ * In the case of an error occuring, NULL is returned and the error code |
|
1299 |
+ * is available from last_error(). |
|
1300 |
+ * |
|
1301 |
+ * The filename pointer should be considered "in use" until close() is |
|
1302 |
+ * called on the CHM helpfile. |
|
1303 |
+ * |
|
1304 |
+ * @param this a self-referential pointer to the mschm_decompressor |
|
1305 |
+ * instance being called |
|
1306 |
+ * @param filename the filename of the CHM helpfile. This is passed |
|
1307 |
+ * directly to mspack_system::open(). |
|
1308 |
+ * @return a pointer to a mschmd_header structure, or NULL on failure |
|
1309 |
+ * @see close() |
|
1310 |
+ */ |
|
1311 |
+ struct mschmd_header *(*open)(struct mschm_decompressor *this, |
|
1312 |
+ char *filename); |
|
1313 |
+ |
|
1314 |
+ /** |
|
1315 |
+ * Closes a previously opened CHM helpfile. |
|
1316 |
+ * |
|
1317 |
+ * This closes a CHM helpfile, frees the mschmd_header and all |
|
1318 |
+ * mschmd_file structures associated with it (if any). This works on |
|
1319 |
+ * both helpfiles opened with open() and helpfiles opened with |
|
1320 |
+ * fast_open(). |
|
1321 |
+ * |
|
1322 |
+ * The CHM header pointer is now invalid and cannot be used again. All |
|
1323 |
+ * mschmd_file pointers referencing that CHM are also now invalid, and |
|
1324 |
+ * cannot be used again. |
|
1325 |
+ * |
|
1326 |
+ * @param this a self-referential pointer to the mschm_decompressor |
|
1327 |
+ * instance being called |
|
1328 |
+ * @param chm the CHM helpfile to close |
|
1329 |
+ * @see open(), fast_open() |
|
1330 |
+ */ |
|
1331 |
+ void (*close)(struct mschm_decompressor *this, |
|
1332 |
+ struct mschmd_header *chm); |
|
1333 |
+ |
|
1334 |
+ /** |
|
1335 |
+ * Extracts a file from a CHM helpfile. |
|
1336 |
+ * |
|
1337 |
+ * This extracts a file from a CHM helpfile and writes it to the given |
|
1338 |
+ * filename. The filename of the file, mscabd_file::filename, is not |
|
1339 |
+ * used by extract(), but can be used by the caller as a guide for |
|
1340 |
+ * constructing an appropriate filename. |
|
1341 |
+ * |
|
1342 |
+ * This method works both with files found in the mschmd_header::files |
|
1343 |
+ * and mschmd_header::sysfiles list and mschmd_file structures generated |
|
1344 |
+ * on the fly by fast_find(). |
|
1345 |
+ * |
|
1346 |
+ * @param this a self-referential pointer to the mscab_decompressor |
|
1347 |
+ * instance being called |
|
1348 |
+ * @param file the file to be decompressed |
|
1349 |
+ * @param filename the filename of the file being written to |
|
1350 |
+ * @return an error code, or MSPACK_ERR_OK if successful |
|
1351 |
+ */ |
|
1352 |
+ int (*extract)(struct mschm_decompressor *this, |
|
1353 |
+ struct mschmd_file *file, |
|
1354 |
+ char *filename); |
|
1355 |
+ |
|
1356 |
+ /** |
|
1357 |
+ * Returns the error code set by the most recently called method. |
|
1358 |
+ * |
|
1359 |
+ * This is useful for open() and fast_open(), which do not return an |
|
1360 |
+ * error code directly. |
|
1361 |
+ * |
|
1362 |
+ * @param this a self-referential pointer to the mschm_decompressor |
|
1363 |
+ * instance being called |
|
1364 |
+ * @return the most recent error code |
|
1365 |
+ * @see open(), search() |
|
1366 |
+ */ |
|
1367 |
+ int (*last_error)(struct mschm_decompressor *this); |
|
1368 |
+ |
|
1369 |
+ /** |
|
1370 |
+ * Opens a CHM helpfile quickly. |
|
1371 |
+ * |
|
1372 |
+ * If the file opened is a valid CHM helpfile, only essential headers |
|
1373 |
+ * will be read. A mschmd_header structure will be still be returned, as |
|
1374 |
+ * with open(), but the mschmd_header::files field will be NULL. No |
|
1375 |
+ * files details will be automatically read. The fast_find() method |
|
1376 |
+ * must be used to obtain file details. |
|
1377 |
+ * |
|
1378 |
+ * In the case of an error occuring, NULL is returned and the error code |
|
1379 |
+ * is available from last_error(). |
|
1380 |
+ * |
|
1381 |
+ * The filename pointer should be considered "in use" until close() is |
|
1382 |
+ * called on the CHM helpfile. |
|
1383 |
+ * |
|
1384 |
+ * @param this a self-referential pointer to the mschm_decompressor |
|
1385 |
+ * instance being called |
|
1386 |
+ * @param filename the filename of the CHM helpfile. This is passed |
|
1387 |
+ * directly to mspack_system::open(). |
|
1388 |
+ * @return a pointer to a mschmd_header structure, or NULL on failure |
|
1389 |
+ * @see open(), close(), fast_find(), extract() |
|
1390 |
+ */ |
|
1391 |
+ struct mschmd_header *(*fast_open)(struct mschm_decompressor *this, |
|
1392 |
+ char *filename); |
|
1393 |
+ |
|
1394 |
+ /** |
|
1395 |
+ * Finds file details quickly. |
|
1396 |
+ * |
|
1397 |
+ * Instead of reading all CHM helpfile headers and building a list of |
|
1398 |
+ * files, fast_open() and fast_find() are intended for finding file |
|
1399 |
+ * details only when they are needed. The CHM file format includes an |
|
1400 |
+ * on-disk file index to allow this. |
|
1401 |
+ * |
|
1402 |
+ * Given a case-sensitive filename, fast_find() will search the on-disk |
|
1403 |
+ * index for that file. |
|
1404 |
+ * |
|
1405 |
+ * If the file was found, the caller-provided mschmd_file structure will |
|
1406 |
+ * be filled out like so: |
|
1407 |
+ * - section: the correct value for the found file |
|
1408 |
+ * - offset: the correct value for the found file |
|
1409 |
+ * - length: the correct value for the found file |
|
1410 |
+ * - all other structure elements: NULL or 0 |
|
1411 |
+ * |
|
1412 |
+ * If the file was not found, MSPACK_ERR_OK will still be returned as the |
|
1413 |
+ * result, but the caller-provided structure will be filled out like so: |
|
1414 |
+ * - section: NULL |
|
1415 |
+ * - offset: 0 |
|
1416 |
+ * - length: 0 |
|
1417 |
+ * - all other structure elements: NULL or 0 |
|
1418 |
+ * |
|
1419 |
+ * This method is intended to be used in conjunction with CHM helpfiles |
|
1420 |
+ * opened with fast_open(), but it also works with helpfiles opened |
|
1421 |
+ * using the regular open(). |
|
1422 |
+ * |
|
1423 |
+ * @param this a self-referential pointer to the mschm_decompressor |
|
1424 |
+ * instance being called |
|
1425 |
+ * @param chm the CHM helpfile to search for the file |
|
1426 |
+ * @param filename the filename of the file to search for |
|
1427 |
+ * @param f_ptr a pointer to a caller-provded mschmd_file structure |
|
1428 |
+ * @param f_size <tt>sizeof(struct mschmd_file)</tt> |
|
1429 |
+ * @return MSPACK_ERR_OK, or an error code |
|
1430 |
+ * @see open(), close(), fast_find(), extract() |
|
1431 |
+ */ |
|
1432 |
+ int (*fast_find)(struct mschm_decompressor *this, |
|
1433 |
+ struct mschmd_header *chm, |
|
1434 |
+ char *filename, |
|
1435 |
+ struct mschmd_file *f_ptr, |
|
1436 |
+ int f_size); |
|
1437 |
+}; |
|
1438 |
+ |
|
1439 |
+/* --- support for .LIT (EBook) file format -------------------------------- */ |
|
1440 |
+ |
|
1441 |
+/** TODO */ |
|
1442 |
+struct mslit_compressor { |
|
1443 |
+ int dummy; |
|
1444 |
+}; |
|
1445 |
+ |
|
1446 |
+/** TODO */ |
|
1447 |
+struct mslit_decompressor { |
|
1448 |
+ int dummy; |
|
1449 |
+}; |
|
1450 |
+ |
|
1451 |
+ |
|
1452 |
+/* --- support for .HLP (MS Help) file format ------------------------------ */ |
|
1453 |
+ |
|
1454 |
+/** TODO */ |
|
1455 |
+struct mshlp_compressor { |
|
1456 |
+ int dummy; |
|
1457 |
+}; |
|
1458 |
+ |
|
1459 |
+/** TODO */ |
|
1460 |
+struct mshlp_decompressor { |
|
1461 |
+ int dummy; |
|
1462 |
+}; |
|
1463 |
+ |
|
1464 |
+ |
|
1465 |
+/* --- support for SZDD file format ---------------------------------------- */ |
|
1466 |
+ |
|
1467 |
+/** TODO */ |
|
1468 |
+struct msszdd_compressor { |
|
1469 |
+ int dummy; |
|
1470 |
+}; |
|
1471 |
+ |
|
1472 |
+/** TODO */ |
|
1473 |
+struct msszdd_decompressor { |
|
1474 |
+ int dummy; |
|
1475 |
+}; |
|
1476 |
+ |
|
1477 |
+/* --- support for KWAJ file format ---------------------------------------- */ |
|
1478 |
+ |
|
1479 |
+/** TODO */ |
|
1480 |
+struct mskwaj_compressor { |
|
1481 |
+ int dummy; |
|
1482 |
+}; |
|
1483 |
+ |
|
1484 |
+/** TODO */ |
|
1485 |
+struct mskwaj_decompressor { |
|
1486 |
+ int dummy; |
|
1487 |
+}; |
|
1488 |
+ |
|
1489 |
+#ifdef __cplusplus |
|
1490 |
+}; |
|
1491 |
+#endif |
|
1492 |
+ |
|
1493 |
+#endif |
0 | 1494 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,114 @@ |
0 |
+/* This file is part of libmspack. |
|
1 |
+ * (C) 2003-2004 Stuart Caie. |
|
2 |
+ * |
|
3 |
+ * The deflate method was created by Phil Katz. MSZIP is equivalent to the |
|
4 |
+ * deflate method. |
|
5 |
+ * |
|
6 |
+ * libmspack is free software; you can redistribute it and/or modify it under |
|
7 |
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
|
8 |
+ * |
|
9 |
+ * For further details, see the file COPYING.LIB distributed with libmspack |
|
10 |
+ */ |
|
11 |
+ |
|
12 |
+#ifndef MSPACK_MSZIP_H |
|
13 |
+#define MSPACK_MSZIP_H 1 |
|
14 |
+ |
|
15 |
+/* MSZIP (deflate) compression / (inflate) decompression definitions */ |
|
16 |
+ |
|
17 |
+#define MSZIP_FRAME_SIZE (32768) /* size of LZ history window */ |
|
18 |
+#define MSZIP_MAX_HUFFBITS (16) /* maximum huffman code length */ |
|
19 |
+#define MSZIP_LITERAL_MAXSYMBOLS (288) /* literal/length huffman tree */ |
|
20 |
+#define MSZIP_LITERAL_TABLEBITS (9) |
|
21 |
+#define MSZIP_DISTANCE_MAXSYMBOLS (32) /* distance huffman tree */ |
|
22 |
+#define MSZIP_DISTANCE_TABLEBITS (6) |
|
23 |
+ |
|
24 |
+/* if there are less direct lookup entries than symbols, the longer |
|
25 |
+ * code pointers will be <= maxsymbols. This must not happen, or we |
|
26 |
+ * will decode entries badly */ |
|
27 |
+#if (1 << MSZIP_LITERAL_TABLEBITS) < (MSZIP_LITERAL_MAXSYMBOLS * 2) |
|
28 |
+# define MSZIP_LITERAL_TABLESIZE (MSZIP_LITERAL_MAXSYMBOLS * 4) |
|
29 |
+#else |
|
30 |
+# define MSZIP_LITERAL_TABLESIZE ((1 << MSZIP_LITERAL_TABLEBITS) + \ |
|
31 |
+ (MSZIP_LITERAL_MAXSYMBOLS * 2)) |
|
32 |
+#endif |
|
33 |
+ |
|
34 |
+#if (1 << MSZIP_DISTANCE_TABLEBITS) < (MSZIP_DISTANCE_MAXSYMBOLS * 2) |
|
35 |
+# define MSZIP_DISTANCE_TABLESIZE (MSZIP_DISTANCE_MAXSYMBOLS * 4) |
|
36 |
+#else |
|
37 |
+# define MSZIP_DISTANCE_TABLESIZE ((1 << MSZIP_DISTANCE_TABLEBITS) + \ |
|
38 |
+ (MSZIP_DISTANCE_MAXSYMBOLS * 2)) |
|
39 |
+#endif |
|
40 |
+ |
|
41 |
+struct mszipd_stream { |
|
42 |
+ struct mspack_system *sys; /* I/O routines */ |
|
43 |
+ struct mspack_file *input; /* input file handle */ |
|
44 |
+ struct mspack_file *output; /* output file handle */ |
|
45 |
+ unsigned int window_posn; /* offset within window */ |
|
46 |
+ |
|
47 |
+ /* inflate() will call this whenever the window should be emptied. */ |
|
48 |
+ int (*flush_window)(struct mszipd_stream *, unsigned int); |
|
49 |
+ |
|
50 |
+ int error, repair_mode, bytes_output; |
|
51 |
+ |
|
52 |
+ /* I/O buffering */ |
|
53 |
+ unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end; |
|
54 |
+ unsigned int bit_buffer, bits_left, inbuf_size; |
|
55 |
+ |
|
56 |
+ |
|
57 |
+ /* huffman code lengths */ |
|
58 |
+ unsigned char LITERAL_len[MSZIP_LITERAL_MAXSYMBOLS]; |
|
59 |
+ unsigned char DISTANCE_len[MSZIP_DISTANCE_MAXSYMBOLS]; |
|
60 |
+ |
|
61 |
+ /* huffman decoding tables */ |
|
62 |
+ unsigned short LITERAL_table [MSZIP_LITERAL_TABLESIZE]; |
|
63 |
+ unsigned short DISTANCE_table[MSZIP_DISTANCE_TABLESIZE]; |
|
64 |
+ |
|
65 |
+ /* 32kb history window */ |
|
66 |
+ unsigned char window[MSZIP_FRAME_SIZE]; |
|
67 |
+}; |
|
68 |
+ |
|
69 |
+/* allocates MS-ZIP decompression stream for decoding the given stream. |
|
70 |
+ * |
|
71 |
+ * - uses system->alloc() to allocate memory |
|
72 |
+ * |
|
73 |
+ * - returns NULL if not enough memory |
|
74 |
+ * |
|
75 |
+ * - input_buffer_size is how many bytes to use as an input bitstream buffer |
|
76 |
+ * |
|
77 |
+ * - if repair_mode is non-zero, errors in decompression will be skipped |
|
78 |
+ * and 'holes' left will be filled with zero bytes. This allows at least |
|
79 |
+ * a partial recovery of erroneous data. |
|
80 |
+ */ |
|
81 |
+extern struct mszipd_stream *mszipd_init(struct mspack_system *system, |
|
82 |
+ struct mspack_file *input, |
|
83 |
+ struct mspack_file *output, |
|
84 |
+ int input_buffer_size, |
|
85 |
+ int repair_mode); |
|
86 |
+ |
|
87 |
+/* decompresses, or decompresses more of, an MS-ZIP stream. |
|
88 |
+ * |
|
89 |
+ * - out_bytes of data will be decompressed and the function will return |
|
90 |
+ * with an MSPACK_ERR_OK return code. |
|
91 |
+ * |
|
92 |
+ * - decompressing will stop as soon as out_bytes is reached. if the true |
|
93 |
+ * amount of bytes decoded spills over that amount, they will be kept for |
|
94 |
+ * a later invocation of mszipd_decompress(). |
|
95 |
+ * |
|
96 |
+ * - the output bytes will be passed to the system->write() function given in |
|
97 |
+ * mszipd_init(), using the output file handle given in mszipd_init(). More |
|
98 |
+ * than one call may be made to system->write() |
|
99 |
+ * |
|
100 |
+ * - MS-ZIP will read input bytes as necessary using the system->read() |
|
101 |
+ * function given in mszipd_init(), using the input file handle given in |
|
102 |
+ * mszipd_init(). This will continue until system->read() returns 0 bytes, |
|
103 |
+ * or an error. |
|
104 |
+ */ |
|
105 |
+extern int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes); |
|
106 |
+ |
|
107 |
+/* frees all stream associated with an MS-ZIP data stream |
|
108 |
+ * |
|
109 |
+ * - calls system->free() using the system pointer given in mszipd_init() |
|
110 |
+ */ |
|
111 |
+void mszipd_free(struct mszipd_stream *zip); |
|
112 |
+ |
|
113 |
+#endif |
0 | 114 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,652 @@ |
0 |
+/* This file is part of libmspack. |
|
1 |
+ * (C) 2003-2004 Stuart Caie. |
|
2 |
+ * |
|
3 |
+ * The deflate method was created by Phil Katz. MSZIP is equivalent to the |
|
4 |
+ * deflate method. |
|
5 |
+ * |
|
6 |
+ * libmspack is free software; you can redistribute it and/or modify it under |
|
7 |
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
|
8 |
+ * |
|
9 |
+ * For further details, see the file COPYING.LIB distributed with libmspack |
|
10 |
+ */ |
|
11 |
+ |
|
12 |
+/* MS-ZIP decompression implementation. */ |
|
13 |
+ |
|
14 |
+#if HAVE_CONFIG_H |
|
15 |
+#include "clamav-config.h" |
|
16 |
+#endif |
|
17 |
+ |
|
18 |
+#include <mspack.h> |
|
19 |
+#include <system.h> |
|
20 |
+#include <mszip.h> |
|
21 |
+ |
|
22 |
+/* match lengths for literal codes 257.. 285 */ |
|
23 |
+static const unsigned short lit_lengths[29] = { |
|
24 |
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, |
|
25 |
+ 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 |
|
26 |
+}; |
|
27 |
+ |
|
28 |
+/* match offsets for distance codes 0 .. 29 */ |
|
29 |
+static const unsigned short dist_offsets[30] = { |
|
30 |
+ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, |
|
31 |
+ 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577 |
|
32 |
+}; |
|
33 |
+ |
|
34 |
+/* extra bits required for literal codes 257.. 285 */ |
|
35 |
+static const unsigned char lit_extrabits[29] = { |
|
36 |
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, |
|
37 |
+ 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 |
|
38 |
+}; |
|
39 |
+ |
|
40 |
+/* extra bits required for distance codes 0 .. 29 */ |
|
41 |
+static const unsigned char dist_extrabits[30] = { |
|
42 |
+ 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, |
|
43 |
+ 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 |
|
44 |
+}; |
|
45 |
+ |
|
46 |
+/* the order of the bit length Huffman code lengths */ |
|
47 |
+static const unsigned char bitlen_order[19] = { |
|
48 |
+ 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 |
|
49 |
+}; |
|
50 |
+ |
|
51 |
+/* ANDing with bit_mask[n] masks the lower n bits */ |
|
52 |
+static const unsigned short bit_mask[17] = { |
|
53 |
+ 0x0000, 0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff, |
|
54 |
+ 0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff |
|
55 |
+}; |
|
56 |
+ |
|
57 |
+#define STORE_BITS do { \ |
|
58 |
+ zip->i_ptr = i_ptr; \ |
|
59 |
+ zip->i_end = i_end; \ |
|
60 |
+ zip->bit_buffer = bit_buffer; \ |
|
61 |
+ zip->bits_left = bits_left; \ |
|
62 |
+} while (0) |
|
63 |
+ |
|
64 |
+#define RESTORE_BITS do { \ |
|
65 |
+ i_ptr = zip->i_ptr; \ |
|
66 |
+ i_end = zip->i_end; \ |
|
67 |
+ bit_buffer = zip->bit_buffer; \ |
|
68 |
+ bits_left = zip->bits_left; \ |
|
69 |
+} while (0) |
|
70 |
+ |
|
71 |
+#define ENSURE_BITS(nbits) do { \ |
|
72 |
+ while (bits_left < (nbits)) { \ |
|
73 |
+ if (i_ptr >= i_end) { \ |
|
74 |
+ if (zipd_read_input(zip)) return zip->error; \ |
|
75 |
+ i_ptr = zip->i_ptr; \ |
|
76 |
+ i_end = zip->i_end; \ |
|
77 |
+ } \ |
|
78 |
+ bit_buffer |= *i_ptr++ << bits_left; bits_left += 8; \ |
|
79 |
+ } \ |
|
80 |
+} while (0) |
|
81 |
+ |
|
82 |
+#define PEEK_BITS(nbits) (bit_buffer & ((1<<(nbits))-1)) |
|
83 |
+#define PEEK_BITS_T(nbits) (bit_buffer & bit_mask[(nbits)]) |
|
84 |
+ |
|
85 |
+#define REMOVE_BITS(nbits) ((bit_buffer >>= (nbits)), (bits_left -= (nbits))) |
|
86 |
+ |
|
87 |
+#define READ_BITS(val, nbits) do { \ |
|
88 |
+ ENSURE_BITS(nbits); (val) = PEEK_BITS(nbits); REMOVE_BITS(nbits); \ |
|
89 |
+} while (0) |
|
90 |
+ |
|
91 |
+#define READ_BITS_T(val, nbits) do { \ |
|
92 |
+ ENSURE_BITS(nbits); (val) = PEEK_BITS_T(nbits); REMOVE_BITS(nbits); \ |
|
93 |
+} while (0) |
|
94 |
+ |
|
95 |
+static int zipd_read_input(struct mszipd_stream *zip) { |
|
96 |
+ int read = zip->sys->read(zip->input, &zip->inbuf[0], (int)zip->inbuf_size); |
|
97 |
+ if (read < 0) return zip->error = MSPACK_ERR_READ; |
|
98 |
+ zip->i_ptr = &zip->inbuf[0]; |
|
99 |
+ zip->i_end = &zip->inbuf[read]; |
|
100 |
+ |
|
101 |
+ return MSPACK_ERR_OK; |
|
102 |
+} |
|
103 |
+ |
|
104 |
+/* inflate() error codes */ |
|
105 |
+#define INF_ERR_BLOCKTYPE (-1) /* unknown block type */ |
|
106 |
+#define INF_ERR_COMPLEMENT (-2) /* block size complement mismatch */ |
|
107 |
+#define INF_ERR_FLUSH (-3) /* error from flush_window() callback */ |
|
108 |
+#define INF_ERR_BITBUF (-4) /* too many bits in bit buffer */ |
|
109 |
+#define INF_ERR_SYMLENS (-5) /* too many symbols in blocktype 2 header */ |
|
110 |
+#define INF_ERR_BITLENTBL (-6) /* failed to build bitlens huffman table */ |
|
111 |
+#define INF_ERR_LITERALTBL (-7) /* failed to build literals huffman table */ |
|
112 |
+#define INF_ERR_DISTANCETBL (-8) /* failed to build distance huffman table */ |
|
113 |
+#define INF_ERR_BITOVERRUN (-9) /* bitlen RLE code goes over table size */ |
|
114 |
+#define INF_ERR_BADBITLEN (-10) /* invalid bit-length code */ |
|
115 |
+#define INF_ERR_LITCODE (-11) /* out-of-range literal code */ |
|
116 |
+#define INF_ERR_DISTCODE (-12) /* out-of-range distance code */ |
|
117 |
+#define INF_ERR_DISTANCE (-13) /* somehow, distance is beyond 32k */ |
|
118 |
+#define INF_ERR_HUFFSYM (-14) /* out of bits decoding huffman symbol */ |
|
119 |
+ |
|
120 |
+/* make_decode_table(nsyms, nbits, length[], table[]) |
|
121 |
+ * |
|
122 |
+ * This function was coded by David Tritscher. It builds a fast huffman |
|
123 |
+ * decoding table out of just a canonical huffman code lengths table. |
|
124 |
+ * |
|
125 |
+ * NOTE: this is NOT identical to the make_decode_table() in lzxd.c. This |
|
126 |
+ * one reverses the quick-lookup bit pattern. Bits are read MSB to LSB in LZX, |
|
127 |
+ * but LSB to MSB in MSZIP. |
|
128 |
+ * |
|
129 |
+ * nsyms = total number of symbols in this huffman tree. |
|
130 |
+ * nbits = any symbols with a code length of nbits or less can be decoded |
|
131 |
+ * in one lookup of the table. |
|
132 |
+ * length = A table to get code lengths from [0 to nsyms-1] |
|
133 |
+ * table = The table to fill up with decoded symbols and pointers. |
|
134 |
+ * |
|
135 |
+ * Returns 0 for OK or 1 for error |
|
136 |
+ */ |
|
137 |
+static int make_decode_table(unsigned int nsyms, unsigned int nbits, |
|
138 |
+ unsigned char *length, unsigned short *table) |
|
139 |
+{ |
|
140 |
+ register unsigned int leaf, reverse, fill; |
|
141 |
+ register unsigned short sym, next_sym; |
|
142 |
+ register unsigned char bit_num; |
|
143 |
+ unsigned int pos = 0; /* the current position in the decode table */ |
|
144 |
+ unsigned int table_mask = 1 << nbits; |
|
145 |
+ unsigned int bit_mask = table_mask >> 1; /* don't do 0 length codes */ |
|
146 |
+ |
|
147 |
+ /* fill entries for codes short enough for a direct mapping */ |
|
148 |
+ for (bit_num = 1; bit_num <= nbits; bit_num++) { |
|
149 |
+ for (sym = 0; sym < nsyms; sym++) { |
|
150 |
+ if (length[sym] != bit_num) continue; |
|
151 |
+ |
|
152 |
+ /* reverse the significant bits */ |
|
153 |
+ fill = length[sym]; reverse = pos >> (nbits - fill); leaf = 0; |
|
154 |
+ do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); |
|
155 |
+ |
|
156 |
+ if((pos += bit_mask) > table_mask) return 1; /* table overrun */ |
|
157 |
+ |
|
158 |
+ /* fill all possible lookups of this symbol with the symbol itself */ |
|
159 |
+ fill = bit_mask; next_sym = 1 << bit_num; |
|
160 |
+ do { table[leaf] = sym; leaf += next_sym; } while (--fill); |
|
161 |
+ } |
|
162 |
+ bit_mask >>= 1; |
|
163 |
+ } |
|
164 |
+ |
|
165 |
+ /* exit with success if table is now complete */ |
|
166 |
+ if (pos == table_mask) return 0; |
|
167 |
+ |
|
168 |
+ /* mark all remaining table entries as unused */ |
|
169 |
+ for (sym = pos; sym < table_mask; sym++) { |
|
170 |
+ reverse = sym; leaf = 0; fill = nbits; |
|
171 |
+ do { leaf <<= 1; leaf |= reverse & 1; reverse >>= 1; } while (--fill); |
|
172 |
+ table[leaf] = 0xFFFF; |
|
173 |
+ } |
|
174 |
+ |
|
175 |
+ /* where should the longer codes be allocated from? */ |
|
176 |
+ next_sym = ((table_mask >> 1) < nsyms) ? nsyms : (table_mask >> 1); |
|
177 |
+ |
|
178 |
+ /* give ourselves room for codes to grow by up to 16 more bits. |
|
179 |
+ * codes now start at bit nbits+16 and end at (nbits+16-codelength) */ |
|
180 |
+ pos <<= 16; |
|
181 |
+ table_mask <<= 16; |
|
182 |
+ bit_mask = 1 << 15; |
|
183 |
+ |
|
184 |
+ for (bit_num = nbits+1; bit_num <= MSZIP_MAX_HUFFBITS; bit_num++) { |
|
185 |
+ for (sym = 0; sym < nsyms; sym++) { |
|
186 |
+ if (length[sym] != bit_num) continue; |
|
187 |
+ |
|
188 |
+ /* leaf = the first nbits of the code, reversed */ |
|
189 |
+ reverse = pos >> 16; leaf = 0; fill = nbits; |
|
190 |
+ do {leaf <<= 1; leaf |= reverse & 1; reverse >>= 1;} while (--fill); |
|
191 |
+ |
|
192 |
+ for (fill = 0; fill < (bit_num - nbits); fill++) { |
|
193 |
+ /* if this path hasn't been taken yet, 'allocate' two entries */ |
|
194 |
+ if (table[leaf] == 0xFFFF) { |
|
195 |
+ table[(next_sym << 1) ] = 0xFFFF; |
|
196 |
+ table[(next_sym << 1) + 1 ] = 0xFFFF; |
|
197 |
+ table[leaf] = next_sym++; |
|
198 |
+ } |
|
199 |
+ /* follow the path and select either left or right for next bit */ |
|
200 |
+ leaf = (table[leaf] << 1) | ((pos >> (15 - fill)) & 1); |
|
201 |
+ } |
|
202 |
+ table[leaf] = sym; |
|
203 |
+ |
|
204 |
+ if ((pos += bit_mask) > table_mask) return 1; /* table overflow */ |
|
205 |
+ } |
|
206 |
+ bit_mask >>= 1; |
|
207 |
+ } |
|
208 |
+ |
|
209 |
+ /* full table? */ |
|
210 |
+ return (pos != table_mask) ? 1 : 0; |
|
211 |
+} |
|
212 |
+ |
|
213 |
+/* READ_HUFFSYM(tablename, var) decodes one huffman symbol from the |
|
214 |
+ * bitstream using the stated table and puts it in var. |
|
215 |
+ */ |
|
216 |
+#define READ_HUFFSYM(tbl, var) do { \ |
|
217 |
+ /* huffman symbols can be up to 16 bits long */ \ |
|
218 |
+ ENSURE_BITS(MSZIP_MAX_HUFFBITS); \ |
|
219 |
+ /* immediate table lookup of [tablebits] bits of the code */ \ |
|
220 |
+ sym = zip->tbl##_table[PEEK_BITS(MSZIP_##tbl##_TABLEBITS)]; \ |
|
221 |
+ /* is the symbol is longer than [tablebits] bits? (i=node index) */ \ |
|
222 |
+ if (sym >= MSZIP_##tbl##_MAXSYMBOLS) { \ |
|
223 |
+ /* decode remaining bits by tree traversal */ \ |
|
224 |
+ i = MSZIP_##tbl##_TABLEBITS - 1; \ |
|
225 |
+ do { \ |
|
226 |
+ /* check next bit. error if we run out of bits before decode */ \ |
|
227 |
+ if (i++ > MSZIP_MAX_HUFFBITS) { \ |
|
228 |
+ D(("out of bits in huffman decode")) \ |
|
229 |
+ return INF_ERR_HUFFSYM; \ |
|
230 |
+ } \ |
|
231 |
+ /* double node index and add 0 (left branch) or 1 (right) */ \ |
|
232 |
+ sym = zip->tbl##_table[(sym << 1) | ((bit_buffer >> i) & 1)]; \ |
|
233 |
+ /* while we are still in node indicies, not decoded symbols */ \ |
|
234 |
+ } while (sym >= MSZIP_##tbl##_MAXSYMBOLS); \ |
|
235 |
+ } \ |
|
236 |
+ /* result */ \ |
|
237 |
+ (var) = sym; \ |
|
238 |
+ /* look up the code length of that symbol and discard those bits */ \ |
|
239 |
+ i = zip->tbl##_len[sym]; \ |
|
240 |
+ REMOVE_BITS(i); \ |
|
241 |
+} while (0) |
|
242 |
+ |
|
243 |
+static int zip_read_lens(struct mszipd_stream *zip) { |
|
244 |
+ /* for the bit buffer and huffman decoding */ |
|
245 |
+ register unsigned int bit_buffer; |
|
246 |
+ register int bits_left; |
|
247 |
+ unsigned char *i_ptr, *i_end; |
|
248 |
+ |
|
249 |
+ /* bitlen Huffman codes -- immediate lookup, 7 bit max code length */ |
|
250 |
+ unsigned short bl_table[(1 << 7)]; |
|
251 |
+ unsigned char bl_len[19]; |
|
252 |
+ |
|
253 |
+ unsigned char lens[MSZIP_LITERAL_MAXSYMBOLS + MSZIP_DISTANCE_MAXSYMBOLS]; |
|
254 |
+ unsigned int lit_codes, dist_codes, code, last_code=0, bitlen_codes, i, run; |
|
255 |
+ |
|
256 |
+ RESTORE_BITS; |
|
257 |
+ |
|
258 |
+ /* read the number of codes */ |
|
259 |
+ READ_BITS(lit_codes, 5); lit_codes += 257; |
|
260 |
+ READ_BITS(dist_codes, 5); dist_codes += 1; |
|
261 |
+ READ_BITS(bitlen_codes, 4); bitlen_codes += 4; |
|
262 |
+ if (lit_codes > MSZIP_LITERAL_MAXSYMBOLS) return INF_ERR_SYMLENS; |
|
263 |
+ if (dist_codes > MSZIP_DISTANCE_MAXSYMBOLS) return INF_ERR_SYMLENS; |
|
264 |
+ |
|
265 |
+ /* read in the bit lengths in their unusual order */ |
|
266 |
+ for (i = 0; i < bitlen_codes; i++) READ_BITS(bl_len[bitlen_order[i]], 3); |
|
267 |
+ while (i < 19) bl_len[bitlen_order[i++]] = 0; |
|
268 |
+ |
|
269 |
+ /* create decoding table with an immediate lookup */ |
|
270 |
+ if (make_decode_table(19, 7, &bl_len[0], &bl_table[0])) { |
|
271 |
+ return INF_ERR_BITLENTBL; |
|
272 |
+ } |
|
273 |
+ |
|
274 |
+ /* read literal / distance code lengths */ |
|
275 |
+ for (i = 0; i < (lit_codes + dist_codes); i++) { |
|
276 |
+ /* single-level huffman lookup */ |
|
277 |
+ ENSURE_BITS(7); |
|
278 |
+ code = bl_table[PEEK_BITS(7)]; |
|
279 |
+ REMOVE_BITS(bl_len[code]); |
|
280 |
+ |
|
281 |
+ if (code < 16) lens[i] = last_code = code; |
|
282 |
+ else { |
|
283 |
+ switch (code) { |
|
284 |
+ case 16: READ_BITS(run, 2); run += 3; code = last_code; break; |
|
285 |
+ case 17: READ_BITS(run, 3); run += 3; code = 0; break; |
|
286 |
+ case 18: READ_BITS(run, 7); run += 11; code = 0; break; |
|
287 |
+ default: D(("bad code!: %u", code)) return INF_ERR_BADBITLEN; |
|
288 |
+ } |
|
289 |
+ if ((i + run) > (lit_codes + dist_codes)) return INF_ERR_BITOVERRUN; |
|
290 |
+ while (run--) lens[i++] = code; |
|
291 |
+ i--; |
|
292 |
+ } |
|
293 |
+ } |
|
294 |
+ |
|
295 |
+ /* copy LITERAL code lengths and clear any remaining */ |
|
296 |
+ i = lit_codes; |
|
297 |
+ zip->sys->copy(&lens[0], &zip->LITERAL_len[0], i); |
|
298 |
+ while (i < MSZIP_LITERAL_MAXSYMBOLS) zip->LITERAL_len[i++] = 0; |
|
299 |
+ |
|
300 |
+ i = dist_codes; |
|
301 |
+ zip->sys->copy(&lens[lit_codes], &zip->DISTANCE_len[0], i); |
|
302 |
+ while (i < MSZIP_DISTANCE_MAXSYMBOLS) zip->DISTANCE_len[i++] = 0; |
|
303 |
+ |
|
304 |
+ STORE_BITS; |
|
305 |
+ return 0; |
|
306 |
+} |
|
307 |
+ |
|
308 |
+/* a clean implementation of RFC 1951 / inflate */ |
|
309 |
+static int inflate(struct mszipd_stream *zip) { |
|
310 |
+ unsigned int last_block, block_type, distance, length, this_run, i; |
|
311 |
+ |
|
312 |
+ /* for the bit buffer and huffman decoding */ |
|
313 |
+ register unsigned int bit_buffer; |
|
314 |
+ register int bits_left; |
|
315 |
+ register unsigned short sym; |
|
316 |
+ unsigned char *i_ptr, *i_end; |
|
317 |
+ |
|
318 |
+ RESTORE_BITS; |
|
319 |
+ |
|
320 |
+ do { |
|
321 |
+ /* read in last block bit */ |
|
322 |
+ READ_BITS(last_block, 1); |
|
323 |
+ |
|
324 |
+ /* read in block type */ |
|
325 |
+ READ_BITS(block_type, 2); |
|
326 |
+ D(("block_type=%u last_block=%u", block_type, last_block)) |
|
327 |
+ |
|
328 |
+ if (block_type == 0) { |
|
329 |
+ /* uncompressed block */ |
|
330 |
+ unsigned char lens_buf[4]; |
|
331 |
+ |
|
332 |
+ /* go to byte boundary */ |
|
333 |
+ i = bits_left & 7; REMOVE_BITS(i); |
|
334 |
+ |
|
335 |
+ /* read 4 bytes of data, emptying the bit-buffer if necessary */ |
|
336 |
+ for (i = 0; (bits_left >= 8); i++) { |
|
337 |
+ if (i == 4) return INF_ERR_BITBUF; |
|
338 |
+ lens_buf[i] = PEEK_BITS(8); |
|
339 |
+ REMOVE_BITS(8); |
|
340 |
+ } |
|
341 |
+ if (bits_left != 0) return INF_ERR_BITBUF; |
|
342 |
+ while (i < 4) { |
|
343 |
+ if (i_ptr >= i_end) { |
|
344 |
+ if (zipd_read_input(zip)) return zip->error; |
|
345 |
+ i_ptr = zip->i_ptr; |
|
346 |
+ i_end = zip->i_end; |
|
347 |
+ } |
|
348 |
+ lens_buf[i++] = *i_ptr++; |
|
349 |
+ } |
|
350 |
+ |
|
351 |
+ /* get the length and its complement */ |
|
352 |
+ length = lens_buf[0] | (lens_buf[1] << 8); |
|
353 |
+ i = lens_buf[2] | (lens_buf[3] << 8); |
|
354 |
+ if (length != (~i & 0xFFFF)) return INF_ERR_COMPLEMENT; |
|
355 |
+ |
|
356 |
+ /* read and copy the uncompressed data into the window */ |
|
357 |
+ while (length > 0) { |
|
358 |
+ if (i_ptr >= i_end) { |
|
359 |
+ if (zipd_read_input(zip)) return zip->error; |
|
360 |
+ i_ptr = zip->i_ptr; |
|
361 |
+ i_end = zip->i_end; |
|
362 |
+ } |
|
363 |
+ |
|
364 |
+ this_run = length; |
|
365 |
+ if (this_run > (unsigned int)(i_end - i_ptr)) this_run = i_end - i_ptr; |
|
366 |
+ if (this_run > (MSZIP_FRAME_SIZE - zip->window_posn)) |
|
367 |
+ this_run = MSZIP_FRAME_SIZE - zip->window_posn; |
|
368 |
+ |
|
369 |
+ zip->sys->copy(i_ptr, &zip->window[zip->window_posn], this_run); |
|
370 |
+ zip->window_posn += this_run; |
|
371 |
+ i_ptr += this_run; |
|
372 |
+ length -= this_run; |
|
373 |
+ |
|
374 |
+ if (zip->window_posn == MSZIP_FRAME_SIZE) { |
|
375 |
+ if (zip->flush_window(zip, MSZIP_FRAME_SIZE)) return INF_ERR_FLUSH; |
|
376 |
+ zip->window_posn = 0; |
|
377 |
+ } |
|
378 |
+ } |
|
379 |
+ } |
|
380 |
+ else if ((block_type == 1) || (block_type == 2)) { |
|
381 |
+ /* Huffman-compressed LZ77 block */ |
|
382 |
+ unsigned int window_posn, match_posn, code; |
|
383 |
+ |
|
384 |
+ if (block_type == 1) { |
|
385 |
+ /* block with fixed Huffman codes */ |
|
386 |
+ i = 0; |
|
387 |
+ while (i < 144) zip->LITERAL_len[i++] = 8; |
|
388 |
+ while (i < 256) zip->LITERAL_len[i++] = 9; |
|
389 |
+ while (i < 280) zip->LITERAL_len[i++] = 7; |
|
390 |
+ while (i < 288) zip->LITERAL_len[i++] = 8; |
|
391 |
+ for (i = 0; i < 32; i++) zip->DISTANCE_len[i] = 5; |
|
392 |
+ } |
|
393 |
+ else { |
|
394 |
+ /* block with dynamic Huffman codes */ |
|
395 |
+ STORE_BITS; |
|
396 |
+ if ((i = zip_read_lens(zip))) return i; |
|
397 |
+ RESTORE_BITS; |
|
398 |
+ } |
|
399 |
+ |
|
400 |
+ /* now huffman lengths are read for either kind of block, |
|
401 |
+ * create huffman decoding tables */ |
|
402 |
+ if (make_decode_table(MSZIP_LITERAL_MAXSYMBOLS, MSZIP_LITERAL_TABLEBITS, |
|
403 |
+ &zip->LITERAL_len[0], &zip->LITERAL_table[0])) |
|
404 |
+ { |
|
405 |
+ return INF_ERR_LITERALTBL; |
|
406 |
+ } |
|
407 |
+ |
|
408 |
+ if (make_decode_table(MSZIP_DISTANCE_MAXSYMBOLS,MSZIP_DISTANCE_TABLEBITS, |
|
409 |
+ &zip->DISTANCE_len[0], &zip->DISTANCE_table[0])) |
|
410 |
+ { |
|
411 |
+ return INF_ERR_DISTANCETBL; |
|
412 |
+ } |
|
413 |
+ |
|
414 |
+ /* decode forever until end of block code */ |
|
415 |
+ window_posn = zip->window_posn; |
|
416 |
+ while (1) { |
|
417 |
+ READ_HUFFSYM(LITERAL, code); |
|
418 |
+ if (code < 256) { |
|
419 |
+ zip->window[window_posn++] = (unsigned char) code; |
|
420 |
+ if (window_posn == MSZIP_FRAME_SIZE) { |
|
421 |
+ if (zip->flush_window(zip, MSZIP_FRAME_SIZE)) return INF_ERR_FLUSH; |
|
422 |
+ window_posn = 0; |
|
423 |
+ } |
|
424 |
+ } |
|
425 |
+ else if (code == 256) { |
|
426 |
+ /* END OF BLOCK CODE: loop break point */ |
|
427 |
+ break; |
|
428 |
+ } |
|
429 |
+ else { |
|
430 |
+ code -= 257; |
|
431 |
+ if (code > 29) return INF_ERR_LITCODE; |
|
432 |
+ READ_BITS_T(length, lit_extrabits[code]); |
|
433 |
+ length += lit_lengths[code]; |
|
434 |
+ |
|
435 |
+ READ_HUFFSYM(DISTANCE, code); |
|
436 |
+ if (code > 30) return INF_ERR_DISTCODE; |
|
437 |
+ READ_BITS_T(distance, dist_extrabits[code]); |
|
438 |
+ distance += dist_offsets[code]; |
|
439 |
+ |
|
440 |
+ /* match position is window position minus distance. If distance |
|
441 |
+ * is more than window position numerically, it must 'wrap |
|
442 |
+ * around' the frame size. */ |
|
443 |
+ match_posn = ((distance > window_posn) ? MSZIP_FRAME_SIZE : 0) |
|
444 |
+ + window_posn - distance; |
|
445 |
+ |
|
446 |
+ /* copy match */ |
|
447 |
+ if (length < 12) { |
|
448 |
+ /* short match, use slower loop but no loop setup code */ |
|
449 |
+ while (length--) { |
|
450 |
+ zip->window[window_posn++] = zip->window[match_posn++]; |
|
451 |
+ match_posn &= MSZIP_FRAME_SIZE - 1; |
|
452 |
+ |
|
453 |
+ if (window_posn == MSZIP_FRAME_SIZE) { |
|
454 |
+ if (zip->flush_window(zip, MSZIP_FRAME_SIZE)) |
|
455 |
+ return INF_ERR_FLUSH; |
|
456 |
+ window_posn = 0; |
|
457 |
+ } |
|
458 |
+ } |
|
459 |
+ } |
|
460 |
+ else { |
|
461 |
+ /* longer match, use faster loop but with setup expense */ |
|
462 |
+ unsigned char *runsrc, *rundest; |
|
463 |
+ do { |
|
464 |
+ this_run = length; |
|
465 |
+ if ((match_posn + this_run) > MSZIP_FRAME_SIZE) |
|
466 |
+ this_run = MSZIP_FRAME_SIZE - match_posn; |
|
467 |
+ if ((window_posn + this_run) > MSZIP_FRAME_SIZE) |
|
468 |
+ this_run = MSZIP_FRAME_SIZE - window_posn; |
|
469 |
+ |
|
470 |
+ rundest = &zip->window[window_posn]; window_posn += this_run; |
|
471 |
+ runsrc = &zip->window[match_posn]; match_posn += this_run; |
|
472 |
+ length -= this_run; |
|
473 |
+ while (this_run--) *rundest++ = *runsrc++; |
|
474 |
+ |
|
475 |
+ /* flush if necessary */ |
|
476 |
+ if (window_posn == MSZIP_FRAME_SIZE) { |
|
477 |
+ if (zip->flush_window(zip, MSZIP_FRAME_SIZE)) |
|
478 |
+ return INF_ERR_FLUSH; |
|
479 |
+ window_posn = 0; |
|
480 |
+ } |
|
481 |
+ if (match_posn == MSZIP_FRAME_SIZE) match_posn = 0; |
|
482 |
+ } while (length > 0); |
|
483 |
+ } |
|
484 |
+ |
|
485 |
+ } /* else (code >= 257) */ |
|
486 |
+ |
|
487 |
+ } /* while (forever) -- break point at 'code == 256' */ |
|
488 |
+ zip->window_posn = window_posn; |
|
489 |
+ } |
|
490 |
+ else { |
|
491 |
+ /* block_type == 3 -- bad block type */ |
|
492 |
+ return INF_ERR_BLOCKTYPE; |
|
493 |
+ } |
|
494 |
+ } while (!last_block); |
|
495 |
+ |
|
496 |
+ /* flush the remaining data */ |
|
497 |
+ if (zip->window_posn) { |
|
498 |
+ if (zip->flush_window(zip, zip->window_posn)) return INF_ERR_FLUSH; |
|
499 |
+ } |
|
500 |
+ STORE_BITS; |
|
501 |
+ |
|
502 |
+ /* return success */ |
|
503 |
+ return 0; |
|
504 |
+} |
|
505 |
+ |
|
506 |
+/* inflate() calls this whenever the window should be flushed. As |
|
507 |
+ * MSZIP only expands to the size of the window, the implementation used |
|
508 |
+ * simply keeps track of the amount of data flushed, and if more than 32k |
|
509 |
+ * is flushed, an error is raised. |
|
510 |
+ */ |
|
511 |
+static int mszipd_flush_window(struct mszipd_stream *zip, |
|
512 |
+ unsigned int data_flushed) |
|
513 |
+{ |
|
514 |
+ zip->bytes_output += data_flushed; |
|
515 |
+ if (zip->bytes_output > MSZIP_FRAME_SIZE) { |
|
516 |
+ D(("overflow: %u bytes flushed, total is now %u", |
|
517 |
+ data_flushed, zip->bytes_output)) |
|
518 |
+ return 1; |
|
519 |
+ } |
|
520 |
+ return 0; |
|
521 |
+} |
|
522 |
+ |
|
523 |
+struct mszipd_stream *mszipd_init(struct mspack_system *system, |
|
524 |
+ struct mspack_file *input, |
|
525 |
+ struct mspack_file *output, |
|
526 |
+ int input_buffer_size, |
|
527 |
+ int repair_mode) |
|
528 |
+{ |
|
529 |
+ struct mszipd_stream *zip; |
|
530 |
+ |
|
531 |
+ if (!system) return NULL; |
|
532 |
+ |
|
533 |
+ input_buffer_size = (input_buffer_size + 1) & -2; |
|
534 |
+ if (!input_buffer_size) return NULL; |
|
535 |
+ |
|
536 |
+ /* allocate decompression state */ |
|
537 |
+ if (!(zip = system->alloc(system, sizeof(struct mszipd_stream)))) { |
|
538 |
+ return NULL; |
|
539 |
+ } |
|
540 |
+ |
|
541 |
+ /* allocate input buffer */ |
|
542 |
+ zip->inbuf = system->alloc(system, (size_t) input_buffer_size); |
|
543 |
+ if (!zip->inbuf) { |
|
544 |
+ system->free(zip); |
|
545 |
+ return NULL; |
|
546 |
+ } |
|
547 |
+ |
|
548 |
+ /* initialise decompression state */ |
|
549 |
+ zip->sys = system; |
|
550 |
+ zip->input = input; |
|
551 |
+ zip->output = output; |
|
552 |
+ zip->inbuf_size = input_buffer_size; |
|
553 |
+ zip->error = MSPACK_ERR_OK; |
|
554 |
+ zip->repair_mode = repair_mode; |
|
555 |
+ zip->flush_window = &mszipd_flush_window; |
|
556 |
+ |
|
557 |
+ zip->i_ptr = zip->i_end = &zip->inbuf[0]; |
|
558 |
+ zip->o_ptr = zip->o_end = NULL; |
|
559 |
+ zip->bit_buffer = 0; zip->bits_left = 0; |
|
560 |
+ return zip; |
|
561 |
+} |
|
562 |
+ |
|
563 |
+int mszipd_decompress(struct mszipd_stream *zip, off_t out_bytes) { |
|
564 |
+ /* for the bit buffer */ |
|
565 |
+ register unsigned int bit_buffer; |
|
566 |
+ register int bits_left; |
|
567 |
+ unsigned char *i_ptr, *i_end; |
|
568 |
+ |
|
569 |
+ int i, state, error; |
|
570 |
+ |
|
571 |
+ /* easy answers */ |
|
572 |
+ if (!zip || (out_bytes < 0)) return MSPACK_ERR_ARGS; |
|
573 |
+ if (zip->error) return zip->error; |
|
574 |
+ |
|
575 |
+ /* flush out any stored-up bytes before we begin */ |
|
576 |
+ i = zip->o_end - zip->o_ptr; |
|
577 |
+ if ((off_t) i > out_bytes) i = (int) out_bytes; |
|
578 |
+ if (i) { |
|
579 |
+ if (zip->sys->write(zip->output, zip->o_ptr, i) != i) { |
|
580 |
+ return zip->error = MSPACK_ERR_WRITE; |
|
581 |
+ } |
|
582 |
+ zip->o_ptr += i; |
|
583 |
+ out_bytes -= i; |
|
584 |
+ } |
|
585 |
+ if (out_bytes == 0) return MSPACK_ERR_OK; |
|
586 |
+ |
|
587 |
+ |
|
588 |
+ while (out_bytes > 0) { |
|
589 |
+ /* unpack another block */ |
|
590 |
+ RESTORE_BITS; |
|
591 |
+ |
|
592 |
+ /* skip to next read 'CK' header */ |
|
593 |
+ i = bits_left & 7; REMOVE_BITS(i); /* align to bytestream */ |
|
594 |
+ state = 0; |
|
595 |
+ do { |
|
596 |
+ READ_BITS(i, 8); |
|
597 |
+ if (i == 'C') state = 1; |
|
598 |
+ else if ((state == 1) && (i == 'K')) state = 2; |
|
599 |
+ else state = 0; |
|
600 |
+ } while (state != 2); |
|
601 |
+ |
|
602 |
+ /* inflate a block, repair and realign if necessary */ |
|
603 |
+ zip->window_posn = 0; |
|
604 |
+ zip->bytes_output = 0; |
|
605 |
+ STORE_BITS; |
|
606 |
+ if ((error = inflate(zip))) { |
|
607 |
+ D(("inflate error %d", i)) |
|
608 |
+ if (zip->repair_mode) { |
|
609 |
+ zip->sys->message(NULL, "MSZIP error, %u bytes of data lost.", |
|
610 |
+ MSZIP_FRAME_SIZE - zip->bytes_output); |
|
611 |
+ for (i = zip->bytes_output; i < MSZIP_FRAME_SIZE; i++) { |
|
612 |
+ zip->window[i] = '\0'; |
|
613 |
+ } |
|
614 |
+ zip->bytes_output = MSZIP_FRAME_SIZE; |
|
615 |
+ } |
|
616 |
+ else { |
|
617 |
+ return zip->error = (error > 0) ? error : MSPACK_ERR_DECRUNCH; |
|
618 |
+ } |
|
619 |
+ } |
|
620 |
+ zip->o_ptr = &zip->window[0]; |
|
621 |
+ zip->o_end = &zip->o_ptr[zip->bytes_output]; |
|
622 |
+ |
|
623 |
+ /* write a frame */ |
|
624 |
+ i = (out_bytes < (off_t)zip->bytes_output) ? |
|
625 |
+ (int)out_bytes : zip->bytes_output; |
|
626 |
+ if (zip->sys->write(zip->output, zip->o_ptr, i) != i) { |
|
627 |
+ return zip->error = MSPACK_ERR_WRITE; |
|
628 |
+ } |
|
629 |
+ |
|
630 |
+ /* mspack errors (i.e. read errors) are fatal and can't be recovered */ |
|
631 |
+ if ((error > 0) && zip->repair_mode) return error; |
|
632 |
+ |
|
633 |
+ zip->o_ptr += i; |
|
634 |
+ out_bytes -= i; |
|
635 |
+ } |
|
636 |
+ |
|
637 |
+ if (out_bytes) { |
|
638 |
+ D(("bytes left to output")) |
|
639 |
+ return zip->error = MSPACK_ERR_DECRUNCH; |
|
640 |
+ } |
|
641 |
+ return MSPACK_ERR_OK; |
|
642 |
+} |
|
643 |
+ |
|
644 |
+void mszipd_free(struct mszipd_stream *zip) { |
|
645 |
+ struct mspack_system *sys; |
|
646 |
+ if (zip) { |
|
647 |
+ sys = zip->sys; |
|
648 |
+ sys->free(zip->inbuf); |
|
649 |
+ sys->free(zip); |
|
650 |
+ } |
|
651 |
+} |
0 | 652 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,120 @@ |
0 |
+/* This file is part of libmspack. |
|
1 |
+ * (C) 2003-2004 Stuart Caie. |
|
2 |
+ * |
|
3 |
+ * The Quantum method was created by David Stafford, adapted by Microsoft |
|
4 |
+ * Corporation. |
|
5 |
+ * |
|
6 |
+ * libmspack is free software; you can redistribute it and/or modify it under |
|
7 |
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
|
8 |
+ * |
|
9 |
+ * For further details, see the file COPYING.LIB distributed with libmspack |
|
10 |
+ */ |
|
11 |
+ |
|
12 |
+#ifndef MSPACK_QTM_H |
|
13 |
+#define MSPACK_QTM_H 1 |
|
14 |
+ |
|
15 |
+/* Quantum compression / decompression definitions */ |
|
16 |
+ |
|
17 |
+#define QTM_FRAME_SIZE (32768) |
|
18 |
+ |
|
19 |
+struct qtmd_modelsym { |
|
20 |
+ unsigned short sym, cumfreq; |
|
21 |
+}; |
|
22 |
+ |
|
23 |
+struct qtmd_model { |
|
24 |
+ int shiftsleft, entries; |
|
25 |
+ struct qtmd_modelsym *syms; |
|
26 |
+}; |
|
27 |
+ |
|
28 |
+struct qtmd_stream { |
|
29 |
+ struct mspack_system *sys; /* I/O routines */ |
|
30 |
+ struct mspack_file *input; /* input file handle */ |
|
31 |
+ struct mspack_file *output; /* output file handle */ |
|
32 |
+ |
|
33 |
+ unsigned char *window; /* decoding window */ |
|
34 |
+ unsigned int window_size; /* window size */ |
|
35 |
+ unsigned int window_posn; /* decompression offset within window */ |
|
36 |
+ unsigned int frame_start; /* start of current frame within window */ |
|
37 |
+ |
|
38 |
+ unsigned short H, L, C; /* high/low/current: arith coding state */ |
|
39 |
+ unsigned char header_read; /* have we started decoding a new frame? */ |
|
40 |
+ |
|
41 |
+ int error; |
|
42 |
+ |
|
43 |
+ /* I/O buffers */ |
|
44 |
+ unsigned char *inbuf, *i_ptr, *i_end, *o_ptr, *o_end; |
|
45 |
+ unsigned int bit_buffer, inbuf_size; |
|
46 |
+ unsigned char bits_left; |
|
47 |
+ |
|
48 |
+ /* four literal models, each representing 64 symbols |
|
49 |
+ * model0 for literals from 0 to 63 (selector = 0) |
|
50 |
+ * model1 for literals from 64 to 127 (selector = 1) |
|
51 |
+ * model2 for literals from 128 to 191 (selector = 2) |
|
52 |
+ * model3 for literals from 129 to 255 (selector = 3) */ |
|
53 |
+ struct qtmd_model model0, model1, model2, model3; |
|
54 |
+ |
|
55 |
+ /* three match models. |
|
56 |
+ * model4 for match with fixed length of 3 bytes |
|
57 |
+ * model5 for match with fixed length of 4 bytes |
|
58 |
+ * model6 for variable length match, encoded with model6len model */ |
|
59 |
+ struct qtmd_model model4, model5, model6, model6len; |
|
60 |
+ |
|
61 |
+ /* selector model. 0-6 to say literal (0,1,2,3) or match (4,5,6) */ |
|
62 |
+ struct qtmd_model model7; |
|
63 |
+ |
|
64 |
+ /* symbol arrays for all models */ |
|
65 |
+ struct qtmd_modelsym m0sym[64 + 1]; |
|
66 |
+ struct qtmd_modelsym m1sym[64 + 1]; |
|
67 |
+ struct qtmd_modelsym m2sym[64 + 1]; |
|
68 |
+ struct qtmd_modelsym m3sym[64 + 1]; |
|
69 |
+ struct qtmd_modelsym m4sym[24 + 1]; |
|
70 |
+ struct qtmd_modelsym m5sym[36 + 1]; |
|
71 |
+ struct qtmd_modelsym m6sym[42 + 1], m6lsym[27 + 1]; |
|
72 |
+ struct qtmd_modelsym m7sym[7 + 1]; |
|
73 |
+}; |
|
74 |
+ |
|
75 |
+/* allocates Quantum decompression state for decoding the given stream. |
|
76 |
+ * |
|
77 |
+ * - returns NULL if window_bits is outwith the range 10 to 21 (inclusive). |
|
78 |
+ * |
|
79 |
+ * - uses system->alloc() to allocate memory |
|
80 |
+ * |
|
81 |
+ * - returns NULL if not enough memory |
|
82 |
+ * |
|
83 |
+ * - window_bits is the size of the Quantum window, from 1Kb (10) to 2Mb (21). |
|
84 |
+ * |
|
85 |
+ * - input_buffer_size is the number of bytes to use to store bitstream data. |
|
86 |
+ */ |
|
87 |
+extern struct qtmd_stream *qtmd_init(struct mspack_system *system, |
|
88 |
+ struct mspack_file *input, |
|
89 |
+ struct mspack_file *output, |
|
90 |
+ int window_bits, |
|
91 |
+ int input_buffer_size); |
|
92 |
+ |
|
93 |
+/* decompresses, or decompresses more of, a Quantum stream. |
|
94 |
+ * |
|
95 |
+ * - out_bytes of data will be decompressed and the function will return |
|
96 |
+ * with an MSPACK_ERR_OK return code. |
|
97 |
+ * |
|
98 |
+ * - decompressing will stop as soon as out_bytes is reached. if the true |
|
99 |
+ * amount of bytes decoded spills over that amount, they will be kept for |
|
100 |
+ * a later invocation of qtmd_decompress(). |
|
101 |
+ * |
|
102 |
+ * - the output bytes will be passed to the system->write() function given in |
|
103 |
+ * qtmd_init(), using the output file handle given in qtmd_init(). More |
|
104 |
+ * than one call may be made to system->write() |
|
105 |
+ * |
|
106 |
+ * - Quantum will read input bytes as necessary using the system->read() |
|
107 |
+ * function given in qtmd_init(), using the input file handle given in |
|
108 |
+ * qtmd_init(). This will continue until system->read() returns 0 bytes, |
|
109 |
+ * or an error. |
|
110 |
+ */ |
|
111 |
+extern int qtmd_decompress(struct qtmd_stream *qtm, off_t out_bytes); |
|
112 |
+ |
|
113 |
+/* frees all state associated with a Quantum data stream |
|
114 |
+ * |
|
115 |
+ * - calls system->free() using the system pointer given in qtmd_init() |
|
116 |
+ */ |
|
117 |
+void qtmd_free(struct qtmd_stream *qtm); |
|
118 |
+ |
|
119 |
+#endif |
0 | 120 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,488 @@ |
0 |
+/* This file is part of libmspack. |
|
1 |
+ * (C) 2003-2004 Stuart Caie. |
|
2 |
+ * |
|
3 |
+ * The Quantum method was created by David Stafford, adapted by Microsoft |
|
4 |
+ * Corporation. |
|
5 |
+ * |
|
6 |
+ * This decompressor is based on an implementation by Matthew Russotto, used |
|
7 |
+ * with permission. |
|
8 |
+ * |
|
9 |
+ * libmspack is free software; you can redistribute it and/or modify it under |
|
10 |
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
|
11 |
+ * |
|
12 |
+ * For further details, see the file COPYING.LIB distributed with libmspack |
|
13 |
+ */ |
|
14 |
+ |
|
15 |
+/* Quantum decompression implementation */ |
|
16 |
+ |
|
17 |
+/* This decompressor was researched and implemented by Matthew Russotto. It |
|
18 |
+ * has since been tidied up by Stuart Caie. More information can be found at |
|
19 |
+ * http://www.speakeasy.org/~russotto/quantumcomp.html |
|
20 |
+ */ |
|
21 |
+ |
|
22 |
+#if HAVE_CONFIG_H |
|
23 |
+#include "clamav-config.h" |
|
24 |
+#endif |
|
25 |
+ |
|
26 |
+#include <mspack.h> |
|
27 |
+#include <system.h> |
|
28 |
+#include <qtm.h> |
|
29 |
+ |
|
30 |
+/* Quantum decompressor bitstream reading macros |
|
31 |
+ * |
|
32 |
+ * STORE_BITS stores bitstream state in qtmd_stream structure |
|
33 |
+ * RESTORE_BITS restores bitstream state from qtmd_stream structure |
|
34 |
+ * READ_BITS(var,n) takes N bits from the buffer and puts them in var |
|
35 |
+ * FILL_BUFFER if there is room for another 16 bits, reads another |
|
36 |
+ * 16 bits from the input stream. |
|
37 |
+ * PEEK_BITS(n) extracts without removing N bits from the bit buffer |
|
38 |
+ * REMOVE_BITS(n) removes N bits from the bit buffer |
|
39 |
+ * |
|
40 |
+ * These bit access routines work by using the area beyond the MSB and the |
|
41 |
+ * LSB as a free source of zeroes. This avoids having to mask any bits. |
|
42 |
+ * So we have to know the bit width of the bitbuffer variable. |
|
43 |
+ */ |
|
44 |
+ |
|
45 |
+#ifdef HAVE_LIMITS_H |
|
46 |
+# include <limits.h> |
|
47 |
+#endif |
|
48 |
+#ifndef CHAR_BIT |
|
49 |
+# define CHAR_BIT (8) |
|
50 |
+#endif |
|
51 |
+#define BITBUF_WIDTH (sizeof(unsigned int) * CHAR_BIT) |
|
52 |
+ |
|
53 |
+#define STORE_BITS do { \ |
|
54 |
+ qtm->i_ptr = i_ptr; \ |
|
55 |
+ qtm->i_end = i_end; \ |
|
56 |
+ qtm->bit_buffer = bit_buffer; \ |
|
57 |
+ qtm->bits_left = bits_left; \ |
|
58 |
+} while (0) |
|
59 |
+ |
|
60 |
+#define RESTORE_BITS do { \ |
|
61 |
+ i_ptr = qtm->i_ptr; \ |
|
62 |
+ i_end = qtm->i_end; \ |
|
63 |
+ bit_buffer = qtm->bit_buffer; \ |
|
64 |
+ bits_left = qtm->bits_left; \ |
|
65 |
+} while (0) |
|
66 |
+ |
|
67 |
+/* adds 16 bits to bit buffer, if there's space for the new bits */ |
|
68 |
+#define FILL_BUFFER do { \ |
|
69 |
+ if (bits_left <= (BITBUF_WIDTH - 16)) { \ |
|
70 |
+ if (i_ptr >= i_end) { \ |
|
71 |
+ if (qtmd_read_input(qtm)) return qtm->error; \ |
|
72 |
+ i_ptr = qtm->i_ptr; \ |
|
73 |
+ i_end = qtm->i_end; \ |
|
74 |
+ } \ |
|
75 |
+ bit_buffer |= ((i_ptr[0] << 8) | i_ptr[1]) \ |
|
76 |
+ << (BITBUF_WIDTH - 16 - bits_left); \ |
|
77 |
+ bits_left += 16; \ |
|
78 |
+ i_ptr += 2; \ |
|
79 |
+ } \ |
|
80 |
+} while (0) |
|
81 |
+ |
|
82 |
+#define PEEK_BITS(n) (bit_buffer >> (BITBUF_WIDTH - (n))) |
|
83 |
+#define REMOVE_BITS(n) ((bit_buffer <<= (n)), (bits_left -= (n))) |
|
84 |
+ |
|
85 |
+#define READ_BITS(val, bits) do { \ |
|
86 |
+ (val) = 0; \ |
|
87 |
+ for (bits_needed = (bits); bits_needed > 0; bits_needed -= bit_run) { \ |
|
88 |
+ FILL_BUFFER; \ |
|
89 |
+ bit_run = (bits_left < bits_needed) ? bits_left : bits_needed; \ |
|
90 |
+ (val) = ((val) << bit_run) | PEEK_BITS(bit_run); \ |
|
91 |
+ REMOVE_BITS(bit_run); \ |
|
92 |
+ } \ |
|
93 |
+} while (0) |
|
94 |
+ |
|
95 |
+static int qtmd_read_input(struct qtmd_stream *qtm) { |
|
96 |
+ int read = qtm->sys->read(qtm->input, &qtm->inbuf[0], (int)qtm->inbuf_size); |
|
97 |
+ if (read < 0) return qtm->error = MSPACK_ERR_READ; |
|
98 |
+ |
|
99 |
+ qtm->i_ptr = &qtm->inbuf[0]; |
|
100 |
+ qtm->i_end = &qtm->inbuf[read]; |
|
101 |
+ return MSPACK_ERR_OK; |
|
102 |
+} |
|
103 |
+ |
|
104 |
+/* Quantum static data tables: |
|
105 |
+ * |
|
106 |
+ * Quantum uses 'position slots' to represent match offsets. For every |
|
107 |
+ * match, a small 'position slot' number and a small offset from that slot |
|
108 |
+ * are encoded instead of one large offset. |
|
109 |
+ * |
|
110 |
+ * position_base[] is an index to the position slot bases |
|
111 |
+ * |
|
112 |
+ * extra_bits[] states how many bits of offset-from-base data is needed. |
|
113 |
+ * |
|
114 |
+ * length_base[] and length_extra[] are equivalent in function, but are |
|
115 |
+ * used for encoding selector 6 (variable length match) match lengths, |
|
116 |
+ * instead of match offsets. |
|
117 |
+ */ |
|
118 |
+static unsigned int position_base[42]; |
|
119 |
+static unsigned char extra_bits[42], length_base[27], length_extra[27]; |
|
120 |
+ |
|
121 |
+static void qtmd_static_init() { |
|
122 |
+ unsigned int i, offset; |
|
123 |
+ |
|
124 |
+ for (i = 0, offset = 0; i < 42; i++) { |
|
125 |
+ position_base[i] = offset; |
|
126 |
+ extra_bits[i] = ((i < 2) ? 0 : (i - 2)) >> 1; |
|
127 |
+ offset += 1 << extra_bits[i]; |
|
128 |
+ } |
|
129 |
+ |
|
130 |
+ for (i = 0, offset = 0; i < 26; i++) { |
|
131 |
+ length_base[i] = offset; |
|
132 |
+ length_extra[i] = (i < 2 ? 0 : i - 2) >> 2; |
|
133 |
+ offset += 1 << length_extra[i]; |
|
134 |
+ } |
|
135 |
+ length_base[26] = 254; length_extra[26] = 0; |
|
136 |
+} |
|
137 |
+ |
|
138 |
+ |
|
139 |
+/* Arithmetic decoder: |
|
140 |
+ * |
|
141 |
+ * GET_SYMBOL(model, var) fetches the next symbol from the stated model |
|
142 |
+ * and puts it in var. |
|
143 |
+ * |
|
144 |
+ * If necessary, qtmd_update_model() is called. |
|
145 |
+ */ |
|
146 |
+#define GET_SYMBOL(model, var) do { \ |
|
147 |
+ range = ((H - L) & 0xFFFF) + 1; \ |
|
148 |
+ symf = ((((C - L + 1) * model.syms[0].cumfreq)-1) / range) & 0xFFFF; \ |
|
149 |
+ \ |
|
150 |
+ for (i = 1; i < model.entries; i++) { \ |
|
151 |
+ if (model.syms[i].cumfreq <= symf) break; \ |
|
152 |
+ } \ |
|
153 |
+ (var) = model.syms[i-1].sym; \ |
|
154 |
+ \ |
|
155 |
+ range = (H - L) + 1; \ |
|
156 |
+ symf = model.syms[0].cumfreq; \ |
|
157 |
+ H = L + ((model.syms[i-1].cumfreq * range) / symf) - 1; \ |
|
158 |
+ L = L + ((model.syms[i].cumfreq * range) / symf); \ |
|
159 |
+ \ |
|
160 |
+ do { model.syms[--i].cumfreq += 8; } while (i > 0); \ |
|
161 |
+ if (model.syms[0].cumfreq > 3800) qtmd_update_model(&model); \ |
|
162 |
+ \ |
|
163 |
+ while (1) { \ |
|
164 |
+ if ((L & 0x8000) != (H & 0x8000)) { \ |
|
165 |
+ if ((L & 0x4000) && !(H & 0x4000)) { \ |
|
166 |
+ /* underflow case */ \ |
|
167 |
+ C ^= 0x4000; L &= 0x3FFF; H |= 0x4000; \ |
|
168 |
+ } \ |
|
169 |
+ else break; \ |
|
170 |
+ } \ |
|
171 |
+ L <<= 1; H = (H << 1) | 1; \ |
|
172 |
+ FILL_BUFFER; \ |
|
173 |
+ C = (C << 1) | PEEK_BITS(1); \ |
|
174 |
+ REMOVE_BITS(1); \ |
|
175 |
+ } \ |
|
176 |
+} while (0) |
|
177 |
+ |
|
178 |
+static void qtmd_update_model(struct qtmd_model *model) { |
|
179 |
+ struct qtmd_modelsym tmp; |
|
180 |
+ int i, j; |
|
181 |
+ |
|
182 |
+ if (--model->shiftsleft) { |
|
183 |
+ for (i = model->entries - 1; i >= 0; i--) { |
|
184 |
+ /* -1, not -2; the 0 entry saves this */ |
|
185 |
+ model->syms[i].cumfreq >>= 1; |
|
186 |
+ if (model->syms[i].cumfreq <= model->syms[i+1].cumfreq) { |
|
187 |
+ model->syms[i].cumfreq = model->syms[i+1].cumfreq + 1; |
|
188 |
+ } |
|
189 |
+ } |
|
190 |
+ } |
|
191 |
+ else { |
|
192 |
+ model->shiftsleft = 50; |
|
193 |
+ for (i = 0; i < model->entries; i++) { |
|
194 |
+ /* no -1, want to include the 0 entry */ |
|
195 |
+ /* this converts cumfreqs into frequencies, then shifts right */ |
|
196 |
+ model->syms[i].cumfreq -= model->syms[i+1].cumfreq; |
|
197 |
+ model->syms[i].cumfreq++; /* avoid losing things entirely */ |
|
198 |
+ model->syms[i].cumfreq >>= 1; |
|
199 |
+ } |
|
200 |
+ |
|
201 |
+ /* now sort by frequencies, decreasing order -- this must be an |
|
202 |
+ * inplace selection sort, or a sort with the same (in)stability |
|
203 |
+ * characteristics */ |
|
204 |
+ for (i = 0; i < model->entries - 1; i++) { |
|
205 |
+ for (j = i + 1; j < model->entries; j++) { |
|
206 |
+ if (model->syms[i].cumfreq < model->syms[j].cumfreq) { |
|
207 |
+ tmp = model->syms[i]; |
|
208 |
+ model->syms[i] = model->syms[j]; |
|
209 |
+ model->syms[j] = tmp; |
|
210 |
+ } |
|
211 |
+ } |
|
212 |
+ } |
|
213 |
+ |
|
214 |
+ /* then convert frequencies back to cumfreq */ |
|
215 |
+ for (i = model->entries - 1; i >= 0; i--) { |
|
216 |
+ model->syms[i].cumfreq += model->syms[i+1].cumfreq; |
|
217 |
+ } |
|
218 |
+ } |
|
219 |
+} |
|
220 |
+ |
|
221 |
+/* Initialises a model to decode symbols from [start] to [start]+[len]-1 */ |
|
222 |
+static void qtmd_init_model(struct qtmd_model *model, |
|
223 |
+ struct qtmd_modelsym *syms, int start, int len) |
|
224 |
+{ |
|
225 |
+ int i; |
|
226 |
+ |
|
227 |
+ model->shiftsleft = 4; |
|
228 |
+ model->entries = len; |
|
229 |
+ model->syms = syms; |
|
230 |
+ |
|
231 |
+ for (i = 0; i <= len; i++) { |
|
232 |
+ syms[i].sym = start + i; /* actual symbol */ |
|
233 |
+ syms[i].cumfreq = len - i; /* current frequency of that symbol */ |
|
234 |
+ } |
|
235 |
+} |
|
236 |
+ |
|
237 |
+ |
|
238 |
+/*-------- main Quantum code --------*/ |
|
239 |
+ |
|
240 |
+struct qtmd_stream *qtmd_init(struct mspack_system *system, |
|
241 |
+ struct mspack_file *input, |
|
242 |
+ struct mspack_file *output, |
|
243 |
+ int window_bits, int input_buffer_size) |
|
244 |
+{ |
|
245 |
+ unsigned int window_size = 1 << window_bits; |
|
246 |
+ struct qtmd_stream *qtm; |
|
247 |
+ int i; |
|
248 |
+ |
|
249 |
+ if (!system) return NULL; |
|
250 |
+ |
|
251 |
+ /* Quantum supports window sizes of 2^10 (1Kb) through 2^21 (2Mb) */ |
|
252 |
+ if (window_bits < 10 || window_bits > 21) return NULL; |
|
253 |
+ |
|
254 |
+ input_buffer_size = (input_buffer_size + 1) & -2; |
|
255 |
+ if (input_buffer_size < 2) return NULL; |
|
256 |
+ |
|
257 |
+ /* initialise static data */ |
|
258 |
+ qtmd_static_init(); |
|
259 |
+ |
|
260 |
+ /* allocate decompression state */ |
|
261 |
+ if (!(qtm = system->alloc(system, sizeof(struct qtmd_stream)))) { |
|
262 |
+ return NULL; |
|
263 |
+ } |
|
264 |
+ |
|
265 |
+ /* allocate decompression window and input buffer */ |
|
266 |
+ qtm->window = system->alloc(system, (size_t) window_size); |
|
267 |
+ qtm->inbuf = system->alloc(system, (size_t) input_buffer_size); |
|
268 |
+ if (!qtm->window || !qtm->inbuf) { |
|
269 |
+ system->free(qtm->window); |
|
270 |
+ system->free(qtm->inbuf); |
|
271 |
+ system->free(qtm); |
|
272 |
+ return NULL; |
|
273 |
+ } |
|
274 |
+ |
|
275 |
+ /* initialise decompression state */ |
|
276 |
+ qtm->sys = system; |
|
277 |
+ qtm->input = input; |
|
278 |
+ qtm->output = output; |
|
279 |
+ qtm->inbuf_size = input_buffer_size; |
|
280 |
+ qtm->window_size = window_size; |
|
281 |
+ qtm->window_posn = 0; |
|
282 |
+ qtm->frame_start = 0; |
|
283 |
+ qtm->header_read = 0; |
|
284 |
+ qtm->error = MSPACK_ERR_OK; |
|
285 |
+ |
|
286 |
+ qtm->i_ptr = qtm->i_end = &qtm->inbuf[0]; |
|
287 |
+ qtm->o_ptr = qtm->o_end = &qtm->window[0]; |
|
288 |
+ qtm->bits_left = 0; |
|
289 |
+ qtm->bit_buffer = 0; |
|
290 |
+ |
|
291 |
+ /* initialise arithmetic coding models |
|
292 |
+ * - model 4 depends on window size, ranges from 20 to 24 |
|
293 |
+ * - model 5 depends on window size, ranges from 20 to 36 |
|
294 |
+ * - model 6pos depends on window size, ranges from 20 to 42 |
|
295 |
+ */ |
|
296 |
+ i = window_bits * 2; |
|
297 |
+ qtmd_init_model(&qtm->model0, &qtm->m0sym[0], 0, 64); |
|
298 |
+ qtmd_init_model(&qtm->model1, &qtm->m1sym[0], 64, 64); |
|
299 |
+ qtmd_init_model(&qtm->model2, &qtm->m2sym[0], 128, 64); |
|
300 |
+ qtmd_init_model(&qtm->model3, &qtm->m3sym[0], 192, 64); |
|
301 |
+ qtmd_init_model(&qtm->model4, &qtm->m4sym[0], 0, (i > 24) ? 24 : i); |
|
302 |
+ qtmd_init_model(&qtm->model5, &qtm->m5sym[0], 0, (i > 36) ? 36 : i); |
|
303 |
+ qtmd_init_model(&qtm->model6, &qtm->m6sym[0], 0, i); |
|
304 |
+ qtmd_init_model(&qtm->model6len, &qtm->m6lsym[0], 0, 27); |
|
305 |
+ qtmd_init_model(&qtm->model7, &qtm->m7sym[0], 0, 7); |
|
306 |
+ |
|
307 |
+ /* all ok */ |
|
308 |
+ return qtm; |
|
309 |
+} |
|
310 |
+ |
|
311 |
+int qtmd_decompress(struct qtmd_stream *qtm, off_t out_bytes) { |
|
312 |
+ unsigned int frame_start, frame_end, window_posn, match_offset, range; |
|
313 |
+ unsigned char *window, *i_ptr, *i_end, *runsrc, *rundest; |
|
314 |
+ int i, j, selector, extra, sym, match_length; |
|
315 |
+ unsigned short H, L, C, symf; |
|
316 |
+ |
|
317 |
+ register unsigned int bit_buffer; |
|
318 |
+ register unsigned char bits_left; |
|
319 |
+ unsigned char bits_needed, bit_run; |
|
320 |
+ |
|
321 |
+ /* easy answers */ |
|
322 |
+ if (!qtm || (out_bytes < 0)) return MSPACK_ERR_ARGS; |
|
323 |
+ if (qtm->error) return qtm->error; |
|
324 |
+ |
|
325 |
+ /* flush out any stored-up bytes before we begin */ |
|
326 |
+ i = qtm->o_end - qtm->o_ptr; |
|
327 |
+ if ((off_t) i > out_bytes) i = (int) out_bytes; |
|
328 |
+ if (i) { |
|
329 |
+ if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { |
|
330 |
+ return qtm->error = MSPACK_ERR_WRITE; |
|
331 |
+ } |
|
332 |
+ qtm->o_ptr += i; |
|
333 |
+ out_bytes -= i; |
|
334 |
+ } |
|
335 |
+ if (out_bytes == 0) return MSPACK_ERR_OK; |
|
336 |
+ |
|
337 |
+ /* restore local state */ |
|
338 |
+ RESTORE_BITS; |
|
339 |
+ window = qtm->window; |
|
340 |
+ window_posn = qtm->window_posn; |
|
341 |
+ frame_start = qtm->frame_start; |
|
342 |
+ H = qtm->H; |
|
343 |
+ L = qtm->L; |
|
344 |
+ C = qtm->C; |
|
345 |
+ |
|
346 |
+ /* while we do not have enough decoded bytes in reserve: */ |
|
347 |
+ while ((qtm->o_end - qtm->o_ptr) < out_bytes) { |
|
348 |
+ |
|
349 |
+ /* read header if necessary. Initialises H, L and C */ |
|
350 |
+ if (!qtm->header_read) { |
|
351 |
+ H = 0xFFFF; L = 0; READ_BITS(C, 16); |
|
352 |
+ qtm->header_read = 1; |
|
353 |
+ } |
|
354 |
+ |
|
355 |
+ /* decode more, at most up to to frame boundary */ |
|
356 |
+ frame_end = window_posn + (out_bytes - (qtm->o_end - qtm->o_ptr)); |
|
357 |
+ if ((frame_start + QTM_FRAME_SIZE) < frame_end) { |
|
358 |
+ frame_end = frame_start + QTM_FRAME_SIZE; |
|
359 |
+ } |
|
360 |
+ |
|
361 |
+ while (window_posn < frame_end) { |
|
362 |
+ GET_SYMBOL(qtm->model7, selector); |
|
363 |
+ if (selector < 4) { |
|
364 |
+ struct qtmd_model *mdl = (selector == 0) ? &qtm->model0 : |
|
365 |
+ ((selector == 1) ? &qtm->model1 : |
|
366 |
+ ((selector == 2) ? &qtm->model2 : |
|
367 |
+ &qtm->model3)); |
|
368 |
+ GET_SYMBOL((*mdl), sym); |
|
369 |
+ window[window_posn++] = sym; |
|
370 |
+ } |
|
371 |
+ else { |
|
372 |
+ switch (selector) { |
|
373 |
+ case 4: /* selector 4 = fixed length match (3 bytes) */ |
|
374 |
+ GET_SYMBOL(qtm->model4, sym); |
|
375 |
+ READ_BITS(extra, extra_bits[sym]); |
|
376 |
+ match_offset = position_base[sym] + extra + 1; |
|
377 |
+ match_length = 3; |
|
378 |
+ break; |
|
379 |
+ |
|
380 |
+ case 5: /* selector 5 = fixed length match (4 bytes) */ |
|
381 |
+ GET_SYMBOL(qtm->model5, sym); |
|
382 |
+ READ_BITS(extra, extra_bits[sym]); |
|
383 |
+ match_offset = position_base[sym] + extra + 1; |
|
384 |
+ match_length = 4; |
|
385 |
+ break; |
|
386 |
+ |
|
387 |
+ case 6: /* selector 6 = variable length match */ |
|
388 |
+ GET_SYMBOL(qtm->model6len, sym); |
|
389 |
+ READ_BITS(extra, length_extra[sym]); |
|
390 |
+ match_length = length_base[sym] + extra + 5; |
|
391 |
+ |
|
392 |
+ GET_SYMBOL(qtm->model6, sym); |
|
393 |
+ READ_BITS(extra, extra_bits[sym]); |
|
394 |
+ match_offset = position_base[sym] + extra + 1; |
|
395 |
+ break; |
|
396 |
+ |
|
397 |
+ default: |
|
398 |
+ /* should be impossible, model7 can only return 0-6 */ |
|
399 |
+ return qtm->error = MSPACK_ERR_DECRUNCH; |
|
400 |
+ } |
|
401 |
+ |
|
402 |
+ rundest = &window[window_posn]; |
|
403 |
+ i = match_length; |
|
404 |
+ /* does match offset wrap the window? */ |
|
405 |
+ if (match_offset > window_posn) { |
|
406 |
+ /* j = length from match offset to end of window */ |
|
407 |
+ j = match_offset - window_posn; |
|
408 |
+ if (j > (int) qtm->window_size) { |
|
409 |
+ D(("match offset beyond window boundaries")) |
|
410 |
+ return qtm->error = MSPACK_ERR_DECRUNCH; |
|
411 |
+ } |
|
412 |
+ runsrc = &window[qtm->window_size - j]; |
|
413 |
+ if (j < i) { |
|
414 |
+ /* if match goes over the window edge, do two copy runs */ |
|
415 |
+ i -= j; while (j-- > 0) *rundest++ = *runsrc++; |
|
416 |
+ runsrc = window; |
|
417 |
+ } |
|
418 |
+ while (i-- > 0) *rundest++ = *runsrc++; |
|
419 |
+ } |
|
420 |
+ else { |
|
421 |
+ runsrc = rundest - match_offset; |
|
422 |
+ while (i-- > 0) *rundest++ = *runsrc++; |
|
423 |
+ } |
|
424 |
+ window_posn += match_length; |
|
425 |
+ } |
|
426 |
+ } /* while (window_posn < frame_end) */ |
|
427 |
+ |
|
428 |
+ qtm->o_end = &window[window_posn]; |
|
429 |
+ |
|
430 |
+ /* another frame completed? */ |
|
431 |
+ if ((window_posn - frame_start) >= QTM_FRAME_SIZE) { |
|
432 |
+ if ((window_posn - frame_start) != QTM_FRAME_SIZE) { |
|
433 |
+ D(("overshot frame alignment")) |
|
434 |
+ return qtm->error = MSPACK_ERR_DECRUNCH; |
|
435 |
+ } |
|
436 |
+ |
|
437 |
+ /* re-align input */ |
|
438 |
+ if (bits_left & 7) REMOVE_BITS(bits_left & 7); |
|
439 |
+ do { READ_BITS(i, 8); } while (i != 0xFF); |
|
440 |
+ qtm->header_read = 0; |
|
441 |
+ |
|
442 |
+ /* window wrap? */ |
|
443 |
+ if (window_posn == qtm->window_size) { |
|
444 |
+ /* flush all currently stored data */ |
|
445 |
+ i = (qtm->o_end - qtm->o_ptr); |
|
446 |
+ if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { |
|
447 |
+ return qtm->error = MSPACK_ERR_WRITE; |
|
448 |
+ } |
|
449 |
+ out_bytes -= i; |
|
450 |
+ qtm->o_ptr = &window[0]; |
|
451 |
+ qtm->o_end = &window[0]; |
|
452 |
+ window_posn = 0; |
|
453 |
+ } |
|
454 |
+ |
|
455 |
+ frame_start = window_posn; |
|
456 |
+ } |
|
457 |
+ |
|
458 |
+ } /* while (more bytes needed) */ |
|
459 |
+ |
|
460 |
+ if (out_bytes) { |
|
461 |
+ i = (int) out_bytes; |
|
462 |
+ if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { |
|
463 |
+ return qtm->error = MSPACK_ERR_WRITE; |
|
464 |
+ } |
|
465 |
+ qtm->o_ptr += i; |
|
466 |
+ } |
|
467 |
+ |
|
468 |
+ /* store local state */ |
|
469 |
+ STORE_BITS; |
|
470 |
+ qtm->window_posn = window_posn; |
|
471 |
+ qtm->frame_start = frame_start; |
|
472 |
+ qtm->H = H; |
|
473 |
+ qtm->L = L; |
|
474 |
+ qtm->C = C; |
|
475 |
+ |
|
476 |
+ return MSPACK_ERR_OK; |
|
477 |
+} |
|
478 |
+ |
|
479 |
+void qtmd_free(struct qtmd_stream *qtm) { |
|
480 |
+ struct mspack_system *sys; |
|
481 |
+ if (qtm) { |
|
482 |
+ sys = qtm->sys; |
|
483 |
+ sys->free(qtm->window); |
|
484 |
+ sys->free(qtm->inbuf); |
|
485 |
+ sys->free(qtm); |
|
486 |
+ } |
|
487 |
+} |
0 | 488 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,251 @@ |
0 |
+/* This file is part of libmspack. |
|
1 |
+ * (C) 2003-2004 Stuart Caie. |
|
2 |
+ * |
|
3 |
+ * libmspack is free software; you can redistribute it and/or modify it under |
|
4 |
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
|
5 |
+ * |
|
6 |
+ * For further details, see the file COPYING.LIB distributed with libmspack |
|
7 |
+ */ |
|
8 |
+ |
|
9 |
+#if HAVE_CONFIG_H |
|
10 |
+#include "clamav-config.h" |
|
11 |
+#endif |
|
12 |
+ |
|
13 |
+#include <mspack.h> |
|
14 |
+#include "others.h" |
|
15 |
+ |
|
16 |
+int mspack_version(int entity) { |
|
17 |
+ switch (entity) { |
|
18 |
+ case MSPACK_VER_LIBRARY: |
|
19 |
+ case MSPACK_VER_SYSTEM: |
|
20 |
+ case MSPACK_VER_MSCABD: |
|
21 |
+ case MSPACK_VER_MSCHMD: |
|
22 |
+ return 1; |
|
23 |
+ case MSPACK_VER_MSCABC: |
|
24 |
+ case MSPACK_VER_MSCHMC: |
|
25 |
+ case MSPACK_VER_MSLITD: |
|
26 |
+ case MSPACK_VER_MSLITC: |
|
27 |
+ case MSPACK_VER_MSHLPD: |
|
28 |
+ case MSPACK_VER_MSHLPC: |
|
29 |
+ case MSPACK_VER_MSSZDDD: |
|
30 |
+ case MSPACK_VER_MSSZDDC: |
|
31 |
+ case MSPACK_VER_MSKWAJD: |
|
32 |
+ case MSPACK_VER_MSKWAJC: |
|
33 |
+ return 0; |
|
34 |
+ } |
|
35 |
+ return -1; |
|
36 |
+} |
|
37 |
+ |
|
38 |
+int mspack_sys_selftest_internal(int offt_size) { |
|
39 |
+ return (sizeof(off_t) == offt_size) ? MSPACK_ERR_OK : MSPACK_ERR_SEEK; |
|
40 |
+} |
|
41 |
+ |
|
42 |
+/* validates a system structure */ |
|
43 |
+int mspack_valid_system(struct mspack_system *sys) { |
|
44 |
+ return (sys != NULL) && (sys->open != NULL) && (sys->close != NULL) && |
|
45 |
+ (sys->read != NULL) && (sys->write != NULL) && (sys->seek != NULL) && |
|
46 |
+ (sys->tell != NULL) && (sys->message != NULL) && (sys->alloc != NULL) && |
|
47 |
+ (sys->free != NULL) && (sys->copy != NULL) && (sys->null_ptr == NULL); |
|
48 |
+} |
|
49 |
+ |
|
50 |
+/* returns the length of a file opened for reading */ |
|
51 |
+int mspack_sys_filelen(struct mspack_system *system, |
|
52 |
+ struct mspack_file *file, off_t *length) |
|
53 |
+{ |
|
54 |
+ off_t current; |
|
55 |
+ |
|
56 |
+ if (!system || !file || !length) return MSPACK_ERR_OPEN; |
|
57 |
+ |
|
58 |
+ /* get current offset */ |
|
59 |
+ current = system->tell(file); |
|
60 |
+ |
|
61 |
+ /* seek to end of file */ |
|
62 |
+ if (system->seek(file, (off_t) 0, MSPACK_SYS_SEEK_END)) { |
|
63 |
+ return MSPACK_ERR_SEEK; |
|
64 |
+ } |
|
65 |
+ |
|
66 |
+ /* get offset of end of file */ |
|
67 |
+ *length = system->tell(file); |
|
68 |
+ |
|
69 |
+ /* seek back to original offset */ |
|
70 |
+ if (system->seek(file, current, MSPACK_SYS_SEEK_START)) { |
|
71 |
+ return MSPACK_ERR_SEEK; |
|
72 |
+ } |
|
73 |
+ |
|
74 |
+ return MSPACK_ERR_OK; |
|
75 |
+} |
|
76 |
+ |
|
77 |
+ |
|
78 |
+ |
|
79 |
+/* definition of mspack_default_system -- if the library is compiled with |
|
80 |
+ * MSPACK_NO_DEFAULT_SYSTEM, no default system will be provided. Otherwise, |
|
81 |
+ * an appropriate default system (e.g. the standard C library, or some native |
|
82 |
+ * API calls) |
|
83 |
+ */ |
|
84 |
+ |
|
85 |
+#ifdef MSPACK_NO_DEFAULT_SYSTEM |
|
86 |
+struct mspack_system *mspack_default_system = NULL; |
|
87 |
+#else |
|
88 |
+ |
|
89 |
+/* implementation of mspack_default_system for standard C library */ |
|
90 |
+ |
|
91 |
+#include <stdio.h> |
|
92 |
+#include <stdlib.h> |
|
93 |
+#include <string.h> |
|
94 |
+#include <stdarg.h> |
|
95 |
+ |
|
96 |
+struct mspack_file_p { |
|
97 |
+ FILE *fh; |
|
98 |
+ char *name; |
|
99 |
+ int desc; |
|
100 |
+}; |
|
101 |
+ |
|
102 |
+static struct mspack_file *msp_open(struct mspack_system *this, |
|
103 |
+ char *filename, int mode) |
|
104 |
+{ |
|
105 |
+ struct mspack_file_p *fh; |
|
106 |
+ char *fmode; |
|
107 |
+ |
|
108 |
+ switch (mode) { |
|
109 |
+ case MSPACK_SYS_OPEN_READ: fmode = "rb"; break; |
|
110 |
+ case MSPACK_SYS_OPEN_WRITE: fmode = "wb"; break; |
|
111 |
+ case MSPACK_SYS_OPEN_UPDATE: fmode = "r+b"; break; |
|
112 |
+ case MSPACK_SYS_OPEN_APPEND: fmode = "ab"; break; |
|
113 |
+ default: return NULL; |
|
114 |
+ } |
|
115 |
+ |
|
116 |
+ if ((fh = malloc(sizeof(struct mspack_file_p)))) { |
|
117 |
+ fh->name = filename; |
|
118 |
+ fh->desc = 0; |
|
119 |
+ if ((fh->fh = fopen(filename, fmode))) return (struct mspack_file *) fh; |
|
120 |
+ free(fh); |
|
121 |
+ } |
|
122 |
+ return NULL; |
|
123 |
+} |
|
124 |
+ |
|
125 |
+static struct mspack_file *msp_dopen(struct mspack_system *this, |
|
126 |
+ int desc, int mode) |
|
127 |
+{ |
|
128 |
+ struct mspack_file_p *fh; |
|
129 |
+ char *fmode; |
|
130 |
+ |
|
131 |
+ switch (mode) { |
|
132 |
+ case MSPACK_SYS_OPEN_READ: fmode = "rb"; break; |
|
133 |
+ case MSPACK_SYS_OPEN_WRITE: fmode = "wb"; break; |
|
134 |
+ case MSPACK_SYS_OPEN_UPDATE: fmode = "r+b"; break; |
|
135 |
+ case MSPACK_SYS_OPEN_APPEND: fmode = "ab"; break; |
|
136 |
+ default: return NULL; |
|
137 |
+ } |
|
138 |
+ |
|
139 |
+ if ((fh = malloc(sizeof(struct mspack_file_p)))) { |
|
140 |
+ fh->name = "descriptor"; |
|
141 |
+ fh->desc = desc; |
|
142 |
+ if ((fh->fh = fdopen(desc, fmode))) return (struct mspack_file *) fh; |
|
143 |
+ free(fh); |
|
144 |
+ } |
|
145 |
+ return NULL; |
|
146 |
+} |
|
147 |
+ |
|
148 |
+static void msp_close(struct mspack_file *file) { |
|
149 |
+ struct mspack_file_p *this = (struct mspack_file_p *) file; |
|
150 |
+ if (this) { |
|
151 |
+ fclose(this->fh); |
|
152 |
+ free(this); |
|
153 |
+ } |
|
154 |
+} |
|
155 |
+ |
|
156 |
+static int msp_read(struct mspack_file *file, void *buffer, int bytes) { |
|
157 |
+ struct mspack_file_p *this = (struct mspack_file_p *) file; |
|
158 |
+ if (this) { |
|
159 |
+ size_t count = fread(buffer, 1, (size_t) bytes, this->fh); |
|
160 |
+ if (!ferror(this->fh)) return (int) count; |
|
161 |
+ } |
|
162 |
+ return -1; |
|
163 |
+} |
|
164 |
+ |
|
165 |
+static int msp_write(struct mspack_file *file, void *buffer, int bytes) { |
|
166 |
+ struct mspack_file_p *this = (struct mspack_file_p *) file; |
|
167 |
+ if (this) { |
|
168 |
+ size_t count = fwrite(buffer, 1, (size_t) bytes, this->fh); |
|
169 |
+ if (!ferror(this->fh)) return (int) count; |
|
170 |
+ } |
|
171 |
+ return -1; |
|
172 |
+} |
|
173 |
+ |
|
174 |
+static int msp_seek(struct mspack_file *file, off_t offset, int mode) { |
|
175 |
+ struct mspack_file_p *this = (struct mspack_file_p *) file; |
|
176 |
+ if (this) { |
|
177 |
+ switch (mode) { |
|
178 |
+ case MSPACK_SYS_SEEK_START: mode = SEEK_SET; break; |
|
179 |
+ case MSPACK_SYS_SEEK_CUR: mode = SEEK_CUR; break; |
|
180 |
+ case MSPACK_SYS_SEEK_END: mode = SEEK_END; break; |
|
181 |
+ default: return -1; |
|
182 |
+ } |
|
183 |
+#ifdef HAVE_FSEEKO |
|
184 |
+ return fseeko(this->fh, offset, mode); |
|
185 |
+#else |
|
186 |
+ return fseek(this->fh, offset, mode); |
|
187 |
+#endif |
|
188 |
+ } |
|
189 |
+ return -1; |
|
190 |
+} |
|
191 |
+ |
|
192 |
+static off_t msp_tell(struct mspack_file *file) { |
|
193 |
+ struct mspack_file_p *this = (struct mspack_file_p *) file; |
|
194 |
+#ifdef HAVE_FSEEKO |
|
195 |
+ return (this) ? (off_t) ftello(this->fh) : 0; |
|
196 |
+#else |
|
197 |
+ return (this) ? (off_t) ftell(this->fh) : 0; |
|
198 |
+#endif |
|
199 |
+} |
|
200 |
+ |
|
201 |
+static void msp_msg(struct mspack_file *file, char *format, ...) { |
|
202 |
+ va_list ap; |
|
203 |
+ char buff[512]; |
|
204 |
+ |
|
205 |
+ va_start(ap, format); |
|
206 |
+ vsnprintf(buff, 512, format, ap); |
|
207 |
+ va_end(ap); |
|
208 |
+ cli_dbgmsg("libmspack: %s\n", buff); |
|
209 |
+} |
|
210 |
+ |
|
211 |
+static void *msp_alloc(struct mspack_system *this, size_t bytes) { |
|
212 |
+#ifdef DEBUG |
|
213 |
+ /* make uninitialised data obvious */ |
|
214 |
+ char *buf = malloc(bytes + 8); |
|
215 |
+ if (buf) memset(buf, 0xDC, bytes); |
|
216 |
+ *((size_t *)buf) = bytes; |
|
217 |
+ return &buf[8]; |
|
218 |
+#else |
|
219 |
+ return malloc(bytes); |
|
220 |
+#endif |
|
221 |
+} |
|
222 |
+ |
|
223 |
+static void msp_free(void *buffer) { |
|
224 |
+#ifdef DEBUG |
|
225 |
+ char *buf = buffer; |
|
226 |
+ size_t bytes; |
|
227 |
+ if (buf) { |
|
228 |
+ buf -= 8; |
|
229 |
+ bytes = *((size_t *)buf); |
|
230 |
+ /* make freed data obvious */ |
|
231 |
+ memset(buf, 0xED, bytes); |
|
232 |
+ free(buf); |
|
233 |
+ } |
|
234 |
+#else |
|
235 |
+ free(buffer); |
|
236 |
+#endif |
|
237 |
+} |
|
238 |
+ |
|
239 |
+static void msp_copy(void *src, void *dest, size_t bytes) { |
|
240 |
+ memcpy(dest, src, bytes); |
|
241 |
+} |
|
242 |
+ |
|
243 |
+static struct mspack_system msp_system = { |
|
244 |
+ &msp_open, &msp_dopen, &msp_close, &msp_read, &msp_write, &msp_seek, |
|
245 |
+ &msp_tell, &msp_msg, &msp_alloc, &msp_free, &msp_copy, NULL |
|
246 |
+}; |
|
247 |
+ |
|
248 |
+struct mspack_system *mspack_default_system = &msp_system; |
|
249 |
+ |
|
250 |
+#endif |
0 | 251 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,58 @@ |
0 |
+/* This file is part of libmspack. |
|
1 |
+ * (C) 2003-2004 Stuart Caie. |
|
2 |
+ * |
|
3 |
+ * libmspack is free software; you can redistribute it and/or modify it under |
|
4 |
+ * the terms of the GNU Lesser General Public License (LGPL) version 2.1 |
|
5 |
+ * |
|
6 |
+ * For further details, see the file COPYING.LIB distributed with libmspack |
|
7 |
+ */ |
|
8 |
+ |
|
9 |
+#ifndef MSPACK_SYSTEM_H |
|
10 |
+#define MSPACK_SYSTEM_H 1 |
|
11 |
+ |
|
12 |
+#ifdef DEBUG |
|
13 |
+# include <stdio.h> |
|
14 |
+# define D(x) do { printf("%s:%d (%s) ",__FILE__, __LINE__, __FUNCTION__); \ |
|
15 |
+ printf x ; fputc('\n', stdout); fflush(stdout);} while (0); |
|
16 |
+#else |
|
17 |
+# define D(x) |
|
18 |
+#endif |
|
19 |
+ |
|
20 |
+/* endian-neutral reading of little-endian data */ |
|
21 |
+#define __egi32(a,n) ( (((a)[n+3]) << 24) | (((a)[n+2]) << 16) | \ |
|
22 |
+ (((a)[n+1]) << 8) | ((a)[n+0]) ) |
|
23 |
+#define EndGetI64(a) ((((unsigned long long int) __egi32(a,4)) << 32) | \ |
|
24 |
+ ((unsigned int) __egi32(a,0))) |
|
25 |
+#define EndGetI32(a) __egi32(a,0) |
|
26 |
+#define EndGetI16(a) ((((a)[1])<<8)|((a)[0])) |
|
27 |
+ |
|
28 |
+/* endian-neutral reading of big-endian data */ |
|
29 |
+#define EndGetM32(a) ((((a)[0])<<24)|(((a)[1])<<16)|(((a)[2])<<8)|((a)[3])) |
|
30 |
+#define EndGetM16(a) ((((a)[0])<<8)|((a)[1])) |
|
31 |
+ |
|
32 |
+extern struct mspack_system *mspack_default_system; |
|
33 |
+ |
|
34 |
+/* returns the length of a file opened for reading */ |
|
35 |
+extern int mspack_sys_filelen(struct mspack_system *system, |
|
36 |
+ struct mspack_file *file, off_t *length); |
|
37 |
+ |
|
38 |
+/* validates a system structure */ |
|
39 |
+extern int mspack_valid_system(struct mspack_system *sys); |
|
40 |
+ |
|
41 |
+/* inline memcmp() */ |
|
42 |
+static inline int memcmp(const void *s1, const void *s2, size_t n) { |
|
43 |
+ unsigned char *c1 = (unsigned char *) s1; |
|
44 |
+ unsigned char *c2 = (unsigned char *) s2; |
|
45 |
+ if (n == 0) return 0; |
|
46 |
+ while (--n && (*c1 == *c2)) c1++, c2++; |
|
47 |
+ return *c1 - *c2; |
|
48 |
+} |
|
49 |
+ |
|
50 |
+/* inline strlen() */ |
|
51 |
+static inline size_t strlen(const char *s) { |
|
52 |
+ const char *e = s; |
|
53 |
+ while (*e) e++; |
|
54 |
+ return e - s; |
|
55 |
+} |
|
56 |
+ |
|
57 |
+#endif |