Commit | Line | Data |
---|---|---|
3719602c PC |
1 | /*************************************************************************** |
2 | ||
3 | libchdr_zlib.c | |
4 | ||
5 | MAME Compressed Hunks of Data file format | |
6 | ||
7 | **************************************************************************** | |
8 | ||
9 | Copyright Aaron Giles | |
10 | All rights reserved. | |
11 | ||
12 | Redistribution and use in source and binary forms, with or without | |
13 | modification, are permitted provided that the following conditions are | |
14 | met: | |
15 | ||
16 | * Redistributions of source code must retain the above copyright | |
17 | notice, this list of conditions and the following disclaimer. | |
18 | * Redistributions in binary form must reproduce the above copyright | |
19 | notice, this list of conditions and the following disclaimer in | |
20 | the documentation and/or other materials provided with the | |
21 | distribution. | |
22 | * Neither the name 'MAME' nor the names of its contributors may be | |
23 | used to endorse or promote products derived from this software | |
24 | without specific prior written permission. | |
25 | ||
26 | THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND ANY EXPRESS OR | |
27 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |
28 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |
29 | DISCLAIMED. IN NO EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT, | |
30 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |
31 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |
32 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
33 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
34 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
35 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
36 | POSSIBILITY OF SUCH DAMAGE. | |
37 | ||
38 | ***************************************************************************/ | |
39 | ||
40 | #include <stddef.h> | |
41 | #include <stdint.h> | |
42 | #include <stdio.h> | |
43 | #include <stdlib.h> | |
44 | #include <string.h> | |
45 | #include <libchdr/chd.h> | |
46 | #include <libchdr/minmax.h> | |
47 | #include <libchdr/cdrom.h> | |
48 | #include <libchdr/huffman.h> | |
49 | #include <libchdr/libchdr_zlib.h> | |
50 | #include <zlib.h> | |
51 | ||
52 | #include <retro_inline.h> | |
53 | #include <streams/file_stream.h> | |
54 | ||
55 | #define TRUE 1 | |
56 | #define FALSE 0 | |
57 | ||
58 | /* cdzl */ | |
59 | ||
60 | chd_error cdzl_codec_init(void *codec, uint32_t hunkbytes) | |
61 | { | |
62 | chd_error ret; | |
63 | cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; | |
64 | ||
65 | /* make sure the CHD's hunk size is an even multiple of the frame size */ | |
66 | if (hunkbytes % CD_FRAME_SIZE != 0) | |
67 | return CHDERR_CODEC_ERROR; | |
68 | ||
69 | cdzl->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes); | |
70 | if (cdzl->buffer == NULL) | |
71 | return CHDERR_OUT_OF_MEMORY; | |
72 | ||
73 | ret = zlib_codec_init(&cdzl->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); | |
74 | if (ret != CHDERR_NONE) | |
75 | return ret; | |
76 | ||
77 | #ifdef WANT_SUBCODE | |
78 | ret = zlib_codec_init(&cdzl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA); | |
79 | if (ret != CHDERR_NONE) | |
80 | return ret; | |
81 | #endif | |
82 | ||
83 | return CHDERR_NONE; | |
84 | } | |
85 | ||
86 | void cdzl_codec_free(void *codec) | |
87 | { | |
88 | cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; | |
89 | ||
90 | zlib_codec_free(&cdzl->base_decompressor); | |
91 | #ifdef WANT_SUBCODE | |
92 | zlib_codec_free(&cdzl->subcode_decompressor); | |
93 | #endif | |
94 | if (cdzl->buffer) | |
95 | free(cdzl->buffer); | |
96 | } | |
97 | ||
98 | chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) | |
99 | { | |
100 | #ifdef WANT_RAW_DATA_SECTOR | |
101 | uint8_t *sector; | |
102 | #endif | |
103 | uint32_t framenum; | |
104 | cdzl_codec_data* cdzl = (cdzl_codec_data*)codec; | |
105 | ||
106 | /* determine header bytes */ | |
107 | uint32_t frames = destlen / CD_FRAME_SIZE; | |
108 | uint32_t complen_bytes = (destlen < 65536) ? 2 : 3; | |
109 | uint32_t ecc_bytes = (frames + 7) / 8; | |
110 | uint32_t header_bytes = ecc_bytes + complen_bytes; | |
111 | ||
112 | /* extract compressed length of base */ | |
113 | uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1]; | |
114 | if (complen_bytes > 2) | |
115 | complen_base = (complen_base << 8) | src[ecc_bytes + 2]; | |
116 | ||
117 | /* reset and decode */ | |
118 | zlib_codec_decompress(&cdzl->base_decompressor, &src[header_bytes], complen_base, &cdzl->buffer[0], frames * CD_MAX_SECTOR_DATA); | |
119 | #ifdef WANT_SUBCODE | |
120 | zlib_codec_decompress(&cdzl->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdzl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA); | |
121 | #endif | |
122 | ||
123 | /* reassemble the data */ | |
124 | for (framenum = 0; framenum < frames; framenum++) | |
125 | { | |
126 | memcpy(&dest[framenum * CD_FRAME_SIZE], &cdzl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA); | |
127 | #ifdef WANT_SUBCODE | |
128 | memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdzl->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA); | |
129 | #endif | |
130 | ||
131 | #ifdef WANT_RAW_DATA_SECTOR | |
132 | /* reconstitute the ECC data and sync header */ | |
133 | sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE]; | |
134 | if ((src[framenum / 8] & (1 << (framenum % 8))) != 0) | |
135 | { | |
136 | memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header)); | |
137 | ecc_generate(sector); | |
138 | } | |
139 | #endif | |
140 | } | |
141 | return CHDERR_NONE; | |
142 | } | |
143 | ||
144 | /*************************************************************************** | |
145 | ZLIB COMPRESSION CODEC | |
146 | ***************************************************************************/ | |
147 | ||
148 | /*------------------------------------------------- | |
149 | zlib_codec_init - initialize the ZLIB codec | |
150 | -------------------------------------------------*/ | |
151 | ||
152 | chd_error zlib_codec_init(void *codec, uint32_t hunkbytes) | |
153 | { | |
154 | int zerr; | |
155 | chd_error err; | |
156 | zlib_codec_data *data = (zlib_codec_data*)codec; | |
157 | ||
158 | /* clear the buffers */ | |
159 | memset(data, 0, sizeof(zlib_codec_data)); | |
160 | ||
161 | /* init the inflater first */ | |
162 | data->inflater.next_in = (Bytef *)data; /* bogus, but that's ok */ | |
163 | data->inflater.avail_in = 0; | |
164 | data->inflater.zalloc = zlib_fast_alloc; | |
165 | data->inflater.zfree = zlib_fast_free; | |
166 | data->inflater.opaque = &data->allocator; | |
167 | zerr = inflateInit2(&data->inflater, -MAX_WBITS); | |
168 | ||
169 | /* convert errors */ | |
170 | if (zerr == Z_MEM_ERROR) | |
171 | err = CHDERR_OUT_OF_MEMORY; | |
172 | else if (zerr != Z_OK) | |
173 | err = CHDERR_CODEC_ERROR; | |
174 | else | |
175 | err = CHDERR_NONE; | |
176 | ||
177 | /* handle an error */ | |
178 | if (err != CHDERR_NONE) | |
179 | zlib_codec_free(data); | |
180 | ||
181 | return err; | |
182 | } | |
183 | ||
184 | /*------------------------------------------------- | |
185 | zlib_codec_free - free data for the ZLIB | |
186 | codec | |
187 | -------------------------------------------------*/ | |
188 | ||
189 | void zlib_codec_free(void *codec) | |
190 | { | |
191 | zlib_codec_data *data = (zlib_codec_data *)codec; | |
192 | ||
193 | /* deinit the streams */ | |
194 | if (data != NULL) | |
195 | { | |
196 | int i; | |
197 | zlib_allocator alloc; | |
198 | ||
199 | inflateEnd(&data->inflater); | |
200 | ||
201 | /* free our fast memory */ | |
202 | alloc = data->allocator; | |
203 | for (i = 0; i < MAX_ZLIB_ALLOCS; i++) | |
204 | if (alloc.allocptr[i]) | |
205 | free(alloc.allocptr[i]); | |
206 | } | |
207 | } | |
208 | ||
209 | /*------------------------------------------------- | |
210 | zlib_codec_decompress - decomrpess data using | |
211 | the ZLIB codec | |
212 | -------------------------------------------------*/ | |
213 | ||
214 | chd_error zlib_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen) | |
215 | { | |
216 | zlib_codec_data *data = (zlib_codec_data *)codec; | |
217 | int zerr; | |
218 | ||
219 | /* reset the decompressor */ | |
220 | data->inflater.next_in = (Bytef *)src; | |
221 | data->inflater.avail_in = complen; | |
222 | data->inflater.total_in = 0; | |
223 | data->inflater.next_out = (Bytef *)dest; | |
224 | data->inflater.avail_out = destlen; | |
225 | data->inflater.total_out = 0; | |
226 | zerr = inflateReset(&data->inflater); | |
227 | if (zerr != Z_OK) | |
228 | return CHDERR_DECOMPRESSION_ERROR; | |
229 | ||
230 | /* do it */ | |
231 | zerr = inflate(&data->inflater, Z_FINISH); | |
232 | (void)zerr; | |
233 | if (data->inflater.total_out != destlen) | |
234 | return CHDERR_DECOMPRESSION_ERROR; | |
235 | ||
236 | return CHDERR_NONE; | |
237 | } | |
238 | ||
239 | /*------------------------------------------------- | |
240 | zlib_fast_alloc - fast malloc for ZLIB, which | |
241 | allocates and frees memory frequently | |
242 | -------------------------------------------------*/ | |
243 | ||
244 | /* Huge alignment values for possible SIMD optimization by compiler (NEON, SSE, AVX) */ | |
245 | #define ZLIB_MIN_ALIGNMENT_BITS 512 | |
246 | #define ZLIB_MIN_ALIGNMENT_BYTES (ZLIB_MIN_ALIGNMENT_BITS / 8) | |
247 | ||
248 | voidpf zlib_fast_alloc(voidpf opaque, uInt items, uInt size) | |
249 | { | |
250 | zlib_allocator *alloc = (zlib_allocator *)opaque; | |
251 | uintptr_t paddr = 0; | |
252 | UINT32 *ptr; | |
253 | int i; | |
254 | ||
255 | /* compute the size, rounding to the nearest 1k */ | |
256 | size = (size * items + 0x3ff) & ~0x3ff; | |
257 | ||
258 | /* reuse a hunk if we can */ | |
259 | for (i = 0; i < MAX_ZLIB_ALLOCS; i++) | |
260 | { | |
261 | ptr = alloc->allocptr[i]; | |
262 | if (ptr && size == *ptr) | |
263 | { | |
264 | /* set the low bit of the size so we don't match next time */ | |
265 | *ptr |= 1; | |
266 | ||
267 | /* return aligned block address */ | |
268 | return (voidpf)(alloc->allocptr2[i]); | |
269 | } | |
270 | } | |
271 | ||
272 | /* alloc a new one */ | |
273 | ptr = (UINT32 *)malloc(size + sizeof(UINT32) + ZLIB_MIN_ALIGNMENT_BYTES); | |
274 | if (!ptr) | |
275 | return NULL; | |
276 | ||
277 | /* put it into the list */ | |
278 | for (i = 0; i < MAX_ZLIB_ALLOCS; i++) | |
279 | if (!alloc->allocptr[i]) | |
280 | { | |
281 | alloc->allocptr[i] = ptr; | |
282 | paddr = (((uintptr_t)ptr) + sizeof(UINT32) + (ZLIB_MIN_ALIGNMENT_BYTES-1)) & (~(ZLIB_MIN_ALIGNMENT_BYTES-1)); | |
283 | alloc->allocptr2[i] = (uint32_t*)paddr; | |
284 | break; | |
285 | } | |
286 | ||
287 | /* set the low bit of the size so we don't match next time */ | |
288 | *ptr = size | 1; | |
289 | ||
290 | /* return aligned block address */ | |
291 | return (voidpf)paddr; | |
292 | } | |
293 | ||
294 | /*------------------------------------------------- | |
295 | zlib_fast_free - fast free for ZLIB, which | |
296 | allocates and frees memory frequently | |
297 | -------------------------------------------------*/ | |
298 | ||
299 | void zlib_fast_free(voidpf opaque, voidpf address) | |
300 | { | |
301 | zlib_allocator *alloc = (zlib_allocator *)opaque; | |
302 | UINT32 *ptr = (UINT32 *)address; | |
303 | int i; | |
304 | ||
305 | /* find the hunk */ | |
306 | for (i = 0; i < MAX_ZLIB_ALLOCS; i++) | |
307 | if (ptr == alloc->allocptr2[i]) | |
308 | { | |
309 | /* clear the low bit of the size to allow matches */ | |
310 | *(alloc->allocptr[i]) &= ~1; | |
311 | return; | |
312 | } | |
313 | } |