git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / formats / libchdr / libchdr_zlib.c
CommitLineData
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
60chd_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
86void 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
98chd_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
152chd_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
189void 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
214chd_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
248voidpf 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
299void 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}