/* See LICENSE file for copyright and license details. */ #include "liblss16.h" #include #if defined(__clang__) # pragma clang diagnostic ignored "-Wimplicit-fallthrough" # pragma clang diagnostic ignored "-Wunsafe-buffer-usage" #endif enum liblss16_decode_state liblss16_decode_to_colour_index(struct liblss16_decoder *decoder, const void *data_, size_t length, size_t *consumed_out, uint8_t *pixels, size_t pixels_size, size_t *npixels_out, enum liblss16_decode_error *error_out) { const uint8_t *data = data_; uint8_t n; uint16_t m; size_t cnt; *consumed_out = 0; *npixels_out = 0; if (decoder->internal.kept) { while (decoder->internal.kept && pixels_size) { *pixels++ = decoder->internal.previous; --decoder->internal.kept; --pixels_size; ++*npixels_out; } if (decoder->internal.kept) return LIBLSS16_DECODE_RUNNING; } if (!length) { enum liblss16_decode_error error; if (decoder->internal.init_state < 4U) error = LIBLSS16_DECODE_TRUNCATED_MAGIC; else if (decoder->internal.init_state < 8U + 16U * 3U) error = LIBLSS16_DECODE_TRUNCATED_HEADER; else if (!decoder->internal.y_rem) return LIBLSS16_DECODE_END_OF_IMAGE; else error = LIBLSS16_DECODE_TRUNCATED_IMAGE; if (error_out) *error_out = error; return LIBLSS16_DECODE_ERROR; } switch (decoder->internal.init_state) { case 0: if (*data++ != 0x3DU) { bad_magic: if (error_out) *error_out = LIBLSS16_DECODE_BAD_MAGIC; return LIBLSS16_DECODE_ERROR; } ++*consumed_out; ++decoder->internal.init_state; if (!--length) break; /* fall through */ case 1: if (*data++ != 0xF3U) goto bad_magic; ++*consumed_out; ++decoder->internal.init_state; if (!--length) break; /* fall through */ case 2: if (*data++ != 0x13U) goto bad_magic; ++*consumed_out; ++decoder->internal.init_state; if (!--length) break; /* fall through */ case 3: if (*data++ != 0x14U) goto bad_magic; ++*consumed_out; ++decoder->internal.init_state; if (!--length) break; /* fall through */ case 4: decoder->image.width = (uint16_t)*data++; ++*consumed_out; ++decoder->internal.init_state; if (!--length) break; /* fall through */ case 5: decoder->image.width |= (uint16_t)((uint16_t)*data++ << 8); ++*consumed_out; ++decoder->internal.init_state; if (!--length) break; /* fall through */ case 6: decoder->image.height = (uint16_t)*data++; ++*consumed_out; ++decoder->internal.init_state; if (!--length) break; /* fall through */ case 7: decoder->image.height |= (uint16_t)((uint16_t)*data++ << 8); ++*consumed_out; ++decoder->internal.init_state; decoder->internal.x_rem = decoder->image.width; decoder->internal.y_rem = decoder->image.height; if (!decoder->image.width || !decoder->image.height || decoder->image.height >> 15) { if (error_out) *error_out = LIBLSS16_DECODE_BAD_IMAGE_SIZE; return LIBLSS16_DECODE_ERROR; } if (!--length) break; /* fall through */ default: while (decoder->internal.init_state < 8U + 16U * 3U) { size_t i = decoder->internal.init_state++ - 8U; uint8_t value = *data++; ++*consumed_out; if (value & 0xC0) { ((uint8_t *)decoder->image.colour_map)[i] = value; if (error_out) *error_out = LIBLSS16_DECODE_BAD_COLOUR; return LIBLSS16_DECODE_ERROR; } else { value = (uint8_t)(((unsigned)value * 255U + 31U) / 63U); ((uint8_t *)decoder->image.colour_map)[i] = value; } if (!--length) return LIBLSS16_DECODE_RUNNING; } for (;;) { if (!decoder->internal.y_rem) return LIBLSS16_DECODE_END_OF_IMAGE; if (decoder->internal.saved) { n = (uint8_t)(decoder->internal.saved - 1U); decoder->internal.saved = 0; } else { uint8_t high, low; if (!pixels_size || !length) return LIBLSS16_DECODE_RUNNING; high = (uint8_t)(*data >> 4); low = (uint8_t)(*data & 15); n = low; decoder->internal.saved = (uint8_t)(high + 1U); ++data; --length; ++*consumed_out; } if (decoder->internal.state == 0U) { if (n != decoder->internal.previous) { decoder->internal.previous = n; m = 1U; goto put_m_pixels; } else { decoder->internal.state = 1U; } } else if (decoder->internal.state == 1U) { if (n) { m = (uint16_t)n; put_m_pixels: decoder->internal.state = 0U; if (m > decoder->internal.x_rem) { if (error_out) *error_out = LIBLSS16_DECODE_EXCESSIVE_RUN_LENGTH; return LIBLSS16_DECODE_ERROR; } decoder->internal.x_rem -= m; if (!decoder->internal.x_rem) { decoder->internal.x_rem = decoder->image.width; decoder->internal.y_rem--; if (decoder->internal.saved > 1U) { if (error_out) *error_out = LIBLSS16_DECODE_BAD_ROW_PADDING; return LIBLSS16_DECODE_ERROR; } decoder->internal.saved = 0; decoder->internal.previous = 0; } cnt = (size_t)m < pixels_size ? (size_t)m : pixels_size; memset(pixels, decoder->internal.previous, cnt); pixels = &pixels[cnt]; pixels_size -= cnt; *npixels_out += cnt; m -= (uint16_t)cnt; if (m) { decoder->internal.kept = m; return LIBLSS16_DECODE_RUNNING; } } else { decoder->internal.state = 2U; } } else if (decoder->internal.state == 2U) { decoder->internal.state = (uint8_t)(n | 128U); } else { m = (uint16_t)((unsigned)(n << 4) + (decoder->internal.state & 15U) + 16U); goto put_m_pixels; } } } return LIBLSS16_DECODE_RUNNING; }