diff --git a/common/rle-common.h b/common/rle-common.h index fdc6ae6..2c74a98 100644 --- a/common/rle-common.h +++ b/common/rle-common.h @@ -6,88 +6,103 @@ // Shared RLE code. -static int _rle_control_byte = -1; -static int _rle_more_literals = 0; - -static int _rle_output_literals(const uint8_t* data, int bytes); -static void _rle_process(uint8_t control_byte, const uint8_t* data, int bytes); +// If we're processing a repeat, but don't have the actual byte to be repeated, +// this stores the number of repeats. If we're not, it's 0. +static int _rle_pending_repeats = 0; +// If we're in the middle of a sequence of literal bytes spread across multiple +// input buffers, this is the counter for how many are left. If we're not, +// it's 0. +static int _rle_pending_literals = 0; #if !defined(MIN) # define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif +static int _rle_output_literals(const uint8_t* data, int bytes, int needed) { + // We are still copying literal bytes to the output stream. + // How many should we copy? The number we need, or the number we have, + // whichever is smaller. + int available = MIN(needed, bytes); + SRAM_WRITE(data, available); + return available; +} + +static void _rle_output_repeats(uint8_t data_byte, int repeats) { + for (int i = 0; i < repeats; ++i) { + SRAM_WRITE(&data_byte, 1); + } +} + /** * Requires these macros: * * #define SRAM_WRITE(buffer, size) */ static void rle_to_sram(const uint8_t* buffer, int bytes) { - if (_rle_control_byte != -1) { - // Now we have the data to process this cached control byte from another - // callback. - int control_byte = _rle_control_byte; - _rle_control_byte = -1; // clear cache - _rle_process(control_byte, buffer, bytes); - } else if (_rle_more_literals) { - int consumed = _rle_output_literals(buffer, bytes); - buffer += consumed; - bytes -= consumed; + if (!bytes) { + // This shouldn't happen, but if it did, our logic would break. + return; } - if (bytes) { - _rle_process(buffer[0], buffer + 1, bytes - 1); + if (_rle_pending_repeats) { + // Now we have the data to process this pending repeat command from another + // input buffer. + _rle_output_repeats(*buffer, _rle_pending_repeats); + // This consumes one byte from buffer. + buffer++; + bytes--; + // The pending repeat is satisfied. + _rle_pending_repeats = 0; + } else if (_rle_pending_literals) { + // We are still processing literals from another input buffer. + int consumed = _rle_output_literals(buffer, bytes, _rle_pending_literals); + // This consumed some number of bytes. + buffer += consumed; + bytes -= consumed; + // Our pending literals are also reduced by the same amount. + _rle_pending_literals -= consumed; } -} -static void rle_reset() { - _rle_control_byte = -1; - _rle_more_literals = 0; -} - -static int _rle_output_literals(const uint8_t* data, int bytes) { - // We are still copying literal bytes to the output stream. - int available = MIN(_rle_more_literals, bytes); - SRAM_WRITE(data, available); - _rle_more_literals -= available; - return available; -} + while (bytes) { + // Extract the control byte. + uint8_t control_byte = buffer[0]; + buffer++; + bytes--; -static void _rle_process(uint8_t control_byte, const uint8_t* data, int bytes) { - while (true) { + // Parse it. bool repeat = control_byte & 0x80; int size = control_byte & 0x7f; if (repeat) { if (!bytes) { // We don't have the byte to repeat. - // Save the control byte for next time and return. - _rle_control_byte = control_byte; + // Save the size for next time. + _rle_pending_repeats = size; return; } - // Output the next byte |size| times. - for (int i = 0; i < size; ++i) { - SRAM_WRITE(data, 1); - } + // Repeat the next byte. + _rle_output_repeats(*buffer, size); // Consume that byte. - data++; + buffer++; bytes--; } else { - _rle_more_literals = size; - int consumed = _rle_output_literals(data, bytes); - data += consumed; + // Output literal bytes, as many as we can. (This could be zero.) + int consumed = _rle_output_literals(buffer, bytes, size); + + // Consume that data. + buffer += consumed; bytes -= consumed; - } - if (!bytes) { - // Nothing left in our input. - return; + // Store the number of unfulfilled literal bytes we need from the next + // buffer. (This could be zero.) + _rle_pending_literals = size - consumed; } - - // Set up the next control byte. - control_byte = data[0]; - data++; - bytes--; } } + +static void rle_reset() { + _rle_pending_repeats = 0; + _rle_pending_literals = 0; +} diff --git a/emulator-patches/kinetoscope.c b/emulator-patches/kinetoscope.c index a321a85..b549068 100644 --- a/emulator-patches/kinetoscope.c +++ b/emulator-patches/kinetoscope.c @@ -244,6 +244,10 @@ static bool fetch_range(const char* url, size_t first_byte, size_t size, static bool fetch_range_to_sram(const char* url, size_t first_byte, size_t size) { + // This shouldn't be necessary, but in case of an incomplete compressed + // buffer being processed before this, reset the RLE decoder now. + rle_reset(); + return fetch_range(url, first_byte, size, http_data_to_sram, NULL); }