git subrepo clone https://github.com/libretro/libretro-common.git deps/libretro-common
[pcsx_rearmed.git] / deps / libretro-common / formats / libchdr / libchdr_lzma.c
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
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  */
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
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
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
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
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
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 */
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
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
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 }