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