mirror of
https://github.com/ekeeke/Genesis-Plus-GX.git
synced 2024-11-10 21:05:12 +01:00
Update libchdr
This commit is contained in:
parent
cf1e6fdf11
commit
2dfa1317b0
@ -1,5 +1,6 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles
|
||||
/* license:BSD-3-Clause
|
||||
* copyright-holders:Aaron Giles
|
||||
*/
|
||||
/***************************************************************************
|
||||
|
||||
bitstream.c
|
||||
@ -11,15 +12,17 @@
|
||||
#include "bitstream.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
//**************************************************************************
|
||||
// INLINE FUNCTIONS
|
||||
//**************************************************************************
|
||||
/***************************************************************************
|
||||
* INLINE FUNCTIONS
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
int bitstream_overflow(struct bitstream* bitstream) { return ((bitstream->doffset - bitstream->bits / 8) > bitstream->dlength); }
|
||||
|
||||
//-------------------------------------------------
|
||||
// create_bitstream - constructor
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* create_bitstream - constructor
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
struct bitstream* create_bitstream(const void *src, uint32_t srclength)
|
||||
{
|
||||
@ -33,17 +36,18 @@ struct bitstream* create_bitstream(const void *src, uint32_t srclength)
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------
|
||||
// bitstream_peek - fetch the requested number of bits
|
||||
// but don't advance the input pointer
|
||||
//-----------------------------------------------------
|
||||
/*-----------------------------------------------------
|
||||
* bitstream_peek - fetch the requested number of bits
|
||||
* but don't advance the input pointer
|
||||
*-----------------------------------------------------
|
||||
*/
|
||||
|
||||
uint32_t bitstream_peek(struct bitstream* bitstream, int numbits)
|
||||
{
|
||||
if (numbits == 0)
|
||||
return 0;
|
||||
|
||||
// fetch data if we need more
|
||||
/* fetch data if we need more */
|
||||
if (numbits > bitstream->bits)
|
||||
{
|
||||
while (bitstream->bits <= 24)
|
||||
@ -55,15 +59,16 @@ uint32_t bitstream_peek(struct bitstream* bitstream, int numbits)
|
||||
}
|
||||
}
|
||||
|
||||
// return the data
|
||||
/* return the data */
|
||||
return bitstream->buffer >> (32 - numbits);
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------
|
||||
// bitstream_remove - advance the input pointer by the
|
||||
// specified number of bits
|
||||
//-----------------------------------------------------
|
||||
/*-----------------------------------------------------
|
||||
* bitstream_remove - advance the input pointer by the
|
||||
* specified number of bits
|
||||
*-----------------------------------------------------
|
||||
*/
|
||||
|
||||
void bitstream_remove(struct bitstream* bitstream, int numbits)
|
||||
{
|
||||
@ -72,9 +77,10 @@ void bitstream_remove(struct bitstream* bitstream, int numbits)
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------
|
||||
// bitstream_read - fetch the requested number of bits
|
||||
//-----------------------------------------------------
|
||||
/*-----------------------------------------------------
|
||||
* bitstream_read - fetch the requested number of bits
|
||||
*-----------------------------------------------------
|
||||
*/
|
||||
|
||||
uint32_t bitstream_read(struct bitstream* bitstream, int numbits)
|
||||
{
|
||||
@ -84,9 +90,10 @@ uint32_t bitstream_read(struct bitstream* bitstream, int numbits)
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// read_offset - return the current read offset
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* read_offset - return the current read offset
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
uint32_t bitstream_read_offset(struct bitstream* bitstream)
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles
|
||||
/***************************************************************************
|
||||
/* license:BSD-3-Clause
|
||||
* copyright-holders:Aaron Giles
|
||||
***************************************************************************
|
||||
|
||||
bitstream.h
|
||||
|
||||
@ -15,18 +15,19 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
//**************************************************************************
|
||||
// TYPE DEFINITIONS
|
||||
//**************************************************************************
|
||||
/***************************************************************************
|
||||
* TYPE DEFINITIONS
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
// helper class for reading from a bit buffer
|
||||
/* helper class for reading from a bit buffer */
|
||||
struct bitstream
|
||||
{
|
||||
uint32_t buffer; // current bit accumulator
|
||||
int bits; // number of bits in the accumulator
|
||||
const uint8_t * read; // read pointer
|
||||
uint32_t doffset; // byte offset within the data
|
||||
uint32_t dlength; // length of the data
|
||||
uint32_t buffer; /* current bit accumulator */
|
||||
int bits; /* number of bits in the accumulator */
|
||||
const uint8_t * read; /* read pointer */
|
||||
uint32_t doffset; /* byte offset within the data */
|
||||
uint32_t dlength; /* length of the data */
|
||||
};
|
||||
|
||||
struct bitstream* create_bitstream(const void *src, uint32_t srclength);
|
||||
|
@ -90,51 +90,51 @@
|
||||
static const uint8_t s_cd_sync_header[12] = { 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00 };
|
||||
#endif
|
||||
|
||||
// V3-V4 entry types
|
||||
/* V3-V4 entry types */
|
||||
enum
|
||||
{
|
||||
V34_MAP_ENTRY_TYPE_INVALID = 0, // invalid type
|
||||
V34_MAP_ENTRY_TYPE_COMPRESSED = 1, // standard compression
|
||||
V34_MAP_ENTRY_TYPE_UNCOMPRESSED = 2, // uncompressed data
|
||||
V34_MAP_ENTRY_TYPE_MINI = 3, // mini: use offset as raw data
|
||||
V34_MAP_ENTRY_TYPE_SELF_HUNK = 4, // same as another hunk in this file
|
||||
V34_MAP_ENTRY_TYPE_PARENT_HUNK = 5, // same as a hunk in the parent file
|
||||
V34_MAP_ENTRY_TYPE_2ND_COMPRESSED = 6 // compressed with secondary algorithm (usually FLAC CDDA)
|
||||
V34_MAP_ENTRY_TYPE_INVALID = 0, /* invalid type */
|
||||
V34_MAP_ENTRY_TYPE_COMPRESSED = 1, /* standard compression */
|
||||
V34_MAP_ENTRY_TYPE_UNCOMPRESSED = 2, /* uncompressed data */
|
||||
V34_MAP_ENTRY_TYPE_MINI = 3, /* mini: use offset as raw data */
|
||||
V34_MAP_ENTRY_TYPE_SELF_HUNK = 4, /* same as another hunk in this file */
|
||||
V34_MAP_ENTRY_TYPE_PARENT_HUNK = 5, /* same as a hunk in the parent file */
|
||||
V34_MAP_ENTRY_TYPE_2ND_COMPRESSED = 6 /* compressed with secondary algorithm (usually FLAC CDDA) */
|
||||
};
|
||||
|
||||
// V5 compression types
|
||||
/* V5 compression types */
|
||||
enum
|
||||
{
|
||||
///< codec #0
|
||||
// these types are live when running
|
||||
/* codec #0
|
||||
* these types are live when running */
|
||||
COMPRESSION_TYPE_0 = 0,
|
||||
///< codec #1
|
||||
/* codec #1 */
|
||||
COMPRESSION_TYPE_1 = 1,
|
||||
///< codec #2
|
||||
/* codec #2 */
|
||||
COMPRESSION_TYPE_2 = 2,
|
||||
///< codec #3
|
||||
/* codec #3 */
|
||||
COMPRESSION_TYPE_3 = 3,
|
||||
///< no compression; implicit length = hunkbytes
|
||||
/* no compression; implicit length = hunkbytes */
|
||||
COMPRESSION_NONE = 4,
|
||||
///< same as another block in this chd
|
||||
/* same as another block in this chd */
|
||||
COMPRESSION_SELF = 5,
|
||||
///< same as a hunk's worth of units in the parent chd
|
||||
/* same as a hunk's worth of units in the parent chd */
|
||||
COMPRESSION_PARENT = 6,
|
||||
|
||||
///< start of small RLE run (4-bit length)
|
||||
// these additional pseudo-types are used for compressed encodings:
|
||||
/* start of small RLE run (4-bit length)
|
||||
* these additional pseudo-types are used for compressed encodings: */
|
||||
COMPRESSION_RLE_SMALL,
|
||||
///< start of large RLE run (8-bit length)
|
||||
/* start of large RLE run (8-bit length) */
|
||||
COMPRESSION_RLE_LARGE,
|
||||
///< same as the last COMPRESSION_SELF block
|
||||
/* same as the last COMPRESSION_SELF block */
|
||||
COMPRESSION_SELF_0,
|
||||
///< same as the last COMPRESSION_SELF block + 1
|
||||
/* same as the last COMPRESSION_SELF block + 1 */
|
||||
COMPRESSION_SELF_1,
|
||||
///< same block in the parent
|
||||
/* same block in the parent */
|
||||
COMPRESSION_PARENT_SELF,
|
||||
///< same as the last COMPRESSION_PARENT block
|
||||
/* same as the last COMPRESSION_PARENT block */
|
||||
COMPRESSION_PARENT_0,
|
||||
///< same as the last COMPRESSION_PARENT block + 1
|
||||
/* same as the last COMPRESSION_PARENT block + 1 */
|
||||
COMPRESSION_PARENT_1
|
||||
};
|
||||
|
||||
@ -225,7 +225,7 @@ struct _lzma_codec_data
|
||||
/* codec-private data for the CDZL codec */
|
||||
typedef struct _cdzl_codec_data cdzl_codec_data;
|
||||
struct _cdzl_codec_data {
|
||||
// internal state
|
||||
/* internal state */
|
||||
zlib_codec_data base_decompressor;
|
||||
#ifdef WANT_SUBCODE
|
||||
zlib_codec_data subcode_decompressor;
|
||||
@ -236,7 +236,7 @@ struct _cdzl_codec_data {
|
||||
/* codec-private data for the CDLZ codec */
|
||||
typedef struct _cdlz_codec_data cdlz_codec_data;
|
||||
struct _cdlz_codec_data {
|
||||
// internal state
|
||||
/* internal state */
|
||||
lzma_codec_data base_decompressor;
|
||||
#ifdef WANT_SUBCODE
|
||||
zlib_codec_data subcode_decompressor;
|
||||
@ -247,7 +247,7 @@ struct _cdlz_codec_data {
|
||||
/* codec-private data for the CDFL codec */
|
||||
typedef struct _cdfl_codec_data cdfl_codec_data;
|
||||
struct _cdfl_codec_data {
|
||||
// internal state
|
||||
/* internal state */
|
||||
int swap_endian;
|
||||
flac_decoder decoder;
|
||||
#ifdef WANT_SUBCODE
|
||||
@ -349,36 +349,39 @@ static chd_error cdfl_codec_init(void* codec, uint32_t hunkbytes);
|
||||
static void cdfl_codec_free(void* codec);
|
||||
static chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen);
|
||||
|
||||
//**************************************************************************
|
||||
// LZMA ALLOCATOR HELPER
|
||||
//**************************************************************************
|
||||
/***************************************************************************
|
||||
* LZMA ALLOCATOR HELPER
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
void *lzma_fast_alloc(void *p, size_t size);
|
||||
void lzma_fast_free(void *p, void *address);
|
||||
|
||||
//-------------------------------------------------
|
||||
// lzma_allocator_init
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* lzma_allocator_init
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void lzma_allocator_init(void* p)
|
||||
{
|
||||
lzma_allocator *codec = (lzma_allocator *)(p);
|
||||
|
||||
// reset pointer list
|
||||
/* reset pointer list */
|
||||
memset(codec->allocptr, 0, sizeof(codec->allocptr));
|
||||
codec->Alloc = lzma_fast_alloc;
|
||||
codec->Free = lzma_fast_free;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// lzma_allocator_free
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* lzma_allocator_free
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void lzma_allocator_free(void* p )
|
||||
{
|
||||
lzma_allocator *codec = (lzma_allocator *)(p);
|
||||
|
||||
// free our memory
|
||||
/* free our memory */
|
||||
int i;
|
||||
for (i = 0 ; i < MAX_LZMA_ALLOCS ; i++)
|
||||
{
|
||||
@ -387,33 +390,35 @@ void lzma_allocator_free(void* p )
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// lzma_fast_alloc - fast malloc for lzma, which
|
||||
// allocates and frees memory frequently
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* lzma_fast_alloc - fast malloc for lzma, which
|
||||
* allocates and frees memory frequently
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void *lzma_fast_alloc(void *p, size_t size)
|
||||
{
|
||||
int scan;
|
||||
uint32_t *addr;
|
||||
lzma_allocator *codec = (lzma_allocator *)(p);
|
||||
|
||||
// compute the size, rounding to the nearest 1k
|
||||
/* compute the size, rounding to the nearest 1k */
|
||||
size = (size + 0x3ff) & ~0x3ff;
|
||||
|
||||
// reuse a hunk if we can
|
||||
/* reuse a hunk if we can */
|
||||
for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
|
||||
{
|
||||
uint32_t *ptr = codec->allocptr[scan];
|
||||
if (ptr != NULL && size == *ptr)
|
||||
{
|
||||
// set the low bit of the size so we don't match next time
|
||||
/* set the low bit of the size so we don't match next time */
|
||||
*ptr |= 1;
|
||||
return ptr + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// alloc a new one and put it into the list
|
||||
uint32_t *addr = (uint32_t *)malloc(sizeof(uint8_t) * (size + sizeof(uint32_t)));
|
||||
/* alloc a new one and put it into the list */
|
||||
addr = (uint32_t *)malloc(sizeof(uint8_t) * (size + sizeof(uint32_t)));
|
||||
if (addr==NULL)
|
||||
return NULL;
|
||||
for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
|
||||
@ -425,70 +430,79 @@ void *lzma_fast_alloc(void *p, size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
// set the low bit of the size so we don't match next time
|
||||
/* set the low bit of the size so we don't match next time */
|
||||
*addr = size | 1;
|
||||
return addr + 1;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// lzma_fast_free - fast free for lzma, which
|
||||
// allocates and frees memory frequently
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* lzma_fast_free - fast free for lzma, which
|
||||
* allocates and frees memory frequently
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void lzma_fast_free(void *p, void *address)
|
||||
{
|
||||
int scan;
|
||||
uint32_t *ptr;
|
||||
lzma_allocator *codec;
|
||||
if (address == NULL)
|
||||
return;
|
||||
|
||||
int scan;
|
||||
lzma_allocator *codec = (lzma_allocator *)(p);
|
||||
codec = (lzma_allocator *)(p);
|
||||
|
||||
// find the hunk
|
||||
uint32_t *ptr = (uint32_t *)(address) - 1;
|
||||
/* find the hunk */
|
||||
ptr = (uint32_t *)(address) - 1;
|
||||
for (scan = 0; scan < MAX_LZMA_ALLOCS; scan++)
|
||||
{
|
||||
if (ptr == codec->allocptr[scan])
|
||||
{
|
||||
// clear the low bit of the size to allow matches
|
||||
/* clear the low bit of the size to allow matches */
|
||||
*ptr &= ~1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// LZMA DECOMPRESSOR
|
||||
//**************************************************************************
|
||||
/***************************************************************************
|
||||
* LZMA DECOMPRESSOR
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// lzma_codec_init - constructor
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* lzma_codec_init - constructor
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
chd_error lzma_codec_init(void* codec, uint32_t hunkbytes)
|
||||
{
|
||||
CLzmaEncProps encoder_props;
|
||||
CLzmaEncHandle enc;
|
||||
Byte decoder_props[LZMA_PROPS_SIZE];
|
||||
lzma_allocator* alloc;
|
||||
SizeT props_size;
|
||||
lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
|
||||
|
||||
// construct the decoder
|
||||
/* construct the decoder */
|
||||
LzmaDec_Construct(&lzma_codec->decoder);
|
||||
|
||||
// FIXME: this code is written in a way that makes it impossible to safely upgrade the LZMA SDK
|
||||
// This code assumes that the current version of the encoder imposes the same requirements on the
|
||||
// decoder as the encoder used to produce the file. This is not necessarily true. The format
|
||||
// needs to be changed so the encoder properties are written to the file.
|
||||
/* FIXME: this code is written in a way that makes it impossible to safely upgrade the LZMA SDK
|
||||
* This code assumes that the current version of the encoder imposes the same requirements on the
|
||||
* decoder as the encoder used to produce the file. This is not necessarily true. The format
|
||||
* needs to be changed so the encoder properties are written to the file.
|
||||
|
||||
// configure the properties like the compressor did
|
||||
CLzmaEncProps encoder_props;
|
||||
* configure the properties like the compressor did */
|
||||
LzmaEncProps_Init(&encoder_props);
|
||||
encoder_props.level = 9;
|
||||
encoder_props.reduceSize = hunkbytes;
|
||||
LzmaEncProps_Normalize(&encoder_props);
|
||||
|
||||
// convert to decoder properties
|
||||
lzma_allocator* alloc = &lzma_codec->allocator;
|
||||
/* convert to decoder properties */
|
||||
alloc = &lzma_codec->allocator;
|
||||
lzma_allocator_init(alloc);
|
||||
CLzmaEncHandle enc = LzmaEnc_Create((ISzAlloc*)alloc);
|
||||
enc = LzmaEnc_Create((ISzAlloc*)alloc);
|
||||
if (!enc)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
if (LzmaEnc_SetProps(enc, &encoder_props) != SZ_OK)
|
||||
@ -496,8 +510,7 @@ chd_error lzma_codec_init(void* codec, uint32_t hunkbytes)
|
||||
LzmaEnc_Destroy(enc, (ISzAlloc*)&alloc, (ISzAlloc*)&alloc);
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
}
|
||||
Byte decoder_props[LZMA_PROPS_SIZE];
|
||||
SizeT props_size = sizeof(decoder_props);
|
||||
props_size = sizeof(decoder_props);
|
||||
if (LzmaEnc_WriteProperties(enc, decoder_props, &props_size) != SZ_OK)
|
||||
{
|
||||
LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc);
|
||||
@ -505,58 +518,62 @@ chd_error lzma_codec_init(void* codec, uint32_t hunkbytes)
|
||||
}
|
||||
LzmaEnc_Destroy(enc, (ISzAlloc*)alloc, (ISzAlloc*)alloc);
|
||||
|
||||
// do memory allocations
|
||||
/* do memory allocations */
|
||||
if (LzmaDec_Allocate(&lzma_codec->decoder, decoder_props, LZMA_PROPS_SIZE, (ISzAlloc*)alloc) != SZ_OK)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
|
||||
// Okay
|
||||
/* Okay */
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// lzma_codec_free
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* lzma_codec_free
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void lzma_codec_free(void* codec)
|
||||
{
|
||||
lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
|
||||
lzma_allocator* alloc = &lzma_codec->allocator;
|
||||
|
||||
// free memory
|
||||
/* free memory */
|
||||
lzma_allocator_free(alloc);
|
||||
LzmaDec_Free(&lzma_codec->decoder, (ISzAlloc*)&lzma_codec->allocator);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// decompress - decompress data using the LZMA
|
||||
// codec
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* decompress - decompress data using the LZMA
|
||||
* codec
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
chd_error lzma_codec_decompress(void* codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
|
||||
{
|
||||
// initialize
|
||||
ELzmaStatus status;
|
||||
SRes res;
|
||||
SizeT consumedlen, decodedlen;
|
||||
/* initialize */
|
||||
lzma_codec_data* lzma_codec = (lzma_codec_data*) codec;
|
||||
LzmaDec_Init(&lzma_codec->decoder);
|
||||
|
||||
// decode
|
||||
SizeT consumedlen = complen;
|
||||
SizeT decodedlen = destlen;
|
||||
ELzmaStatus status;
|
||||
SRes res = LzmaDec_DecodeToBuf(&lzma_codec->decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status);
|
||||
/* decode */
|
||||
consumedlen = complen;
|
||||
decodedlen = destlen;
|
||||
res = LzmaDec_DecodeToBuf(&lzma_codec->decoder, dest, &decodedlen, src, &consumedlen, LZMA_FINISH_END, &status);
|
||||
if ((res != SZ_OK && res != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) || consumedlen != complen || decodedlen != destlen)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
// cdlz
|
||||
/* cdlz */
|
||||
chd_error cdlz_codec_init(void* codec, uint32_t hunkbytes)
|
||||
{
|
||||
chd_error ret;
|
||||
cdlz_codec_data* cdlz = (cdlz_codec_data*) codec;
|
||||
|
||||
// allocate buffer
|
||||
/* allocate buffer */
|
||||
cdlz->buffer = (uint8_t*)malloc(sizeof(uint8_t) * hunkbytes);
|
||||
if (cdlz->buffer == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
@ -591,24 +608,24 @@ chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t comple
|
||||
uint32_t framenum;
|
||||
cdlz_codec_data* cdlz = (cdlz_codec_data*)codec;
|
||||
|
||||
// determine header bytes
|
||||
/* determine header bytes */
|
||||
uint32_t frames = destlen / CD_FRAME_SIZE;
|
||||
uint32_t complen_bytes = (destlen < 65536) ? 2 : 3;
|
||||
uint32_t ecc_bytes = (frames + 7) / 8;
|
||||
uint32_t header_bytes = ecc_bytes + complen_bytes;
|
||||
|
||||
// extract compressed length of base
|
||||
/* extract compressed length of base */
|
||||
uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1];
|
||||
if (complen_bytes > 2)
|
||||
complen_base = (complen_base << 8) | src[ecc_bytes + 2];
|
||||
|
||||
// reset and decode
|
||||
/* reset and decode */
|
||||
lzma_codec_decompress(&cdlz->base_decompressor, &src[header_bytes], complen_base, &cdlz->buffer[0], frames * CD_MAX_SECTOR_DATA);
|
||||
#ifdef WANT_SUBCODE
|
||||
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);
|
||||
#endif
|
||||
|
||||
// reassemble the data
|
||||
/* reassemble the data */
|
||||
for (framenum = 0; framenum < frames; framenum++)
|
||||
{
|
||||
memcpy(&dest[framenum * CD_FRAME_SIZE], &cdlz->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);
|
||||
@ -617,7 +634,7 @@ chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t comple
|
||||
#endif
|
||||
|
||||
#ifdef WANT_RAW_DATA_SECTOR
|
||||
// reconstitute the ECC data and sync header
|
||||
/* reconstitute the ECC data and sync header */
|
||||
uint8_t *sector = &dest[framenum * CD_FRAME_SIZE];
|
||||
if ((src[framenum / 8] & (1 << (framenum % 8))) != 0)
|
||||
{
|
||||
@ -630,14 +647,14 @@ chd_error cdlz_codec_decompress(void *codec, const uint8_t *src, uint32_t comple
|
||||
}
|
||||
|
||||
|
||||
// cdzl
|
||||
/* cdzl */
|
||||
|
||||
chd_error cdzl_codec_init(void *codec, uint32_t hunkbytes)
|
||||
{
|
||||
chd_error ret;
|
||||
cdzl_codec_data* cdzl = (cdzl_codec_data*)codec;
|
||||
|
||||
// make sure the CHD's hunk size is an even multiple of the frame size
|
||||
/* make sure the CHD's hunk size is an even multiple of the frame size */
|
||||
if (hunkbytes % CD_FRAME_SIZE != 0)
|
||||
return CHDERR_CODEC_ERROR;
|
||||
|
||||
@ -675,24 +692,24 @@ chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t comple
|
||||
uint32_t framenum;
|
||||
cdzl_codec_data* cdzl = (cdzl_codec_data*)codec;
|
||||
|
||||
// determine header bytes
|
||||
/* determine header bytes */
|
||||
uint32_t frames = destlen / CD_FRAME_SIZE;
|
||||
uint32_t complen_bytes = (destlen < 65536) ? 2 : 3;
|
||||
uint32_t ecc_bytes = (frames + 7) / 8;
|
||||
uint32_t header_bytes = ecc_bytes + complen_bytes;
|
||||
|
||||
// extract compressed length of base
|
||||
/* extract compressed length of base */
|
||||
uint32_t complen_base = (src[ecc_bytes + 0] << 8) | src[ecc_bytes + 1];
|
||||
if (complen_bytes > 2)
|
||||
complen_base = (complen_base << 8) | src[ecc_bytes + 2];
|
||||
|
||||
// reset and decode
|
||||
/* reset and decode */
|
||||
zlib_codec_decompress(&cdzl->base_decompressor, &src[header_bytes], complen_base, &cdzl->buffer[0], frames * CD_MAX_SECTOR_DATA);
|
||||
#ifdef WANT_SUBCODE
|
||||
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);
|
||||
#endif
|
||||
|
||||
// reassemble the data
|
||||
/* reassemble the data */
|
||||
for (framenum = 0; framenum < frames; framenum++)
|
||||
{
|
||||
memcpy(&dest[framenum * CD_FRAME_SIZE], &cdzl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);
|
||||
@ -701,7 +718,7 @@ chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t comple
|
||||
#endif
|
||||
|
||||
#ifdef WANT_RAW_DATA_SECTOR
|
||||
// reconstitute the ECC data and sync header
|
||||
/* reconstitute the ECC data and sync header */
|
||||
uint8_t *sector = &dest[framenum * CD_FRAME_SIZE];
|
||||
if ((src[framenum / 8] & (1 << (framenum % 8))) != 0)
|
||||
{
|
||||
@ -713,20 +730,22 @@ chd_error cdzl_codec_decompress(void *codec, const uint8_t *src, uint32_t comple
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
//**************************************************************************
|
||||
// CD FLAC DECOMPRESSOR
|
||||
//**************************************************************************
|
||||
/***************************************************************************
|
||||
* CD FLAC DECOMPRESSOR
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------
|
||||
// cdfl_codec_blocksize - return the optimal block size
|
||||
//------------------------------------------------------
|
||||
/*------------------------------------------------------
|
||||
* cdfl_codec_blocksize - return the optimal block size
|
||||
*------------------------------------------------------
|
||||
*/
|
||||
|
||||
static uint32_t cdfl_codec_blocksize(uint32_t bytes)
|
||||
{
|
||||
// determine FLAC block size, which must be 16-65535
|
||||
// clamp to 2k since that's supposed to be the sweet spot
|
||||
/* determine FLAC block size, which must be 16-65535
|
||||
* clamp to 2k since that's supposed to be the sweet spot */
|
||||
uint32_t hunkbytes = bytes / 4;
|
||||
while (hunkbytes > 2048)
|
||||
hunkbytes /= 2;
|
||||
@ -735,9 +754,13 @@ static uint32_t cdfl_codec_blocksize(uint32_t bytes)
|
||||
|
||||
chd_error cdfl_codec_init(void *codec, uint32_t hunkbytes)
|
||||
{
|
||||
#ifdef WANT_SUBCODE
|
||||
chd_error ret;
|
||||
#endif
|
||||
uint16_t native_endian = 0;
|
||||
cdfl_codec_data *cdfl = (cdfl_codec_data*)codec;
|
||||
|
||||
// make sure the CHD's hunk size is an even multiple of the frame size
|
||||
/* make sure the CHD's hunk size is an even multiple of the frame size */
|
||||
if (hunkbytes % CD_FRAME_SIZE != 0)
|
||||
return CHDERR_CODEC_ERROR;
|
||||
|
||||
@ -745,19 +768,18 @@ chd_error cdfl_codec_init(void *codec, uint32_t hunkbytes)
|
||||
if (cdfl->buffer == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
|
||||
// determine whether we want native or swapped samples
|
||||
uint16_t native_endian = 0;
|
||||
/* determine whether we want native or swapped samples */
|
||||
*(uint8_t *)(&native_endian) = 1;
|
||||
cdfl->swap_endian = (native_endian & 1);
|
||||
|
||||
#ifdef WANT_SUBCODE
|
||||
// init zlib inflater
|
||||
chd_error ret = zlib_codec_init(&cdfl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
|
||||
/* init zlib inflater */
|
||||
ret = zlib_codec_init(&cdfl->subcode_decompressor, (hunkbytes / CD_FRAME_SIZE) * CD_MAX_SECTOR_DATA);
|
||||
if (ret != CHDERR_NONE)
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
// flac decoder init
|
||||
/* flac decoder init */
|
||||
flac_decoder_init(&cdfl->decoder);
|
||||
if (cdfl->decoder.decoder == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
@ -779,27 +801,33 @@ void cdfl_codec_free(void *codec)
|
||||
chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t complen, uint8_t *dest, uint32_t destlen)
|
||||
{
|
||||
uint32_t framenum;
|
||||
uint8_t *buffer;
|
||||
#ifdef WANT_SUBCODE
|
||||
uint32_t offset;
|
||||
chd_error ret;
|
||||
#endif
|
||||
cdfl_codec_data *cdfl = (cdfl_codec_data*)codec;
|
||||
|
||||
// reset and decode
|
||||
/* reset and decode */
|
||||
uint32_t frames = destlen / CD_FRAME_SIZE;
|
||||
|
||||
if (!flac_decoder_reset(&cdfl->decoder, 44100, 2, cdfl_codec_blocksize(frames * CD_MAX_SECTOR_DATA), src, complen))
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
uint8_t *buffer = &cdfl->buffer[0];
|
||||
buffer = &cdfl->buffer[0];
|
||||
if (!flac_decoder_decode_interleaved(&cdfl->decoder, (int16_t *)(buffer), frames * CD_MAX_SECTOR_DATA/4, cdfl->swap_endian))
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
|
||||
#ifdef WANT_SUBCODE
|
||||
// inflate the subcode data
|
||||
uint32_t offset = flac_decoder_finish(&cdfl->decoder);
|
||||
chd_error ret = zlib_codec_decompress(&cdfl->subcode_decompressor, src + offset, complen - offset, &cdfl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA);
|
||||
/* inflate the subcode data */
|
||||
offset = flac_decoder_finish(&cdfl->decoder);
|
||||
ret = zlib_codec_decompress(&cdfl->subcode_decompressor, src + offset, complen - offset, &cdfl->buffer[frames * CD_MAX_SECTOR_DATA], frames * CD_MAX_SUBCODE_DATA);
|
||||
if (ret != CHDERR_NONE)
|
||||
return ret;
|
||||
#else
|
||||
flac_decoder_finish(&cdfl->decoder);
|
||||
#endif
|
||||
|
||||
// reassemble the data
|
||||
/* reassemble the data */
|
||||
for (framenum = 0; framenum < frames; framenum++)
|
||||
{
|
||||
memcpy(&dest[framenum * CD_FRAME_SIZE], &cdfl->buffer[framenum * CD_MAX_SECTOR_DATA], CD_MAX_SECTOR_DATA);
|
||||
@ -816,7 +844,7 @@ chd_error cdfl_codec_decompress(void *codec, const uint8_t *src, uint32_t comple
|
||||
|
||||
#define CHD_MAKE_TAG(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
|
||||
|
||||
// general codecs with CD frontend
|
||||
/* general codecs with CD frontend */
|
||||
#define CHD_CODEC_CD_ZLIB CHD_MAKE_TAG('c','d','z','l')
|
||||
#define CHD_CODEC_CD_LZMA CHD_MAKE_TAG('c','d','l','z')
|
||||
#define CHD_CODEC_CD_FLAC CHD_MAKE_TAG('c','d','f','l')
|
||||
@ -899,7 +927,7 @@ static const codec_interface codec_interfaces[] =
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline UINT64 get_bigendian_uint64(const UINT8 *base)
|
||||
INLINE UINT64 get_bigendian_uint64(const UINT8 *base)
|
||||
{
|
||||
return ((UINT64)base[0] << 56) | ((UINT64)base[1] << 48) | ((UINT64)base[2] << 40) | ((UINT64)base[3] << 32) |
|
||||
((UINT64)base[4] << 24) | ((UINT64)base[5] << 16) | ((UINT64)base[6] << 8) | (UINT64)base[7];
|
||||
@ -911,7 +939,7 @@ static inline UINT64 get_bigendian_uint64(const UINT8 *base)
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline void put_bigendian_uint64(UINT8 *base, UINT64 value)
|
||||
INLINE void put_bigendian_uint64(UINT8 *base, UINT64 value)
|
||||
{
|
||||
base[0] = value >> 56;
|
||||
base[1] = value >> 48;
|
||||
@ -928,7 +956,7 @@ static inline void put_bigendian_uint64(UINT8 *base, UINT64 value)
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline UINT64 get_bigendian_uint48(const UINT8 *base)
|
||||
INLINE UINT64 get_bigendian_uint48(const UINT8 *base)
|
||||
{
|
||||
return ((UINT64)base[0] << 40) | ((UINT64)base[1] << 32) |
|
||||
((UINT64)base[2] << 24) | ((UINT64)base[3] << 16) | ((UINT64)base[4] << 8) | (UINT64)base[5];
|
||||
@ -939,7 +967,7 @@ static inline UINT64 get_bigendian_uint48(const UINT8 *base)
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline void put_bigendian_uint48(UINT8 *base, UINT64 value)
|
||||
INLINE void put_bigendian_uint48(UINT8 *base, UINT64 value)
|
||||
{
|
||||
value &= 0xffffffffffff;
|
||||
base[0] = value >> 40;
|
||||
@ -954,7 +982,7 @@ static inline void put_bigendian_uint48(UINT8 *base, UINT64 value)
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline UINT32 get_bigendian_uint32(const UINT8 *base)
|
||||
INLINE UINT32 get_bigendian_uint32(const UINT8 *base)
|
||||
{
|
||||
return (base[0] << 24) | (base[1] << 16) | (base[2] << 8) | base[3];
|
||||
}
|
||||
@ -965,7 +993,7 @@ static inline UINT32 get_bigendian_uint32(const UINT8 *base)
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline void put_bigendian_uint24(UINT8 *base, UINT32 value)
|
||||
INLINE void put_bigendian_uint24(UINT8 *base, UINT32 value)
|
||||
{
|
||||
value &= 0xffffff;
|
||||
base[0] = value >> 16;
|
||||
@ -979,7 +1007,7 @@ static inline void put_bigendian_uint24(UINT8 *base, UINT32 value)
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline void put_bigendian_uint32(UINT8 *base, UINT32 value)
|
||||
INLINE void put_bigendian_uint32(UINT8 *base, UINT32 value)
|
||||
{
|
||||
value &= 0xffffff;
|
||||
base[0] = value >> 16;
|
||||
@ -992,7 +1020,7 @@ static inline void put_bigendian_uint32(UINT8 *base, UINT32 value)
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline UINT32 get_bigendian_uint24(const UINT8 *base)
|
||||
INLINE UINT32 get_bigendian_uint24(const UINT8 *base)
|
||||
{
|
||||
return (base[0] << 16) | (base[1] << 8) | base[2];
|
||||
}
|
||||
@ -1002,7 +1030,7 @@ static inline UINT32 get_bigendian_uint24(const UINT8 *base)
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline UINT16 get_bigendian_uint16(const UINT8 *base)
|
||||
INLINE UINT16 get_bigendian_uint16(const UINT8 *base)
|
||||
{
|
||||
return (base[0] << 8) | base[1];
|
||||
}
|
||||
@ -1013,7 +1041,7 @@ static inline UINT16 get_bigendian_uint16(const UINT8 *base)
|
||||
the data stream in bigendian order
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline void put_bigendian_uint16(UINT8 *base, UINT16 value)
|
||||
INLINE void put_bigendian_uint16(UINT8 *base, UINT16 value)
|
||||
{
|
||||
base[0] = value >> 8;
|
||||
base[1] = value;
|
||||
@ -1025,7 +1053,7 @@ static inline void put_bigendian_uint16(UINT8 *base, UINT16 value)
|
||||
entry from the datastream
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline void map_extract(const UINT8 *base, map_entry *entry)
|
||||
INLINE void map_extract(const UINT8 *base, map_entry *entry)
|
||||
{
|
||||
entry->offset = get_bigendian_uint64(&base[0]);
|
||||
entry->crc = get_bigendian_uint32(&base[8]);
|
||||
@ -1039,7 +1067,7 @@ static inline void map_extract(const UINT8 *base, map_entry *entry)
|
||||
entry to the datastream
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline void map_assemble(UINT8 *base, map_entry *entry)
|
||||
INLINE void map_assemble(UINT8 *base, map_entry *entry)
|
||||
{
|
||||
put_bigendian_uint64(&base[0], entry->offset);
|
||||
put_bigendian_uint32(&base[8], entry->crc);
|
||||
@ -1051,7 +1079,7 @@ static inline void map_assemble(UINT8 *base, map_entry *entry)
|
||||
/*-------------------------------------------------
|
||||
map_size_v5 - calculate CHDv5 map size
|
||||
-------------------------------------------------*/
|
||||
static inline int map_size_v5(chd_header* header)
|
||||
INLINE int map_size_v5(chd_header* header)
|
||||
{
|
||||
return header->hunkcount * header->mapentrybytes;
|
||||
}
|
||||
@ -1102,7 +1130,7 @@ uint16_t crc16(const void *data, uint32_t length)
|
||||
|
||||
const uint8_t *src = (uint8_t*)data;
|
||||
|
||||
// fetch the current value into a local and rip through the source data
|
||||
/* fetch the current value into a local and rip through the source data */
|
||||
while (length-- != 0)
|
||||
crc = (crc << 8) ^ s_table[(crc >> 8) ^ *src++];
|
||||
return crc;
|
||||
@ -1114,31 +1142,46 @@ uint16_t crc16(const void *data, uint32_t length)
|
||||
|
||||
static chd_error decompress_v5_map(chd_file* chd, chd_header* header)
|
||||
{
|
||||
uint8_t rawbuf[16];
|
||||
uint16_t mapcrc;
|
||||
uint32_t mapbytes;
|
||||
uint64_t firstoffs;
|
||||
uint32_t last_self = 0;
|
||||
uint64_t last_parent = 0;
|
||||
uint8_t lastcomp = 0;
|
||||
int hunknum, repcount = 0;
|
||||
enum huffman_error err;
|
||||
uint8_t lengthbits, selfbits, parentbits;
|
||||
uint8_t* compressed;
|
||||
struct bitstream* bitbuf;
|
||||
struct huffman_decoder* decoder;
|
||||
uint64_t curoffset;
|
||||
if (header->mapoffset == 0)
|
||||
{
|
||||
//memset(header->rawmap, 0xff,map_size_v5(header));
|
||||
#if 0
|
||||
memset(header->rawmap, 0xff,map_size_v5(header));
|
||||
#endif
|
||||
return CHDERR_READ_ERROR;
|
||||
}
|
||||
|
||||
// read the reader
|
||||
uint8_t rawbuf[16];
|
||||
/* read the reader */
|
||||
core_fseek(chd->file, header->mapoffset, SEEK_SET);
|
||||
core_fread(chd->file, rawbuf, sizeof(rawbuf));
|
||||
uint32_t const mapbytes = get_bigendian_uint32(&rawbuf[0]);
|
||||
uint64_t const firstoffs = get_bigendian_uint48(&rawbuf[4]);
|
||||
uint16_t const mapcrc = get_bigendian_uint16(&rawbuf[10]);
|
||||
uint8_t const lengthbits = rawbuf[12];
|
||||
uint8_t const selfbits = rawbuf[13];
|
||||
uint8_t const parentbits = rawbuf[14];
|
||||
mapbytes = get_bigendian_uint32(&rawbuf[0]);
|
||||
firstoffs = get_bigendian_uint48(&rawbuf[4]);
|
||||
mapcrc = get_bigendian_uint16(&rawbuf[10]);
|
||||
lengthbits = rawbuf[12];
|
||||
selfbits = rawbuf[13];
|
||||
parentbits = rawbuf[14];
|
||||
|
||||
// now read the map
|
||||
uint8_t* compressed = (uint8_t*)malloc(sizeof(uint8_t) * mapbytes);
|
||||
/* now read the map */
|
||||
compressed = (uint8_t*)malloc(sizeof(uint8_t) * mapbytes);
|
||||
if (compressed == NULL)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
|
||||
core_fseek(chd->file, header->mapoffset + 16, SEEK_SET);
|
||||
core_fread(chd->file, compressed, mapbytes);
|
||||
struct bitstream* bitbuf = create_bitstream(compressed, sizeof(uint8_t) * mapbytes);
|
||||
bitbuf = create_bitstream(compressed, sizeof(uint8_t) * mapbytes);
|
||||
if (bitbuf == NULL)
|
||||
{
|
||||
free(compressed);
|
||||
@ -1153,8 +1196,8 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// first decode the compression types
|
||||
struct huffman_decoder* decoder = create_huffman_decoder(16, 8);
|
||||
/* first decode the compression types */
|
||||
decoder = create_huffman_decoder(16, 8);
|
||||
if (decoder == NULL)
|
||||
{
|
||||
free(compressed);
|
||||
@ -1162,7 +1205,7 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header)
|
||||
return CHDERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
enum huffman_error err = huffman_import_tree_rle(decoder, bitbuf);
|
||||
err = huffman_import_tree_rle(decoder, bitbuf);
|
||||
if (err != HUFFERR_NONE)
|
||||
{
|
||||
free(compressed);
|
||||
@ -1171,8 +1214,6 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
}
|
||||
|
||||
uint8_t lastcomp = 0;
|
||||
int hunknum, repcount = 0;
|
||||
for (hunknum = 0; hunknum < header->hunkcount; hunknum++)
|
||||
{
|
||||
uint8_t *rawmap = header->rawmap + (hunknum * 12);
|
||||
@ -1190,10 +1231,8 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header)
|
||||
}
|
||||
}
|
||||
|
||||
// then iterate through the hunks and extract the needed data
|
||||
uint64_t curoffset = firstoffs;
|
||||
uint32_t last_self = 0;
|
||||
uint64_t last_parent = 0;
|
||||
/* then iterate through the hunks and extract the needed data */
|
||||
curoffset = firstoffs;
|
||||
for (hunknum = 0; hunknum < header->hunkcount; hunknum++)
|
||||
{
|
||||
uint8_t *rawmap = header->rawmap + (hunknum * 12);
|
||||
@ -1202,7 +1241,7 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header)
|
||||
uint16_t crc = 0;
|
||||
switch (rawmap[0])
|
||||
{
|
||||
// base types
|
||||
/* base types */
|
||||
case COMPRESSION_TYPE_0:
|
||||
case COMPRESSION_TYPE_1:
|
||||
case COMPRESSION_TYPE_2:
|
||||
@ -1225,7 +1264,7 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header)
|
||||
last_parent = offset;
|
||||
break;
|
||||
|
||||
// pseudo-types; convert into base types
|
||||
/* pseudo-types; convert into base types */
|
||||
case COMPRESSION_SELF_1:
|
||||
last_self++;
|
||||
case COMPRESSION_SELF_0:
|
||||
@ -1245,22 +1284,22 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header)
|
||||
offset = last_parent;
|
||||
break;
|
||||
}
|
||||
// UINT24 length
|
||||
/* UINT24 length */
|
||||
put_bigendian_uint24(&rawmap[1], length);
|
||||
|
||||
// UINT48 offset
|
||||
/* UINT48 offset */
|
||||
put_bigendian_uint48(&rawmap[4], offset);
|
||||
|
||||
// crc16
|
||||
/* crc16 */
|
||||
put_bigendian_uint16(&rawmap[10], crc);
|
||||
}
|
||||
|
||||
// free memory
|
||||
/* free memory */
|
||||
free(compressed);
|
||||
free(bitbuf);
|
||||
delete_huffman_decoder(decoder);
|
||||
|
||||
// verify the final CRC
|
||||
/* verify the final CRC */
|
||||
if (crc16(&header->rawmap[0], header->hunkcount * 12) != mapcrc)
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
|
||||
@ -1272,7 +1311,7 @@ static chd_error decompress_v5_map(chd_file* chd, chd_header* header)
|
||||
entry in old format from the datastream
|
||||
-------------------------------------------------*/
|
||||
|
||||
static inline void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 hunkbytes)
|
||||
INLINE void map_extract_old(const UINT8 *base, map_entry *entry, UINT32 hunkbytes)
|
||||
{
|
||||
entry->offset = get_bigendian_uint64(&base[0]);
|
||||
entry->crc = 0;
|
||||
@ -1402,7 +1441,7 @@ chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **
|
||||
else
|
||||
{
|
||||
int i, decompnum;
|
||||
// verify the compression types and initialize the codecs
|
||||
/* verify the compression types and initialize the codecs */
|
||||
for (decompnum = 0; decompnum < ARRAY_LENGTH(newchd->header.compression); decompnum++)
|
||||
{
|
||||
for (i = 0 ; i < ARRAY_LENGTH(codec_interfaces) ; i++)
|
||||
@ -1440,9 +1479,11 @@ chd_error chd_open_file(core_file *file, int mode, chd_file *parent, chd_file **
|
||||
}
|
||||
}
|
||||
|
||||
// HACK
|
||||
//if (err != CHDERR_NONE)
|
||||
// EARLY_EXIT(err);
|
||||
#if 0
|
||||
/* HACK */
|
||||
if (err != CHDERR_NONE)
|
||||
EARLY_EXIT(err);
|
||||
#endif
|
||||
|
||||
/* all done */
|
||||
*chd = newchd;
|
||||
@ -1517,7 +1558,7 @@ void chd_close(chd_file *chd)
|
||||
else
|
||||
{
|
||||
int i;
|
||||
// Free the codecs
|
||||
/* Free the codecs */
|
||||
for (i = 0 ; i < 4 ; i++)
|
||||
{
|
||||
void* codec = NULL;
|
||||
@ -1541,7 +1582,7 @@ void chd_close(chd_file *chd)
|
||||
}
|
||||
}
|
||||
|
||||
// Free the raw map
|
||||
/* Free the raw map */
|
||||
if (chd->header.rawmap != NULL)
|
||||
free(chd->header.rawmap);
|
||||
}
|
||||
@ -1938,10 +1979,10 @@ static chd_error header_read(core_file *file, chd_header *header)
|
||||
memcpy(header->parentsha1, &rawheader[104], CHD_SHA1_BYTES);
|
||||
memcpy(header->rawsha1, &rawheader[64], CHD_SHA1_BYTES);
|
||||
|
||||
// determine properties of map entries
|
||||
header->mapentrybytes = 12; //TODO compressed() ? 12 : 4;
|
||||
/* determine properties of map entries */
|
||||
header->mapentrybytes = 12; /*TODO compressed() ? 12 : 4; */
|
||||
|
||||
// hack
|
||||
/* hack */
|
||||
header->totalhunks = header->hunkcount;
|
||||
}
|
||||
|
||||
@ -1999,7 +2040,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
{
|
||||
chd_error err;
|
||||
|
||||
// punt if no file
|
||||
/* punt if no file */
|
||||
if (chd->file == NULL)
|
||||
return CHDERR_INVALID_FILE;
|
||||
|
||||
@ -2012,6 +2053,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
|
||||
if (chd->header.version < 5)
|
||||
{
|
||||
void* codec;
|
||||
map_entry *entry = &chd->map[hunknum];
|
||||
UINT32 bytes;
|
||||
|
||||
@ -2028,8 +2070,8 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
return CHDERR_READ_ERROR;
|
||||
|
||||
/* now decompress using the codec */
|
||||
err = CHDERR_NONE;
|
||||
void* codec = &chd->zlib_codec_data;
|
||||
err = CHDERR_NONE;
|
||||
codec = &chd->zlib_codec_data;
|
||||
if (chd->codecintf[0]->decompress != NULL)
|
||||
err = (*chd->codecintf[0]->decompress)(codec, chd->compressed, entry->length, dest, chd->header.hunkbytes);
|
||||
if (err != CHDERR_NONE)
|
||||
@ -2070,8 +2112,8 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
// get a pointer to the map entry
|
||||
void* codec = NULL;
|
||||
/* get a pointer to the map entry */
|
||||
uint64_t blockoffs;
|
||||
uint32_t blocklen;
|
||||
#ifdef VERIFY_BLOCK_CRC
|
||||
@ -2079,7 +2121,7 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
#endif
|
||||
uint8_t *rawmap = &chd->header.rawmap[chd->header.mapentrybytes * hunknum];
|
||||
|
||||
// uncompressed case
|
||||
/* uncompressed case */
|
||||
/* TODO
|
||||
if (!compressed())
|
||||
{
|
||||
@ -2095,13 +2137,12 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
return CHDERR_NONE;
|
||||
}*/
|
||||
|
||||
// compressed case
|
||||
/* compressed case */
|
||||
blocklen = get_bigendian_uint24(&rawmap[1]);
|
||||
blockoffs = get_bigendian_uint48(&rawmap[4]);
|
||||
#ifdef VERIFY_BLOCK_CRC
|
||||
blockcrc = get_bigendian_uint16(&rawmap[10]);
|
||||
#endif
|
||||
void* codec = NULL;
|
||||
switch (rawmap[0])
|
||||
{
|
||||
case COMPRESSION_TYPE_0:
|
||||
@ -2148,16 +2189,18 @@ static chd_error hunk_read_into_memory(chd_file *chd, UINT32 hunknum, UINT8 *des
|
||||
return hunk_read_into_memory(chd, blockoffs, dest);
|
||||
|
||||
case COMPRESSION_PARENT:
|
||||
// TODO
|
||||
//if (m_parent_missing)
|
||||
// return CHDERR_REQUIRES_PARENT;
|
||||
//return m_parent->read_bytes(uint64_t(blockoffs) * uint64_t(m_parent->unit_bytes()), dest, m_hunkbytes);
|
||||
/* TODO */
|
||||
#if 0
|
||||
if (m_parent_missing)
|
||||
return CHDERR_REQUIRES_PARENT;
|
||||
return m_parent->read_bytes(uint64_t(blockoffs) * uint64_t(m_parent->unit_bytes()), dest, m_hunkbytes);
|
||||
#endif
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
}
|
||||
return CHDERR_NONE;
|
||||
}
|
||||
|
||||
// We should not reach this code
|
||||
/* We should not reach this code */
|
||||
return CHDERR_DECOMPRESSION_ERROR;
|
||||
}
|
||||
|
||||
@ -2360,11 +2403,12 @@ static void zlib_codec_free(void *codec)
|
||||
if (data != NULL)
|
||||
{
|
||||
int i;
|
||||
zlib_allocator alloc;
|
||||
|
||||
inflateEnd(&data->inflater);
|
||||
|
||||
/* free our fast memory */
|
||||
zlib_allocator alloc = data->allocator;
|
||||
alloc = data->allocator;
|
||||
for (i = 0; i < MAX_ZLIB_ALLOCS; i++)
|
||||
if (alloc.allocptr[i])
|
||||
free(alloc.allocptr[i]);
|
||||
|
@ -24,9 +24,10 @@ static FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const F
|
||||
FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void* client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[]);
|
||||
static void flac_decoder_error_callback_static(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
|
||||
|
||||
//-------------------------------------------------
|
||||
// flac_decoder - constructor
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* flac_decoder - constructor
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void flac_decoder_init(flac_decoder *decoder)
|
||||
{
|
||||
@ -44,9 +45,10 @@ void flac_decoder_init(flac_decoder *decoder)
|
||||
decoder->uncompressed_swap = 0;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// flac_decoder - destructor
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* flac_decoder - destructor
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void flac_decoder_free(flac_decoder* decoder)
|
||||
{
|
||||
@ -55,10 +57,11 @@ void flac_decoder_free(flac_decoder* decoder)
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// reset - reset state with the original
|
||||
// parameters
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* reset - reset state with the original
|
||||
* parameters
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
static int flac_decoder_internal_reset(flac_decoder* decoder)
|
||||
{
|
||||
@ -78,30 +81,31 @@ static int flac_decoder_internal_reset(flac_decoder* decoder)
|
||||
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// reset - reset state with new memory parameters
|
||||
// and a custom-generated header
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* reset - reset state with new memory parameters
|
||||
* and a custom-generated header
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_channels, uint32_t block_size, const void *buffer, uint32_t length)
|
||||
{
|
||||
// modify the template header with our parameters
|
||||
/* modify the template header with our parameters */
|
||||
static const uint8_t s_header_template[0x2a] =
|
||||
{
|
||||
0x66, 0x4C, 0x61, 0x43, // +00: 'fLaC' stream header
|
||||
0x80, // +04: metadata block type 0 (STREAMINFO),
|
||||
// flagged as last block
|
||||
0x00, 0x00, 0x22, // +05: metadata block length = 0x22
|
||||
0x00, 0x00, // +08: minimum block size
|
||||
0x00, 0x00, // +0A: maximum block size
|
||||
0x00, 0x00, 0x00, // +0C: minimum frame size (0 == unknown)
|
||||
0x00, 0x00, 0x00, // +0F: maximum frame size (0 == unknown)
|
||||
0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x00, 0x00, 0x00, // +12: sample rate (0x0ac44 == 44100),
|
||||
// numchannels (2), sample bits (16),
|
||||
// samples in stream (0 == unknown)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // +1A: MD5 signature (0 == none)
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 //
|
||||
// +2A: start of stream data
|
||||
0x66, 0x4C, 0x61, 0x43, /* +00: 'fLaC' stream header */
|
||||
0x80, /* +04: metadata block type 0 (STREAMINFO), */
|
||||
/* flagged as last block */
|
||||
0x00, 0x00, 0x22, /* +05: metadata block length = 0x22 */
|
||||
0x00, 0x00, /* +08: minimum block size */
|
||||
0x00, 0x00, /* +0A: maximum block size */
|
||||
0x00, 0x00, 0x00, /* +0C: minimum frame size (0 == unknown) */
|
||||
0x00, 0x00, 0x00, /* +0F: maximum frame size (0 == unknown) */
|
||||
0x0A, 0xC4, 0x42, 0xF0, 0x00, 0x00, 0x00, 0x00, /* +12: sample rate (0x0ac44 == 44100), */
|
||||
/* numchannels (2), sample bits (16), */
|
||||
/* samples in stream (0 == unknown) */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* +1A: MD5 signature (0 == none) */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* */
|
||||
/* +2A: start of stream data */
|
||||
};
|
||||
memcpy(decoder->custom_header, s_header_template, sizeof(s_header_template));
|
||||
decoder->custom_header[0x08] = decoder->custom_header[0x0a] = block_size >> 8;
|
||||
@ -110,7 +114,7 @@ int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_
|
||||
decoder->custom_header[0x13] = sample_rate >> 4;
|
||||
decoder->custom_header[0x14] = (sample_rate << 4) | ((num_channels - 1) << 1);
|
||||
|
||||
// configure the header ahead of the provided buffer
|
||||
/* configure the header ahead of the provided buffer */
|
||||
decoder->compressed_start = (const FLAC__byte *)(decoder->custom_header);
|
||||
decoder->compressed_length = sizeof(decoder->custom_header);
|
||||
decoder->compressed2_start = (const FLAC__byte *)(buffer);
|
||||
@ -119,21 +123,22 @@ int flac_decoder_reset(flac_decoder* decoder, uint32_t sample_rate, uint8_t num_
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// decode_interleaved - decode to an interleaved
|
||||
// sound stream
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* decode_interleaved - decode to an interleaved
|
||||
* sound stream
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uint32_t num_samples, int swap_endian)
|
||||
{
|
||||
// configure the uncompressed buffer
|
||||
/* configure the uncompressed buffer */
|
||||
memset(decoder->uncompressed_start, 0, sizeof(decoder->uncompressed_start));
|
||||
decoder->uncompressed_start[0] = samples;
|
||||
decoder->uncompressed_offset = 0;
|
||||
decoder->uncompressed_length = num_samples;
|
||||
decoder->uncompressed_swap = swap_endian;
|
||||
|
||||
// loop until we get everything we want
|
||||
/* loop until we get everything we want */
|
||||
while (decoder->uncompressed_offset < decoder->uncompressed_length)
|
||||
if (!FLAC__stream_decoder_process_single(decoder->decoder))
|
||||
return 0;
|
||||
@ -141,20 +146,22 @@ int flac_decoder_decode_interleaved(flac_decoder* decoder, int16_t *samples, uin
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
/*
|
||||
//-------------------------------------------------
|
||||
// decode - decode to an multiple independent
|
||||
// data streams
|
||||
//-------------------------------------------------
|
||||
*-------------------------------------------------
|
||||
* decode - decode to an multiple independent
|
||||
* data streams
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
bool flac_decoder::decode(int16_t **samples, uint32_t num_samples, bool swap_endian)
|
||||
{
|
||||
// make sure we don't have too many channels
|
||||
/* make sure we don't have too many channels */
|
||||
int chans = channels();
|
||||
if (chans > ARRAY_LENGTH(m_uncompressed_start))
|
||||
return false;
|
||||
|
||||
// configure the uncompressed buffer
|
||||
/* configure the uncompressed buffer */
|
||||
memset(m_uncompressed_start, 0, sizeof(m_uncompressed_start));
|
||||
for (int curchan = 0; curchan < chans; curchan++)
|
||||
m_uncompressed_start[curchan] = samples[curchan];
|
||||
@ -162,26 +169,27 @@ bool flac_decoder::decode(int16_t **samples, uint32_t num_samples, bool swap_end
|
||||
m_uncompressed_length = num_samples;
|
||||
m_uncompressed_swap = swap_endian;
|
||||
|
||||
// loop until we get everything we want
|
||||
/* loop until we get everything we want */
|
||||
while (m_uncompressed_offset < m_uncompressed_length)
|
||||
if (!FLAC__stream_decoder_process_single(m_decoder))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
*/
|
||||
#endif
|
||||
|
||||
//-------------------------------------------------
|
||||
// finish - finish up the decode
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* finish - finish up the decode
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
uint32_t flac_decoder_finish(flac_decoder* decoder)
|
||||
{
|
||||
// get the final decoding position and move forward
|
||||
/* get the final decoding position and move forward */
|
||||
FLAC__uint64 position = 0;
|
||||
FLAC__stream_decoder_get_decode_position(decoder->decoder, &position);
|
||||
FLAC__stream_decoder_finish(decoder->decoder);
|
||||
|
||||
// adjust position if we provided the header
|
||||
/* adjust position if we provided the header */
|
||||
if (position == 0)
|
||||
return 0;
|
||||
if (decoder->compressed_start == (const FLAC__byte *)(decoder->custom_header))
|
||||
@ -190,10 +198,11 @@ uint32_t flac_decoder_finish(flac_decoder* decoder)
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// read_callback - handle reads from the input
|
||||
// stream
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* read_callback - handle reads from the input
|
||||
* stream
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
|
||||
@ -208,7 +217,7 @@ FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC
|
||||
|
||||
uint32_t expected = *bytes;
|
||||
|
||||
// copy from primary buffer first
|
||||
/* copy from primary buffer first */
|
||||
uint32_t outputpos = 0;
|
||||
if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length)
|
||||
{
|
||||
@ -218,7 +227,7 @@ FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC
|
||||
decoder->compressed_offset += bytes_to_copy;
|
||||
}
|
||||
|
||||
// once we're out of that, copy from the secondary buffer
|
||||
/* once we're out of that, copy from the secondary buffer */
|
||||
if (outputpos < *bytes && decoder->compressed_offset < decoder->compressed_length + decoder->compressed2_length)
|
||||
{
|
||||
uint32_t bytes_to_copy = MIN(*bytes - outputpos, decoder->compressed2_length - (decoder->compressed_offset - decoder->compressed_length));
|
||||
@ -228,33 +237,36 @@ FLAC__StreamDecoderReadStatus flac_decoder_read_callback(void* client_data, FLAC
|
||||
}
|
||||
*bytes = outputpos;
|
||||
|
||||
// return based on whether we ran out of data
|
||||
/* return based on whether we ran out of data */
|
||||
return (*bytes < expected) ? FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM : FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// metadata_callback - handle STREAMINFO metadata
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* metadata_callback - handle STREAMINFO metadata
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void flac_decoder_metadata_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data)
|
||||
{
|
||||
// ignore all but STREAMINFO metadata
|
||||
flac_decoder *fldecoder;
|
||||
/* ignore all but STREAMINFO metadata */
|
||||
if (metadata->type != FLAC__METADATA_TYPE_STREAMINFO)
|
||||
return;
|
||||
|
||||
// parse out the data we care about
|
||||
flac_decoder *fldecoder = (flac_decoder *)(client_data);
|
||||
/* parse out the data we care about */
|
||||
fldecoder = (flac_decoder *)(client_data);
|
||||
fldecoder->sample_rate = metadata->data.stream_info.sample_rate;
|
||||
fldecoder->bits_per_sample = metadata->data.stream_info.bits_per_sample;
|
||||
fldecoder->channels = metadata->data.stream_info.channels;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// tell_callback - handle requests to find out
|
||||
// where in the input stream we are
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* tell_callback - handle requests to find out
|
||||
* where in the input stream we are
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
|
||||
{
|
||||
@ -263,10 +275,11 @@ FLAC__StreamDecoderTellStatus flac_decoder_tell_callback_static(const FLAC__Stre
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// write_callback - handle writes to the output
|
||||
// stream
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* write_callback - handle writes to the output
|
||||
* stream
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data)
|
||||
{
|
||||
@ -275,14 +288,15 @@ FLAC__StreamDecoderWriteStatus flac_decoder_write_callback_static(const FLAC__St
|
||||
|
||||
FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void *client_data, const FLAC__Frame *frame, const FLAC__int32 * const buffer[])
|
||||
{
|
||||
int sampnum, chan;
|
||||
int shift, blocksize;
|
||||
flac_decoder * decoder = (flac_decoder *)client_data;
|
||||
|
||||
assert(frame->header.channels == decoder->channels);
|
||||
|
||||
// interleaved case
|
||||
int sampnum, chan;
|
||||
int shift = decoder->uncompressed_swap ? 8 : 0;
|
||||
int blocksize = frame->header.blocksize;
|
||||
/* interleaved case */
|
||||
shift = decoder->uncompressed_swap ? 8 : 0;
|
||||
blocksize = frame->header.blocksize;
|
||||
if (decoder->uncompressed_start[1] == NULL)
|
||||
{
|
||||
int16_t *dest = decoder->uncompressed_start[0] + decoder->uncompressed_offset * frame->header.channels;
|
||||
@ -291,7 +305,7 @@ FLAC__StreamDecoderWriteStatus flac_decoder_write_callback(void *client_data, co
|
||||
*dest++ = (int16_t)((((uint16_t)buffer[chan][sampnum]) << shift) | (((uint16_t)buffer[chan][sampnum]) >> shift));
|
||||
}
|
||||
|
||||
// non-interleaved case
|
||||
/* non-interleaved case */
|
||||
else
|
||||
{
|
||||
for (sampnum = 0; sampnum < blocksize && decoder->uncompressed_offset < decoder->uncompressed_length; sampnum++, decoder->uncompressed_offset++)
|
||||
|
@ -1,6 +1,6 @@
|
||||
// license:BSD-3-Clause
|
||||
// copyright-holders:Aaron Giles
|
||||
/***************************************************************************
|
||||
/* license:BSD-3-Clause
|
||||
* copyright-holders:Aaron Giles
|
||||
***************************************************************************
|
||||
|
||||
huffman.c
|
||||
|
||||
@ -105,29 +105,33 @@
|
||||
|
||||
#define MAX(x,y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
//**************************************************************************
|
||||
// MACROS
|
||||
//**************************************************************************
|
||||
/***************************************************************************
|
||||
* MACROS
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
#define MAKE_LOOKUP(code,bits) (((code) << 5) | ((bits) & 0x1f))
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// IMPLEMENTATION
|
||||
//**************************************************************************
|
||||
/***************************************************************************
|
||||
* IMPLEMENTATION
|
||||
* **************************************************************************
|
||||
*/
|
||||
|
||||
//-------------------------------------------------
|
||||
// huffman_context_base - create an encoding/
|
||||
// decoding context
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* huffman_context_base - create an encoding/
|
||||
* decoding context
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
struct huffman_decoder* create_huffman_decoder(int numcodes, int maxbits)
|
||||
{
|
||||
// limit to 24 bits
|
||||
struct huffman_decoder* decoder;
|
||||
/* limit to 24 bits */
|
||||
if (maxbits > 24)
|
||||
return NULL;
|
||||
|
||||
struct huffman_decoder* decoder = (struct huffman_decoder*)malloc(sizeof(struct huffman_decoder));
|
||||
decoder = (struct huffman_decoder*)malloc(sizeof(struct huffman_decoder));
|
||||
decoder->numcodes = numcodes;
|
||||
decoder->maxbits = maxbits;
|
||||
decoder->lookup = (lookup_value*)malloc(sizeof(lookup_value) * (1 << maxbits));
|
||||
@ -150,33 +154,37 @@ void delete_huffman_decoder(struct huffman_decoder* decoder)
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// decode_one - decode a single code from the
|
||||
// huffman stream
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* decode_one - decode a single code from the
|
||||
* huffman stream
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
uint32_t huffman_decode_one(struct huffman_decoder* decoder, struct bitstream* bitbuf)
|
||||
{
|
||||
// peek ahead to get maxbits worth of data
|
||||
/* peek ahead to get maxbits worth of data */
|
||||
uint32_t bits = bitstream_peek(bitbuf, decoder->maxbits);
|
||||
|
||||
// look it up, then remove the actual number of bits for this code
|
||||
/* look it up, then remove the actual number of bits for this code */
|
||||
lookup_value lookup = decoder->lookup[bits];
|
||||
bitstream_remove(bitbuf, lookup & 0x1f);
|
||||
|
||||
// return the value
|
||||
/* return the value */
|
||||
return lookup >> 5;
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
// import_tree_rle - import an RLE-encoded
|
||||
// huffman tree from a source data stream
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* import_tree_rle - import an RLE-encoded
|
||||
* huffman tree from a source data stream
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, struct bitstream* bitbuf)
|
||||
{
|
||||
// bits per entry depends on the maxbits
|
||||
enum huffman_error error;
|
||||
/* bits per entry depends on the maxbits */
|
||||
int numbits;
|
||||
int curnode;
|
||||
if (decoder->maxbits >= 16)
|
||||
numbits = 5;
|
||||
else if (decoder->maxbits >= 8)
|
||||
@ -184,24 +192,23 @@ enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, stru
|
||||
else
|
||||
numbits = 3;
|
||||
|
||||
// loop until we read all the nodes
|
||||
int curnode;
|
||||
/* loop until we read all the nodes */
|
||||
for (curnode = 0; curnode < decoder->numcodes; )
|
||||
{
|
||||
// a non-one value is just raw
|
||||
/* a non-one value is just raw */
|
||||
int nodebits = bitstream_read(bitbuf, numbits);
|
||||
if (nodebits != 1)
|
||||
decoder->huffnode[curnode++].numbits = nodebits;
|
||||
|
||||
// a one value is an escape code
|
||||
/* a one value is an escape code */
|
||||
else
|
||||
{
|
||||
// a double 1 is just a single 1
|
||||
/* a double 1 is just a single 1 */
|
||||
nodebits = bitstream_read(bitbuf, numbits);
|
||||
if (nodebits == 1)
|
||||
decoder->huffnode[curnode++].numbits = nodebits;
|
||||
|
||||
// otherwise, we need one for value for the repeat count
|
||||
/* otherwise, we need one for value for the repeat count */
|
||||
else
|
||||
{
|
||||
int repcount = bitstream_read(bitbuf, numbits) + 3;
|
||||
@ -211,35 +218,44 @@ enum huffman_error huffman_import_tree_rle(struct huffman_decoder* decoder, stru
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we ended up with the right number
|
||||
/* make sure we ended up with the right number */
|
||||
if (curnode != decoder->numcodes)
|
||||
return HUFFERR_INVALID_DATA;
|
||||
|
||||
// assign canonical codes for all nodes based on their code lengths
|
||||
enum huffman_error error = huffman_assign_canonical_codes(decoder);
|
||||
/* assign canonical codes for all nodes based on their code lengths */
|
||||
error = huffman_assign_canonical_codes(decoder);
|
||||
if (error != HUFFERR_NONE)
|
||||
return error;
|
||||
|
||||
// build the lookup table
|
||||
/* build the lookup table */
|
||||
huffman_build_lookup_table(decoder);
|
||||
|
||||
// determine final input length and report errors
|
||||
/* determine final input length and report errors */
|
||||
return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// import_tree_huffman - import a huffman-encoded
|
||||
// huffman tree from a source data stream
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* import_tree_huffman - import a huffman-encoded
|
||||
* huffman tree from a source data stream
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder, struct bitstream* bitbuf)
|
||||
{
|
||||
// start by parsing the lengths for the small tree
|
||||
struct huffman_decoder* smallhuff = create_huffman_decoder(24, 6);
|
||||
smallhuff->huffnode[0].numbits = bitstream_read(bitbuf, 3);
|
||||
int start = bitstream_read(bitbuf, 3) + 1;
|
||||
int last = 0;
|
||||
int curcode;
|
||||
uint32_t temp;
|
||||
enum huffman_error error;
|
||||
uint8_t rlefullbits = 0;
|
||||
int index, count = 0;
|
||||
int start;
|
||||
/* start by parsing the lengths for the small tree */
|
||||
struct huffman_decoder* smallhuff = create_huffman_decoder(24, 6);
|
||||
|
||||
smallhuff->huffnode[0].numbits = bitstream_read(bitbuf, 3);
|
||||
start = bitstream_read(bitbuf, 3) + 1;
|
||||
|
||||
for (index = 1; index < 24; index++)
|
||||
{
|
||||
if (index < start || count == 7)
|
||||
@ -251,21 +267,18 @@ enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder,
|
||||
}
|
||||
}
|
||||
|
||||
// then regenerate the tree
|
||||
enum huffman_error error = huffman_assign_canonical_codes(smallhuff);
|
||||
/* then regenerate the tree */
|
||||
error = huffman_assign_canonical_codes(smallhuff);
|
||||
if (error != HUFFERR_NONE)
|
||||
return error;
|
||||
huffman_build_lookup_table(smallhuff);
|
||||
|
||||
// determine the maximum length of an RLE count
|
||||
uint32_t temp = decoder->numcodes - 9;
|
||||
uint8_t rlefullbits = 0;
|
||||
/* determine the maximum length of an RLE count */
|
||||
temp = decoder->numcodes - 9;
|
||||
while (temp != 0)
|
||||
temp >>= 1, rlefullbits++;
|
||||
|
||||
// now process the rest of the data
|
||||
int last = 0;
|
||||
int curcode;
|
||||
/* now process the rest of the data */
|
||||
for (curcode = 0; curcode < decoder->numcodes; )
|
||||
{
|
||||
int value = huffman_decode_one(smallhuff, bitbuf);
|
||||
@ -281,51 +294,53 @@ enum huffman_error huffman_import_tree_huffman(struct huffman_decoder* decoder,
|
||||
}
|
||||
}
|
||||
|
||||
// make sure we ended up with the right number
|
||||
/* make sure we ended up with the right number */
|
||||
if (curcode != decoder->numcodes)
|
||||
return HUFFERR_INVALID_DATA;
|
||||
|
||||
// assign canonical codes for all nodes based on their code lengths
|
||||
/* assign canonical codes for all nodes based on their code lengths */
|
||||
error = huffman_assign_canonical_codes(decoder);
|
||||
if (error != HUFFERR_NONE)
|
||||
return error;
|
||||
|
||||
// build the lookup table
|
||||
/* build the lookup table */
|
||||
huffman_build_lookup_table(decoder);
|
||||
|
||||
// determine final input length and report errors
|
||||
/* determine final input length and report errors */
|
||||
return bitstream_overflow(bitbuf) ? HUFFERR_INPUT_BUFFER_TOO_SMALL : HUFFERR_NONE;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// compute_tree_from_histo - common backend for
|
||||
// computing a tree based on the data histogram
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* compute_tree_from_histo - common backend for
|
||||
* computing a tree based on the data histogram
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decoder)
|
||||
{
|
||||
// compute the number of data items in the histogram
|
||||
/* compute the number of data items in the histogram */
|
||||
int i;
|
||||
uint32_t upperweight;
|
||||
uint32_t lowerweight = 0;
|
||||
uint32_t sdatacount = 0;
|
||||
for (i = 0; i < decoder->numcodes; i++)
|
||||
sdatacount += decoder->datahisto[i];
|
||||
|
||||
// binary search to achieve the optimum encoding
|
||||
uint32_t lowerweight = 0;
|
||||
uint32_t upperweight = sdatacount * 2;
|
||||
/* binary search to achieve the optimum encoding */
|
||||
upperweight = sdatacount * 2;
|
||||
while (1)
|
||||
{
|
||||
// build a tree using the current weight
|
||||
/* build a tree using the current weight */
|
||||
uint32_t curweight = (upperweight + lowerweight) / 2;
|
||||
int curmaxbits = huffman_build_tree(decoder, sdatacount, curweight);
|
||||
|
||||
// apply binary search here
|
||||
/* apply binary search here */
|
||||
if (curmaxbits <= decoder->maxbits)
|
||||
{
|
||||
lowerweight = curweight;
|
||||
|
||||
// early out if it worked with the raw weights, or if we're done searching
|
||||
/* early out if it worked with the raw weights, or if we're done searching */
|
||||
if (curweight == sdatacount || (upperweight - lowerweight) <= 1)
|
||||
break;
|
||||
}
|
||||
@ -333,20 +348,22 @@ enum huffman_error huffman_compute_tree_from_histo(struct huffman_decoder* decod
|
||||
upperweight = curweight;
|
||||
}
|
||||
|
||||
// assign canonical codes for all nodes based on their code lengths
|
||||
/* assign canonical codes for all nodes based on their code lengths */
|
||||
return huffman_assign_canonical_codes(decoder);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//**************************************************************************
|
||||
// INTERNAL FUNCTIONS
|
||||
//**************************************************************************
|
||||
/***************************************************************************
|
||||
* INTERNAL FUNCTIONS
|
||||
***************************************************************************
|
||||
*/
|
||||
|
||||
//-------------------------------------------------
|
||||
// tree_node_compare - compare two tree nodes
|
||||
// by weight
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* tree_node_compare - compare two tree nodes
|
||||
* by weight
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
static int huffman_tree_node_compare(const void *item1, const void *item2)
|
||||
{
|
||||
@ -360,14 +377,17 @@ static int huffman_tree_node_compare(const void *item1, const void *item2)
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// build_tree - build a huffman tree based on the
|
||||
// data distribution
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* build_tree - build a huffman tree based on the
|
||||
* data distribution
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint32_t totalweight)
|
||||
{
|
||||
// make a list of all non-zero nodes
|
||||
int nextalloc;
|
||||
int maxbits = 0;
|
||||
/* make a list of all non-zero nodes */
|
||||
struct node_t** list = (struct node_t**)malloc(sizeof(struct node_t*) * decoder->numcodes * 2);
|
||||
int curcode, listitems = 0;
|
||||
memset(decoder->huffnode, 0, decoder->numcodes * sizeof(decoder->huffnode[0]));
|
||||
@ -378,7 +398,7 @@ int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint
|
||||
decoder->huffnode[curcode].count = decoder->datahisto[curcode];
|
||||
decoder->huffnode[curcode].bits = curcode;
|
||||
|
||||
// scale the weight by the current effective length, ensuring we don't go to 0
|
||||
/* scale the weight by the current effective length, ensuring we don't go to 0 */
|
||||
decoder->huffnode[curcode].weight = ((uint64_t)decoder->datahisto[curcode]) * ((uint64_t)totalweight) / ((uint64_t)totaldata);
|
||||
if (decoder->huffnode[curcode].weight == 0)
|
||||
decoder->huffnode[curcode].weight = 1;
|
||||
@ -389,7 +409,7 @@ int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint
|
||||
fprintf(stderr, "weight: %d code: %d\n", list[i]->m_weight, list[i]->m_bits);
|
||||
}
|
||||
*/
|
||||
// sort the list by weight, largest weight first
|
||||
/* sort the list by weight, largest weight first */
|
||||
qsort(&list[0], listitems, sizeof(list[0]), huffman_tree_node_compare);
|
||||
/*
|
||||
fprintf(stderr, "Post-sort:\n");
|
||||
@ -398,22 +418,23 @@ int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint
|
||||
}
|
||||
fprintf(stderr, "===================\n");
|
||||
*/
|
||||
// now build the tree
|
||||
int nextalloc = decoder->numcodes;
|
||||
/* now build the tree */
|
||||
nextalloc = decoder->numcodes;
|
||||
|
||||
while (listitems > 1)
|
||||
{
|
||||
// remove lowest two items
|
||||
int curitem;
|
||||
/* remove lowest two items */
|
||||
struct node_t* node1 = &(*list[--listitems]);
|
||||
struct node_t* node0 = &(*list[--listitems]);
|
||||
|
||||
// create new node
|
||||
/* create new node */
|
||||
struct node_t* newnode = &decoder->huffnode[nextalloc++];
|
||||
newnode->parent = NULL;
|
||||
node0->parent = node1->parent = newnode;
|
||||
newnode->weight = node0->weight + node1->weight;
|
||||
|
||||
// insert into list at appropriate location
|
||||
int curitem;
|
||||
/* insert into list at appropriate location */
|
||||
for (curitem = 0; curitem < listitems; curitem++)
|
||||
if (newnode->weight > list[curitem]->weight)
|
||||
{
|
||||
@ -424,8 +445,7 @@ int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint
|
||||
listitems++;
|
||||
}
|
||||
|
||||
// compute the number of bits in each code, and fill in another histogram
|
||||
int maxbits = 0;
|
||||
/* compute the number of bits in each code, and fill in another histogram */
|
||||
for (curcode = 0; curcode < decoder->numcodes; curcode++)
|
||||
{
|
||||
struct node_t *curnode;
|
||||
@ -436,13 +456,13 @@ int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint
|
||||
// if we have a non-zero weight, compute the number of bits
|
||||
if (node->weight > 0)
|
||||
{
|
||||
// determine the number of bits for this node
|
||||
/* determine the number of bits for this node */
|
||||
for (curnode = node; curnode->parent != NULL; curnode = curnode->parent)
|
||||
node->numbits++;
|
||||
if (node->numbits == 0)
|
||||
node->numbits = 1;
|
||||
|
||||
// keep track of the max
|
||||
/* keep track of the max */
|
||||
maxbits = MAX(maxbits, ((int)node->numbits));
|
||||
}
|
||||
}
|
||||
@ -450,15 +470,17 @@ int huffman_build_tree(struct huffman_decoder* decoder, uint32_t totaldata, uint
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// assign_canonical_codes - assign canonical codes
|
||||
// to all the nodes based on the number of bits
|
||||
// in each
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* assign_canonical_codes - assign canonical codes
|
||||
* to all the nodes based on the number of bits
|
||||
* in each
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decoder)
|
||||
{
|
||||
// build up a histogram of bit lengths
|
||||
uint32_t curstart = 0;
|
||||
/* build up a histogram of bit lengths */
|
||||
int curcode, codelen;
|
||||
uint32_t bithisto[33] = { 0 };
|
||||
for (curcode = 0; curcode < decoder->numcodes; curcode++)
|
||||
@ -470,8 +492,7 @@ enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decode
|
||||
bithisto[node->numbits]++;
|
||||
}
|
||||
|
||||
// for each code length, determine the starting code number
|
||||
uint32_t curstart = 0;
|
||||
/* for each code length, determine the starting code number */
|
||||
for (codelen = 32; codelen > 0; codelen--)
|
||||
{
|
||||
uint32_t nextstart = (curstart + bithisto[codelen]) >> 1;
|
||||
@ -481,7 +502,7 @@ enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decode
|
||||
curstart = nextstart;
|
||||
}
|
||||
|
||||
// now assign canonical codes
|
||||
/* now assign canonical codes */
|
||||
for (curcode = 0; curcode < decoder->numcodes; curcode++)
|
||||
{
|
||||
struct node_t* node = &decoder->huffnode[curcode];
|
||||
@ -492,30 +513,34 @@ enum huffman_error huffman_assign_canonical_codes(struct huffman_decoder* decode
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------
|
||||
// build_lookup_table - build a lookup table for
|
||||
// fast decoding
|
||||
//-------------------------------------------------
|
||||
/*-------------------------------------------------
|
||||
* build_lookup_table - build a lookup table for
|
||||
* fast decoding
|
||||
*-------------------------------------------------
|
||||
*/
|
||||
|
||||
void huffman_build_lookup_table(struct huffman_decoder* decoder)
|
||||
{
|
||||
// iterate over all codes
|
||||
/* iterate over all codes */
|
||||
int curcode;
|
||||
for (curcode = 0; curcode < decoder->numcodes; curcode++)
|
||||
{
|
||||
// process all nodes which have non-zero bits
|
||||
/* process all nodes which have non-zero bits */
|
||||
struct node_t* node = &decoder->huffnode[curcode];
|
||||
if (node->numbits > 0)
|
||||
{
|
||||
// set up the entry
|
||||
int shift;
|
||||
lookup_value *dest;
|
||||
lookup_value *destend;
|
||||
/* set up the entry */
|
||||
lookup_value value = MAKE_LOOKUP(curcode, node->numbits);
|
||||
|
||||
// fill all matching entries
|
||||
int shift = decoder->maxbits - node->numbits;
|
||||
lookup_value *dest = &decoder->lookup[node->bits << shift];
|
||||
lookup_value *destend = &decoder->lookup[((node->bits + 1) << shift) - 1];
|
||||
/* fill all matching entries */
|
||||
shift = decoder->maxbits - node->numbits;
|
||||
dest = &decoder->lookup[node->bits << shift];
|
||||
destend = &decoder->lookup[((node->bits + 1) << shift) - 1];
|
||||
while (dest <= destend)
|
||||
*dest++ = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user