1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
12 #include "sjme/inflate.h"
13 #include "sjme/debug.h"
15 sjme_errorCode
sjme_inflate_processCodes(
16 sjme_attrInNotNull sjme_inflate_state
* state
)
19 sjme_inflate_buffer
* inBuffer
;
20 sjme_inflate_buffer
* outBuffer
;
21 sjme_inflate_window
* window
;
22 sjme_juint code
, windowReadLen
, windowReadDist
;
25 return SJME_ERROR_NULL_ARGUMENTS
;
27 /* This must be set!! */
28 if (state
->readCode
== NULL
||
29 state
->readDist
== NULL
)
30 return SJME_ERROR_ILLEGAL_STATE
;
32 /* Read as much as possible until we hit saturation. */
33 inBuffer
= &state
->input
;
34 outBuffer
= &state
->output
;
35 window
= &state
->window
;
36 while (outBuffer
->ready
< SJME_INFLATE_IO_BUFFER_SATURATED
)
40 if (sjme_error_is(error
= state
->readCode(state
, &code
)) ||
42 return sjme_error_default(error
);
47 /* Reset back to initial step. */
48 state
->step
= SJME_INFLATE_STEP_CHECK_BTYPE
;
49 state
->readCode
= NULL
;
52 return SJME_ERROR_NONE
;
55 /* Literal byte value. */
56 else if (code
>= 0 && code
<= 255)
58 if (sjme_error_is(error
= sjme_inflate_bitOut(
60 SJME_INFLATE_LSB
, window
,
62 return sjme_error_default(error
);
66 else if (code
>= 257 && code
<= 285)
68 /* Read in window length. */
69 windowReadLen
= INT32_MAX
;
70 if (sjme_error_is(error
= sjme_inflate_processLength(
71 state
, inBuffer
, code
, &windowReadLen
)) ||
72 windowReadLen
== INT32_MAX
)
73 return sjme_error_default(error
);
75 /* Read in distance. */
76 windowReadDist
= INT32_MAX
;
77 if (sjme_error_is(error
= sjme_inflate_processDistance(
78 state
, inBuffer
, code
, &windowReadDist
)) ||
79 windowReadDist
== INT32_MAX
)
80 return sjme_error_default(error
);
82 /* Copy from the input window. */
83 if (sjme_error_is(error
= sjme_inflate_processWindow(
84 state
, outBuffer
, window
,
85 windowReadDist
, windowReadLen
)))
86 return sjme_error_default(error
);
91 return SJME_ERROR_INFLATE_INVALID_CODE
;
94 sjme_message("Code: %d 0x%x", code
, code
);
95 sjme_message_hexDump(&window
->window
[0],
99 /* If we over-saturated, just stop and give all the data. */
100 if (outBuffer
->ready
>= SJME_INFLATE_IO_BUFFER_SATURATED
)
101 return SJME_ERROR_BUFFER_SATURATED
;
102 return SJME_ERROR_NONE
;
105 sjme_errorCode
sjme_inflate_processDistance(
106 sjme_attrInNotNull sjme_inflate_state
* state
,
107 sjme_attrInNotNull sjme_inflate_buffer
* inBuffer
,
108 sjme_attrInRange(257, 285) sjme_juint origCode
,
109 sjme_attrOutNotNull sjme_juint
* outDist
)
111 sjme_errorCode error
;
112 sjme_juint base
, result
, i
, readIn
;
114 if (state
== NULL
|| inBuffer
== NULL
|| outDist
== NULL
)
115 return SJME_ERROR_NULL_ARGUMENTS
;
117 /* Read in distance code. */
119 if (sjme_error_is(error
= state
->readDist(
120 state
, &base
)) || base
== INT32_MAX
)
121 return sjme_error_default(error
);
123 /* Must be too high of a code! */
125 return SJME_ERROR_INFLATE_INVALID_CODE
;
127 /* Calculate the required distance to use */
129 for (i
= 0; i
< base
; i
++)
131 /* Similar to length but in groups of two. */
135 result
+= (1 << ((((i
/ 2)) - 1)));
138 /* Also any extra bits needed as part of the distance. */
141 /* Similarly the same as length, just smaller parts. */
142 i
= ((base
/ 2)) - 1;
144 /* Read in given bits. */
146 if (sjme_error_is(error
= sjme_inflate_bitIn(
148 SJME_INFLATE_LSB
, SJME_INFLATE_POP
,
151 return sjme_error_default(error
);
153 /* Add in extra value. */
157 /* Give the result. */
159 return SJME_ERROR_NONE
;
162 sjme_errorCode
sjme_inflate_processLength(
163 sjme_attrInNotNull sjme_inflate_state
* state
,
164 sjme_attrInNotNull sjme_inflate_buffer
* inBuffer
,
165 sjme_attrInRange(257, 285) sjme_juint code
,
166 sjme_attrOutNotNull sjme_juint
* outLength
)
168 sjme_errorCode error
;
169 sjme_juint base
, result
, i
, readIn
;
171 if (state
== NULL
|| inBuffer
== NULL
|| outLength
== NULL
)
172 return SJME_ERROR_NULL_ARGUMENTS
;
174 if (code
< 257 || code
> 285)
175 return SJME_ERROR_INFLATE_INVALID_CODE
;
177 /* Maximum distance possible? */
181 return SJME_ERROR_NONE
;
184 /* Get the base distance code. */
187 /* Calculate the required length to use */
189 for (i
= 0; i
< base
; i
++)
191 /* Determine how many groups of 4 the code is long. Since zero */
192 /* appears as items then subtract 1 to make it longer. However */
193 /* after the first 8 it goes up in a standard pattern. */
197 result
+= (1 << ((((i
/ 4)) - 1)));
200 /* Also any extra bits needed as part of the length. */
203 /* Calculate needed amount. Same as the length, it goes up in */
204 /* a specific pattern as well except without single increments. */
205 i
= ((base
/ 4)) - 1;
207 /* Read in given bits. */
209 if (sjme_error_is(error
= sjme_inflate_bitIn(
211 SJME_INFLATE_LSB
, SJME_INFLATE_POP
,
214 return sjme_error_default(error
);
216 /* Add in extra value. */
220 /* Give the result. */
222 return SJME_ERROR_NONE
;
225 sjme_errorCode
sjme_inflate_processWindow(
226 sjme_attrInNotNull sjme_inflate_state
* state
,
227 sjme_attrInNotNull sjme_inflate_buffer
* outBuffer
,
228 sjme_attrInNotNull sjme_inflate_window
* window
,
229 sjme_attrInPositive sjme_juint windowDist
,
230 sjme_attrInPositive sjme_juint windowLen
)
232 sjme_errorCode error
;
233 sjme_juint maxLen
, i
, w
, readBase
;
236 if (state
== NULL
|| outBuffer
== NULL
|| window
== NULL
)
237 return SJME_ERROR_NULL_ARGUMENTS
;
239 /* The length chunk can never exceed the distance, however it does */
240 /* wrap around accordingly. */
241 maxLen
= (windowLen
> windowDist
? windowDist
: windowLen
);
243 sjme_message("Dist %d > %d", windowDist
, window
->length
);
245 /* Cannot read more than what there is. */
246 if (windowDist
> window
->length
)
247 return SJME_ERROR_INFLATE_DISTANCE_OUT_OF_RANGE
;
249 /* Setup buffer for the sliding window chunk. */
250 chunk
= sjme_alloca(sizeof(*chunk
) * maxLen
);
252 return SJME_ERROR_OUT_OF_MEMORY
;
253 memset(chunk
, 0, sizeof(*chunk
) * maxLen
);
255 /* Can read in one full slice? */
256 readBase
= (window
->end
- windowDist
) & SJME_INFLATE_WINDOW_MASK
;
257 if (readBase
< window
->end
)
258 memmove(chunk
, &window
->window
[readBase
], maxLen
);
260 /* Need to copy in two slices. */
263 i
= SJME_INFLATE_WINDOW_SIZE
- readBase
;
264 memmove(&chunk
[0], &window
->window
[readBase
], i
);
265 memmove(&chunk
[i
], &window
->window
[0], maxLen
- i
);
269 sjme_message("Dist chunk: %d", maxLen
);
270 sjme_message_hexDump(chunk
, maxLen
);
273 for (i
= 0, w
= 0; i
< windowLen
; i
++)
275 /* Write value to the output. */
276 if (sjme_error_is(error
= sjme_inflate_bitOut(
277 outBuffer
, SJME_INFLATE_LSB
, window
,
278 8, chunk
[w
] & 0xFF)))
279 return sjme_error_default(error
);
281 /* Move window up, handle wrap around. */
287 return SJME_ERROR_NONE
;
291 sjme_errorCode
sjme_inflate_readCodeDynamic(
292 sjme_attrInNotNull sjme_inflate_state
* state
,
293 sjme_attrOutNotNull sjme_juint
* outCode
)
295 if (state
== NULL
|| outCode
== NULL
)
296 return SJME_ERROR_NULL_ARGUMENTS
;
299 return sjme_error_notImplemented(0);
302 sjme_errorCode
sjme_inflate_readDistDynamic(
303 sjme_attrInNotNull sjme_inflate_state
* state
,
304 sjme_attrOutNotNull sjme_juint
* outDist
)
306 if (state
== NULL
|| outDist
== NULL
)
307 return SJME_ERROR_NULL_ARGUMENTS
;
310 return sjme_error_notImplemented(0);
313 sjme_errorCode
sjme_inflate_readCodeFixed(
314 sjme_attrInNotNull sjme_inflate_state
* state
,
315 sjme_attrOutNotNull sjme_juint
* outCode
)
317 sjme_errorCode error
;
318 sjme_inflate_buffer
* inBuffer
;
319 sjme_juint hiSeven
, bitsNeeded
, litBase
, litSub
, raw
;
321 if (state
== NULL
|| outCode
== NULL
)
322 return SJME_ERROR_NULL_ARGUMENTS
;
324 /* We at least need 7 bits for the minimum code length. */
325 inBuffer
= &state
->input
;
326 if (sjme_error_is(error
= sjme_inflate_bitNeed(inBuffer
,
328 return sjme_error_default(error
);
330 /* Read in upper 7 bits first, as a peek. */
332 if (sjme_error_is(error
= sjme_inflate_bitIn(inBuffer
,
333 SJME_INFLATE_MSB
, SJME_INFLATE_PEEK
, 7,
334 &hiSeven
)) || hiSeven
== INT32_MAX
)
335 return sjme_error_default(error
);
337 /* Determine the actual number of bits we need. */
338 /* 0b0000000 - 0b0010111 */
339 if (hiSeven
>= 0 && hiSeven
<= 23)
346 /* 0b0011000[0] - 0b1011111[1] */
347 else if (hiSeven
>= 24 && hiSeven
<= 95)
354 /* 0b1100000[0] - 0b1100011[1] */
355 else if (hiSeven
>= 96 && hiSeven
<= 99)
362 /* 0b1100100[00] - 0b1111111[11] */
370 /* Now that we know what we need, make sure we have it. */
371 if (sjme_error_is(error
= sjme_inflate_bitNeed(inBuffer
,
373 return sjme_error_default(error
);
375 /* Pop everything off now, so we can recover the code. */
377 if (sjme_error_is(error
= sjme_inflate_bitIn(inBuffer
,
378 SJME_INFLATE_MSB
, SJME_INFLATE_POP
,
380 &raw
)) || raw
== INT32_MAX
)
381 return sjme_error_default(error
);
383 /* Recover the code. */
384 *outCode
= litBase
+ (raw
- litSub
);
385 return SJME_ERROR_NONE
;
388 sjme_errorCode
sjme_inflate_readDistFixed(
389 sjme_attrInNotNull sjme_inflate_state
* state
,
390 sjme_attrOutNotNull sjme_juint
* outDist
)
392 if (state
== NULL
|| outDist
== NULL
)
393 return SJME_ERROR_NULL_ARGUMENTS
;
395 /* Just a basic bit read. */
396 return sjme_inflate_bitIn(&state
->input
,
397 SJME_INFLATE_MSB
, SJME_INFLATE_POP
,