mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-12 18:45:33 -10:00
A streamed IPS patch built its target as the source arrived: a second buffer the size of the ROM, taken at open, every byte of the source copied into it, then the records laid over it, and the source freed. The target of an IPS patch is the source with the records on it, and the source is the content buffer the load already owns. patch_stream_finish_into() finishes a stream whose source the caller owns (malloc'd, given to patch_stream_set_source()): an IPS patch puts its records on that buffer in place, growing it with realloc() only when the target is longer, and any other format hands back its target in the buffer's place. On failure the buffer is still the unpatched source, as before. Fed in place, IPS copies nothing while the source arrives; a chunk fed from elsewhere copies what came before through to the target and carries on as before. The target is no longer taken at open but by the first copy-through, so the in-place path never takes one. The content load finishes its patch with finish_into(). A 16 MiB ROM with a 2000-record IPS patch, first touch: 6.7 ms -> 0.05 ms to apply, and 16 MiB less heap during the load. The patch_stream test finishes every in-place and fallback comparison with finish_into() on a buffer of its own, and checks a 4 MiB IPS source is patched where it lies without being moved or copied. Leaving the grown tail unzeroed, skipping the records in place or in the catch-up, copying nothing in the catch-up, or copying through while fed in place each fail it.
136 lines
6.4 KiB
C
136 lines
6.4 KiB
C
/* RetroArch - A frontend for libretro.
|
|
*
|
|
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
|
* of the GNU General Public License as published by the Free Software Found-
|
|
* ation, either version 3 of the License, or (at your option) any later version.
|
|
*
|
|
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
|
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
* PURPOSE. See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with RetroArch.
|
|
* If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifndef __RARCH_PATCH_STREAM_H
|
|
#define __RARCH_PATCH_STREAM_H
|
|
|
|
#include <stdint.h>
|
|
#include <stddef.h>
|
|
#include <boolean.h>
|
|
#include <retro_common_api.h>
|
|
|
|
RETRO_BEGIN_DECLS
|
|
|
|
/* Incremental counterpart to the whole-buffer appliers in task_patch.c.
|
|
*
|
|
* The whole-buffer form needs the entire source resident before it can
|
|
* start, so patching is a separate pass after the content has finished
|
|
* loading. This form consumes the source as consecutive chunks, which
|
|
* lets the patch advance on the same task tick that produced the bytes -
|
|
* the load, the decompression and the patching interleave instead of
|
|
* running as three serial passes over the same data.
|
|
*
|
|
* Output is byte-identical to the corresponding whole-buffer applier,
|
|
* and independent of where the chunk boundaries fall.
|
|
*
|
|
* Residency differs per format, because the formats differ in how they
|
|
* address the source:
|
|
* IPS - source read strictly sequentially; nothing is retained.
|
|
* UPS - source consumed in lockstep with the target; only a small
|
|
* rolling carry is retained.
|
|
* BPS - SourceCopy seeks arbitrarily, so the source must be retained
|
|
* in full (as it is today). The gain here is the overlap with
|
|
* the load, not reduced memory.
|
|
* xdelta - as BPS: a window names the source segment it needs, which
|
|
* may be anywhere, so the source is retained.
|
|
*
|
|
* What a damaged patch does also differs per format, and not in the way
|
|
* one would assume, so it is worth stating. BPS and UPS carry
|
|
* checksums and xdelta is structured enough that damage almost always
|
|
* violates it, so for those three a damaged patch is refused: finish()
|
|
* fails, the caller keeps the unpatched buffer, and the content runs
|
|
* unpatched. IPS carries no checksum, no length and no framing beyond
|
|
* a terminator, so a truncated one is indistinguishable from a shorter
|
|
* patch that simply ends - its intact leading records are applied and
|
|
* the result is a partially patched target. That is what the
|
|
* whole-buffer applier has always done and these appliers reproduce it
|
|
* exactly; it is a property of the format, not a decision taken here.
|
|
* A caller that needs to know a patch applied in full cannot learn it
|
|
* from IPS.
|
|
*
|
|
* Usage:
|
|
* ps = patch_stream_ips_open(patch, patch_len, source_len);
|
|
* ... per tick: patch_stream_feed(ps, chunk, chunk_len);
|
|
* patch_stream_finish(ps, &out, &out_len);
|
|
* patch_stream_free(ps);
|
|
*
|
|
* The patch file itself is always fully resident: patches are small and
|
|
* are read before the content in every existing flow.
|
|
*/
|
|
|
|
typedef struct patch_stream patch_stream_t;
|
|
|
|
/* @patch : the patch file, fully in memory; must outlive the stream.
|
|
* @patch_len : its length in bytes.
|
|
* @src_len : the source length the caller will feed in total.
|
|
*
|
|
* Returns NULL if the patch is malformed at the header level (the same
|
|
* structural checks the whole-buffer applier performs up front). */
|
|
patch_stream_t *patch_stream_ips_open(const uint8_t *patch, size_t patch_len,
|
|
size_t src_len);
|
|
patch_stream_t *patch_stream_ups_open(const uint8_t *patch, size_t patch_len,
|
|
size_t src_len);
|
|
patch_stream_t *patch_stream_bps_open(const uint8_t *patch, size_t patch_len,
|
|
size_t src_len);
|
|
/* .xdelta (VCDIFF). Like BPS, a window may name any part of the
|
|
* source, so the source is retained in full and the gain is the
|
|
* overlap with the load. Built only when HAVE_XDELTA is set; returns
|
|
* NULL otherwise, which the caller already treats as "not streamable". */
|
|
patch_stream_t *patch_stream_xdelta_open(const uint8_t *patch,
|
|
size_t patch_len, size_t src_len);
|
|
|
|
/* The source this stream is about to be fed lies whole, in the order it
|
|
* will be fed, at @base, and stays there until patch_stream_finish() or
|
|
* patch_stream_free(): the stream reads what it has been fed in place
|
|
* rather than keeping a copy of it - BPS keeps the whole source for
|
|
* SourceCopy, UPS the part not yet consumed, and IPS copies it through
|
|
* to the target. A chunk fed from anywhere else is taken as before,
|
|
* with a copy of what came before it. Call before the first feed. */
|
|
void patch_stream_set_source(patch_stream_t *ps, const uint8_t *base);
|
|
|
|
/* Feed the next consecutive span of source bytes. Chunks must arrive in
|
|
* order and must not overlap; any sizes are accepted. Returns the bytes
|
|
* taken from this chunk. */
|
|
size_t patch_stream_feed(patch_stream_t *ps, const uint8_t *chunk, size_t len);
|
|
|
|
/* No more source will arrive. Emits whatever tail depends only on the
|
|
* patch, detaches the finished target into @out (caller frees) and its
|
|
* length into @out_len. Returns false if the patch was malformed in a
|
|
* way only detectable during application, in which case @out is
|
|
* untouched and the caller should fall back to the unpatched content. */
|
|
bool patch_stream_finish(patch_stream_t *ps, uint8_t **out, size_t *out_len);
|
|
|
|
/* As patch_stream_finish(), for a caller that owns the source: *buf, of
|
|
* *len bytes, allocated with malloc() and given to
|
|
* patch_stream_set_source(). On success *buf and *len are the target -
|
|
* an IPS patch is applied to the source where it lies, resized if the
|
|
* target is longer, any other the finished target, the source freed.
|
|
* On failure, and for an empty target, which is not handed back, *buf
|
|
* is still the unpatched source. */
|
|
bool patch_stream_finish_into(patch_stream_t *ps, uint8_t **buf,
|
|
size_t *len);
|
|
|
|
/* True once the stream can no longer produce a result - a malformed
|
|
* patch, or an allocation that failed. A caller feeding a long source
|
|
* can stop early on this rather than pushing the remaining megabytes
|
|
* through a stream whose finish is already going to fail. */
|
|
bool patch_stream_failed(patch_stream_t *ps);
|
|
|
|
/* NULL-safe. */
|
|
void patch_stream_free(patch_stream_t *ps);
|
|
|
|
RETRO_END_DECLS
|
|
|
|
#endif
|