1 /***************************************************************************
2
3 libchdr_lzma_codec.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/lzma.h>
49 #include <libchdr/huffman.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 /***************************************************************************
59 * LZMA ALLOCATOR HELPER
60 ***************************************************************************
61 */
62
63 /*-------------------------------------------------
64 * lzma_fast_alloc - fast malloc for lzma, which
65 * allocates and frees memory frequently
66 *-------------------------------------------------
67 */
68
lzma_fast_alloc(void * p,size_t size)69 static void *lzma_fast_alloc(void *p, size_t size)
70 {
71 int scan;
72 uint32_t *addr = NULL;
73 lzma_allocator *codec = (lzma_allocator *)(p);
74
75 /* compute the size, rounding to the nearest 1k */
76 size = (size + 0x3ff) & ~0x3ff;
77
78 /* reuse a hunk if we can */
79 for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
80 {
81 uint32_t *ptr = codec->allocptr[scan];
82 if (ptr != NULL && size == *ptr)
83 {
84 /* set the low bit of the size so we don't match next time */
85 *ptr |= 1;
86 return ptr + 1;
87 }
88 }
89
90 /* alloc a new one and put it into the list */
91 addr = (uint32_t *)malloc(sizeof(uint32_t) * size + sizeof(uintptr_t));
92 if (!addr)
93 return NULL;
94
95 for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
96 {
97 if (codec->allocptr[scan] == NULL)
98 {
99 codec->allocptr[scan] = addr;
100 break;
101 }
102 }
103
104 /* set the low bit of the size so we don't match next time */
105 *addr = (uint32_t)(size | 1);
106 return addr + (sizeof(uint32_t) == sizeof(uintptr_t) ? 1 : 2);
107 }
108
109 /*-------------------------------------------------
110 * lzma_fast_free - fast free for lzma, which
111 * allocates and frees memory frequently
112 *-------------------------------------------------
113 */
lzma_fast_free(void * p,void * address)114 static void lzma_fast_free(void *p, void *address)
115 {
116 int scan;
117 uint32_t *ptr;
118 lzma_allocator *codec;
119 if (address == NULL)
120 return;
121
122 codec = (lzma_allocator *)(p);
123
124 /* find the hunk */
125 ptr = (uint32_t *)(address) - 1;
126 for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
127 {
128 if (ptr == codec->allocptr[scan])
129 {
130 /* clear the low bit of the size to allow matches */
131 *ptr &= ~1;
132 return;
133 }
134 }
135 }
136
137 /*-------------------------------------------------
138 * lzma_allocator_init
139 *-------------------------------------------------
140 */
141
lzma_allocator_init(void * p)142 void lzma_allocator_init(void* p)
143 {
144 lzma_allocator *codec = (lzma_allocator *)(p);
145
146 /* reset pointer list */
147 memset(codec->allocptr, 0, sizeof(codec->allocptr));
148 codec->Alloc = lzma_fast_alloc;
149 codec->Free = lzma_fast_free;
150 }
151
152 /*-------------------------------------------------
153 * lzma_allocator_free
154 *-------------------------------------------------
155 */
156
lzma_allocator_free(void * p)157 void lzma_allocator_free(void* p )
158 {
159 lzma_allocator *codec = (lzma_allocator *)(p);
160
161 /* free our memory */
162 int i;
163 for (i = 0 ; i < MAX_LZMA_ALLOCS ; i++)
164 {
165 if (codec->allocptr[i] != NULL)
166 free(codec->allocptr[i]);
167 }
168 }
169
170 /***************************************************************************
171 * LZMA DECOMPRESSOR
172 ***************************************************************************
173 */
174
175 /*-------------------------------------------------
176 * lzma_codec_init - constructor
177 *-------------------------------------------------
178 */
179
lzma_codec_init(void * codec,uint32_t hunkbytes)180 chd_error lzma_codec_init(void* codec, uint32_t hunkbytes)
181 {
182 CLzmaEncProps encoder_props;
183 CLzmaEncHandle enc;
184 uint8_t decoder_props[LZMA_PROPS_SIZE];
185 lzma_allocator* alloc;
186 size_t props_size;
187 lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
188
189 /* construct the decoder */
190 LzmaDec_Construct(&lzma_codec->decoder);
191
192 /* FIXME: this code is written in a way that makes it impossible to safely upgrade the LZMA SDK
193 * This code assumes that the current version of the encoder imposes the same requirements on the
194 * decoder as the encoder used to produce the file. This is not necessarily true. The format
195 * needs to be changed so the encoder properties are written to the file.
196
197 * configure the properties like the compressor did */
198 LzmaEncProps_Init(&encoder_props);
199 encoder_props.level = 9;
200 encoder_props.reduceSize = hunkbytes;
201 LzmaEncProps_Normalize(&encoder_props);
202
203 /* convert to decoder properties */
204 alloc = &lzma_codec->allocator;
205 lzma_allocator_init(alloc);
206 enc = LzmaEnc_Create((ISzAlloc*)alloc);
207 if (!enc)
208 return CHDERR_DECOMPRESSION_ERROR;
209 if (LzmaEnc_SetProps(enc, &encoder_props) != SZ_OK)
210 {
211 LzmaEnc_Destroy(enc, (ISzAlloc*)&alloc, (ISzAlloc*)&alloc);
212 return CHDERR_DECOMPRESSION_ERROR;
213 }
214 props_size = sizeof(decoder_props);
215 if (LzmaEnc_WriteProperties(enc, decoder_props, &props_size) != SZ_OK)
216 {
217 LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc);
218 return CHDERR_DECOMPRESSION_ERROR;
219 }
220 LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc);
221
222 /* do memory allocations */
223 if (LzmaDec_Allocate(&lzma_codec->decoder, decoder_props, LZMA_PROPS_SIZE, (ISzAlloc*)alloc) != SZ_OK)
224 return CHDERR_DECOMPRESSION_ERROR;
225
226 /* Okay */
227 return CHDERR_NONE;
228 }
229
230 /*-------------------------------------------------
231 * lzma_codec_free
232 *-------------------------------------------------
233 */
234
lzma_codec_free(void * codec)235 void lzma_codec_free(void* codec)
236 {
237 lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
238 lzma_allocator* alloc = &lzma_codec->allocator;
239
240 /* free memory */
241 LzmaDec_Free(&lzma_codec->decoder, (ISzAlloc*)&lzma_codec->allocator);
242 lzma_allocator_free(alloc);
243 }
244
245 /*-------------------------------------------------
246 * decompress - decompress data using the LZMA
247 * codec
248 *-------------------------------------------------
249 */
250
lzma_codec_decompress(void * codec,const uint8_t * src,uint32_t complen,uint8_t * dest,uint32_t destlen)251 chd_error lzma_codec_decompress(void* codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
252 {
253 ELzmaStatus status;
254 SRes res;
255 size_t consumedlen, decodedlen;
256 /* initialize */
257 lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
258 LzmaDec_Init(&lzma_codec->decoder);
259
260 /* decode */
261 consumedlen = complen;
262 decodedlen = destlen;
263 res = LzmaDec_DecodeToBuf(&lzma_codec->decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status);
264 if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen)
265 return CHDERR_DECOMPRESSION_ERROR;
266 return CHDERR_NONE;
267 }
268
269 /* cdlz */
cdlz_codec_init(void * codec,uint32_t hunkbytes)270 chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes)
271 {
272 chd_error ret;
273 cdlz_codec_data* cdlz = (cdlz_codec_data*) codec;
274
275 /* allocate buffer */
276 cdlz->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);
277 if (cdlz->buffer == NULL)
278 return CHDERR_OUT_OF_MEMORY;
279
280 ret = lzma_codec_init(&cdlz->base_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
281 if (ret != CHDERR_NONE)
282 return ret;
283
284 #ifdef WANT_SUBCODE
285 ret = zlib_codec_init(&cdlz->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
286 if (ret != CHDERR_NONE)
287 return ret;
288 #endif
289
290 return CHDERR_NONE;
291 }
292
cdlz_codec_free(void * codec)293 void cdlz_codec_free(void* codec)
294 {
295 cdlz_codec_data* cdlz = (cdlz_codec_data*) codec;
296
297 lzma_codec_free(&cdlz->base_decompressor);
298 #ifdef WANT_SUBCODE
299 zlib_codec_free(&cdlz->subcode_decompressor);
300 #endif
301 if (cdlz->buffer)
302 free(cdlz->buffer);
303 }
304
cdlz_codec_decompress(void * codec,const uint8_t * src,uint32_t complen,uint8_t * dest,uint32_t destlen)305 chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
306 {
307 #ifdef WANT_RAW_DATA_SECTOR
308 uint8_t *sector;
309 #endif
310 uint32_t framenum;
311 cdlz_codec_data* cdlz = (cdlz_codec_data*)codec;
312
313 /* determine header bytes */
314 uint32_t frames = destlen / CD_FRAME_SIZE;
315 uint32_t complen_bytes = (destlen < 65536) ? 2 : 3;
316 uint32_t ecc_bytes = (frames + 7) / 8;
317 uint32_t header_bytes = ecc_bytes + complen_bytes;
318
319 /* extract compressed length of base */
320 uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1];
321 if (complen_bytes > 2)
322 complen_base = (complen_base << 8) | src[ecc_bytes + 2];
323
324 /* reset and decode */
325 lzma_codec_decompress(&cdlz->base_decompressor, &src[header_bytes], complen_base, &cdlz->buffer[0], frames * CD_MAX_SECTOR_DATA);
326 #ifdef WANT_SUBCODE
327 if (header_bytes + complen_base >= complen)
328 return CHDERR_DECOMPRESSION_ERROR;
329 zlib_codec_decompress(&cdlz->subcode_decompressor, &src[header_bytes + complen_base], complen - complen_base - header_bytes, &cdlz->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA);
330 #endif
331
332 /* reassemble the data */
333 for (framenum = 0; framenum < frames; framenum++)
334 {
335 memcpy(&dest[framenum * CD_FRAME_SIZE], &cdlz->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);
336 #ifdef WANT_SUBCODE
337 memcpy(&dest[framenum * CD_FRAME_SIZE + CD_MAX_SECTOR_DATA], &cdlz->buffer[frames * CD_MAX_SECTOR_DATA + framenum * CD_MAX_SUBCODE_DATA], CD_MAX_SUBCODE_DATA);
338 #endif
339
340 #ifdef WANT_RAW_DATA_SECTOR
341 /* reconstitute the ECC data and sync header */
342 sector = (uint8_t *)&dest[framenum * CD_FRAME_SIZE];
343 if ((src[framenum / 8] & (1 << (framenum % 8))) != 0)
344 {
345 memcpy(sector, s_cd_sync_header, sizeof(s_cd_sync_header));
346 ecc_generate(sector);
347 }
348 #endif
349 }
350 return CHDERR_NONE;
351 }
352