XDelta patch support (Take 2) (#15915)

* 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`
This commit is contained in:
Jesse Talavera-Greenberg 2023-11-23 23:19:07 -05:00 committed by GitHub
parent 3e6ada7239
commit cbf49a0b77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
38 changed files with 12159 additions and 46 deletions

View file

@ -1,4 +1,5 @@
# Future
- PATCHES: Add support for XDelta-formatted patches.
# 1.16.0
- 3DS: Update __system_initArgv

View file

@ -250,7 +250,24 @@ OBJ += frontend/frontend_driver.o \
ifeq ($(HAVE_PATCH), 1)
DEFINES += -DHAVE_PATCH
OBJ += tasks/task_patch.o
OBJ += tasks/task_patch.o
ifeq ($(HAVE_XDELTA), 1)
DEFINES += -DHAVE_XDELTA -DSECONDARY_DJW -DSECONDARY_LZMA -DSECONDARY_FGK
INCLUDE_DIRS += -I$(DEPS_DIR)/xdelta3 -I$(LIBRETRO_COMM_DIR)
LIBS += -llzma
OBJ += $(DEPS_DIR)/xdelta3/xdelta3.o
HEADERS += xdelta3.h \
xdelta3-cfgs.h \
xdelta3-fgk.h \
xdelta3-hash.h \
xdelta3-internal.h \
xdelta3-list.h \
xdelta3-lzma.h \
xdelta3-second.h
# These headers are added to the makefile because xdelta3 does weird things
# with its #includes, which affects dependency tracking and project analysis
# (e.g. for IDEs).
endif
endif
OBJ += \

View file

@ -66,6 +66,7 @@ HAVE_CHD = 0 # disabled due to static libretro-common and libchdr conflicts betw
HAVE_STB_VORBIS = 1
HAVE_IBXM = 1
HAVE_CORE_INFO_CACHE = 1
HAVE_XDELTA = 0 # disabled because <lzma.h> isn't available (or we haven't figured out how to install it)
HAVE_RGUI = 1
HAVE_MATERIALUI = 0

View file

@ -105,6 +105,7 @@ HAVE_OZONE = 0
HAVE_ZLIB = 1
HAVE_CONFIGFILE = 1
HAVE_PATCH = 1
HAVE_XDELTA = 0 # Disabled until we figure out how to include <lzma.h>
HAVE_CHEATS = 1
HAVE_CHEEVOS = 0
HAVE_LIBSHAKE = 0

View file

@ -129,6 +129,7 @@ HAVE_ZLIB := 1
HAVE_7ZIP := 1
HAVE_CONFIGFILE := 1
HAVE_PATCH := 1
HAVE_XDELTA := 0 # disabled because <lzma.h> isn't available (or we haven't figured out how to install it)
HAVE_CHEATS := 1
HAVE_SCREENSHOTS := 1
HAVE_REWIND := 1

View file

@ -54,6 +54,7 @@ else
HAVE_MENU = 1
HAVE_CONFIGFILE = 1
HAVE_PATCH = 1
HAVE_PATCH = 0 # disabled because <lzma.h> isn't available (or we haven't figured out how to install it)
HAVE_CHEATS = 1
HAVE_RGUI = 1
HAVE_MATERIALUI = 0

View file

@ -6,7 +6,7 @@ HAVE_THREADS ?= 1
BIG_STACK ?= 0
LOAD_WITHOUT_CORE_INFO ?= 0
HAVE_STATIC_DUMMY ?= 0
HAVE_XDELTA ?= 1
TARGET = retroarchpsp
ifeq ($(DEBUG), 1)

View file

@ -107,6 +107,7 @@ HAVE_OZONE = 0
HAVE_ZLIB = 1
HAVE_CONFIGFILE = 1
HAVE_PATCH = 1
HAVE_XDELTA = 0 # disabled because <lzma.h> isn't available (or we haven't figured out how to install it)
HAVE_CHEATS = 1
HAVE_CHEEVOS = 0
HAVE_LIBSHAKE = 0

View file

@ -107,6 +107,7 @@ HAVE_OZONE = 0
HAVE_ZLIB = 1
HAVE_CONFIGFILE = 1
HAVE_PATCH = 1
HAVE_XDELTA = 0 # Disabled until we figure out how to include <lzma.h>
HAVE_CHEATS = 1
HAVE_CHEEVOS = 0
HAVE_LIBSHAKE = 0

View file

@ -84,6 +84,7 @@ else
HAVE_GFX_WIDGETS := 1
HAVE_CONFIGFILE := 1
HAVE_PATCH := 1
HAVE_XDELTA := 1 # disabled because <lzma.h> isn't available (or we haven't figured out how to install it)
HAVE_CHEATS := 1
HAVE_OVERLAY := 1
HAVE_MATERIALUI := 1

View file

@ -137,6 +137,7 @@ HAVE_ZLIB := 1
HAVE_7ZIP := 1
HAVE_CONFIGFILE := 1
HAVE_PATCH := 1
HAVE_XDELTA := 0 # disabled because <lzma.h> isn't available (or we haven't figured out how to install it)
HAVE_CHEATS := 1
HAVE_SCREENSHOTS := 1
HAVE_REWIND := 1

View file

@ -143,6 +143,7 @@ endif
HAVE_RBMP = 1
HAVE_CONFIGFILE = 1
HAVE_PATCH = 1
HAVE_XDELTA = 0 # disabled because <lzma.h> isn't available (or we haven't figured out how to install it)
HAVE_REWIND = 1
HAVE_CHEATS = 1
HAVE_MENU = 1

View file

@ -1040,7 +1040,7 @@
#define DEFAULT_NOTIFICATION_SHOW_CHEATS_APPLIED true
/* Display a notification when applying an
* IPS/BPS/UPS patch file */
* IPS/BPS/UPS/Xdelta patch file */
#define DEFAULT_NOTIFICATION_SHOW_PATCH_APPLIED true
/* Display a notification when loading an

View file

@ -2917,6 +2917,7 @@ void config_set_defaults(void *data)
retroarch_ctl(RARCH_CTL_UNSET_UPS_PREF, NULL);
retroarch_ctl(RARCH_CTL_UNSET_BPS_PREF, NULL);
retroarch_ctl(RARCH_CTL_UNSET_IPS_PREF, NULL);
retroarch_ctl(RARCH_CTL_UNSET_XDELTA_PREF, NULL);
*recording_st->output_dir = '\0';
*recording_st->config_dir = '\0';

176
deps/xdelta3/LICENSE vendored Normal file
View file

@ -0,0 +1,176 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

37
deps/xdelta3/README.md vendored Normal file
View file

@ -0,0 +1,37 @@
Xdelta 3.x readme.txt
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
2009, 2010, 2011, 2012, 2013, 2014, 2015
<josh.macdonald@gmail.com>
Thanks for downloading Xdelta!
This directory contains the Xdelta3 command-line interface (CLI) and source
distribution for VCDIFF differential compression, a.k.a. delta
compression. The latest information and downloads are available here:
http://xdelta.org/
http://github.com/jmacd/xdelta/
Xdelta can be configured to use XZ Utils for secondary compression:
http://tukaani.org/xz/
The command-line syntax is detailed here:
https://github.com/jmacd/xdelta/blob/wiki/CommandLineSyntax.md
Run 'xdelta3 -h' for brief help. Run 'xdelta3 test' for built-in tests.
Sample commands (like gzip, -e means encode, -d means decode)
xdelta3 -9 -S lzma -e -f -s OLD_FILE NEW_FILE DELTA_FILE
xdelta3 -d -s OLD_FILE DELTA_FILE DECODED_FILE
File bug reports and browse open support issues here:
https://github.com/jmacd/xdelta/issues
The source distribution contains the C/C++/Python APIs, Unix, Microsoft VC++
and Cygwin builds. Xdelta3 is covered under the terms of the APL, see
LICENSE.

171
deps/xdelta3/xdelta3-cfgs.h vendored Normal file
View file

@ -0,0 +1,171 @@
/* 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.
*/
/******************************************************************
SOFT string matcher
******************************************************************/
#if XD3_BUILD_SOFT
#define TEMPLATE soft
#define LLOOK stream->smatcher.large_look
#define LSTEP stream->smatcher.large_step
#define SLOOK stream->smatcher.small_look
#define SCHAIN stream->smatcher.small_chain
#define SLCHAIN stream->smatcher.small_lchain
#define MAXLAZY stream->smatcher.max_lazy
#define LONGENOUGH stream->smatcher.long_enough
#define SOFTCFG 1
#include "xdelta3.c"
#undef SOFTCFG
#undef TEMPLATE
#undef LLOOK
#undef SLOOK
#undef LSTEP
#undef SCHAIN
#undef SLCHAIN
#undef MAXLAZY
#undef LONGENOUGH
#endif
#define SOFTCFG 0
/************************************************************
FASTEST string matcher
**********************************************************/
#if XD3_BUILD_FASTEST
#define TEMPLATE fastest
#define LLOOK 9
#define LSTEP 26
#define SLOOK 4U
#define SCHAIN 1
#define SLCHAIN 1
#define MAXLAZY 6
#define LONGENOUGH 6
#include "xdelta3.c"
#undef TEMPLATE
#undef LLOOK
#undef SLOOK
#undef LSTEP
#undef SCHAIN
#undef SLCHAIN
#undef MAXLAZY
#undef LONGENOUGH
#endif
/************************************************************
FASTER string matcher
**********************************************************/
#if XD3_BUILD_FASTER
#define TEMPLATE faster
#define LLOOK 9
#define LSTEP 15
#define SLOOK 4U
#define SCHAIN 1
#define SLCHAIN 1
#define MAXLAZY 18
#define LONGENOUGH 18
#include "xdelta3.c"
#undef TEMPLATE
#undef LLOOK
#undef SLOOK
#undef LSTEP
#undef SCHAIN
#undef SLCHAIN
#undef MAXLAZY
#undef LONGENOUGH
#endif
/******************************************************
FAST string matcher
********************************************************/
#if XD3_BUILD_FAST
#define TEMPLATE fast
#define LLOOK 9
#define LSTEP 8
#define SLOOK 4U
#define SCHAIN 4
#define SLCHAIN 1
#define MAXLAZY 18
#define LONGENOUGH 35
#include "xdelta3.c"
#undef TEMPLATE
#undef LLOOK
#undef SLOOK
#undef LSTEP
#undef SCHAIN
#undef SLCHAIN
#undef MAXLAZY
#undef LONGENOUGH
#endif
/**************************************************
SLOW string matcher
**************************************************************/
#if XD3_BUILD_SLOW
#define TEMPLATE slow
#define LLOOK 9
#define LSTEP 2
#define SLOOK 4U
#define SCHAIN 44
#define SLCHAIN 13
#define MAXLAZY 90
#define LONGENOUGH 70
#include "xdelta3.c"
#undef TEMPLATE
#undef LLOOK
#undef SLOOK
#undef LSTEP
#undef SCHAIN
#undef SLCHAIN
#undef MAXLAZY
#undef LONGENOUGH
#endif
/********************************************************
DEFAULT string matcher
************************************************************/
#if XD3_BUILD_DEFAULT
#define TEMPLATE default
#define LLOOK 9
#define LSTEP 3
#define SLOOK 4U
#define SCHAIN 8
#define SLCHAIN 2
#define MAXLAZY 36
#define LONGENOUGH 70
#include "xdelta3.c"
#undef TEMPLATE
#undef LLOOK
#undef SLOOK
#undef LSTEP
#undef SCHAIN
#undef SLCHAIN
#undef MAXLAZY
#undef LONGENOUGH
#endif

1222
deps/xdelta3/xdelta3-decode.h vendored Normal file

File diff suppressed because it is too large Load diff

1838
deps/xdelta3/xdelta3-djw.h vendored Normal file

File diff suppressed because it is too large Load diff

862
deps/xdelta3/xdelta3-fgk.h vendored Normal file
View file

@ -0,0 +1,862 @@
/* 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.
For demonstration purposes only.
*/
#ifndef _XDELTA3_FGK_h_
#define _XDELTA3_FGK_h_
/* To include RetroArch's INLINE macro */
#include "retro_inline.h"
/* An implementation of the FGK algorithm described by D.E. Knuth in
* "Dynamic Huffman Coding" in Journal of Algorithms 6. */
/* A 32bit counter (fgk_weight) is used as the frequency counter for
* nodes in the huffman tree. TODO: Need oto test for overflow and/or
* reset stats. */
typedef struct _fgk_stream fgk_stream;
typedef struct _fgk_node fgk_node;
typedef struct _fgk_block fgk_block;
typedef unsigned int fgk_bit;
typedef uint32_t fgk_weight;
struct _fgk_block {
union {
fgk_node *un_leader;
fgk_block *un_freeptr;
} un;
};
#define block_leader un.un_leader
#define block_freeptr un.un_freeptr
/* The code can also support fixed huffman encoding/decoding. */
#define IS_ADAPTIVE 1
/* weight is a count of the number of times this element has been seen
* in the current encoding/decoding. parent, right_child, and
* left_child are pointers defining the tree structure. right and
* left point to neighbors in an ordered sequence of weights. The
* left child of a node is always guaranteed to have weight not
* greater than its sibling. fgk_blockLeader points to the element
* with the same weight as itself which is closest to the next
* increasing weight block. */
struct _fgk_node
{
fgk_weight weight;
fgk_node *parent;
fgk_node *left_child;
fgk_node *right_child;
fgk_node *left;
fgk_node *right;
fgk_block *my_block;
};
/* alphabet_size is the a count of the number of possible leaves in
* the huffman tree. The number of total nodes counting internal
* nodes is ((2 * alphabet_size) - 1). zero_freq_count is the number
* of elements remaining which have zero frequency. zero_freq_exp and
* zero_freq_rem satisfy the equation zero_freq_count =
* 2^zero_freq_exp + zero_freq_rem. root_node is the root of the
* tree, which is initialized to a node with zero frequency and
* contains the 0th such element. free_node contains a pointer to the
* next available fgk_node space. alphabet contains all the elements
* and is indexed by N. remaining_zeros points to the head of the
* list of zeros. */
struct _fgk_stream
{
usize_t alphabet_size;
usize_t zero_freq_count;
usize_t zero_freq_exp;
usize_t zero_freq_rem;
usize_t coded_depth;
usize_t total_nodes;
usize_t total_blocks;
fgk_bit *coded_bits;
fgk_block *block_array;
fgk_block *free_block;
fgk_node *decode_ptr;
fgk_node *remaining_zeros;
fgk_node *alphabet;
fgk_node *root_node;
fgk_node *free_node;
};
/*********************************************************************/
/* Encoder */
/*********************************************************************/
static fgk_stream* fgk_alloc (xd3_stream *stream /*, usize_t alphabet_size */);
static int fgk_init (xd3_stream *stream,
fgk_stream *h,
int is_encode);
static usize_t fgk_encode_data (fgk_stream *h,
usize_t n);
static INLINE fgk_bit fgk_get_encoded_bit (fgk_stream *h);
static int xd3_encode_fgk (xd3_stream *stream,
fgk_stream *sec_stream,
xd3_output *input,
xd3_output *output,
xd3_sec_cfg *cfg);
/*********************************************************************/
/* Decoder */
/*********************************************************************/
static INLINE int fgk_decode_bit (fgk_stream *h,
fgk_bit b);
static usize_t fgk_decode_data (fgk_stream *h);
static void fgk_destroy (xd3_stream *stream,
fgk_stream *h);
static int xd3_decode_fgk (xd3_stream *stream,
fgk_stream *sec_stream,
const uint8_t **input,
const uint8_t *const input_end,
uint8_t **output,
const uint8_t *const output_end);
/*********************************************************************/
/* Private */
/*********************************************************************/
static unsigned int fgk_find_nth_zero (fgk_stream *h, usize_t n);
static usize_t fgk_nth_zero (fgk_stream *h, usize_t n);
static void fgk_update_tree (fgk_stream *h, usize_t n);
static fgk_node* fgk_increase_zero_weight (fgk_stream *h, usize_t n);
static void fgk_eliminate_zero (fgk_stream* h, fgk_node *node);
static void fgk_move_right (fgk_stream *h, fgk_node *node);
static void fgk_promote (fgk_stream *h, fgk_node *node);
static void fgk_init_node (fgk_node *node, usize_t i, usize_t size);
static fgk_block* fgk_make_block (fgk_stream *h, fgk_node *l);
static void fgk_free_block (fgk_stream *h, fgk_block *b);
static void fgk_factor_remaining (fgk_stream *h);
static INLINE void fgk_swap_ptrs (fgk_node **one, fgk_node **two);
/*********************************************************************/
/* Basic Routines */
/*********************************************************************/
/* returns an initialized huffman encoder for an alphabet with the
* given size. returns NULL if enough memory cannot be allocated */
static fgk_stream* fgk_alloc (xd3_stream *stream /*, int alphabet_size0 */)
{
usize_t alphabet_size0 = ALPHABET_SIZE;
fgk_stream *h;
if ((h = (fgk_stream*) xd3_alloc (stream, 1, sizeof (fgk_stream))) == NULL)
{
return NULL;
}
h->total_nodes = (2 * alphabet_size0) - 1;
h->total_blocks = (2 * h->total_nodes);
h->alphabet = (fgk_node*) xd3_alloc (stream, h->total_nodes, sizeof (fgk_node));
h->block_array = (fgk_block*) xd3_alloc (stream, h->total_blocks, sizeof (fgk_block));
h->coded_bits = (fgk_bit*) xd3_alloc (stream, alphabet_size0, sizeof (fgk_bit));
if (h->coded_bits == NULL ||
h->alphabet == NULL ||
h->block_array == NULL)
{
fgk_destroy (stream, h);
return NULL;
}
h->alphabet_size = alphabet_size0;
return h;
}
static int fgk_init (xd3_stream *stream, fgk_stream *h, int is_encode)
{
usize_t ui;
ssize_t si;
h->root_node = h->alphabet;
h->decode_ptr = h->root_node;
h->free_node = h->alphabet + h->alphabet_size;
h->remaining_zeros = h->alphabet;
h->coded_depth = 0;
h->zero_freq_count = h->alphabet_size + 2;
/* after two calls to factor_remaining, zero_freq_count == alphabet_size */
fgk_factor_remaining(h); /* set ZFE and ZFR */
fgk_factor_remaining(h); /* set ZFDB according to prev state */
IF_DEBUG (memset (h->alphabet, 0, sizeof (h->alphabet[0]) * h->total_nodes));
for (ui = 0; ui < h->total_blocks-1; ui += 1)
{
h->block_array[ui].block_freeptr = &h->block_array[ui + 1];
}
h->block_array[h->total_blocks - 1].block_freeptr = NULL;
h->free_block = h->block_array;
/* Zero frequency nodes are inserted in the first alphabet_size
* positions, with Value, weight, and a pointer to the next zero
* frequency node. */
for (si = h->alphabet_size - 1; si >= 0; si -= 1)
{
fgk_init_node (h->alphabet + si, (usize_t) si, h->alphabet_size);
}
return 0;
}
static void fgk_swap_ptrs(fgk_node **one, fgk_node **two)
{
fgk_node *tmp = *one;
*one = *two;
*two = tmp;
}
/* Takes huffman transmitter h and n, the nth elt in the alphabet, and
* returns the number of required to encode n. */
static usize_t fgk_encode_data (fgk_stream* h, usize_t n)
{
fgk_node *target_ptr = h->alphabet + n;
XD3_ASSERT (n < h->alphabet_size);
h->coded_depth = 0;
/* First encode the binary representation of the nth remaining
* zero frequency element in reverse such that bit, which will be
* encoded from h->coded_depth down to 0 will arrive in increasing
* order following the tree path. If there is only one left, it
* is not neccesary to encode these bits. */
if (IS_ADAPTIVE && target_ptr->weight == 0)
{
usize_t where, shift;
usize_t bits;
where = fgk_find_nth_zero(h, n);
shift = 1;
if (h->zero_freq_rem == 0)
{
bits = h->zero_freq_exp;
}
else
{
bits = h->zero_freq_exp + 1;
}
while (bits > 0)
{
h->coded_bits[h->coded_depth++] = (shift & where) && 1;
bits -= 1;
shift <<= 1;
};
target_ptr = h->remaining_zeros;
}
/* The path from root to node is filled into coded_bits in reverse so
* that it is encoded in the right order */
while (target_ptr != h->root_node)
{
h->coded_bits[h->coded_depth++] = (target_ptr->parent->right_child == target_ptr);
target_ptr = target_ptr->parent;
}
if (IS_ADAPTIVE)
{
fgk_update_tree(h, n);
}
return h->coded_depth;
}
/* Should be called as many times as fgk_encode_data returns.
*/
static INLINE fgk_bit fgk_get_encoded_bit (fgk_stream *h)
{
XD3_ASSERT (h->coded_depth > 0);
return h->coded_bits[--h->coded_depth];
}
/* This procedure updates the tree after alphabet[n] has been encoded
* or decoded.
*/
static void fgk_update_tree (fgk_stream *h, usize_t n)
{
fgk_node *incr_node;
if (h->alphabet[n].weight == 0)
{
incr_node = fgk_increase_zero_weight (h, n);
}
else
{
incr_node = h->alphabet + n;
}
while (incr_node != h->root_node)
{
fgk_move_right (h, incr_node);
fgk_promote (h, incr_node);
incr_node->weight += 1; /* incr the parent */
incr_node = incr_node->parent; /* repeat */
}
h->root_node->weight += 1;
}
static void fgk_move_right (fgk_stream *h, fgk_node *move_fwd)
{
fgk_node **fwd_par_ptr, **back_par_ptr;
fgk_node *move_back, *tmp;
move_back = move_fwd->my_block->block_leader;
if (move_fwd == move_back ||
move_fwd->parent == move_back ||
move_fwd->weight == 0)
{
return;
}
move_back->right->left = move_fwd;
if (move_fwd->left)
{
move_fwd->left->right = move_back;
}
tmp = move_fwd->right;
move_fwd->right = move_back->right;
if (tmp == move_back)
{
move_back->right = move_fwd;
}
else
{
tmp->left = move_back;
move_back->right = tmp;
}
tmp = move_back->left;
move_back->left = move_fwd->left;
if (tmp == move_fwd)
{
move_fwd->left = move_back;
}
else
{
tmp->right = move_fwd;
move_fwd->left = tmp;
}
if (move_fwd->parent->right_child == move_fwd)
{
fwd_par_ptr = &move_fwd->parent->right_child;
}
else
{
fwd_par_ptr = &move_fwd->parent->left_child;
}
if (move_back->parent->right_child == move_back)
{
back_par_ptr = &move_back->parent->right_child;
}
else
{
back_par_ptr = &move_back->parent->left_child;
}
fgk_swap_ptrs (&move_fwd->parent, &move_back->parent);
fgk_swap_ptrs (fwd_par_ptr, back_par_ptr);
move_fwd->my_block->block_leader = move_fwd;
}
/* Shifts node, the leader of its block, into the next block. */
static void fgk_promote (fgk_stream *h, fgk_node *node)
{
fgk_node *my_left, *my_right;
fgk_block *cur_block;
my_right = node->right;
my_left = node->left;
cur_block = node->my_block;
if (node->weight == 0)
{
return;
}
/* if left is right child, parent of remaining zeros case (?), means parent
* has same weight as right child. */
if (my_left == node->right_child &&
node->left_child &&
node->left_child->weight == 0)
{
XD3_ASSERT (node->left_child == h->remaining_zeros);
XD3_ASSERT (node->right_child->weight == (node->weight+1)); /* child weight was already incremented */
if (node->weight == (my_right->weight - 1) && my_right != h->root_node)
{
fgk_free_block (h, cur_block);
node->my_block = my_right->my_block;
my_left->my_block = my_right->my_block;
}
return;
}
if (my_left == h->remaining_zeros)
{
return;
}
/* true if not the leftmost node */
if (my_left->my_block == cur_block)
{
my_left->my_block->block_leader = my_left;
}
else
{
fgk_free_block (h, cur_block);
}
/* node->parent != my_right */
if ((node->weight == (my_right->weight - 1)) && (my_right != h->root_node))
{
node->my_block = my_right->my_block;
}
else
{
node->my_block = fgk_make_block (h, node);
}
}
/* When an element is seen the first time this is called to remove it from the list of
* zero weight elements and introduce a new internal node to the tree. */
static fgk_node* fgk_increase_zero_weight (fgk_stream *h, usize_t n)
{
fgk_node *this_zero, *new_internal, *zero_ptr;
this_zero = h->alphabet + n;
if (h->zero_freq_count == 1)
{
/* this is the last one */
this_zero->right_child = NULL;
if (this_zero->right->weight == 1)
{
this_zero->my_block = this_zero->right->my_block;
}
else
{
this_zero->my_block = fgk_make_block (h, this_zero);
}
h->remaining_zeros = NULL;
return this_zero;
}
zero_ptr = h->remaining_zeros;
new_internal = h->free_node++;
new_internal->parent = zero_ptr->parent;
new_internal->right = zero_ptr->right;
new_internal->weight = 0;
new_internal->right_child = this_zero;
new_internal->left = this_zero;
if (h->remaining_zeros == h->root_node)
{
/* This is the first element to be coded */
h->root_node = new_internal;
this_zero->my_block = fgk_make_block (h, this_zero);
new_internal->my_block = fgk_make_block (h, new_internal);
}
else
{
new_internal->right->left = new_internal;
if (zero_ptr->parent->right_child == zero_ptr)
{
zero_ptr->parent->right_child = new_internal;
}
else
{
zero_ptr->parent->left_child = new_internal;
}
if (new_internal->right->weight == 1)
{
new_internal->my_block = new_internal->right->my_block;
}
else
{
new_internal->my_block = fgk_make_block (h, new_internal);
}
this_zero->my_block = new_internal->my_block;
}
fgk_eliminate_zero (h, this_zero);
new_internal->left_child = h->remaining_zeros;
this_zero->right = new_internal;
this_zero->left = h->remaining_zeros;
this_zero->parent = new_internal;
this_zero->left_child = NULL;
this_zero->right_child = NULL;
h->remaining_zeros->parent = new_internal;
h->remaining_zeros->right = this_zero;
return this_zero;
}
/* When a zero frequency element is encoded, it is followed by the
* binary representation of the index into the remaining elements.
* Sets a cache to the element before it so that it can be removed
* without calling this procedure again. */
static unsigned int fgk_find_nth_zero (fgk_stream* h, usize_t n)
{
fgk_node *target_ptr = h->alphabet + n;
fgk_node *head_ptr = h->remaining_zeros;
unsigned int idx = 0;
while (target_ptr != head_ptr)
{
head_ptr = head_ptr->right_child;
idx += 1;
}
return idx;
}
/* Splices node out of the list of zeros. */
static void fgk_eliminate_zero (fgk_stream* h, fgk_node *node)
{
if (h->zero_freq_count == 1)
{
return;
}
fgk_factor_remaining(h);
if (node->left_child == NULL)
{
h->remaining_zeros = h->remaining_zeros->right_child;
h->remaining_zeros->left_child = NULL;
}
else if (node->right_child == NULL)
{
node->left_child->right_child = NULL;
}
else
{
node->right_child->left_child = node->left_child;
node->left_child->right_child = node->right_child;
}
}
static void fgk_init_node (fgk_node *node, usize_t i, usize_t size)
{
if (i < size - 1)
{
node->right_child = node + 1;
}
else
{
node->right_child = NULL;
}
if (i >= 1)
{
node->left_child = node - 1;
}
else
{
node->left_child = NULL;
}
node->weight = 0;
node->parent = NULL;
node->right = NULL;
node->left = NULL;
node->my_block = NULL;
}
/* The data structure used is an array of blocks, which are unions of
* free pointers and huffnode pointers. free blocks are a linked list
* of free blocks, the front of which is h->free_block. The used
* blocks are pointers to the head of each block. */
static fgk_block* fgk_make_block (fgk_stream *h, fgk_node* lead)
{
fgk_block *ret = h->free_block;
XD3_ASSERT (h->free_block != NULL);
h->free_block = h->free_block->block_freeptr;
ret->block_leader = lead;
return ret;
}
/* Restores the block to the front of the free list. */
static void fgk_free_block (fgk_stream *h, fgk_block *b)
{
b->block_freeptr = h->free_block;
h->free_block = b;
}
/* sets zero_freq_count, zero_freq_rem, and zero_freq_exp to satsity
* the equation given above. */
static void fgk_factor_remaining (fgk_stream *h)
{
unsigned int i;
i = (--h->zero_freq_count);
h->zero_freq_exp = 0;
while (i > 1)
{
h->zero_freq_exp += 1;
i >>= 1;
}
i = 1 << h->zero_freq_exp;
h->zero_freq_rem = h->zero_freq_count - i;
}
/* receives a bit at a time and returns true when a complete code has
* been received.
*/
static INLINE int fgk_decode_bit (fgk_stream* h, fgk_bit b)
{
XD3_ASSERT (b == 1 || b == 0);
if (IS_ADAPTIVE && h->decode_ptr->weight == 0)
{
usize_t bitsreq;
if (h->zero_freq_rem == 0)
{
bitsreq = h->zero_freq_exp;
}
else
{
bitsreq = h->zero_freq_exp + 1;
}
h->coded_bits[h->coded_depth] = b;
h->coded_depth += 1;
return h->coded_depth >= bitsreq;
}
else
{
if (b)
{
h->decode_ptr = h->decode_ptr->right_child;
}
else
{
h->decode_ptr = h->decode_ptr->left_child;
}
if (h->decode_ptr->left_child == NULL)
{
/* If the weight is non-zero, finished. */
if (h->decode_ptr->weight != 0)
{
return 1;
}
/* zero_freq_count is dropping to 0, finished. */
return h->zero_freq_count == 1;
}
else
{
return 0;
}
}
}
static usize_t fgk_nth_zero (fgk_stream* h, usize_t n)
{
fgk_node *ret = h->remaining_zeros;
/* ERROR: if during this loop (ret->right_child == NULL) then the
* encoder's zero count is too high. Could return an error code
* now, but is probably unnecessary overhead, since the caller
* should check integrity anyway. */
for (; n != 0 && ret->right_child != NULL; n -= 1)
{
ret = ret->right_child;
}
return (usize_t)(ret - h->alphabet);
}
/* once fgk_decode_bit returns 1, this retrieves an index into the
* alphabet otherwise this returns 0, indicating more bits are
* required.
*/
static usize_t fgk_decode_data (fgk_stream* h)
{
usize_t elt = (usize_t)(h->decode_ptr - h->alphabet);
if (IS_ADAPTIVE && h->decode_ptr->weight == 0) {
usize_t i = 0;
usize_t n = 0;
if (h->coded_depth > 0)
{
for (; i < h->coded_depth - 1; i += 1)
{
n |= h->coded_bits[i];
n <<= 1;
}
}
n |= h->coded_bits[i];
elt = fgk_nth_zero(h, n);
}
h->coded_depth = 0;
if (IS_ADAPTIVE)
{
fgk_update_tree(h, elt);
}
h->decode_ptr = h->root_node;
return elt;
}
static void fgk_destroy (xd3_stream *stream,
fgk_stream *h)
{
if (h != NULL)
{
xd3_free (stream, h->alphabet);
xd3_free (stream, h->coded_bits);
xd3_free (stream, h->block_array);
xd3_free (stream, h);
}
}
/*********************************************************************/
/* Xdelta */
/*********************************************************************/
#if XD3_ENCODER
static int
xd3_encode_fgk (xd3_stream *stream, fgk_stream *sec_stream, xd3_output *input, xd3_output *output, xd3_sec_cfg *cfg)
{
bit_state bstate = BIT_STATE_ENCODE_INIT;
xd3_output *cur_page;
int ret;
/* OPT: quit compression early if it looks bad */
for (cur_page = input; cur_page; cur_page = cur_page->next_page)
{
const uint8_t *inp = cur_page->base;
const uint8_t *inp_max = inp + cur_page->next;
while (inp < inp_max)
{
usize_t bits = fgk_encode_data (sec_stream, *inp++);
while (bits--)
{
if ((ret = xd3_encode_bit (stream, & output, & bstate, fgk_get_encoded_bit (sec_stream)))) { return ret; }
}
}
}
return xd3_flush_bits (stream, & output, & bstate);
}
#endif
static int
xd3_decode_fgk (xd3_stream *stream,
fgk_stream *sec_stream,
const uint8_t **input_pos,
const uint8_t *const input_max,
uint8_t **output_pos,
const uint8_t *const output_max)
{
bit_state bstate;
uint8_t *output = *output_pos;
const uint8_t *input = *input_pos;
for (;;)
{
if (input == input_max)
{
stream->msg = "secondary decoder end of input";
return XD3_INTERNAL;
}
bstate.cur_byte = *input++;
for (bstate.cur_mask = 1; bstate.cur_mask != 0x100; bstate.cur_mask <<= 1)
{
int done = fgk_decode_bit (sec_stream, (bstate.cur_byte & bstate.cur_mask) ? 1U : 0U);
if (! done) { continue; }
*output++ = fgk_decode_data (sec_stream);
if (output == output_max)
{
/* During regression testing: */
IF_REGRESSION ({
int ret;
bstate.cur_mask <<= 1;
if ((ret = xd3_test_clean_bits (stream, & bstate))) { return ret; }
});
(*output_pos) = output;
(*input_pos) = input;
return 0;
}
}
}
}
#endif /* _XDELTA3_FGK_ */

163
deps/xdelta3/xdelta3-hash.h vendored Normal file
View file

@ -0,0 +1,163 @@
/* 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_HASH_H_
#define _XDELTA3_HASH_H_
/* To include RetroArch's INLINE macro */
#include "retro_inline.h"
#include "xdelta3-internal.h"
#if XD3_DEBUG
#define SMALL_HASH_DEBUG1(s,inp) \
uint32_t debug_state; \
uint32_t debug_hval = xd3_checksum_hash (& (s)->small_hash, \
xd3_scksum (&debug_state, (inp), (s)->smatcher.small_look))
#define SMALL_HASH_DEBUG2(s,inp) \
XD3_ASSERT (debug_hval == xd3_checksum_hash (& (s)->small_hash, \
xd3_scksum (&debug_state, (inp), (s)->smatcher.small_look)))
#else
#define SMALL_HASH_DEBUG1(s,inp)
#define SMALL_HASH_DEBUG2(s,inp)
#endif /* XD3_DEBUG */
#if UNALIGNED_OK
#define UNALIGNED_READ32(dest,src) (*(dest)) = (*(uint32_t*)(src))
#else
#define UNALIGNED_READ32(dest,src) memcpy((dest), (src), 4);
#endif
/* These are good hash multipliers for 32-bit and 64-bit LCGs: see
* "linear congruential generators of different sizes and good lattice
* structure" */
#define xd3_hash_multiplier32 1597334677U
#define xd3_hash_multiplier64 1181783497276652981ULL
/* TODO: small cksum is hard-coded for 4 bytes (i.e., "look" is unused) */
static INLINE uint32_t
xd3_scksum (uint32_t *state,
const uint8_t *base,
const usize_t look)
{
UNALIGNED_READ32(state, base);
return (*state) * xd3_hash_multiplier32;
}
static INLINE uint32_t
xd3_small_cksum_update (uint32_t *state,
const uint8_t *base,
usize_t look)
{
UNALIGNED_READ32(state, base+1);
return (*state) * xd3_hash_multiplier32;
}
#if XD3_ENCODER
INLINE usize_t
xd3_checksum_hash (const xd3_hash_cfg *cfg, const usize_t cksum)
{
return (cksum >> cfg->shift) ^ (cksum & cfg->mask);
}
#if SIZEOF_USIZE_T == 4
INLINE uint32_t
xd3_large32_cksum (xd3_hash_cfg *cfg, const uint8_t *base, const usize_t look)
{
uint32_t h = 0;
for (usize_t i = 0; i < look; i++) {
h += base[i] * cfg->powers[i];
}
return h;
}
INLINE uint32_t
xd3_large32_cksum_update (xd3_hash_cfg *cfg, const uint32_t cksum,
const uint8_t *base, const usize_t look)
{
return xd3_hash_multiplier32 * cksum - cfg->multiplier * base[0] + base[look];
}
#endif
#if SIZEOF_USIZE_T == 8
INLINE uint64_t
xd3_large64_cksum (xd3_hash_cfg *cfg, const uint8_t *base, const usize_t look)
{
uint64_t h = 0;
usize_t i;
for (i = 0; i < look; i++) {
h += base[i] * cfg->powers[i];
}
return h;
}
INLINE uint64_t
xd3_large64_cksum_update (xd3_hash_cfg *cfg, const uint64_t cksum,
const uint8_t *base, const usize_t look)
{
return xd3_hash_multiplier64 * cksum - cfg->multiplier * base[0] + base[look];
}
#endif
static usize_t
xd3_size_hashtable_bits (usize_t slots)
{
usize_t bits = (SIZEOF_USIZE_T * 8) - 1;
usize_t i;
for (i = 3; i <= bits; i += 1)
{
if (slots < (1U << i))
{
/* Note: this is the compaction=1 setting measured in
* checksum_test */
bits = i - 1;
break;
}
}
return bits;
}
int
xd3_size_hashtable (xd3_stream *stream,
usize_t slots,
usize_t look,
xd3_hash_cfg *cfg)
{
usize_t bits = xd3_size_hashtable_bits (slots);
int i;
cfg->size = (1U << bits);
cfg->mask = (cfg->size - 1);
cfg->shift = (SIZEOF_USIZE_T * 8) - bits;
cfg->look = look;
if ((cfg->powers =
(usize_t*) xd3_alloc0 (stream, look, sizeof (usize_t))) == NULL)
{
return ENOMEM;
}
cfg->powers[look-1] = 1;
for (i = look-2; i >= 0; i--)
{
cfg->powers[i] = cfg->powers[i+1] * xd3_hash_multiplier;
}
cfg->multiplier = cfg->powers[0] * xd3_hash_multiplier;
return 0;
}
#endif /* XD3_ENCODER */
#endif /* _XDELTA3_HASH_H_ */

387
deps/xdelta3/xdelta3-internal.h vendored Normal file
View file

@ -0,0 +1,387 @@
/* 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_INTERNAL_H__
#define XDELTA3_INTERNAL_H__
/* To include RetroArch's INLINE macro */
#include "retro_inline.h"
#include "xdelta3.h"
typedef struct _main_file main_file;
typedef struct _main_extcomp main_extcomp;
void main_buffree (void *ptr);
void* main_bufalloc (size_t size);
void main_file_init (main_file *xfile);
int main_file_close (main_file *xfile);
void main_file_cleanup (main_file *xfile);
int main_file_isopen (main_file *xfile);
int main_file_open (main_file *xfile, const char* name, int mode);
int main_file_exists (main_file *xfile);
int main_file_stat (main_file *xfile, xoff_t *size);
int xd3_whole_append_window (xd3_stream *stream);
int xd3_main_cmdline (int argc, char **argv);
int main_file_read (main_file *ifile,
uint8_t *buf,
size_t size,
size_t *nread,
const char *msg);
int main_file_write (main_file *ofile, uint8_t *buf,
usize_t size, const char *msg);
void* main_malloc (size_t size);
void main_free (void *ptr);
int test_compare_files (const char* f0, const char* f1);
usize_t xd3_bytes_on_srcblk (xd3_source *src, xoff_t blkno);
xoff_t xd3_source_eof(const xd3_source *src);
uint32_t xd3_large_cksum_update (uint32_t cksum,
const uint8_t *base,
usize_t look);
int xd3_emit_byte (xd3_stream *stream,
xd3_output **outputp,
uint8_t code);
int xd3_emit_bytes (xd3_stream *stream,
xd3_output **outputp,
const uint8_t *base,
usize_t size);
xd3_output* xd3_alloc_output (xd3_stream *stream,
xd3_output *old_output);
int xd3_encode_init_full (xd3_stream *stream);
usize_t xd3_pow2_roundup (usize_t x);
long get_millisecs_now (void);
int xd3_process_stream (int is_encode,
xd3_stream *stream,
int (*func) (xd3_stream *),
int close_stream,
const uint8_t *input,
usize_t input_size,
uint8_t *output,
usize_t *output_size,
usize_t output_size_max);
#if PYTHON_MODULE || SWIG_MODULE || NOT_MAIN
int xd3_main_cmdline (int argc, char **argv);
#endif
#if REGRESSION_TEST
int xd3_selftest (void);
#endif
/* main_file->mode values */
typedef enum
{
XO_READ = 0,
XO_WRITE = 1
} main_file_modes;
#ifndef XD3_POSIX
#define XD3_POSIX 0
#endif
#ifndef XD3_STDIO
#define XD3_STDIO 0
#endif
#ifndef XD3_WIN32
#define XD3_WIN32 0
#endif
#ifndef NOT_MAIN
#define NOT_MAIN 0
#endif
/* If none are set, default to posix. */
#if (XD3_POSIX + XD3_STDIO + XD3_WIN32) == 0
#undef XD3_POSIX
#define XD3_POSIX 1
#endif
struct _main_file
{
#if XD3_WIN32
HANDLE file;
#elif XD3_STDIO
FILE *file;
#elif XD3_POSIX
int file;
#endif
int mode; /* XO_READ and XO_WRITE */
const char *filename; /* File name or /dev/stdin,
* /dev/stdout, /dev/stderr. */
char *filename_copy; /* File name or /dev/stdin,
* /dev/stdout, /dev/stderr. */
const char *realname; /* File name or /dev/stdin,
* /dev/stdout, /dev/stderr. */
const main_extcomp *compressor; /* External compression struct. */
int flags; /* RD_FIRST, RD_NONEXTERNAL, ... */
xoff_t nread; /* for input position */
xoff_t nwrite; /* for output position */
uint8_t *snprintf_buf; /* internal snprintf() use */
int size_known; /* Set by main_set_souze */
xoff_t source_position; /* for avoiding seek in getblk_func */
int seek_failed; /* after seek fails once, try FIFO */
};
#ifndef UINT32_MAX
#define UINT32_MAX 4294967295U
#endif
#ifndef UINT64_MAX
#define UINT64_MAX 18446744073709551615ULL
#endif
#define UINT32_OFLOW_MASK 0xfe000000U
#define UINT64_OFLOW_MASK 0xfe00000000000000ULL
/*********************************************************************
Integer encoder/decoder functions
**********************************************************************/
/* Consume N bytes of input, only used by the decoder. */
#define DECODE_INPUT(n) \
do { \
stream->total_in += (xoff_t) (n); \
stream->avail_in -= (n); \
stream->next_in += (n); \
} while (0)
#define DECODE_INTEGER_TYPE(PART,OFLOW) \
while (stream->avail_in != 0) \
{ \
usize_t next = stream->next_in[0]; \
\
DECODE_INPUT(1); \
\
if (PART & OFLOW) \
{ \
stream->msg = "overflow in decode_integer"; \
return XD3_INVALID_INPUT; \
} \
\
PART = (PART << 7) | (next & 127); \
\
if ((next & 128) == 0) \
{ \
(*val) = PART; \
PART = 0; \
return 0; \
} \
} \
\
stream->msg = "further input required"; \
return XD3_INPUT
#define READ_INTEGER_TYPE(TYPE, OFLOW) \
TYPE val = 0; \
const uint8_t *inp = (*inpp); \
usize_t next; \
\
do \
{ \
if (inp == maxp) \
{ \
stream->msg = "end-of-input in read_integer"; \
return XD3_INVALID_INPUT; \
} \
\
if (val & OFLOW) \
{ \
stream->msg = "overflow in read_intger"; \
return XD3_INVALID_INPUT; \
} \
\
next = (*inp++); \
val = (val << 7) | (next & 127); \
} \
while (next & 128); \
\
(*valp) = val; \
(*inpp) = inp; \
\
return 0
#define EMIT_INTEGER_TYPE() \
/* max 64-bit value in base-7 encoding is 9.1 bytes */ \
uint8_t buf[10]; \
usize_t bufi = 10; \
\
/* This loop performs division and turns on all MSBs. */ \
do \
{ \
buf[--bufi] = (num & 127) | 128; \
num >>= 7U; \
} \
while (num != 0); \
\
/* Turn off MSB of the last byte. */ \
buf[9] &= 127; \
\
return xd3_emit_bytes (stream, output, buf + bufi, 10 - bufi)
#define IF_SIZEOF32(x) if (num < (1U << (7 * (x)))) return (x);
#define IF_SIZEOF64(x) if (num < (1ULL << (7 * (x)))) return (x);
#if USE_UINT32
static INLINE uint32_t
xd3_sizeof_uint32_t (uint32_t num)
{
IF_SIZEOF32(1);
IF_SIZEOF32(2);
IF_SIZEOF32(3);
IF_SIZEOF32(4);
return 5;
}
static INLINE int
xd3_decode_uint32_t (xd3_stream *stream, uint32_t *val)
{ DECODE_INTEGER_TYPE (stream->dec_32part, UINT32_OFLOW_MASK); }
static INLINE int
xd3_read_uint32_t (xd3_stream *stream, const uint8_t **inpp,
const uint8_t *maxp, uint32_t *valp)
{ READ_INTEGER_TYPE (uint32_t, UINT32_OFLOW_MASK); }
#if XD3_ENCODER
static INLINE int
xd3_emit_uint32_t (xd3_stream *stream, xd3_output **output, uint32_t num)
{ EMIT_INTEGER_TYPE (); }
#endif /* XD3_ENCODER */
#endif /* USE_UINT32 */
#if USE_UINT64
static INLINE uint32_t
xd3_sizeof_uint64_t (uint64_t num)
{
IF_SIZEOF64(1);
IF_SIZEOF64(2);
IF_SIZEOF64(3);
IF_SIZEOF64(4);
IF_SIZEOF64(5);
IF_SIZEOF64(6);
IF_SIZEOF64(7);
IF_SIZEOF64(8);
IF_SIZEOF64(9);
return 10;
}
static INLINE int
xd3_decode_uint64_t (xd3_stream *stream, uint64_t *val)
{ DECODE_INTEGER_TYPE (stream->dec_64part, UINT64_OFLOW_MASK); }
static INLINE int
xd3_read_uint64_t (xd3_stream *stream, const uint8_t **inpp,
const uint8_t *maxp, uint64_t *valp)
{ READ_INTEGER_TYPE (uint64_t, UINT64_OFLOW_MASK); }
#if XD3_ENCODER
static INLINE int
xd3_emit_uint64_t (xd3_stream *stream, xd3_output **output, uint64_t num)
{ EMIT_INTEGER_TYPE (); }
#endif /* XD3_ENCODER */
#endif /* USE_UINT64 */
#if SIZEOF_USIZE_T == 4
#define USIZE_T_MAX UINT32_MAX
#define USIZE_T_MAXBLKSZ 0x80000000U
#define XD3_MAXSRCWINSZ (1ULL << 31)
#define xd3_large_cksum xd3_large32_cksum
#define xd3_large_cksum_update xd3_large32_cksum_update
#define xd3_hash_multiplier xd3_hash_multiplier32
static INLINE uint32_t xd3_sizeof_size (usize_t num)
{ return xd3_sizeof_uint32_t (num); }
static INLINE int xd3_decode_size (xd3_stream *stream, usize_t *valp)
{ return xd3_decode_uint32_t (stream, (uint32_t*) valp); }
static INLINE int xd3_read_size (xd3_stream *stream, const uint8_t **inpp,
const uint8_t *maxp, usize_t *valp)
{ return xd3_read_uint32_t (stream, inpp, maxp, (uint32_t*) valp); }
#if XD3_ENCODER
static INLINE int xd3_emit_size (xd3_stream *stream, xd3_output **output, usize_t num)
{ return xd3_emit_uint32_t (stream, output, num); }
#endif
#elif SIZEOF_USIZE_T == 8
#define USIZE_T_MAX UINT64_MAX
#define USIZE_T_MAXBLKSZ 0x8000000000000000ULL
#define XD3_MAXSRCWINSZ (1ULL << 61)
#define xd3_large_cksum xd3_large64_cksum
#define xd3_large_cksum_update xd3_large64_cksum_update
#define xd3_hash_multiplier xd3_hash_multiplier64
static INLINE uint32_t xd3_sizeof_size (usize_t num)
{ return xd3_sizeof_uint64_t (num); }
static INLINE int xd3_decode_size (xd3_stream *stream, usize_t *valp)
{ return xd3_decode_uint64_t (stream, (uint64_t*) valp); }
static INLINE int xd3_read_size (xd3_stream *stream, const uint8_t **inpp,
const uint8_t *maxp, usize_t *valp)
{ return xd3_read_uint64_t (stream, inpp, maxp, (uint64_t*) valp); }
#if XD3_ENCODER
static INLINE int xd3_emit_size (xd3_stream *stream, xd3_output **output, usize_t num)
{ return xd3_emit_uint64_t (stream, output, num); }
#endif
#endif /* SIZEOF_USIZE_T */
#if SIZEOF_XOFF_T == 4
#define XOFF_T_MAX UINT32_MAX
static INLINE int xd3_decode_offset (xd3_stream *stream, xoff_t *valp)
{ return xd3_decode_uint32_t (stream, (uint32_t*) valp); }
#if XD3_ENCODER
static INLINE int xd3_emit_offset (xd3_stream *stream, xd3_output **output, xoff_t num)
{ return xd3_emit_uint32_t (stream, output, num); }
#endif
#elif SIZEOF_XOFF_T == 8
#define XOFF_T_MAX UINT64_MAX
static INLINE int xd3_decode_offset (xd3_stream *stream, xoff_t *valp)
{ return xd3_decode_uint64_t (stream, (uint64_t*) valp); }
#if XD3_ENCODER
static INLINE int xd3_emit_offset (xd3_stream *stream, xd3_output **output, xoff_t num)
{ return xd3_emit_uint64_t (stream, output, num); }
#endif
#endif
#define USIZE_T_OVERFLOW(a,b) ((USIZE_T_MAX - (usize_t) (a)) < (usize_t) (b))
#define XOFF_T_OVERFLOW(a,b) ((XOFF_T_MAX - (xoff_t) (a)) < (xoff_t) (b))
int xd3_size_hashtable (xd3_stream *stream,
usize_t slots,
usize_t look,
xd3_hash_cfg *cfg);
usize_t xd3_checksum_hash (const xd3_hash_cfg *cfg, const usize_t cksum);
#if USE_UINT32
uint32_t xd3_large32_cksum (xd3_hash_cfg *cfg, const uint8_t *base, const usize_t look);
uint32_t xd3_large32_cksum_update (xd3_hash_cfg *cfg, const uint32_t cksum,
const uint8_t *base, const usize_t look);
#endif /* USE_UINT32 */
#if USE_UINT64
uint64_t xd3_large64_cksum (xd3_hash_cfg *cfg, const uint8_t *base, const usize_t look);
uint64_t xd3_large64_cksum_update (xd3_hash_cfg *cfg, const uint64_t cksum,
const uint8_t *base, const usize_t look);
#endif /* USE_UINT64 */
#define MAX_LRU_SIZE 32U
#define XD3_MINSRCWINSZ (XD3_ALLOCSIZE * MAX_LRU_SIZE)
#endif /* XDELTA3_INTERNAL_H__ */

130
deps/xdelta3/xdelta3-list.h vendored Normal file
View file

@ -0,0 +1,130 @@
/* 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_LIST__
#define __XDELTA3_LIST__
/* To include RetroArch's INLINE macro */
#include "retro_inline.h"
#define XD3_MAKELIST(LTYPE,ETYPE,LNAME) \
\
static INLINE ETYPE* \
LTYPE ## _entry (LTYPE* l) \
{ \
return (ETYPE*) ((char*) l - (ptrdiff_t) &((ETYPE*) 0)->LNAME); \
} \
\
static INLINE void \
LTYPE ## _init (LTYPE *l) \
{ \
l->next = l; \
l->prev = l; \
} \
\
static INLINE void \
LTYPE ## _add (LTYPE *prev, LTYPE *next, LTYPE *ins) \
{ \
next->prev = ins; \
prev->next = ins; \
ins->next = next; \
ins->prev = prev; \
} \
\
static INLINE void \
LTYPE ## _push_back (LTYPE *l, ETYPE *i) \
{ \
LTYPE ## _add (l->prev, l, & i->LNAME); \
} \
\
static INLINE void \
LTYPE ## _del (LTYPE *next, \
LTYPE *prev) \
{ \
next->prev = prev; \
prev->next = next; \
} \
\
static INLINE ETYPE* \
LTYPE ## _remove (ETYPE *f) \
{ \
LTYPE *i = f->LNAME.next; \
LTYPE ## _del (f->LNAME.next, f->LNAME.prev); \
return LTYPE ## _entry (i); \
} \
\
static INLINE ETYPE* \
LTYPE ## _pop_back (LTYPE *l) \
{ \
LTYPE *i = l->prev; \
LTYPE ## _del (i->next, i->prev); \
return LTYPE ## _entry (i); \
} \
\
static INLINE ETYPE* \
LTYPE ## _pop_front (LTYPE *l) \
{ \
LTYPE *i = l->next; \
LTYPE ## _del (i->next, i->prev); \
return LTYPE ## _entry (i); \
} \
\
static INLINE int \
LTYPE ## _empty (LTYPE *l) \
{ \
return l == l->next; \
} \
\
static INLINE ETYPE* \
LTYPE ## _front (LTYPE *f) \
{ \
return LTYPE ## _entry (f->next); \
} \
\
static INLINE ETYPE* \
LTYPE ## _back (LTYPE *f) \
{ \
return LTYPE ## _entry (f->prev); \
} \
\
static INLINE int \
LTYPE ## _end (LTYPE *f, ETYPE *i) \
{ \
return f == & i->LNAME; \
} \
\
static INLINE ETYPE* \
LTYPE ## _next (ETYPE *f) \
{ \
return LTYPE ## _entry (f->LNAME.next); \
} \
\
static INLINE usize_t \
LTYPE ## _length (LTYPE *l) \
{ \
LTYPE *p; \
usize_t c = 0; \
\
for (p = l->next; p != l; p = p->next) \
{ \
c += 1; \
} \
\
return c; \
} \
\
typedef int unused_ ## LTYPE
#endif

195
deps/xdelta3/xdelta3-lzma.h vendored Normal file
View file

@ -0,0 +1,195 @@
/* 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.
*/
/* Note: The use of the _easy_ decoder means we're not calling the
* xd3_stream malloc hooks. TODO(jmacd) Fix if anyone cares. */
#ifndef _XDELTA3_LZMA_H_
#define _XDELTA3_LZMA_H_
#include <lzma.h>
typedef struct _xd3_lzma_stream xd3_lzma_stream;
struct _xd3_lzma_stream {
lzma_stream lzma;
lzma_options_lzma options;
lzma_filter filters[2];
};
static xd3_sec_stream*
xd3_lzma_alloc (xd3_stream *stream)
{
return (xd3_sec_stream*) xd3_alloc (stream, sizeof (xd3_lzma_stream), 1);
}
static void
xd3_lzma_destroy (xd3_stream *stream, xd3_sec_stream *sec_stream)
{
xd3_lzma_stream *ls = (xd3_lzma_stream*) sec_stream;
lzma_end (&ls->lzma);
xd3_free (stream, ls);
}
static int
xd3_lzma_init (xd3_stream *stream, xd3_lzma_stream *sec, int is_encode)
{
int ret;
memset (&sec->lzma, 0, sizeof(sec->lzma));
if (is_encode)
{
uint32_t preset =
(stream->flags & XD3_COMPLEVEL_MASK) >> XD3_COMPLEVEL_SHIFT;
if (lzma_lzma_preset(&sec->options, preset))
{
stream->msg = "invalid lzma preset";
return XD3_INVALID;
}
sec->filters[0].id = LZMA_FILTER_LZMA2;
sec->filters[0].options = &sec->options;
sec->filters[1].id = LZMA_VLI_UNKNOWN;
ret = lzma_stream_encoder (&sec->lzma, &sec->filters[0], LZMA_CHECK_NONE);
}
else
{
ret = lzma_stream_decoder (&sec->lzma, UINT64_MAX, LZMA_TELL_NO_CHECK);
}
if (ret != LZMA_OK)
{
stream->msg = "lzma stream init failed";
return XD3_INTERNAL;
}
return 0;
}
static int xd3_decode_lzma (xd3_stream *stream, xd3_lzma_stream *sec,
const uint8_t **input_pos,
const uint8_t *const input_end,
uint8_t **output_pos,
const uint8_t *const output_end)
{
uint8_t *output = *output_pos;
const uint8_t *input = *input_pos;
size_t avail_in = input_end - input;
size_t avail_out = output_end - output;
sec->lzma.avail_in = avail_in;
sec->lzma.next_in = input;
sec->lzma.avail_out = avail_out;
sec->lzma.next_out = output;
while (1)
{
int lret = lzma_code (&sec->lzma, LZMA_RUN);
switch (lret)
{
case LZMA_NO_CHECK:
case LZMA_OK:
if (sec->lzma.avail_out == 0)
{
(*output_pos) = sec->lzma.next_out;
(*input_pos) = sec->lzma.next_in;
return 0;
}
break;
default:
stream->msg = "lzma decoding error";
return XD3_INTERNAL;
}
}
}
#if XD3_ENCODER
static int xd3_encode_lzma (xd3_stream *stream,
xd3_lzma_stream *sec,
xd3_output *input,
xd3_output *output,
xd3_sec_cfg *cfg)
{
lzma_action action = LZMA_RUN;
cfg->inefficient = 1; /* Can't skip windows */
sec->lzma.next_in = NULL;
sec->lzma.avail_in = 0;
sec->lzma.next_out = (output->base + output->next);
sec->lzma.avail_out = (output->avail - output->next);
while (1)
{
int lret;
size_t nwrite;
if (sec->lzma.avail_in == 0 && input != NULL)
{
sec->lzma.avail_in = input->next;
sec->lzma.next_in = input->base;
if ((input = input->next_page) == NULL)
{
action = LZMA_SYNC_FLUSH;
}
}
lret = lzma_code (&sec->lzma, action);
nwrite = (output->avail - output->next) - sec->lzma.avail_out;
if (nwrite != 0)
{
output->next += nwrite;
if (output->next == output->avail)
{
if ((output = xd3_alloc_output (stream, output)) == NULL)
{
return ENOMEM;
}
sec->lzma.next_out = output->base;
sec->lzma.avail_out = output->avail;
}
}
switch (lret)
{
case LZMA_OK:
break;
case LZMA_STREAM_END:
return 0;
default:
stream->msg = "lzma encoding error";
return XD3_INTERNAL;
}
}
return 0;
}
#endif /* XD3_ENCODER */
#endif /* _XDELTA3_LZMA_H_ */

321
deps/xdelta3/xdelta3-second.h vendored Normal file
View file

@ -0,0 +1,321 @@
/* 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_ */

4813
deps/xdelta3/xdelta3.c vendored Normal file

File diff suppressed because it is too large Load diff

1502
deps/xdelta3/xdelta3.h vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -226,6 +226,12 @@ Attempts to apply a IPS patch to the current content image. No files are altered
If this flag is not specified, RetroArch will look for a .ips file with same basename as content specified.
Note that RetroArch cannot perform any error checking if patching was successful due to how IPS works.
.TP
\fB--xdelta PATCH\fR
Attempts to apply an Xdelta patch to the current content image. No files are altered.
If this flag is not specified, RetroArch will look for a .xdelta file with same basename as content specified.
Only available if RetroArch was built with Xdelta support.
.TP
\fB--no-patch\fR
Disables all kinds of content patching.

View file

@ -63,6 +63,7 @@ RETRO_BEGIN_DECLS
#define FILE_PATH_UPS_EXTENSION ".ups"
#define FILE_PATH_IPS_EXTENSION ".ips"
#define FILE_PATH_BPS_EXTENSION ".bps"
#define FILE_PATH_XDELTA_EXTENSION ".xdelta"
#define FILE_PATH_RDB_EXTENSION ".rdb"
#define FILE_PATH_RDB_EXTENSION_NO_DOT "rdb"
#define FILE_PATH_ZIP_EXTENSION ".zip"

View file

@ -6,6 +6,7 @@ HAVE_CORE_INFO_CACHE=yes # Core info cache support
HAVE_BLUETOOTH=no # Bluetooth support
HAVE_NVDA=yes # NVDA support
HAVE_PATCH=yes # Softpatching support (BPS/IPS/UPS)
HAVE_XDELTA=yes # Xdelta softpatching support (requires softpatching)
HAVE_SAPI=no # SAPI support
HAVE_VIDEO_FILTER=yes # Video filter support
HAVE_WINRAWINPUT=yes # Windows Raw Input support (XP and higher)

View file

@ -270,6 +270,7 @@ enum
RA_OPT_APPENDCONFIG,
RA_OPT_BPS,
RA_OPT_IPS,
RA_OPT_XDELTA,
RA_OPT_NO_PATCH,
RA_OPT_RECORDCONFIG,
RA_OPT_SUBSYSTEM,
@ -4607,6 +4608,11 @@ void retroarch_override_setting_set(
p_rarch->flags |= RARCH_FLAGS_HAS_SET_IPS_PREF;
#endif
break;
case RARCH_OVERRIDE_SETTING_XDELTA_PREF:
#if defined(HAVE_PATCH) && defined(HAVE_XDELTA)
p_rarch->flags |= RARCH_FLAGS_HAS_SET_XDELTA_PREF;
#endif
break;
case RARCH_OVERRIDE_SETTING_LOG_TO_FILE:
p_rarch->flags |= RARCH_FLAGS_HAS_SET_LOG_TO_FILE;
break;
@ -4682,6 +4688,11 @@ void retroarch_override_setting_unset(
case RARCH_OVERRIDE_SETTING_IPS_PREF:
#ifdef HAVE_PATCH
p_rarch->flags &= ~RARCH_FLAGS_HAS_SET_IPS_PREF;
#endif
break;
case RARCH_OVERRIDE_SETTING_XDELTA_PREF:
#if defined(HAVE_PATCH) && defined(HAVE_XDELTA)
p_rarch->flags &= ~RARCH_FLAGS_HAS_SET_XDELTA_PREF;
#endif
break;
case RARCH_OVERRIDE_SETTING_LOG_TO_FILE:
@ -4734,7 +4745,8 @@ static void global_free(struct rarch_state *p_rarch)
p_rarch->flags &= ~(
RARCH_FLAGS_BPS_PREF
| RARCH_FLAGS_IPS_PREF
| RARCH_FLAGS_UPS_PREF);
| RARCH_FLAGS_UPS_PREF
| RARCH_FLAGS_XDELTA_PREF);
runloop_st->flags &= ~RUNLOOP_FLAG_PATCH_BLOCKED;
#endif
@ -4759,6 +4771,7 @@ static void global_free(struct rarch_state *p_rarch)
*runloop_st->name.ups = '\0';
*runloop_st->name.bps = '\0';
*runloop_st->name.ips = '\0';
*runloop_st->name.xdelta = '\0';
*runloop_st->name.savefile = '\0';
*runloop_st->name.savestate = '\0';
*runloop_st->name.replay = '\0';
@ -5445,10 +5458,18 @@ static void retroarch_print_help(const char *arg0)
"Specifies path for BPS patch that will be applied to content.\n"
" --ips=FILE "
"Specifies path for IPS patch that will be applied to content.\n"
, sizeof(buf));
#ifdef HAVE_XDELTA
strlcat(buf,
" --xdelta=FILE "
"Specifies path for Xdelta patch that will be applied to content.\n"
, sizeof(buf));
#endif /* HAVE_XDELTA */
strlcat(buf,
" --no-patch "
"Disables all forms of content patching.\n"
, sizeof(buf));
#endif
#endif /* HAVE_PATCH */
#ifdef HAVE_SCREENSHOTS
strlcat(buf,
@ -5713,8 +5734,11 @@ static bool retroarch_parse_input_and_config(
{ "ups", 1, NULL, 'U' },
{ "bps", 1, NULL, RA_OPT_BPS },
{ "ips", 1, NULL, RA_OPT_IPS },
#ifdef HAVE_XDELTA
{ "xdelta", 1, NULL, RA_OPT_XDELTA },
#endif /* HAVE_XDELTA */
{ "no-patch", 0, NULL, RA_OPT_NO_PATCH },
#endif
#endif /* HAVE_PATCH */
{ "detach", 0, NULL, 'D' },
{ "features", 0, NULL, RA_OPT_FEATURES },
{ "subsystem", 1, NULL, RA_OPT_SUBSYSTEM },
@ -5788,10 +5812,11 @@ static bool retroarch_parse_input_and_config(
p_rarch->flags &= ~RARCH_FLAGS_HAS_SET_USERNAME;
#ifdef HAVE_PATCH
p_rarch->flags &= ~( RARCH_FLAGS_UPS_PREF | RARCH_FLAGS_IPS_PREF
| RARCH_FLAGS_BPS_PREF);
| RARCH_FLAGS_BPS_PREF | RARCH_FLAGS_XDELTA_PREF);
*runloop_st->name.ups = '\0';
*runloop_st->name.bps = '\0';
*runloop_st->name.ips = '\0';
*runloop_st->name.xdelta = '\0';
#endif
#ifdef HAVE_CONFIGFILE
runloop_st->flags &= ~RUNLOOP_FLAG_OVERRIDES_ACTIVE;
@ -6195,7 +6220,14 @@ static bool retroarch_parse_input_and_config(
retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_IPS_PREF, NULL);
#endif
break;
case RA_OPT_XDELTA:
#if defined(HAVE_PATCH) && defined(HAVE_XDELTA)
strlcpy(runloop_st->name.xdelta, optarg,
sizeof(runloop_st->name.xdelta));
p_rarch->flags |= RARCH_FLAGS_XDELTA_PREF;
retroarch_override_setting_set(RARCH_OVERRIDE_SETTING_XDELTA_PREF, NULL);
#endif
break;
case RA_OPT_NO_PATCH:
#ifdef HAVE_PATCH
runloop_st->flags |= RUNLOOP_FLAG_PATCH_BLOCKED;
@ -6842,7 +6874,12 @@ bool retroarch_ctl(enum rarch_ctl_state state, void *data)
case RARCH_CTL_UNSET_IPS_PREF:
p_rarch->flags &= ~RARCH_FLAGS_IPS_PREF;
break;
#endif
#ifdef HAVE_XDELTA
case RARCH_CTL_UNSET_XDELTA_PREF:
p_rarch->flags &= ~RARCH_FLAGS_XDELTA_PREF;
break;
#endif /* HAVE_XDELTA */
#endif /* HAVE_PATCH */
case RARCH_CTL_IS_DUMMY_CORE:
return runloop_st->current_core_type == CORE_TYPE_DUMMY;
case RARCH_CTL_IS_CORE_LOADED:
@ -7100,7 +7137,11 @@ bool retroarch_override_setting_is_set(
return ((p_rarch->flags & RARCH_FLAGS_HAS_SET_BPS_PREF) > 0);
case RARCH_OVERRIDE_SETTING_IPS_PREF:
return ((p_rarch->flags & RARCH_FLAGS_HAS_SET_IPS_PREF) > 0);
#endif
#ifdef HAVE_XDELTA
case RARCH_OVERRIDE_SETTING_XDELTA_PREF:
return ((p_rarch->flags & RARCH_FLAGS_HAS_SET_XDELTA_PREF) > 0);
#endif /* HAVE_XDELTA */
#endif /* HAVE_PATCH */
case RARCH_OVERRIDE_SETTING_LOG_TO_FILE:
return ((p_rarch->flags & RARCH_FLAGS_HAS_SET_LOG_TO_FILE) > 0);
case RARCH_OVERRIDE_SETTING_DATABASE_SCAN:

View file

@ -59,7 +59,7 @@
#define RETRO_ENVIRONMENT_POLL_TYPE_OVERRIDE (4 | RETRO_ENVIRONMENT_RETROARCH_START_BLOCK)
/* unsigned * --
* Tells the frontend to override the poll type behavior.
* Tells the frontend to override the poll type behavior.
* Allows the frontend to influence the polling behavior of the
* frontend.
*
@ -104,7 +104,9 @@ enum rarch_state_flags
RARCH_FLAGS_BPS_PREF = (1 << 11),
RARCH_FLAGS_IPS_PREF = (1 << 12),
RARCH_FLAGS_BLOCK_CONFIG_READ = (1 << 13),
RARCH_FLAGS_CLI_DATABASE_SCAN = (1 << 14)
RARCH_FLAGS_CLI_DATABASE_SCAN = (1 << 14),
RARCH_FLAGS_HAS_SET_XDELTA_PREF = (1 << 15),
RARCH_FLAGS_XDELTA_PREF = (1 << 16)
};
bool retroarch_ctl(enum rarch_ctl_state state, void *data);
@ -182,7 +184,7 @@ const char* config_get_microphone_driver_options(void);
unsigned int retroarch_get_core_requested_rotation(void);
/*
Returns final rotation including both user chosen video rotation
Returns final rotation including both user chosen video rotation
and core requested rotation if allowed by video_allow_rotate
*/
unsigned int retroarch_get_rotation(void);

View file

@ -62,6 +62,7 @@ enum rarch_ctl_state
RARCH_CTL_UNSET_BPS_PREF,
RARCH_CTL_UNSET_UPS_PREF,
RARCH_CTL_UNSET_IPS_PREF,
RARCH_CTL_UNSET_XDELTA_PREF,
#ifdef HAVE_CONFIGFILE
/* Block config read */
@ -122,6 +123,7 @@ enum rarch_override_setting
RARCH_OVERRIDE_SETTING_UPS_PREF,
RARCH_OVERRIDE_SETTING_BPS_PREF,
RARCH_OVERRIDE_SETTING_IPS_PREF,
RARCH_OVERRIDE_SETTING_XDELTA_PREF,
RARCH_OVERRIDE_SETTING_LIBRETRO_DEVICE,
RARCH_OVERRIDE_SETTING_LOG_TO_FILE,
RARCH_OVERRIDE_SETTING_DATABASE_SCAN,

View file

@ -4874,6 +4874,16 @@ void runloop_path_fill_names(void)
".ips",
sizeof(runloop_st->name.ips) - len);
}
if (string_is_empty(runloop_st->name.xdelta))
{
size_t len = strlcpy(runloop_st->name.xdelta,
runloop_st->runtime_content_path_basename,
sizeof(runloop_st->name.xdelta));
strlcpy(runloop_st->name.xdelta + len,
".xdelta",
sizeof(runloop_st->name.xdelta) - len);
}
}

View file

@ -294,6 +294,7 @@ struct runloop
char ups[8192];
char bps[8192];
char ips[8192];
char xdelta[8192];
char label[8192];
} name;

View file

@ -123,7 +123,8 @@ enum content_information_flags
CONTENT_INFO_FLAG_IS_UPS_PREF = (1 << 5),
CONTENT_INFO_FLAG_PATCH_IS_BLOCKED = (1 << 6),
CONTENT_INFO_FLAG_BIOS_IS_MISSING = (1 << 7),
CONTENT_INFO_FLAG_CHECK_FW_BEFORE_LOADING = (1 << 8)
CONTENT_INFO_FLAG_CHECK_FW_BEFORE_LOADING = (1 << 8),
CONTENT_INFO_FLAG_IS_XDELTA_PREF = (1 << 9)
};
struct content_information_ctx
@ -131,6 +132,7 @@ struct content_information_ctx
char *name_ips;
char *name_bps;
char *name_ups;
char *name_xdelta;
char *valid_extensions;
char *directory_cache;
@ -437,7 +439,7 @@ static content_file_list_t *content_file_list_init(size_t size)
calloc(size, sizeof(struct retro_game_info))))
{
/* Create retro_game_info_ext object */
if ((file_list->game_info_ext =
if ((file_list->game_info_ext =
(struct retro_game_info_ext *)
calloc(size, sizeof(struct retro_game_info_ext))))
return file_list;
@ -726,9 +728,11 @@ static bool content_file_load_into_memory(
content_ctx->flags & CONTENT_INFO_FLAG_IS_IPS_PREF,
content_ctx->flags & CONTENT_INFO_FLAG_IS_BPS_PREF,
content_ctx->flags & CONTENT_INFO_FLAG_IS_UPS_PREF,
content_ctx->flags & CONTENT_INFO_FLAG_IS_XDELTA_PREF,
content_ctx->name_ips,
content_ctx->name_bps,
content_ctx->name_ups,
content_ctx->name_xdelta,
(uint8_t**)&content_data,
(void*)&content_size);
#endif
@ -1026,7 +1030,7 @@ static bool content_file_load(
* It would be better to set the ACL to allow full access for all application packages. However,
* this is substantially easier than writing out new functions to do this
* Copy ACL from localstate
* I am genuinely really proud of these work arounds
* I am genuinely really proud of these work arounds
*/
wchar_t wcontent_path[MAX_PATH];
mbstowcs(wcontent_path, content_path, MAX_PATH);
@ -1061,7 +1065,7 @@ static bool content_file_load(
"VFSCACHE\\",
sizeof(new_basedir) - _len);
basedir_attribs = GetFileAttributes(new_basedir);
if ( (basedir_attribs == INVALID_FILE_ATTRIBUTES)
if ( (basedir_attribs == INVALID_FILE_ATTRIBUTES)
|| (!(basedir_attribs & FILE_ATTRIBUTE_DIRECTORY)))
{
if (!CreateDirectoryA(new_basedir, NULL))
@ -1279,7 +1283,7 @@ CONTENT_INFO_FLAG_NEED_FULLPATH);
if (string_is_empty(content_path))
{
if ( (flags & CONTENT_ST_FLAG_CORE_DOES_NOT_NEED_CONTENT)
&& content_ctx->flags
&& content_ctx->flags
& CONTENT_INFO_FLAG_SET_SUPPORTS_NO_GAME_ENABLE)
string_list_append(content, "", attr);
}
@ -1537,7 +1541,7 @@ void menu_content_environment_get(int *argc, char *argv[],
wrap_args->content_path = path_get(RARCH_PATH_CONTENT);
if (!retroarch_override_setting_is_set(
RARCH_OVERRIDE_SETTING_LIBRETRO, NULL))
wrap_args->libretro_path = string_is_empty(path_get(RARCH_PATH_CORE))
wrap_args->libretro_path = string_is_empty(path_get(RARCH_PATH_CORE))
? NULL
: path_get(RARCH_PATH_CORE);
}
@ -1557,7 +1561,7 @@ static void task_push_to_history_list(
uint8_t flags = content_get_flags();
/* Push entry to top of history playlist */
if ( (flags & CONTENT_ST_FLAG_IS_INITED)
if ( (flags & CONTENT_ST_FLAG_IS_INITED)
|| (flags & CONTENT_ST_FLAG_CORE_DOES_NOT_NEED_CONTENT))
{
char tmp[PATH_MAX_LENGTH];
@ -1664,7 +1668,7 @@ static void task_push_to_history_list(
label = runloop_st->name.label;
if (
settings && settings->bools.history_list_enable
settings && settings->bools.history_list_enable
&& playlist_hist)
{
char subsystem_name[PATH_MAX_LENGTH];
@ -1673,7 +1677,7 @@ static void task_push_to_history_list(
subsystem_name[0] = '\0';
content_get_subsystem_friendly_name(path_get(RARCH_PATH_SUBSYSTEM), subsystem_name, sizeof(subsystem_name));
/* The push function reads our entry as const,
/* The push function reads our entry as const,
* so these casts are safe */
entry.path = (char*)tmp;
entry.label = (char*)label;
@ -1866,7 +1870,7 @@ static bool firmware_update_status(
bool set_missing_firmware = false;
core_info_t *core_info = NULL;
runloop_state_t *runloop_st = runloop_state_get_ptr();
core_info_get_current_core(&core_info);
if (!core_info)
@ -1941,6 +1945,10 @@ bool task_push_start_dummy_core(content_ctx_info_t *content_info)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_BPS_PREF;
if (rarch_flags & RARCH_FLAGS_UPS_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_UPS_PREF;
#ifdef HAVE_XDELTA
if (rarch_flags & RARCH_FLAGS_XDELTA_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_XDELTA_PREF;
#endif /* HAVE_XDELTA */
if (runloop_st->flags & RUNLOOP_FLAG_PATCH_BLOCKED)
content_ctx.flags |= CONTENT_INFO_FLAG_PATCH_IS_BLOCKED;
#endif
@ -1951,6 +1959,7 @@ bool task_push_start_dummy_core(content_ctx_info_t *content_info)
content_ctx.name_ips = NULL;
content_ctx.name_bps = NULL;
content_ctx.name_ups = NULL;
content_ctx.name_xdelta = NULL;
content_ctx.valid_extensions = NULL;
content_ctx.subsystem.data = NULL;
@ -1962,6 +1971,8 @@ bool task_push_start_dummy_core(content_ctx_info_t *content_info)
content_ctx.name_bps = strdup(runloop_st->name.bps);
if (!string_is_empty(runloop_st->name.ups))
content_ctx.name_ups = strdup(runloop_st->name.ups);
if (!string_is_empty(runloop_st->name.xdelta))
content_ctx.name_xdelta = strdup(runloop_st->name.xdelta);
if (!string_is_empty(path_dir_system))
content_ctx.directory_system = strdup(path_dir_system);
@ -1989,6 +2000,8 @@ bool task_push_start_dummy_core(content_ctx_info_t *content_info)
free(content_ctx.name_bps);
if (content_ctx.name_ups)
free(content_ctx.name_ups);
if (content_ctx.name_xdelta)
free(content_ctx.name_xdelta);
if (content_ctx.directory_system)
free(content_ctx.directory_system);
@ -2031,6 +2044,10 @@ bool task_push_load_content_from_playlist_from_menu(
content_ctx.flags |= CONTENT_INFO_FLAG_IS_BPS_PREF;
if (rarch_flags & RARCH_FLAGS_UPS_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_UPS_PREF;
#ifdef HAVE_XDELTA
if (rarch_flags & RARCH_FLAGS_XDELTA_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_XDELTA_PREF;
#endif /* HAVE_XDELTA */
if (runloop_st->flags & RUNLOOP_FLAG_PATCH_BLOCKED)
content_ctx.flags |= CONTENT_INFO_FLAG_PATCH_IS_BLOCKED;
#endif
@ -2041,6 +2058,7 @@ bool task_push_load_content_from_playlist_from_menu(
content_ctx.name_ips = NULL;
content_ctx.name_bps = NULL;
content_ctx.name_ups = NULL;
content_ctx.name_xdelta = NULL;
content_ctx.valid_extensions = NULL;
content_ctx.subsystem.data = NULL;
@ -2052,6 +2070,8 @@ bool task_push_load_content_from_playlist_from_menu(
content_ctx.name_bps = strdup(runloop_st->name.bps);
if (!string_is_empty(runloop_st->name.ups))
content_ctx.name_ups = strdup(runloop_st->name.ups);
if (!string_is_empty(runloop_st->name.xdelta))
content_ctx.name_xdelta = strdup(runloop_st->name.xdelta);
if (label)
strlcpy(runloop_st->name.label, label, sizeof(runloop_st->name.label));
else
@ -2138,6 +2158,8 @@ end:
free(content_ctx.name_bps);
if (content_ctx.name_ups)
free(content_ctx.name_ups);
if (content_ctx.name_xdelta)
free(content_ctx.name_xdelta);
if (content_ctx.directory_system)
free(content_ctx.directory_system);
@ -2171,6 +2193,10 @@ bool task_push_start_current_core(content_ctx_info_t *content_info)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_BPS_PREF;
if (rarch_flags & RARCH_FLAGS_UPS_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_UPS_PREF;
#ifdef HAVE_XDELTA
if (rarch_flags & RARCH_FLAGS_XDELTA_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_XDELTA_PREF;
#endif
if (runloop_st->flags & RUNLOOP_FLAG_PATCH_BLOCKED)
content_ctx.flags |= CONTENT_INFO_FLAG_PATCH_IS_BLOCKED;
}
@ -2182,6 +2208,7 @@ bool task_push_start_current_core(content_ctx_info_t *content_info)
content_ctx.name_ips = NULL;
content_ctx.name_bps = NULL;
content_ctx.name_ups = NULL;
content_ctx.name_xdelta = NULL;
content_ctx.valid_extensions = NULL;
content_ctx.subsystem.data = NULL;
@ -2193,6 +2220,8 @@ bool task_push_start_current_core(content_ctx_info_t *content_info)
content_ctx.name_bps = strdup(runloop_st->name.bps);
if (!string_is_empty(runloop_st->name.ups))
content_ctx.name_ups = strdup(runloop_st->name.ups);
if (!string_is_empty(runloop_st->name.xdelta))
content_ctx.name_xdelta = strdup(runloop_st->name.xdelta);
if (!string_is_empty(path_dir_system))
content_ctx.directory_system = strdup(path_dir_system);
@ -2240,6 +2269,8 @@ end:
free(content_ctx.name_bps);
if (content_ctx.name_ups)
free(content_ctx.name_ups);
if (content_ctx.name_xdelta)
free(content_ctx.name_xdelta);
if (content_ctx.directory_system)
free(content_ctx.directory_system);
@ -2405,6 +2436,10 @@ bool task_push_load_content_with_new_core_from_menu(
content_ctx.flags |= CONTENT_INFO_FLAG_IS_BPS_PREF;
if (rarch_flags & RARCH_FLAGS_UPS_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_UPS_PREF;
#ifdef HAVE_XDELTA
if (rarch_flags & RARCH_FLAGS_XDELTA_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_XDELTA_PREF;
#endif
if (runloop_st->flags & RUNLOOP_FLAG_PATCH_BLOCKED)
content_ctx.flags |= CONTENT_INFO_FLAG_PATCH_IS_BLOCKED;
}
@ -2416,6 +2451,7 @@ bool task_push_load_content_with_new_core_from_menu(
content_ctx.name_ips = NULL;
content_ctx.name_bps = NULL;
content_ctx.name_ups = NULL;
content_ctx.name_xdelta = NULL;
content_ctx.valid_extensions = NULL;
content_ctx.subsystem.data = NULL;
@ -2427,6 +2463,8 @@ bool task_push_load_content_with_new_core_from_menu(
content_ctx.name_bps = strdup(runloop_st->name.bps);
if (!string_is_empty(runloop_st->name.ups))
content_ctx.name_ups = strdup(runloop_st->name.ups);
if (!string_is_empty(runloop_st->name.xdelta))
content_ctx.name_xdelta = strdup(runloop_st->name.xdelta);
runloop_st->name.label[0] = '\0';
@ -2475,6 +2513,8 @@ end:
free(content_ctx.name_bps);
if (content_ctx.name_ups)
free(content_ctx.name_ups);
if (content_ctx.name_xdelta)
free(content_ctx.name_xdelta);
if (content_ctx.directory_system)
free(content_ctx.directory_system);
@ -2513,6 +2553,10 @@ static bool task_load_content_internal(
content_ctx.flags |= CONTENT_INFO_FLAG_IS_BPS_PREF;
if (rarch_flags & RARCH_FLAGS_UPS_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_UPS_PREF;
#ifdef HAVE_XDELTA
if (rarch_flags & RARCH_FLAGS_XDELTA_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_XDELTA_PREF;
#endif /* HAVE_XDELTA */
if (runloop_st->flags & RUNLOOP_FLAG_PATCH_BLOCKED)
content_ctx.flags |= CONTENT_INFO_FLAG_PATCH_IS_BLOCKED;
#endif
@ -2523,6 +2567,7 @@ static bool task_load_content_internal(
content_ctx.name_ips = NULL;
content_ctx.name_bps = NULL;
content_ctx.name_ups = NULL;
content_ctx.name_xdelta = NULL;
content_ctx.valid_extensions = NULL;
content_ctx.subsystem.data = NULL;
@ -2555,6 +2600,8 @@ static bool task_load_content_internal(
content_ctx.name_bps = strdup(runloop_st->name.bps);
if (!string_is_empty(runloop_st->name.ups))
content_ctx.name_ups = strdup(runloop_st->name.ups);
if (!string_is_empty(runloop_st->name.xdelta))
content_ctx.name_xdelta = strdup(runloop_st->name.xdelta);
if (!string_is_empty(path_dir_system))
content_ctx.directory_system = strdup(path_dir_system);
@ -2587,6 +2634,8 @@ end:
free(content_ctx.name_bps);
if (content_ctx.name_ups)
free(content_ctx.name_ups);
if (content_ctx.name_xdelta)
free(content_ctx.name_xdelta);
if (content_ctx.directory_system)
free(content_ctx.directory_system);
if (content_ctx.directory_cache)
@ -2779,7 +2828,7 @@ void content_set_subsystem(unsigned idx)
p_content->pending_subsystem_id = idx;
if ( subsystem
if ( subsystem
&& (runloop_st->subsystem_current_count > 0))
{
strlcpy(p_content->pending_subsystem_ident,
@ -2801,7 +2850,7 @@ bool content_set_subsystem_by_name(const char* subsystem_name)
rarch_system_info_t *sys_info = &runloop_st->system;
unsigned i = 0;
/* Core not loaded completely, use the data we peeked on load core */
const struct retro_subsystem_info
const struct retro_subsystem_info
*subsystem = runloop_st->subsystem_data;
/* Core fully loaded, use the subsystem data */
@ -2911,7 +2960,7 @@ static uint32_t file_crc32(uint32_t crc, const char *path)
for (i = 0; i < CRC32_MAX_MB; i++)
{
int64_t nread = filestream_read(file, buf, CRC32_BUFFER_SIZE);
if (nread < 0)
if (nread < 0)
{
free(buf);
filestream_close(file);
@ -3018,14 +3067,19 @@ bool content_init(void)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_BPS_PREF;
if (rarch_flags & RARCH_FLAGS_UPS_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_UPS_PREF;
#ifdef HAVE_XDELTA
if (rarch_flags & RARCH_FLAGS_XDELTA_PREF)
content_ctx.flags |= CONTENT_INFO_FLAG_IS_XDELTA_PREF;
#endif /* HAVE_XDELTA */
if (runloop_st->flags & RUNLOOP_FLAG_PATCH_BLOCKED)
content_ctx.flags |= CONTENT_INFO_FLAG_PATCH_IS_BLOCKED;
#endif
#endif /* HAVE_PATCH */
content_ctx.directory_system = NULL;
content_ctx.directory_cache = NULL;
content_ctx.name_ips = NULL;
content_ctx.name_bps = NULL;
content_ctx.name_ups = NULL;
content_ctx.name_xdelta = NULL;
content_ctx.valid_extensions = NULL;
content_ctx.subsystem.data = NULL;
@ -3037,6 +3091,8 @@ bool content_init(void)
content_ctx.name_bps = strdup(runloop_st->name.bps);
if (!string_is_empty(runloop_st->name.ups))
content_ctx.name_ups = strdup(runloop_st->name.ups);
if (!string_is_empty(runloop_st->name.xdelta))
content_ctx.name_xdelta = strdup(runloop_st->name.xdelta);
if (sys_info)
{
@ -3080,13 +3136,15 @@ bool content_init(void)
free(content_ctx.name_bps);
if (content_ctx.name_ups)
free(content_ctx.name_ups);
if (content_ctx.name_xdelta)
free(content_ctx.name_xdelta);
if (content_ctx.directory_system)
free(content_ctx.directory_system);
if (content_ctx.directory_cache)
free(content_ctx.directory_cache);
if (content_ctx.valid_extensions)
free(content_ctx.valid_extensions);
if (error_enum != MSG_UNKNOWN)
{
switch (error_enum)

View file

@ -36,6 +36,10 @@
#include "../verbosity.h"
#include "../configuration.h"
#if HAVE_XDELTA
#include "../deps/xdelta3/xdelta3.h"
#endif
enum bps_mode
{
SOURCE_READ = 0,
@ -57,7 +61,8 @@ enum patch_error
PATCH_TARGET_INVALID,
PATCH_SOURCE_CHECKSUM_INVALID,
PATCH_TARGET_CHECKSUM_INVALID,
PATCH_PATCH_CHECKSUM_INVALID
PATCH_PATCH_CHECKSUM_INVALID,
PATCH_PATCH_UNSUPPORTED
};
struct bps_data
@ -381,7 +386,7 @@ static enum patch_error ups_apply_patch(
}
data.target_length = (unsigned)*targetlength;
while (data.patch_offset < data.patch_length - 12)
{
unsigned length = (unsigned)ups_decode(&data);
@ -540,7 +545,7 @@ static enum patch_error ips_apply_patch(
patchdata[3] != 'C' ||
patchdata[4] != 'H')
return PATCH_PATCH_INVALID;
if ((error_patch = ips_alloc_targetdata(
patchdata, patchlen, sourcelength,
targetdata, targetlength)) != PATCH_SUCCESS)
@ -611,6 +616,105 @@ static enum patch_error ips_apply_patch(
return PATCH_PATCH_INVALID;
}
static enum patch_error xdelta_apply_patch(
const uint8_t *patchdata, uint64_t patchlen,
const uint8_t *sourcedata, uint64_t sourcelength,
uint8_t **targetdata, uint64_t *targetlength)
{
#if defined(HAVE_PATCH) && defined(HAVE_XDELTA)
int ret;
enum patch_error error_patch = PATCH_SUCCESS;
xd3_stream stream;
xd3_config config;
xd3_source source;
/* Validate the magic number, as given by RFC 3284 section 4.1 */
if (patchlen < 8 ||
patchdata[0] != 0xD6 ||
patchdata[1] != 0xC3 ||
patchdata[2] != 0xC4 ||
patchdata[3] != 0x00)
return PATCH_PATCH_INVALID_HEADER;
xd3_init_config(&config, XD3_SKIP_EMIT);
/* The first pass is just to compute the buffer size,
* no need to emit patched data yet */
if (xd3_config_stream(&stream, &config) != 0)
return PATCH_UNKNOWN;
memset(&source, 0, sizeof(source));
source.blksize = sourcelength;
source.onblk = sourcelength;
source.curblk = sourcedata;
source.curblkno = 0;
xd3_set_source_and_size(&stream, &source, sourcelength);
do
{ /* Make a first pass over the patch, to compute the target size.
* XDelta3 doesn't store the target size in the patch file,
* so we have to either compute it ourselves
* or keep reallocating a buffer as we go.
* I went with the former because it's simpler and fails sooner.
*/
switch (ret = xd3_decode_input(&stream))
{ /* xd3 works like a zlib-styled state machine (stream is the machine) */
case XD3_INPUT: /* When starting the first pass, provide the input */
xd3_avail_input(&stream, patchdata, patchlen);
RARCH_DBG("[xdelta] Provided %lu bytes of input to xd3_stream\n", patchlen);
break;
case XD3_GOTHEADER:
case XD3_WINSTART:
*targetlength += stream.winsize;
RARCH_DBG("[xdelta] Discovered a window of %lu bytes (target filesize is %lu bytes)\n", stream.winsize, *targetlength);
/* xdelta updates the active stream window in the GOTHEADER and WINSTART states */
break;
case XD3_OUTPUT:
xd3_consume_output(&stream); /* Need to call this after every output */
break;
case XD3_INVALID_INPUT:
error_patch = PATCH_PATCH_INVALID;
RARCH_ERR("[xdelta] Invalid input in xd3_stream (%s)\n", xd3_errstring(&stream));
goto cleanup_stream;
case XD3_INTERNAL:
error_patch = PATCH_UNKNOWN;
RARCH_ERR("[xdelta] Internal error in xd3_stream (%s)\n", xd3_errstring(&stream));
goto cleanup_stream;
case XD3_WINFINISH:
RARCH_DBG("[xdelta] Finished processing window #%d\n", stream.current_window);
break;
default:
RARCH_DBG("[xdelta] xd3_decode_input returned %ld (%s; %s)\n", ret, xd3_strerror(ret), stream.msg);
}
} while (stream.avail_in);
*targetdata = malloc(*targetlength);
switch (ret = xd3_decode_memory(
patchdata, patchlen,
sourcedata, sourcelength,
*targetdata, targetlength, *targetlength, 0))
{
case 0: /* Success */
break;
case ENOSPC:
error_patch = PATCH_TARGET_ALLOC_FAILED;
free(*targetdata);
goto cleanup_stream;
default:
error_patch = PATCH_UNKNOWN;
free(*targetdata);
goto cleanup_stream;
}
cleanup_stream:
xd3_close_stream(&stream);
xd3_free_stream(&stream);
return error_patch;
#else /* HAVE_PATCH is defined and HAVE_XDELTA is defined */
return PATCH_PATCH_UNSUPPORTED;
#endif
}
static bool apply_patch_content(uint8_t **buf,
ssize_t *size, const char *patch_desc, const char *patch_path,
patch_func_t func, void *patch_data, int64_t patch_size)
@ -662,7 +766,7 @@ static bool apply_patch_content(uint8_t **buf,
static bool try_bps_patch(bool allow_bps, const char *name_bps,
uint8_t **buf, ssize_t *size)
{
if ( allow_bps
if ( allow_bps
&& !string_is_empty(name_bps)
&& path_is_valid(name_bps)
)
@ -715,7 +819,7 @@ static bool try_ups_patch(bool allow_ups, const char *name_ups,
static bool try_ips_patch(bool allow_ips,
const char *name_ips, uint8_t **buf, ssize_t *size)
{
if ( allow_ips
if ( allow_ips
&& !string_is_empty(name_ips)
&& path_is_valid(name_ips)
)
@ -738,6 +842,34 @@ static bool try_ips_patch(bool allow_ips,
return false;
}
static bool try_xdelta_patch(bool allow_xdelta,
const char *name_xdelta, uint8_t **buf, ssize_t *size)
{
#if defined(HAVE_PATCH) && defined(HAVE_XDELTA)
if ( allow_xdelta
&& !string_is_empty(name_xdelta)
&& path_is_valid(name_xdelta)
)
{
int64_t patch_size;
bool ret = false;
void *patch_data = NULL;
if (!filestream_read_file(name_xdelta, &patch_data, &patch_size))
return false;
if (patch_size >= 0)
ret = apply_patch_content(buf, size, "Xdelta", name_xdelta,
xdelta_apply_patch, patch_data, patch_size);
if (patch_data)
free(patch_data);
return ret;
}
#endif
return false;
}
/**
* patch_content:
* @buf : buffer of the content file.
@ -750,20 +882,24 @@ bool patch_content(
bool is_ips_pref,
bool is_bps_pref,
bool is_ups_pref,
bool is_xdelta_pref,
const char *name_ips,
const char *name_bps,
const char *name_ups,
const char *name_xdelta,
uint8_t **buf,
void *data)
{
ssize_t *size = (ssize_t*)data;
bool allow_ups = !is_bps_pref && !is_ips_pref;
bool allow_ips = !is_ups_pref && !is_bps_pref;
bool allow_bps = !is_ups_pref && !is_ips_pref;
ssize_t *size = (ssize_t*)data;
bool allow_ups = !is_bps_pref && !is_ips_pref && !is_xdelta_pref;
bool allow_ips = !is_ups_pref && !is_bps_pref && !is_xdelta_pref;
bool allow_bps = !is_ups_pref && !is_ips_pref && !is_xdelta_pref;
bool allow_xdelta = !is_bps_pref && !is_ups_pref && !is_ips_pref;
if ( (unsigned)is_ips_pref
+ (unsigned)is_bps_pref
+ (unsigned)is_ups_pref > 1)
+ (unsigned)is_ups_pref
+ (unsigned)is_xdelta_pref > 1)
{
RARCH_WARN("%s\n",
msg_hash_to_str(MSG_SEVERAL_PATCHES_ARE_EXPLICITLY_DEFINED));
@ -773,16 +909,19 @@ bool patch_content(
/* Attempt to apply first (non-indexed) patch */
if ( try_ips_patch(allow_ips, name_ips, buf, size)
|| try_bps_patch(allow_bps, name_bps, buf, size)
|| try_ups_patch(allow_ups, name_ups, buf, size))
|| try_ups_patch(allow_ups, name_ups, buf, size)
|| try_xdelta_patch(allow_xdelta, name_xdelta, buf, size))
{
/* A patch has been found. Now attempt to apply
* any additional 'indexed' patch files */
size_t name_ips_len = strlen(name_ips);
size_t name_bps_len = strlen(name_bps);
size_t name_ups_len = strlen(name_ups);
char *name_ips_indexed = (char*)malloc((name_ips_len + 2) * sizeof(char));
char *name_bps_indexed = (char*)malloc((name_bps_len + 2) * sizeof(char));
char *name_ups_indexed = (char*)malloc((name_ups_len + 2) * sizeof(char));
size_t name_ips_len = strlen(name_ips);
size_t name_bps_len = strlen(name_bps);
size_t name_ups_len = strlen(name_ups);
size_t name_xdelta_len = strlen(name_xdelta);
char *name_ips_indexed = (char*)malloc((name_ips_len + 2) * sizeof(char));
char *name_bps_indexed = (char*)malloc((name_bps_len + 2) * sizeof(char));
char *name_ups_indexed = (char*)malloc((name_ups_len + 2) * sizeof(char));
char *name_xdelta_indexed = (char*)malloc((name_xdelta_len + 2) * sizeof(char));
/* First patch already applied -> index
* for subsequent patches starts at 1 */
size_t patch_index = 1;
@ -790,12 +929,14 @@ bool patch_content(
strlcpy(name_ips_indexed, name_ips, (name_ips_len + 1) * sizeof(char));
strlcpy(name_bps_indexed, name_bps, (name_bps_len + 1) * sizeof(char));
strlcpy(name_ups_indexed, name_ups, (name_ups_len + 1) * sizeof(char));
strlcpy(name_xdelta_indexed, name_xdelta, (name_xdelta_len + 1) * sizeof(char));
/* Ensure that we NUL terminate *after* the
* index character */
name_ips_indexed[name_ips_len + 1] = '\0';
name_bps_indexed[name_bps_len + 1] = '\0';
name_ups_indexed[name_ups_len + 1] = '\0';
name_xdelta_indexed[name_xdelta_len + 1] = '\0';
/* try to patch "*.ipsX" */
while (patch_index < 10)
@ -815,10 +956,12 @@ bool patch_content(
name_ips_indexed[name_ips_len] = index_char;
name_bps_indexed[name_bps_len] = index_char;
name_ups_indexed[name_ups_len] = index_char;
name_xdelta_indexed[name_xdelta_len] = index_char;
if ( !try_ips_patch(allow_ips, name_ips_indexed, buf, size)
&& !try_bps_patch(allow_bps, name_bps_indexed, buf, size)
&& !try_ups_patch(allow_ups, name_ups_indexed, buf, size))
&& !try_ups_patch(allow_ups, name_ups_indexed, buf, size)
&& !try_xdelta_patch(allow_xdelta, name_xdelta_indexed, buf, size))
break;
patch_index++;
@ -827,6 +970,7 @@ bool patch_content(
free(name_ips_indexed);
free(name_bps_indexed);
free(name_ups_indexed);
free(name_xdelta_indexed);
return true;
}

View file

@ -240,9 +240,11 @@ bool patch_content(
bool is_ips_pref,
bool is_bps_pref,
bool is_ups_pref,
bool is_xdelta_pref,
const char *name_ips,
const char *name_bps,
const char *name_ups,
const char *name_xdelta,
uint8_t **buf,
void *data);