mirror of
https://github.com/libretro/RetroArch
synced 2024-12-28 18:31:05 +00:00
cbf49a0b77
* Add xdelta in deps * Include <assert.h> in xdelta3.h - Otherwise the static_assert calls can fail * Build xdelta3 in Makefile.common * Add xdelta support to the softpatching infrastructure - The patching itself isn't fully implemented yet * Adjust how xdelta3.h checks the sizes of some types - Now checks max values instead of relying on autotools * Add some enums that were excluded by the cherry-pick * Remove stray whitespace * Adjust SIZE macros in xdelta3.h - Move them outside the XD3_USE_LARGEFILE64 block - Add more SIZE declarations - Make SIZEOF_UNSIGNED_LONG_LONG contingent on the presence of ULLONG_MAX * Reintegrate xdelta support * Enable support for xdelta's secondary compressors - Necessary for some patches * Fix some format specifiers * Remove unnecessary files from xdelta * Include xdelta3.h with a relative path * Add xdelta3 headers to HEADERS variable * Gate Xdelta support behind HAVE_XDELTA - HAVE_XDELTA is on by default - HAVE_PATCH is still required for HAVE_XDELTA to be meaningful - Support is mostly contingent on the availability of LZMA - Anything modern should be okay - Legacy platforms (e.g. DOS) may need to have Xdelta support disabled - At least until some other solution can be found * Disable HAVE_XDELTA on platforms where the build recently failed - These come from looking at the failed builds on GitHub - These are guesses, and may turn out to be wrong * Fix a potential memory leak - Whoops, looks like I need to call two cleanup functions - xd3_close_stream exists separately from xd3_free_stream * Split the --help printout for --xdelta into its own strlcat call - GCC was complaining about #ifdefs within macro arguments being non-portable * Fix some incorrect printf format specifiers * Modify Xdelta to adhere to C89 - It's mostly using RetroArch's INLINE macro instead of the inline keyword * Slight cleanups * Remove a stray comma that was hindering C89 builds * Add XDelta support to CHANGES.md * Change how the xdelta patch's name is computed - To be in line with other recent refactoring * Fix an incorrect merge - Whoops, this part was from before I figured out how to get the size of a patched file * Explain the song-and-dance behind computing a patched file's size * Define some XDelta3-related constants to 0 on 32-bit platforms * Adjust some Xdelta-related macro definitions - Exclude the encoder, since we're not making patches - Move some #defines to after inclusion of <stdint.h>, to fix undefined behavior - Remove _WIN32_WINNT overrides, since they were for code that we're not using * Fix Xdelta support * Wrap an encoder-only function in `#if XD3_ENCODER`
322 lines
6.9 KiB
C
322 lines
6.9 KiB
C
/* xdelta3 - delta compression tools and library
|
|
Copyright 2016 Joshua MacDonald
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
#ifndef _XDELTA3_SECOND_H_
|
|
#define _XDELTA3_SECOND_H_
|
|
|
|
static INLINE void xd3_bit_state_encode_init (bit_state *bits)
|
|
{
|
|
bits->cur_byte = 0;
|
|
bits->cur_mask = 1;
|
|
}
|
|
|
|
static INLINE int xd3_decode_bits (xd3_stream *stream,
|
|
bit_state *bits,
|
|
const uint8_t **input,
|
|
const uint8_t *input_max,
|
|
usize_t nbits,
|
|
usize_t *valuep)
|
|
{
|
|
usize_t value = 0;
|
|
usize_t vmask = 1 << nbits;
|
|
|
|
if (bits->cur_mask == 0x100) { goto next_byte; }
|
|
|
|
for (;;)
|
|
{
|
|
do
|
|
{
|
|
vmask >>= 1;
|
|
|
|
if (bits->cur_byte & bits->cur_mask)
|
|
{
|
|
value |= vmask;
|
|
}
|
|
|
|
bits->cur_mask <<= 1;
|
|
|
|
if (vmask == 1) { goto done; }
|
|
}
|
|
while (bits->cur_mask != 0x100);
|
|
|
|
next_byte:
|
|
|
|
if (*input == input_max)
|
|
{
|
|
stream->msg = "secondary decoder end of input";
|
|
return XD3_INTERNAL;
|
|
}
|
|
|
|
bits->cur_byte = *(*input)++;
|
|
bits->cur_mask = 1;
|
|
}
|
|
|
|
done:
|
|
|
|
IF_DEBUG2 (DP(RINT "(d) %"W"u ", value));
|
|
|
|
(*valuep) = value;
|
|
return 0;
|
|
}
|
|
|
|
#if REGRESSION_TEST
|
|
/* There may be extra bits at the end of secondary decompression, this macro
|
|
* checks for non-zero bits. This is overly strict, but helps pass the
|
|
* single-bit-error regression test. */
|
|
static int
|
|
xd3_test_clean_bits (xd3_stream *stream, bit_state *bits)
|
|
{
|
|
for (; bits->cur_mask != 0x100; bits->cur_mask <<= 1)
|
|
{
|
|
if (bits->cur_byte & bits->cur_mask)
|
|
{
|
|
stream->msg = "secondary decoder garbage";
|
|
return XD3_INTERNAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
xd3_get_secondary (xd3_stream *stream, xd3_sec_stream **sec_streamp,
|
|
int is_encode)
|
|
{
|
|
if (*sec_streamp == NULL)
|
|
{
|
|
int ret;
|
|
|
|
if ((*sec_streamp = stream->sec_type->alloc (stream)) == NULL)
|
|
{
|
|
stream->msg = "error initializing secondary stream";
|
|
return XD3_INVALID;
|
|
}
|
|
|
|
if ((ret = stream->sec_type->init (stream, *sec_streamp, is_encode)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xd3_decode_secondary (xd3_stream *stream,
|
|
xd3_desect *sect,
|
|
xd3_sec_stream **sec_streamp)
|
|
{
|
|
usize_t dec_size;
|
|
uint8_t *out_used;
|
|
int ret;
|
|
|
|
if ((ret = xd3_get_secondary (stream, sec_streamp, 0)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
/* Decode the size, allocate the buffer. */
|
|
if ((ret = xd3_read_size (stream, & sect->buf,
|
|
sect->buf_max, & dec_size)) ||
|
|
(ret = xd3_decode_allocate (stream, dec_size,
|
|
& sect->copied2, & sect->alloc2)))
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (dec_size == 0)
|
|
{
|
|
stream->msg = "secondary decoder invalid output size";
|
|
return XD3_INVALID_INPUT;
|
|
}
|
|
|
|
out_used = sect->copied2;
|
|
|
|
if ((ret = stream->sec_type->decode (stream, *sec_streamp,
|
|
& sect->buf, sect->buf_max,
|
|
& out_used, out_used + dec_size)))
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
if (sect->buf != sect->buf_max)
|
|
{
|
|
stream->msg = "secondary decoder finished with unused input";
|
|
return XD3_INTERNAL;
|
|
}
|
|
|
|
if (out_used != sect->copied2 + dec_size)
|
|
{
|
|
stream->msg = "secondary decoder short output";
|
|
return XD3_INTERNAL;
|
|
}
|
|
|
|
sect->buf = sect->copied2;
|
|
sect->buf_max = sect->copied2 + dec_size;
|
|
sect->size = dec_size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if XD3_ENCODER
|
|
static INLINE int xd3_encode_bit (xd3_stream *stream,
|
|
xd3_output **output,
|
|
bit_state *bits,
|
|
usize_t bit)
|
|
{
|
|
int ret;
|
|
|
|
if (bit)
|
|
{
|
|
bits->cur_byte |= bits->cur_mask;
|
|
}
|
|
|
|
/* OPT: Might help to buffer more than 8 bits at once. */
|
|
if (bits->cur_mask == 0x80)
|
|
{
|
|
if ((ret = xd3_emit_byte (stream, output, bits->cur_byte)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
bits->cur_mask = 1;
|
|
bits->cur_byte = 0;
|
|
}
|
|
else
|
|
{
|
|
bits->cur_mask <<= 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static INLINE int xd3_flush_bits (xd3_stream *stream,
|
|
xd3_output **output,
|
|
bit_state *bits)
|
|
{
|
|
return (bits->cur_mask == 1) ? 0 :
|
|
xd3_emit_byte (stream, output, bits->cur_byte);
|
|
}
|
|
|
|
static INLINE int xd3_encode_bits (xd3_stream *stream,
|
|
xd3_output **output,
|
|
bit_state *bits,
|
|
usize_t nbits,
|
|
usize_t value)
|
|
{
|
|
int ret;
|
|
usize_t mask = 1 << nbits;
|
|
|
|
XD3_ASSERT (nbits > 0);
|
|
XD3_ASSERT (nbits < sizeof (usize_t) * 8);
|
|
XD3_ASSERT (value < mask);
|
|
|
|
do
|
|
{
|
|
mask >>= 1;
|
|
|
|
if ((ret = xd3_encode_bit (stream, output, bits, value & mask)))
|
|
{
|
|
return ret;
|
|
}
|
|
}
|
|
while (mask != 1);
|
|
|
|
IF_DEBUG2 (DP(RINT "(e) %"W"u ", value));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
xd3_encode_secondary (xd3_stream *stream,
|
|
xd3_output **head,
|
|
xd3_output **tail,
|
|
xd3_sec_stream **sec_streamp,
|
|
xd3_sec_cfg *cfg,
|
|
int *did_it)
|
|
{
|
|
xd3_output *tmp_head;
|
|
xd3_output *tmp_tail;
|
|
|
|
usize_t comp_size;
|
|
usize_t orig_size;
|
|
|
|
int ret;
|
|
|
|
orig_size = xd3_sizeof_output (*head);
|
|
|
|
if (orig_size < SECONDARY_MIN_INPUT) { return 0; }
|
|
|
|
if ((ret = xd3_get_secondary (stream, sec_streamp, 1)) != 0)
|
|
{
|
|
return ret;
|
|
}
|
|
|
|
tmp_head = xd3_alloc_output (stream, NULL);
|
|
|
|
/* Encode the size, encode the data. Encoding the size makes it
|
|
* simpler, but is a little gross. Should not need the entire
|
|
* section in contiguous memory, but it is much easier this way. */
|
|
if ((ret = xd3_emit_size (stream, & tmp_head, orig_size)) ||
|
|
(ret = stream->sec_type->encode (stream, *sec_streamp, *head,
|
|
tmp_head, cfg)))
|
|
{
|
|
goto getout;
|
|
}
|
|
|
|
/* If the secondary compressor determines it's no good, it returns
|
|
* XD3_NOSECOND. */
|
|
|
|
/* Setup tmp_tail, comp_size */
|
|
tmp_tail = tmp_head;
|
|
comp_size = tmp_head->next;
|
|
|
|
while (tmp_tail->next_page != NULL)
|
|
{
|
|
tmp_tail = tmp_tail->next_page;
|
|
comp_size += tmp_tail->next;
|
|
}
|
|
|
|
XD3_ASSERT (comp_size == xd3_sizeof_output (tmp_head));
|
|
XD3_ASSERT (tmp_tail != NULL);
|
|
|
|
if (comp_size < (orig_size - SECONDARY_MIN_SAVINGS) || cfg->inefficient)
|
|
{
|
|
if (comp_size < orig_size)
|
|
{
|
|
IF_DEBUG1(DP(RINT "[encode_secondary] saved %"W"u bytes: %"W"u -> %"W"u (%0.2f%%)\n",
|
|
orig_size - comp_size, orig_size, comp_size,
|
|
100.0 * (double) comp_size / (double) orig_size));
|
|
}
|
|
|
|
xd3_free_output (stream, *head);
|
|
|
|
*head = tmp_head;
|
|
*tail = tmp_tail;
|
|
*did_it = 1;
|
|
}
|
|
else
|
|
{
|
|
getout:
|
|
if (ret == XD3_NOSECOND) { ret = 0; }
|
|
xd3_free_output (stream, tmp_head);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
#endif /* XD3_ENCODER */
|
|
#endif /* _XDELTA3_SECOND_H_ */
|