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