diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2013-03-11 18:46:03 +0100 |
---|---|---|
committer | Dominik Riebeling <Dominik.Riebeling@gmail.com> | 2013-11-04 22:14:17 +0100 |
commit | 739a7ae0e9acb27227f5473a003833ea5a9c97ef (patch) | |
tree | 81f6dfd81b4343745b21a6c00c286791827175e4 /rbutil/rbutilqt/mspack/qtmd.c | |
parent | 27111d83be815602ac35354e6a8e4e158c5968f9 (diff) | |
download | rockbox-739a7ae0e9acb27227f5473a003833ea5a9c97ef.tar.gz rockbox-739a7ae0e9acb27227f5473a003833ea5a9c97ef.zip |
Add libmspack to rbutil
Change-Id: I520c14131ec1e12013f106c13cba00aac058ad83
Reviewed-on: http://gerrit.rockbox.org/391
Reviewed-by: Dominik Riebeling <Dominik.Riebeling@gmail.com>
Diffstat (limited to 'rbutil/rbutilqt/mspack/qtmd.c')
-rw-r--r-- | rbutil/rbutilqt/mspack/qtmd.c | 489 |
1 files changed, 489 insertions, 0 deletions
diff --git a/rbutil/rbutilqt/mspack/qtmd.c b/rbutil/rbutilqt/mspack/qtmd.c new file mode 100644 index 0000000000..12b27f5608 --- /dev/null +++ b/rbutil/rbutilqt/mspack/qtmd.c | |||
@@ -0,0 +1,489 @@ | |||
1 | /* This file is part of libmspack. | ||
2 | * (C) 2003-2004 Stuart Caie. | ||
3 | * | ||
4 | * The Quantum method was created by David Stafford, adapted by Microsoft | ||
5 | * Corporation. | ||
6 | * | ||
7 | * This decompressor is based on an implementation by Matthew Russotto, used | ||
8 | * with permission. | ||
9 | * | ||
10 | * libmspack is free software; you can redistribute it and/or modify it under | ||
11 | * the terms of the GNU Lesser General Public License (LGPL) version 2.1 | ||
12 | * | ||
13 | * For further details, see the file COPYING.LIB distributed with libmspack | ||
14 | */ | ||
15 | |||
16 | /* Quantum decompression implementation */ | ||
17 | |||
18 | /* This decompressor was researched and implemented by Matthew Russotto. It | ||
19 | * has since been tidied up by Stuart Caie. More information can be found at | ||
20 | * http://www.speakeasy.org/~russotto/quantumcomp.html | ||
21 | */ | ||
22 | |||
23 | #include <system.h> | ||
24 | #include <qtm.h> | ||
25 | |||
26 | /* import bit-reading macros and code */ | ||
27 | #define BITS_TYPE struct qtmd_stream | ||
28 | #define BITS_VAR qtm | ||
29 | #define BITS_ORDER_MSB | ||
30 | #define READ_BYTES do { \ | ||
31 | unsigned char b0, b1; \ | ||
32 | READ_IF_NEEDED; b0 = *i_ptr++; \ | ||
33 | READ_IF_NEEDED; b1 = *i_ptr++; \ | ||
34 | INJECT_BITS((b0 << 8) | b1, 16); \ | ||
35 | } while (0) | ||
36 | #include <readbits.h> | ||
37 | |||
38 | /* Quantum static data tables: | ||
39 | * | ||
40 | * Quantum uses 'position slots' to represent match offsets. For every | ||
41 | * match, a small 'position slot' number and a small offset from that slot | ||
42 | * are encoded instead of one large offset. | ||
43 | * | ||
44 | * position_base[] is an index to the position slot bases | ||
45 | * | ||
46 | * extra_bits[] states how many bits of offset-from-base data is needed. | ||
47 | * | ||
48 | * length_base[] and length_extra[] are equivalent in function, but are | ||
49 | * used for encoding selector 6 (variable length match) match lengths, | ||
50 | * instead of match offsets. | ||
51 | * | ||
52 | * They are generated with the following code: | ||
53 | * unsigned int i, offset; | ||
54 | * for (i = 0, offset = 0; i < 42; i++) { | ||
55 | * position_base[i] = offset; | ||
56 | * extra_bits[i] = ((i < 2) ? 0 : (i - 2)) >> 1; | ||
57 | * offset += 1 << extra_bits[i]; | ||
58 | * } | ||
59 | * for (i = 0, offset = 0; i < 26; i++) { | ||
60 | * length_base[i] = offset; | ||
61 | * length_extra[i] = (i < 2 ? 0 : i - 2) >> 2; | ||
62 | * offset += 1 << length_extra[i]; | ||
63 | * } | ||
64 | * length_base[26] = 254; length_extra[26] = 0; | ||
65 | */ | ||
66 | static const unsigned int position_base[42] = { | ||
67 | 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 48, 64, 96, 128, 192, 256, 384, 512, 768, | ||
68 | 1024, 1536, 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, | ||
69 | 65536, 98304, 131072, 196608, 262144, 393216, 524288, 786432, 1048576, 1572864 | ||
70 | }; | ||
71 | static const unsigned char extra_bits[42] = { | ||
72 | 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, | ||
73 | 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19, 19 | ||
74 | }; | ||
75 | static const unsigned char length_base[27] = { | ||
76 | 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 18, 22, 26, | ||
77 | 30, 38, 46, 54, 62, 78, 94, 110, 126, 158, 190, 222, 254 | ||
78 | }; | ||
79 | static const unsigned char length_extra[27] = { | ||
80 | 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, | ||
81 | 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 | ||
82 | }; | ||
83 | |||
84 | |||
85 | /* Arithmetic decoder: | ||
86 | * | ||
87 | * GET_SYMBOL(model, var) fetches the next symbol from the stated model | ||
88 | * and puts it in var. | ||
89 | * | ||
90 | * If necessary, qtmd_update_model() is called. | ||
91 | */ | ||
92 | #define GET_SYMBOL(model, var) do { \ | ||
93 | range = ((H - L) & 0xFFFF) + 1; \ | ||
94 | symf = ((((C - L + 1) * model.syms[0].cumfreq)-1) / range) & 0xFFFF; \ | ||
95 | \ | ||
96 | for (i = 1; i < model.entries; i++) { \ | ||
97 | if (model.syms[i].cumfreq <= symf) break; \ | ||
98 | } \ | ||
99 | (var) = model.syms[i-1].sym; \ | ||
100 | \ | ||
101 | range = (H - L) + 1; \ | ||
102 | symf = model.syms[0].cumfreq; \ | ||
103 | H = L + ((model.syms[i-1].cumfreq * range) / symf) - 1; \ | ||
104 | L = L + ((model.syms[i].cumfreq * range) / symf); \ | ||
105 | \ | ||
106 | do { model.syms[--i].cumfreq += 8; } while (i > 0); \ | ||
107 | if (model.syms[0].cumfreq > 3800) qtmd_update_model(&model); \ | ||
108 | \ | ||
109 | while (1) { \ | ||
110 | if ((L & 0x8000) != (H & 0x8000)) { \ | ||
111 | if ((L & 0x4000) && !(H & 0x4000)) { \ | ||
112 | /* underflow case */ \ | ||
113 | C ^= 0x4000; L &= 0x3FFF; H |= 0x4000; \ | ||
114 | } \ | ||
115 | else break; \ | ||
116 | } \ | ||
117 | L <<= 1; H = (H << 1) | 1; \ | ||
118 | ENSURE_BITS(1); \ | ||
119 | C = (C << 1) | PEEK_BITS(1); \ | ||
120 | REMOVE_BITS(1); \ | ||
121 | } \ | ||
122 | } while (0) | ||
123 | |||
124 | static void qtmd_update_model(struct qtmd_model *model) { | ||
125 | struct qtmd_modelsym tmp; | ||
126 | int i, j; | ||
127 | |||
128 | if (--model->shiftsleft) { | ||
129 | for (i = model->entries - 1; i >= 0; i--) { | ||
130 | /* -1, not -2; the 0 entry saves this */ | ||
131 | model->syms[i].cumfreq >>= 1; | ||
132 | if (model->syms[i].cumfreq <= model->syms[i+1].cumfreq) { | ||
133 | model->syms[i].cumfreq = model->syms[i+1].cumfreq + 1; | ||
134 | } | ||
135 | } | ||
136 | } | ||
137 | else { | ||
138 | model->shiftsleft = 50; | ||
139 | for (i = 0; i < model->entries; i++) { | ||
140 | /* no -1, want to include the 0 entry */ | ||
141 | /* this converts cumfreqs into frequencies, then shifts right */ | ||
142 | model->syms[i].cumfreq -= model->syms[i+1].cumfreq; | ||
143 | model->syms[i].cumfreq++; /* avoid losing things entirely */ | ||
144 | model->syms[i].cumfreq >>= 1; | ||
145 | } | ||
146 | |||
147 | /* now sort by frequencies, decreasing order -- this must be an | ||
148 | * inplace selection sort, or a sort with the same (in)stability | ||
149 | * characteristics */ | ||
150 | for (i = 0; i < model->entries - 1; i++) { | ||
151 | for (j = i + 1; j < model->entries; j++) { | ||
152 | if (model->syms[i].cumfreq < model->syms[j].cumfreq) { | ||
153 | tmp = model->syms[i]; | ||
154 | model->syms[i] = model->syms[j]; | ||
155 | model->syms[j] = tmp; | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | |||
160 | /* then convert frequencies back to cumfreq */ | ||
161 | for (i = model->entries - 1; i >= 0; i--) { | ||
162 | model->syms[i].cumfreq += model->syms[i+1].cumfreq; | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | |||
167 | /* Initialises a model to decode symbols from [start] to [start]+[len]-1 */ | ||
168 | static void qtmd_init_model(struct qtmd_model *model, | ||
169 | struct qtmd_modelsym *syms, int start, int len) | ||
170 | { | ||
171 | int i; | ||
172 | |||
173 | model->shiftsleft = 4; | ||
174 | model->entries = len; | ||
175 | model->syms = syms; | ||
176 | |||
177 | for (i = 0; i <= len; i++) { | ||
178 | syms[i].sym = start + i; /* actual symbol */ | ||
179 | syms[i].cumfreq = len - i; /* current frequency of that symbol */ | ||
180 | } | ||
181 | } | ||
182 | |||
183 | |||
184 | /*-------- main Quantum code --------*/ | ||
185 | |||
186 | struct qtmd_stream *qtmd_init(struct mspack_system *system, | ||
187 | struct mspack_file *input, | ||
188 | struct mspack_file *output, | ||
189 | int window_bits, int input_buffer_size) | ||
190 | { | ||
191 | unsigned int window_size = 1 << window_bits; | ||
192 | struct qtmd_stream *qtm; | ||
193 | int i; | ||
194 | |||
195 | if (!system) return NULL; | ||
196 | |||
197 | /* Quantum supports window sizes of 2^10 (1Kb) through 2^21 (2Mb) */ | ||
198 | if (window_bits < 10 || window_bits > 21) return NULL; | ||
199 | |||
200 | input_buffer_size = (input_buffer_size + 1) & -2; | ||
201 | if (input_buffer_size < 2) return NULL; | ||
202 | |||
203 | /* allocate decompression state */ | ||
204 | if (!(qtm = (struct qtmd_stream *) system->alloc(system, sizeof(struct qtmd_stream)))) { | ||
205 | return NULL; | ||
206 | } | ||
207 | |||
208 | /* allocate decompression window and input buffer */ | ||
209 | qtm->window = (unsigned char *) system->alloc(system, (size_t) window_size); | ||
210 | qtm->inbuf = (unsigned char *) system->alloc(system, (size_t) input_buffer_size); | ||
211 | if (!qtm->window || !qtm->inbuf) { | ||
212 | system->free(qtm->window); | ||
213 | system->free(qtm->inbuf); | ||
214 | system->free(qtm); | ||
215 | return NULL; | ||
216 | } | ||
217 | |||
218 | /* initialise decompression state */ | ||
219 | qtm->sys = system; | ||
220 | qtm->input = input; | ||
221 | qtm->output = output; | ||
222 | qtm->inbuf_size = input_buffer_size; | ||
223 | qtm->window_size = window_size; | ||
224 | qtm->window_posn = 0; | ||
225 | qtm->frame_todo = QTM_FRAME_SIZE; | ||
226 | qtm->header_read = 0; | ||
227 | qtm->error = MSPACK_ERR_OK; | ||
228 | |||
229 | qtm->i_ptr = qtm->i_end = &qtm->inbuf[0]; | ||
230 | qtm->o_ptr = qtm->o_end = &qtm->window[0]; | ||
231 | qtm->input_end = 0; | ||
232 | qtm->bits_left = 0; | ||
233 | qtm->bit_buffer = 0; | ||
234 | |||
235 | /* initialise arithmetic coding models | ||
236 | * - model 4 depends on window size, ranges from 20 to 24 | ||
237 | * - model 5 depends on window size, ranges from 20 to 36 | ||
238 | * - model 6pos depends on window size, ranges from 20 to 42 | ||
239 | */ | ||
240 | i = window_bits * 2; | ||
241 | qtmd_init_model(&qtm->model0, &qtm->m0sym[0], 0, 64); | ||
242 | qtmd_init_model(&qtm->model1, &qtm->m1sym[0], 64, 64); | ||
243 | qtmd_init_model(&qtm->model2, &qtm->m2sym[0], 128, 64); | ||
244 | qtmd_init_model(&qtm->model3, &qtm->m3sym[0], 192, 64); | ||
245 | qtmd_init_model(&qtm->model4, &qtm->m4sym[0], 0, (i > 24) ? 24 : i); | ||
246 | qtmd_init_model(&qtm->model5, &qtm->m5sym[0], 0, (i > 36) ? 36 : i); | ||
247 | qtmd_init_model(&qtm->model6, &qtm->m6sym[0], 0, i); | ||
248 | qtmd_init_model(&qtm->model6len, &qtm->m6lsym[0], 0, 27); | ||
249 | qtmd_init_model(&qtm->model7, &qtm->m7sym[0], 0, 7); | ||
250 | |||
251 | /* all ok */ | ||
252 | return qtm; | ||
253 | } | ||
254 | |||
255 | int qtmd_decompress(struct qtmd_stream *qtm, off_t out_bytes) { | ||
256 | unsigned int frame_todo, frame_end, window_posn, match_offset, range; | ||
257 | unsigned char *window, *i_ptr, *i_end, *runsrc, *rundest; | ||
258 | int i, j, selector, extra, sym, match_length; | ||
259 | unsigned short H, L, C, symf; | ||
260 | |||
261 | register unsigned int bit_buffer; | ||
262 | register unsigned char bits_left; | ||
263 | |||
264 | /* easy answers */ | ||
265 | if (!qtm || (out_bytes < 0)) return MSPACK_ERR_ARGS; | ||
266 | if (qtm->error) return qtm->error; | ||
267 | |||
268 | /* flush out any stored-up bytes before we begin */ | ||
269 | i = qtm->o_end - qtm->o_ptr; | ||
270 | if ((off_t) i > out_bytes) i = (int) out_bytes; | ||
271 | if (i) { | ||
272 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
273 | return qtm->error = MSPACK_ERR_WRITE; | ||
274 | } | ||
275 | qtm->o_ptr += i; | ||
276 | out_bytes -= i; | ||
277 | } | ||
278 | if (out_bytes == 0) return MSPACK_ERR_OK; | ||
279 | |||
280 | /* restore local state */ | ||
281 | RESTORE_BITS; | ||
282 | window = qtm->window; | ||
283 | window_posn = qtm->window_posn; | ||
284 | frame_todo = qtm->frame_todo; | ||
285 | H = qtm->H; | ||
286 | L = qtm->L; | ||
287 | C = qtm->C; | ||
288 | |||
289 | /* while we do not have enough decoded bytes in reserve: */ | ||
290 | while ((qtm->o_end - qtm->o_ptr) < out_bytes) { | ||
291 | /* read header if necessary. Initialises H, L and C */ | ||
292 | if (!qtm->header_read) { | ||
293 | H = 0xFFFF; L = 0; READ_BITS(C, 16); | ||
294 | qtm->header_read = 1; | ||
295 | } | ||
296 | |||
297 | /* decode more, up to the number of bytes needed, the frame boundary, | ||
298 | * or the window boundary, whichever comes first */ | ||
299 | frame_end = window_posn + (out_bytes - (qtm->o_end - qtm->o_ptr)); | ||
300 | if ((window_posn + frame_todo) < frame_end) { | ||
301 | frame_end = window_posn + frame_todo; | ||
302 | } | ||
303 | if (frame_end > qtm->window_size) { | ||
304 | frame_end = qtm->window_size; | ||
305 | } | ||
306 | |||
307 | while (window_posn < frame_end) { | ||
308 | GET_SYMBOL(qtm->model7, selector); | ||
309 | if (selector < 4) { | ||
310 | /* literal byte */ | ||
311 | struct qtmd_model *mdl = (selector == 0) ? &qtm->model0 : | ||
312 | ((selector == 1) ? &qtm->model1 : | ||
313 | ((selector == 2) ? &qtm->model2 : | ||
314 | &qtm->model3)); | ||
315 | GET_SYMBOL((*mdl), sym); | ||
316 | window[window_posn++] = sym; | ||
317 | frame_todo--; | ||
318 | } | ||
319 | else { | ||
320 | /* match repeated string */ | ||
321 | switch (selector) { | ||
322 | case 4: /* selector 4 = fixed length match (3 bytes) */ | ||
323 | GET_SYMBOL(qtm->model4, sym); | ||
324 | READ_MANY_BITS(extra, extra_bits[sym]); | ||
325 | match_offset = position_base[sym] + extra + 1; | ||
326 | match_length = 3; | ||
327 | break; | ||
328 | |||
329 | case 5: /* selector 5 = fixed length match (4 bytes) */ | ||
330 | GET_SYMBOL(qtm->model5, sym); | ||
331 | READ_MANY_BITS(extra, extra_bits[sym]); | ||
332 | match_offset = position_base[sym] + extra + 1; | ||
333 | match_length = 4; | ||
334 | break; | ||
335 | |||
336 | case 6: /* selector 6 = variable length match */ | ||
337 | GET_SYMBOL(qtm->model6len, sym); | ||
338 | READ_MANY_BITS(extra, length_extra[sym]); | ||
339 | match_length = length_base[sym] + extra + 5; | ||
340 | |||
341 | GET_SYMBOL(qtm->model6, sym); | ||
342 | READ_MANY_BITS(extra, extra_bits[sym]); | ||
343 | match_offset = position_base[sym] + extra + 1; | ||
344 | break; | ||
345 | |||
346 | default: | ||
347 | /* should be impossible, model7 can only return 0-6 */ | ||
348 | D(("got %d from selector", selector)) | ||
349 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
350 | } | ||
351 | |||
352 | rundest = &window[window_posn]; | ||
353 | frame_todo -= match_length; | ||
354 | |||
355 | /* does match destination wrap the window? This situation is possible | ||
356 | * where the window size is less than the 32k frame size, but matches | ||
357 | * must not go beyond a frame boundary */ | ||
358 | if ((window_posn + match_length) > qtm->window_size) { | ||
359 | /* copy first part of match, before window end */ | ||
360 | i = qtm->window_size - window_posn; | ||
361 | j = window_posn - match_offset; | ||
362 | while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; | ||
363 | |||
364 | /* flush currently stored data */ | ||
365 | i = (&window[qtm->window_size] - qtm->o_ptr); | ||
366 | |||
367 | /* this should not happen, but if it does then this code | ||
368 | * can't handle the situation (can't flush up to the end of | ||
369 | * the window, but can't break out either because we haven't | ||
370 | * finished writing the match). bail out in this case */ | ||
371 | if (i > out_bytes) { | ||
372 | D(("during window-wrap match; %d bytes to flush but only need %d", | ||
373 | i, (int) out_bytes)) | ||
374 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
375 | } | ||
376 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
377 | return qtm->error = MSPACK_ERR_WRITE; | ||
378 | } | ||
379 | out_bytes -= i; | ||
380 | qtm->o_ptr = &window[0]; | ||
381 | qtm->o_end = &window[0]; | ||
382 | |||
383 | /* copy second part of match, after window wrap */ | ||
384 | rundest = &window[0]; | ||
385 | i = match_length - (qtm->window_size - window_posn); | ||
386 | while (i--) *rundest++ = window[j++ & (qtm->window_size - 1)]; | ||
387 | window_posn = window_posn + match_length - qtm->window_size; | ||
388 | |||
389 | break; /* because "window_posn < frame_end" has now failed */ | ||
390 | } | ||
391 | else { | ||
392 | /* normal match - output won't wrap window or frame end */ | ||
393 | i = match_length; | ||
394 | |||
395 | /* does match _offset_ wrap the window? */ | ||
396 | if (match_offset > window_posn) { | ||
397 | /* j = length from match offset to end of window */ | ||
398 | j = match_offset - window_posn; | ||
399 | if (j > (int) qtm->window_size) { | ||
400 | D(("match offset beyond window boundaries")) | ||
401 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
402 | } | ||
403 | runsrc = &window[qtm->window_size - j]; | ||
404 | if (j < i) { | ||
405 | /* if match goes over the window edge, do two copy runs */ | ||
406 | i -= j; while (j-- > 0) *rundest++ = *runsrc++; | ||
407 | runsrc = window; | ||
408 | } | ||
409 | while (i-- > 0) *rundest++ = *runsrc++; | ||
410 | } | ||
411 | else { | ||
412 | runsrc = rundest - match_offset; | ||
413 | while (i-- > 0) *rundest++ = *runsrc++; | ||
414 | } | ||
415 | window_posn += match_length; | ||
416 | } | ||
417 | } /* if (window_posn+match_length > frame_end) */ | ||
418 | } /* while (window_posn < frame_end) */ | ||
419 | |||
420 | qtm->o_end = &window[window_posn]; | ||
421 | |||
422 | /* if we subtracted too much from frame_todo, it will | ||
423 | * wrap around past zero and go above its max value */ | ||
424 | if (frame_todo > QTM_FRAME_SIZE) { | ||
425 | D(("overshot frame alignment")) | ||
426 | return qtm->error = MSPACK_ERR_DECRUNCH; | ||
427 | } | ||
428 | |||
429 | /* another frame completed? */ | ||
430 | if (frame_todo == 0) { | ||
431 | /* re-align input */ | ||
432 | if (bits_left & 7) REMOVE_BITS(bits_left & 7); | ||
433 | |||
434 | /* special Quantum hack -- cabd.c injects a trailer byte to allow the | ||
435 | * decompressor to realign itself. CAB Quantum blocks, unlike LZX | ||
436 | * blocks, can have anything from 0 to 4 trailing null bytes. */ | ||
437 | do { READ_BITS(i, 8); } while (i != 0xFF); | ||
438 | |||
439 | qtm->header_read = 0; | ||
440 | |||
441 | frame_todo = QTM_FRAME_SIZE; | ||
442 | } | ||
443 | |||
444 | /* window wrap? */ | ||
445 | if (window_posn == qtm->window_size) { | ||
446 | /* flush all currently stored data */ | ||
447 | i = (qtm->o_end - qtm->o_ptr); | ||
448 | /* break out if we have more than enough to finish this request */ | ||
449 | if (i >= out_bytes) break; | ||
450 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
451 | return qtm->error = MSPACK_ERR_WRITE; | ||
452 | } | ||
453 | out_bytes -= i; | ||
454 | qtm->o_ptr = &window[0]; | ||
455 | qtm->o_end = &window[0]; | ||
456 | window_posn = 0; | ||
457 | } | ||
458 | |||
459 | } /* while (more bytes needed) */ | ||
460 | |||
461 | if (out_bytes) { | ||
462 | i = (int) out_bytes; | ||
463 | if (qtm->sys->write(qtm->output, qtm->o_ptr, i) != i) { | ||
464 | return qtm->error = MSPACK_ERR_WRITE; | ||
465 | } | ||
466 | qtm->o_ptr += i; | ||
467 | } | ||
468 | |||
469 | /* store local state */ | ||
470 | |||
471 | STORE_BITS; | ||
472 | qtm->window_posn = window_posn; | ||
473 | qtm->frame_todo = frame_todo; | ||
474 | qtm->H = H; | ||
475 | qtm->L = L; | ||
476 | qtm->C = C; | ||
477 | |||
478 | return MSPACK_ERR_OK; | ||
479 | } | ||
480 | |||
481 | void qtmd_free(struct qtmd_stream *qtm) { | ||
482 | struct mspack_system *sys; | ||
483 | if (qtm) { | ||
484 | sys = qtm->sys; | ||
485 | sys->free(qtm->window); | ||
486 | sys->free(qtm->inbuf); | ||
487 | sys->free(qtm); | ||
488 | } | ||
489 | } | ||