diff options
Diffstat (limited to 'Source')
100 files changed, 44476 insertions, 163 deletions
diff --git a/Source/3rdParty/Wuff/wuff.c b/Source/3rdParty/Wuff/wuff.c new file mode 100644 index 0000000..f359733 --- /dev/null +++ b/Source/3rdParty/Wuff/wuff.c @@ -0,0 +1,203 @@ +#include <string.h> + +#include "wuff_config.h" +#include "wuff.h" +#include "wuff_internal.h" + + +wuff_sint32 wuff_open(struct wuff_handle ** handle_pointer, struct wuff_callback * callback, void * userdata) +{ + struct wuff_handle * handle; + wuff_sint32 wuff_status; + + if (handle_pointer == NULL || callback == NULL) + return WUFF_INVALID_PARAM; + + handle = wuff_alloc(sizeof(struct wuff_handle)); + if (handle == NULL) + return WUFF_MEMALLOC_ERROR; + + memset(handle, 0, sizeof(struct wuff_handle)); + handle->buffer.data = NULL; + handle->callback = callback; + handle->userdata = userdata; + + wuff_status = wuff_setup(handle); + if (wuff_status < 0) + { + wuff_cleanup(handle); + return wuff_status; + } + + *handle_pointer = handle; + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_close(struct wuff_handle * handle) +{ + wuff_sint32 wuff_status; + + if (handle == NULL) + return WUFF_INVALID_PARAM; + + wuff_status = wuff_cleanup(handle); + WUFF_STATUS_BAIL() + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_seek(struct wuff_handle * handle, wuff_uint64 offset) +{ + wuff_sint32 wuff_status; + wuff_uint64 seek_offset; + + if (handle == NULL) + return WUFF_INVALID_PARAM; + + /* Clamp offset to stream length. */ + offset = offset <= handle->stream.length ? offset : handle->stream.length; + seek_offset = offset * handle->stream.header.block_size; + wuff_status = handle->callback->seek(handle->userdata, handle->stream.data.offset + seek_offset); + WUFF_STATUS_BAIL() + + handle->stream.position = offset; + handle->output.block_offset = 0; + + /* A new position requires an empty buffer. */ + wuff_status = wuff_buffer_clear(handle); + WUFF_STATUS_BAIL() + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_tell(struct wuff_handle * handle, wuff_uint64 * offset) +{ + if (handle == NULL) + return WUFF_INVALID_PARAM; + + *offset = handle->stream.position; + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_stream_info(struct wuff_handle * handle, struct wuff_info * info) +{ + if (handle == NULL || info == NULL) + return WUFF_INVALID_PARAM; + + info->format = handle->stream.format; + info->channels = handle->stream.header.channels; + info->sample_rate = handle->stream.header.sample_rate; + info->bits_per_sample = handle->stream.header.bits_per_sample; + info->length = handle->stream.length; + /* Think about adding channel mapping and perhaps other things. */ + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_format(struct wuff_handle * handle, wuff_uint16 format) +{ + wuff_sint32 wuff_status; + + if (handle == NULL) + return WUFF_INVALID_PARAM; + else if (format >= WUFF_FORMAT_MAX) + return WUFF_FORMAT_UNSUPPORTED; + + /* A format change resets the position to the start of the block. */ + wuff_status = wuff_seek(handle, handle->stream.position); + WUFF_STATUS_BAIL() + + wuff_status = wuff_set_output_format(handle, format); + WUFF_STATUS_BAIL() + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_read(struct wuff_handle * handle, wuff_uint8 * out_buffer, size_t * out_size) +{ + size_t current_offset; + size_t r_samples, num_samples; + wuff_uint8 head_offset, head, tail, sample_size; + wuff_uint8 * in_buffer; + wuff_sint32 wuff_status; + + if (handle == NULL || out_buffer == NULL || out_size == NULL) + return WUFF_INVALID_PARAM; + + if (*out_size == 0) + return WUFF_SUCCESS; + + sample_size = (wuff_uint8)handle->output.bytes_per_sample; + + /* Calculating the number of samples that fit into the application buffer. */ + /* The first and last sample may be truncated. */ + current_offset = handle->output.block_offset; + head_offset = current_offset % sample_size; + head = head_offset == 0 ? 0 : sample_size - head_offset; + num_samples = wuff_calculate_samples(*out_size, sample_size, &head, &tail); + + /* Requesting the number of samples from the buffer. */ + /* Calculate the new sample count if necessary and write the output. */ + r_samples = num_samples; + wuff_status = wuff_buffer_request(handle, &in_buffer, &r_samples); + WUFF_STATUS_BAIL() + else if (r_samples == 0) + { + /* Possible EOF. */ + *out_size = 0; + } + else + { + if (r_samples == 1 && head != 0) + { + /* Only the first truncated sample fits. */ + /* I really hope nobody will use small buffers like this. */ + num_samples = 0; + tail = 0; + } + else + { + /* At this point the first (possibly truncated) sample will be fully written. */ + /* Subtract the first and last sample from the count if they're truncated. */ + if (r_samples < num_samples) + tail = 0; + num_samples = r_samples - !!head - !!tail; + } + + handle->output.function(out_buffer, in_buffer, num_samples, head_offset, head, tail); + + /* Report the number of bytes written. */ + *out_size = num_samples * sample_size + head + tail; + + /* Adjust the block offset and sample position. */ + current_offset += *out_size; + if (current_offset >= handle->output.block_size) + { + handle->stream.position += current_offset / handle->output.block_size; + handle->output.block_offset = current_offset % handle->output.block_size; + } + else + { + handle->output.block_offset = current_offset; + } + + /* Release the fully processed samples from the buffer. */ + wuff_status = wuff_buffer_release(handle, head_offset + head == sample_size ? num_samples + 1 : num_samples); + WUFF_STATUS_BAIL() + } + + return WUFF_SUCCESS; +} + +void wuff_version(struct wuff_version * version) +{ + if (version == NULL) + return; + + version->major = WUFF_VERSION_MAJOR; + version->minor = WUFF_VERSION_MINOR; + version->build = WUFF_VERSION_BUILD; + version->revision = WUFF_VERSION_REVISION; +} diff --git a/Source/3rdParty/Wuff/wuff.h b/Source/3rdParty/Wuff/wuff.h new file mode 100644 index 0000000..c7cc109 --- /dev/null +++ b/Source/3rdParty/Wuff/wuff.h @@ -0,0 +1,274 @@ +/* + * Wuff - A very basic WAVE reader + */ + +#ifndef WUFF_H +#define WUFF_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#define WUFF_VERSION_MAJOR 0 +#define WUFF_VERSION_MINOR 0 +#define WUFF_VERSION_BUILD 0 +#define WUFF_VERSION_REVISION 2 + + +#ifndef WUFF_API_OVERRIDE + #if defined(_WIN32) && defined(WUFF_DYNAMIC_LIB) + #define WUFF_EXPORT __declspec(dllexport) + #define WUFF_IMPORT __declspec(dllimport) + #else + #define WUFF_EXPORT + #define WUFF_IMPORT + #endif +#endif + +#ifdef WUFF_BUILDING_CORE + #define WUFF_API WUFF_EXPORT +#else + #define WUFF_API WUFF_IMPORT +#endif + + +#ifdef WUFF_FORCE_STDINT_H + #include <stdint.h> +typedef uint8_t wuff_uint8; +typedef int8_t wuff_sint8; +typedef uint16_t wuff_uint16; +typedef int16_t wuff_sint16; +typedef uint32_t wuff_uint32; +typedef int32_t wuff_sint32; + #ifdef WUFF_NO_64BIT_TYPE +typedef uint32_t wuff_uint64; +typedef int32_t wuff_sint64; + #else +typedef uint64_t wuff_uint64; +typedef int64_t wuff_sint64; + #endif +#elif _MSC_VER +typedef unsigned __int8 wuff_uint8; +typedef signed __int8 wuff_sint8; +typedef unsigned __int16 wuff_uint16; +typedef signed __int16 wuff_sint16; +typedef unsigned __int32 wuff_uint32; +typedef signed __int32 wuff_sint32; +typedef unsigned __int64 wuff_uint64; +typedef signed __int64 wuff_sint64; +#else +typedef unsigned char wuff_uint8; +typedef signed char wuff_sint8; +typedef unsigned short wuff_uint16; +typedef signed short wuff_sint16; +typedef unsigned int wuff_uint32; +typedef signed int wuff_sint32; + #ifdef WUFF_NO_64BIT_TYPE +typedef unsigned long wuff_uint64; +typedef signed long wuff_sint64; + #else +typedef unsigned long long wuff_uint64; +typedef signed long long wuff_sint64; + #endif +#endif + +/** @file */ +/** + * Opaque structure used to identify the open Wuff streams. + */ +struct wuff_handle; + +/** + * Callbacks that control the delivery of the data of the WAVE file. + * + * The return values of the functions indicate their status. A zero or positive + * value means success and a negative value failure. The macros WUFF_SUCCESS and + * WUFF_ERROR, or a value equal or below WUFF_USER_ERROR can be used. The error + * value will be returned by the function called by the application. + */ +struct wuff_callback +{ + /** + * The read callback requests the linking application to write at least + * 'size' bytes into the memory where 'buffer' is pointing to. The value + * pointed to by 'size' must be update to the actual number of bytes + * written. Zero will be interepreted as the end-of-file. + * + * @param userdata The userdata set with wuff_open. + * @param buffer A pointer to the memory where the data can be written to. + * @param size A pointer to the size of the buffer and the bytes written. + */ + wuff_sint32 (* read)(void * userdata, wuff_uint8 * buffer, size_t * size); + + /** + * The seek callback requests the linking application to seek to a new byte + * offset in the WAVE data. The next call to the read callback must then + * write data starting from this position. The offset is always relative + * to the beginning of the WAVE data. + * + * @param userdata The userdata set with wuff_open. + * @param offset The new offset. + */ + wuff_sint32 (* seek)(void * userdata, wuff_uint64 offset); + + /** + * The tell callback requests the linking application to write the current + * byte position to the integer pointed to by 'offset'. + * + * @param userdata The userdata set with wuff_open. + * @param offset A pointer to an integer where the current position can be written to. + */ + wuff_sint32 (* tell)(void * userdata, wuff_uint64 * offset); +}; + + +/** + * Stream information structure. + */ +struct wuff_info +{ + wuff_uint16 format; /**< The format of the stream. + * See "Wuff raw sample formats" below. */ + wuff_uint16 channels; /**< The number of channels in the stream. */ + wuff_uint32 sample_rate; /**< The sample rate in hertz. */ + wuff_uint16 bits_per_sample; /**< The number of bits per sample. */ + wuff_uint64 length; /**< The length of the stream in samples. */ +}; + + +/** + * Version information structure. + */ +struct wuff_version +{ + wuff_uint16 major; + wuff_uint16 minor; + wuff_uint16 build; + wuff_uint16 revision; +}; + + +/** + * Opens a new Wuff stream. This will read from the callbacks immediately, make + * sure they're ready. It will check if the WAVE file is supported. + * + * @param handle A pointer to pointer of a wuff_handle that will be + * initialized if the function succeeds. + * @param callback The callbacks for the data of the WAVE file. + * @param userdata A void pointer that will be passed to the callbacks. + * @return Returns a negative value if an error occured. + */ +WUFF_API wuff_sint32 wuff_open(struct wuff_handle ** handle, struct wuff_callback * callback, void * userdata); + +/** + * Closes a Wuff stream. + * + * @param handle The Wuff stream handle. + * @return Returns a negative value if an error occured. + */ +WUFF_API wuff_sint32 wuff_close(struct wuff_handle * handle); + +/** + * Fills the wuff_info struct with information about the stream. + * + * @param handle The Wuff stream handle. + * @param info A pointer to a wuff_info struct. + * @return Returns a negative value if an error occured. + */ +WUFF_API wuff_sint32 wuff_stream_info(struct wuff_handle * handle, struct wuff_info * info); + +/** + * Sets the output format of the decoder. A new format resets the decoder output + * to the beginning of the current block (the sample of the first channel). + * + * @param handle The Wuff stream handle. + * @param format The new output format. + * @return Returns a negative value if an error occured. + */ +WUFF_API wuff_sint32 wuff_format(struct wuff_handle * handle, wuff_uint16 format); + +/** + * Decodes samples to the passed memory location. The size_t pointer points to + * the maximum number of bytes that can be written to the buffer. This count + * will be adjusted to the number of bytes written to the buffer. + * + * @param handle The Wuff stream handle. + * @param buffer The buffer to write to. + * @param size The maximum number of bytes to write to the buffer. + * @return Returns a negative value if an error occured. + */ +WUFF_API wuff_sint32 wuff_read(struct wuff_handle * handle, wuff_uint8 * buffer, size_t * size); + +/** + * Seeks to a sample location. + * The next call to wuff_read will return samples starting from this position. + * + * @param handle The Wuff stream handle. + * @param offset The sample offset to seek to. + * @return Returns a negative value if an error occured. + */ +WUFF_API wuff_sint32 wuff_seek(struct wuff_handle * handle, wuff_uint64 offset); + +/** + * Sets the current position. + * + * @param handle The Wuff stream handle. + * @param offset A pointer to a integer that will receive the sample offset. + * @return Returns a negative value if an error occured. + */ +WUFF_API wuff_sint32 wuff_tell(struct wuff_handle * handle, wuff_uint64 * offset); + +/** + * Copies the Wuff version of the binary into the struct. + * For compile-time version information use the WUFF_VERSION_MAJOR, + * WUFF_VERSION_MINOR, WUFF_VERSION_BUILD, and WUFF_VERSION_REVISION macros. + * + * @param version A pointer to a wuff_version struct that will receive the + * version information. + */ +WUFF_API void wuff_version(struct wuff_version * version); + + +/* Wuff raw sample formats. */ +#define WUFF_FORMAT_PCM_U8 0 +#define WUFF_FORMAT_PCM_S16 1 +#define WUFF_FORMAT_PCM_S24 2 +#define WUFF_FORMAT_PCM_S32 3 +#define WUFF_FORMAT_IEEE_FLOAT_32 4 +#define WUFF_FORMAT_IEEE_FLOAT_64 5 +#define WUFF_FORMAT_MAX 6 + + +/* Success and error return values for all functions. */ +#define WUFF_STREAM_EOF 100 + +#define WUFF_SUCCESS 0 + +#define WUFF_ERROR -1 +#define WUFF_INVALID_PARAM -2 +#define WUFF_MEMALLOC_ERROR -3 + +#define WUFF_STREAM_NOT_RIFF -100 +#define WUFF_STREAM_NOT_WAVE -101 +#define WUFF_STREAM_INVALID -102 +#define WUFF_STREAM_ZERO_CHANNELS -103 +#define WUFF_STREAM_ZERO_SAMPLE_RATE -104 +#define WUFF_STREAM_ZERO_BITS_PER_SAMPLE -105 +#define WUFF_STREAM_FORMAT_CHUNK_MISSING -106 +#define WUFF_STREAM_DATA_CHUNK_MISSING -107 +#define WUFF_STREAM_CHUNK_NOT_FOUND -108 + +#define WUFF_FORMAT_UNSUPPORTED -200 + +#define WUFF_BUFFER_INVALID_SIZE -300 +#define WUFF_BUFFER_INVALID_STREAM_POSITION -301 + +#define WUFF_USER_ERROR -10000 + + +#ifdef __cplusplus +} +#endif + +#endif /* WUFF_H */ diff --git a/Source/3rdParty/Wuff/wuff_config.h b/Source/3rdParty/Wuff/wuff_config.h new file mode 100644 index 0000000..ed17005 --- /dev/null +++ b/Source/3rdParty/Wuff/wuff_config.h @@ -0,0 +1,50 @@ +#ifndef WUFF_CONFIG_H +#define WUFF_CONFIG_H + +/* Defines that the internal code is being built. */ +/* The wuff.h header uses this to change export and import macros. */ +#define WUFF_BUILDING_CORE + +#ifndef WUFF_INLINE_OVERRIDE + #ifdef __cplusplus + #define WUFF_INLINE inline + #else + #ifdef _MSC_VER + #define WUFF_INLINE __inline + #elif __GNUC__ + #define WUFF_INLINE __inline__ + #else + #define WUFF_INLINE + #endif + #endif +#endif + + +#ifndef WUFF_GCC_VISIBILITY_OVERRIDE + #if __GNUC__ >= 4 + #define WUFF_INTERN_API __attribute__((visibility("hidden"))) + #else + #define WUFF_INTERN_API + #endif +#endif + + +#ifdef WUFF_MEMALLOC_OVERRIDE + #ifdef __cplusplus +extern "C" { + #endif + + /* Define your own memory allocator. */ + void * wuff_alloc(size_t size); + void wuff_free(void * mem); + + #ifdef __cplusplus +} + #endif +#else +WUFF_INTERN_API void * wuff_alloc(size_t size); +WUFF_INTERN_API void wuff_free(void * mem); +#endif + + +#endif /* WUFF_CONFIG_H */ diff --git a/Source/3rdParty/Wuff/wuff_convert.c b/Source/3rdParty/Wuff/wuff_convert.c new file mode 100644 index 0000000..9e60ff5 --- /dev/null +++ b/Source/3rdParty/Wuff/wuff_convert.c @@ -0,0 +1,827 @@ +#include <string.h> + +#include "wuff_config.h" +#include "wuff.h" +#include "wuff_convert.h" + +/* + * int8 functions. + */ + +WUFF_CONV_FUNC(wuff_int8_to_int8) +{ + (void)offset; + memcpy(dst, src, samples + head + tail); +} + +WUFF_CONV_FUNC(wuff_int8_to_int16) +{ + wuff_sint16 i16; + size_t i; + + if (head != 0) + { + i16 = (src[0] - 128) << 8; + memcpy(dst, (wuff_uint8 *)&i16 + offset, head); + src += 1; + dst += head; + } + + for (i = 0; i < samples; i++) + { + i16 = (src[i] - 128) << 8; + memcpy(dst + i * 2, &i16, 2); + } + + if (tail != 0) + { + i16 = (src[samples] - 128) << 8; + memcpy(dst + samples * 2, &i16, tail); + } +} + +WUFF_CONV_FUNC(wuff_int8_to_int24) +{ + wuff_sint32 i24; + size_t i; + + if (head != 0) + { + i24 = (src[0] - 128) << 24; + memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head); + src += 1; + dst += head; + } + + for (i = 0; i < samples; i++) + { + i24 = (src[i] - 128) << 24; + memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3); + } + + if (tail != 0) + { + i24 = (src[samples] - 128) << 24; + memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail); + } +} + +WUFF_CONV_FUNC(wuff_int8_to_int32) +{ + wuff_sint32 i32; + size_t i; + + if (head != 0) + { + i32 = (src[0] - 128) << 24; + memcpy(dst, (wuff_uint8 *)&i32 + offset, head); + src += 1; + dst += head; + } + + for (i = 0; i < samples; i++) + { + i32 = (src[i] - 128) << 24; + memcpy(dst + i * 4, &i32, 4); + } + + if (tail != 0) + { + i32 = (src[samples] - 128) << 24; + memcpy(dst + samples * 4, &i32, tail); + } +} + +WUFF_CONV_FUNC(wuff_int8_to_float32) +{ + float f32; + size_t i; + + if (head != 0) + { + f32 = (float)(src[0] - 128) / 128.0f; + memcpy(dst, (wuff_uint8 *)&f32 + offset, head); + src += 1; + dst += head; + } + + for (i = 0; i < samples; i++) + { + f32 = (float)(src[i] - 128) / 128.0f; + memcpy(dst + i * 4, &f32, 4); + } + + if (tail != 0) + { + f32 = (float)(src[samples] - 128) / 128.0f; + memcpy(dst + samples * 4, &f32, tail); + } +} + +WUFF_CONV_FUNC(wuff_int8_to_float64) +{ + double f64; + size_t i; + + if (head != 0) + { + f64 = (double)(src[0] - 128) / 128.0; + memcpy(dst, (wuff_uint8 *)&f64 + offset, head); + src += 1; + dst += head; + } + + for (i = 0; i < samples; i++) + { + f64 = (double)(src[i] - 128) / 128.0; + memcpy(dst + i * 8, &f64, 8); + } + + if (tail != 0) + { + f64 = (double)(src[samples] - 128) / 128.0; + memcpy(dst + samples * 8, &f64, tail); + } +} + +/* + * int16 functions. + */ + +WUFF_CONV_FUNC(wuff_int16_to_int8) +{ + wuff_sint16 i16; + size_t i; + (void)offset; (void)head; (void)tail; + + for (i = 0; i < samples; i++) + { + memcpy(&i16, src + i * 2, 2); + dst[i] = (i16 >> 8) + 128; + } +} + +WUFF_CONV_FUNC(wuff_int16_to_int16) +{ + memcpy(dst, src + offset, samples * 2 + head + tail); +} + +WUFF_CONV_FUNC(wuff_int16_to_int24) +{ + wuff_sint16 i16; + wuff_sint32 i24; + size_t i; + + if (head != 0) + { + memcpy(&i16, src, 2); + i24 = i16 << 16; + memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head); + src += 2; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&i16, src + i * 2, 2); + i24 = i16 << 16; + memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3); + } + + if (tail != 0) + { + memcpy(&i16, src + samples * 2, 2); + i24 = i16 << 16; + memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail); + } +} + +WUFF_CONV_FUNC(wuff_int16_to_int32) +{ + wuff_sint16 i16; + wuff_sint32 i32; + size_t i; + + if (head != 0) + { + memcpy(&i16, src, 2); + i32 = i16 << 16; + memcpy(dst, (wuff_uint8 *)&i32 + offset, head); + src += 2; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&i16, src + i * 2, 2); + i32 = i16 << 16; + memcpy(dst + i * 4, &i32, 4); + } + + if (tail != 0) + { + memcpy(&i16, src + samples * 2, 2); + i32 = i16 << 16; + memcpy(dst + samples * 4, &i32, tail); + } +} + +WUFF_CONV_FUNC(wuff_int16_to_float32) +{ + wuff_sint16 i16; + float f32; + size_t i; + + if (head != 0) + { + memcpy(&i16, src, 2); + f32 = (float)i16 / 32768.0f; + memcpy(dst, (wuff_uint8 *)&f32 + offset, head); + src += 2; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&i16, src + i * 2, 2); + f32 = (float)i16 / 32768.0f; + memcpy(dst + i * 4, &f32, 4); + } + + if (tail != 0) + { + memcpy(&i16, src + samples * 2, 2); + f32 = (float)i16 / 32768.0f; + memcpy(dst + samples * 4, &f32, tail); + } +} + +WUFF_CONV_FUNC(wuff_int16_to_float64) +{ + wuff_sint16 i16; + double f64; + size_t i; + + if (head != 0) + { + memcpy(&i16, src, 2); + f64 = (double)i16 / 32768.0; + memcpy(dst, (wuff_uint8 *)&f64 + offset, head); + src += 2; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&i16, src + i * 2, 2); + f64 = (double)i16 / 32768.0; + memcpy(dst + i * 8, &f64, 8); + } + + if (tail != 0) + { + memcpy(&i16, src + samples * 2, 2); + f64 = (double)i16 / 32768.0; + memcpy(dst + samples * 8, &f64, tail); + } +} + +/* + * int24 functions. + */ + +WUFF_CONV_FUNC(wuff_int24_to_int8) +{ + wuff_sint32 i24 = 0; + size_t i; + (void)offset; (void)head; (void)tail; + + for (i = 0; i < samples; i++) + { + memcpy((wuff_uint8 *)&i24 + 1, src + i * 3, 3); + dst[i] = (wuff_uint8)((i24 >> 16) + 128); + } +} + +WUFF_CONV_FUNC(wuff_int24_to_int16) +{ + size_t i; + + if (head != 0) + { + memcpy(dst, src + 1 + offset, head); + src += 3; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(dst + i * 2, src + 1 + i * 3, 2); + } + + if (tail != 0) + { + memcpy(dst + samples * 2, src + 1 + samples * 3, tail); + } +} + +WUFF_CONV_FUNC(wuff_int24_to_int24) +{ + memcpy(dst, src + offset, samples * 3 + head + tail); +} + +WUFF_CONV_FUNC(wuff_int24_to_int32) +{ + wuff_sint32 i32 = 0; + size_t i; + + if (head != 0) + { + memcpy((wuff_uint8 *)&i32 + 1, src, 3); + memcpy(dst, (wuff_uint8 *)&i32 + offset, head); + src += 3; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy((wuff_uint8 *)&i32 + 1, src + i * 3, 3); + memcpy(dst + i * 4, &i32, 4); + } + + if (tail != 0) + { + memcpy((wuff_uint8 *)&i32 + 1, src + samples * 3, 3); + memcpy(dst + samples * 4, &i32, tail); + } +} + +WUFF_CONV_FUNC(wuff_int24_to_float32) +{ + wuff_sint32 i24 = 0; + float f32; + size_t i; + + if (head != 0) + { + memcpy((wuff_uint8 *)&i24 + 1, src, 3); + f32 = (float)((double)i24 / 2147483648.0); + memcpy(dst, (wuff_uint8 *)&f32 + offset, head); + src += 3; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy((wuff_uint8 *)&i24 + 1, src + i * 3, 3); + f32 = (float)((double)i24 / 2147483648.0); + memcpy(dst + i * 4, &f32, 4); + } + + if (tail != 0) + { + memcpy((wuff_uint8 *)&i24 + 1, src + samples * 3, 3); + f32 = (float)((double)i24 / 2147483648.0); + memcpy(dst + samples * 4, &f32, tail); + } +} + +WUFF_CONV_FUNC(wuff_int24_to_float64) +{ + wuff_sint32 i24 = 0; + double f64; + size_t i; + + if (head != 0) + { + memcpy((wuff_uint8 *)&i24 + 1, src, 3); + f64 = (double)i24 / 2147483648.0; + memcpy(dst, (wuff_uint8 *)&f64 + offset, head); + src += 3; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy((wuff_uint8 *)&i24 + 1, src + i * 3, 3); + f64 = (double)i24 / 2147483648.0; + memcpy(dst + i * 8, &f64, 8); + } + + if (tail != 0) + { + memcpy((wuff_uint8 *)&i24 + 1, src + samples * 3, 3); + f64 = (double)i24 / 2147483648.0; + memcpy(dst + samples * 8, &f64, tail); + } +} + +/* + * int32 functions. + */ + +WUFF_CONV_FUNC(wuff_int32_to_int8) +{ + wuff_sint32 i32 = 0; + size_t i; + (void)offset; (void)head; (void)tail; + + for (i = 0; i < samples; i++) + { + memcpy(&i32, src + i * 4, 4); + dst[i] = (i32 >> 24) + 128; + } +} + +WUFF_CONV_FUNC(wuff_int32_to_int16) +{ + size_t i; + + if (head != 0) + { + memcpy(dst, src + 2 + offset, head); + src += 4; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(dst + i * 2, src + 2 + i * 4, 2); + } + + if (tail != 0) + { + memcpy(dst + samples * 2, src + 2 + samples * 4, tail); + } +} + +WUFF_CONV_FUNC(wuff_int32_to_int24) +{ + size_t i; + + if (head != 0) + { + memcpy(dst, src + 1 + offset, head); + src += 4; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(dst + i * 3, src + 1 + i * 4, 3); + } + + if (tail != 0) + { + memcpy(dst + samples * 3, src + 1 + samples * 4, tail); + } +} + +WUFF_CONV_FUNC(wuff_int32_to_int32) +{ + memcpy(dst, src + offset, samples * 4 + head + tail); +} + +WUFF_CONV_FUNC(wuff_int32_to_float32) +{ + wuff_sint32 i32; + float f32; + size_t i; + + if (head != 0) + { + memcpy(&i32, src, 4); + f32 = (float)((double)i32 / 2147483648.0); + memcpy(dst, (wuff_uint8 *)&f32 + offset, head); + src += 4; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&i32, src + i * 4, 4); + f32 = (float)((double)i32 / 2147483648.0); + memcpy(dst + i * 4, &f32, 4); + } + + if (tail != 0) + { + memcpy(&i32, src + samples * 4, 4); + f32 = (float)((double)i32 / 2147483648.0); + memcpy(dst + samples * 4, &f32, tail); + } +} + +WUFF_CONV_FUNC(wuff_int32_to_float64) +{ + wuff_sint32 i32; + double f64; + size_t i; + + if (head != 0) + { + memcpy(&i32, src, 4); + f64 = (double)i32 / 2147483648.0; + memcpy(dst, (wuff_uint8 *)&f64 + offset, head); + src += 4; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&i32, src + i * 4, 4); + f64 = (double)i32 / 2147483648.0; + memcpy(dst + i * 8, &f64, 8); + } + + if (tail != 0) + { + memcpy(&i32, src + samples * 4, 4); + f64 = (double)i32 / 2147483648.0; + memcpy(dst + samples * 8, &f64, tail); + } +} + +/* + * float32 functions. + */ + +WUFF_CONV_FUNC(wuff_float32_to_int8) +{ + float f32; + size_t i; + (void)offset; (void)head; (void)tail; + + for (i = 0; i < samples; i++) + { + memcpy(&f32, src + i * 4, 4); + dst[i] = (wuff_uint8)((f32 * 127.5f) + 128.0f); + } +} + +WUFF_CONV_FUNC(wuff_float32_to_int16) +{ + float f32; + wuff_sint16 i16; + size_t i; + + if (head != 0) + { + memcpy(&f32, src, 4); + i16 = (wuff_sint16)(f32 * 32767.5f); + memcpy(dst, (wuff_uint8 *)&i16 + offset, head); + src += 4; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&f32, src + i * 4, 4); + i16 = (wuff_sint16)(f32 * 32767.5f); + memcpy(dst + i * 2, &i16, 2); + } + + if (tail != 0) + { + memcpy(&f32, src + i * 4, 4); + i16 = (wuff_sint16)(f32 * 32767.5f); + memcpy(dst + i * 2, &i16, tail); + } +} + +WUFF_CONV_FUNC(wuff_float32_to_int24) +{ + float f32; + wuff_sint32 i24; + size_t i; + + if (head != 0) + { + memcpy(&f32, src, 4); + i24 = (wuff_sint32)((double)f32 * 2147483647.5); + memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head); + src += 4; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&f32, src + i * 4, 4); + i24 = (wuff_sint32)((double)f32 * 2147483647.5); + memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3); + } + + if (tail != 0) + { + memcpy(&f32, src + samples * 4, 4); + i24 = (wuff_sint32)((double)f32 * 2147483647.5); + memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail); + } +} + +WUFF_CONV_FUNC(wuff_float32_to_int32) +{ + float f32; + wuff_sint32 i32; + size_t i; + + if (head != 0) + { + memcpy(&f32, src, 4); + i32 = (wuff_sint32)((double)f32 * 2147483647.5); + memcpy(dst, (wuff_uint8 *)&i32 + offset, head); + src += 4; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&f32, src + i * 4, 4); + i32 = (wuff_sint32)((double)f32 * 2147483647.5); + memcpy(dst + i * 4, &i32, 4); + } + + if (tail != 0) + { + memcpy(&f32, src + samples * 4, 4); + i32 = (wuff_sint32)((double)f32 * 2147483647.5); + memcpy(dst + samples * 4, &i32, tail); + } +} + +WUFF_CONV_FUNC(wuff_float32_to_float32) +{ + memcpy(dst, src + offset, samples * 4 + head + tail); +} + +WUFF_CONV_FUNC(wuff_float32_to_float64) +{ + float f32; + double f64; + size_t i; + + if (head != 0) + { + memcpy(&f32, src, 4); + f64 = f32; + memcpy(dst, (wuff_uint8 *)&f64 + offset, head); + src += 4; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&f32, src + i * 4, 4); + f64 = f32; + memcpy(dst + i * 8, &f64, 8); + } + + if (tail != 0) + { + memcpy(&f32, src + samples * 4, 4); + f64 = f32; + memcpy(dst + samples * 8, &f64, tail); + } +} + +/* + * float64 functions. + */ + +WUFF_CONV_FUNC(wuff_float64_to_int8) +{ + double f64; + size_t i; + (void)offset; (void)head; (void)tail; + + for (i = 0; i < samples; i++) + { + memcpy(&f64, src + i * 8, 8); + dst[i] = (wuff_uint8)((f64 * 127.5) + 128.0); + } +} + +WUFF_CONV_FUNC(wuff_float64_to_int16) +{ + double f64; + wuff_sint16 i16; + size_t i; + + if (head != 0) + { + memcpy(&f64, src, 8); + i16 = (wuff_sint16)(f64 * 32767.5); + memcpy(dst, (wuff_uint8 *)&i16 + offset, head); + src += 8; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&f64, src + i * 8, 8); + i16 = (wuff_sint16)(f64 * 32767.5); + memcpy(dst + i * 2, &i16, 2); + } + + if (tail != 0) + { + memcpy(&f64, src + i * 8, 8); + i16 = (wuff_sint16)(f64 * 32767.5); + memcpy(dst + i * 2, &i16, tail); + } +} + +WUFF_CONV_FUNC(wuff_float64_to_int24) +{ + double f64; + wuff_sint32 i24; + size_t i; + + if (head != 0) + { + memcpy(&f64, src, 8); + i24 = (wuff_sint32)(f64 * 2147483647.5); + memcpy(dst, (wuff_uint8 *)&i24 + 1 + offset, head); + src += 8; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&f64, src + i * 8, 8); + i24 = (wuff_sint32)(f64 * 2147483647.5); + memcpy(dst + i * 3, (wuff_uint8 *)&i24 + 1, 3); + } + + if (tail != 0) + { + memcpy(&f64, src + samples * 8, 8); + i24 = (wuff_sint32)(f64 * 2147483647.5); + memcpy(dst + samples * 3, (wuff_uint8 *)&i24 + 1, tail); + } +} + +WUFF_CONV_FUNC(wuff_float64_to_int32) +{ + double f64; + wuff_sint32 i32; + size_t i; + + if (head != 0) + { + memcpy(&f64, src, 8); + i32 = (wuff_sint32)(f64 * 2147483647.5); + memcpy(dst, (wuff_uint8 *)&i32 + offset, head); + src += 8; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&f64, src + i * 8, 8); + i32 = (wuff_sint32)(f64 * 2147483647.5); + memcpy(dst + i * 4, &i32, 4); + } + + if (tail != 0) + { + memcpy(&f64, src + samples * 8, 8); + i32 = (wuff_sint32)(f64 * 2147483647.5); + memcpy(dst + samples * 4, &i32, tail); + } +} + +WUFF_CONV_FUNC(wuff_float64_to_float32) +{ + double f64; + float f32; + size_t i; + + if (head != 0) + { + memcpy(&f64, src, 8); + f32 = (float)f64; + memcpy(dst, (wuff_uint8 *)&f32 + offset, head); + src += 8; + dst += head; + } + + for (i = 0; i < samples; i++) + { + memcpy(&f64, src + i * 8, 8); + f32 = (float)f64; + memcpy(dst + i * 4, &f32, 4); + } + + if (tail != 0) + { + memcpy(&f64, src + samples * 8, 8); + f32 = (float)f64; + memcpy(dst + samples * 4, &f32, tail); + } +} + +WUFF_CONV_FUNC(wuff_float64_to_float64) +{ + memcpy(dst, src + offset, samples * 8 + head + tail); +} diff --git a/Source/3rdParty/Wuff/wuff_convert.h b/Source/3rdParty/Wuff/wuff_convert.h new file mode 100644 index 0000000..280973d --- /dev/null +++ b/Source/3rdParty/Wuff/wuff_convert.h @@ -0,0 +1,48 @@ +#ifndef WUFF_CONVERT_H +#define WUFF_CONVERT_H + +#define WUFF_CONV_FUNC(name) WUFF_INTERN_API void name(wuff_uint8 * dst, wuff_uint8 * src, size_t samples, wuff_uint8 offset, wuff_uint8 head, wuff_uint8 tail) + +WUFF_CONV_FUNC(wuff_int8_to_int8); +WUFF_CONV_FUNC(wuff_int8_to_int16); +WUFF_CONV_FUNC(wuff_int8_to_int24); +WUFF_CONV_FUNC(wuff_int8_to_int32); +WUFF_CONV_FUNC(wuff_int8_to_float32); +WUFF_CONV_FUNC(wuff_int8_to_float64); + +WUFF_CONV_FUNC(wuff_int16_to_int8); +WUFF_CONV_FUNC(wuff_int16_to_int16); +WUFF_CONV_FUNC(wuff_int16_to_int24); +WUFF_CONV_FUNC(wuff_int16_to_int32); +WUFF_CONV_FUNC(wuff_int16_to_float32); +WUFF_CONV_FUNC(wuff_int16_to_float64); + +WUFF_CONV_FUNC(wuff_int24_to_int8); +WUFF_CONV_FUNC(wuff_int24_to_int16); +WUFF_CONV_FUNC(wuff_int24_to_int24); +WUFF_CONV_FUNC(wuff_int24_to_int32); +WUFF_CONV_FUNC(wuff_int24_to_float32); +WUFF_CONV_FUNC(wuff_int24_to_float64); + +WUFF_CONV_FUNC(wuff_int32_to_int8); +WUFF_CONV_FUNC(wuff_int32_to_int16); +WUFF_CONV_FUNC(wuff_int32_to_int24); +WUFF_CONV_FUNC(wuff_int32_to_int32); +WUFF_CONV_FUNC(wuff_int32_to_float32); +WUFF_CONV_FUNC(wuff_int32_to_float64); + +WUFF_CONV_FUNC(wuff_float32_to_int8); +WUFF_CONV_FUNC(wuff_float32_to_int16); +WUFF_CONV_FUNC(wuff_float32_to_int24); +WUFF_CONV_FUNC(wuff_float32_to_int32); +WUFF_CONV_FUNC(wuff_float32_to_float32); +WUFF_CONV_FUNC(wuff_float32_to_float64); + +WUFF_CONV_FUNC(wuff_float64_to_int8); +WUFF_CONV_FUNC(wuff_float64_to_int16); +WUFF_CONV_FUNC(wuff_float64_to_int24); +WUFF_CONV_FUNC(wuff_float64_to_int32); +WUFF_CONV_FUNC(wuff_float64_to_float32); +WUFF_CONV_FUNC(wuff_float64_to_float64); + +#endif /* WUFF_CONVERT_H */ diff --git a/Source/3rdParty/Wuff/wuff_internal.c b/Source/3rdParty/Wuff/wuff_internal.c new file mode 100644 index 0000000..e481332 --- /dev/null +++ b/Source/3rdParty/Wuff/wuff_internal.c @@ -0,0 +1,540 @@ +#include <stdlib.h> +#include <string.h> + +#include "wuff_config.h" +#include "wuff.h" +#include "wuff_internal.h" +#include "wuff_convert.h" + + +wuff_sint32 wuff_setup(struct wuff_handle * handle) +{ + wuff_sint32 wuff_status; + + if (handle == NULL) + return WUFF_INVALID_PARAM; + + wuff_status = wuff_init_stream(handle); + WUFF_STATUS_BAIL() + + /* Allocating the buffer for the handle requires information from the stream. */ + wuff_status = wuff_buffer_alloc(handle); + WUFF_STATUS_BAIL() + + /* The output format defaults to the stream format. */ + wuff_status = wuff_format(handle, handle->stream.format); + WUFF_STATUS_BAIL() + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_cleanup(struct wuff_handle * handle) +{ + if (handle == NULL) + return WUFF_INVALID_PARAM; + + if (handle->buffer.data != NULL) + wuff_free(handle->buffer.data); + wuff_free(handle); + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_set_output_format(struct wuff_handle * handle, wuff_uint16 format) +{ + wuff_uint16 bits; + wuff_uint16 stream_format; + + if (handle == NULL) + return WUFF_INVALID_PARAM; + else if (format >= WUFF_FORMAT_MAX) + return WUFF_FORMAT_UNSUPPORTED; + + stream_format = handle->stream.format; + + switch (format) + { + case WUFF_FORMAT_PCM_U8: + bits = 8; + switch (stream_format) + { + case WUFF_FORMAT_PCM_U8: + handle->output.function = wuff_int8_to_int8; + break; + case WUFF_FORMAT_PCM_S16: + handle->output.function = wuff_int16_to_int8; + break; + case WUFF_FORMAT_PCM_S24: + handle->output.function = wuff_int24_to_int8; + break; + case WUFF_FORMAT_PCM_S32: + handle->output.function = wuff_int32_to_int8; + break; + case WUFF_FORMAT_IEEE_FLOAT_32: + handle->output.function = wuff_float32_to_int8; + break; + case WUFF_FORMAT_IEEE_FLOAT_64: + handle->output.function = wuff_float64_to_int8; + break; + } + break; + case WUFF_FORMAT_PCM_S16: + bits = 16; + switch (stream_format) + { + case WUFF_FORMAT_PCM_U8: + handle->output.function = wuff_int8_to_int16; + break; + case WUFF_FORMAT_PCM_S16: + handle->output.function = wuff_int16_to_int16; + break; + case WUFF_FORMAT_PCM_S24: + handle->output.function = wuff_int24_to_int16; + break; + case WUFF_FORMAT_PCM_S32: + handle->output.function = wuff_int32_to_int16; + break; + case WUFF_FORMAT_IEEE_FLOAT_32: + handle->output.function = wuff_float32_to_int16; + break; + case WUFF_FORMAT_IEEE_FLOAT_64: + handle->output.function = wuff_float64_to_int16; + break; + } + break; + case WUFF_FORMAT_PCM_S24: + bits = 24; + switch (stream_format) + { + case WUFF_FORMAT_PCM_U8: + handle->output.function = wuff_int8_to_int24; + break; + case WUFF_FORMAT_PCM_S16: + handle->output.function = wuff_int16_to_int24; + break; + case WUFF_FORMAT_PCM_S24: + handle->output.function = wuff_int24_to_int24; + break; + case WUFF_FORMAT_PCM_S32: + handle->output.function = wuff_int32_to_int24; + break; + case WUFF_FORMAT_IEEE_FLOAT_32: + handle->output.function = wuff_float32_to_int24; + break; + case WUFF_FORMAT_IEEE_FLOAT_64: + handle->output.function = wuff_float64_to_int24; + break; + } + break; + case WUFF_FORMAT_PCM_S32: + bits = 32; + switch (stream_format) + { + case WUFF_FORMAT_PCM_U8: + handle->output.function = wuff_int8_to_int32; + break; + case WUFF_FORMAT_PCM_S16: + handle->output.function = wuff_int16_to_int32; + break; + case WUFF_FORMAT_PCM_S24: + handle->output.function = wuff_int24_to_int32; + break; + case WUFF_FORMAT_PCM_S32: + handle->output.function = wuff_int32_to_int32; + break; + case WUFF_FORMAT_IEEE_FLOAT_32: + handle->output.function = wuff_float32_to_int32; + break; + case WUFF_FORMAT_IEEE_FLOAT_64: + handle->output.function = wuff_float64_to_int32; + break; + } + break; + case WUFF_FORMAT_IEEE_FLOAT_32: + bits = 32; + switch (stream_format) + { + case WUFF_FORMAT_PCM_U8: + handle->output.function = wuff_int8_to_float32; + break; + case WUFF_FORMAT_PCM_S16: + handle->output.function = wuff_int16_to_float32; + break; + case WUFF_FORMAT_PCM_S24: + handle->output.function = wuff_int24_to_float32; + break; + case WUFF_FORMAT_PCM_S32: + handle->output.function = wuff_int32_to_float32; + break; + case WUFF_FORMAT_IEEE_FLOAT_32: + handle->output.function = wuff_float32_to_float32; + break; + case WUFF_FORMAT_IEEE_FLOAT_64: + handle->output.function = wuff_float64_to_float32; + break; + } + break; + case WUFF_FORMAT_IEEE_FLOAT_64: + bits = 64; + switch (stream_format) + { + case WUFF_FORMAT_PCM_U8: + handle->output.function = wuff_int8_to_float64; + break; + case WUFF_FORMAT_PCM_S16: + handle->output.function = wuff_int16_to_float64; + break; + case WUFF_FORMAT_PCM_S24: + handle->output.function = wuff_int24_to_float64; + break; + case WUFF_FORMAT_PCM_S32: + handle->output.function = wuff_int32_to_float64; + break; + case WUFF_FORMAT_IEEE_FLOAT_32: + handle->output.function = wuff_float32_to_float64; + break; + case WUFF_FORMAT_IEEE_FLOAT_64: + handle->output.function = wuff_float64_to_float64; + break; + } + break; + default: + return WUFF_FORMAT_UNSUPPORTED; + } + + handle->output.format = format; + handle->output.bytes_per_sample = bits / 8; + handle->output.block_size = handle->stream.header.channels * (bits / 8); + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_check_bits(wuff_uint16 bits, wuff_uint16 * format) +{ + if (*format == WUFF_FORMAT_PCM) + { + switch (bits) + { + case 8: + *format = WUFF_FORMAT_PCM_U8; + break; + case 16: + *format = WUFF_FORMAT_PCM_S16; + break; + case 24: + *format = WUFF_FORMAT_PCM_S24; + break; + case 32: + *format = WUFF_FORMAT_PCM_S32; + break; + default: + return WUFF_FORMAT_UNSUPPORTED; + } + } + else if (*format == WUFF_FORMAT_IEEE_FLOAT) + { + switch (bits) + { + case 32: + *format = WUFF_FORMAT_IEEE_FLOAT_32; + break; + case 64: + *format = WUFF_FORMAT_IEEE_FLOAT_64; + break; + default: + return WUFF_FORMAT_UNSUPPORTED; + } + } + else + { + return WUFF_FORMAT_UNSUPPORTED; + } + + return WUFF_SUCCESS; +} + +size_t wuff_calculate_samples(size_t target_size, wuff_uint8 sample_size, wuff_uint8 * head, wuff_uint8 * tail) +{ + size_t samples = 0; + + if (*head != 0) + { + if (target_size <= *head) + { + *head = (wuff_uint8)target_size; + *tail = 0; + return 1; + } + target_size -= *head; + ++samples; + } + + samples = target_size / sample_size; + *tail = target_size % sample_size; + if (*tail != 0) + ++samples; + return samples; +} + +wuff_sint32 wuff_init_stream(struct wuff_handle * handle) +{ + /* Allocate some space on the stack. */ + /* No need to do dynamic allocation for simple header probing. */ + wuff_uint8 buffer[WUFF_HEADER_FETCH_SIZE]; + size_t buffer_size = WUFF_HEADER_FETCH_SIZE; + wuff_uint64 search_offset; + struct wuff_chunk_header chunk; + wuff_sint32 wuff_status; + + wuff_status = handle->callback->read(handle->userdata, buffer, &buffer_size); + WUFF_STATUS_BAIL() + else if (buffer_size < WUFF_STREAM_MIN_SIZE) + return WUFF_STREAM_NOT_RIFF; + + /* Check for RIFF signature. */ + wuff_copy_chunk_header_data(&chunk, buffer); + if (chunk.id != WUFF_RIFF_CHUNK_ID) + return WUFF_STREAM_NOT_RIFF; + handle->stream.size = chunk.size; + + /* Check for WAVE format. */ + wuff_copy_chunk_header_data(&chunk, buffer + 8); + if (chunk.id != WUFF_WAVE_CHUNK_ID) + return WUFF_STREAM_NOT_WAVE; + + /* Search fmt chunk. */ + wuff_copy_chunk_header_data(&chunk, buffer + 12); + search_offset = 12; + if (chunk.id != WUFF_FORMAT_CHUNK_ID) + { + chunk.id = 0; + /* The fmt chunk must appear before the data chunk. */ + wuff_status = wuff_search_chunk(handle, &chunk, &search_offset, WUFF_FORMAT_CHUNK_ID, WUFF_DATA_CHUNK_ID); + if (wuff_status == WUFF_STREAM_CHUNK_NOT_FOUND) + return WUFF_STREAM_FORMAT_CHUNK_MISSING; + else WUFF_STATUS_BAIL() + + /* In case the fmt chunk is not the first chunk, align it on the stack buffer as if it were. */ + buffer_size = WUFF_HEADER_FETCH_SIZE - 20; + wuff_status = handle->callback->read(handle->userdata, buffer + 20, &buffer_size); + WUFF_STATUS_BAIL() + /* EOF bail. */ + else if (buffer_size < WUFF_HEADER_MIN_SIZE) + return WUFF_STREAM_INVALID; + } + + /* Extract header information. */ + handle->stream.header.size = chunk.size; + handle->stream.header.offset = search_offset + 8; + handle->stream.header.format = wuff_get_uint16(buffer + 20); + handle->stream.header.channels = wuff_get_uint16(buffer + 22); + handle->stream.header.sample_rate = wuff_get_uint32(buffer + 24); + handle->stream.header.bits_per_sample = wuff_get_uint16(buffer + 34); + handle->stream.header.bytes_per_sample = handle->stream.header.bits_per_sample / 8; + handle->stream.header.block_size = handle->stream.header.channels * handle->stream.header.bytes_per_sample; + + /* Bail on invalid streams. */ + if (handle->stream.header.channels == 0) + return WUFF_STREAM_ZERO_CHANNELS; + else if (handle->stream.header.sample_rate == 0) + return WUFF_STREAM_ZERO_SAMPLE_RATE; + else if (handle->stream.header.bits_per_sample == 0) + return WUFF_STREAM_ZERO_BITS_PER_SAMPLE; + + /* Grab the format from the extended header. */ + if (handle->stream.header.size > WUFF_HEADER_MIN_SIZE && wuff_get_uint16(buffer + 36) == 22) + { + if (handle->stream.header.format == WUFF_FORMAT_EXTENSIBLE) + handle->stream.header.format = wuff_get_uint16(buffer + 44); + } + + /* The check if this format is actually supported. */ + handle->stream.format = handle->stream.header.format; + wuff_status = wuff_check_bits(handle->stream.header.bits_per_sample, &handle->stream.format); + WUFF_STATUS_BAIL() + + /* The search for the data chunk begins. */ + wuff_copy_chunk_header_data(&chunk, buffer + 20 + handle->stream.header.size); + search_offset = handle->stream.header.offset + handle->stream.header.size; + wuff_status = wuff_search_chunk(handle, &chunk, &search_offset, WUFF_DATA_CHUNK_ID, 0); + if (wuff_status == WUFF_STREAM_CHUNK_NOT_FOUND) + return WUFF_STREAM_DATA_CHUNK_MISSING; + else WUFF_STATUS_BAIL() + + handle->stream.data.size = chunk.size; + handle->stream.data.offset = search_offset + 8; + handle->stream.length = handle->stream.data.size / handle->stream.header.channels / handle->stream.header.bytes_per_sample; + handle->stream.position = 0; + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_search_chunk(struct wuff_handle * handle, struct wuff_chunk_header * chunk, wuff_uint64 * offset, wuff_uint32 id, wuff_uint32 stop_id) +{ + wuff_uint8 buffer[8]; + wuff_uint64 search_offset; + size_t buffer_size; + wuff_sint32 wuff_status = 0; + + if (chunk->id != 0 && chunk->id == id) + return WUFF_SUCCESS; + + /* Copy the current file position. */ + search_offset = *offset; + + while (wuff_status >= 0) + { + search_offset += 8 + chunk->size; + /* FIXME: Non-compliant RIFFs may not pad to WORD alignment. What now? */ + if (search_offset & 1) + search_offset++; + + wuff_status = handle->callback->seek(handle->userdata, search_offset); + WUFF_STATUS_BAIL() + /*else if (wuff_status == WUFF_CALLBACK_EOF) + return WUFF_STREAM_CHUNK_NOT_FOUND;*/ + + buffer_size = 8; + wuff_status = handle->callback->read(handle->userdata, buffer, &buffer_size); + WUFF_STATUS_BAIL() + + wuff_copy_chunk_header_data(chunk, buffer); + /* Bail if we're at the EOF or the stop id. */ + if (buffer_size < 8 || (stop_id != 0 && chunk->id == stop_id)) + return WUFF_STREAM_CHUNK_NOT_FOUND; + else if (chunk->id == id) + break; + } + + /* Report chunk offset. */ + *offset = search_offset; + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_buffer_alloc(struct wuff_handle * handle) +{ + wuff_sint32 wuff_status; + + if (handle == NULL) + return WUFF_INVALID_PARAM; + + /* Try to allocate a buffer for 0.25 seconds, but clamp at some minimum and maximum value. */ + handle->buffer.size = handle->stream.header.sample_rate * handle->stream.header.block_size / 4; + if (handle->buffer.size < WUFF_BUFFER_MIN_SIZE) + handle->buffer.size = WUFF_BUFFER_MIN_SIZE; + else if (handle->buffer.size > WUFF_BUFFER_MAX_SIZE) + handle->buffer.size = WUFF_BUFFER_MAX_SIZE; + + handle->buffer.data = wuff_alloc(handle->buffer.size); + if (handle->buffer.data == NULL) + return WUFF_MEMALLOC_ERROR; + + /* Just in case, let's null the offsets. */ + wuff_status = wuff_buffer_clear(handle); + WUFF_STATUS_BAIL() + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_buffer_clear(struct wuff_handle * handle) +{ + wuff_uint64 position; + wuff_sint32 wuff_status; + + if (handle == NULL) + return WUFF_INVALID_PARAM; + + wuff_status = handle->callback->tell(handle->userdata, &position); + WUFF_STATUS_BAIL() + + if (position < handle->stream.data.offset || position > handle->stream.data.offset + handle->stream.data.size) + return WUFF_BUFFER_INVALID_STREAM_POSITION; + + handle->buffer.bytes_left = handle->stream.data.size - (position - handle->stream.data.offset); + handle->buffer.offset = 0; + handle->buffer.end = 0; + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_buffer_fill(struct wuff_handle * handle) +{ + size_t bytes_in_buffer; + size_t bytes_to_read; + wuff_sint32 wuff_status; + + if (handle == NULL) + return WUFF_INVALID_PARAM; + + /* Check if there are bytes in the buffer and move them to the start of the buffer. */ + /* Probably not the most efficient way. Think on it some more! */ + bytes_in_buffer = handle->buffer.end - handle->buffer.offset; + + if (bytes_in_buffer == handle->buffer.size) + return WUFF_SUCCESS; + else if (bytes_in_buffer > 0) + memmove(handle->buffer.data, handle->buffer.data + handle->buffer.offset, bytes_in_buffer); + + bytes_to_read = handle->buffer.size - bytes_in_buffer; + if (bytes_to_read > handle->buffer.bytes_left) + bytes_to_read = (size_t)handle->buffer.bytes_left; + + wuff_status = handle->callback->read(handle->userdata, handle->buffer.data + bytes_in_buffer, &bytes_to_read); + WUFF_STATUS_BAIL() + + handle->buffer.offset = 0; + handle->buffer.end = bytes_in_buffer + bytes_to_read; + handle->buffer.bytes_left -= bytes_to_read; + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_buffer_release(struct wuff_handle * handle, size_t samples) +{ + size_t size; + + if (handle == NULL) + return WUFF_INVALID_PARAM; + + size = samples * handle->stream.header.bytes_per_sample; + + /* Check for an attempt to release more samples than the buffer could hold. */ + /* "This should never happen." Let's throw an error anyway in case.*/ + if (size > handle->buffer.end - handle->buffer.offset) + return WUFF_BUFFER_INVALID_SIZE; + + handle->buffer.offset += size; + + return WUFF_SUCCESS; +} + +wuff_sint32 wuff_buffer_request(struct wuff_handle * handle, wuff_uint8 ** buffer, size_t * samples) +{ + size_t request_samples = *samples; + size_t buffer_samples, size; + size_t bps = handle->stream.header.bytes_per_sample; + wuff_sint32 wuff_status; + + if (handle == NULL || buffer == NULL || samples == NULL) + return WUFF_INVALID_PARAM; + + /* Fill the buffer some more if the requested size is bigger than the current data in the buffer. */ + size = request_samples * bps; + if (size > handle->buffer.end - handle->buffer.offset) + { + wuff_status = wuff_buffer_fill(handle); + WUFF_STATUS_BAIL() + } + + buffer_samples = (handle->buffer.end - handle->buffer.offset) / bps; + + /* Report sample count change. */ + if (buffer_samples < request_samples) + *samples = buffer_samples; + + /* Report sample buffer start. */ + *buffer = handle->buffer.data + handle->buffer.offset; + + return WUFF_SUCCESS; +} diff --git a/Source/3rdParty/Wuff/wuff_internal.h b/Source/3rdParty/Wuff/wuff_internal.h new file mode 100644 index 0000000..2679cdb --- /dev/null +++ b/Source/3rdParty/Wuff/wuff_internal.h @@ -0,0 +1,171 @@ +#ifndef WUFF_INTERNAL_H +#define WUFF_INTERNAL_H + +#define WUFF_BUFFER_MIN_SIZE 4096 +#define WUFF_BUFFER_MAX_SIZE 2097152 +#define WUFF_STREAM_MIN_SIZE 36 +#define WUFF_HEADER_MIN_SIZE 16 +#define WUFF_HEADER_FETCH_SIZE 80 + +#define WUFF_FORMAT_PCM 1 +#define WUFF_FORMAT_IEEE_FLOAT 3 +#define WUFF_FORMAT_EXTENSIBLE 0xFFFE + + +#define WUFF_RIFF_CHUNK_ID wuff_get_chunk_id("RIFF") +#define WUFF_WAVE_CHUNK_ID wuff_get_chunk_id("WAVE") +#define WUFF_FORMAT_CHUNK_ID wuff_get_chunk_id("fmt ") +#define WUFF_DATA_CHUNK_ID wuff_get_chunk_id("data") + +#define WUFF_STATUS_BAIL() if (wuff_status < 0) return wuff_status; + + +static WUFF_INLINE wuff_uint32 wuff_get_uint32(wuff_uint8 * data) +{ + return data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24); +} + +static WUFF_INLINE wuff_uint16 wuff_get_uint16(wuff_uint8 * data) +{ + return data[0] + (data[1] << 8); +} + +struct wuff_chunk_header +{ + wuff_uint32 id; + wuff_uint32 size; +}; + +static WUFF_INLINE wuff_uint32 wuff_get_chunk_id(const char txt[5]) +{ + const wuff_uint8 * id = (wuff_uint8*) txt; + wuff_uint32 int_id; + wuff_uint8 * id_bytes = (wuff_uint8 *)&int_id; + id_bytes[0] = id[0]; + id_bytes[1] = id[1]; + id_bytes[2] = id[2]; + id_bytes[3] = id[3]; + + return int_id; +} + +static WUFF_INLINE void wuff_copy_chunk_header_data(struct wuff_chunk_header * chunk, wuff_uint8 * data) +{ + wuff_uint8 * id = (wuff_uint8 *)&chunk->id; + id[0] = data[0]; + id[1] = data[1]; + id[2] = data[2]; + id[3] = data[3]; + + chunk->size = wuff_get_uint32(data + 4); +} + +struct wuff_stream_header +{ + wuff_uint64 size; + wuff_uint64 offset; + + wuff_uint16 format; + wuff_uint16 channels; + wuff_uint32 sample_rate; + wuff_uint16 bits_per_sample; + wuff_uint16 bytes_per_sample; + size_t block_size; +}; + +struct wuff_stream_data +{ + wuff_uint64 size; + wuff_uint64 offset; +}; + +struct wuff_buffer +{ + wuff_uint8 * data; + wuff_uint64 bytes_left; + size_t size; + size_t offset; + size_t end; +}; + +struct wuff_output +{ + wuff_uint16 format; + size_t bytes_per_sample; + size_t block_size; + size_t block_offset; + void (* function)(wuff_uint8 *, wuff_uint8 *, size_t, wuff_uint8, wuff_uint8, wuff_uint8); +}; + +struct wuff_stream +{ + wuff_uint64 size; + wuff_uint64 length; + wuff_uint16 format; + + wuff_uint64 position; + + struct wuff_stream_header header; + struct wuff_stream_data data; +}; + +struct wuff_handle +{ + struct wuff_stream stream; + struct wuff_buffer buffer; + struct wuff_output output; + struct wuff_callback * callback; + void * userdata; +}; + + +/* Initializes the stream, allocates the buffer, and sets the output format. */ +/* Expects a nulled wuff_handle and the callbacks set and ready. */ +WUFF_INTERN_API wuff_sint32 wuff_setup(struct wuff_handle * handle); + +/* Cleans the stream up, frees the buffer and the wuff_handle. */ +WUFF_INTERN_API wuff_sint32 wuff_cleanup(struct wuff_handle * handle); + +/* Called by wuff_setup. Initializes the stream by reading the data from the */ +/* callbacks, searching for headers and stream information. */ +WUFF_INTERN_API wuff_sint32 wuff_init_stream(struct wuff_handle * handle); + +/* Searches for a specific chunk id and stops before another if it's not 0. */ +/* If the id in wuff_chunk_header is not 0, it will be checked too and if */ +/* they match, then the function will return immediately. */ +/* Expects offset to point to the file position of a chunk and */ +/* wuff_chunk_header to have the size of this chunk. */ +WUFF_INTERN_API wuff_sint32 wuff_search_chunk(struct wuff_handle * handle, struct wuff_chunk_header * chunk, wuff_uint64 * offset, wuff_uint32 id, wuff_uint32 stop_id); + +/* Sets the output struct of the stream to the new format. */ +WUFF_INTERN_API wuff_sint32 wuff_set_output_format(struct wuff_handle * handle, wuff_uint16); + +/* Checks if the number of bits per samples is supported and writes the */ +/* output identifier to the 16-bit integer. */ +WUFF_INTERN_API wuff_sint32 wuff_check_bits(wuff_uint16 bits, wuff_uint16 * format); + +/* Calculates the number of samples that have to be requested from the buffer */ +/* by also taking the truncated samples at the start and end into account. */ +/* The return value is the number of samples needed. */ +WUFF_INTERN_API size_t wuff_calculate_samples(size_t target_size, wuff_uint8 sample_size, wuff_uint8 * head, wuff_uint8 * tail); + + +/* Allocates the buffer for the input stream. */ +/* Expects the stream to be initialized, as format information is needed. */ +WUFF_INTERN_API wuff_sint32 wuff_buffer_alloc(struct wuff_handle * handle); + +/* Fills the buffer with new data. */ +WUFF_INTERN_API wuff_sint32 wuff_buffer_fill(struct wuff_handle * handle); + +/* Marks all bytes in the buffer as free. */ +WUFF_INTERN_API wuff_sint32 wuff_buffer_clear(struct wuff_handle * handle); + +/* Requests samples and a pointer to them. */ +/* The number of samples may be lower than requested. */ +WUFF_INTERN_API wuff_sint32 wuff_buffer_request(struct wuff_handle * handle, wuff_uint8 ** buffer, size_t * samples); + +/* Releases the number of samples from the buffer. */ +WUFF_INTERN_API wuff_sint32 wuff_buffer_release(struct wuff_handle * handle, size_t samples); + + +#endif /* WUFF_INTERNAL_H */ diff --git a/Source/3rdParty/Wuff/wuff_memory.c b/Source/3rdParty/Wuff/wuff_memory.c new file mode 100644 index 0000000..eaffa4e --- /dev/null +++ b/Source/3rdParty/Wuff/wuff_memory.c @@ -0,0 +1,17 @@ +#include <stdlib.h> + +#include "wuff_config.h" + +/* Default memory allocators. */ +/* They can be overridden with custom functions at build time. */ +#ifndef WUFF_MEMALLOC_OVERRIDE +void * wuff_alloc(size_t size) +{ + return malloc(size); +} + +void wuff_free(void * mem) +{ + free(mem); +} +#endif diff --git a/Source/3rdParty/lodepng/lodepng.cpp b/Source/3rdParty/lodepng/lodepng.cpp new file mode 100644 index 0000000..37b0562 --- /dev/null +++ b/Source/3rdParty/lodepng/lodepng.cpp @@ -0,0 +1,6232 @@ +/* +LodePNG version 20170917 + +Copyright (c) 2005-2017 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +/* +The manual and changelog are in the header file "lodepng.h" +Rename this file to lodepng.cpp to use it for C++, or to lodepng.c to use it for C. +*/ + +#include "lodepng.h" + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> + +#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ +#pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ +#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ +#endif /*_MSC_VER */ + +const char* LODEPNG_VERSION_STRING = "20170917"; + +/* +This source file is built up in the following large parts. The code sections +with the "LODEPNG_COMPILE_" #defines divide this up further in an intermixed way. +-Tools for C and common code for PNG and Zlib +-C Code for Zlib (huffman, deflate, ...) +-C Code for PNG (file format chunks, adam7, PNG filters, color conversions, ...) +-The C++ wrapper around all of the above +*/ + +/*The malloc, realloc and free functions defined here with "lodepng_" in front +of the name, so that you can easily change them to others related to your +platform if needed. Everything else in the code calls these. Pass +-DLODEPNG_NO_COMPILE_ALLOCATORS to the compiler, or comment out +#define LODEPNG_COMPILE_ALLOCATORS in the header, to disable the ones here and +define them in your own project's source files without needing to change +lodepng source code. Don't forget to remove "static" if you copypaste them +from here.*/ + +#ifdef LODEPNG_COMPILE_ALLOCATORS +static void* lodepng_malloc(size_t size) +{ + return malloc(size); +} + +static void* lodepng_realloc(void* ptr, size_t new_size) +{ + return realloc(ptr, new_size); +} + +static void lodepng_free(void* ptr) +{ + free(ptr); +} +#else /*LODEPNG_COMPILE_ALLOCATORS*/ +void* lodepng_malloc(size_t size); +void* lodepng_realloc(void* ptr, size_t new_size); +void lodepng_free(void* ptr); +#endif /*LODEPNG_COMPILE_ALLOCATORS*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // Tools for C, and common code for PNG and Zlib. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/* +Often in case of an error a value is assigned to a variable and then it breaks +out of a loop (to go to the cleanup phase of a function). This macro does that. +It makes the error handling code shorter and more readable. + +Example: if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83); +*/ +#define CERROR_BREAK(errorvar, code)\ +{\ + errorvar = code;\ + break;\ +} + +/*version of CERROR_BREAK that assumes the common case where the error variable is named "error"*/ +#define ERROR_BREAK(code) CERROR_BREAK(error, code) + +/*Set error var to the error code, and return it.*/ +#define CERROR_RETURN_ERROR(errorvar, code)\ +{\ + errorvar = code;\ + return code;\ +} + +/*Try the code, if it returns error, also return the error.*/ +#define CERROR_TRY_RETURN(call)\ +{\ + unsigned error = call;\ + if(error) return error;\ +} + +/*Set error var to the error code, and return from the void function.*/ +#define CERROR_RETURN(errorvar, code)\ +{\ + errorvar = code;\ + return;\ +} + +/* +About uivector, ucvector and string: +-All of them wrap dynamic arrays or text strings in a similar way. +-LodePNG was originally written in C++. The vectors replace the std::vectors that were used in the C++ version. +-The string tools are made to avoid problems with compilers that declare things like strncat as deprecated. +-They're not used in the interface, only internally in this file as static functions. +-As with many other structs in this file, the init and cleanup functions serve as ctor and dtor. +*/ + +#ifdef LODEPNG_COMPILE_ZLIB +/*dynamic vector of unsigned ints*/ +typedef struct uivector +{ + unsigned* data; + size_t size; /*size in number of unsigned longs*/ + size_t allocsize; /*allocated size in bytes*/ +} uivector; + +static void uivector_cleanup(void* p) +{ + ((uivector*)p)->size = ((uivector*)p)->allocsize = 0; + lodepng_free(((uivector*)p)->data); + ((uivector*)p)->data = NULL; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_reserve(uivector* p, size_t allocsize) +{ + if(allocsize > p->allocsize) + { + size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); + void* data = lodepng_realloc(p->data, newsize); + if(data) + { + p->allocsize = newsize; + p->data = (unsigned*)data; + } + else return 0; /*error: not enough memory*/ + } + return 1; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_resize(uivector* p, size_t size) +{ + if(!uivector_reserve(p, size * sizeof(unsigned))) return 0; + p->size = size; + return 1; /*success*/ +} + +/*resize and give all new elements the value*/ +static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) +{ + size_t oldsize = p->size, i; + if(!uivector_resize(p, size)) return 0; + for(i = oldsize; i < size; ++i) p->data[i] = value; + return 1; +} + +static void uivector_init(uivector* p) +{ + p->data = NULL; + p->size = p->allocsize = 0; +} + +#ifdef LODEPNG_COMPILE_ENCODER +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_push_back(uivector* p, unsigned c) +{ + if(!uivector_resize(p, p->size + 1)) return 0; + p->data[p->size - 1] = c; + return 1; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* /////////////////////////////////////////////////////////////////////////// */ + +/*dynamic vector of unsigned chars*/ +typedef struct ucvector +{ + unsigned char* data; + size_t size; /*used size*/ + size_t allocsize; /*allocated size*/ +} ucvector; + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_reserve(ucvector* p, size_t allocsize) +{ + if(allocsize > p->allocsize) + { + size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); + void* data = lodepng_realloc(p->data, newsize); + if(data) + { + p->allocsize = newsize; + p->data = (unsigned char*)data; + } + else return 0; /*error: not enough memory*/ + } + return 1; +} + +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_resize(ucvector* p, size_t size) +{ + if(!ucvector_reserve(p, size * sizeof(unsigned char))) return 0; + p->size = size; + return 1; /*success*/ +} + +#ifdef LODEPNG_COMPILE_PNG + +static void ucvector_cleanup(void* p) +{ + ((ucvector*)p)->size = ((ucvector*)p)->allocsize = 0; + lodepng_free(((ucvector*)p)->data); + ((ucvector*)p)->data = NULL; +} + +static void ucvector_init(ucvector* p) +{ + p->data = NULL; + p->size = p->allocsize = 0; +} +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ZLIB +/*you can both convert from vector to buffer&size and vica versa. If you use +init_buffer to take over a buffer and size, it is not needed to use cleanup*/ +static void ucvector_init_buffer(ucvector* p, unsigned char* buffer, size_t size) +{ + p->data = buffer; + p->allocsize = p->size = size; +} +#endif /*LODEPNG_COMPILE_ZLIB*/ + +#if (defined(LODEPNG_COMPILE_PNG) && defined(LODEPNG_COMPILE_ANCILLARY_CHUNKS)) || defined(LODEPNG_COMPILE_ENCODER) +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_push_back(ucvector* p, unsigned char c) +{ + if(!ucvector_resize(p, p->size + 1)) return 0; + p->data[p->size - 1] = c; + return 1; +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ + + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned string_resize(char** out, size_t size) +{ + char* data = (char*)lodepng_realloc(*out, size + 1); + if(data) + { + data[size] = 0; /*null termination char*/ + *out = data; + } + return data != 0; +} + +/*init a {char*, size_t} pair for use as string*/ +static void string_init(char** out) +{ + *out = NULL; + string_resize(out, 0); +} + +/*free the above pair again*/ +static void string_cleanup(char** out) +{ + lodepng_free(*out); + *out = NULL; +} + +static void string_set(char** out, const char* in) +{ + size_t insize = strlen(in), i; + if(string_resize(out, insize)) + { + for(i = 0; i != insize; ++i) + { + (*out)[i] = in[i]; + } + } +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned lodepng_read32bitInt(const unsigned char* buffer) +{ + return (unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); +} + +#if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER) +/*buffer must have at least 4 allocated bytes available*/ +static void lodepng_set32bitInt(unsigned char* buffer, unsigned value) +{ + buffer[0] = (unsigned char)((value >> 24) & 0xff); + buffer[1] = (unsigned char)((value >> 16) & 0xff); + buffer[2] = (unsigned char)((value >> 8) & 0xff); + buffer[3] = (unsigned char)((value ) & 0xff); +} +#endif /*defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER)*/ + +#ifdef LODEPNG_COMPILE_ENCODER +static void lodepng_add32bitInt(ucvector* buffer, unsigned value) +{ + ucvector_resize(buffer, buffer->size + 4); /*todo: give error if resize failed*/ + lodepng_set32bitInt(&buffer->data[buffer->size - 4], value); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / File IO / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DISK + +/* returns negative value on error. This should be pure C compatible, so no fstat. */ +static long lodepng_filesize(const char* filename) +{ + FILE* file; + long size; + file = fopen(filename, "rb"); + if(!file) return -1; + + if(fseek(file, 0, SEEK_END) != 0) + { + fclose(file); + return -1; + } + + size = ftell(file); + /* It may give LONG_MAX as directory size, this is invalid for us. */ + if(size == LONG_MAX) size = -1; + + fclose(file); + return size; +} + +/* load file into buffer that already has the correct allocated size. Returns error code.*/ +static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) +{ + FILE* file; + size_t readsize; + file = fopen(filename, "rb"); + if(!file) return 78; + + readsize = fread(out, 1, size, file); + fclose(file); + + if (readsize != size) return 78; + return 0; +} + +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) +{ + long size = lodepng_filesize(filename); + if (size < 0) return 78; + *outsize = (size_t)size; + + *out = (unsigned char*)lodepng_malloc((size_t)size); + if(!(*out) && size > 0) return 83; /*the above malloc failed*/ + + return lodepng_buffer_file(*out, (size_t)size, filename); +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename) +{ + FILE* file; + file = fopen(filename, "wb" ); + if(!file) return 79; + fwrite((char*)buffer , 1 , buffersize, file); + fclose(file); + return 0; +} + +#endif /*LODEPNG_COMPILE_DISK*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of common code and tools. Begin of Zlib related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_ENCODER +/*TODO: this ignores potential out of memory errors*/ +#define addBitToStream(/*size_t**/ bitpointer, /*ucvector**/ bitstream, /*unsigned char*/ bit)\ +{\ + /*add a new byte at the end*/\ + if(((*bitpointer) & 7) == 0) ucvector_push_back(bitstream, (unsigned char)0);\ + /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/\ + (bitstream->data[bitstream->size - 1]) |= (bit << ((*bitpointer) & 0x7));\ + ++(*bitpointer);\ +} + +static void addBitsToStream(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) +{ + size_t i; + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1)); +} + +static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) +{ + size_t i; + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1)); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +#define READBIT(bitpointer, bitstream) ((bitstream[bitpointer >> 3] >> (bitpointer & 0x7)) & (unsigned char)1) + +static unsigned char readBitFromStream(size_t* bitpointer, const unsigned char* bitstream) +{ + unsigned char result = (unsigned char)(READBIT(*bitpointer, bitstream)); + ++(*bitpointer); + return result; +} + +static unsigned readBitsFromStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) +{ + unsigned result = 0, i; + for(i = 0; i != nbits; ++i) + { + result += ((unsigned)READBIT(*bitpointer, bitstream)) << i; + ++(*bitpointer); + } + return result; +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflate - Huffman / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#define FIRST_LENGTH_CODE_INDEX 257 +#define LAST_LENGTH_CODE_INDEX 285 +/*256 literals, the end code, some length codes, and 2 unused codes*/ +#define NUM_DEFLATE_CODE_SYMBOLS 288 +/*the distance codes have their own symbols, 30 used, 2 unused*/ +#define NUM_DISTANCE_SYMBOLS 32 +/*the code length codes. 0-15: code lengths, 16: copy previous 3-6 times, 17: 3-10 zeros, 18: 11-138 zeros*/ +#define NUM_CODE_LENGTH_CODES 19 + +/*the base lengths represented by codes 257-285*/ +static const unsigned LENGTHBASE[29] + = {3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, + 67, 83, 99, 115, 131, 163, 195, 227, 258}; + +/*the extra bits used by codes 257-285 (added to base length)*/ +static const unsigned LENGTHEXTRA[29] + = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 0}; + +/*the base backwards distances (the bits of distance codes appear after length codes and use their own huffman tree)*/ +static const unsigned DISTANCEBASE[30] + = {1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, + 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577}; + +/*the extra bits of backwards distances (added to base)*/ +static const unsigned DISTANCEEXTRA[30] + = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; + +/*the order in which "code length alphabet code lengths" are stored, out of this +the huffman tree of the dynamic huffman tree lengths is generated*/ +static const unsigned CLCL_ORDER[NUM_CODE_LENGTH_CODES] + = {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +/* ////////////////////////////////////////////////////////////////////////// */ + +/* +Huffman tree struct, containing multiple representations of the tree +*/ +typedef struct HuffmanTree +{ + unsigned* tree2d; + unsigned* tree1d; + unsigned* lengths; /*the lengths of the codes of the 1d-tree*/ + unsigned maxbitlen; /*maximum number of bits a single code can get*/ + unsigned numcodes; /*number of symbols in the alphabet = number of codes*/ +} HuffmanTree; + +/*function used for debug purposes to draw the tree in ascii art with C++*/ +/* +static void HuffmanTree_draw(HuffmanTree* tree) +{ + std::cout << "tree. length: " << tree->numcodes << " maxbitlen: " << tree->maxbitlen << std::endl; + for(size_t i = 0; i != tree->tree1d.size; ++i) + { + if(tree->lengths.data[i]) + std::cout << i << " " << tree->tree1d.data[i] << " " << tree->lengths.data[i] << std::endl; + } + std::cout << std::endl; +}*/ + +static void HuffmanTree_init(HuffmanTree* tree) +{ + tree->tree2d = 0; + tree->tree1d = 0; + tree->lengths = 0; +} + +static void HuffmanTree_cleanup(HuffmanTree* tree) +{ + lodepng_free(tree->tree2d); + lodepng_free(tree->tree1d); + lodepng_free(tree->lengths); +} + +/*the tree representation used by the decoder. return value is error*/ +static unsigned HuffmanTree_make2DTree(HuffmanTree* tree) +{ + unsigned nodefilled = 0; /*up to which node it is filled*/ + unsigned treepos = 0; /*position in the tree (1 of the numcodes columns)*/ + unsigned n, i; + + tree->tree2d = (unsigned*)lodepng_malloc(tree->numcodes * 2 * sizeof(unsigned)); + if(!tree->tree2d) return 83; /*alloc fail*/ + + /* + convert tree1d[] to tree2d[][]. In the 2D array, a value of 32767 means + uninited, a value >= numcodes is an address to another bit, a value < numcodes + is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as + many columns as codes - 1. + A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. + Here, the internal nodes are stored (what their 0 and 1 option point to). + There is only memory for such good tree currently, if there are more nodes + (due to too long length codes), error 55 will happen + */ + for(n = 0; n < tree->numcodes * 2; ++n) + { + tree->tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/ + } + + for(n = 0; n < tree->numcodes; ++n) /*the codes*/ + { + for(i = 0; i != tree->lengths[n]; ++i) /*the bits for this code*/ + { + unsigned char bit = (unsigned char)((tree->tree1d[n] >> (tree->lengths[n] - i - 1)) & 1); + /*oversubscribed, see comment in lodepng_error_text*/ + if(treepos > 2147483647 || treepos + 2 > tree->numcodes) return 55; + if(tree->tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ + { + if(i + 1 == tree->lengths[n]) /*last bit*/ + { + tree->tree2d[2 * treepos + bit] = n; /*put the current code in it*/ + treepos = 0; + } + else + { + /*put address of the next step in here, first that address has to be found of course + (it's just nodefilled + 1)...*/ + ++nodefilled; + /*addresses encoded with numcodes added to it*/ + tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes; + treepos = nodefilled; + } + } + else treepos = tree->tree2d[2 * treepos + bit] - tree->numcodes; + } + } + + for(n = 0; n < tree->numcodes * 2; ++n) + { + if(tree->tree2d[n] == 32767) tree->tree2d[n] = 0; /*remove possible remaining 32767's*/ + } + + return 0; +} + +/* +Second step for the ...makeFromLengths and ...makeFromFrequencies functions. +numcodes, lengths and maxbitlen must already be filled in correctly. return +value is error. +*/ +static unsigned HuffmanTree_makeFromLengths2(HuffmanTree* tree) +{ + uivector blcount; + uivector nextcode; + unsigned error = 0; + unsigned bits, n; + + uivector_init(&blcount); + uivector_init(&nextcode); + + tree->tree1d = (unsigned*)lodepng_malloc(tree->numcodes * sizeof(unsigned)); + if(!tree->tree1d) error = 83; /*alloc fail*/ + + if(!uivector_resizev(&blcount, tree->maxbitlen + 1, 0) + || !uivector_resizev(&nextcode, tree->maxbitlen + 1, 0)) + error = 83; /*alloc fail*/ + + if(!error) + { + /*step 1: count number of instances of each code length*/ + for(bits = 0; bits != tree->numcodes; ++bits) ++blcount.data[tree->lengths[bits]]; + /*step 2: generate the nextcode values*/ + for(bits = 1; bits <= tree->maxbitlen; ++bits) + { + nextcode.data[bits] = (nextcode.data[bits - 1] + blcount.data[bits - 1]) << 1; + } + /*step 3: generate all the codes*/ + for(n = 0; n != tree->numcodes; ++n) + { + if(tree->lengths[n] != 0) tree->tree1d[n] = nextcode.data[tree->lengths[n]]++; + } + } + + uivector_cleanup(&blcount); + uivector_cleanup(&nextcode); + + if(!error) return HuffmanTree_make2DTree(tree); + else return error; +} + +/* +given the code lengths (as stored in the PNG file), generate the tree as defined +by Deflate. maxbitlen is the maximum bits that a code in the tree can have. +return value is error. +*/ +static unsigned HuffmanTree_makeFromLengths(HuffmanTree* tree, const unsigned* bitlen, + size_t numcodes, unsigned maxbitlen) +{ + unsigned i; + tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + tree->maxbitlen = maxbitlen; + return HuffmanTree_makeFromLengths2(tree); +} + +#ifdef LODEPNG_COMPILE_ENCODER + +/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", +Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ + +/*chain node for boundary package merge*/ +typedef struct BPMNode +{ + int weight; /*the sum of all weights in this chain*/ + unsigned index; /*index of this leaf node (called "count" in the paper)*/ + struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ + int in_use; +} BPMNode; + +/*lists of chains*/ +typedef struct BPMLists +{ + /*memory pool*/ + unsigned memsize; + BPMNode* memory; + unsigned numfree; + unsigned nextfree; + BPMNode** freelist; + /*two heads of lookahead chains per list*/ + unsigned listsize; + BPMNode** chains0; + BPMNode** chains1; +} BPMLists; + +/*creates a new chain node with the given parameters, from the memory in the lists */ +static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) +{ + unsigned i; + BPMNode* result; + + /*memory full, so garbage collect*/ + if(lists->nextfree >= lists->numfree) + { + /*mark only those that are in use*/ + for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; + for(i = 0; i != lists->listsize; ++i) + { + BPMNode* node; + for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; + for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; + } + /*collect those that are free*/ + lists->numfree = 0; + for(i = 0; i != lists->memsize; ++i) + { + if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; + } + lists->nextfree = 0; + } + + result = lists->freelist[lists->nextfree++]; + result->weight = weight; + result->index = index; + result->tail = tail; + return result; +} + +/*sort the leaves with stable mergesort*/ +static void bpmnode_sort(BPMNode* leaves, size_t num) +{ + BPMNode* mem = (BPMNode*)lodepng_malloc(sizeof(*leaves) * num); + size_t width, counter = 0; + for(width = 1; width < num; width *= 2) + { + BPMNode* a = (counter & 1) ? mem : leaves; + BPMNode* b = (counter & 1) ? leaves : mem; + size_t p; + for(p = 0; p < num; p += 2 * width) + { + size_t q = (p + width > num) ? num : (p + width); + size_t r = (p + 2 * width > num) ? num : (p + 2 * width); + size_t i = p, j = q, k; + for(k = p; k < r; k++) + { + if(i < q && (j >= r || a[i].weight <= a[j].weight)) b[k] = a[i++]; + else b[k] = a[j++]; + } + } + counter++; + } + if(counter & 1) memcpy(leaves, mem, sizeof(*leaves) * num); + lodepng_free(mem); +} + +/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ +static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) +{ + unsigned lastindex = lists->chains1[c]->index; + + if(c == 0) + { + if(lastindex >= numpresent) return; + lists->chains0[c] = lists->chains1[c]; + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); + } + else + { + /*sum of the weights of the head nodes of the previous lookahead chains.*/ + int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; + lists->chains0[c] = lists->chains1[c]; + if(lastindex < numpresent && sum > leaves[lastindex].weight) + { + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); + return; + } + lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); + /*in the end we are only interested in the chain of the last list, so no + need to recurse if we're at the last one (this gives measurable speedup)*/ + if(num + 1 < (int)(2 * numpresent - 2)) + { + boundaryPM(lists, leaves, numpresent, c - 1, num); + boundaryPM(lists, leaves, numpresent, c - 1, num); + } + } +} + +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen) +{ + unsigned error = 0; + unsigned i; + size_t numpresent = 0; /*number of symbols with non-zero frequency*/ + BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ + + if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ + if((1u << maxbitlen) < numcodes) return 80; /*error: represent all symbols*/ + + leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); + if(!leaves) return 83; /*alloc fail*/ + + for(i = 0; i != numcodes; ++i) + { + if(frequencies[i] > 0) + { + leaves[numpresent].weight = (int)frequencies[i]; + leaves[numpresent].index = i; + ++numpresent; + } + } + + for(i = 0; i != numcodes; ++i) lengths[i] = 0; + + /*ensure at least two present symbols. There should be at least one symbol + according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To + make these work as well ensure there are at least two symbols. The + Package-Merge code below also doesn't work correctly if there's only one + symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/ + if(numpresent == 0) + { + lengths[0] = lengths[1] = 1; /*note that for RFC 1951 section 3.2.7, only lengths[0] = 1 is needed*/ + } + else if(numpresent == 1) + { + lengths[leaves[0].index] = 1; + lengths[leaves[0].index == 0 ? 1 : 0] = 1; + } + else + { + BPMLists lists; + BPMNode* node; + + bpmnode_sort(leaves, numpresent); + + lists.listsize = maxbitlen; + lists.memsize = 2 * maxbitlen * (maxbitlen + 1); + lists.nextfree = 0; + lists.numfree = lists.memsize; + lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); + lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); + lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ + + if(!error) + { + for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; + + bpmnode_create(&lists, leaves[0].weight, 1, 0); + bpmnode_create(&lists, leaves[1].weight, 2, 0); + + for(i = 0; i != lists.listsize; ++i) + { + lists.chains0[i] = &lists.memory[0]; + lists.chains1[i] = &lists.memory[1]; + } + + /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ + for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); + + for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) + { + for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; + } + } + + lodepng_free(lists.memory); + lodepng_free(lists.freelist); + lodepng_free(lists.chains0); + lodepng_free(lists.chains1); + } + + lodepng_free(leaves); + return error; +} + +/*Create the Huffman tree given the symbol frequencies*/ +static unsigned HuffmanTree_makeFromFrequencies(HuffmanTree* tree, const unsigned* frequencies, + size_t mincodes, size_t numcodes, unsigned maxbitlen) +{ + unsigned error = 0; + while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ + tree->maxbitlen = maxbitlen; + tree->numcodes = (unsigned)numcodes; /*number of symbols*/ + tree->lengths = (unsigned*)lodepng_realloc(tree->lengths, numcodes * sizeof(unsigned)); + if(!tree->lengths) return 83; /*alloc fail*/ + /*initialize all lengths to 0*/ + memset(tree->lengths, 0, numcodes * sizeof(unsigned)); + + error = lodepng_huffman_code_lengths(tree->lengths, frequencies, numcodes, maxbitlen); + if(!error) error = HuffmanTree_makeFromLengths2(tree); + return error; +} + +static unsigned HuffmanTree_getCode(const HuffmanTree* tree, unsigned index) +{ + return tree->tree1d[index]; +} + +static unsigned HuffmanTree_getLength(const HuffmanTree* tree, unsigned index) +{ + return tree->lengths[index]; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*get the literal and length code tree of a deflated block with fixed tree, as per the deflate specification*/ +static unsigned generateFixedLitLenTree(HuffmanTree* tree) +{ + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ + for(i = 0; i <= 143; ++i) bitlen[i] = 8; + for(i = 144; i <= 255; ++i) bitlen[i] = 9; + for(i = 256; i <= 279; ++i) bitlen[i] = 7; + for(i = 280; i <= 287; ++i) bitlen[i] = 8; + + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +/*get the distance code tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static unsigned generateFixedDistanceTree(HuffmanTree* tree) +{ + unsigned i, error = 0; + unsigned* bitlen = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen) return 83; /*alloc fail*/ + + /*there are 32 distance codes, but 30-31 are unused*/ + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; + error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); + + lodepng_free(bitlen); + return error; +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* +returns the code, or (unsigned)(-1) if error happened +inbitlength is the length of the complete buffer, in bits (so its byte length times 8) +*/ +static unsigned huffmanDecodeSymbol(const unsigned char* in, size_t* bp, + const HuffmanTree* codetree, size_t inbitlength) +{ + unsigned treepos = 0, ct; + for(;;) + { + if(*bp >= inbitlength) return (unsigned)(-1); /*error: end of input memory reached without endcode*/ + /* + decode the symbol from the tree. The "readBitFromStream" code is inlined in + the expression below because this is the biggest bottleneck while decoding + */ + ct = codetree->tree2d[(treepos << 1) + READBIT(*bp, in)]; + ++(*bp); + if(ct < codetree->numcodes) return ct; /*the symbol is decoded, return it*/ + else treepos = ct - codetree->numcodes; /*symbol not yet decoded, instead move tree position*/ + + if(treepos >= codetree->numcodes) return (unsigned)(-1); /*error: it appeared outside the codetree*/ + } +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Inflator (Decompressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*get the tree of a deflated block with fixed tree, as specified in the deflate specification*/ +static void getTreeInflateFixed(HuffmanTree* tree_ll, HuffmanTree* tree_d) +{ + /*TODO: check for out of memory errors*/ + generateFixedLitLenTree(tree_ll); + generateFixedDistanceTree(tree_d); +} + +/*get the tree of a deflated block with dynamic tree, the tree itself is also Huffman compressed with a known tree*/ +static unsigned getTreeInflateDynamic(HuffmanTree* tree_ll, HuffmanTree* tree_d, + const unsigned char* in, size_t* bp, size_t inlength) +{ + /*make sure that length values that aren't filled in will be 0, or a wrong tree will be generated*/ + unsigned error = 0; + unsigned n, HLIT, HDIST, HCLEN, i; + size_t inbitlength = inlength * 8; + + /*see comments in deflateDynamic for explanation of the context and these variables, it is analogous*/ + unsigned* bitlen_ll = 0; /*lit,len code lengths*/ + unsigned* bitlen_d = 0; /*dist code lengths*/ + /*code length code lengths ("clcl"), the bit lengths of the huffman tree used to compress bitlen_ll and bitlen_d*/ + unsigned* bitlen_cl = 0; + HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ + + if((*bp) + 14 > (inlength << 3)) return 49; /*error: the bit pointer is or will go past the memory*/ + + /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ + HLIT = readBitsFromStream(bp, in, 5) + 257; + /*number of distance codes. Unlike the spec, the value 1 is added to it here already*/ + HDIST = readBitsFromStream(bp, in, 5) + 1; + /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ + HCLEN = readBitsFromStream(bp, in, 4) + 4; + + if((*bp) + HCLEN * 3 > (inlength << 3)) return 50; /*error: the bit pointer is or will go past the memory*/ + + HuffmanTree_init(&tree_cl); + + while(!error) + { + /*read the code length codes out of 3 * (amount of code length codes) bits*/ + + bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); + if(!bitlen_cl) ERROR_BREAK(83 /*alloc fail*/); + + for(i = 0; i != NUM_CODE_LENGTH_CODES; ++i) + { + if(i < HCLEN) bitlen_cl[CLCL_ORDER[i]] = readBitsFromStream(bp, in, 3); + else bitlen_cl[CLCL_ORDER[i]] = 0; /*if not, it must stay 0*/ + } + + error = HuffmanTree_makeFromLengths(&tree_cl, bitlen_cl, NUM_CODE_LENGTH_CODES, 7); + if(error) break; + + /*now we can use this tree to read the lengths for the tree that this function will return*/ + bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); + bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); + if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != NUM_DEFLATE_CODE_SYMBOLS; ++i) bitlen_ll[i] = 0; + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen_d[i] = 0; + + /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ + i = 0; + while(i < HLIT + HDIST) + { + unsigned code = huffmanDecodeSymbol(in, bp, &tree_cl, inbitlength); + if(code <= 15) /*a length code*/ + { + if(i < HLIT) bitlen_ll[i] = code; + else bitlen_d[i - HLIT] = code; + ++i; + } + else if(code == 16) /*repeat previous*/ + { + unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ + unsigned value; /*set value to the previous code*/ + + if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ + + if((*bp + 2) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 2); + + if(i < HLIT + 1) value = bitlen_ll[i - 1]; + else value = bitlen_d[i - HLIT - 1]; + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ + if(i < HLIT) bitlen_ll[i] = value; + else bitlen_d[i - HLIT] = value; + ++i; + } + } + else if(code == 17) /*repeat "0" 3-10 times*/ + { + unsigned replength = 3; /*read in the bits that indicate repeat length*/ + if((*bp + 3) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 3); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } + else if(code == 18) /*repeat "0" 11-138 times*/ + { + unsigned replength = 11; /*read in the bits that indicate repeat length*/ + if((*bp + 7) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ + replength += readBitsFromStream(bp, in, 7); + + /*repeat this value in the next lengths*/ + for(n = 0; n < replength; ++n) + { + if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ + + if(i < HLIT) bitlen_ll[i] = 0; + else bitlen_d[i - HLIT] = 0; + ++i; + } + } + else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + if(code == (unsigned)(-1)) + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = (*bp) > inbitlength ? 10 : 11; + } + else error = 16; /*unexisting code, this can never happen*/ + break; + } + } + if(error) break; + + if(bitlen_ll[256] == 0) ERROR_BREAK(64); /*the length of the end code 256 must be larger than 0*/ + + /*now we've finally got HLIT and HDIST, so generate the code trees, and the function is done*/ + error = HuffmanTree_makeFromLengths(tree_ll, bitlen_ll, NUM_DEFLATE_CODE_SYMBOLS, 15); + if(error) break; + error = HuffmanTree_makeFromLengths(tree_d, bitlen_d, NUM_DISTANCE_SYMBOLS, 15); + + break; /*end of error-while*/ + } + + lodepng_free(bitlen_cl); + lodepng_free(bitlen_ll); + lodepng_free(bitlen_d); + HuffmanTree_cleanup(&tree_cl); + + return error; +} + +/*inflate a block with dynamic of fixed Huffman tree*/ +static unsigned inflateHuffmanBlock(ucvector* out, const unsigned char* in, size_t* bp, + size_t* pos, size_t inlength, unsigned btype) +{ + unsigned error = 0; + HuffmanTree tree_ll; /*the huffman tree for literal and length codes*/ + HuffmanTree tree_d; /*the huffman tree for distance codes*/ + size_t inbitlength = inlength * 8; + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + if(btype == 1) getTreeInflateFixed(&tree_ll, &tree_d); + else if(btype == 2) error = getTreeInflateDynamic(&tree_ll, &tree_d, in, bp, inlength); + + while(!error) /*decode all symbols until end reached, breaks at end code*/ + { + /*code_ll is literal, length or end code*/ + unsigned code_ll = huffmanDecodeSymbol(in, bp, &tree_ll, inbitlength); + if(code_ll <= 255) /*literal symbol*/ + { + /*ucvector_push_back would do the same, but for some reason the two lines below run 10% faster*/ + if(!ucvector_resize(out, (*pos) + 1)) ERROR_BREAK(83 /*alloc fail*/); + out->data[*pos] = (unsigned char)code_ll; + ++(*pos); + } + else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ + { + unsigned code_d, distance; + unsigned numextrabits_l, numextrabits_d; /*extra bits for length and distance*/ + size_t start, forward, backward, length; + + /*part 1: get length base*/ + length = LENGTHBASE[code_ll - FIRST_LENGTH_CODE_INDEX]; + + /*part 2: get extra bits and add the value of that to length*/ + numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; + if((*bp + numextrabits_l) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ + length += readBitsFromStream(bp, in, numextrabits_l); + + /*part 3: get distance code*/ + code_d = huffmanDecodeSymbol(in, bp, &tree_d, inbitlength); + if(code_d > 29) + { + if(code_d == (unsigned)(-1)) /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = (*bp) > inlength * 8 ? 10 : 11; + } + else error = 18; /*error: invalid distance code (30-31 are never used)*/ + break; + } + distance = DISTANCEBASE[code_d]; + + /*part 4: get extra bits from distance*/ + numextrabits_d = DISTANCEEXTRA[code_d]; + if((*bp + numextrabits_d) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ + distance += readBitsFromStream(bp, in, numextrabits_d); + + /*part 5: fill in all the out[n] values based on the length and dist*/ + start = (*pos); + if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ + backward = start - distance; + + if(!ucvector_resize(out, (*pos) + length)) ERROR_BREAK(83 /*alloc fail*/); + if (distance < length) { + for(forward = 0; forward < length; ++forward) + { + out->data[(*pos)++] = out->data[backward++]; + } + } else { + memcpy(out->data + *pos, out->data + backward, length); + *pos += length; + } + } + else if(code_ll == 256) + { + break; /*end code, break the loop*/ + } + else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ + { + /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol + (10=no endcode, 11=wrong jump outside of tree)*/ + error = ((*bp) > inlength * 8) ? 10 : 11; + break; + } + } + + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned inflateNoCompression(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength) +{ + size_t p; + unsigned LEN, NLEN, n, error = 0; + + /*go to first boundary of byte*/ + while(((*bp) & 0x7) != 0) ++(*bp); + p = (*bp) / 8; /*byte position*/ + + /*read LEN (2 bytes) and NLEN (2 bytes)*/ + if(p + 4 >= inlength) return 52; /*error, bit pointer will jump past memory*/ + LEN = in[p] + 256u * in[p + 1]; p += 2; + NLEN = in[p] + 256u * in[p + 1]; p += 2; + + /*check if 16-bit NLEN is really the one's complement of LEN*/ + if(LEN + NLEN != 65535) return 21; /*error: NLEN is not one's complement of LEN*/ + + if(!ucvector_resize(out, (*pos) + LEN)) return 83; /*alloc fail*/ + + /*read the literal data: LEN bytes are now stored in the out buffer*/ + if(p + LEN > inlength) return 23; /*error: reading outside of in buffer*/ + for(n = 0; n < LEN; ++n) out->data[(*pos)++] = in[p++]; + + (*bp) = p * 8; + + return error; +} + +static unsigned lodepng_inflatev(ucvector* out, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + /*bit pointer in the "in" data, current byte is bp >> 3, current bit is bp & 0x7 (from lsb to msb of the byte)*/ + size_t bp = 0; + unsigned BFINAL = 0; + size_t pos = 0; /*byte position in the out buffer*/ + unsigned error = 0; + + (void)settings; + + while(!BFINAL) + { + unsigned BTYPE; + if(bp + 2 >= insize * 8) return 52; /*error, bit pointer will jump past memory*/ + BFINAL = readBitFromStream(&bp, in); + BTYPE = 1u * readBitFromStream(&bp, in); + BTYPE += 2u * readBitFromStream(&bp, in); + + if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ + else if(BTYPE == 0) error = inflateNoCompression(out, in, &bp, &pos, insize); /*no compression*/ + else error = inflateHuffmanBlock(out, in, &bp, &pos, insize, BTYPE); /*compression, BTYPE 01 or 10*/ + + if(error) return error; + } + + return error; +} + +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + unsigned error; + ucvector v; + ucvector_init_buffer(&v, *out, *outsize); + error = lodepng_inflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings) +{ + if(settings->custom_inflate) + { + return settings->custom_inflate(out, outsize, in, insize, settings); + } + else + { + return lodepng_inflate(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Deflator (Compressor) / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static const size_t MAX_SUPPORTED_DEFLATE_LENGTH = 258; + +/*bitlen is the size in bits of the code*/ +static void addHuffmanSymbol(size_t* bp, ucvector* compressed, unsigned code, unsigned bitlen) +{ + addBitsToStreamReversed(bp, compressed, code, bitlen); +} + +/*search the index in the array, that has the largest value smaller than or equal to the given value, +given array must be sorted (if no value is smaller, it returns the size of the given array)*/ +static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) +{ + /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ + size_t left = 1; + size_t right = array_size - 1; + + while(left <= right) { + size_t mid = (left + right) >> 1; + if (array[mid] >= value) right = mid - 1; + else left = mid + 1; + } + if(left >= array_size || array[left] > value) left--; + return left; +} + +static void addLengthDistance(uivector* values, size_t length, size_t distance) +{ + /*values in encoded vector are those used by deflate: + 0-255: literal bytes + 256: end + 257-285: length/distance pair (length code, followed by extra length bits, distance code, extra distance bits) + 286-287: invalid*/ + + unsigned length_code = (unsigned)searchCodeIndex(LENGTHBASE, 29, length); + unsigned extra_length = (unsigned)(length - LENGTHBASE[length_code]); + unsigned dist_code = (unsigned)searchCodeIndex(DISTANCEBASE, 30, distance); + unsigned extra_distance = (unsigned)(distance - DISTANCEBASE[dist_code]); + + uivector_push_back(values, length_code + FIRST_LENGTH_CODE_INDEX); + uivector_push_back(values, extra_length); + uivector_push_back(values, dist_code); + uivector_push_back(values, extra_distance); +} + +/*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 +bytes as input because 3 is the minimum match length for deflate*/ +static const unsigned HASH_NUM_VALUES = 65536; +static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/ + +typedef struct Hash +{ + int* head; /*hash value to head circular pos - can be outdated if went around window*/ + /*circular pos to prev circular pos*/ + unsigned short* chain; + int* val; /*circular pos to hash value*/ + + /*TODO: do this not only for zeros but for any repeated byte. However for PNG + it's always going to be the zeros that dominate, so not important for PNG*/ + int* headz; /*similar to head, but for chainz*/ + unsigned short* chainz; /*those with same amount of zeros*/ + unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/ +} Hash; + +static unsigned hash_init(Hash* hash, unsigned windowsize) +{ + unsigned i; + hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES); + hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize); + hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1)); + hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + + if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) + { + return 83; /*alloc fail*/ + } + + /*initialize hash table*/ + for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; + for(i = 0; i != windowsize; ++i) hash->val[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ + + for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ + + return 0; +} + +static void hash_cleanup(Hash* hash) +{ + lodepng_free(hash->head); + lodepng_free(hash->val); + lodepng_free(hash->chain); + + lodepng_free(hash->zeros); + lodepng_free(hash->headz); + lodepng_free(hash->chainz); +} + + + +static unsigned getHash(const unsigned char* data, size_t size, size_t pos) +{ + unsigned result = 0; + if(pos + 2 < size) + { + /*A simple shift and xor hash is used. Since the data of PNGs is dominated + by zeroes due to the filters, a better hash does not have a significant + effect on speed in traversing the chain, and causes more time spend on + calculating the hash.*/ + result ^= (unsigned)(data[pos + 0] << 0u); + result ^= (unsigned)(data[pos + 1] << 4u); + result ^= (unsigned)(data[pos + 2] << 8u); + } else { + size_t amount, i; + if(pos >= size) return 0; + amount = size - pos; + for(i = 0; i != amount; ++i) result ^= (unsigned)(data[pos + i] << (i * 8u)); + } + return result & HASH_BIT_MASK; +} + +static unsigned countZeros(const unsigned char* data, size_t size, size_t pos) +{ + const unsigned char* start = data + pos; + const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; + if(end > data + size) end = data + size; + data = start; + while(data != end && *data == 0) ++data; + /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ + return (unsigned)(data - start); +} + +/*wpos = pos & (windowsize - 1)*/ +static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) +{ + hash->val[wpos] = (int)hashval; + if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; + hash->head[hashval] = wpos; + + hash->zeros[wpos] = numzeros; + if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; + hash->headz[numzeros] = wpos; +} + +/* +LZ77-encode the data. Return value is error code. The input are raw bytes, the output +is in the form of unsigned integers with codes representing for example literal bytes, or +length/distance pairs. +It uses a hash table technique to let it encode faster. When doing LZ77 encoding, a +sliding window (of windowsize) is used, and all past bytes in that window can be used as +the "dictionary". A brute force search through all possible distances would be slow, and +this hash technique is one out of several ways to speed this up. +*/ +static unsigned encodeLZ77(uivector* out, Hash* hash, + const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize, + unsigned minmatch, unsigned nicematch, unsigned lazymatching) +{ + size_t pos; + unsigned i, error = 0; + /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/ + unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8; + unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64; + + unsigned usezeros = 1; /*not sure if setting it to false for windowsize < 8192 is better or worse*/ + unsigned numzeros = 0; + + unsigned offset; /*the offset represents the distance in LZ77 terminology*/ + unsigned length; + unsigned lazy = 0; + unsigned lazylength = 0, lazyoffset = 0; + unsigned hashval; + unsigned current_offset, current_length; + unsigned prev_offset; + const unsigned char *lastptr, *foreptr, *backptr; + unsigned hashpos; + + if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ + if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ + + if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH; + + for(pos = inpos; pos < insize; ++pos) + { + size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ + unsigned chainlength = 0; + + hashval = getHash(in, insize, pos); + + if(usezeros && hashval == 0) + { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } + else + { + numzeros = 0; + } + + updateHashChain(hash, wpos, hashval, numzeros); + + /*the length and offset found for the current position*/ + length = 0; + offset = 0; + + hashpos = hash->chain[wpos]; + + lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH]; + + /*search for the longest string*/ + prev_offset = 0; + for(;;) + { + if(chainlength++ >= maxchainlength) break; + current_offset = hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize; + + if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ + prev_offset = current_offset; + if(current_offset > 0) + { + /*test the next characters*/ + foreptr = &in[pos]; + backptr = &in[pos - current_offset]; + + /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/ + if(numzeros >= 3) + { + unsigned skip = hash->zeros[hashpos]; + if(skip > numzeros) skip = numzeros; + backptr += skip; + foreptr += skip; + } + + while(foreptr != lastptr && *backptr == *foreptr) /*maximum supported length by deflate is max length*/ + { + ++backptr; + ++foreptr; + } + current_length = (unsigned)(foreptr - &in[pos]); + + if(current_length > length) + { + length = current_length; /*the longest length*/ + offset = current_offset; /*the offset that is related to this longest length*/ + /*jump out once a length of max length is found (speed gain). This also jumps + out if length is MAX_SUPPORTED_DEFLATE_LENGTH*/ + if(current_length >= nicematch) break; + } + } + + if(hashpos == hash->chain[hashpos]) break; + + if(numzeros >= 3 && length > numzeros) + { + hashpos = hash->chainz[hashpos]; + if(hash->zeros[hashpos] != numzeros) break; + } + else + { + hashpos = hash->chain[hashpos]; + /*outdated hash value, happens if particular value was not encountered in whole last window*/ + if(hash->val[hashpos] != (int)hashval) break; + } + } + + if(lazymatching) + { + if(!lazy && length >= 3 && length <= maxlazymatch && length < MAX_SUPPORTED_DEFLATE_LENGTH) + { + lazy = 1; + lazylength = length; + lazyoffset = offset; + continue; /*try the next byte*/ + } + if(lazy) + { + lazy = 0; + if(pos == 0) ERROR_BREAK(81); + if(length > lazylength + 1) + { + /*push the previous character as literal*/ + if(!uivector_push_back(out, in[pos - 1])) ERROR_BREAK(83 /*alloc fail*/); + } + else + { + length = lazylength; + offset = lazyoffset; + hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ + hash->headz[numzeros] = -1; /*idem*/ + --pos; + } + } + } + if(length >= 3 && offset > windowsize) ERROR_BREAK(86 /*too big (or overflown negative) offset*/); + + /*encode it as length/distance pair or literal value*/ + if(length < 3) /*only lengths of 3 or higher are supported as length/distance pair*/ + { + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } + else if(length < minmatch || (length == 3 && offset > 4096)) + { + /*compensate for the fact that longer offsets have more extra bits, a + length of only 3 may be not worth it then*/ + if(!uivector_push_back(out, in[pos])) ERROR_BREAK(83 /*alloc fail*/); + } + else + { + addLengthDistance(out, length, offset); + for(i = 1; i < length; ++i) + { + ++pos; + wpos = pos & (windowsize - 1); + hashval = getHash(in, insize, pos); + if(usezeros && hashval == 0) + { + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; + } + else + { + numzeros = 0; + } + updateHashChain(hash, wpos, hashval, numzeros); + } + } + } /*end of the loop through each character of input*/ + + return error; +} + +/* /////////////////////////////////////////////////////////////////////////// */ + +static unsigned deflateNoCompression(ucvector* out, const unsigned char* data, size_t datasize) +{ + /*non compressed deflate block data: 1 bit BFINAL,2 bits BTYPE,(5 bits): it jumps to start of next byte, + 2 bytes LEN, 2 bytes NLEN, LEN bytes literal DATA*/ + + size_t i, j, numdeflateblocks = (datasize + 65534) / 65535; + unsigned datapos = 0; + for(i = 0; i != numdeflateblocks; ++i) + { + unsigned BFINAL, BTYPE, LEN, NLEN; + unsigned char firstbyte; + + BFINAL = (i == numdeflateblocks - 1); + BTYPE = 0; + + firstbyte = (unsigned char)(BFINAL + ((BTYPE & 1) << 1) + ((BTYPE & 2) << 1)); + ucvector_push_back(out, firstbyte); + + LEN = 65535; + if(datasize - datapos < 65535) LEN = (unsigned)datasize - datapos; + NLEN = 65535 - LEN; + + ucvector_push_back(out, (unsigned char)(LEN & 255)); + ucvector_push_back(out, (unsigned char)(LEN >> 8)); + ucvector_push_back(out, (unsigned char)(NLEN & 255)); + ucvector_push_back(out, (unsigned char)(NLEN >> 8)); + + /*Decompressed data*/ + for(j = 0; j < 65535 && datapos < datasize; ++j) + { + ucvector_push_back(out, data[datapos++]); + } + } + + return 0; +} + +/* +write the lz77-encoded data, which has lit, len and dist codes, to compressed stream using huffman trees. +tree_ll: the tree for lit and len codes. +tree_d: the tree for distance codes. +*/ +static void writeLZ77data(size_t* bp, ucvector* out, const uivector* lz77_encoded, + const HuffmanTree* tree_ll, const HuffmanTree* tree_d) +{ + size_t i = 0; + for(i = 0; i != lz77_encoded->size; ++i) + { + unsigned val = lz77_encoded->data[i]; + addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_ll, val), HuffmanTree_getLength(tree_ll, val)); + if(val > 256) /*for a length code, 3 more things have to be added*/ + { + unsigned length_index = val - FIRST_LENGTH_CODE_INDEX; + unsigned n_length_extra_bits = LENGTHEXTRA[length_index]; + unsigned length_extra_bits = lz77_encoded->data[++i]; + + unsigned distance_code = lz77_encoded->data[++i]; + + unsigned distance_index = distance_code; + unsigned n_distance_extra_bits = DISTANCEEXTRA[distance_index]; + unsigned distance_extra_bits = lz77_encoded->data[++i]; + + addBitsToStream(bp, out, length_extra_bits, n_length_extra_bits); + addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_d, distance_code), + HuffmanTree_getLength(tree_d, distance_code)); + addBitsToStream(bp, out, distance_extra_bits, n_distance_extra_bits); + } + } +} + +/*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/ +static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, + const unsigned char* data, size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) +{ + unsigned error = 0; + + /* + A block is compressed as follows: The PNG data is lz77 encoded, resulting in + literal bytes and length/distance pairs. This is then huffman compressed with + two huffman trees. One huffman tree is used for the lit and len values ("ll"), + another huffman tree is used for the dist values ("d"). These two trees are + stored using their code lengths, and to compress even more these code lengths + are also run-length encoded and huffman compressed. This gives a huffman tree + of code lengths "cl". The code lenghts used to describe this third tree are + the code length code lengths ("clcl"). + */ + + /*The lz77 encoded data, represented with integers since there will also be length and distance codes in it*/ + uivector lz77_encoded; + HuffmanTree tree_ll; /*tree for lit,len values*/ + HuffmanTree tree_d; /*tree for distance codes*/ + HuffmanTree tree_cl; /*tree for encoding the code lengths representing tree_ll and tree_d*/ + uivector frequencies_ll; /*frequency of lit,len codes*/ + uivector frequencies_d; /*frequency of dist codes*/ + uivector frequencies_cl; /*frequency of code length codes*/ + uivector bitlen_lld; /*lit,len,dist code lenghts (int bits), literally (without repeat codes).*/ + uivector bitlen_lld_e; /*bitlen_lld encoded with repeat codes (this is a rudemtary run length compression)*/ + /*bitlen_cl is the code length code lengths ("clcl"). The bit lengths of codes to represent tree_cl + (these are written as is in the file, it would be crazy to compress these using yet another huffman + tree that needs to be represented by yet another set of code lengths)*/ + uivector bitlen_cl; + size_t datasize = dataend - datapos; + + /* + Due to the huffman compression of huffman tree representations ("two levels"), there are some anologies: + bitlen_lld is to tree_cl what data is to tree_ll and tree_d. + bitlen_lld_e is to bitlen_lld what lz77_encoded is to data. + bitlen_cl is to bitlen_lld_e what bitlen_lld is to lz77_encoded. + */ + + unsigned BFINAL = final; + size_t numcodes_ll, numcodes_d, i; + unsigned HLIT, HDIST, HCLEN; + + uivector_init(&lz77_encoded); + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + HuffmanTree_init(&tree_cl); + uivector_init(&frequencies_ll); + uivector_init(&frequencies_d); + uivector_init(&frequencies_cl); + uivector_init(&bitlen_lld); + uivector_init(&bitlen_lld_e); + uivector_init(&bitlen_cl); + + /*This while loop never loops due to a break at the end, it is here to + allow breaking out of it to the cleanup phase on error conditions.*/ + while(!error) + { + if(settings->use_lz77) + { + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(error) break; + } + else + { + if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); + for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ + } + + if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83 /*alloc fail*/); + if(!uivector_resizev(&frequencies_d, 30, 0)) ERROR_BREAK(83 /*alloc fail*/); + + /*Count the frequencies of lit, len and dist codes*/ + for(i = 0; i != lz77_encoded.size; ++i) + { + unsigned symbol = lz77_encoded.data[i]; + ++frequencies_ll.data[symbol]; + if(symbol > 256) + { + unsigned dist = lz77_encoded.data[i + 2]; + ++frequencies_d.data[dist]; + i += 3; + } + } + frequencies_ll.data[256] = 1; /*there will be exactly 1 end code, at the end of the block*/ + + /*Make both huffman trees, one for the lit and len codes, one for the dist codes*/ + error = HuffmanTree_makeFromFrequencies(&tree_ll, frequencies_ll.data, 257, frequencies_ll.size, 15); + if(error) break; + /*2, not 1, is chosen for mincodes: some buggy PNG decoders require at least 2 symbols in the dist tree*/ + error = HuffmanTree_makeFromFrequencies(&tree_d, frequencies_d.data, 2, frequencies_d.size, 15); + if(error) break; + + numcodes_ll = tree_ll.numcodes; if(numcodes_ll > 286) numcodes_ll = 286; + numcodes_d = tree_d.numcodes; if(numcodes_d > 30) numcodes_d = 30; + /*store the code lengths of both generated trees in bitlen_lld*/ + for(i = 0; i != numcodes_ll; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); + for(i = 0; i != numcodes_d; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); + + /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), + 17 (3-10 zeroes), 18 (11-138 zeroes)*/ + for(i = 0; i != (unsigned)bitlen_lld.size; ++i) + { + unsigned j = 0; /*amount of repititions*/ + while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) ++j; + + if(bitlen_lld.data[i] == 0 && j >= 2) /*repeat code for zeroes*/ + { + ++j; /*include the first zero*/ + if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ + { + uivector_push_back(&bitlen_lld_e, 17); + uivector_push_back(&bitlen_lld_e, j - 3); + } + else /*repeat code 18 supports max 138 zeroes*/ + { + if(j > 138) j = 138; + uivector_push_back(&bitlen_lld_e, 18); + uivector_push_back(&bitlen_lld_e, j - 11); + } + i += (j - 1); + } + else if(j >= 3) /*repeat code for value other than zero*/ + { + size_t k; + unsigned num = j / 6, rest = j % 6; + uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); + for(k = 0; k < num; ++k) + { + uivector_push_back(&bitlen_lld_e, 16); + uivector_push_back(&bitlen_lld_e, 6 - 3); + } + if(rest >= 3) + { + uivector_push_back(&bitlen_lld_e, 16); + uivector_push_back(&bitlen_lld_e, rest - 3); + } + else j -= rest; + i += j; + } + else /*too short to benefit from repeat code*/ + { + uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); + } + } + + /*generate tree_cl, the huffmantree of huffmantrees*/ + + if(!uivector_resizev(&frequencies_cl, NUM_CODE_LENGTH_CODES, 0)) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != bitlen_lld_e.size; ++i) + { + ++frequencies_cl.data[bitlen_lld_e.data[i]]; + /*after a repeat code come the bits that specify the number of repetitions, + those don't need to be in the frequencies_cl calculation*/ + if(bitlen_lld_e.data[i] >= 16) ++i; + } + + error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl.data, + frequencies_cl.size, frequencies_cl.size, 7); + if(error) break; + + if(!uivector_resize(&bitlen_cl, tree_cl.numcodes)) ERROR_BREAK(83 /*alloc fail*/); + for(i = 0; i != tree_cl.numcodes; ++i) + { + /*lenghts of code length tree is in the order as specified by deflate*/ + bitlen_cl.data[i] = HuffmanTree_getLength(&tree_cl, CLCL_ORDER[i]); + } + while(bitlen_cl.data[bitlen_cl.size - 1] == 0 && bitlen_cl.size > 4) + { + /*remove zeros at the end, but minimum size must be 4*/ + if(!uivector_resize(&bitlen_cl, bitlen_cl.size - 1)) ERROR_BREAK(83 /*alloc fail*/); + } + if(error) break; + + /* + Write everything into the output + + After the BFINAL and BTYPE, the dynamic block consists out of the following: + - 5 bits HLIT, 5 bits HDIST, 4 bits HCLEN + - (HCLEN+4)*3 bits code lengths of code length alphabet + - HLIT + 257 code lenghts of lit/length alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - HDIST + 1 code lengths of distance alphabet (encoded using the code length + alphabet, + possible repetition codes 16, 17, 18) + - compressed data + - 256 (end code) + */ + + /*Write block type*/ + addBitToStream(bp, out, BFINAL); + addBitToStream(bp, out, 0); /*first bit of BTYPE "dynamic"*/ + addBitToStream(bp, out, 1); /*second bit of BTYPE "dynamic"*/ + + /*write the HLIT, HDIST and HCLEN values*/ + HLIT = (unsigned)(numcodes_ll - 257); + HDIST = (unsigned)(numcodes_d - 1); + HCLEN = (unsigned)bitlen_cl.size - 4; + /*trim zeroes for HCLEN. HLIT and HDIST were already trimmed at tree creation*/ + while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) --HCLEN; + addBitsToStream(bp, out, HLIT, 5); + addBitsToStream(bp, out, HDIST, 5); + addBitsToStream(bp, out, HCLEN, 4); + + /*write the code lenghts of the code length alphabet*/ + for(i = 0; i != HCLEN + 4; ++i) addBitsToStream(bp, out, bitlen_cl.data[i], 3); + + /*write the lenghts of the lit/len AND the dist alphabet*/ + for(i = 0; i != bitlen_lld_e.size; ++i) + { + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_cl, bitlen_lld_e.data[i]), + HuffmanTree_getLength(&tree_cl, bitlen_lld_e.data[i])); + /*extra bits of repeat codes*/ + if(bitlen_lld_e.data[i] == 16) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 2); + else if(bitlen_lld_e.data[i] == 17) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 3); + else if(bitlen_lld_e.data[i] == 18) addBitsToStream(bp, out, bitlen_lld_e.data[++i], 7); + } + + /*write the compressed data symbols*/ + writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); + /*error: the length of the end code 256 must be larger than 0*/ + if(HuffmanTree_getLength(&tree_ll, 256) == 0) ERROR_BREAK(64); + + /*write the end code*/ + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); + + break; /*end of error-while*/ + } + + /*cleanup*/ + uivector_cleanup(&lz77_encoded); + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + HuffmanTree_cleanup(&tree_cl); + uivector_cleanup(&frequencies_ll); + uivector_cleanup(&frequencies_d); + uivector_cleanup(&frequencies_cl); + uivector_cleanup(&bitlen_lld_e); + uivector_cleanup(&bitlen_lld); + uivector_cleanup(&bitlen_cl); + + return error; +} + +static unsigned deflateFixed(ucvector* out, size_t* bp, Hash* hash, + const unsigned char* data, + size_t datapos, size_t dataend, + const LodePNGCompressSettings* settings, unsigned final) +{ + HuffmanTree tree_ll; /*tree for literal values and length codes*/ + HuffmanTree tree_d; /*tree for distance codes*/ + + unsigned BFINAL = final; + unsigned error = 0; + size_t i; + + HuffmanTree_init(&tree_ll); + HuffmanTree_init(&tree_d); + + generateFixedLitLenTree(&tree_ll); + generateFixedDistanceTree(&tree_d); + + addBitToStream(bp, out, BFINAL); + addBitToStream(bp, out, 1); /*first bit of BTYPE*/ + addBitToStream(bp, out, 0); /*second bit of BTYPE*/ + + if(settings->use_lz77) /*LZ77 encoded*/ + { + uivector lz77_encoded; + uivector_init(&lz77_encoded); + error = encodeLZ77(&lz77_encoded, hash, data, datapos, dataend, settings->windowsize, + settings->minmatch, settings->nicematch, settings->lazymatching); + if(!error) writeLZ77data(bp, out, &lz77_encoded, &tree_ll, &tree_d); + uivector_cleanup(&lz77_encoded); + } + else /*no LZ77, but still will be Huffman compressed*/ + { + for(i = datapos; i < dataend; ++i) + { + addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, data[i]), HuffmanTree_getLength(&tree_ll, data[i])); + } + } + /*add END code*/ + if(!error) addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, 256), HuffmanTree_getLength(&tree_ll, 256)); + + /*cleanup*/ + HuffmanTree_cleanup(&tree_ll); + HuffmanTree_cleanup(&tree_d); + + return error; +} + +static unsigned lodepng_deflatev(ucvector* out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + unsigned error = 0; + size_t i, blocksize, numdeflateblocks; + size_t bp = 0; /*the bit pointer*/ + Hash hash; + + if(settings->btype > 2) return 61; + else if(settings->btype == 0) return deflateNoCompression(out, in, insize); + else if(settings->btype == 1) blocksize = insize; + else /*if(settings->btype == 2)*/ + { + /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ + blocksize = insize / 8 + 8; + if(blocksize < 65536) blocksize = 65536; + if(blocksize > 262144) blocksize = 262144; + } + + numdeflateblocks = (insize + blocksize - 1) / blocksize; + if(numdeflateblocks == 0) numdeflateblocks = 1; + + error = hash_init(&hash, settings->windowsize); + if(error) return error; + + for(i = 0; i != numdeflateblocks && !error; ++i) + { + unsigned final = (i == numdeflateblocks - 1); + size_t start = i * blocksize; + size_t end = start + blocksize; + if(end > insize) end = insize; + + if(settings->btype == 1) error = deflateFixed(out, &bp, &hash, in, start, end, settings, final); + else if(settings->btype == 2) error = deflateDynamic(out, &bp, &hash, in, start, end, settings, final); + } + + hash_cleanup(&hash); + + return error; +} + +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + unsigned error; + ucvector v; + ucvector_init_buffer(&v, *out, *outsize); + error = lodepng_deflatev(&v, in, insize, settings); + *out = v.data; + *outsize = v.size; + return error; +} + +static unsigned deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings) +{ + if(settings->custom_deflate) + { + return settings->custom_deflate(out, outsize, in, insize, settings); + } + else + { + return lodepng_deflate(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Adler32 */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned update_adler32(unsigned adler, const unsigned char* data, unsigned len) +{ + unsigned s1 = adler & 0xffff; + unsigned s2 = (adler >> 16) & 0xffff; + + while(len > 0) + { + /*at least 5550 sums can be done before the sums overflow, saving a lot of module divisions*/ + unsigned amount = len > 5550 ? 5550 : len; + len -= amount; + while(amount > 0) + { + s1 += (*data++); + s2 += s1; + --amount; + } + s1 %= 65521; + s2 %= 65521; + } + + return (s2 << 16) | s1; +} + +/*Return the adler32 of the bytes data[0..len-1]*/ +static unsigned adler32(const unsigned char* data, unsigned len) +{ + return update_adler32(1L, data, len); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Zlib / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_DECODER + +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + unsigned error = 0; + unsigned CM, CINFO, FDICT; + + if(insize < 2) return 53; /*error, size of zlib data too small*/ + /*read information from zlib header*/ + if((in[0] * 256 + in[1]) % 31 != 0) + { + /*error: 256 * in[0] + in[1] must be a multiple of 31, the FCHECK value is supposed to be made that way*/ + return 24; + } + + CM = in[0] & 15; + CINFO = (in[0] >> 4) & 15; + /*FCHECK = in[1] & 31;*/ /*FCHECK is already tested above*/ + FDICT = (in[1] >> 5) & 1; + /*FLEVEL = (in[1] >> 6) & 3;*/ /*FLEVEL is not used here*/ + + if(CM != 8 || CINFO > 7) + { + /*error: only compression method 8: inflate with sliding window of 32k is supported by the PNG spec*/ + return 25; + } + if(FDICT != 0) + { + /*error: the specification of PNG says about the zlib stream: + "The additional flags shall not specify a preset dictionary."*/ + return 26; + } + + error = inflate(out, outsize, in + 2, insize - 2, settings); + if(error) return error; + + if(!settings->ignore_adler32) + { + unsigned ADLER32 = lodepng_read32bitInt(&in[insize - 4]); + unsigned checksum = adler32(*out, (unsigned)(*outsize)); + if(checksum != ADLER32) return 58; /*error, adler checksum not correct, data must be corrupted*/ + } + + return 0; /*no error*/ +} + +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + if(settings->custom_zlib) + { + return settings->custom_zlib(out, outsize, in, insize, settings); + } + else + { + return lodepng_zlib_decompress(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER + +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + /*initially, *out must be NULL and outsize 0, if you just give some random *out + that's pointing to a non allocated buffer, this'll crash*/ + ucvector outv; + size_t i; + unsigned error; + unsigned char* deflatedata = 0; + size_t deflatesize = 0; + + /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ + unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ + unsigned FLEVEL = 0; + unsigned FDICT = 0; + unsigned CMFFLG = 256 * CMF + FDICT * 32 + FLEVEL * 64; + unsigned FCHECK = 31 - CMFFLG % 31; + CMFFLG += FCHECK; + + /*ucvector-controlled version of the output buffer, for dynamic array*/ + ucvector_init_buffer(&outv, *out, *outsize); + + ucvector_push_back(&outv, (unsigned char)(CMFFLG >> 8)); + ucvector_push_back(&outv, (unsigned char)(CMFFLG & 255)); + + error = deflate(&deflatedata, &deflatesize, in, insize, settings); + + if(!error) + { + unsigned ADLER32 = adler32(in, (unsigned)insize); + for(i = 0; i != deflatesize; ++i) ucvector_push_back(&outv, deflatedata[i]); + lodepng_free(deflatedata); + lodepng_add32bitInt(&outv, ADLER32); + } + + *out = outv.data; + *outsize = outv.size; + + return error; +} + +/* compress using the default or custom zlib function */ +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + if(settings->custom_zlib) + { + return settings->custom_zlib(out, outsize, in, insize, settings); + } + else + { + return lodepng_zlib_compress(out, outsize, in, insize, settings); + } +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#else /*no LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DECODER +static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGDecompressSettings* settings) +{ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER +static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, + size_t insize, const LodePNGCompressSettings* settings) +{ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ + return settings->custom_zlib(out, outsize, in, insize, settings); +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#endif /*LODEPNG_COMPILE_ZLIB*/ + +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/*this is a good tradeoff between speed and compression ratio*/ +#define DEFAULT_WINDOWSIZE 2048 + +void lodepng_compress_settings_init(LodePNGCompressSettings* settings) +{ + /*compress with dynamic huffman tree (not in the mathematical sense, just not the predefined one)*/ + settings->btype = 2; + settings->use_lz77 = 1; + settings->windowsize = DEFAULT_WINDOWSIZE; + settings->minmatch = 3; + settings->nicematch = 128; + settings->lazymatching = 1; + + settings->custom_zlib = 0; + settings->custom_deflate = 0; + settings->custom_context = 0; +} + +const LodePNGCompressSettings lodepng_default_compress_settings = {2, 1, DEFAULT_WINDOWSIZE, 3, 128, 1, 0, 0, 0}; + + +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DECODER + +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings) +{ + settings->ignore_adler32 = 0; + + settings->custom_zlib = 0; + settings->custom_inflate = 0; + settings->custom_context = 0; +} + +const LodePNGDecompressSettings lodepng_default_decompress_settings = {0, 0, 0, 0}; + +#endif /*LODEPNG_COMPILE_DECODER*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // End of Zlib related code. Begin of PNG related code. // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_PNG + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / CRC32 / */ +/* ////////////////////////////////////////////////////////////////////////// */ + + +#ifndef LODEPNG_NO_COMPILE_CRC +/* CRC polynomial: 0xedb88320 */ +static unsigned lodepng_crc32_table[256] = { + 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, + 249268274u, 2044508324u, 3772115230u, 2547177864u, 162941995u, 2125561021u, 3887607047u, 2428444049u, + 498536548u, 1789927666u, 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, + 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, 4195302755u, 2366115317u, + 997073096u, 1281953886u, 3579855332u, 2724688242u, 1006888145u, 1258607687u, 3524101629u, 2768942443u, + 901097722u, 1119000684u, 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, + 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, 3485111705u, 3099436303u, + 671266974u, 1594198024u, 3322730930u, 2970347812u, 795835527u, 1483230225u, 3244367275u, 3060149565u, + 1994146192u, 31158534u, 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, + 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, 2439277719u, 3865271297u, + 1802195444u, 476864866u, 2238001368u, 4066508878u, 1812370925u, 453092731u, 2181625025u, 4111451223u, + 1706088902u, 314042704u, 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, + 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, 2765210733u, 3554079995u, + 1131014506u, 879679996u, 2909243462u, 3663771856u, 1141124467u, 855842277u, 2852801631u, 3708648649u, + 1342533948u, 654459306u, 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, + 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, 3082640443u, 3233442989u, + 3988292384u, 2596254646u, 62317068u, 1957810842u, 3939845945u, 2647816111u, 81470997u, 1943803523u, + 3814918930u, 2489596804u, 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, + 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, 426522225u, 1852507879u, + 4275313526u, 2312317920u, 282753626u, 1742555852u, 4189708143u, 2394877945u, 397917763u, 1622183637u, + 3604390888u, 2714866558u, 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, + 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, 829329135u, 1181335161u, + 3412177804u, 3160834842u, 628085408u, 1382605366u, 3423369109u, 3138078467u, 570562233u, 1426400815u, + 3317316542u, 2998733608u, 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, + 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, 1913087877u, 83908371u, + 2512341634u, 3803740692u, 2075208622u, 213261112u, 2463272603u, 3855990285u, 2094854071u, 198958881u, + 2262029012u, 4057260610u, 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, + 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, 1634467795u, 376229701u, + 2685067896u, 3608007406u, 1308918612u, 956543938u, 2808555105u, 3495958263u, 1231636301u, 1047427035u, + 2932959818u, 3654703836u, 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, + 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, 1423857449u, 601450431u, + 3009837614u, 3294710456u, 1567103746u, 711928724u, 3020668471u, 3272380065u, 1510334235u, 755167117u +}; + +/*Return the CRC of the bytes buf[0..len-1].*/ +unsigned lodepng_crc32(const unsigned char* data, size_t length) +{ + unsigned r = 0xffffffffu; + size_t i; + for(i = 0; i < length; ++i) + { + r = lodepng_crc32_table[(r ^ data[i]) & 0xff] ^ (r >> 8); + } + return r ^ 0xffffffffu; +} +#else /* !LODEPNG_NO_COMPILE_CRC */ +unsigned lodepng_crc32(const unsigned char* data, size_t length); +#endif /* !LODEPNG_NO_COMPILE_CRC */ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Reading and writing single bits and bytes from/to stream for LodePNG / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) +{ + unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); + ++(*bitpointer); + return result; +} + +static unsigned readBitsFromReversedStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) +{ + unsigned result = 0; + size_t i; + for(i = 0 ; i < nbits; ++i) + { + result <<= 1; + result |= (unsigned)readBitFromReversedStream(bitpointer, bitstream); + } + return result; +} + +#ifdef LODEPNG_COMPILE_DECODER +static void setBitOfReversedStream0(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) +{ + /*the current bit in bitstream must be 0 for this to work*/ + if(bit) + { + /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ + bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); + } + ++(*bitpointer); +} +#endif /*LODEPNG_COMPILE_DECODER*/ + +static void setBitOfReversedStream(size_t* bitpointer, unsigned char* bitstream, unsigned char bit) +{ + /*the current bit in bitstream may be 0 or 1 for this to work*/ + if(bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7)))); + else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); + ++(*bitpointer); +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG chunks / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +unsigned lodepng_chunk_length(const unsigned char* chunk) +{ + return lodepng_read32bitInt(&chunk[0]); +} + +void lodepng_chunk_type(char type[5], const unsigned char* chunk) +{ + unsigned i; + for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; + type[4] = 0; /*null termination char*/ +} + +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type) +{ + if(strlen(type) != 4) return 0; + return (chunk[4] == type[0] && chunk[5] == type[1] && chunk[6] == type[2] && chunk[7] == type[3]); +} + +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk) +{ + return((chunk[4] & 32) != 0); +} + +unsigned char lodepng_chunk_private(const unsigned char* chunk) +{ + return((chunk[6] & 32) != 0); +} + +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk) +{ + return((chunk[7] & 32) != 0); +} + +unsigned char* lodepng_chunk_data(unsigned char* chunk) +{ + return &chunk[8]; +} + +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk) +{ + return &chunk[8]; +} + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk) +{ + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_read32bitInt(&chunk[length + 8]); + /*the CRC is taken of the data and the 4 chunk type letters, not the length*/ + unsigned checksum = lodepng_crc32(&chunk[4], length + 4); + if(CRC != checksum) return 1; + else return 0; +} + +void lodepng_chunk_generate_crc(unsigned char* chunk) +{ + unsigned length = lodepng_chunk_length(chunk); + unsigned CRC = lodepng_crc32(&chunk[4], length + 4); + lodepng_set32bitInt(chunk + 8 + length, CRC); +} + +unsigned char* lodepng_chunk_next(unsigned char* chunk) +{ + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + return &chunk[total_chunk_length]; +} + +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk) +{ + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + return &chunk[total_chunk_length]; +} + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk) +{ + unsigned i; + unsigned total_chunk_length = lodepng_chunk_length(chunk) + 12; + unsigned char *chunk_start, *new_buffer; + size_t new_length = (*outlength) + total_chunk_length; + if(new_length < total_chunk_length || new_length < (*outlength)) return 77; /*integer overflow happened*/ + + new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); + if(!new_buffer) return 83; /*alloc fail*/ + (*out) = new_buffer; + (*outlength) = new_length; + chunk_start = &(*out)[new_length - total_chunk_length]; + + for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; + + return 0; +} + +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data) +{ + unsigned i; + unsigned char *chunk, *new_buffer; + size_t new_length = (*outlength) + length + 12; + if(new_length < length + 12 || new_length < (*outlength)) return 77; /*integer overflow happened*/ + new_buffer = (unsigned char*)lodepng_realloc(*out, new_length); + if(!new_buffer) return 83; /*alloc fail*/ + (*out) = new_buffer; + (*outlength) = new_length; + chunk = &(*out)[(*outlength) - length - 12]; + + /*1: length*/ + lodepng_set32bitInt(chunk, (unsigned)length); + + /*2: chunk name (4 letters)*/ + chunk[4] = (unsigned char)type[0]; + chunk[5] = (unsigned char)type[1]; + chunk[6] = (unsigned char)type[2]; + chunk[7] = (unsigned char)type[3]; + + /*3: the data*/ + for(i = 0; i != length; ++i) chunk[8 + i] = data[i]; + + /*4: CRC (of the chunkname characters and the data)*/ + lodepng_chunk_generate_crc(chunk); + + return 0; +} + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / Color types and such / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*return type is a LodePNG error code*/ +static unsigned checkColorValidity(LodePNGColorType colortype, unsigned bd) /*bd = bitdepth*/ +{ + switch(colortype) + { + case 0: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 || bd == 16)) return 37; break; /*grey*/ + case 2: if(!( bd == 8 || bd == 16)) return 37; break; /*RGB*/ + case 3: if(!(bd == 1 || bd == 2 || bd == 4 || bd == 8 )) return 37; break; /*palette*/ + case 4: if(!( bd == 8 || bd == 16)) return 37; break; /*grey + alpha*/ + case 6: if(!( bd == 8 || bd == 16)) return 37; break; /*RGBA*/ + default: return 31; + } + return 0; /*allowed color type / bits combination*/ +} + +static unsigned getNumColorChannels(LodePNGColorType colortype) +{ + switch(colortype) + { + case 0: return 1; /*grey*/ + case 2: return 3; /*RGB*/ + case 3: return 1; /*palette*/ + case 4: return 2; /*grey + alpha*/ + case 6: return 4; /*RGBA*/ + } + return 0; /*unexisting color type*/ +} + +static unsigned lodepng_get_bpp_lct(LodePNGColorType colortype, unsigned bitdepth) +{ + /*bits per pixel is amount of channels * bits per channel*/ + return getNumColorChannels(colortype) * bitdepth; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +void lodepng_color_mode_init(LodePNGColorMode* info) +{ + info->key_defined = 0; + info->key_r = info->key_g = info->key_b = 0; + info->colortype = LCT_RGBA; + info->bitdepth = 8; + info->palette = 0; + info->palettesize = 0; +} + +void lodepng_color_mode_cleanup(LodePNGColorMode* info) +{ + lodepng_palette_clear(info); +} + +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source) +{ + size_t i; + lodepng_color_mode_cleanup(dest); + *dest = *source; + if(source->palette) + { + dest->palette = (unsigned char*)lodepng_malloc(1024); + if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ + for(i = 0; i != source->palettesize * 4; ++i) dest->palette[i] = source->palette[i]; + } + return 0; +} + +static int lodepng_color_mode_equal(const LodePNGColorMode* a, const LodePNGColorMode* b) +{ + size_t i; + if(a->colortype != b->colortype) return 0; + if(a->bitdepth != b->bitdepth) return 0; + if(a->key_defined != b->key_defined) return 0; + if(a->key_defined) + { + if(a->key_r != b->key_r) return 0; + if(a->key_g != b->key_g) return 0; + if(a->key_b != b->key_b) return 0; + } + /*if one of the palette sizes is 0, then we consider it to be the same as the + other: it means that e.g. the palette was not given by the user and should be + considered the same as the palette inside the PNG.*/ + if(1/*a->palettesize != 0 && b->palettesize != 0*/) { + if(a->palettesize != b->palettesize) return 0; + for(i = 0; i != a->palettesize * 4; ++i) + { + if(a->palette[i] != b->palette[i]) return 0; + } + } + return 1; +} + +void lodepng_palette_clear(LodePNGColorMode* info) +{ + if(info->palette) lodepng_free(info->palette); + info->palette = 0; + info->palettesize = 0; +} + +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + unsigned char* data; + /*the same resize technique as C++ std::vectors is used, and here it's made so that for a palette with + the max of 256 colors, it'll have the exact alloc size*/ + if(!info->palette) /*allocate palette if empty*/ + { + /*room for 256 colors with 4 bytes each*/ + data = (unsigned char*)lodepng_realloc(info->palette, 1024); + if(!data) return 83; /*alloc fail*/ + else info->palette = data; + } + info->palette[4 * info->palettesize + 0] = r; + info->palette[4 * info->palettesize + 1] = g; + info->palette[4 * info->palettesize + 2] = b; + info->palette[4 * info->palettesize + 3] = a; + ++info->palettesize; + return 0; +} + +unsigned lodepng_get_bpp(const LodePNGColorMode* info) +{ + /*calculate bits per pixel out of colortype and bitdepth*/ + return lodepng_get_bpp_lct(info->colortype, info->bitdepth); +} + +unsigned lodepng_get_channels(const LodePNGColorMode* info) +{ + return getNumColorChannels(info->colortype); +} + +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info) +{ + return info->colortype == LCT_GREY || info->colortype == LCT_GREY_ALPHA; +} + +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info) +{ + return (info->colortype & 4) != 0; /*4 or 6*/ +} + +unsigned lodepng_is_palette_type(const LodePNGColorMode* info) +{ + return info->colortype == LCT_PALETTE; +} + +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) +{ + size_t i; + for(i = 0; i != info->palettesize; ++i) + { + if(info->palette[i * 4 + 3] < 255) return 1; + } + return 0; +} + +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info) +{ + return info->key_defined + || lodepng_is_alpha_type(info) + || lodepng_has_palette_alpha(info); +} + +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) +{ + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp(color); + size_t n = w * h; + return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; +} + +size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) +{ + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); + size_t n = w * h; + return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; +} + + +#ifdef LODEPNG_COMPILE_PNG +#ifdef LODEPNG_COMPILE_DECODER +/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer*/ +static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) +{ + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp(color); + size_t line = ((w / 8) * bpp) + ((w & 7) * bpp + 7) / 8; + return h * line; +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static void LodePNGUnknownChunks_init(LodePNGInfo* info) +{ + unsigned i; + for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; + for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; +} + +static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) +{ + unsigned i; + for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); +} + +static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) +{ + unsigned i; + + LodePNGUnknownChunks_cleanup(dest); + + for(i = 0; i != 3; ++i) + { + size_t j; + dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; + dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); + if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ + for(j = 0; j < src->unknown_chunks_size[i]; ++j) + { + dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; + } + } + + return 0; +} + +/******************************************************************************/ + +static void LodePNGText_init(LodePNGInfo* info) +{ + info->text_num = 0; + info->text_keys = NULL; + info->text_strings = NULL; +} + +static void LodePNGText_cleanup(LodePNGInfo* info) +{ + size_t i; + for(i = 0; i != info->text_num; ++i) + { + string_cleanup(&info->text_keys[i]); + string_cleanup(&info->text_strings[i]); + } + lodepng_free(info->text_keys); + lodepng_free(info->text_strings); +} + +static unsigned LodePNGText_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + size_t i = 0; + dest->text_keys = 0; + dest->text_strings = 0; + dest->text_num = 0; + for(i = 0; i != source->text_num; ++i) + { + CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); + } + return 0; +} + +void lodepng_clear_text(LodePNGInfo* info) +{ + LodePNGText_cleanup(info); +} + +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str) +{ + char** new_keys = (char**)(lodepng_realloc(info->text_keys, sizeof(char*) * (info->text_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->text_strings, sizeof(char*) * (info->text_num + 1))); + if(!new_keys || !new_strings) + { + lodepng_free(new_keys); + lodepng_free(new_strings); + return 83; /*alloc fail*/ + } + + ++info->text_num; + info->text_keys = new_keys; + info->text_strings = new_strings; + + string_init(&info->text_keys[info->text_num - 1]); + string_set(&info->text_keys[info->text_num - 1], key); + + string_init(&info->text_strings[info->text_num - 1]); + string_set(&info->text_strings[info->text_num - 1], str); + + return 0; +} + +/******************************************************************************/ + +static void LodePNGIText_init(LodePNGInfo* info) +{ + info->itext_num = 0; + info->itext_keys = NULL; + info->itext_langtags = NULL; + info->itext_transkeys = NULL; + info->itext_strings = NULL; +} + +static void LodePNGIText_cleanup(LodePNGInfo* info) +{ + size_t i; + for(i = 0; i != info->itext_num; ++i) + { + string_cleanup(&info->itext_keys[i]); + string_cleanup(&info->itext_langtags[i]); + string_cleanup(&info->itext_transkeys[i]); + string_cleanup(&info->itext_strings[i]); + } + lodepng_free(info->itext_keys); + lodepng_free(info->itext_langtags); + lodepng_free(info->itext_transkeys); + lodepng_free(info->itext_strings); +} + +static unsigned LodePNGIText_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + size_t i = 0; + dest->itext_keys = 0; + dest->itext_langtags = 0; + dest->itext_transkeys = 0; + dest->itext_strings = 0; + dest->itext_num = 0; + for(i = 0; i != source->itext_num; ++i) + { + CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], + source->itext_transkeys[i], source->itext_strings[i])); + } + return 0; +} + +void lodepng_clear_itext(LodePNGInfo* info) +{ + LodePNGIText_cleanup(info); +} + +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str) +{ + char** new_keys = (char**)(lodepng_realloc(info->itext_keys, sizeof(char*) * (info->itext_num + 1))); + char** new_langtags = (char**)(lodepng_realloc(info->itext_langtags, sizeof(char*) * (info->itext_num + 1))); + char** new_transkeys = (char**)(lodepng_realloc(info->itext_transkeys, sizeof(char*) * (info->itext_num + 1))); + char** new_strings = (char**)(lodepng_realloc(info->itext_strings, sizeof(char*) * (info->itext_num + 1))); + if(!new_keys || !new_langtags || !new_transkeys || !new_strings) + { + lodepng_free(new_keys); + lodepng_free(new_langtags); + lodepng_free(new_transkeys); + lodepng_free(new_strings); + return 83; /*alloc fail*/ + } + + ++info->itext_num; + info->itext_keys = new_keys; + info->itext_langtags = new_langtags; + info->itext_transkeys = new_transkeys; + info->itext_strings = new_strings; + + string_init(&info->itext_keys[info->itext_num - 1]); + string_set(&info->itext_keys[info->itext_num - 1], key); + + string_init(&info->itext_langtags[info->itext_num - 1]); + string_set(&info->itext_langtags[info->itext_num - 1], langtag); + + string_init(&info->itext_transkeys[info->itext_num - 1]); + string_set(&info->itext_transkeys[info->itext_num - 1], transkey); + + string_init(&info->itext_strings[info->itext_num - 1]); + string_set(&info->itext_strings[info->itext_num - 1], str); + + return 0; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +void lodepng_info_init(LodePNGInfo* info) +{ + lodepng_color_mode_init(&info->color); + info->interlace_method = 0; + info->compression_method = 0; + info->filter_method = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + info->background_defined = 0; + info->background_r = info->background_g = info->background_b = 0; + + LodePNGText_init(info); + LodePNGIText_init(info); + + info->time_defined = 0; + info->phys_defined = 0; + + LodePNGUnknownChunks_init(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +void lodepng_info_cleanup(LodePNGInfo* info) +{ + lodepng_color_mode_cleanup(&info->color); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + LodePNGText_cleanup(info); + LodePNGIText_cleanup(info); + + LodePNGUnknownChunks_cleanup(info); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source) +{ + lodepng_info_cleanup(dest); + *dest = *source; + lodepng_color_mode_init(&dest->color); + CERROR_TRY_RETURN(lodepng_color_mode_copy(&dest->color, &source->color)); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + CERROR_TRY_RETURN(LodePNGText_copy(dest, source)); + CERROR_TRY_RETURN(LodePNGIText_copy(dest, source)); + + LodePNGUnknownChunks_init(dest); + CERROR_TRY_RETURN(LodePNGUnknownChunks_copy(dest, source)); +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + return 0; +} + +void lodepng_info_swap(LodePNGInfo* a, LodePNGInfo* b) +{ + LodePNGInfo temp = *a; + *a = *b; + *b = temp; +} + +/* ////////////////////////////////////////////////////////////////////////// */ + +/*index: bitgroup index, bits: bitgroup size(1, 2 or 4), in: bitgroup value, out: octet array to add bits to*/ +static void addColorBits(unsigned char* out, size_t index, unsigned bits, unsigned in) +{ + unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ + /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ + unsigned p = index & m; + in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ + in = in << (bits * (m - p)); + if(p == 0) out[index * bits / 8] = in; + else out[index * bits / 8] |= in; +} + +typedef struct ColorTree ColorTree; + +/* +One node of a color tree +This is the data structure used to count the number of unique colors and to get a palette +index for a color. It's like an octree, but because the alpha channel is used too, each +node has 16 instead of 8 children. +*/ +struct ColorTree +{ + ColorTree* children[16]; /*up to 16 pointers to ColorTree of next level*/ + int index; /*the payload. Only has a meaningful value if this is in the last level*/ +}; + +static void color_tree_init(ColorTree* tree) +{ + int i; + for(i = 0; i != 16; ++i) tree->children[i] = 0; + tree->index = -1; +} + +static void color_tree_cleanup(ColorTree* tree) +{ + int i; + for(i = 0; i != 16; ++i) + { + if(tree->children[i]) + { + color_tree_cleanup(tree->children[i]); + lodepng_free(tree->children[i]); + } + } +} + +/*returns -1 if color not present, its index otherwise*/ +static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + int bit = 0; + for(bit = 0; bit < 8; ++bit) + { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) return -1; + else tree = tree->children[i]; + } + return tree ? tree->index : -1; +} + +#ifdef LODEPNG_COMPILE_ENCODER +static int color_tree_has(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return color_tree_get(tree, r, g, b, a) >= 0; +} +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/*color is not allowed to already exist. +Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")*/ +static void color_tree_add(ColorTree* tree, + unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) +{ + int bit; + for(bit = 0; bit < 8; ++bit) + { + int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); + if(!tree->children[i]) + { + tree->children[i] = (ColorTree*)lodepng_malloc(sizeof(ColorTree)); + color_tree_init(tree->children[i]); + } + tree = tree->children[i]; + } + tree->index = (int)index; +} + +/*put a pixel, given its RGBA color, into image of any color type*/ +static unsigned rgba8ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, ColorTree* tree /*for palette*/, + unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + if(mode->colortype == LCT_GREY) + { + unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; + if(mode->bitdepth == 8) out[i] = grey; + else if(mode->bitdepth == 16) out[i * 2 + 0] = out[i * 2 + 1] = grey; + else + { + /*take the most significant bits of grey*/ + grey = (grey >> (8 - mode->bitdepth)) & ((1 << mode->bitdepth) - 1); + addColorBits(out, i, mode->bitdepth, grey); + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + out[i * 3 + 0] = r; + out[i * 3 + 1] = g; + out[i * 3 + 2] = b; + } + else + { + out[i * 6 + 0] = out[i * 6 + 1] = r; + out[i * 6 + 2] = out[i * 6 + 3] = g; + out[i * 6 + 4] = out[i * 6 + 5] = b; + } + } + else if(mode->colortype == LCT_PALETTE) + { + int index = color_tree_get(tree, r, g, b, a); + if(index < 0) return 82; /*color not in palette*/ + if(mode->bitdepth == 8) out[i] = index; + else addColorBits(out, i, mode->bitdepth, (unsigned)index); + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + unsigned char grey = r; /*((unsigned short)r + g + b) / 3*/; + if(mode->bitdepth == 8) + { + out[i * 2 + 0] = grey; + out[i * 2 + 1] = a; + } + else if(mode->bitdepth == 16) + { + out[i * 4 + 0] = out[i * 4 + 1] = grey; + out[i * 4 + 2] = out[i * 4 + 3] = a; + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + out[i * 4 + 0] = r; + out[i * 4 + 1] = g; + out[i * 4 + 2] = b; + out[i * 4 + 3] = a; + } + else + { + out[i * 8 + 0] = out[i * 8 + 1] = r; + out[i * 8 + 2] = out[i * 8 + 3] = g; + out[i * 8 + 4] = out[i * 8 + 5] = b; + out[i * 8 + 6] = out[i * 8 + 7] = a; + } + } + + return 0; /*no error*/ +} + +/*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/ +static void rgba16ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, + unsigned short r, unsigned short g, unsigned short b, unsigned short a) +{ + if(mode->colortype == LCT_GREY) + { + unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; + out[i * 2 + 0] = (grey >> 8) & 255; + out[i * 2 + 1] = grey & 255; + } + else if(mode->colortype == LCT_RGB) + { + out[i * 6 + 0] = (r >> 8) & 255; + out[i * 6 + 1] = r & 255; + out[i * 6 + 2] = (g >> 8) & 255; + out[i * 6 + 3] = g & 255; + out[i * 6 + 4] = (b >> 8) & 255; + out[i * 6 + 5] = b & 255; + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; + out[i * 4 + 0] = (grey >> 8) & 255; + out[i * 4 + 1] = grey & 255; + out[i * 4 + 2] = (a >> 8) & 255; + out[i * 4 + 3] = a & 255; + } + else if(mode->colortype == LCT_RGBA) + { + out[i * 8 + 0] = (r >> 8) & 255; + out[i * 8 + 1] = r & 255; + out[i * 8 + 2] = (g >> 8) & 255; + out[i * 8 + 3] = g & 255; + out[i * 8 + 4] = (b >> 8) & 255; + out[i * 8 + 5] = b & 255; + out[i * 8 + 6] = (a >> 8) & 255; + out[i * 8 + 7] = a & 255; + } +} + +/*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/ +static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, + unsigned char* b, unsigned char* a, + const unsigned char* in, size_t i, + const LodePNGColorMode* mode) +{ + if(mode->colortype == LCT_GREY) + { + if(mode->bitdepth == 8) + { + *r = *g = *b = in[i]; + if(mode->key_defined && *r == mode->key_r) *a = 0; + else *a = 255; + } + else if(mode->bitdepth == 16) + { + *r = *g = *b = in[i * 2 + 0]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 255; + } + else + { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = i * mode->bitdepth; + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + *r = *g = *b = (value * 255) / highest; + if(mode->key_defined && value == mode->key_r) *a = 0; + else *a = 255; + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + *r = in[i * 3 + 0]; *g = in[i * 3 + 1]; *b = in[i * 3 + 2]; + if(mode->key_defined && *r == mode->key_r && *g == mode->key_g && *b == mode->key_b) *a = 0; + else *a = 255; + } + else + { + *r = in[i * 6 + 0]; + *g = in[i * 6 + 2]; + *b = in[i * 6 + 4]; + if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 255; + } + } + else if(mode->colortype == LCT_PALETTE) + { + unsigned index; + if(mode->bitdepth == 8) index = in[i]; + else + { + size_t j = i * mode->bitdepth; + index = readBitsFromReversedStream(&j, in, mode->bitdepth); + } + + if(index >= mode->palettesize) + { + /*This is an error according to the PNG spec, but common PNG decoders make it black instead. + Done here too, slightly faster due to no error handling needed.*/ + *r = *g = *b = 0; + *a = 255; + } + else + { + *r = mode->palette[index * 4 + 0]; + *g = mode->palette[index * 4 + 1]; + *b = mode->palette[index * 4 + 2]; + *a = mode->palette[index * 4 + 3]; + } + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + if(mode->bitdepth == 8) + { + *r = *g = *b = in[i * 2 + 0]; + *a = in[i * 2 + 1]; + } + else + { + *r = *g = *b = in[i * 4 + 0]; + *a = in[i * 4 + 2]; + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + *r = in[i * 4 + 0]; + *g = in[i * 4 + 1]; + *b = in[i * 4 + 2]; + *a = in[i * 4 + 3]; + } + else + { + *r = in[i * 8 + 0]; + *g = in[i * 8 + 2]; + *b = in[i * 8 + 4]; + *a = in[i * 8 + 6]; + } + } +} + +/*Similar to getPixelColorRGBA8, but with all the for loops inside of the color +mode test cases, optimized to convert the colors much faster, when converting +to RGBA or RGB with 8 bit per cannel. buffer must be RGBA or RGB output with +enough memory, if has_alpha is true the output is RGBA. mode has the color mode +of the input buffer.*/ +static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, + unsigned has_alpha, const unsigned char* in, + const LodePNGColorMode* mode) +{ + unsigned num_channels = has_alpha ? 4 : 3; + size_t i; + if(mode->colortype == LCT_GREY) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i]; + if(has_alpha) buffer[3] = mode->key_defined && in[i] == mode->key_r ? 0 : 255; + } + } + else if(mode->bitdepth == 16) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 2]; + if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; + } + } + else + { + unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); + buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; + if(has_alpha) buffer[3] = mode->key_defined && value == mode->key_r ? 0 : 255; + } + } + } + else if(mode->colortype == LCT_RGB) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 3 + 0]; + buffer[1] = in[i * 3 + 1]; + buffer[2] = in[i * 3 + 2]; + if(has_alpha) buffer[3] = mode->key_defined && buffer[0] == mode->key_r + && buffer[1]== mode->key_g && buffer[2] == mode->key_b ? 0 : 255; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 6 + 0]; + buffer[1] = in[i * 6 + 2]; + buffer[2] = in[i * 6 + 4]; + if(has_alpha) buffer[3] = mode->key_defined + && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b ? 0 : 255; + } + } + } + else if(mode->colortype == LCT_PALETTE) + { + unsigned index; + size_t j = 0; + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + if(mode->bitdepth == 8) index = in[i]; + else index = readBitsFromReversedStream(&j, in, mode->bitdepth); + + if(index >= mode->palettesize) + { + /*This is an error according to the PNG spec, but most PNG decoders make it black instead. + Done here too, slightly faster due to no error handling needed.*/ + buffer[0] = buffer[1] = buffer[2] = 0; + if(has_alpha) buffer[3] = 255; + } + else + { + buffer[0] = mode->palette[index * 4 + 0]; + buffer[1] = mode->palette[index * 4 + 1]; + buffer[2] = mode->palette[index * 4 + 2]; + if(has_alpha) buffer[3] = mode->palette[index * 4 + 3]; + } + } + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; + if(has_alpha) buffer[3] = in[i * 2 + 1]; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; + if(has_alpha) buffer[3] = in[i * 4 + 2]; + } + } + } + else if(mode->colortype == LCT_RGBA) + { + if(mode->bitdepth == 8) + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 4 + 0]; + buffer[1] = in[i * 4 + 1]; + buffer[2] = in[i * 4 + 2]; + if(has_alpha) buffer[3] = in[i * 4 + 3]; + } + } + else + { + for(i = 0; i != numpixels; ++i, buffer += num_channels) + { + buffer[0] = in[i * 8 + 0]; + buffer[1] = in[i * 8 + 2]; + buffer[2] = in[i * 8 + 4]; + if(has_alpha) buffer[3] = in[i * 8 + 6]; + } + } + } +} + +/*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with +given color type, but the given color type must be 16-bit itself.*/ +static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, + const unsigned char* in, size_t i, const LodePNGColorMode* mode) +{ + if(mode->colortype == LCT_GREY) + { + *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; + if(mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r) *a = 0; + else *a = 65535; + } + else if(mode->colortype == LCT_RGB) + { + *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; + *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; + *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; + if(mode->key_defined + && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + else *a = 65535; + } + else if(mode->colortype == LCT_GREY_ALPHA) + { + *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; + *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; + } + else if(mode->colortype == LCT_RGBA) + { + *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; + *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; + *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; + *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; + } +} + +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h) +{ + size_t i; + ColorTree tree; + size_t numpixels = w * h; + unsigned error = 0; + + if(lodepng_color_mode_equal(mode_out, mode_in)) + { + size_t numbytes = lodepng_get_raw_size(w, h, mode_in); + for(i = 0; i != numbytes; ++i) out[i] = in[i]; + return 0; + } + + if(mode_out->colortype == LCT_PALETTE) + { + size_t palettesize = mode_out->palettesize; + const unsigned char* palette = mode_out->palette; + size_t palsize = 1u << mode_out->bitdepth; + /*if the user specified output palette but did not give the values, assume + they want the values of the input color type (assuming that one is palette). + Note that we never create a new palette ourselves.*/ + if(palettesize == 0) + { + palettesize = mode_in->palettesize; + palette = mode_in->palette; + } + if(palettesize < palsize) palsize = palettesize; + color_tree_init(&tree); + for(i = 0; i != palsize; ++i) + { + const unsigned char* p = &palette[i * 4]; + color_tree_add(&tree, p[0], p[1], p[2], p[3], i); + } + } + + if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) + { + for(i = 0; i != numpixels; ++i) + { + unsigned short r = 0, g = 0, b = 0, a = 0; + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + rgba16ToPixel(out, i, mode_out, r, g, b, a); + } + } + else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) + { + getPixelColorsRGBA8(out, numpixels, 1, in, mode_in); + } + else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) + { + getPixelColorsRGBA8(out, numpixels, 0, in, mode_in); + } + else + { + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); + error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); + if (error) break; + } + } + + if(mode_out->colortype == LCT_PALETTE) + { + color_tree_cleanup(&tree); + } + + return error; +} + +#ifdef LODEPNG_COMPILE_ENCODER + +void lodepng_color_profile_init(LodePNGColorProfile* profile) +{ + profile->colored = 0; + profile->key = 0; + profile->key_r = profile->key_g = profile->key_b = 0; + profile->alpha = 0; + profile->numcolors = 0; + profile->bits = 1; +} + +/*function used for debug purposes with C++*/ +/*void printColorProfile(LodePNGColorProfile* p) +{ + std::cout << "colored: " << (int)p->colored << ", "; + std::cout << "key: " << (int)p->key << ", "; + std::cout << "key_r: " << (int)p->key_r << ", "; + std::cout << "key_g: " << (int)p->key_g << ", "; + std::cout << "key_b: " << (int)p->key_b << ", "; + std::cout << "alpha: " << (int)p->alpha << ", "; + std::cout << "numcolors: " << (int)p->numcolors << ", "; + std::cout << "bits: " << (int)p->bits << std::endl; +}*/ + +/*Returns how many bits needed to represent given value (max 8 bit)*/ +static unsigned getValueRequiredBits(unsigned char value) +{ + if(value == 0 || value == 255) return 1; + /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ + if(value % 17 == 0) return value % 85 == 0 ? 2 : 4; + return 8; +} + +/*profile must already have been inited with mode. +It's ok to set some parameters of profile to done already.*/ +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* mode) +{ + unsigned error = 0; + size_t i; + ColorTree tree; + size_t numpixels = w * h; + + unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; + unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; + unsigned numcolors_done = 0; + unsigned bpp = lodepng_get_bpp(mode); + unsigned bits_done = bpp == 1 ? 1 : 0; + unsigned maxnumcolors = 257; + unsigned sixteen = 0; + if(bpp <= 8) maxnumcolors = bpp == 1 ? 2 : (bpp == 2 ? 4 : (bpp == 4 ? 16 : 256)); + + color_tree_init(&tree); + + /*Check if the 16-bit input is truly 16-bit*/ + if(mode->bitdepth == 16) + { + unsigned short r, g, b, a; + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || + (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ + { + sixteen = 1; + break; + } + } + } + + if(sixteen) + { + unsigned short r = 0, g = 0, b = 0, a = 0; + profile->bits = 16; + bits_done = numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ + + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + + if(!colored_done && (r != g || r != b)) + { + profile->colored = 1; + colored_done = 1; + } + + if(!alpha_done) + { + unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); + if(a != 65535 && (a != 0 || (profile->key && !matchkey))) + { + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + } + else if(a == 0 && !profile->alpha && !profile->key) + { + profile->key = 1; + profile->key_r = r; + profile->key_g = g; + profile->key_b = b; + } + else if(a == 65535 && profile->key && matchkey) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + } + } + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + if(profile->key && !profile->alpha) + { + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + } + } + } + } + else /* < 16-bit */ + { + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); + + if(!bits_done && profile->bits < 8) + { + /*only r is checked, < 8 bits is only relevant for greyscale*/ + unsigned bits = getValueRequiredBits(r); + if(bits > profile->bits) profile->bits = bits; + } + bits_done = (profile->bits >= bpp); + + if(!colored_done && (r != g || r != b)) + { + profile->colored = 1; + colored_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/ + } + + if(!alpha_done) + { + unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); + if(a != 255 && (a != 0 || (profile->key && !matchkey))) + { + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + else if(a == 0 && !profile->alpha && !profile->key) + { + profile->key = 1; + profile->key_r = r; + profile->key_g = g; + profile->key_b = b; + } + else if(a == 255 && profile->key && matchkey) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + } + + if(!numcolors_done) + { + if(!color_tree_has(&tree, r, g, b, a)) + { + color_tree_add(&tree, r, g, b, a, profile->numcolors); + if(profile->numcolors < 256) + { + unsigned char* p = profile->palette; + unsigned n = profile->numcolors; + p[n * 4 + 0] = r; + p[n * 4 + 1] = g; + p[n * 4 + 2] = b; + p[n * 4 + 3] = a; + } + ++profile->numcolors; + numcolors_done = profile->numcolors >= maxnumcolors; + } + } + + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + if(profile->key && !profile->alpha) + { + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); + if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + profile->key = 0; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + } + } + + /*make the profile's key always 16-bit for consistency - repeat each byte twice*/ + profile->key_r += (profile->key_r << 8); + profile->key_g += (profile->key_g << 8); + profile->key_b += (profile->key_b << 8); + } + + color_tree_cleanup(&tree); + return error; +} + +/*Automatically chooses color type that gives smallest amount of bits in the +output image, e.g. grey if there are only greyscale pixels, palette if there +are less than 256 colors, ... +Updates values of mode with a potentially smaller color model. mode_out should +contain the user chosen color model, but will be overwritten with the new chosen one.*/ +unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in) +{ + LodePNGColorProfile prof; + unsigned error = 0; + unsigned i, n, palettebits, palette_ok; + + lodepng_color_profile_init(&prof); + error = lodepng_get_color_profile(&prof, image, w, h, mode_in); + if(error) return error; + mode_out->key_defined = 0; + + if(prof.key && w * h <= 16) + { + prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ + prof.key = 0; + if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } + n = prof.numcolors; + palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); + palette_ok = n <= 256 && prof.bits <= 8; + if(w * h < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ + if(!prof.colored && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/ + + if(palette_ok) + { + unsigned char* p = prof.palette; + lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ + for(i = 0; i != prof.numcolors; ++i) + { + error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); + if(error) break; + } + + mode_out->colortype = LCT_PALETTE; + mode_out->bitdepth = palettebits; + + if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize + && mode_in->bitdepth == mode_out->bitdepth) + { + /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/ + lodepng_color_mode_cleanup(mode_out); + lodepng_color_mode_copy(mode_out, mode_in); + } + } + else /*8-bit or 16-bit per channel*/ + { + mode_out->bitdepth = prof.bits; + mode_out->colortype = prof.alpha ? (prof.colored ? LCT_RGBA : LCT_GREY_ALPHA) + : (prof.colored ? LCT_RGB : LCT_GREY); + + if(prof.key) + { + unsigned mask = (1u << mode_out->bitdepth) - 1u; /*profile always uses 16-bit, mask converts it*/ + mode_out->key_r = prof.key_r & mask; + mode_out->key_g = prof.key_g & mask; + mode_out->key_b = prof.key_b & mask; + mode_out->key_defined = 1; + } + } + + return error; +} + +#endif /* #ifdef LODEPNG_COMPILE_ENCODER */ + +/* +Paeth predicter, used by PNG filter type 4 +The parameters are of type short, but should come from unsigned chars, the shorts +are only needed to make the paeth calculation correct. +*/ +static unsigned char paethPredictor(short a, short b, short c) +{ + short pa = abs(b - c); + short pb = abs(a - c); + short pc = abs(a + b - c - c); + + if(pc < pa && pc < pb) return (unsigned char)c; + else if(pb < pa) return (unsigned char)b; + else return (unsigned char)a; +} + +/*shared values used by multiple Adam7 related functions*/ + +static const unsigned ADAM7_IX[7] = { 0, 4, 0, 2, 0, 1, 0 }; /*x start values*/ +static const unsigned ADAM7_IY[7] = { 0, 0, 4, 0, 2, 0, 1 }; /*y start values*/ +static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ +static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ + +/* +Outputs various dimensions and positions in the image related to the Adam7 reduced images. +passw: output containing the width of the 7 passes +passh: output containing the height of the 7 passes +filter_passstart: output containing the index of the start and end of each + reduced image with filter bytes +padded_passstart output containing the index of the start and end of each + reduced image when without filter bytes but with padded scanlines +passstart: output containing the index of the start and end of each reduced + image without padding between scanlines, but still padding between the images +w, h: width and height of non-interlaced image +bpp: bits per pixel +"padded" is only relevant if bpp is less than 8 and a scanline or image does not + end at a full byte +*/ +static void Adam7_getpassvalues(unsigned passw[7], unsigned passh[7], size_t filter_passstart[8], + size_t padded_passstart[8], size_t passstart[8], unsigned w, unsigned h, unsigned bpp) +{ + /*the passstart values have 8 values: the 8th one indicates the byte after the end of the 7th (= last) pass*/ + unsigned i; + + /*calculate width and height in pixels of each pass*/ + for(i = 0; i != 7; ++i) + { + passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; + passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; + if(passw[i] == 0) passh[i] = 0; + if(passh[i] == 0) passw[i] = 0; + } + + filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; + for(i = 0; i != 7; ++i) + { + /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ + filter_passstart[i + 1] = filter_passstart[i] + + ((passw[i] && passh[i]) ? passh[i] * (1 + (passw[i] * bpp + 7) / 8) : 0); + /*bits padded if needed to fill full byte at end of each scanline*/ + padded_passstart[i + 1] = padded_passstart[i] + passh[i] * ((passw[i] * bpp + 7) / 8); + /*only padded at end of reduced image*/ + passstart[i + 1] = passstart[i] + (passh[i] * passw[i] * bpp + 7) / 8; + } +} + +#ifdef LODEPNG_COMPILE_DECODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Decoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*read the information from the header and store it in the LodePNGInfo. return value is error*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, LodePNGState* state, + const unsigned char* in, size_t insize) +{ + LodePNGInfo* info = &state->info_png; + if(insize == 0 || in == 0) + { + CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ + } + if(insize < 33) + { + CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ + } + + /*when decoding a new PNG image, make sure all parameters created after previous decoding are reset*/ + lodepng_info_cleanup(info); + lodepng_info_init(info); + + if(in[0] != 137 || in[1] != 80 || in[2] != 78 || in[3] != 71 + || in[4] != 13 || in[5] != 10 || in[6] != 26 || in[7] != 10) + { + CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ + } + if(lodepng_chunk_length(in + 8) != 13) + { + CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ + } + if(!lodepng_chunk_type_equals(in + 8, "IHDR")) + { + CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ + } + + /*read the values given in the header*/ + *w = lodepng_read32bitInt(&in[16]); + *h = lodepng_read32bitInt(&in[20]); + info->color.bitdepth = in[24]; + info->color.colortype = (LodePNGColorType)in[25]; + info->compression_method = in[26]; + info->filter_method = in[27]; + info->interlace_method = in[28]; + + if(*w == 0 || *h == 0) + { + CERROR_RETURN_ERROR(state->error, 93); + } + + if(!state->decoder.ignore_crc) + { + unsigned CRC = lodepng_read32bitInt(&in[29]); + unsigned checksum = lodepng_crc32(&in[12], 17); + if(CRC != checksum) + { + CERROR_RETURN_ERROR(state->error, 57); /*invalid CRC*/ + } + } + + /*error: only compression method 0 is allowed in the specification*/ + if(info->compression_method != 0) CERROR_RETURN_ERROR(state->error, 32); + /*error: only filter method 0 is allowed in the specification*/ + if(info->filter_method != 0) CERROR_RETURN_ERROR(state->error, 33); + /*error: only interlace methods 0 and 1 exist in the specification*/ + if(info->interlace_method > 1) CERROR_RETURN_ERROR(state->error, 34); + + state->error = checkColorValidity(info->color.colortype, info->color.bitdepth); + return state->error; +} + +static unsigned unfilterScanline(unsigned char* recon, const unsigned char* scanline, const unsigned char* precon, + size_t bytewidth, unsigned char filterType, size_t length) +{ + /* + For PNG filter method 0 + unfilter a PNG image scanline by scanline. when the pixels are smaller than 1 byte, + the filter works byte per byte (bytewidth = 1) + precon is the previous unfiltered scanline, recon the result, scanline the current one + the incoming scanlines do NOT include the filtertype byte, that one is given in the parameter filterType instead + recon and scanline MAY be the same memory address! precon must be disjoint. + */ + + size_t i; + switch(filterType) + { + case 0: + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + break; + case 1: + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; + break; + case 2: + if(precon) + { + for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; + } + else + { + for(i = 0; i != length; ++i) recon[i] = scanline[i]; + } + break; + case 3: + if(precon) + { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1); + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1); + } + else + { + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1); + } + break; + case 4: + if(precon) + { + for(i = 0; i != bytewidth; ++i) + { + recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ + } + for(i = bytewidth; i < length; ++i) + { + recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); + } + } + else + { + for(i = 0; i != bytewidth; ++i) + { + recon[i] = scanline[i]; + } + for(i = bytewidth; i < length; ++i) + { + /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ + recon[i] = (scanline[i] + recon[i - bytewidth]); + } + } + break; + default: return 36; /*error: unexisting filter type given*/ + } + return 0; +} + +static unsigned unfilter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + /* + For PNG filter method 0 + this function unfilters a single image (e.g. without interlacing this is called once, with Adam7 seven times) + out must have enough bytes allocated already, in must have the scanlines + 1 filtertype byte per scanline + w and h are image dimensions or dimensions of reduced image, bpp is bits per pixel + in and out are allowed to be the same memory address (but aren't the same size since in has the extra filter bytes) + */ + + unsigned y; + unsigned char* prevline = 0; + + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7) / 8; + size_t linebytes = (w * bpp + 7) / 8; + + for(y = 0; y < h; ++y) + { + size_t outindex = linebytes * y; + size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + unsigned char filterType = in[inindex]; + + CERROR_TRY_RETURN(unfilterScanline(&out[outindex], &in[inindex + 1], prevline, bytewidth, filterType, linebytes)); + + prevline = &out[outindex]; + } + + return 0; +} + +/* +in: Adam7 interlaced image, with no padding bits between scanlines, but between + reduced images so that each reduced image starts at a byte. +out: the same pixels, but re-ordered so that they're now a non-interlaced image with size w*h +bpp: bits per pixel +out has the following size in bits: w * h * bpp. +in is possibly bigger due to padding bits between reduced images. +out must be big enough AND must be 0 everywhere if bpp < 8 in the current implementation +(because that's likely a little bit faster) +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_deinterlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + size_t bytewidth = bpp / 8; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; + size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + for(b = 0; b < bytewidth; ++b) + { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } + else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + for(b = 0; b < bpp; ++b) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ + setBitOfReversedStream0(&obp, out, bit); + } + } + } + } +} + +static void removePaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) +{ + /* + After filtering there are still padding bits if scanlines have non multiple of 8 bit amounts. They need + to be removed (except at last scanline of (Adam7-reduced) image) before working with pure image buffers + for the Adam7 code, the color convert code and the output to the user. + in and out are allowed to be the same buffer, in may also be higher but still overlapping; in must + have >= ilinebits*h bits, out must have >= olinebits*h bits, olinebits must be <= ilinebits + also used to move bits after earlier such operations happened, e.g. in a sequence of reduced images from Adam7 + only useful if (ilinebits - olinebits) is a value in the range 1..7 + */ + unsigned y; + size_t diff = ilinebits - olinebits; + size_t ibp = 0, obp = 0; /*input and output bit pointers*/ + for(y = 0; y < h; ++y) + { + size_t x; + for(x = 0; x < olinebits; ++x) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + ibp += diff; + } +} + +/*out must be buffer big enough to contain full image, and in must contain the full decompressed data from +the IDAT chunks (with filter index bytes and possible padding bits) +return value is error*/ +static unsigned postProcessScanlines(unsigned char* out, unsigned char* in, + unsigned w, unsigned h, const LodePNGInfo* info_png) +{ + /* + This function converts the filtered-padded-interlaced data into pure 2D image buffer with the PNG's colortype. + Steps: + *) if no Adam7: 1) unfilter 2) remove padding bits (= posible extra bits per scanline if bpp < 8) + *) if adam7: 1) 7x unfilter 2) 7x remove padding bits 3) Adam7_deinterlace + NOTE: the in buffer will be overwritten with intermediate data! + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + if(bpp == 0) return 31; /*error: invalid colortype*/ + + if(info_png->interlace_method == 0) + { + if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) + { + CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); + removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); + } + /*we can immediately filter into the out buffer, no other steps needed*/ + else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); + } + else /*interlace_method is 1 (Adam7)*/ + { + unsigned passw[7], passh[7]; size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + for(i = 0; i != 7; ++i) + { + CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); + /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, + move bytes instead of bits or move not at all*/ + if(bpp < 8) + { + /*remove padding bits in scanlines; after this there still may be padding + bits between the different reduced images: each reduced image still starts nicely at a byte*/ + removePaddingBits(&in[passstart[i]], &in[padded_passstart[i]], passw[i] * bpp, + ((passw[i] * bpp + 7) / 8) * 8, passh[i]); + } + } + + Adam7_deinterlace(out, in, w, h, bpp); + } + + return 0; +} + +static unsigned readChunk_PLTE(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) +{ + unsigned pos = 0, i; + if(color->palette) lodepng_free(color->palette); + color->palettesize = chunkLength / 3; + color->palette = (unsigned char*)lodepng_malloc(4 * color->palettesize); + if(!color->palette && color->palettesize) + { + color->palettesize = 0; + return 83; /*alloc fail*/ + } + if(color->palettesize > 256) return 38; /*error: palette too big*/ + + for(i = 0; i != color->palettesize; ++i) + { + color->palette[4 * i + 0] = data[pos++]; /*R*/ + color->palette[4 * i + 1] = data[pos++]; /*G*/ + color->palette[4 * i + 2] = data[pos++]; /*B*/ + color->palette[4 * i + 3] = 255; /*alpha*/ + } + + return 0; /* OK */ +} + +static unsigned readChunk_tRNS(LodePNGColorMode* color, const unsigned char* data, size_t chunkLength) +{ + unsigned i; + if(color->colortype == LCT_PALETTE) + { + /*error: more alpha values given than there are palette entries*/ + if(chunkLength > color->palettesize) return 38; + + for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; + } + else if(color->colortype == LCT_GREY) + { + /*error: this chunk must be 2 bytes for greyscale image*/ + if(chunkLength != 2) return 30; + + color->key_defined = 1; + color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; + } + else if(color->colortype == LCT_RGB) + { + /*error: this chunk must be 6 bytes for RGB image*/ + if(chunkLength != 6) return 41; + + color->key_defined = 1; + color->key_r = 256u * data[0] + data[1]; + color->key_g = 256u * data[2] + data[3]; + color->key_b = 256u * data[4] + data[5]; + } + else return 42; /*error: tRNS chunk not allowed for other color models*/ + + return 0; /* OK */ +} + + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*background color chunk (bKGD)*/ +static unsigned readChunk_bKGD(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(info->color.colortype == LCT_PALETTE) + { + /*error: this chunk must be 1 byte for indexed color image*/ + if(chunkLength != 1) return 43; + + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = data[0]; + } + else if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) + { + /*error: this chunk must be 2 bytes for greyscale image*/ + if(chunkLength != 2) return 44; + + info->background_defined = 1; + info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; + } + else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) + { + /*error: this chunk must be 6 bytes for greyscale image*/ + if(chunkLength != 6) return 45; + + info->background_defined = 1; + info->background_r = 256u * data[0] + data[1]; + info->background_g = 256u * data[2] + data[3]; + info->background_b = 256u * data[4] + data[5]; + } + + return 0; /* OK */ +} + +/*text chunk (tEXt)*/ +static unsigned readChunk_tEXt(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + char *key = 0, *str = 0; + unsigned i; + + while(!error) /*not really a while loop, only used to break on error*/ + { + unsigned length, string2_begin; + + length = 0; + while(length < chunkLength && data[length] != 0) ++length; + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + string2_begin = length + 1; /*skip keyword null terminator*/ + + length = chunkLength < string2_begin ? 0 : chunkLength - string2_begin; + str = (char*)lodepng_malloc(length + 1); + if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ + + str[length] = 0; + for(i = 0; i != length; ++i) str[i] = (char)data[string2_begin + i]; + + error = lodepng_add_text(info, key, str); + + break; + } + + lodepng_free(key); + lodepng_free(str); + + return error; +} + +/*compressed text chunk (zTXt)*/ +static unsigned readChunk_zTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, + const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + unsigned i; + + unsigned length, string2_begin; + char *key = 0; + ucvector decoded; + + ucvector_init(&decoded); + + while(!error) /*not really a while loop, only used to break on error*/ + { + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + string2_begin = length + 2; + if(string2_begin > chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ + + length = chunkLength - string2_begin; + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&decoded.data, &decoded.size, + (unsigned char*)(&data[string2_begin]), + length, zlibsettings); + if(error) break; + ucvector_push_back(&decoded, 0); + + error = lodepng_add_text(info, key, (char*)decoded.data); + + break; + } + + lodepng_free(key); + ucvector_cleanup(&decoded); + + return error; +} + +/*international text chunk (iTXt)*/ +static unsigned readChunk_iTXt(LodePNGInfo* info, const LodePNGDecompressSettings* zlibsettings, + const unsigned char* data, size_t chunkLength) +{ + unsigned error = 0; + unsigned i; + + unsigned length, begin, compressed; + char *key = 0, *langtag = 0, *transkey = 0; + ucvector decoded; + ucvector_init(&decoded); + + while(!error) /*not really a while loop, only used to break on error*/ + { + /*Quick check if the chunk length isn't too small. Even without check + it'd still fail with other error checks below if it's too short. This just gives a different error code.*/ + if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ + + /*read the key*/ + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; + if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ + if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ + + key = (char*)lodepng_malloc(length + 1); + if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ + + key[length] = 0; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; + + /*read the compression method*/ + compressed = data[length + 1]; + if(data[length + 2] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ + + /*even though it's not allowed by the standard, no error is thrown if + there's no null termination char, if the text is empty for the next 3 texts*/ + + /*read the langtag*/ + begin = length + 3; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + langtag = (char*)lodepng_malloc(length + 1); + if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ + + langtag[length] = 0; + for(i = 0; i != length; ++i) langtag[i] = (char)data[begin + i]; + + /*read the transkey*/ + begin += length + 1; + length = 0; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; + + transkey = (char*)lodepng_malloc(length + 1); + if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ + + transkey[length] = 0; + for(i = 0; i != length; ++i) transkey[i] = (char)data[begin + i]; + + /*read the actual text*/ + begin += length + 1; + + length = chunkLength < begin ? 0 : chunkLength - begin; + + if(compressed) + { + /*will fail if zlib error, e.g. if length is too small*/ + error = zlib_decompress(&decoded.data, &decoded.size, + (unsigned char*)(&data[begin]), + length, zlibsettings); + if(error) break; + if(decoded.allocsize < decoded.size) decoded.allocsize = decoded.size; + ucvector_push_back(&decoded, 0); + } + else + { + if(!ucvector_resize(&decoded, length + 1)) CERROR_BREAK(error, 83 /*alloc fail*/); + + decoded.data[length] = 0; + for(i = 0; i != length; ++i) decoded.data[i] = data[begin + i]; + } + + error = lodepng_add_itext(info, key, langtag, transkey, (char*)decoded.data); + + break; + } + + lodepng_free(key); + lodepng_free(langtag); + lodepng_free(transkey); + ucvector_cleanup(&decoded); + + return error; +} + +static unsigned readChunk_tIME(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 7) return 73; /*invalid tIME chunk size*/ + + info->time_defined = 1; + info->time.year = 256u * data[0] + data[1]; + info->time.month = data[2]; + info->time.day = data[3]; + info->time.hour = data[4]; + info->time.minute = data[5]; + info->time.second = data[6]; + + return 0; /* OK */ +} + +static unsigned readChunk_pHYs(LodePNGInfo* info, const unsigned char* data, size_t chunkLength) +{ + if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/ + + info->phys_defined = 1; + info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; + info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7]; + info->phys_unit = data[8]; + + return 0; /* OK */ +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*read a PNG, the result will be in the same color type as the PNG (hence "generic")*/ +static void decodeGeneric(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) +{ + unsigned char IEND = 0; + const unsigned char* chunk; + size_t i; + ucvector idat; /*the data from idat chunks*/ + ucvector scanlines; + size_t predict; + size_t numpixels; + size_t outsize = 0; + + /*for unknown chunk order*/ + unsigned unknown = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned critical_pos = 1; /*1 = after IHDR, 2 = after PLTE, 3 = after IDAT*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + + /*provide some proper output values if error will happen*/ + *out = 0; + + state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ + if(state->error) return; + + numpixels = *w * *h; + + /*multiplication overflow*/ + if(*h != 0 && numpixels / *h != *w) CERROR_RETURN(state->error, 92); + /*multiplication overflow possible further below. Allows up to 2^31-1 pixel + bytes with 16-bit RGBA, the rest is room for filter bytes.*/ + if(numpixels > 268435455) CERROR_RETURN(state->error, 92); + + ucvector_init(&idat); + chunk = &in[33]; /*first byte of the first chunk after the header*/ + + /*loop through the chunks, ignoring unknown chunks and stopping at IEND chunk. + IDAT data is put at the start of the in buffer*/ + while(!IEND && !state->error) + { + unsigned chunkLength; + const unsigned char* data; /*the data in the chunk*/ + + /*error: size of the in buffer too small to contain next chunk*/ + if((size_t)((chunk - in) + 12) > insize || chunk < in) CERROR_BREAK(state->error, 30); + + /*length of the data of the chunk, excluding the length bytes, chunk type and CRC bytes*/ + chunkLength = lodepng_chunk_length(chunk); + /*error: chunk length larger than the max PNG chunk size*/ + if(chunkLength > 2147483647) CERROR_BREAK(state->error, 63); + + if((size_t)((chunk - in) + chunkLength + 12) > insize || (chunk + chunkLength + 12) < in) + { + CERROR_BREAK(state->error, 64); /*error: size of the in buffer too small to contain next chunk*/ + } + + data = lodepng_chunk_data_const(chunk); + + /*IDAT chunk, containing compressed image data*/ + if(lodepng_chunk_type_equals(chunk, "IDAT")) + { + size_t oldsize = idat.size; + if(!ucvector_resize(&idat, oldsize + chunkLength)) CERROR_BREAK(state->error, 83 /*alloc fail*/); + for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 3; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + /*IEND chunk*/ + else if(lodepng_chunk_type_equals(chunk, "IEND")) + { + IEND = 1; + } + /*palette chunk (PLTE)*/ + else if(lodepng_chunk_type_equals(chunk, "PLTE")) + { + state->error = readChunk_PLTE(&state->info_png.color, data, chunkLength); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + critical_pos = 2; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + /*palette transparency chunk (tRNS)*/ + else if(lodepng_chunk_type_equals(chunk, "tRNS")) + { + state->error = readChunk_tRNS(&state->info_png.color, data, chunkLength); + if(state->error) break; + } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*background color chunk (bKGD)*/ + else if(lodepng_chunk_type_equals(chunk, "bKGD")) + { + state->error = readChunk_bKGD(&state->info_png, data, chunkLength); + if(state->error) break; + } + /*text chunk (tEXt)*/ + else if(lodepng_chunk_type_equals(chunk, "tEXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_tEXt(&state->info_png, data, chunkLength); + if(state->error) break; + } + } + /*compressed text chunk (zTXt)*/ + else if(lodepng_chunk_type_equals(chunk, "zTXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_zTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + if(state->error) break; + } + } + /*international text chunk (iTXt)*/ + else if(lodepng_chunk_type_equals(chunk, "iTXt")) + { + if(state->decoder.read_text_chunks) + { + state->error = readChunk_iTXt(&state->info_png, &state->decoder.zlibsettings, data, chunkLength); + if(state->error) break; + } + } + else if(lodepng_chunk_type_equals(chunk, "tIME")) + { + state->error = readChunk_tIME(&state->info_png, data, chunkLength); + if(state->error) break; + } + else if(lodepng_chunk_type_equals(chunk, "pHYs")) + { + state->error = readChunk_pHYs(&state->info_png, data, chunkLength); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + else /*it's not an implemented chunk type, so ignore it: skip over the data*/ + { + /*error: unknown critical chunk (5th bit of first byte of chunk type is 0)*/ + if(!lodepng_chunk_ancillary(chunk)) CERROR_BREAK(state->error, 69); + + unknown = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + if(state->decoder.remember_unknown_chunks) + { + state->error = lodepng_chunk_append(&state->info_png.unknown_chunks_data[critical_pos - 1], + &state->info_png.unknown_chunks_size[critical_pos - 1], chunk); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + } + + if(!state->decoder.ignore_crc && !unknown) /*check CRC if wanted, only on known chunk types*/ + { + if(lodepng_chunk_check_crc(chunk)) CERROR_BREAK(state->error, 57); /*invalid CRC*/ + } + + if(!IEND) chunk = lodepng_chunk_next_const(chunk); + } + + ucvector_init(&scanlines); + /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. + If the decompressed size does not match the prediction, the image must be corrupt.*/ + if(state->info_png.interlace_method == 0) + { + /*The extra *h is added because this are the filter bytes every scanline starts with*/ + predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h; + } + else + { + /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/ + const LodePNGColorMode* color = &state->info_png.color; + predict = 0; + predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); + if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); + predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color) + ((*h + 3) >> 3); + if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color) + ((*h + 3) >> 2); + predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color) + ((*h + 1) >> 2); + if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color) + ((*h + 1) >> 1); + predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color) + ((*h + 0) >> 1); + } + if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/ + if(!state->error) + { + state->error = zlib_decompress(&scanlines.data, &scanlines.size, idat.data, + idat.size, &state->decoder.zlibsettings); + if(!state->error && scanlines.size != predict) state->error = 91; /*decompressed size doesn't match prediction*/ + } + ucvector_cleanup(&idat); + + if(!state->error) + { + outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!*out) state->error = 83; /*alloc fail*/ + } + if(!state->error) + { + for(i = 0; i < outsize; i++) (*out)[i] = 0; + state->error = postProcessScanlines(*out, scanlines.data, *w, *h, &state->info_png); + } + ucvector_cleanup(&scanlines); +} + +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize) +{ + *out = 0; + decodeGeneric(out, w, h, state, in, insize); + if(state->error) return state->error; + if(!state->decoder.color_convert || lodepng_color_mode_equal(&state->info_raw, &state->info_png.color)) + { + /*same color type, no copying or converting of data needed*/ + /*store the info_png color settings on the info_raw so that the info_raw still reflects what colortype + the raw image has to the end user*/ + if(!state->decoder.color_convert) + { + state->error = lodepng_color_mode_copy(&state->info_raw, &state->info_png.color); + if(state->error) return state->error; + } + } + else + { + /*color conversion needed; sort of copy of the data*/ + unsigned char* data = *out; + size_t outsize; + + /*TODO: check if this works according to the statement in the documentation: "The converter can convert + from greyscale input color type, to 8-bit greyscale or greyscale with alpha"*/ + if(!(state->info_raw.colortype == LCT_RGB || state->info_raw.colortype == LCT_RGBA) + && !(state->info_raw.bitdepth == 8)) + { + return 56; /*unsupported color mode conversion*/ + } + + outsize = lodepng_get_raw_size(*w, *h, &state->info_raw); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!(*out)) + { + state->error = 83; /*alloc fail*/ + } + else state->error = lodepng_convert(*out, data, &state->info_raw, + &state->info_png.color, *w, *h); + lodepng_free(data); + } + return state->error; +} + +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + error = lodepng_decode(out, w, h, &state, in, insize); + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) +{ + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGBA, 8); +} + +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, const unsigned char* in, size_t insize) +{ + return lodepng_decode_memory(out, w, h, in, insize, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer = 0; + size_t buffersize; + unsigned error; + error = lodepng_load_file(&buffer, &buffersize, filename); + if(!error) error = lodepng_decode_memory(out, w, h, buffer, buffersize, colortype, bitdepth); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) +{ + return lodepng_decode_file(out, w, h, filename, LCT_RGBA, 8); +} + +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename) +{ + return lodepng_decode_file(out, w, h, filename, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings) +{ + settings->color_convert = 1; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->read_text_chunks = 1; + settings->remember_unknown_chunks = 0; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + settings->ignore_crc = 0; + lodepng_decompress_settings_init(&settings->zlibsettings); +} + +#endif /*LODEPNG_COMPILE_DECODER*/ + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) + +void lodepng_state_init(LodePNGState* state) +{ +#ifdef LODEPNG_COMPILE_DECODER + lodepng_decoder_settings_init(&state->decoder); +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + lodepng_encoder_settings_init(&state->encoder); +#endif /*LODEPNG_COMPILE_ENCODER*/ + lodepng_color_mode_init(&state->info_raw); + lodepng_info_init(&state->info_png); + state->error = 1; +} + +void lodepng_state_cleanup(LodePNGState* state) +{ + lodepng_color_mode_cleanup(&state->info_raw); + lodepng_info_cleanup(&state->info_png); +} + +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source) +{ + lodepng_state_cleanup(dest); + *dest = *source; + lodepng_color_mode_init(&dest->info_raw); + lodepng_info_init(&dest->info_png); + dest->error = lodepng_color_mode_copy(&dest->info_raw, &source->info_raw); if(dest->error) return; + dest->error = lodepng_info_copy(&dest->info_png, &source->info_png); if(dest->error) return; +} + +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_ENCODER + +/* ////////////////////////////////////////////////////////////////////////// */ +/* / PNG Encoder / */ +/* ////////////////////////////////////////////////////////////////////////// */ + +/*chunkName must be string of 4 characters*/ +static unsigned addChunk(ucvector* out, const char* chunkName, const unsigned char* data, size_t length) +{ + CERROR_TRY_RETURN(lodepng_chunk_create(&out->data, &out->size, (unsigned)length, chunkName, data)); + out->allocsize = out->size; /*fix the allocsize again*/ + return 0; +} + +static void writeSignature(ucvector* out) +{ + /*8 bytes PNG signature, aka the magic bytes*/ + ucvector_push_back(out, 137); + ucvector_push_back(out, 80); + ucvector_push_back(out, 78); + ucvector_push_back(out, 71); + ucvector_push_back(out, 13); + ucvector_push_back(out, 10); + ucvector_push_back(out, 26); + ucvector_push_back(out, 10); +} + +static unsigned addChunk_IHDR(ucvector* out, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth, unsigned interlace_method) +{ + unsigned error = 0; + ucvector header; + ucvector_init(&header); + + lodepng_add32bitInt(&header, w); /*width*/ + lodepng_add32bitInt(&header, h); /*height*/ + ucvector_push_back(&header, (unsigned char)bitdepth); /*bit depth*/ + ucvector_push_back(&header, (unsigned char)colortype); /*color type*/ + ucvector_push_back(&header, 0); /*compression method*/ + ucvector_push_back(&header, 0); /*filter method*/ + ucvector_push_back(&header, interlace_method); /*interlace method*/ + + error = addChunk(out, "IHDR", header.data, header.size); + ucvector_cleanup(&header); + + return error; +} + +static unsigned addChunk_PLTE(ucvector* out, const LodePNGColorMode* info) +{ + unsigned error = 0; + size_t i; + ucvector PLTE; + ucvector_init(&PLTE); + for(i = 0; i != info->palettesize * 4; ++i) + { + /*add all channels except alpha channel*/ + if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]); + } + error = addChunk(out, "PLTE", PLTE.data, PLTE.size); + ucvector_cleanup(&PLTE); + + return error; +} + +static unsigned addChunk_tRNS(ucvector* out, const LodePNGColorMode* info) +{ + unsigned error = 0; + size_t i; + ucvector tRNS; + ucvector_init(&tRNS); + if(info->colortype == LCT_PALETTE) + { + size_t amount = info->palettesize; + /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ + for(i = info->palettesize; i != 0; --i) + { + if(info->palette[4 * (i - 1) + 3] == 255) --amount; + else break; + } + /*add only alpha channel*/ + for(i = 0; i != amount; ++i) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); + } + else if(info->colortype == LCT_GREY) + { + if(info->key_defined) + { + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); + } + } + else if(info->colortype == LCT_RGB) + { + if(info->key_defined) + { + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b & 255)); + } + } + + error = addChunk(out, "tRNS", tRNS.data, tRNS.size); + ucvector_cleanup(&tRNS); + + return error; +} + +static unsigned addChunk_IDAT(ucvector* out, const unsigned char* data, size_t datasize, + LodePNGCompressSettings* zlibsettings) +{ + ucvector zlibdata; + unsigned error = 0; + + /*compress with the Zlib compressor*/ + ucvector_init(&zlibdata); + error = zlib_compress(&zlibdata.data, &zlibdata.size, data, datasize, zlibsettings); + if(!error) error = addChunk(out, "IDAT", zlibdata.data, zlibdata.size); + ucvector_cleanup(&zlibdata); + + return error; +} + +static unsigned addChunk_IEND(ucvector* out) +{ + unsigned error = 0; + error = addChunk(out, "IEND", 0, 0); + return error; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + +static unsigned addChunk_tEXt(ucvector* out, const char* keyword, const char* textstring) +{ + unsigned error = 0; + size_t i; + ucvector text; + ucvector_init(&text); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&text, 0); /*0 termination char*/ + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)textstring[i]); + error = addChunk(out, "tEXt", text.data, text.size); + ucvector_cleanup(&text); + + return error; +} + +static unsigned addChunk_zTXt(ucvector* out, const char* keyword, const char* textstring, + LodePNGCompressSettings* zlibsettings) +{ + unsigned error = 0; + ucvector data, compressed; + size_t i, textsize = strlen(textstring); + + ucvector_init(&data); + ucvector_init(&compressed); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&data, 0); /*0 termination char*/ + ucvector_push_back(&data, 0); /*compression method: 0*/ + + error = zlib_compress(&compressed.data, &compressed.size, + (unsigned char*)textstring, textsize, zlibsettings); + if(!error) + { + for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); + error = addChunk(out, "zTXt", data.data, data.size); + } + + ucvector_cleanup(&compressed); + ucvector_cleanup(&data); + return error; +} + +static unsigned addChunk_iTXt(ucvector* out, unsigned compressed, const char* keyword, const char* langtag, + const char* transkey, const char* textstring, LodePNGCompressSettings* zlibsettings) +{ + unsigned error = 0; + ucvector data; + size_t i, textsize = strlen(textstring); + + ucvector_init(&data); + + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); + if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ + ucvector_push_back(&data, 0); /*null termination char*/ + ucvector_push_back(&data, compressed ? 1 : 0); /*compression flag*/ + ucvector_push_back(&data, 0); /*compression method*/ + for(i = 0; langtag[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)langtag[i]); + ucvector_push_back(&data, 0); /*null termination char*/ + for(i = 0; transkey[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)transkey[i]); + ucvector_push_back(&data, 0); /*null termination char*/ + + if(compressed) + { + ucvector compressed_data; + ucvector_init(&compressed_data); + error = zlib_compress(&compressed_data.data, &compressed_data.size, + (unsigned char*)textstring, textsize, zlibsettings); + if(!error) + { + for(i = 0; i != compressed_data.size; ++i) ucvector_push_back(&data, compressed_data.data[i]); + } + ucvector_cleanup(&compressed_data); + } + else /*not compressed*/ + { + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)textstring[i]); + } + + if(!error) error = addChunk(out, "iTXt", data.data, data.size); + ucvector_cleanup(&data); + return error; +} + +static unsigned addChunk_bKGD(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector bKGD; + ucvector_init(&bKGD); + if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); + } + else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b & 255)); + } + else if(info->color.colortype == LCT_PALETTE) + { + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); /*palette index*/ + } + + error = addChunk(out, "bKGD", bKGD.data, bKGD.size); + ucvector_cleanup(&bKGD); + + return error; +} + +static unsigned addChunk_tIME(ucvector* out, const LodePNGTime* time) +{ + unsigned error = 0; + unsigned char* data = (unsigned char*)lodepng_malloc(7); + if(!data) return 83; /*alloc fail*/ + data[0] = (unsigned char)(time->year >> 8); + data[1] = (unsigned char)(time->year & 255); + data[2] = (unsigned char)time->month; + data[3] = (unsigned char)time->day; + data[4] = (unsigned char)time->hour; + data[5] = (unsigned char)time->minute; + data[6] = (unsigned char)time->second; + error = addChunk(out, "tIME", data, 7); + lodepng_free(data); + return error; +} + +static unsigned addChunk_pHYs(ucvector* out, const LodePNGInfo* info) +{ + unsigned error = 0; + ucvector data; + ucvector_init(&data); + + lodepng_add32bitInt(&data, info->phys_x); + lodepng_add32bitInt(&data, info->phys_y); + ucvector_push_back(&data, info->phys_unit); + + error = addChunk(out, "pHYs", data.data, data.size); + ucvector_cleanup(&data); + + return error; +} + +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +static void filterScanline(unsigned char* out, const unsigned char* scanline, const unsigned char* prevline, + size_t length, size_t bytewidth, unsigned char filterType) +{ + size_t i; + switch(filterType) + { + case 0: /*None*/ + for(i = 0; i != length; ++i) out[i] = scanline[i]; + break; + case 1: /*Sub*/ + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; + break; + case 2: /*Up*/ + if(prevline) + { + for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; + } + else + { + for(i = 0; i != length; ++i) out[i] = scanline[i]; + } + break; + case 3: /*Average*/ + if(prevline) + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); + } + else + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); + } + break; + case 4: /*Paeth*/ + if(prevline) + { + /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ + for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); + for(i = bytewidth; i < length; ++i) + { + out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); + } + } + else + { + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ + for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); + } + break; + default: return; /*unexisting filter type given*/ + } +} + +/* log2 approximation. A slight bit faster than std::log. */ +static float flog2(float f) +{ + float result = 0; + while(f > 32) { result += 4; f /= 16; } + while(f > 2) { ++result; f /= 2; } + return result + 1.442695f * (f * f * f / 3 - 3 * f * f / 2 + 3 * f - 1.83333f); +} + +static unsigned filter(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* info, const LodePNGEncoderSettings* settings) +{ + /* + For PNG filter method 0 + out must be a buffer with as size: h + (w * h * bpp + 7) / 8, because there are + the scanlines with 1 extra byte per scanline + */ + + unsigned bpp = lodepng_get_bpp(info); + /*the width of a scanline in bytes, not including the filter type*/ + size_t linebytes = (w * bpp + 7) / 8; + /*bytewidth is used for filtering, is 1 when bpp < 8, number of bytes per pixel otherwise*/ + size_t bytewidth = (bpp + 7) / 8; + const unsigned char* prevline = 0; + unsigned x, y; + unsigned error = 0; + LodePNGFilterStrategy strategy = settings->filter_strategy; + + /* + There is a heuristic called the minimum sum of absolute differences heuristic, suggested by the PNG standard: + * If the image type is Palette, or the bit depth is smaller than 8, then do not filter the image (i.e. + use fixed filtering, with the filter None). + * (The other case) If the image type is Grayscale or RGB (with or without Alpha), and the bit depth is + not smaller than 8, then use adaptive filtering heuristic as follows: independently for each row, apply + all five filters and select the filter that produces the smallest sum of absolute values per row. + This heuristic is used if filter strategy is LFS_MINSUM and filter_palette_zero is true. + + If filter_palette_zero is true and filter_strategy is not LFS_MINSUM, the above heuristic is followed, + but for "the other case", whatever strategy filter_strategy is set to instead of the minimum sum + heuristic is used. + */ + if(settings->filter_palette_zero && + (info->colortype == LCT_PALETTE || info->bitdepth < 8)) strategy = LFS_ZERO; + + if(bpp == 0) return 31; /*error: invalid color type*/ + + if(strategy == LFS_ZERO) + { + for(y = 0; y != h; ++y) + { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + out[outindex] = 0; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, 0); + prevline = &in[inindex]; + } + } + else if(strategy == LFS_MINSUM) + { + /*adaptive filtering*/ + size_t sum[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned char type, bestType = 0; + + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + + if(!error) + { + for(y = 0; y != h; ++y) + { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) + { + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + + /*calculate the sum of the result*/ + sum[type] = 0; + if(type == 0) + { + for(x = 0; x != linebytes; ++x) sum[type] += (unsigned char)(attempt[type][x]); + } + else + { + for(x = 0; x != linebytes; ++x) + { + /*For differences, each byte should be treated as signed, values above 127 are negative + (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. + This means filtertype 0 is almost never chosen, but that is justified.*/ + unsigned char s = attempt[type][x]; + sum[type] += s < 128 ? s : (255U - s); + } + } + + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum[type] < smallest) + { + bestType = type; + smallest = sum[type]; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else if(strategy == LFS_ENTROPY) + { + float sum[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + float smallest = 0; + unsigned type, bestType = 0; + unsigned count[256]; + + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + + for(y = 0; y != h; ++y) + { + /*try the 5 filter types*/ + for(type = 0; type != 5; ++type) + { + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + for(x = 0; x != 256; ++x) count[x] = 0; + for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; + ++count[type]; /*the filter type itself is part of the scanline*/ + sum[type] = 0; + for(x = 0; x != 256; ++x) + { + float p = count[x] / (float)(linebytes + 1); + sum[type] += count[x] == 0 ? 0 : flog2(1 / p) * p; + } + /*check if this is smallest sum (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || sum[type] < smallest) + { + bestType = type; + smallest = sum[type]; + } + } + + prevline = &in[y * linebytes]; + + /*now fill the out values*/ + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else if(strategy == LFS_PREDEFINED) + { + for(y = 0; y != h; ++y) + { + size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ + size_t inindex = linebytes * y; + unsigned char type = settings->predefined_filters[y]; + out[outindex] = type; /*filter type byte*/ + filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); + prevline = &in[inindex]; + } + } + else if(strategy == LFS_BRUTE_FORCE) + { + /*brute force filter chooser. + deflate the scanline after every filter attempt to see which one deflates best. + This is very slow and gives only slightly smaller, sometimes even larger, result*/ + size_t size[5]; + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ + size_t smallest = 0; + unsigned type = 0, bestType = 0; + unsigned char* dummy; + LodePNGCompressSettings zlibsettings = settings->zlibsettings; + /*use fixed tree on the attempts so that the tree is not adapted to the filtertype on purpose, + to simulate the true case where the tree is the same for the whole image. Sometimes it gives + better result with dynamic tree anyway. Using the fixed tree sometimes gives worse, but in rare + cases better compression. It does make this a bit less slow, so it's worth doing this.*/ + zlibsettings.btype = 1; + /*a custom encoder likely doesn't read the btype setting and is optimized for complete PNG + images only, so disable it*/ + zlibsettings.custom_zlib = 0; + zlibsettings.custom_deflate = 0; + for(type = 0; type != 5; ++type) + { + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ + } + for(y = 0; y != h; ++y) /*try the 5 filter types*/ + { + for(type = 0; type != 5; ++type) + { + unsigned testsize = linebytes; + /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ + + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + size[type] = 0; + dummy = 0; + zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); + lodepng_free(dummy); + /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ + if(type == 0 || size[type] < smallest) + { + bestType = type; + smallest = size[type]; + } + } + prevline = &in[y * linebytes]; + out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; + } + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); + } + else return 88; /* unknown filter strategy */ + + return error; +} + +static void addPaddingBits(unsigned char* out, const unsigned char* in, + size_t olinebits, size_t ilinebits, unsigned h) +{ + /*The opposite of the removePaddingBits function + olinebits must be >= ilinebits*/ + unsigned y; + size_t diff = olinebits - ilinebits; + size_t obp = 0, ibp = 0; /*bit pointers*/ + for(y = 0; y != h; ++y) + { + size_t x; + for(x = 0; x < ilinebits; ++x) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + /*obp += diff; --> no, fill in some value in the padding bits too, to avoid + "Use of uninitialised value of size ###" warning from valgrind*/ + for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); + } +} + +/* +in: non-interlaced image with size w*h +out: the same pixels, but re-ordered according to PNG's Adam7 interlacing, with + no padding bits between scanlines, but between reduced images so that each + reduced image starts at a byte. +bpp: bits per pixel +there are no padding bits, not between scanlines, not between reduced images +in has the following size in bits: w * h * bpp. +out is possibly bigger due to padding bits between reduced images +NOTE: comments about padding bits are only relevant if bpp < 8 +*/ +static void Adam7_interlace(unsigned char* out, const unsigned char* in, unsigned w, unsigned h, unsigned bpp) +{ + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned i; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + if(bpp >= 8) + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + size_t bytewidth = bpp / 8; + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; + size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; + for(b = 0; b < bytewidth; ++b) + { + out[pixeloutstart + b] = in[pixelinstart + b]; + } + } + } + } + else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ + { + for(i = 0; i != 7; ++i) + { + unsigned x, y, b; + unsigned ilinebits = bpp * passw[i]; + unsigned olinebits = bpp * w; + size_t obp, ibp; /*bit pointers (for out and in buffer)*/ + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) + { + ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; + obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); + for(b = 0; b < bpp; ++b) + { + unsigned char bit = readBitFromReversedStream(&ibp, in); + setBitOfReversedStream(&obp, out, bit); + } + } + } + } +} + +/*out must be buffer big enough to contain uncompressed IDAT chunk data, and in must contain the full image. +return value is error**/ +static unsigned preProcessScanlines(unsigned char** out, size_t* outsize, const unsigned char* in, + unsigned w, unsigned h, + const LodePNGInfo* info_png, const LodePNGEncoderSettings* settings) +{ + /* + This function converts the pure 2D image with the PNG's colortype, into filtered-padded-interlaced data. Steps: + *) if no Adam7: 1) add padding bits (= posible extra bits per scanline if bpp < 8) 2) filter + *) if adam7: 1) Adam7_interlace 2) 7x add padding bits 3) 7x filter + */ + unsigned bpp = lodepng_get_bpp(&info_png->color); + unsigned error = 0; + + if(info_png->interlace_method == 0) + { + *outsize = h + (h * ((w * bpp + 7) / 8)); /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out) && (*outsize)) error = 83; /*alloc fail*/ + + if(!error) + { + /*non multiple of 8 bits per scanline, padding bits needed per scanline*/ + if(bpp < 8 && w * bpp != ((w * bpp + 7) / 8) * 8) + { + unsigned char* padded = (unsigned char*)lodepng_malloc(h * ((w * bpp + 7) / 8)); + if(!padded) error = 83; /*alloc fail*/ + if(!error) + { + addPaddingBits(padded, in, ((w * bpp + 7) / 8) * 8, w * bpp, h); + error = filter(*out, padded, w, h, &info_png->color, settings); + } + lodepng_free(padded); + } + else + { + /*we can immediately filter into the out buffer, no other steps needed*/ + error = filter(*out, in, w, h, &info_png->color, settings); + } + } + } + else /*interlace_method is 1 (Adam7)*/ + { + unsigned passw[7], passh[7]; + size_t filter_passstart[8], padded_passstart[8], passstart[8]; + unsigned char* adam7; + + Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); + + *outsize = filter_passstart[7]; /*image size plus an extra byte per scanline + possible padding bits*/ + *out = (unsigned char*)lodepng_malloc(*outsize); + if(!(*out)) error = 83; /*alloc fail*/ + + adam7 = (unsigned char*)lodepng_malloc(passstart[7]); + if(!adam7 && passstart[7]) error = 83; /*alloc fail*/ + + if(!error) + { + unsigned i; + + Adam7_interlace(adam7, in, w, h, bpp); + for(i = 0; i != 7; ++i) + { + if(bpp < 8) + { + unsigned char* padded = (unsigned char*)lodepng_malloc(padded_passstart[i + 1] - padded_passstart[i]); + if(!padded) ERROR_BREAK(83); /*alloc fail*/ + addPaddingBits(padded, &adam7[passstart[i]], + ((passw[i] * bpp + 7) / 8) * 8, passw[i] * bpp, passh[i]); + error = filter(&(*out)[filter_passstart[i]], padded, + passw[i], passh[i], &info_png->color, settings); + lodepng_free(padded); + } + else + { + error = filter(&(*out)[filter_passstart[i]], &adam7[padded_passstart[i]], + passw[i], passh[i], &info_png->color, settings); + } + + if(error) break; + } + } + + lodepng_free(adam7); + } + + return error; +} + +/* +palette must have 4 * palettesize bytes allocated, and given in format RGBARGBARGBARGBA... +returns 0 if the palette is opaque, +returns 1 if the palette has a single color with alpha 0 ==> color key +returns 2 if the palette is semi-translucent. +*/ +static unsigned getPaletteTranslucency(const unsigned char* palette, size_t palettesize) +{ + size_t i; + unsigned key = 0; + unsigned r = 0, g = 0, b = 0; /*the value of the color with alpha 0, so long as color keying is possible*/ + for(i = 0; i != palettesize; ++i) + { + if(!key && palette[4 * i + 3] == 0) + { + r = palette[4 * i + 0]; g = palette[4 * i + 1]; b = palette[4 * i + 2]; + key = 1; + i = (size_t)(-1); /*restart from beginning, to detect earlier opaque colors with key's value*/ + } + else if(palette[4 * i + 3] != 255) return 2; + /*when key, no opaque RGB may have key's RGB*/ + else if(key && r == palette[i * 4 + 0] && g == palette[i * 4 + 1] && b == palette[i * 4 + 2]) return 2; + } + return key; +} + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +static unsigned addUnknownChunks(ucvector* out, unsigned char* data, size_t datasize) +{ + unsigned char* inchunk = data; + while((size_t)(inchunk - data) < datasize) + { + CERROR_TRY_RETURN(lodepng_chunk_append(&out->data, &out->size, inchunk)); + out->allocsize = out->size; /*fix the allocsize again*/ + inchunk = lodepng_chunk_next(inchunk); + } + return 0; +} +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state) +{ + LodePNGInfo info; + ucvector outv; + unsigned char* data = 0; /*uncompressed version of the IDAT chunk data*/ + size_t datasize = 0; + + /*provide some proper output values if error will happen*/ + *out = 0; + *outsize = 0; + state->error = 0; + + /*check input values validity*/ + if((state->info_png.color.colortype == LCT_PALETTE || state->encoder.force_palette) + && (state->info_png.color.palettesize == 0 || state->info_png.color.palettesize > 256)) + { + CERROR_RETURN_ERROR(state->error, 68); /*invalid palette size, it is only allowed to be 1-256*/ + } + if(state->encoder.zlibsettings.btype > 2) + { + CERROR_RETURN_ERROR(state->error, 61); /*error: unexisting btype*/ + } + if(state->info_png.interlace_method > 1) + { + CERROR_RETURN_ERROR(state->error, 71); /*error: unexisting interlace mode*/ + } + state->error = checkColorValidity(state->info_png.color.colortype, state->info_png.color.bitdepth); + if(state->error) return state->error; /*error: unexisting color type given*/ + state->error = checkColorValidity(state->info_raw.colortype, state->info_raw.bitdepth); + if(state->error) return state->error; /*error: unexisting color type given*/ + + /* color convert and compute scanline filter types */ + lodepng_info_init(&info); + lodepng_info_copy(&info, &state->info_png); + if(state->encoder.auto_convert) + { + state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); + } + if (!state->error) + { + if(!lodepng_color_mode_equal(&state->info_raw, &info.color)) + { + unsigned char* converted; + size_t size = (w * h * (size_t)lodepng_get_bpp(&info.color) + 7) / 8; + + converted = (unsigned char*)lodepng_malloc(size); + if(!converted && size) state->error = 83; /*alloc fail*/ + if(!state->error) + { + state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); + } + if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); + lodepng_free(converted); + } + else preProcessScanlines(&data, &datasize, image, w, h, &info, &state->encoder); + } + + /* output all PNG chunks */ + ucvector_init(&outv); + while(!state->error) /*while only executed once, to break on error*/ + { +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + size_t i; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*write signature and chunks*/ + writeSignature(&outv); + /*IHDR*/ + addChunk_IHDR(&outv, w, h, info.color.colortype, info.color.bitdepth, info.interlace_method); +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*unknown chunks between IHDR and PLTE*/ + if(info.unknown_chunks_data[0]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[0], info.unknown_chunks_size[0]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*PLTE*/ + if(info.color.colortype == LCT_PALETTE) + { + addChunk_PLTE(&outv, &info.color); + } + if(state->encoder.force_palette && (info.color.colortype == LCT_RGB || info.color.colortype == LCT_RGBA)) + { + addChunk_PLTE(&outv, &info.color); + } + /*tRNS*/ + if(info.color.colortype == LCT_PALETTE && getPaletteTranslucency(info.color.palette, info.color.palettesize) != 0) + { + addChunk_tRNS(&outv, &info.color); + } + if((info.color.colortype == LCT_GREY || info.color.colortype == LCT_RGB) && info.color.key_defined) + { + addChunk_tRNS(&outv, &info.color); + } +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*bKGD (must come between PLTE and the IDAt chunks*/ + if(info.background_defined) addChunk_bKGD(&outv, &info); + /*pHYs (must come before the IDAT chunks)*/ + if(info.phys_defined) addChunk_pHYs(&outv, &info); + + /*unknown chunks between PLTE and IDAT*/ + if(info.unknown_chunks_data[1]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[1], info.unknown_chunks_size[1]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + /*IDAT (multiple IDAT chunks must be consecutive)*/ + state->error = addChunk_IDAT(&outv, data, datasize, &state->encoder.zlibsettings); + if(state->error) break; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*tIME*/ + if(info.time_defined) addChunk_tIME(&outv, &info.time); + /*tEXt and/or zTXt*/ + for(i = 0; i != info.text_num; ++i) + { + if(strlen(info.text_keys[i]) > 79) + { + state->error = 66; /*text chunk too large*/ + break; + } + if(strlen(info.text_keys[i]) < 1) + { + state->error = 67; /*text chunk too small*/ + break; + } + if(state->encoder.text_compression) + { + addChunk_zTXt(&outv, info.text_keys[i], info.text_strings[i], &state->encoder.zlibsettings); + } + else + { + addChunk_tEXt(&outv, info.text_keys[i], info.text_strings[i]); + } + } + /*LodePNG version id in text chunk*/ + if(state->encoder.add_id) + { + unsigned alread_added_id_text = 0; + for(i = 0; i != info.text_num; ++i) + { + if(!strcmp(info.text_keys[i], "LodePNG")) + { + alread_added_id_text = 1; + break; + } + } + if(alread_added_id_text == 0) + { + addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ + } + } + /*iTXt*/ + for(i = 0; i != info.itext_num; ++i) + { + if(strlen(info.itext_keys[i]) > 79) + { + state->error = 66; /*text chunk too large*/ + break; + } + if(strlen(info.itext_keys[i]) < 1) + { + state->error = 67; /*text chunk too small*/ + break; + } + addChunk_iTXt(&outv, state->encoder.text_compression, + info.itext_keys[i], info.itext_langtags[i], info.itext_transkeys[i], info.itext_strings[i], + &state->encoder.zlibsettings); + } + + /*unknown chunks between IDAT and IEND*/ + if(info.unknown_chunks_data[2]) + { + state->error = addUnknownChunks(&outv, info.unknown_chunks_data[2], info.unknown_chunks_size[2]); + if(state->error) break; + } +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + addChunk_IEND(&outv); + + break; /*this isn't really a while loop; no error happened so break out now!*/ + } + + lodepng_info_cleanup(&info); + lodepng_free(data); + /*instead of cleaning the vector up, give it to the output*/ + *out = outv.data; + *outsize = outv.size; + + return state->error; +} + +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, const unsigned char* image, + unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned error; + LodePNGState state; + lodepng_state_init(&state); + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + state.info_png.color.colortype = colortype; + state.info_png.color.bitdepth = bitdepth; + lodepng_encode(out, outsize, image, w, h, &state); + error = state.error; + lodepng_state_cleanup(&state); + return error; +} + +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_memory(out, outsize, image, w, h, LCT_RGB, 8); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned lodepng_encode_file(const char* filename, const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, image, w, h, colortype, bitdepth); + if(!error) error = lodepng_save_file(buffer, buffersize, filename); + lodepng_free(buffer); + return error; +} + +unsigned lodepng_encode32_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_file(filename, image, w, h, LCT_RGBA, 8); +} + +unsigned lodepng_encode24_file(const char* filename, const unsigned char* image, unsigned w, unsigned h) +{ + return lodepng_encode_file(filename, image, w, h, LCT_RGB, 8); +} +#endif /*LODEPNG_COMPILE_DISK*/ + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings) +{ + lodepng_compress_settings_init(&settings->zlibsettings); + settings->filter_palette_zero = 1; + settings->filter_strategy = LFS_MINSUM; + settings->auto_convert = 1; + settings->force_palette = 0; + settings->predefined_filters = 0; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + settings->add_id = 0; + settings->text_compression = 1; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/* +This returns the description of a numerical error code in English. This is also +the documentation of all the error codes. +*/ +const char* lodepng_error_text(unsigned code) +{ + switch(code) + { + case 0: return "no error, everything went ok"; + case 1: return "nothing done yet"; /*the Encoder/Decoder has done nothing yet, error checking makes no sense yet*/ + case 10: return "end of input memory reached without huffman end code"; /*while huffman decoding*/ + case 11: return "error in code tree made it jump outside of huffman tree"; /*while huffman decoding*/ + case 13: return "problem while processing dynamic deflate block"; + case 14: return "problem while processing dynamic deflate block"; + case 15: return "problem while processing dynamic deflate block"; + case 16: return "unexisting code while processing dynamic deflate block"; + case 17: return "end of out buffer memory reached while inflating"; + case 18: return "invalid distance code while inflating"; + case 19: return "end of out buffer memory reached while inflating"; + case 20: return "invalid deflate block BTYPE encountered while decoding"; + case 21: return "NLEN is not ones complement of LEN in a deflate block"; + /*end of out buffer memory reached while inflating: + This can happen if the inflated deflate data is longer than the amount of bytes required to fill up + all the pixels of the image, given the color depth and image dimensions. Something that doesn't + happen in a normal, well encoded, PNG image.*/ + case 22: return "end of out buffer memory reached while inflating"; + case 23: return "end of in buffer memory reached while inflating"; + case 24: return "invalid FCHECK in zlib header"; + case 25: return "invalid compression method in zlib header"; + case 26: return "FDICT encountered in zlib header while it's not used for PNG"; + case 27: return "PNG file is smaller than a PNG header"; + /*Checks the magic file header, the first 8 bytes of the PNG file*/ + case 28: return "incorrect PNG signature, it's no PNG or corrupted"; + case 29: return "first chunk is not the header chunk"; + case 30: return "chunk length too large, chunk broken off at end of file"; + case 31: return "illegal PNG color type or bpp"; + case 32: return "illegal PNG compression method"; + case 33: return "illegal PNG filter method"; + case 34: return "illegal PNG interlace method"; + case 35: return "chunk length of a chunk is too large or the chunk too small"; + case 36: return "illegal PNG filter type encountered"; + case 37: return "illegal bit depth for this color type given"; + case 38: return "the palette is too big"; /*more than 256 colors*/ + case 39: return "more palette alpha values given in tRNS chunk than there are colors in the palette"; + case 40: return "tRNS chunk has wrong size for greyscale image"; + case 41: return "tRNS chunk has wrong size for RGB image"; + case 42: return "tRNS chunk appeared while it was not allowed for this color type"; + case 43: return "bKGD chunk has wrong size for palette image"; + case 44: return "bKGD chunk has wrong size for greyscale image"; + case 45: return "bKGD chunk has wrong size for RGB image"; + case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; + case 49: return "jumped past memory while generating dynamic huffman tree"; + case 50: return "jumped past memory while generating dynamic huffman tree"; + case 51: return "jumped past memory while inflating huffman block"; + case 52: return "jumped past memory while inflating"; + case 53: return "size of zlib data too small"; + case 54: return "repeat symbol in tree while there was no value symbol yet"; + /*jumped past tree while generating huffman tree, this could be when the + tree will have more leaves than symbols after generating it out of the + given lenghts. They call this an oversubscribed dynamic bit lengths tree in zlib.*/ + case 55: return "jumped past tree while generating huffman tree"; + case 56: return "given output image colortype or bitdepth not supported for color conversion"; + case 57: return "invalid CRC encountered (checking CRC can be disabled)"; + case 58: return "invalid ADLER32 encountered (checking ADLER32 can be disabled)"; + case 59: return "requested color conversion not supported"; + case 60: return "invalid window size given in the settings of the encoder (must be 0-32768)"; + case 61: return "invalid BTYPE given in the settings of the encoder (only 0, 1 and 2 are allowed)"; + /*LodePNG leaves the choice of RGB to greyscale conversion formula to the user.*/ + case 62: return "conversion from color to greyscale not supported"; + case 63: return "length of a chunk too long, max allowed for PNG is 2147483647 bytes per chunk"; /*(2^31-1)*/ + /*this would result in the inability of a deflated block to ever contain an end code. It must be at least 1.*/ + case 64: return "the length of the END symbol 256 in the Huffman tree is 0"; + case 66: return "the length of a text chunk keyword given to the encoder is longer than the maximum of 79 bytes"; + case 67: return "the length of a text chunk keyword given to the encoder is smaller than the minimum of 1 byte"; + case 68: return "tried to encode a PLTE chunk with a palette that has less than 1 or more than 256 colors"; + case 69: return "unknown chunk type with 'critical' flag encountered by the decoder"; + case 71: return "unexisting interlace mode given to encoder (must be 0 or 1)"; + case 72: return "while decoding, unexisting compression method encountering in zTXt or iTXt chunk (it must be 0)"; + case 73: return "invalid tIME chunk size"; + case 74: return "invalid pHYs chunk size"; + /*length could be wrong, or data chopped off*/ + case 75: return "no null termination char found while decoding text chunk"; + case 76: return "iTXt chunk too short to contain required bytes"; + case 77: return "integer overflow in buffer size"; + case 78: return "failed to open file for reading"; /*file doesn't exist or couldn't be opened for reading*/ + case 79: return "failed to open file for writing"; + case 80: return "tried creating a tree of 0 symbols"; + case 81: return "lazy matching at pos 0 is impossible"; + case 82: return "color conversion to palette requested while a color isn't in palette"; + case 83: return "memory allocation failed"; + case 84: return "given image too small to contain all pixels to be encoded"; + case 86: return "impossible offset in lz77 encoding (internal bug)"; + case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined"; + case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy"; + case 89: return "text chunk keyword too short or long: must have size 1-79"; + /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ + case 90: return "windowsize must be a power of two"; + case 91: return "invalid decompressed idat size"; + case 92: return "too many pixels, not supported"; + case 93: return "zero width or height is invalid"; + case 94: return "header chunk must have a size of 13 bytes"; + } + return "unknown error code"; +} +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* // C++ Wrapper // */ +/* ////////////////////////////////////////////////////////////////////////// */ +/* ////////////////////////////////////////////////////////////////////////// */ + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng +{ + +#ifdef LODEPNG_COMPILE_DISK +unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename) +{ + long size = lodepng_filesize(filename.c_str()); + if(size < 0) return 78; + buffer.resize((size_t)size); + return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str()); +} + +/*write given buffer to the file, overwriting the file, it doesn't append to it.*/ +unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename) +{ + return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str()); +} +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +unsigned decompress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings) +{ + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_decompress(&buffer, &buffersize, in, insize, &settings); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, + const LodePNGDecompressSettings& settings) +{ + return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned compress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings) +{ + unsigned char* buffer = 0; + size_t buffersize = 0; + unsigned error = zlib_compress(&buffer, &buffersize, in, insize, &settings); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned compress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, + const LodePNGCompressSettings& settings) +{ + return compress(out, in.empty() ? 0 : &in[0], in.size(), settings); +} +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ + + +#ifdef LODEPNG_COMPILE_PNG + +State::State() +{ + lodepng_state_init(this); +} + +State::State(const State& other) +{ + lodepng_state_init(this); + lodepng_state_copy(this, &other); +} + +State::~State() +{ + lodepng_state_cleanup(this); +} + +State& State::operator=(const State& other) +{ + lodepng_state_copy(this, &other); + return *this; +} + +#ifdef LODEPNG_COMPILE_DECODER + +unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const unsigned char* in, + size_t insize, LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + unsigned error = lodepng_decode_memory(&buffer, &w, &h, in, insize, colortype, bitdepth); + if(buffer && !error) + { + State state; + state.info_raw.colortype = colortype; + state.info_raw.bitdepth = bitdepth; + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, + const std::vector<unsigned char>& in, LodePNGColorType colortype, unsigned bitdepth) +{ + return decode(out, w, h, in.empty() ? 0 : &in[0], (unsigned)in.size(), colortype, bitdepth); +} + +unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize) +{ + unsigned char* buffer = NULL; + unsigned error = lodepng_decode(&buffer, &w, &h, &state, in, insize); + if(buffer && !error) + { + size_t buffersize = lodepng_get_raw_size(w, h, &state.info_raw); + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + } + lodepng_free(buffer); + return error; +} + +unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, + State& state, + const std::vector<unsigned char>& in) +{ + return decode(out, w, h, state, in.empty() ? 0 : &in[0], in.size()); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, const std::string& filename, + LodePNGColorType colortype, unsigned bitdepth) +{ + std::vector<unsigned char> buffer; + unsigned error = load_file(buffer, filename); + if(error) return error; + return decode(out, w, h, buffer, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DECODER */ +#endif /* LODEPNG_COMPILE_DISK */ + +#ifdef LODEPNG_COMPILE_ENCODER +unsigned encode(std::vector<unsigned char>& out, const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode_memory(&buffer, &buffersize, in, w, h, colortype, bitdepth); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector<unsigned char>& out, + const std::vector<unsigned char>& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} + +unsigned encode(std::vector<unsigned char>& out, + const unsigned char* in, unsigned w, unsigned h, + State& state) +{ + unsigned char* buffer; + size_t buffersize; + unsigned error = lodepng_encode(&buffer, &buffersize, in, w, h, &state); + if(buffer) + { + out.insert(out.end(), &buffer[0], &buffer[buffersize]); + lodepng_free(buffer); + } + return error; +} + +unsigned encode(std::vector<unsigned char>& out, + const std::vector<unsigned char>& in, unsigned w, unsigned h, + State& state) +{ + if(lodepng_get_raw_size(w, h, &state.info_raw) > in.size()) return 84; + return encode(out, in.empty() ? 0 : &in[0], w, h, state); +} + +#ifdef LODEPNG_COMPILE_DISK +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + std::vector<unsigned char> buffer; + unsigned error = encode(buffer, in, w, h, colortype, bitdepth); + if(!error) error = save_file(buffer, filename); + return error; +} + +unsigned encode(const std::string& filename, + const std::vector<unsigned char>& in, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth) +{ + if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; + return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); +} +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_PNG */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ diff --git a/Source/3rdParty/lodepng/lodepng.h b/Source/3rdParty/lodepng/lodepng.h new file mode 100644 index 0000000..d633bfa --- /dev/null +++ b/Source/3rdParty/lodepng/lodepng.h @@ -0,0 +1,1762 @@ +/* +LodePNG version 20170917 + +Copyright (c) 2005-2017 Lode Vandevenne + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ + +#ifndef LODEPNG_H +#define LODEPNG_H + +#include <string.h> /*for size_t*/ + +extern const char* LODEPNG_VERSION_STRING; + +/* +The following #defines are used to create code sections. They can be disabled +to disable code sections, which can give faster compile time and smaller binary. +The "NO_COMPILE" defines are designed to be used to pass as defines to the +compiler command to disable them without modifying this header, e.g. +-DLODEPNG_NO_COMPILE_ZLIB for gcc. +In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to +allow implementing a custom lodepng_crc32. +*/ +/*deflate & zlib. If disabled, you must specify alternative zlib functions in +the custom_zlib field of the compress and decompress settings*/ +#ifndef LODEPNG_NO_COMPILE_ZLIB +#define LODEPNG_COMPILE_ZLIB +#endif +/*png encoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_PNG +#define LODEPNG_COMPILE_PNG +#endif +/*deflate&zlib decoder and png decoder*/ +#ifndef LODEPNG_NO_COMPILE_DECODER +#define LODEPNG_COMPILE_DECODER +#endif +/*deflate&zlib encoder and png encoder*/ +#ifndef LODEPNG_NO_COMPILE_ENCODER +#define LODEPNG_COMPILE_ENCODER +#endif +/*the optional built in harddisk file loading and saving functions*/ +#ifndef LODEPNG_NO_COMPILE_DISK +#define LODEPNG_COMPILE_DISK +#endif +/*support for chunks other than IHDR, IDAT, PLTE, tRNS, IEND: ancillary and unknown chunks*/ +#ifndef LODEPNG_NO_COMPILE_ANCILLARY_CHUNKS +#define LODEPNG_COMPILE_ANCILLARY_CHUNKS +#endif +/*ability to convert error numerical codes to English text string*/ +#ifndef LODEPNG_NO_COMPILE_ERROR_TEXT +#define LODEPNG_COMPILE_ERROR_TEXT +#endif +/*Compile the default allocators (C's free, malloc and realloc). If you disable this, +you can define the functions lodepng_free, lodepng_malloc and lodepng_realloc in your +source files with custom allocators.*/ +#ifndef LODEPNG_NO_COMPILE_ALLOCATORS +#define LODEPNG_COMPILE_ALLOCATORS +#endif +/*compile the C++ version (you can disable the C++ wrapper here even when compiling for C++)*/ +#ifdef __cplusplus +#ifndef LODEPNG_NO_COMPILE_CPP +#define LODEPNG_COMPILE_CPP +#endif +#endif + +#ifdef LODEPNG_COMPILE_CPP +#include <vector> +#include <string> +#endif /*LODEPNG_COMPILE_CPP*/ + +#ifdef LODEPNG_COMPILE_PNG +/*The PNG color types (also used for raw).*/ +typedef enum LodePNGColorType +{ + LCT_GREY = 0, /*greyscale: 1,2,4,8,16 bit*/ + LCT_RGB = 2, /*RGB: 8,16 bit*/ + LCT_PALETTE = 3, /*palette: 1,2,4,8 bit*/ + LCT_GREY_ALPHA = 4, /*greyscale with alpha: 8,16 bit*/ + LCT_RGBA = 6 /*RGB with alpha: 8,16 bit*/ +} LodePNGColorType; + +#ifdef LODEPNG_COMPILE_DECODER +/* +Converts PNG data in memory to raw pixel data. +out: Output parameter. Pointer to buffer that will contain the raw pixel data. + After decoding, its size is w * h * (bytes per pixel) bytes larger than + initially. Bytes per pixel depends on colortype and bitdepth. + Must be freed after usage with free(*out). + Note: for 16-bit per channel colors, uses big endian format like PNG does. +w: Output parameter. Pointer to width of pixel data. +h: Output parameter. Pointer to height of pixel data. +in: Memory buffer with the PNG file. +insize: size of the in buffer. +colortype: the desired color type for the raw output image. See explanation on PNG color types. +bitdepth: the desired bit depth for the raw output image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_decode_memory(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_memory, but always decodes to 32-bit RGBA raw image*/ +unsigned lodepng_decode32(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +/*Same as lodepng_decode_memory, but always decodes to 24-bit RGB raw image*/ +unsigned lodepng_decode24(unsigned char** out, unsigned* w, unsigned* h, + const unsigned char* in, size_t insize); + +#ifdef LODEPNG_COMPILE_DISK +/* +Load PNG from disk, from file with given name. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_decode_file, but always decodes to 32-bit RGBA raw image.*/ +unsigned lodepng_decode32_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); + +/*Same as lodepng_decode_file, but always decodes to 24-bit RGB raw image.*/ +unsigned lodepng_decode24_file(unsigned char** out, unsigned* w, unsigned* h, + const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_DECODER*/ + + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Converts raw pixel data into a PNG image in memory. The colortype and bitdepth + of the output PNG image cannot be chosen, they are automatically determined + by the colortype, bitdepth and content of the input pixel data. + Note: for 16-bit per channel colors, needs big endian format like PNG does. +out: Output parameter. Pointer to buffer that will contain the PNG image data. + Must be freed after usage with free(*out). +outsize: Output parameter. Pointer to the size in bytes of the out buffer. +image: The raw pixel data to encode. The size of this buffer should be + w * h * (bytes per pixel), bytes per pixel depends on colortype and bitdepth. +w: width of the raw pixel data in pixels. +h: height of the raw pixel data in pixels. +colortype: the color type of the raw input image. See explanation on PNG color types. +bitdepth: the bit depth of the raw input image. See explanation on PNG color types. +Return value: LodePNG error code (0 means no error). +*/ +unsigned lodepng_encode_memory(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_memory, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_memory, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DISK +/* +Converts raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned lodepng_encode_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h, + LodePNGColorType colortype, unsigned bitdepth); + +/*Same as lodepng_encode_file, but always encodes from 32-bit RGBA raw image.*/ +unsigned lodepng_encode32_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); + +/*Same as lodepng_encode_file, but always encodes from 24-bit RGB raw image.*/ +unsigned lodepng_encode24_file(const char* filename, + const unsigned char* image, unsigned w, unsigned h); +#endif /*LODEPNG_COMPILE_DISK*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#ifdef LODEPNG_COMPILE_CPP +namespace lodepng +{ +#ifdef LODEPNG_COMPILE_DECODER +/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype +is the format to output the pixels to. Default is RGBA 8-bit per channel.*/ +unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, + const unsigned char* in, size_t insize, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, + const std::vector<unsigned char>& in, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts PNG file from disk to raw pixel data in memory. +Same as the other decode functions, but instead takes a filename as input. +*/ +unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, + const std::string& filename, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/*Same as lodepng_encode_memory, but encodes to an std::vector. colortype +is that of the raw input data. The output PNG color type will be auto chosen.*/ +unsigned encode(std::vector<unsigned char>& out, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(std::vector<unsigned char>& out, + const std::vector<unsigned char>& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#ifdef LODEPNG_COMPILE_DISK +/* +Converts 32-bit RGBA raw pixel data into a PNG file on disk. +Same as the other encode functions, but instead takes a filename as output. +NOTE: This overwrites existing files without warning! +*/ +unsigned encode(const std::string& filename, + const unsigned char* in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +unsigned encode(const std::string& filename, + const std::vector<unsigned char>& in, unsigned w, unsigned h, + LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ +#endif /*LODEPNG_COMPILE_PNG*/ + +#ifdef LODEPNG_COMPILE_ERROR_TEXT +/*Returns an English description of the numerical error code.*/ +const char* lodepng_error_text(unsigned code); +#endif /*LODEPNG_COMPILE_ERROR_TEXT*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Settings for zlib decompression*/ +typedef struct LodePNGDecompressSettings LodePNGDecompressSettings; +struct LodePNGDecompressSettings +{ + unsigned ignore_adler32; /*if 1, continue and don't give an error message if the Adler32 checksum is corrupted*/ + + /*use custom zlib decoder instead of built in one (default: null)*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + /*use custom deflate decoder instead of built in one (default: null) + if custom_zlib is used, custom_deflate is ignored since only the built in + zlib function will call custom_deflate*/ + unsigned (*custom_inflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGDecompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGDecompressSettings lodepng_default_decompress_settings; +void lodepng_decompress_settings_init(LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Settings for zlib compression. Tweaking these settings tweaks the balance +between speed and compression ratio. +*/ +typedef struct LodePNGCompressSettings LodePNGCompressSettings; +struct LodePNGCompressSettings /*deflate = compress*/ +{ + /*LZ77 related settings*/ + unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/ + unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/ + unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/ + unsigned minmatch; /*mininum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/ + unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/ + unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/ + + /*use custom zlib encoder instead of built in one (default: null)*/ + unsigned (*custom_zlib)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + /*use custom deflate encoder instead of built in one (default: null) + if custom_zlib is used, custom_deflate is ignored since only the built in + zlib function will call custom_deflate*/ + unsigned (*custom_deflate)(unsigned char**, size_t*, + const unsigned char*, size_t, + const LodePNGCompressSettings*); + + const void* custom_context; /*optional custom settings for custom functions*/ +}; + +extern const LodePNGCompressSettings lodepng_default_compress_settings; +void lodepng_compress_settings_init(LodePNGCompressSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_PNG +/* +Color mode of an image. Contains all information required to decode the pixel +bits to RGBA colors. This information is the same as used in the PNG file +format, and is used both for PNG and raw image data in LodePNG. +*/ +typedef struct LodePNGColorMode +{ + /*header (IHDR)*/ + LodePNGColorType colortype; /*color type, see PNG standard or documentation further in this header file*/ + unsigned bitdepth; /*bits per sample, see PNG standard or documentation further in this header file*/ + + /* + palette (PLTE and tRNS) + + Dynamically allocated with the colors of the palette, including alpha. + When encoding a PNG, to store your colors in the palette of the LodePNGColorMode, first use + lodepng_palette_clear, then for each color use lodepng_palette_add. + If you encode an image without alpha with palette, don't forget to put value 255 in each A byte of the palette. + + When decoding, by default you can ignore this palette, since LodePNG already + fills the palette colors in the pixels of the raw RGBA output. + + The palette is only supported for color type 3. + */ + unsigned char* palette; /*palette in RGBARGBA... order. When allocated, must be either 0, or have size 1024*/ + size_t palettesize; /*palette size in number of colors (amount of bytes is 4 * palettesize)*/ + + /* + transparent color key (tRNS) + + This color uses the same bit depth as the bitdepth value in this struct, which can be 1-bit to 16-bit. + For greyscale PNGs, r, g and b will all 3 be set to the same. + + When decoding, by default you can ignore this information, since LodePNG sets + pixels with this key to transparent already in the raw RGBA output. + + The color key is only supported for color types 0 and 2. + */ + unsigned key_defined; /*is a transparent color key given? 0 = false, 1 = true*/ + unsigned key_r; /*red/greyscale component of color key*/ + unsigned key_g; /*green component of color key*/ + unsigned key_b; /*blue component of color key*/ +} LodePNGColorMode; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_color_mode_init(LodePNGColorMode* info); +void lodepng_color_mode_cleanup(LodePNGColorMode* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_color_mode_copy(LodePNGColorMode* dest, const LodePNGColorMode* source); + +void lodepng_palette_clear(LodePNGColorMode* info); +/*add 1 color to the palette*/ +unsigned lodepng_palette_add(LodePNGColorMode* info, + unsigned char r, unsigned char g, unsigned char b, unsigned char a); + +/*get the total amount of bits per pixel, based on colortype and bitdepth in the struct*/ +unsigned lodepng_get_bpp(const LodePNGColorMode* info); +/*get the amount of color channels used, based on colortype in the struct. +If a palette is used, it counts as 1 channel.*/ +unsigned lodepng_get_channels(const LodePNGColorMode* info); +/*is it a greyscale type? (only colortype 0 or 4)*/ +unsigned lodepng_is_greyscale_type(const LodePNGColorMode* info); +/*has it got an alpha channel? (only colortype 2 or 6)*/ +unsigned lodepng_is_alpha_type(const LodePNGColorMode* info); +/*has it got a palette? (only colortype 3)*/ +unsigned lodepng_is_palette_type(const LodePNGColorMode* info); +/*only returns true if there is a palette and there is a value in the palette with alpha < 255. +Loops through the palette to check this.*/ +unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info); +/* +Check if the given color info indicates the possibility of having non-opaque pixels in the PNG image. +Returns true if the image can have translucent or invisible pixels (it still be opaque if it doesn't use such pixels). +Returns false if the image can only have opaque pixels. +In detail, it returns true only if it's a color type with alpha, or has a palette with non-opaque values, +or if "key_defined" is true. +*/ +unsigned lodepng_can_have_alpha(const LodePNGColorMode* info); +/*Returns the byte size of a raw image buffer with given width, height and color mode*/ +size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +/*The information of a Time chunk in PNG.*/ +typedef struct LodePNGTime +{ + unsigned year; /*2 bytes used (0-65535)*/ + unsigned month; /*1-12*/ + unsigned day; /*1-31*/ + unsigned hour; /*0-23*/ + unsigned minute; /*0-59*/ + unsigned second; /*0-60 (to allow for leap seconds)*/ +} LodePNGTime; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/*Information about the PNG image, except pixels, width and height.*/ +typedef struct LodePNGInfo +{ + /*header (IHDR), palette (PLTE) and transparency (tRNS) chunks*/ + unsigned compression_method;/*compression method of the original file. Always 0.*/ + unsigned filter_method; /*filter method of the original file*/ + unsigned interlace_method; /*interlace method of the original file*/ + LodePNGColorMode color; /*color type and bits, palette and transparency of the PNG file*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /* + suggested background color chunk (bKGD) + This color uses the same color mode as the PNG (except alpha channel), which can be 1-bit to 16-bit. + + For greyscale PNGs, r, g and b will all 3 be set to the same. When encoding + the encoder writes the red one. For palette PNGs: When decoding, the RGB value + will be stored, not a palette index. But when encoding, specify the index of + the palette in background_r, the other two are then ignored. + + The decoder does not use this background color to edit the color of pixels. + */ + unsigned background_defined; /*is a suggested background color given?*/ + unsigned background_r; /*red component of suggested background color*/ + unsigned background_g; /*green component of suggested background color*/ + unsigned background_b; /*blue component of suggested background color*/ + + /* + non-international text chunks (tEXt and zTXt) + + The char** arrays each contain num strings. The actual messages are in + text_strings, while text_keys are keywords that give a short description what + the actual text represents, e.g. Title, Author, Description, or anything else. + + A keyword is minimum 1 character and maximum 79 characters long. It's + discouraged to use a single line length longer than 79 characters for texts. + + Don't allocate these text buffers yourself. Use the init/cleanup functions + correctly and use lodepng_add_text and lodepng_clear_text. + */ + size_t text_num; /*the amount of texts in these char** buffers (there may be more texts in itext)*/ + char** text_keys; /*the keyword of a text chunk (e.g. "Comment")*/ + char** text_strings; /*the actual text*/ + + /* + international text chunks (iTXt) + Similar to the non-international text chunks, but with additional strings + "langtags" and "transkeys". + */ + size_t itext_num; /*the amount of international texts in this PNG*/ + char** itext_keys; /*the English keyword of the text chunk (e.g. "Comment")*/ + char** itext_langtags; /*language tag for this text's language, ISO/IEC 646 string, e.g. ISO 639 language tag*/ + char** itext_transkeys; /*keyword translated to the international language - UTF-8 string*/ + char** itext_strings; /*the actual international text - UTF-8 string*/ + + /*time chunk (tIME)*/ + unsigned time_defined; /*set to 1 to make the encoder generate a tIME chunk*/ + LodePNGTime time; + + /*phys chunk (pHYs)*/ + unsigned phys_defined; /*if 0, there is no pHYs chunk and the values below are undefined, if 1 else there is one*/ + unsigned phys_x; /*pixels per unit in x direction*/ + unsigned phys_y; /*pixels per unit in y direction*/ + unsigned phys_unit; /*may be 0 (unknown unit) or 1 (metre)*/ + + /* + unknown chunks + There are 3 buffers, one for each position in the PNG where unknown chunks can appear + each buffer contains all unknown chunks for that position consecutively + The 3 buffers are the unknown chunks between certain critical chunks: + 0: IHDR-PLTE, 1: PLTE-IDAT, 2: IDAT-IEND + Do not allocate or traverse this data yourself. Use the chunk traversing functions declared + later, such as lodepng_chunk_next and lodepng_chunk_append, to read/write this struct. + */ + unsigned char* unknown_chunks_data[3]; + size_t unknown_chunks_size[3]; /*size in bytes of the unknown chunks, given for protection*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGInfo; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_info_init(LodePNGInfo* info); +void lodepng_info_cleanup(LodePNGInfo* info); +/*return value is error code (0 means no error)*/ +unsigned lodepng_info_copy(LodePNGInfo* dest, const LodePNGInfo* source); + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS +void lodepng_clear_text(LodePNGInfo* info); /*use this to clear the texts again after you filled them in*/ +unsigned lodepng_add_text(LodePNGInfo* info, const char* key, const char* str); /*push back both texts at once*/ + +void lodepng_clear_itext(LodePNGInfo* info); /*use this to clear the itexts again after you filled them in*/ +unsigned lodepng_add_itext(LodePNGInfo* info, const char* key, const char* langtag, + const char* transkey, const char* str); /*push back the 4 texts of 1 chunk at once*/ +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ + +/* +Converts raw buffer from one color type to another color type, based on +LodePNGColorMode structs to describe the input and output color type. +See the reference manual at the end of this header file to see which color conversions are supported. +return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) +The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel +of the output color type (lodepng_get_bpp). +For < 8 bpp images, there should not be padding bits at the end of scanlines. +For 16-bit per channel colors, uses big endian format like PNG does. +Return value is LodePNG error code +*/ +unsigned lodepng_convert(unsigned char* out, const unsigned char* in, + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h); + +#ifdef LODEPNG_COMPILE_DECODER +/* +Settings for the decoder. This contains settings for the PNG and the Zlib +decoder, but not the Info settings from the Info structs. +*/ +typedef struct LodePNGDecoderSettings +{ + LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ + + unsigned ignore_crc; /*ignore CRC checksums*/ + + unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ + +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + unsigned read_text_chunks; /*if false but remember_unknown_chunks is true, they're stored in the unknown chunks*/ + /*store all bytes from unknown chunks in the LodePNGInfo (off by default, useful for a png editor)*/ + unsigned remember_unknown_chunks; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGDecoderSettings; + +void lodepng_decoder_settings_init(LodePNGDecoderSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/*automatically use color type with less bits per pixel if losslessly possible. Default: AUTO*/ +typedef enum LodePNGFilterStrategy +{ + /*every filter at zero*/ + LFS_ZERO, + /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/ + LFS_MINSUM, + /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending + on the image, this is better or worse than minsum.*/ + LFS_ENTROPY, + /* + Brute-force-search PNG filters by compressing each filter for each scanline. + Experimental, very slow, and only rarely gives better compression than MINSUM. + */ + LFS_BRUTE_FORCE, + /*use predefined_filters buffer: you specify the filter type for each scanline*/ + LFS_PREDEFINED +} LodePNGFilterStrategy; + +/*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding. +Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ +typedef struct LodePNGColorProfile +{ + unsigned colored; /*not greyscale*/ + unsigned key; /*image is not opaque and color key is possible instead of full alpha*/ + unsigned short key_r; /*key values, always as 16-bit, in 8-bit case the byte is duplicated, e.g. 65535 means 255*/ + unsigned short key_g; + unsigned short key_b; + unsigned alpha; /*image is not opaque and alpha channel or alpha palette required*/ + unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/ + unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/ + unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.*/ +} LodePNGColorProfile; + +void lodepng_color_profile_init(LodePNGColorProfile* profile); + +/*Get a LodePNGColorProfile of the image.*/ +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); +/*The function LodePNG uses internally to decide the PNG color with auto_convert. +Chooses an optimal color model, e.g. grey if only grey pixels, palette if < 256 colors, ...*/ +unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); + +/*Settings for the encoder.*/ +typedef struct LodePNGEncoderSettings +{ + LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/ + + unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/ + + /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than + 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to + completely follow the official PNG heuristic, filter_palette_zero must be true and + filter_strategy must be LFS_MINSUM*/ + unsigned filter_palette_zero; + /*Which filter strategy to use when not using zeroes due to filter_palette_zero. + Set filter_palette_zero to 0 to ensure always using your chosen strategy. Default: LFS_MINSUM*/ + LodePNGFilterStrategy filter_strategy; + /*used if filter_strategy is LFS_PREDEFINED. In that case, this must point to a buffer with + the same length as the amount of scanlines in the image, and each value must <= 5. You + have to cleanup this buffer, LodePNG will never free it. Don't forget that filter_palette_zero + must be set to 0 to ensure this is also used on palette or low bitdepth images.*/ + const unsigned char* predefined_filters; + + /*force creating a PLTE chunk if colortype is 2 or 6 (= a suggested palette). + If colortype is 3, PLTE is _always_ created.*/ + unsigned force_palette; +#ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS + /*add LodePNG identifier and version as a text chunk, for debugging*/ + unsigned add_id; + /*encode text chunks as zTXt chunks instead of tEXt chunks, and use compression in iTXt chunks*/ + unsigned text_compression; +#endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ +} LodePNGEncoderSettings; + +void lodepng_encoder_settings_init(LodePNGEncoderSettings* settings); +#endif /*LODEPNG_COMPILE_ENCODER*/ + + +#if defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) +/*The settings, state and information for extended encoding and decoding.*/ +typedef struct LodePNGState +{ +#ifdef LODEPNG_COMPILE_DECODER + LodePNGDecoderSettings decoder; /*the decoding settings*/ +#endif /*LODEPNG_COMPILE_DECODER*/ +#ifdef LODEPNG_COMPILE_ENCODER + LodePNGEncoderSettings encoder; /*the encoding settings*/ +#endif /*LODEPNG_COMPILE_ENCODER*/ + LodePNGColorMode info_raw; /*specifies the format in which you would like to get the raw pixel buffer*/ + LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ + unsigned error; +#ifdef LODEPNG_COMPILE_CPP + /* For the lodepng::State subclass. */ + virtual ~LodePNGState(){} +#endif +} LodePNGState; + +/*init, cleanup and copy functions to use with this struct*/ +void lodepng_state_init(LodePNGState* state); +void lodepng_state_cleanup(LodePNGState* state); +void lodepng_state_copy(LodePNGState* dest, const LodePNGState* source); +#endif /* defined(LODEPNG_COMPILE_DECODER) || defined(LODEPNG_COMPILE_ENCODER) */ + +#ifdef LODEPNG_COMPILE_DECODER +/* +Same as lodepng_decode_memory, but uses a LodePNGState to allow custom settings and +getting much more information about the PNG image and color mode. +*/ +unsigned lodepng_decode(unsigned char** out, unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); + +/* +Read the PNG header, but not the actual data. This returns only the information +that is in the header chunk of the PNG, such as width, height and color type. The +information is placed in the info_png field of the LodePNGState. +*/ +unsigned lodepng_inspect(unsigned* w, unsigned* h, + LodePNGState* state, + const unsigned char* in, size_t insize); +#endif /*LODEPNG_COMPILE_DECODER*/ + + +#ifdef LODEPNG_COMPILE_ENCODER +/*This function allocates the out buffer with standard malloc and stores the size in *outsize.*/ +unsigned lodepng_encode(unsigned char** out, size_t* outsize, + const unsigned char* image, unsigned w, unsigned h, + LodePNGState* state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +/* +The lodepng_chunk functions are normally not needed, except to traverse the +unknown chunks stored in the LodePNGInfo struct, or add new ones to it. +It also allows traversing the chunks of an encoded PNG file yourself. + +PNG standard chunk naming conventions: +First byte: uppercase = critical, lowercase = ancillary +Second byte: uppercase = public, lowercase = private +Third byte: must be uppercase +Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy +*/ + +/* +Gets the length of the data of the chunk. Total chunk length has 12 bytes more. +There must be at least 4 bytes to read from. If the result value is too large, +it may be corrupt data. +*/ +unsigned lodepng_chunk_length(const unsigned char* chunk); + +/*puts the 4-byte type in null terminated string*/ +void lodepng_chunk_type(char type[5], const unsigned char* chunk); + +/*check if the type is the given type*/ +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type); + +/*0: it's one of the critical chunk types, 1: it's an ancillary chunk (see PNG standard)*/ +unsigned char lodepng_chunk_ancillary(const unsigned char* chunk); + +/*0: public, 1: private (see PNG standard)*/ +unsigned char lodepng_chunk_private(const unsigned char* chunk); + +/*0: the chunk is unsafe to copy, 1: the chunk is safe to copy (see PNG standard)*/ +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk); + +/*get pointer to the data of the chunk, where the input points to the header of the chunk*/ +unsigned char* lodepng_chunk_data(unsigned char* chunk); +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk); + +/*returns 0 if the crc is correct, 1 if it's incorrect (0 for OK as usual!)*/ +unsigned lodepng_chunk_check_crc(const unsigned char* chunk); + +/*generates the correct CRC from the data and puts it in the last 4 bytes of the chunk*/ +void lodepng_chunk_generate_crc(unsigned char* chunk); + +/*iterate to next chunks. don't use on IEND chunk, as there is no next chunk then*/ +unsigned char* lodepng_chunk_next(unsigned char* chunk); +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk); + +/* +Appends chunk to the data in out. The given chunk should already have its chunk header. +The out variable and outlength are updated to reflect the new reallocated buffer. +Returns error code (0 if it went ok) +*/ +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk); + +/* +Appends new chunk to out. The chunk to append is given by giving its length, type +and data separately. The type is a 4-letter string. +The out variable and outlength are updated to reflect the new reallocated buffer. +Returne error code (0 if it went ok) +*/ +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data); + + +/*Calculate CRC32 of buffer*/ +unsigned lodepng_crc32(const unsigned char* buf, size_t len); +#endif /*LODEPNG_COMPILE_PNG*/ + + +#ifdef LODEPNG_COMPILE_ZLIB +/* +This zlib part can be used independently to zlib compress and decompress a +buffer. It cannot be used to create gzip files however, and it only supports the +part of zlib that is required for PNG, it does not support dictionaries. +*/ + +#ifdef LODEPNG_COMPILE_DECODER +/*Inflate a buffer. Inflate is the decompression step of deflate. Out buffer must be freed after use.*/ +unsigned lodepng_inflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); + +/* +Decompresses Zlib data. Reallocates the out buffer and appends the data. The +data must be according to the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_decompress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGDecompressSettings* settings); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* +Compresses data with Zlib. Reallocates the out buffer and appends the data. +Zlib adds a small header and trailer around the deflate data. +The data is output in the format of the zlib specification. +Either, *out must be NULL and *outsize must be 0, or, *out must be a valid +buffer and *outsize its size in bytes. out must be freed by user after usage. +*/ +unsigned lodepng_zlib_compress(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +/* +Find length-limited Huffman code for given frequencies. This function is in the +public interface only for tests, it's used internally by lodepng_deflate. +*/ +unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, + size_t numcodes, unsigned maxbitlen); + +/*Compress a buffer with deflate. See RFC 1951. Out buffer must be freed after use.*/ +unsigned lodepng_deflate(unsigned char** out, size_t* outsize, + const unsigned char* in, size_t insize, + const LodePNGCompressSettings* settings); + +#endif /*LODEPNG_COMPILE_ENCODER*/ +#endif /*LODEPNG_COMPILE_ZLIB*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into buffer. The function allocates the out buffer, and +after usage you should free it. +out: output parameter, contains pointer to loaded buffer. +outsize: output parameter, size of the allocated out buffer +filename: the path to the file to load +return value: error code (0 means ok) +*/ +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename); + +/* +Save a file from buffer to disk. Warning, if it exists, this function overwrites +the file without warning! +buffer: the buffer to write +buffersize: size of the buffer to write +filename: the path to the file to save to +return value: error code (0 means ok) +*/ +unsigned lodepng_save_file(const unsigned char* buffer, size_t buffersize, const char* filename); +#endif /*LODEPNG_COMPILE_DISK*/ + +#ifdef LODEPNG_COMPILE_CPP +/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */ +namespace lodepng +{ +#ifdef LODEPNG_COMPILE_PNG +class State : public LodePNGState +{ + public: + State(); + State(const State& other); + virtual ~State(); + State& operator=(const State& other); +}; + +#ifdef LODEPNG_COMPILE_DECODER +/* Same as other lodepng::decode, but using a State for more settings and information. */ +unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, + State& state, + const unsigned char* in, size_t insize); +unsigned decode(std::vector<unsigned char>& out, unsigned& w, unsigned& h, + State& state, + const std::vector<unsigned char>& in); +#endif /*LODEPNG_COMPILE_DECODER*/ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Same as other lodepng::encode, but using a State for more settings and information. */ +unsigned encode(std::vector<unsigned char>& out, + const unsigned char* in, unsigned w, unsigned h, + State& state); +unsigned encode(std::vector<unsigned char>& out, + const std::vector<unsigned char>& in, unsigned w, unsigned h, + State& state); +#endif /*LODEPNG_COMPILE_ENCODER*/ + +#ifdef LODEPNG_COMPILE_DISK +/* +Load a file from disk into an std::vector. +return value: error code (0 means ok) +*/ +unsigned load_file(std::vector<unsigned char>& buffer, const std::string& filename); + +/* +Save the binary data in an std::vector to a file on disk. The file is overwritten +without warning. +*/ +unsigned save_file(const std::vector<unsigned char>& buffer, const std::string& filename); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_PNG */ + +#ifdef LODEPNG_COMPILE_ZLIB +#ifdef LODEPNG_COMPILE_DECODER +/* Zlib-decompress an unsigned char buffer */ +unsigned decompress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); + +/* Zlib-decompress an std::vector */ +unsigned decompress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, + const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); +#endif /* LODEPNG_COMPILE_DECODER */ + +#ifdef LODEPNG_COMPILE_ENCODER +/* Zlib-compress an unsigned char buffer */ +unsigned compress(std::vector<unsigned char>& out, const unsigned char* in, size_t insize, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); + +/* Zlib-compress an std::vector */ +unsigned compress(std::vector<unsigned char>& out, const std::vector<unsigned char>& in, + const LodePNGCompressSettings& settings = lodepng_default_compress_settings); +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ +} /* namespace lodepng */ +#endif /*LODEPNG_COMPILE_CPP*/ + +/* +TODO: +[.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often +[.] check compatibility with various compilers - done but needs to be redone for every newer version +[X] converting color to 16-bit per channel types +[ ] read all public PNG chunk types (but never let the color profile and gamma ones touch RGB values) +[ ] make sure encoder generates no chunks with size > (2^31)-1 +[ ] partial decoding (stream processing) +[X] let the "isFullyOpaque" function check color keys and transparent palettes too +[X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" +[ ] don't stop decoding on errors like 69, 57, 58 (make warnings) +[ ] make warnings like: oob palette, checksum fail, data after iend, wrong/unknown crit chunk, no null terminator in text, ... +[ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes +[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... +[ ] allow user to give data (void*) to custom allocator +*/ + +#endif /*LODEPNG_H inclusion guard*/ + +/* +LodePNG Documentation +--------------------- + +0. table of contents +-------------------- + + 1. about + 1.1. supported features + 1.2. features not supported + 2. C and C++ version + 3. security + 4. decoding + 5. encoding + 6. color conversions + 6.1. PNG color types + 6.2. color conversions + 6.3. padding bits + 6.4. A note about 16-bits per channel and endianness + 7. error values + 8. chunks and PNG editing + 9. compiler support + 10. examples + 10.1. decoder C++ example + 10.2. decoder C example + 11. state settings reference + 12. changes + 13. contact information + + +1. about +-------- + +PNG is a file format to store raster images losslessly with good compression, +supporting different color types and alpha channel. + +LodePNG is a PNG codec according to the Portable Network Graphics (PNG) +Specification (Second Edition) - W3C Recommendation 10 November 2003. + +The specifications used are: + +*) Portable Network Graphics (PNG) Specification (Second Edition): + http://www.w3.org/TR/2003/REC-PNG-20031110 +*) RFC 1950 ZLIB Compressed Data Format version 3.3: + http://www.gzip.org/zlib/rfc-zlib.html +*) RFC 1951 DEFLATE Compressed Data Format Specification ver 1.3: + http://www.gzip.org/zlib/rfc-deflate.html + +The most recent version of LodePNG can currently be found at +http://lodev.org/lodepng/ + +LodePNG works both in C (ISO C90) and C++, with a C++ wrapper that adds +extra functionality. + +LodePNG exists out of two files: +-lodepng.h: the header file for both C and C++ +-lodepng.c(pp): give it the name lodepng.c or lodepng.cpp (or .cc) depending on your usage + +If you want to start using LodePNG right away without reading this doc, get the +examples from the LodePNG website to see how to use it in code, or check the +smaller examples in chapter 13 here. + +LodePNG is simple but only supports the basic requirements. To achieve +simplicity, the following design choices were made: There are no dependencies +on any external library. There are functions to decode and encode a PNG with +a single function call, and extended versions of these functions taking a +LodePNGState struct allowing to specify or get more information. By default +the colors of the raw image are always RGB or RGBA, no matter what color type +the PNG file uses. To read and write files, there are simple functions to +convert the files to/from buffers in memory. + +This all makes LodePNG suitable for loading textures in games, demos and small +programs, ... It's less suitable for full fledged image editors, loading PNGs +over network (it requires all the image data to be available before decoding can +begin), life-critical systems, ... + +1.1. supported features +----------------------- + +The following features are supported by the decoder: + +*) decoding of PNGs with any color type, bit depth and interlace mode, to a 24- or 32-bit color raw image, + or the same color type as the PNG +*) encoding of PNGs, from any raw image to 24- or 32-bit color, or the same color type as the raw image +*) Adam7 interlace and deinterlace for any color type +*) loading the image from harddisk or decoding it from a buffer from other sources than harddisk +*) support for alpha channels, including RGBA color model, translucent palettes and color keying +*) zlib decompression (inflate) +*) zlib compression (deflate) +*) CRC32 and ADLER32 checksums +*) handling of unknown chunks, allowing making a PNG editor that stores custom and unknown chunks. +*) the following chunks are supported (generated/interpreted) by both encoder and decoder: + IHDR: header information + PLTE: color palette + IDAT: pixel data + IEND: the final chunk + tRNS: transparency for palettized images + tEXt: textual information + zTXt: compressed textual information + iTXt: international textual information + bKGD: suggested background color + pHYs: physical dimensions + tIME: modification time + +1.2. features not supported +--------------------------- + +The following features are _not_ supported: + +*) some features needed to make a conformant PNG-Editor might be still missing. +*) partial loading/stream processing. All data must be available and is processed in one call. +*) The following public chunks are not supported but treated as unknown chunks by LodePNG + cHRM, gAMA, iCCP, sRGB, sBIT, hIST, sPLT + Some of these are not supported on purpose: LodePNG wants to provide the RGB values + stored in the pixels, not values modified by system dependent gamma or color models. + + +2. C and C++ version +-------------------- + +The C version uses buffers allocated with alloc that you need to free() +yourself. You need to use init and cleanup functions for each struct whenever +using a struct from the C version to avoid exploits and memory leaks. + +The C++ version has extra functions with std::vectors in the interface and the +lodepng::State class which is a LodePNGState with constructor and destructor. + +These files work without modification for both C and C++ compilers because all +the additional C++ code is in "#ifdef __cplusplus" blocks that make C-compilers +ignore it, and the C code is made to compile both with strict ISO C90 and C++. + +To use the C++ version, you need to rename the source file to lodepng.cpp +(instead of lodepng.c), and compile it with a C++ compiler. + +To use the C version, you need to rename the source file to lodepng.c (instead +of lodepng.cpp), and compile it with a C compiler. + + +3. Security +----------- + +Even if carefully designed, it's always possible that LodePNG contains possible +exploits. If you discover one, please let me know, and it will be fixed. + +When using LodePNG, care has to be taken with the C version of LodePNG, as well +as the C-style structs when working with C++. The following conventions are used +for all C-style structs: + +-if a struct has a corresponding init function, always call the init function when making a new one +-if a struct has a corresponding cleanup function, call it before the struct disappears to avoid memory leaks +-if a struct has a corresponding copy function, use the copy function instead of "=". + The destination must also be inited already. + + +4. Decoding +----------- + +Decoding converts a PNG compressed image to a raw pixel buffer. + +Most documentation on using the decoder is at its declarations in the header +above. For C, simple decoding can be done with functions such as +lodepng_decode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_decode. For C++, all decoding can be done with the +various lodepng::decode functions, and lodepng::State can be used for advanced +features. + +When using the LodePNGState, it uses the following fields for decoding: +*) LodePNGInfo info_png: it stores extra information about the PNG (the input) in here +*) LodePNGColorMode info_raw: here you can say what color mode of the raw image (the output) you want to get +*) LodePNGDecoderSettings decoder: you can specify a few extra settings for the decoder to use + +LodePNGInfo info_png +-------------------- + +After decoding, this contains extra information of the PNG image, except the actual +pixels, width and height because these are already gotten directly from the decoder +functions. + +It contains for example the original color type of the PNG image, text comments, +suggested background color, etc... More details about the LodePNGInfo struct are +at its declaration documentation. + +LodePNGColorMode info_raw +------------------------- + +When decoding, here you can specify which color type you want +the resulting raw image to be. If this is different from the colortype of the +PNG, then the decoder will automatically convert the result. This conversion +always works, except if you want it to convert a color PNG to greyscale or to +a palette with missing colors. + +By default, 32-bit color is used for the result. + +LodePNGDecoderSettings decoder +------------------------------ + +The settings can be used to ignore the errors created by invalid CRC and Adler32 +chunks, and to disable the decoding of tEXt chunks. + +There's also a setting color_convert, true by default. If false, no conversion +is done, the resulting data will be as it was in the PNG (after decompression) +and you'll have to puzzle the colors of the pixels together yourself using the +color type information in the LodePNGInfo. + + +5. Encoding +----------- + +Encoding converts a raw pixel buffer to a PNG compressed image. + +Most documentation on using the encoder is at its declarations in the header +above. For C, simple encoding can be done with functions such as +lodepng_encode32, and more advanced decoding can be done with the struct +LodePNGState and lodepng_encode. For C++, all encoding can be done with the +various lodepng::encode functions, and lodepng::State can be used for advanced +features. + +Like the decoder, the encoder can also give errors. However it gives less errors +since the encoder input is trusted, the decoder input (a PNG image that could +be forged by anyone) is not trusted. + +When using the LodePNGState, it uses the following fields for encoding: +*) LodePNGInfo info_png: here you specify how you want the PNG (the output) to be. +*) LodePNGColorMode info_raw: here you say what color type of the raw image (the input) has +*) LodePNGEncoderSettings encoder: you can specify a few settings for the encoder to use + +LodePNGInfo info_png +-------------------- + +When encoding, you use this the opposite way as when decoding: for encoding, +you fill in the values you want the PNG to have before encoding. By default it's +not needed to specify a color type for the PNG since it's automatically chosen, +but it's possible to choose it yourself given the right settings. + +The encoder will not always exactly match the LodePNGInfo struct you give, +it tries as close as possible. Some things are ignored by the encoder. The +encoder uses, for example, the following settings from it when applicable: +colortype and bitdepth, text chunks, time chunk, the color key, the palette, the +background color, the interlace method, unknown chunks, ... + +When encoding to a PNG with colortype 3, the encoder will generate a PLTE chunk. +If the palette contains any colors for which the alpha channel is not 255 (so +there are translucent colors in the palette), it'll add a tRNS chunk. + +LodePNGColorMode info_raw +------------------------- + +You specify the color type of the raw image that you give to the input here, +including a possible transparent color key and palette you happen to be using in +your raw image data. + +By default, 32-bit color is assumed, meaning your input has to be in RGBA +format with 4 bytes (unsigned chars) per pixel. + +LodePNGEncoderSettings encoder +------------------------------ + +The following settings are supported (some are in sub-structs): +*) auto_convert: when this option is enabled, the encoder will +automatically choose the smallest possible color mode (including color key) that +can encode the colors of all pixels without information loss. +*) btype: the block type for LZ77. 0 = uncompressed, 1 = fixed huffman tree, + 2 = dynamic huffman tree (best compression). Should be 2 for proper + compression. +*) use_lz77: whether or not to use LZ77 for compressed block types. Should be + true for proper compression. +*) windowsize: the window size used by the LZ77 encoder (1 - 32768). Has value + 2048 by default, but can be set to 32768 for better, but slow, compression. +*) force_palette: if colortype is 2 or 6, you can make the encoder write a PLTE + chunk if force_palette is true. This can used as suggested palette to convert + to by viewers that don't support more than 256 colors (if those still exist) +*) add_id: add text chunk "Encoder: LodePNG <version>" to the image. +*) text_compression: default 1. If 1, it'll store texts as zTXt instead of tEXt chunks. + zTXt chunks use zlib compression on the text. This gives a smaller result on + large texts but a larger result on small texts (such as a single program name). + It's all tEXt or all zTXt though, there's no separate setting per text yet. + + +6. color conversions +-------------------- + +An important thing to note about LodePNG, is that the color type of the PNG, and +the color type of the raw image, are completely independent. By default, when +you decode a PNG, you get the result as a raw image in the color type you want, +no matter whether the PNG was encoded with a palette, greyscale or RGBA color. +And if you encode an image, by default LodePNG will automatically choose the PNG +color type that gives good compression based on the values of colors and amount +of colors in the image. It can be configured to let you control it instead as +well, though. + +To be able to do this, LodePNG does conversions from one color mode to another. +It can convert from almost any color type to any other color type, except the +following conversions: RGB to greyscale is not supported, and converting to a +palette when the palette doesn't have a required color is not supported. This is +not supported on purpose: this is information loss which requires a color +reduction algorithm that is beyong the scope of a PNG encoder (yes, RGB to grey +is easy, but there are multiple ways if you want to give some channels more +weight). + +By default, when decoding, you get the raw image in 32-bit RGBA or 24-bit RGB +color, no matter what color type the PNG has. And by default when encoding, +LodePNG automatically picks the best color model for the output PNG, and expects +the input image to be 32-bit RGBA or 24-bit RGB. So, unless you want to control +the color format of the images yourself, you can skip this chapter. + +6.1. PNG color types +-------------------- + +A PNG image can have many color types, ranging from 1-bit color to 64-bit color, +as well as palettized color modes. After the zlib decompression and unfiltering +in the PNG image is done, the raw pixel data will have that color type and thus +a certain amount of bits per pixel. If you want the output raw image after +decoding to have another color type, a conversion is done by LodePNG. + +The PNG specification gives the following color types: + +0: greyscale, bit depths 1, 2, 4, 8, 16 +2: RGB, bit depths 8 and 16 +3: palette, bit depths 1, 2, 4 and 8 +4: greyscale with alpha, bit depths 8 and 16 +6: RGBA, bit depths 8 and 16 + +Bit depth is the amount of bits per pixel per color channel. So the total amount +of bits per pixel is: amount of channels * bitdepth. + +6.2. color conversions +---------------------- + +As explained in the sections about the encoder and decoder, you can specify +color types and bit depths in info_png and info_raw to change the default +behaviour. + +If, when decoding, you want the raw image to be something else than the default, +you need to set the color type and bit depth you want in the LodePNGColorMode, +or the parameters colortype and bitdepth of the simple decoding function. + +If, when encoding, you use another color type than the default in the raw input +image, you need to specify its color type and bit depth in the LodePNGColorMode +of the raw image, or use the parameters colortype and bitdepth of the simple +encoding function. + +If, when encoding, you don't want LodePNG to choose the output PNG color type +but control it yourself, you need to set auto_convert in the encoder settings +to false, and specify the color type you want in the LodePNGInfo of the +encoder (including palette: it can generate a palette if auto_convert is true, +otherwise not). + +If the input and output color type differ (whether user chosen or auto chosen), +LodePNG will do a color conversion, which follows the rules below, and may +sometimes result in an error. + +To avoid some confusion: +-the decoder converts from PNG to raw image +-the encoder converts from raw image to PNG +-the colortype and bitdepth in LodePNGColorMode info_raw, are those of the raw image +-the colortype and bitdepth in the color field of LodePNGInfo info_png, are those of the PNG +-when encoding, the color type in LodePNGInfo is ignored if auto_convert + is enabled, it is automatically generated instead +-when decoding, the color type in LodePNGInfo is set by the decoder to that of the original + PNG image, but it can be ignored since the raw image has the color type you requested instead +-if the color type of the LodePNGColorMode and PNG image aren't the same, a conversion + between the color types is done if the color types are supported. If it is not + supported, an error is returned. If the types are the same, no conversion is done. +-even though some conversions aren't supported, LodePNG supports loading PNGs from any + colortype and saving PNGs to any colortype, sometimes it just requires preparing + the raw image correctly before encoding. +-both encoder and decoder use the same color converter. + +Non supported color conversions: +-color to greyscale: no error is thrown, but the result will look ugly because +only the red channel is taken +-anything to palette when that palette does not have that color in it: in this +case an error is thrown + +Supported color conversions: +-anything to 8-bit RGB, 8-bit RGBA, 16-bit RGB, 16-bit RGBA +-any grey or grey+alpha, to grey or grey+alpha +-anything to a palette, as long as the palette has the requested colors in it +-removing alpha channel +-higher to smaller bitdepth, and vice versa + +If you want no color conversion to be done (e.g. for speed or control): +-In the encoder, you can make it save a PNG with any color type by giving the +raw color mode and LodePNGInfo the same color mode, and setting auto_convert to +false. +-In the decoder, you can make it store the pixel data in the same color type +as the PNG has, by setting the color_convert setting to false. Settings in +info_raw are then ignored. + +The function lodepng_convert does the color conversion. It is available in the +interface but normally isn't needed since the encoder and decoder already call +it. + +6.3. padding bits +----------------- + +In the PNG file format, if a less than 8-bit per pixel color type is used and the scanlines +have a bit amount that isn't a multiple of 8, then padding bits are used so that each +scanline starts at a fresh byte. But that is NOT true for the LodePNG raw input and output. +The raw input image you give to the encoder, and the raw output image you get from the decoder +will NOT have these padding bits, e.g. in the case of a 1-bit image with a width +of 7 pixels, the first pixel of the second scanline will the the 8th bit of the first byte, +not the first bit of a new byte. + +6.4. A note about 16-bits per channel and endianness +---------------------------------------------------- + +LodePNG uses unsigned char arrays for 16-bit per channel colors too, just like +for any other color format. The 16-bit values are stored in big endian (most +significant byte first) in these arrays. This is the opposite order of the +little endian used by x86 CPU's. + +LodePNG always uses big endian because the PNG file format does so internally. +Conversions to other formats than PNG uses internally are not supported by +LodePNG on purpose, there are myriads of formats, including endianness of 16-bit +colors, the order in which you store R, G, B and A, and so on. Supporting and +converting to/from all that is outside the scope of LodePNG. + +This may mean that, depending on your use case, you may want to convert the big +endian output of LodePNG to little endian with a for loop. This is certainly not +always needed, many applications and libraries support big endian 16-bit colors +anyway, but it means you cannot simply cast the unsigned char* buffer to an +unsigned short* buffer on x86 CPUs. + + +7. error values +--------------- + +All functions in LodePNG that return an error code, return 0 if everything went +OK, or a non-zero code if there was an error. + +The meaning of the LodePNG error values can be retrieved with the function +lodepng_error_text: given the numerical error code, it returns a description +of the error in English as a string. + +Check the implementation of lodepng_error_text to see the meaning of each code. + + +8. chunks and PNG editing +------------------------- + +If you want to add extra chunks to a PNG you encode, or use LodePNG for a PNG +editor that should follow the rules about handling of unknown chunks, or if your +program is able to read other types of chunks than the ones handled by LodePNG, +then that's possible with the chunk functions of LodePNG. + +A PNG chunk has the following layout: + +4 bytes length +4 bytes type name +length bytes data +4 bytes CRC + +8.1. iterating through chunks +----------------------------- + +If you have a buffer containing the PNG image data, then the first chunk (the +IHDR chunk) starts at byte number 8 of that buffer. The first 8 bytes are the +signature of the PNG and are not part of a chunk. But if you start at byte 8 +then you have a chunk, and can check the following things of it. + +NOTE: none of these functions check for memory buffer boundaries. To avoid +exploits, always make sure the buffer contains all the data of the chunks. +When using lodepng_chunk_next, make sure the returned value is within the +allocated memory. + +unsigned lodepng_chunk_length(const unsigned char* chunk): + +Get the length of the chunk's data. The total chunk length is this length + 12. + +void lodepng_chunk_type(char type[5], const unsigned char* chunk): +unsigned char lodepng_chunk_type_equals(const unsigned char* chunk, const char* type): + +Get the type of the chunk or compare if it's a certain type + +unsigned char lodepng_chunk_critical(const unsigned char* chunk): +unsigned char lodepng_chunk_private(const unsigned char* chunk): +unsigned char lodepng_chunk_safetocopy(const unsigned char* chunk): + +Check if the chunk is critical in the PNG standard (only IHDR, PLTE, IDAT and IEND are). +Check if the chunk is private (public chunks are part of the standard, private ones not). +Check if the chunk is safe to copy. If it's not, then, when modifying data in a critical +chunk, unsafe to copy chunks of the old image may NOT be saved in the new one if your +program doesn't handle that type of unknown chunk. + +unsigned char* lodepng_chunk_data(unsigned char* chunk): +const unsigned char* lodepng_chunk_data_const(const unsigned char* chunk): + +Get a pointer to the start of the data of the chunk. + +unsigned lodepng_chunk_check_crc(const unsigned char* chunk): +void lodepng_chunk_generate_crc(unsigned char* chunk): + +Check if the crc is correct or generate a correct one. + +unsigned char* lodepng_chunk_next(unsigned char* chunk): +const unsigned char* lodepng_chunk_next_const(const unsigned char* chunk): + +Iterate to the next chunk. This works if you have a buffer with consecutive chunks. Note that these +functions do no boundary checking of the allocated data whatsoever, so make sure there is enough +data available in the buffer to be able to go to the next chunk. + +unsigned lodepng_chunk_append(unsigned char** out, size_t* outlength, const unsigned char* chunk): +unsigned lodepng_chunk_create(unsigned char** out, size_t* outlength, unsigned length, + const char* type, const unsigned char* data): + +These functions are used to create new chunks that are appended to the data in *out that has +length *outlength. The append function appends an existing chunk to the new data. The create +function creates a new chunk with the given parameters and appends it. Type is the 4-letter +name of the chunk. + +8.2. chunks in info_png +----------------------- + +The LodePNGInfo struct contains fields with the unknown chunk in it. It has 3 +buffers (each with size) to contain 3 types of unknown chunks: +the ones that come before the PLTE chunk, the ones that come between the PLTE +and the IDAT chunks, and the ones that come after the IDAT chunks. +It's necessary to make the distionction between these 3 cases because the PNG +standard forces to keep the ordering of unknown chunks compared to the critical +chunks, but does not force any other ordering rules. + +info_png.unknown_chunks_data[0] is the chunks before PLTE +info_png.unknown_chunks_data[1] is the chunks after PLTE, before IDAT +info_png.unknown_chunks_data[2] is the chunks after IDAT + +The chunks in these 3 buffers can be iterated through and read by using the same +way described in the previous subchapter. + +When using the decoder to decode a PNG, you can make it store all unknown chunks +if you set the option settings.remember_unknown_chunks to 1. By default, this +option is off (0). + +The encoder will always encode unknown chunks that are stored in the info_png. +If you need it to add a particular chunk that isn't known by LodePNG, you can +use lodepng_chunk_append or lodepng_chunk_create to the chunk data in +info_png.unknown_chunks_data[x]. + +Chunks that are known by LodePNG should not be added in that way. E.g. to make +LodePNG add a bKGD chunk, set background_defined to true and add the correct +parameters there instead. + + +9. compiler support +------------------- + +No libraries other than the current standard C library are needed to compile +LodePNG. For the C++ version, only the standard C++ library is needed on top. +Add the files lodepng.c(pp) and lodepng.h to your project, include +lodepng.h where needed, and your program can read/write PNG files. + +It is compatible with C90 and up, and C++03 and up. + +If performance is important, use optimization when compiling! For both the +encoder and decoder, this makes a large difference. + +Make sure that LodePNG is compiled with the same compiler of the same version +and with the same settings as the rest of the program, or the interfaces with +std::vectors and std::strings in C++ can be incompatible. + +CHAR_BITS must be 8 or higher, because LodePNG uses unsigned chars for octets. + +*) gcc and g++ + +LodePNG is developed in gcc so this compiler is natively supported. It gives no +warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++ +version 4.7.1 on Linux, 32-bit and 64-bit. + +*) Clang + +Fully supported and warning-free. + +*) Mingw + +The Mingw compiler (a port of gcc for Windows) should be fully supported by +LodePNG. + +*) Visual Studio and Visual C++ Express Edition + +LodePNG should be warning-free with warning level W4. Two warnings were disabled +with pragmas though: warning 4244 about implicit conversions, and warning 4996 +where it wants to use a non-standard function fopen_s instead of the standard C +fopen. + +Visual Studio may want "stdafx.h" files to be included in each source file and +give an error "unexpected end of file while looking for precompiled header". +This is not standard C++ and will not be added to the stock LodePNG. You can +disable it for lodepng.cpp only by right clicking it, Properties, C/C++, +Precompiled Headers, and set it to Not Using Precompiled Headers there. + +NOTE: Modern versions of VS should be fully supported, but old versions, e.g. +VS6, are not guaranteed to work. + +*) Compilers on Macintosh + +LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for +C and C++. + +*) Other Compilers + +If you encounter problems on any compilers, feel free to let me know and I may +try to fix it if the compiler is modern and standards complient. + + +10. examples +------------ + +This decoder example shows the most basic usage of LodePNG. More complex +examples can be found on the LodePNG website. + +10.1. decoder C++ example +------------------------- + +#include "lodepng.h" +#include <iostream> + +int main(int argc, char *argv[]) +{ + const char* filename = argc > 1 ? argv[1] : "test.png"; + + //load and decode + std::vector<unsigned char> image; + unsigned width, height; + unsigned error = lodepng::decode(image, width, height, filename); + + //if there's an error, display it + if(error) std::cout << "decoder error " << error << ": " << lodepng_error_text(error) << std::endl; + + //the pixels are now in the vector "image", 4 bytes per pixel, ordered RGBARGBA..., use it as texture, draw it, ... +} + +10.2. decoder C example +----------------------- + +#include "lodepng.h" + +int main(int argc, char *argv[]) +{ + unsigned error; + unsigned char* image; + size_t width, height; + const char* filename = argc > 1 ? argv[1] : "test.png"; + + error = lodepng_decode32_file(&image, &width, &height, filename); + + if(error) printf("decoder error %u: %s\n", error, lodepng_error_text(error)); + + / * use image here * / + + free(image); + return 0; +} + +11. state settings reference +---------------------------- + +A quick reference of some settings to set on the LodePNGState + +For decoding: + +state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums +state.decoder.zlibsettings.custom_...: use custom inflate function +state.decoder.ignore_crc: ignore CRC checksums +state.decoder.color_convert: convert internal PNG color to chosen one +state.decoder.read_text_chunks: whether to read in text metadata chunks +state.decoder.remember_unknown_chunks: whether to read in unknown chunks +state.info_raw.colortype: desired color type for decoded image +state.info_raw.bitdepth: desired bit depth for decoded image +state.info_raw....: more color settings, see struct LodePNGColorMode +state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo + +For encoding: + +state.encoder.zlibsettings.btype: disable compression by setting it to 0 +state.encoder.zlibsettings.use_lz77: use LZ77 in compression +state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize +state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match +state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching +state.encoder.zlibsettings.lazymatching: try one more LZ77 matching +state.encoder.zlibsettings.custom_...: use custom deflate function +state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png +state.encoder.filter_palette_zero: PNG filter strategy for palette +state.encoder.filter_strategy: PNG filter strategy to encode with +state.encoder.force_palette: add palette even if not encoding to one +state.encoder.add_id: add LodePNG identifier and version as a text chunk +state.encoder.text_compression: use compressed text chunks for metadata +state.info_raw.colortype: color type of raw input image you provide +state.info_raw.bitdepth: bit depth of raw input image you provide +state.info_raw: more color settings, see struct LodePNGColorMode +state.info_png.color.colortype: desired color type if auto_convert is false +state.info_png.color.bitdepth: desired bit depth if auto_convert is false +state.info_png.color....: more color settings, see struct LodePNGColorMode +state.info_png....: more PNG related settings, see struct LodePNGInfo + + +12. changes +----------- + +The version number of LodePNG is the date of the change given in the format +yyyymmdd. + +Some changes aren't backwards compatible. Those are indicated with a (!) +symbol. + +*) 17 sep 2017: fix memory leak for some encoder input error cases +*) 27 nov 2016: grey+alpha auto color model detection bugfix +*) 18 apr 2016: Changed qsort to custom stable sort (for platforms w/o qsort). +*) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within + the limits of pure C90). +*) 08 dec 2015: Made load_file function return error if file can't be opened. +*) 24 okt 2015: Bugfix with decoding to palette output. +*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding. +*) 23 aug 2014: Reduced needless memory usage of decoder. +*) 28 jun 2014: Removed fix_png setting, always support palette OOB for + simplicity. Made ColorProfile public. +*) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization. +*) 22 dec 2013: Power of two windowsize required for optimization. +*) 15 apr 2013: Fixed bug with LAC_ALPHA and color key. +*) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png). +*) 11 mar 2013 (!): Bugfix with custom free. Changed from "my" to "lodepng_" + prefix for the custom allocators and made it possible with a new #define to + use custom ones in your project without needing to change lodepng's code. +*) 28 jan 2013: Bugfix with color key. +*) 27 okt 2012: Tweaks in text chunk keyword length error handling. +*) 8 okt 2012 (!): Added new filter strategy (entropy) and new auto color mode. + (no palette). Better deflate tree encoding. New compression tweak settings. + Faster color conversions while decoding. Some internal cleanups. +*) 23 sep 2012: Reduced warnings in Visual Studio a little bit. +*) 1 sep 2012 (!): Removed #define's for giving custom (de)compression functions + and made it work with function pointers instead. +*) 23 jun 2012: Added more filter strategies. Made it easier to use custom alloc + and free functions and toggle #defines from compiler flags. Small fixes. +*) 6 may 2012 (!): Made plugging in custom zlib/deflate functions more flexible. +*) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed + redundant C++ codec classes. Reduced amount of structs. Everything changed, + but it is cleaner now imho and functionality remains the same. Also fixed + several bugs and shrunk the implementation code. Made new samples. +*) 6 nov 2011 (!): By default, the encoder now automatically chooses the best + PNG color model and bit depth, based on the amount and type of colors of the + raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color. +*) 9 okt 2011: simpler hash chain implementation for the encoder. +*) 8 sep 2011: lz77 encoder lazy matching instead of greedy matching. +*) 23 aug 2011: tweaked the zlib compression parameters after benchmarking. + A bug with the PNG filtertype heuristic was fixed, so that it chooses much + better ones (it's quite significant). A setting to do an experimental, slow, + brute force search for PNG filter types is added. +*) 17 aug 2011 (!): changed some C zlib related function names. +*) 16 aug 2011: made the code less wide (max 120 characters per line). +*) 17 apr 2011: code cleanup. Bugfixes. Convert low to 16-bit per sample colors. +*) 21 feb 2011: fixed compiling for C90. Fixed compiling with sections disabled. +*) 11 dec 2010: encoding is made faster, based on suggestion by Peter Eastman + to optimize long sequences of zeros. +*) 13 nov 2010: added LodePNG_InfoColor_hasPaletteAlpha and + LodePNG_InfoColor_canHaveAlpha functions for convenience. +*) 7 nov 2010: added LodePNG_error_text function to get error code description. +*) 30 okt 2010: made decoding slightly faster +*) 26 okt 2010: (!) changed some C function and struct names (more consistent). + Reorganized the documentation and the declaration order in the header. +*) 08 aug 2010: only changed some comments and external samples. +*) 05 jul 2010: fixed bug thanks to warnings in the new gcc version. +*) 14 mar 2010: fixed bug where too much memory was allocated for char buffers. +*) 02 sep 2008: fixed bug where it could create empty tree that linux apps could + read by ignoring the problem but windows apps couldn't. +*) 06 jun 2008: added more error checks for out of memory cases. +*) 26 apr 2008: added a few more checks here and there to ensure more safety. +*) 06 mar 2008: crash with encoding of strings fixed +*) 02 feb 2008: support for international text chunks added (iTXt) +*) 23 jan 2008: small cleanups, and #defines to divide code in sections +*) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor. +*) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder. +*) 17 jan 2008: ability to encode and decode compressed zTXt chunks added + Also various fixes, such as in the deflate and the padding bits code. +*) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved + filtering code of encoder. +*) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A + C++ wrapper around this provides an interface almost identical to before. + Having LodePNG be pure ISO C90 makes it more portable. The C and C++ code + are together in these files but it works both for C and C++ compilers. +*) 29 dec 2007: (!) changed most integer types to unsigned int + other tweaks +*) 30 aug 2007: bug fixed which makes this Borland C++ compatible +*) 09 aug 2007: some VS2005 warnings removed again +*) 21 jul 2007: deflate code placed in new namespace separate from zlib code +*) 08 jun 2007: fixed bug with 2- and 4-bit color, and small interlaced images +*) 04 jun 2007: improved support for Visual Studio 2005: crash with accessing + invalid std::vector element [0] fixed, and level 3 and 4 warnings removed +*) 02 jun 2007: made the encoder add a tag with version by default +*) 27 may 2007: zlib and png code separated (but still in the same file), + simple encoder/decoder functions added for more simple usage cases +*) 19 may 2007: minor fixes, some code cleaning, new error added (error 69), + moved some examples from here to lodepng_examples.cpp +*) 12 may 2007: palette decoding bug fixed +*) 24 apr 2007: changed the license from BSD to the zlib license +*) 11 mar 2007: very simple addition: ability to encode bKGD chunks. +*) 04 mar 2007: (!) tEXt chunk related fixes, and support for encoding + palettized PNG images. Plus little interface change with palette and texts. +*) 03 mar 2007: Made it encode dynamic Huffman shorter with repeat codes. + Fixed a bug where the end code of a block had length 0 in the Huffman tree. +*) 26 feb 2007: Huffman compression with dynamic trees (BTYPE 2) now implemented + and supported by the encoder, resulting in smaller PNGs at the output. +*) 27 jan 2007: Made the Adler-32 test faster so that a timewaste is gone. +*) 24 jan 2007: gave encoder an error interface. Added color conversion from any + greyscale type to 8-bit greyscale with or without alpha. +*) 21 jan 2007: (!) Totally changed the interface. It allows more color types + to convert to and is more uniform. See the manual for how it works now. +*) 07 jan 2007: Some cleanup & fixes, and a few changes over the last days: + encode/decode custom tEXt chunks, separate classes for zlib & deflate, and + at last made the decoder give errors for incorrect Adler32 or Crc. +*) 01 jan 2007: Fixed bug with encoding PNGs with less than 8 bits per channel. +*) 29 dec 2006: Added support for encoding images without alpha channel, and + cleaned out code as well as making certain parts faster. +*) 28 dec 2006: Added "Settings" to the encoder. +*) 26 dec 2006: The encoder now does LZ77 encoding and produces much smaller files now. + Removed some code duplication in the decoder. Fixed little bug in an example. +*) 09 dec 2006: (!) Placed output parameters of public functions as first parameter. + Fixed a bug of the decoder with 16-bit per color. +*) 15 okt 2006: Changed documentation structure +*) 09 okt 2006: Encoder class added. It encodes a valid PNG image from the + given image buffer, however for now it's not compressed. +*) 08 sep 2006: (!) Changed to interface with a Decoder class +*) 30 jul 2006: (!) LodePNG_InfoPng , width and height are now retrieved in different + way. Renamed decodePNG to decodePNGGeneric. +*) 29 jul 2006: (!) Changed the interface: image info is now returned as a + struct of type LodePNG::LodePNG_Info, instead of a vector, which was a bit clumsy. +*) 28 jul 2006: Cleaned the code and added new error checks. + Corrected terminology "deflate" into "inflate". +*) 23 jun 2006: Added SDL example in the documentation in the header, this + example allows easy debugging by displaying the PNG and its transparency. +*) 22 jun 2006: (!) Changed way to obtain error value. Added + loadFile function for convenience. Made decodePNG32 faster. +*) 21 jun 2006: (!) Changed type of info vector to unsigned. + Changed position of palette in info vector. Fixed an important bug that + happened on PNGs with an uncompressed block. +*) 16 jun 2006: Internally changed unsigned into unsigned where + needed, and performed some optimizations. +*) 07 jun 2006: (!) Renamed functions to decodePNG and placed them + in LodePNG namespace. Changed the order of the parameters. Rewrote the + documentation in the header. Renamed files to lodepng.cpp and lodepng.h +*) 22 apr 2006: Optimized and improved some code +*) 07 sep 2005: (!) Changed to std::vector interface +*) 12 aug 2005: Initial release (C++, decoder only) + + +13. contact information +----------------------- + +Feel free to contact me with suggestions, problems, comments, ... concerning +LodePNG. If you encounter a PNG image that doesn't work properly with this +decoder, feel free to send it and I'll use it to find and fix the problem. + +My email address is (puzzle the account and domain together with an @ symbol): +Domain: gmail dot com. +Account: lode dot vandevenne. + + +Copyright (c) 2005-2017 Lode Vandevenne +*/ diff --git a/Source/3rdParty/physfs/physfs.c b/Source/3rdParty/physfs/physfs.c new file mode 100644 index 0000000..19148ad --- /dev/null +++ b/Source/3rdParty/physfs/physfs.c @@ -0,0 +1,3302 @@ +/** + * PhysicsFS; a portable, flexible file i/o abstraction. + * + * Documentation is in physfs.h. It's verbose, honest. :) + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if defined(_MSC_VER) +#include <stdarg.h> + +/* this code came from https://stackoverflow.com/a/8712996 */ +int __PHYSFS_msvc_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap) +{ + int count = -1; + + if (size != 0) + count = _vsnprintf_s(outBuf, size, _TRUNCATE, format, ap); + if (count == -1) + count = _vscprintf(format, ap); + + return count; +} + +int __PHYSFS_msvc_snprintf(char *outBuf, size_t size, const char *format, ...) +{ + int count; + va_list ap; + + va_start(ap, format); + count = __PHYSFS_msvc_vsnprintf(outBuf, size, format, ap); + va_end(ap); + + return count; +} +#endif + + +typedef struct __PHYSFS_DIRHANDLE__ +{ + void *opaque; /* Instance data unique to the archiver. */ + char *dirName; /* Path to archive in platform-dependent notation. */ + char *mountPoint; /* Mountpoint in virtual file tree. */ + const PHYSFS_Archiver *funcs; /* Ptr to archiver info for this handle. */ + struct __PHYSFS_DIRHANDLE__ *next; /* linked list stuff. */ +} DirHandle; + + +typedef struct __PHYSFS_FILEHANDLE__ +{ + PHYSFS_Io *io; /* Instance data unique to the archiver for this file. */ + PHYSFS_uint8 forReading; /* Non-zero if reading, zero if write/append */ + const DirHandle *dirHandle; /* Archiver instance that created this */ + PHYSFS_uint8 *buffer; /* Buffer, if set (NULL otherwise). Don't touch! */ + size_t bufsize; /* Bufsize, if set (0 otherwise). Don't touch! */ + size_t buffill; /* Buffer fill size. Don't touch! */ + size_t bufpos; /* Buffer position. Don't touch! */ + struct __PHYSFS_FILEHANDLE__ *next; /* linked list stuff. */ +} FileHandle; + + +typedef struct __PHYSFS_ERRSTATETYPE__ +{ + void *tid; + PHYSFS_ErrorCode code; + struct __PHYSFS_ERRSTATETYPE__ *next; +} ErrState; + + +/* General PhysicsFS state ... */ +static int initialized = 0; +static ErrState *errorStates = NULL; +static DirHandle *searchPath = NULL; +static DirHandle *writeDir = NULL; +static FileHandle *openWriteList = NULL; +static FileHandle *openReadList = NULL; +static char *baseDir = NULL; +static char *userDir = NULL; +static char *prefDir = NULL; +static int allowSymLinks = 0; +static PHYSFS_Archiver **archivers = NULL; +static PHYSFS_ArchiveInfo **archiveInfo = NULL; +static volatile size_t numArchivers = 0; + +/* mutexes ... */ +static void *errorLock = NULL; /* protects error message list. */ +static void *stateLock = NULL; /* protects other PhysFS static state. */ + +/* allocator ... */ +static int externalAllocator = 0; +PHYSFS_Allocator allocator; + + +#ifdef PHYSFS_NEED_ATOMIC_OP_FALLBACK +static inline int __PHYSFS_atomicAdd(int *ptrval, const int val) +{ + int retval; + __PHYSFS_platformGrabMutex(stateLock); + retval = *ptrval; + *ptrval = retval + val; + __PHYSFS_platformReleaseMutex(stateLock); + return retval; +} /* __PHYSFS_atomicAdd */ + +int __PHYSFS_ATOMIC_INCR(int *ptrval) +{ + return __PHYSFS_atomicAdd(ptrval, 1); +} /* __PHYSFS_ATOMIC_INCR */ + +int __PHYSFS_ATOMIC_DECR(int *ptrval) +{ + return __PHYSFS_atomicAdd(ptrval, -1); +} /* __PHYSFS_ATOMIC_DECR */ +#endif + + + +/* PHYSFS_Io implementation for i/o to physical filesystem... */ + +/* !!! FIXME: maybe refcount the paths in a string pool? */ +typedef struct __PHYSFS_NativeIoInfo +{ + void *handle; + const char *path; + int mode; /* 'r', 'w', or 'a' */ +} NativeIoInfo; + +static PHYSFS_sint64 nativeIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len) +{ + NativeIoInfo *info = (NativeIoInfo *) io->opaque; + return __PHYSFS_platformRead(info->handle, buf, len); +} /* nativeIo_read */ + +static PHYSFS_sint64 nativeIo_write(PHYSFS_Io *io, const void *buffer, + PHYSFS_uint64 len) +{ + NativeIoInfo *info = (NativeIoInfo *) io->opaque; + return __PHYSFS_platformWrite(info->handle, buffer, len); +} /* nativeIo_write */ + +static int nativeIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset) +{ + NativeIoInfo *info = (NativeIoInfo *) io->opaque; + return __PHYSFS_platformSeek(info->handle, offset); +} /* nativeIo_seek */ + +static PHYSFS_sint64 nativeIo_tell(PHYSFS_Io *io) +{ + NativeIoInfo *info = (NativeIoInfo *) io->opaque; + return __PHYSFS_platformTell(info->handle); +} /* nativeIo_tell */ + +static PHYSFS_sint64 nativeIo_length(PHYSFS_Io *io) +{ + NativeIoInfo *info = (NativeIoInfo *) io->opaque; + return __PHYSFS_platformFileLength(info->handle); +} /* nativeIo_length */ + +static PHYSFS_Io *nativeIo_duplicate(PHYSFS_Io *io) +{ + NativeIoInfo *info = (NativeIoInfo *) io->opaque; + return __PHYSFS_createNativeIo(info->path, info->mode); +} /* nativeIo_duplicate */ + +static int nativeIo_flush(PHYSFS_Io *io) +{ + NativeIoInfo *info = (NativeIoInfo *) io->opaque; + return __PHYSFS_platformFlush(info->handle); +} /* nativeIo_flush */ + +static void nativeIo_destroy(PHYSFS_Io *io) +{ + NativeIoInfo *info = (NativeIoInfo *) io->opaque; + __PHYSFS_platformClose(info->handle); + allocator.Free((void *) info->path); + allocator.Free(info); + allocator.Free(io); +} /* nativeIo_destroy */ + +static const PHYSFS_Io __PHYSFS_nativeIoInterface = +{ + CURRENT_PHYSFS_IO_API_VERSION, NULL, + nativeIo_read, + nativeIo_write, + nativeIo_seek, + nativeIo_tell, + nativeIo_length, + nativeIo_duplicate, + nativeIo_flush, + nativeIo_destroy +}; + +PHYSFS_Io *__PHYSFS_createNativeIo(const char *path, const int mode) +{ + PHYSFS_Io *io = NULL; + NativeIoInfo *info = NULL; + void *handle = NULL; + char *pathdup = NULL; + + assert((mode == 'r') || (mode == 'w') || (mode == 'a')); + + io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); + GOTO_IF(!io, PHYSFS_ERR_OUT_OF_MEMORY, createNativeIo_failed); + info = (NativeIoInfo *) allocator.Malloc(sizeof (NativeIoInfo)); + GOTO_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, createNativeIo_failed); + pathdup = (char *) allocator.Malloc(strlen(path) + 1); + GOTO_IF(!pathdup, PHYSFS_ERR_OUT_OF_MEMORY, createNativeIo_failed); + + if (mode == 'r') + handle = __PHYSFS_platformOpenRead(path); + else if (mode == 'w') + handle = __PHYSFS_platformOpenWrite(path); + else if (mode == 'a') + handle = __PHYSFS_platformOpenAppend(path); + + GOTO_IF_ERRPASS(!handle, createNativeIo_failed); + + strcpy(pathdup, path); + info->handle = handle; + info->path = pathdup; + info->mode = mode; + memcpy(io, &__PHYSFS_nativeIoInterface, sizeof (*io)); + io->opaque = info; + return io; + +createNativeIo_failed: + if (handle != NULL) __PHYSFS_platformClose(handle); + if (pathdup != NULL) allocator.Free(pathdup); + if (info != NULL) allocator.Free(info); + if (io != NULL) allocator.Free(io); + return NULL; +} /* __PHYSFS_createNativeIo */ + + +/* PHYSFS_Io implementation for i/o to a memory buffer... */ + +typedef struct __PHYSFS_MemoryIoInfo +{ + const PHYSFS_uint8 *buf; + PHYSFS_uint64 len; + PHYSFS_uint64 pos; + PHYSFS_Io *parent; + int refcount; + void (*destruct)(void *); +} MemoryIoInfo; + +static PHYSFS_sint64 memoryIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len) +{ + MemoryIoInfo *info = (MemoryIoInfo *) io->opaque; + const PHYSFS_uint64 avail = info->len - info->pos; + assert(avail <= info->len); + + if (avail == 0) + return 0; /* we're at EOF; nothing to do. */ + + if (len > avail) + len = avail; + + memcpy(buf, info->buf + info->pos, (size_t) len); + info->pos += len; + return len; +} /* memoryIo_read */ + +static PHYSFS_sint64 memoryIo_write(PHYSFS_Io *io, const void *buffer, + PHYSFS_uint64 len) +{ + BAIL(PHYSFS_ERR_OPEN_FOR_READING, -1); +} /* memoryIo_write */ + +static int memoryIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset) +{ + MemoryIoInfo *info = (MemoryIoInfo *) io->opaque; + BAIL_IF(offset > info->len, PHYSFS_ERR_PAST_EOF, 0); + info->pos = offset; + return 1; +} /* memoryIo_seek */ + +static PHYSFS_sint64 memoryIo_tell(PHYSFS_Io *io) +{ + const MemoryIoInfo *info = (MemoryIoInfo *) io->opaque; + return (PHYSFS_sint64) info->pos; +} /* memoryIo_tell */ + +static PHYSFS_sint64 memoryIo_length(PHYSFS_Io *io) +{ + const MemoryIoInfo *info = (MemoryIoInfo *) io->opaque; + return (PHYSFS_sint64) info->len; +} /* memoryIo_length */ + +static PHYSFS_Io *memoryIo_duplicate(PHYSFS_Io *io) +{ + MemoryIoInfo *info = (MemoryIoInfo *) io->opaque; + MemoryIoInfo *newinfo = NULL; + PHYSFS_Io *parent = info->parent; + PHYSFS_Io *retval = NULL; + + /* avoid deep copies. */ + assert((!parent) || (!((MemoryIoInfo *) parent->opaque)->parent) ); + + /* share the buffer between duplicates. */ + if (parent != NULL) /* dup the parent, increment its refcount. */ + return parent->duplicate(parent); + + /* we're the parent. */ + + retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); + BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + newinfo = (MemoryIoInfo *) allocator.Malloc(sizeof (MemoryIoInfo)); + if (!newinfo) + { + allocator.Free(retval); + BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + __PHYSFS_ATOMIC_INCR(&info->refcount); + + memset(newinfo, '\0', sizeof (*info)); + newinfo->buf = info->buf; + newinfo->len = info->len; + newinfo->pos = 0; + newinfo->parent = io; + newinfo->refcount = 0; + newinfo->destruct = NULL; + + memcpy(retval, io, sizeof (*retval)); + retval->opaque = newinfo; + return retval; +} /* memoryIo_duplicate */ + +static int memoryIo_flush(PHYSFS_Io *io) { return 1; /* it's read-only. */ } + +static void memoryIo_destroy(PHYSFS_Io *io) +{ + MemoryIoInfo *info = (MemoryIoInfo *) io->opaque; + PHYSFS_Io *parent = info->parent; + + if (parent != NULL) + { + assert(info->buf == ((MemoryIoInfo *) info->parent->opaque)->buf); + assert(info->len == ((MemoryIoInfo *) info->parent->opaque)->len); + assert(info->refcount == 0); + assert(info->destruct == NULL); + allocator.Free(info); + allocator.Free(io); + parent->destroy(parent); /* decrements refcount. */ + return; + } /* if */ + + /* we _are_ the parent. */ + assert(info->refcount > 0); /* even in a race, we hold a reference. */ + + if (__PHYSFS_ATOMIC_DECR(&info->refcount) == 0) + { + void (*destruct)(void *) = info->destruct; + void *buf = (void *) info->buf; + io->opaque = NULL; /* kill this here in case of race. */ + allocator.Free(info); + allocator.Free(io); + if (destruct != NULL) + destruct(buf); + } /* if */ +} /* memoryIo_destroy */ + + +static const PHYSFS_Io __PHYSFS_memoryIoInterface = +{ + CURRENT_PHYSFS_IO_API_VERSION, NULL, + memoryIo_read, + memoryIo_write, + memoryIo_seek, + memoryIo_tell, + memoryIo_length, + memoryIo_duplicate, + memoryIo_flush, + memoryIo_destroy +}; + +PHYSFS_Io *__PHYSFS_createMemoryIo(const void *buf, PHYSFS_uint64 len, + void (*destruct)(void *)) +{ + PHYSFS_Io *io = NULL; + MemoryIoInfo *info = NULL; + + io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); + GOTO_IF(!io, PHYSFS_ERR_OUT_OF_MEMORY, createMemoryIo_failed); + info = (MemoryIoInfo *) allocator.Malloc(sizeof (MemoryIoInfo)); + GOTO_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, createMemoryIo_failed); + + memset(info, '\0', sizeof (*info)); + info->buf = (const PHYSFS_uint8 *) buf; + info->len = len; + info->pos = 0; + info->parent = NULL; + info->refcount = 1; + info->destruct = destruct; + + memcpy(io, &__PHYSFS_memoryIoInterface, sizeof (*io)); + io->opaque = info; + return io; + +createMemoryIo_failed: + if (info != NULL) allocator.Free(info); + if (io != NULL) allocator.Free(io); + return NULL; +} /* __PHYSFS_createMemoryIo */ + + +/* PHYSFS_Io implementation for i/o to a PHYSFS_File... */ + +static PHYSFS_sint64 handleIo_read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len) +{ + return PHYSFS_readBytes((PHYSFS_File *) io->opaque, buf, len); +} /* handleIo_read */ + +static PHYSFS_sint64 handleIo_write(PHYSFS_Io *io, const void *buffer, + PHYSFS_uint64 len) +{ + return PHYSFS_writeBytes((PHYSFS_File *) io->opaque, buffer, len); +} /* handleIo_write */ + +static int handleIo_seek(PHYSFS_Io *io, PHYSFS_uint64 offset) +{ + return PHYSFS_seek((PHYSFS_File *) io->opaque, offset); +} /* handleIo_seek */ + +static PHYSFS_sint64 handleIo_tell(PHYSFS_Io *io) +{ + return PHYSFS_tell((PHYSFS_File *) io->opaque); +} /* handleIo_tell */ + +static PHYSFS_sint64 handleIo_length(PHYSFS_Io *io) +{ + return PHYSFS_fileLength((PHYSFS_File *) io->opaque); +} /* handleIo_length */ + +static PHYSFS_Io *handleIo_duplicate(PHYSFS_Io *io) +{ + /* + * There's no duplicate at the PHYSFS_File level, so we break the + * abstraction. We're allowed to: we're physfs.c! + */ + FileHandle *origfh = (FileHandle *) io->opaque; + FileHandle *newfh = (FileHandle *) allocator.Malloc(sizeof (FileHandle)); + PHYSFS_Io *retval = NULL; + + GOTO_IF(!newfh, PHYSFS_ERR_OUT_OF_MEMORY, handleIo_dupe_failed); + memset(newfh, '\0', sizeof (*newfh)); + + retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); + GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, handleIo_dupe_failed); + +#if 0 /* we don't buffer the duplicate, at least not at the moment. */ + if (origfh->buffer != NULL) + { + newfh->buffer = (PHYSFS_uint8 *) allocator.Malloc(origfh->bufsize); + if (!newfh->buffer) + GOTO(PHYSFS_ERR_OUT_OF_MEMORY, handleIo_dupe_failed); + newfh->bufsize = origfh->bufsize; + } /* if */ +#endif + + newfh->io = origfh->io->duplicate(origfh->io); + GOTO_IF_ERRPASS(!newfh->io, handleIo_dupe_failed); + + newfh->forReading = origfh->forReading; + newfh->dirHandle = origfh->dirHandle; + + __PHYSFS_platformGrabMutex(stateLock); + if (newfh->forReading) + { + newfh->next = openReadList; + openReadList = newfh; + } /* if */ + else + { + newfh->next = openWriteList; + openWriteList = newfh; + } /* else */ + __PHYSFS_platformReleaseMutex(stateLock); + + memcpy(retval, io, sizeof (PHYSFS_Io)); + retval->opaque = newfh; + return retval; + +handleIo_dupe_failed: + if (newfh) + { + if (newfh->io != NULL) newfh->io->destroy(newfh->io); + if (newfh->buffer != NULL) allocator.Free(newfh->buffer); + allocator.Free(newfh); + } /* if */ + + return NULL; +} /* handleIo_duplicate */ + +static int handleIo_flush(PHYSFS_Io *io) +{ + return PHYSFS_flush((PHYSFS_File *) io->opaque); +} /* handleIo_flush */ + +static void handleIo_destroy(PHYSFS_Io *io) +{ + if (io->opaque != NULL) + PHYSFS_close((PHYSFS_File *) io->opaque); + allocator.Free(io); +} /* handleIo_destroy */ + +static const PHYSFS_Io __PHYSFS_handleIoInterface = +{ + CURRENT_PHYSFS_IO_API_VERSION, NULL, + handleIo_read, + handleIo_write, + handleIo_seek, + handleIo_tell, + handleIo_length, + handleIo_duplicate, + handleIo_flush, + handleIo_destroy +}; + +static PHYSFS_Io *__PHYSFS_createHandleIo(PHYSFS_File *f) +{ + PHYSFS_Io *io = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); + BAIL_IF(!io, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + memcpy(io, &__PHYSFS_handleIoInterface, sizeof (*io)); + io->opaque = f; + return io; +} /* __PHYSFS_createHandleIo */ + + +/* functions ... */ + +typedef struct +{ + char **list; + PHYSFS_uint32 size; + PHYSFS_ErrorCode errcode; +} EnumStringListCallbackData; + +static void enumStringListCallback(void *data, const char *str) +{ + void *ptr; + char *newstr; + EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data; + + if (pecd->errcode) + return; + + ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *)); + newstr = (char *) allocator.Malloc(strlen(str) + 1); + if (ptr != NULL) + pecd->list = (char **) ptr; + + if ((ptr == NULL) || (newstr == NULL)) + { + pecd->errcode = PHYSFS_ERR_OUT_OF_MEMORY; + pecd->list[pecd->size] = NULL; + PHYSFS_freeList(pecd->list); + return; + } /* if */ + + strcpy(newstr, str); + pecd->list[pecd->size] = newstr; + pecd->size++; +} /* enumStringListCallback */ + + +static char **doEnumStringList(void (*func)(PHYSFS_StringCallback, void *)) +{ + EnumStringListCallbackData ecd; + memset(&ecd, '\0', sizeof (ecd)); + ecd.list = (char **) allocator.Malloc(sizeof (char *)); + BAIL_IF(!ecd.list, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + func(enumStringListCallback, &ecd); + + if (ecd.errcode) + { + PHYSFS_setErrorCode(ecd.errcode); + return NULL; + } /* if */ + + ecd.list[ecd.size] = NULL; + return ecd.list; +} /* doEnumStringList */ + + +static void __PHYSFS_bubble_sort(void *a, size_t lo, size_t hi, + int (*cmpfn)(void *, size_t, size_t), + void (*swapfn)(void *, size_t, size_t)) +{ + size_t i; + int sorted; + + do + { + sorted = 1; + for (i = lo; i < hi; i++) + { + if (cmpfn(a, i, i + 1) > 0) + { + swapfn(a, i, i + 1); + sorted = 0; + } /* if */ + } /* for */ + } while (!sorted); +} /* __PHYSFS_bubble_sort */ + + +static void __PHYSFS_quick_sort(void *a, size_t lo, size_t hi, + int (*cmpfn)(void *, size_t, size_t), + void (*swapfn)(void *, size_t, size_t)) +{ + size_t i; + size_t j; + size_t v; + + if ((hi - lo) <= PHYSFS_QUICKSORT_THRESHOLD) + __PHYSFS_bubble_sort(a, lo, hi, cmpfn, swapfn); + else + { + i = (hi + lo) / 2; + + if (cmpfn(a, lo, i) > 0) swapfn(a, lo, i); + if (cmpfn(a, lo, hi) > 0) swapfn(a, lo, hi); + if (cmpfn(a, i, hi) > 0) swapfn(a, i, hi); + + j = hi - 1; + swapfn(a, i, j); + i = lo; + v = j; + while (1) + { + while(cmpfn(a, ++i, v) < 0) { /* do nothing */ } + while(cmpfn(a, --j, v) > 0) { /* do nothing */ } + if (j < i) + break; + swapfn(a, i, j); + } /* while */ + if (i != (hi-1)) + swapfn(a, i, hi-1); + __PHYSFS_quick_sort(a, lo, j, cmpfn, swapfn); + __PHYSFS_quick_sort(a, i+1, hi, cmpfn, swapfn); + } /* else */ +} /* __PHYSFS_quick_sort */ + + +void __PHYSFS_sort(void *entries, size_t max, + int (*cmpfn)(void *, size_t, size_t), + void (*swapfn)(void *, size_t, size_t)) +{ + /* + * Quicksort w/ Bubblesort fallback algorithm inspired by code from here: + * https://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html + */ + if (max > 0) + __PHYSFS_quick_sort(entries, 0, max - 1, cmpfn, swapfn); +} /* __PHYSFS_sort */ + + +static ErrState *findErrorForCurrentThread(void) +{ + ErrState *i; + void *tid; + + if (errorLock != NULL) + __PHYSFS_platformGrabMutex(errorLock); + + if (errorStates != NULL) + { + tid = __PHYSFS_platformGetThreadID(); + + for (i = errorStates; i != NULL; i = i->next) + { + if (i->tid == tid) + { + if (errorLock != NULL) + __PHYSFS_platformReleaseMutex(errorLock); + return i; + } /* if */ + } /* for */ + } /* if */ + + if (errorLock != NULL) + __PHYSFS_platformReleaseMutex(errorLock); + + return NULL; /* no error available. */ +} /* findErrorForCurrentThread */ + + +/* this doesn't reset the error state. */ +static inline PHYSFS_ErrorCode currentErrorCode(void) +{ + const ErrState *err = findErrorForCurrentThread(); + return err ? err->code : PHYSFS_ERR_OK; +} /* currentErrorCode */ + + +PHYSFS_ErrorCode PHYSFS_getLastErrorCode(void) +{ + ErrState *err = findErrorForCurrentThread(); + const PHYSFS_ErrorCode retval = (err) ? err->code : PHYSFS_ERR_OK; + if (err) + err->code = PHYSFS_ERR_OK; + return retval; +} /* PHYSFS_getLastErrorCode */ + + +PHYSFS_DECL const char *PHYSFS_getErrorByCode(PHYSFS_ErrorCode code) +{ + switch (code) + { + case PHYSFS_ERR_OK: return "no error"; + case PHYSFS_ERR_OTHER_ERROR: return "unknown error"; + case PHYSFS_ERR_OUT_OF_MEMORY: return "out of memory"; + case PHYSFS_ERR_NOT_INITIALIZED: return "not initialized"; + case PHYSFS_ERR_IS_INITIALIZED: return "already initialized"; + case PHYSFS_ERR_ARGV0_IS_NULL: return "argv[0] is NULL"; + case PHYSFS_ERR_UNSUPPORTED: return "unsupported"; + case PHYSFS_ERR_PAST_EOF: return "past end of file"; + case PHYSFS_ERR_FILES_STILL_OPEN: return "files still open"; + case PHYSFS_ERR_INVALID_ARGUMENT: return "invalid argument"; + case PHYSFS_ERR_NOT_MOUNTED: return "not mounted"; + case PHYSFS_ERR_NOT_FOUND: return "not found"; + case PHYSFS_ERR_SYMLINK_FORBIDDEN: return "symlinks are forbidden"; + case PHYSFS_ERR_NO_WRITE_DIR: return "write directory is not set"; + case PHYSFS_ERR_OPEN_FOR_READING: return "file open for reading"; + case PHYSFS_ERR_OPEN_FOR_WRITING: return "file open for writing"; + case PHYSFS_ERR_NOT_A_FILE: return "not a file"; + case PHYSFS_ERR_READ_ONLY: return "read-only filesystem"; + case PHYSFS_ERR_CORRUPT: return "corrupted"; + case PHYSFS_ERR_SYMLINK_LOOP: return "infinite symbolic link loop"; + case PHYSFS_ERR_IO: return "i/o error"; + case PHYSFS_ERR_PERMISSION: return "permission denied"; + case PHYSFS_ERR_NO_SPACE: return "no space available for writing"; + case PHYSFS_ERR_BAD_FILENAME: return "filename is illegal or insecure"; + case PHYSFS_ERR_BUSY: return "tried to modify a file the OS needs"; + case PHYSFS_ERR_DIR_NOT_EMPTY: return "directory isn't empty"; + case PHYSFS_ERR_OS_ERROR: return "OS reported an error"; + case PHYSFS_ERR_DUPLICATE: return "duplicate resource"; + case PHYSFS_ERR_BAD_PASSWORD: return "bad password"; + case PHYSFS_ERR_APP_CALLBACK: return "app callback reported error"; + } /* switch */ + + return NULL; /* don't know this error code. */ +} /* PHYSFS_getErrorByCode */ + + +void PHYSFS_setErrorCode(PHYSFS_ErrorCode errcode) +{ + ErrState *err; + + if (!errcode) + return; + + err = findErrorForCurrentThread(); + if (err == NULL) + { + err = (ErrState *) allocator.Malloc(sizeof (ErrState)); + if (err == NULL) + return; /* uhh...? */ + + memset(err, '\0', sizeof (ErrState)); + err->tid = __PHYSFS_platformGetThreadID(); + + if (errorLock != NULL) + __PHYSFS_platformGrabMutex(errorLock); + + err->next = errorStates; + errorStates = err; + + if (errorLock != NULL) + __PHYSFS_platformReleaseMutex(errorLock); + } /* if */ + + err->code = errcode; +} /* PHYSFS_setErrorCode */ + + +const char *PHYSFS_getLastError(void) +{ + const PHYSFS_ErrorCode err = PHYSFS_getLastErrorCode(); + return (err) ? PHYSFS_getErrorByCode(err) : NULL; +} /* PHYSFS_getLastError */ + + +/* MAKE SURE that errorLock is held before calling this! */ +static void freeErrorStates(void) +{ + ErrState *i; + ErrState *next; + + for (i = errorStates; i != NULL; i = next) + { + next = i->next; + allocator.Free(i); + } /* for */ + + errorStates = NULL; +} /* freeErrorStates */ + + +void PHYSFS_getLinkedVersion(PHYSFS_Version *ver) +{ + if (ver != NULL) + { + ver->major = PHYSFS_VER_MAJOR; + ver->minor = PHYSFS_VER_MINOR; + ver->patch = PHYSFS_VER_PATCH; + } /* if */ +} /* PHYSFS_getLinkedVersion */ + + +static const char *find_filename_extension(const char *fname) +{ + const char *retval = NULL; + if (fname != NULL) + { + const char *p = strchr(fname, '.'); + retval = p; + + while (p != NULL) + { + p = strchr(p + 1, '.'); + if (p != NULL) + retval = p; + } /* while */ + + if (retval != NULL) + retval++; /* skip '.' */ + } /* if */ + + return retval; +} /* find_filename_extension */ + + +static DirHandle *tryOpenDir(PHYSFS_Io *io, const PHYSFS_Archiver *funcs, + const char *d, int forWriting, int *_claimed) +{ + DirHandle *retval = NULL; + void *opaque = NULL; + + if (io != NULL) + BAIL_IF_ERRPASS(!io->seek(io, 0), NULL); + + opaque = funcs->openArchive(io, d, forWriting, _claimed); + if (opaque != NULL) + { + retval = (DirHandle *) allocator.Malloc(sizeof (DirHandle)); + if (retval == NULL) + funcs->closeArchive(opaque); + else + { + memset(retval, '\0', sizeof (DirHandle)); + retval->mountPoint = NULL; + retval->funcs = funcs; + retval->opaque = opaque; + } /* else */ + } /* if */ + + return retval; +} /* tryOpenDir */ + + +static DirHandle *openDirectory(PHYSFS_Io *io, const char *d, int forWriting) +{ + DirHandle *retval = NULL; + PHYSFS_Archiver **i; + const char *ext; + int created_io = 0; + int claimed = 0; + PHYSFS_ErrorCode errcode; + + assert((io != NULL) || (d != NULL)); + + if (io == NULL) + { + /* DIR gets first shot (unlike the rest, it doesn't deal with files). */ + retval = tryOpenDir(io, &__PHYSFS_Archiver_DIR, d, forWriting, &claimed); + if (retval || claimed) + return retval; + + io = __PHYSFS_createNativeIo(d, forWriting ? 'w' : 'r'); + BAIL_IF_ERRPASS(!io, 0); + created_io = 1; + } /* if */ + + ext = find_filename_extension(d); + if (ext != NULL) + { + /* Look for archivers with matching file extensions first... */ + for (i = archivers; (*i != NULL) && (retval == NULL) && !claimed; i++) + { + if (PHYSFS_utf8stricmp(ext, (*i)->info.extension) == 0) + retval = tryOpenDir(io, *i, d, forWriting, &claimed); + } /* for */ + + /* failing an exact file extension match, try all the others... */ + for (i = archivers; (*i != NULL) && (retval == NULL) && !claimed; i++) + { + if (PHYSFS_utf8stricmp(ext, (*i)->info.extension) != 0) + retval = tryOpenDir(io, *i, d, forWriting, &claimed); + } /* for */ + } /* if */ + + else /* no extension? Try them all. */ + { + for (i = archivers; (*i != NULL) && (retval == NULL) && !claimed; i++) + retval = tryOpenDir(io, *i, d, forWriting, &claimed); + } /* else */ + + errcode = currentErrorCode(); + + if ((!retval) && (created_io)) + io->destroy(io); + + BAIL_IF(!retval, claimed ? errcode : PHYSFS_ERR_UNSUPPORTED, NULL); + return retval; +} /* openDirectory */ + + +/* + * Make a platform-independent path string sane. Doesn't actually check the + * file hierarchy, it just cleans up the string. + * (dst) must be a buffer at least as big as (src), as this is where the + * cleaned up string is deposited. + * If there are illegal bits in the path (".." entries, etc) then we + * return zero and (dst) is undefined. Non-zero if the path was sanitized. + */ +static int sanitizePlatformIndependentPath(const char *src, char *dst) +{ + char *prev; + char ch; + + while (*src == '/') /* skip initial '/' chars... */ + src++; + + /* Make sure the entire string isn't "." or ".." */ + if ((strcmp(src, ".") == 0) || (strcmp(src, "..") == 0)) + BAIL(PHYSFS_ERR_BAD_FILENAME, 0); + + prev = dst; + do + { + ch = *(src++); + + if ((ch == ':') || (ch == '\\')) /* illegal chars in a physfs path. */ + BAIL(PHYSFS_ERR_BAD_FILENAME, 0); + + if (ch == '/') /* path separator. */ + { + *dst = '\0'; /* "." and ".." are illegal pathnames. */ + if ((strcmp(prev, ".") == 0) || (strcmp(prev, "..") == 0)) + BAIL(PHYSFS_ERR_BAD_FILENAME, 0); + + while (*src == '/') /* chop out doubles... */ + src++; + + if (*src == '\0') /* ends with a pathsep? */ + break; /* we're done, don't add final pathsep to dst. */ + + prev = dst + 1; + } /* if */ + + *(dst++) = ch; + } while (ch != '\0'); + + return 1; +} /* sanitizePlatformIndependentPath */ + + +/* + * Figure out if (fname) is part of (h)'s mountpoint. (fname) must be an + * output from sanitizePlatformIndependentPath(), so that it is in a known + * state. + * + * This only finds legitimate segments of a mountpoint. If the mountpoint is + * "/a/b/c" and (fname) is "/a/b/c", "/", or "/a/b/c/d", then the results are + * all zero. "/a/b" will succeed, though. + */ +static int partOfMountPoint(DirHandle *h, char *fname) +{ + int rc; + size_t len, mntpntlen; + + if (h->mountPoint == NULL) + return 0; + else if (*fname == '\0') + return 1; + + len = strlen(fname); + mntpntlen = strlen(h->mountPoint); + if (len > mntpntlen) /* can't be a subset of mountpoint. */ + return 0; + + /* if true, must be not a match or a complete match, but not a subset. */ + if ((len + 1) == mntpntlen) + return 0; + + rc = strncmp(fname, h->mountPoint, len); /* !!! FIXME: case insensitive? */ + if (rc != 0) + return 0; /* not a match. */ + + /* make sure /a/b matches /a/b/ and not /a/bc ... */ + return h->mountPoint[len] == '/'; +} /* partOfMountPoint */ + + +static DirHandle *createDirHandle(PHYSFS_Io *io, const char *newDir, + const char *mountPoint, int forWriting) +{ + DirHandle *dirHandle = NULL; + char *tmpmntpnt = NULL; + + assert(newDir != NULL); /* should have caught this higher up. */ + + if (mountPoint != NULL) + { + const size_t len = strlen(mountPoint) + 1; + tmpmntpnt = (char *) __PHYSFS_smallAlloc(len); + GOTO_IF(!tmpmntpnt, PHYSFS_ERR_OUT_OF_MEMORY, badDirHandle); + if (!sanitizePlatformIndependentPath(mountPoint, tmpmntpnt)) + goto badDirHandle; + mountPoint = tmpmntpnt; /* sanitized version. */ + } /* if */ + + dirHandle = openDirectory(io, newDir, forWriting); + GOTO_IF_ERRPASS(!dirHandle, badDirHandle); + + dirHandle->dirName = (char *) allocator.Malloc(strlen(newDir) + 1); + GOTO_IF(!dirHandle->dirName, PHYSFS_ERR_OUT_OF_MEMORY, badDirHandle); + strcpy(dirHandle->dirName, newDir); + + if ((mountPoint != NULL) && (*mountPoint != '\0')) + { + dirHandle->mountPoint = (char *)allocator.Malloc(strlen(mountPoint)+2); + if (!dirHandle->mountPoint) + GOTO(PHYSFS_ERR_OUT_OF_MEMORY, badDirHandle); + strcpy(dirHandle->mountPoint, mountPoint); + strcat(dirHandle->mountPoint, "/"); + } /* if */ + + __PHYSFS_smallFree(tmpmntpnt); + return dirHandle; + +badDirHandle: + if (dirHandle != NULL) + { + dirHandle->funcs->closeArchive(dirHandle->opaque); + allocator.Free(dirHandle->dirName); + allocator.Free(dirHandle->mountPoint); + allocator.Free(dirHandle); + } /* if */ + + __PHYSFS_smallFree(tmpmntpnt); + return NULL; +} /* createDirHandle */ + + +/* MAKE SURE you've got the stateLock held before calling this! */ +static int freeDirHandle(DirHandle *dh, FileHandle *openList) +{ + FileHandle *i; + + if (dh == NULL) + return 1; + + for (i = openList; i != NULL; i = i->next) + BAIL_IF(i->dirHandle == dh, PHYSFS_ERR_FILES_STILL_OPEN, 0); + + dh->funcs->closeArchive(dh->opaque); + allocator.Free(dh->dirName); + allocator.Free(dh->mountPoint); + allocator.Free(dh); + return 1; +} /* freeDirHandle */ + + +static char *calculateBaseDir(const char *argv0) +{ + const char dirsep = __PHYSFS_platformDirSeparator; + char *retval = NULL; + char *ptr = NULL; + + /* Give the platform layer first shot at this. */ + retval = __PHYSFS_platformCalcBaseDir(argv0); + if (retval != NULL) + return retval; + + /* We need argv0 to go on. */ + BAIL_IF(argv0 == NULL, PHYSFS_ERR_ARGV0_IS_NULL, NULL); + + ptr = strrchr(argv0, dirsep); + if (ptr != NULL) + { + const size_t size = ((size_t) (ptr - argv0)) + 1; + retval = (char *) allocator.Malloc(size + 1); + BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + memcpy(retval, argv0, size); + retval[size] = '\0'; + return retval; + } /* if */ + + /* argv0 wasn't helpful. */ + BAIL(PHYSFS_ERR_INVALID_ARGUMENT, NULL); +} /* calculateBaseDir */ + + +static int initializeMutexes(void) +{ + errorLock = __PHYSFS_platformCreateMutex(); + if (errorLock == NULL) + goto initializeMutexes_failed; + + stateLock = __PHYSFS_platformCreateMutex(); + if (stateLock == NULL) + goto initializeMutexes_failed; + + return 1; /* success. */ + +initializeMutexes_failed: + if (errorLock != NULL) + __PHYSFS_platformDestroyMutex(errorLock); + + if (stateLock != NULL) + __PHYSFS_platformDestroyMutex(stateLock); + + errorLock = stateLock = NULL; + return 0; /* failed. */ +} /* initializeMutexes */ + + +static int doRegisterArchiver(const PHYSFS_Archiver *_archiver); + +static int initStaticArchivers(void) +{ + #define REGISTER_STATIC_ARCHIVER(arc) { \ + if (!doRegisterArchiver(&__PHYSFS_Archiver_##arc)) { \ + return 0; \ + } \ + } + + #if PHYSFS_SUPPORTS_ZIP + REGISTER_STATIC_ARCHIVER(ZIP); + #endif + #if PHYSFS_SUPPORTS_7Z + SZIP_global_init(); + REGISTER_STATIC_ARCHIVER(7Z); + #endif + #if PHYSFS_SUPPORTS_GRP + REGISTER_STATIC_ARCHIVER(GRP); + #endif + #if PHYSFS_SUPPORTS_QPAK + REGISTER_STATIC_ARCHIVER(QPAK); + #endif + #if PHYSFS_SUPPORTS_HOG + REGISTER_STATIC_ARCHIVER(HOG); + #endif + #if PHYSFS_SUPPORTS_MVL + REGISTER_STATIC_ARCHIVER(MVL); + #endif + #if PHYSFS_SUPPORTS_WAD + REGISTER_STATIC_ARCHIVER(WAD); + #endif + #if PHYSFS_SUPPORTS_SLB + REGISTER_STATIC_ARCHIVER(SLB); + #endif + #if PHYSFS_SUPPORTS_ISO9660 + REGISTER_STATIC_ARCHIVER(ISO9660); + #endif + #if PHYSFS_SUPPORTS_VDF + REGISTER_STATIC_ARCHIVER(VDF) + #endif + + #undef REGISTER_STATIC_ARCHIVER + + return 1; +} /* initStaticArchivers */ + + +static void setDefaultAllocator(void); +static int doDeinit(void); + +int PHYSFS_init(const char *argv0) +{ + BAIL_IF(initialized, PHYSFS_ERR_IS_INITIALIZED, 0); + + if (!externalAllocator) + setDefaultAllocator(); + + if ((allocator.Init != NULL) && (!allocator.Init())) return 0; + + if (!__PHYSFS_platformInit()) + { + if (allocator.Deinit != NULL) allocator.Deinit(); + return 0; + } /* if */ + + /* everything below here can be cleaned up safely by doDeinit(). */ + + if (!initializeMutexes()) goto initFailed; + + baseDir = calculateBaseDir(argv0); + if (!baseDir) goto initFailed; + + userDir = __PHYSFS_platformCalcUserDir(); + if (!userDir) goto initFailed; + + /* Platform layer is required to append a dirsep. */ + assert(baseDir[strlen(baseDir) - 1] == __PHYSFS_platformDirSeparator); + assert(userDir[strlen(userDir) - 1] == __PHYSFS_platformDirSeparator); + + if (!initStaticArchivers()) goto initFailed; + + initialized = 1; + + /* This makes sure that the error subsystem is initialized. */ + PHYSFS_setErrorCode(PHYSFS_getLastErrorCode()); + + return 1; + +initFailed: + doDeinit(); + return 0; +} /* PHYSFS_init */ + + +/* MAKE SURE you hold stateLock before calling this! */ +static int closeFileHandleList(FileHandle **list) +{ + FileHandle *i; + FileHandle *next = NULL; + + for (i = *list; i != NULL; i = next) + { + PHYSFS_Io *io = i->io; + next = i->next; + + if (io->flush && !io->flush(io)) + { + *list = i; + return 0; + } /* if */ + + io->destroy(io); + allocator.Free(i); + } /* for */ + + *list = NULL; + return 1; +} /* closeFileHandleList */ + + +/* MAKE SURE you hold the stateLock before calling this! */ +static void freeSearchPath(void) +{ + DirHandle *i; + DirHandle *next = NULL; + + closeFileHandleList(&openReadList); + + if (searchPath != NULL) + { + for (i = searchPath; i != NULL; i = next) + { + next = i->next; + freeDirHandle(i, openReadList); + } /* for */ + searchPath = NULL; + } /* if */ +} /* freeSearchPath */ + + +/* MAKE SURE you hold stateLock before calling this! */ +static int archiverInUse(const PHYSFS_Archiver *arc, const DirHandle *list) +{ + const DirHandle *i; + for (i = list; i != NULL; i = i->next) + { + if (i->funcs == arc) + return 1; + } /* for */ + + return 0; /* not in use */ +} /* archiverInUse */ + + +/* MAKE SURE you hold stateLock before calling this! */ +static int doDeregisterArchiver(const size_t idx) +{ + const size_t len = (numArchivers - idx) * sizeof (void *); + PHYSFS_ArchiveInfo *info = archiveInfo[idx]; + PHYSFS_Archiver *arc = archivers[idx]; + + /* make sure nothing is still using this archiver */ + if (archiverInUse(arc, searchPath) || archiverInUse(arc, writeDir)) + BAIL(PHYSFS_ERR_FILES_STILL_OPEN, 0); + + allocator.Free((void *) info->extension); + allocator.Free((void *) info->description); + allocator.Free((void *) info->author); + allocator.Free((void *) info->url); + allocator.Free((void *) arc); + + memmove(&archiveInfo[idx], &archiveInfo[idx+1], len); + memmove(&archivers[idx], &archivers[idx+1], len); + + assert(numArchivers > 0); + numArchivers--; + + return 1; +} /* doDeregisterArchiver */ + + +/* Does NOT hold the state lock; we're shutting down. */ +static void freeArchivers(void) +{ + while (numArchivers > 0) + { + if (!doDeregisterArchiver(numArchivers - 1)) + assert(!"nothing should be mounted during shutdown."); + } /* while */ + + allocator.Free(archivers); + allocator.Free(archiveInfo); + archivers = NULL; + archiveInfo = NULL; +} /* freeArchivers */ + + +static int doDeinit(void) +{ + closeFileHandleList(&openWriteList); + BAIL_IF(!PHYSFS_setWriteDir(NULL), PHYSFS_ERR_FILES_STILL_OPEN, 0); + + freeSearchPath(); + freeArchivers(); + freeErrorStates(); + + if (baseDir != NULL) + { + allocator.Free(baseDir); + baseDir = NULL; + } /* if */ + + if (userDir != NULL) + { + allocator.Free(userDir); + userDir = NULL; + } /* if */ + + if (prefDir != NULL) + { + allocator.Free(prefDir); + prefDir = NULL; + } /* if */ + + if (archiveInfo != NULL) + { + allocator.Free(archiveInfo); + archiveInfo = NULL; + } /* if */ + + if (archivers != NULL) + { + allocator.Free(archivers); + archivers = NULL; + } /* if */ + + allowSymLinks = 0; + initialized = 0; + + if (errorLock) __PHYSFS_platformDestroyMutex(errorLock); + if (stateLock) __PHYSFS_platformDestroyMutex(stateLock); + + if (allocator.Deinit != NULL) + allocator.Deinit(); + + errorLock = stateLock = NULL; + + __PHYSFS_platformDeinit(); + + return 1; +} /* doDeinit */ + + +int PHYSFS_deinit(void) +{ + BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0); + return doDeinit(); +} /* PHYSFS_deinit */ + + +int PHYSFS_isInit(void) +{ + return initialized; +} /* PHYSFS_isInit */ + + +char *__PHYSFS_strdup(const char *str) +{ + char *retval = (char *) allocator.Malloc(strlen(str) + 1); + if (retval) + strcpy(retval, str); + return retval; +} /* __PHYSFS_strdup */ + + +PHYSFS_uint32 __PHYSFS_hashString(const char *str, size_t len) +{ + PHYSFS_uint32 hash = 5381; + while (len--) + hash = ((hash << 5) + hash) ^ *(str++); + return hash; +} /* __PHYSFS_hashString */ + + +/* MAKE SURE you hold stateLock before calling this! */ +static int doRegisterArchiver(const PHYSFS_Archiver *_archiver) +{ + const PHYSFS_uint32 maxver = CURRENT_PHYSFS_ARCHIVER_API_VERSION; + const size_t len = (numArchivers + 2) * sizeof (void *); + PHYSFS_Archiver *archiver = NULL; + PHYSFS_ArchiveInfo *info = NULL; + const char *ext = NULL; + void *ptr = NULL; + size_t i; + + BAIL_IF(!_archiver, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(_archiver->version > maxver, PHYSFS_ERR_UNSUPPORTED, 0); + BAIL_IF(!_archiver->info.extension, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->info.description, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->info.author, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->info.url, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->openArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->enumerate, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->openRead, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->openWrite, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->openAppend, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->remove, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->mkdir, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->closeArchive, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!_archiver->stat, PHYSFS_ERR_INVALID_ARGUMENT, 0); + + ext = _archiver->info.extension; + for (i = 0; i < numArchivers; i++) + { + if (PHYSFS_utf8stricmp(archiveInfo[i]->extension, ext) == 0) + BAIL(PHYSFS_ERR_DUPLICATE, 0); + } /* for */ + + /* make a copy of the data. */ + archiver = (PHYSFS_Archiver *) allocator.Malloc(sizeof (*archiver)); + GOTO_IF(!archiver, PHYSFS_ERR_OUT_OF_MEMORY, regfailed); + + /* Must copy sizeof (OLD_VERSION_OF_STRUCT) when version changes! */ + memcpy(archiver, _archiver, sizeof (*archiver)); + + info = (PHYSFS_ArchiveInfo *) &archiver->info; + memset(info, '\0', sizeof (*info)); /* NULL in case an alloc fails. */ + #define CPYSTR(item) \ + info->item = __PHYSFS_strdup(_archiver->info.item); \ + GOTO_IF(!info->item, PHYSFS_ERR_OUT_OF_MEMORY, regfailed); + CPYSTR(extension); + CPYSTR(description); + CPYSTR(author); + CPYSTR(url); + info->supportsSymlinks = _archiver->info.supportsSymlinks; + #undef CPYSTR + + ptr = allocator.Realloc(archiveInfo, len); + GOTO_IF(!ptr, PHYSFS_ERR_OUT_OF_MEMORY, regfailed); + archiveInfo = (PHYSFS_ArchiveInfo **) ptr; + + ptr = allocator.Realloc(archivers, len); + GOTO_IF(!ptr, PHYSFS_ERR_OUT_OF_MEMORY, regfailed); + archivers = (PHYSFS_Archiver **) ptr; + + archiveInfo[numArchivers] = info; + archiveInfo[numArchivers + 1] = NULL; + + archivers[numArchivers] = archiver; + archivers[numArchivers + 1] = NULL; + + numArchivers++; + + return 1; + +regfailed: + if (info != NULL) + { + allocator.Free((void *) info->extension); + allocator.Free((void *) info->description); + allocator.Free((void *) info->author); + allocator.Free((void *) info->url); + } /* if */ + allocator.Free(archiver); + + return 0; +} /* doRegisterArchiver */ + + +int PHYSFS_registerArchiver(const PHYSFS_Archiver *archiver) +{ + int retval; + BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0); + __PHYSFS_platformGrabMutex(stateLock); + retval = doRegisterArchiver(archiver); + __PHYSFS_platformReleaseMutex(stateLock); + return retval; +} /* PHYSFS_registerArchiver */ + + +int PHYSFS_deregisterArchiver(const char *ext) +{ + size_t i; + + BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0); + BAIL_IF(!ext, PHYSFS_ERR_INVALID_ARGUMENT, 0); + + __PHYSFS_platformGrabMutex(stateLock); + for (i = 0; i < numArchivers; i++) + { + if (PHYSFS_utf8stricmp(archiveInfo[i]->extension, ext) == 0) + { + const int retval = doDeregisterArchiver(i); + __PHYSFS_platformReleaseMutex(stateLock); + return retval; + } /* if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + + BAIL(PHYSFS_ERR_NOT_FOUND, 0); +} /* PHYSFS_deregisterArchiver */ + + +const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void) +{ + BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, NULL); + return (const PHYSFS_ArchiveInfo **) archiveInfo; +} /* PHYSFS_supportedArchiveTypes */ + + +void PHYSFS_freeList(void *list) +{ + void **i; + if (list != NULL) + { + for (i = (void **) list; *i != NULL; i++) + allocator.Free(*i); + + allocator.Free(list); + } /* if */ +} /* PHYSFS_freeList */ + + +const char *PHYSFS_getDirSeparator(void) +{ + static char retval[2] = { __PHYSFS_platformDirSeparator, '\0' }; + return retval; +} /* PHYSFS_getDirSeparator */ + + +char **PHYSFS_getCdRomDirs(void) +{ + return doEnumStringList(__PHYSFS_platformDetectAvailableCDs); +} /* PHYSFS_getCdRomDirs */ + + +void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback callback, void *data) +{ + __PHYSFS_platformDetectAvailableCDs(callback, data); +} /* PHYSFS_getCdRomDirsCallback */ + + +const char *PHYSFS_getPrefDir(const char *org, const char *app) +{ + const char dirsep = __PHYSFS_platformDirSeparator; + PHYSFS_Stat statbuf; + char *ptr = NULL; + char *endstr = NULL; + + BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0); + BAIL_IF(!org, PHYSFS_ERR_INVALID_ARGUMENT, NULL); + BAIL_IF(*org == '\0', PHYSFS_ERR_INVALID_ARGUMENT, NULL); + BAIL_IF(!app, PHYSFS_ERR_INVALID_ARGUMENT, NULL); + BAIL_IF(*app == '\0', PHYSFS_ERR_INVALID_ARGUMENT, NULL); + + allocator.Free(prefDir); + prefDir = __PHYSFS_platformCalcPrefDir(org, app); + BAIL_IF_ERRPASS(!prefDir, NULL); + + assert(strlen(prefDir) > 0); + endstr = prefDir + (strlen(prefDir) - 1); + assert(*endstr == dirsep); + *endstr = '\0'; /* mask out the final dirsep for now. */ + + if (!__PHYSFS_platformStat(prefDir, &statbuf, 1)) + { + for (ptr = strchr(prefDir, dirsep); ptr; ptr = strchr(ptr+1, dirsep)) + { + *ptr = '\0'; + __PHYSFS_platformMkDir(prefDir); + *ptr = dirsep; + } /* for */ + + if (!__PHYSFS_platformMkDir(prefDir)) + { + allocator.Free(prefDir); + prefDir = NULL; + } /* if */ + } /* if */ + + *endstr = dirsep; /* readd the final dirsep. */ + + return prefDir; +} /* PHYSFS_getPrefDir */ + + +const char *PHYSFS_getBaseDir(void) +{ + return baseDir; /* this is calculated in PHYSFS_init()... */ +} /* PHYSFS_getBaseDir */ + + +const char *__PHYSFS_getUserDir(void) /* not deprecated internal version. */ +{ + return userDir; /* this is calculated in PHYSFS_init()... */ +} /* __PHYSFS_getUserDir */ + + +const char *PHYSFS_getUserDir(void) +{ + return __PHYSFS_getUserDir(); +} /* PHYSFS_getUserDir */ + + +const char *PHYSFS_getWriteDir(void) +{ + const char *retval = NULL; + + __PHYSFS_platformGrabMutex(stateLock); + if (writeDir != NULL) + retval = writeDir->dirName; + __PHYSFS_platformReleaseMutex(stateLock); + + return retval; +} /* PHYSFS_getWriteDir */ + + +int PHYSFS_setWriteDir(const char *newDir) +{ + int retval = 1; + + __PHYSFS_platformGrabMutex(stateLock); + + if (writeDir != NULL) + { + BAIL_IF_MUTEX_ERRPASS(!freeDirHandle(writeDir, openWriteList), + stateLock, 0); + writeDir = NULL; + } /* if */ + + if (newDir != NULL) + { + writeDir = createDirHandle(NULL, newDir, NULL, 1); + retval = (writeDir != NULL); + } /* if */ + + __PHYSFS_platformReleaseMutex(stateLock); + + return retval; +} /* PHYSFS_setWriteDir */ + + +static int doMount(PHYSFS_Io *io, const char *fname, + const char *mountPoint, int appendToPath) +{ + DirHandle *dh; + DirHandle *prev = NULL; + DirHandle *i; + + BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0); + + if (mountPoint == NULL) + mountPoint = "/"; + + __PHYSFS_platformGrabMutex(stateLock); + + for (i = searchPath; i != NULL; i = i->next) + { + /* already in search path? */ + if ((i->dirName != NULL) && (strcmp(fname, i->dirName) == 0)) + BAIL_MUTEX_ERRPASS(stateLock, 1); + prev = i; + } /* for */ + + dh = createDirHandle(io, fname, mountPoint, 0); + BAIL_IF_MUTEX_ERRPASS(!dh, stateLock, 0); + + if (appendToPath) + { + if (prev == NULL) + searchPath = dh; + else + prev->next = dh; + } /* if */ + else + { + dh->next = searchPath; + searchPath = dh; + } /* else */ + + __PHYSFS_platformReleaseMutex(stateLock); + return 1; +} /* doMount */ + + +int PHYSFS_mountIo(PHYSFS_Io *io, const char *fname, + const char *mountPoint, int appendToPath) +{ + BAIL_IF(!io, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(io->version != 0, PHYSFS_ERR_UNSUPPORTED, 0); + return doMount(io, fname, mountPoint, appendToPath); +} /* PHYSFS_mountIo */ + + +int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len, void (*del)(void *), + const char *fname, const char *mountPoint, + int appendToPath) +{ + int retval = 0; + PHYSFS_Io *io = NULL; + + BAIL_IF(!buf, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0); + + io = __PHYSFS_createMemoryIo(buf, len, del); + BAIL_IF_ERRPASS(!io, 0); + retval = doMount(io, fname, mountPoint, appendToPath); + if (!retval) + { + /* docs say not to call (del) in case of failure, so cheat. */ + MemoryIoInfo *info = (MemoryIoInfo *) io->opaque; + info->destruct = NULL; + io->destroy(io); + } /* if */ + + return retval; +} /* PHYSFS_mountMemory */ + + +int PHYSFS_mountHandle(PHYSFS_File *file, const char *fname, + const char *mountPoint, int appendToPath) +{ + int retval = 0; + PHYSFS_Io *io = NULL; + + BAIL_IF(!file, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!fname, PHYSFS_ERR_INVALID_ARGUMENT, 0); + + io = __PHYSFS_createHandleIo(file); + BAIL_IF_ERRPASS(!io, 0); + retval = doMount(io, fname, mountPoint, appendToPath); + if (!retval) + { + /* docs say not to destruct in case of failure, so cheat. */ + io->opaque = NULL; + io->destroy(io); + } /* if */ + + return retval; +} /* PHYSFS_mountHandle */ + + +int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath) +{ + BAIL_IF(!newDir, PHYSFS_ERR_INVALID_ARGUMENT, 0); + return doMount(NULL, newDir, mountPoint, appendToPath); +} /* PHYSFS_mount */ + + +int PHYSFS_addToSearchPath(const char *newDir, int appendToPath) +{ + return PHYSFS_mount(newDir, NULL, appendToPath); +} /* PHYSFS_addToSearchPath */ + + +int PHYSFS_removeFromSearchPath(const char *oldDir) +{ + return PHYSFS_unmount(oldDir); +} /* PHYSFS_removeFromSearchPath */ + + +int PHYSFS_unmount(const char *oldDir) +{ + DirHandle *i; + DirHandle *prev = NULL; + DirHandle *next = NULL; + + BAIL_IF(oldDir == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0); + + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; i != NULL; i = i->next) + { + if (strcmp(i->dirName, oldDir) == 0) + { + next = i->next; + BAIL_IF_MUTEX_ERRPASS(!freeDirHandle(i, openReadList), + stateLock, 0); + + if (prev == NULL) + searchPath = next; + else + prev->next = next; + + BAIL_MUTEX_ERRPASS(stateLock, 1); + } /* if */ + prev = i; + } /* for */ + + BAIL_MUTEX(PHYSFS_ERR_NOT_MOUNTED, stateLock, 0); +} /* PHYSFS_unmount */ + + +char **PHYSFS_getSearchPath(void) +{ + return doEnumStringList(PHYSFS_getSearchPathCallback); +} /* PHYSFS_getSearchPath */ + + +const char *PHYSFS_getMountPoint(const char *dir) +{ + DirHandle *i; + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; i != NULL; i = i->next) + { + if (strcmp(i->dirName, dir) == 0) + { + const char *retval = ((i->mountPoint) ? i->mountPoint : "/"); + __PHYSFS_platformReleaseMutex(stateLock); + return retval; + } /* if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + + BAIL(PHYSFS_ERR_NOT_MOUNTED, NULL); +} /* PHYSFS_getMountPoint */ + + +void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback callback, void *data) +{ + DirHandle *i; + + __PHYSFS_platformGrabMutex(stateLock); + + for (i = searchPath; i != NULL; i = i->next) + callback(data, i->dirName); + + __PHYSFS_platformReleaseMutex(stateLock); +} /* PHYSFS_getSearchPathCallback */ + + +typedef struct setSaneCfgEnumData +{ + const char *archiveExt; + size_t archiveExtLen; + int archivesFirst; + PHYSFS_ErrorCode errcode; +} setSaneCfgEnumData; + +static PHYSFS_EnumerateCallbackResult setSaneCfgEnumCallback(void *_data, + const char *dir, const char *f) +{ + setSaneCfgEnumData *data = (setSaneCfgEnumData *) _data; + const size_t extlen = data->archiveExtLen; + const size_t l = strlen(f); + const char *ext; + + if ((l > extlen) && (f[l - extlen - 1] == '.')) + { + ext = f + (l - extlen); + if (PHYSFS_utf8stricmp(ext, data->archiveExt) == 0) + { + const char dirsep = __PHYSFS_platformDirSeparator; + const char *d = PHYSFS_getRealDir(f); + const size_t allocsize = strlen(d) + l + 2; + char *str = (char *) __PHYSFS_smallAlloc(allocsize); + if (str == NULL) + data->errcode = PHYSFS_ERR_OUT_OF_MEMORY; + else + { + snprintf(str, allocsize, "%s%c%s", d, dirsep, f); + if (!PHYSFS_mount(str, NULL, data->archivesFirst == 0)) + data->errcode = currentErrorCode(); + __PHYSFS_smallFree(str); + } /* else */ + } /* if */ + } /* if */ + + /* !!! FIXME: if we want to abort on errors... */ + /*return (data->errcode != PHYSFS_ERR_OK) ? PHYSFS_ENUM_ERROR : PHYSFS_ENUM_OK;*/ + + return PHYSFS_ENUM_OK; /* keep going */ +} /* setSaneCfgEnumCallback */ + + +int PHYSFS_setSaneConfig(const char *organization, const char *appName, + const char *archiveExt, int includeCdRoms, + int archivesFirst) +{ + const char *basedir; + const char *prefdir; + + BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0); + + prefdir = PHYSFS_getPrefDir(organization, appName); + BAIL_IF_ERRPASS(!prefdir, 0); + + basedir = PHYSFS_getBaseDir(); + BAIL_IF_ERRPASS(!basedir, 0); + + BAIL_IF(!PHYSFS_setWriteDir(prefdir), PHYSFS_ERR_NO_WRITE_DIR, 0); + + /* !!! FIXME: these can fail and we should report that... */ + + /* Put write dir first in search path... */ + PHYSFS_mount(prefdir, NULL, 0); + + /* Put base path on search path... */ + PHYSFS_mount(basedir, NULL, 1); + + /* handle CD-ROMs... */ + if (includeCdRoms) + { + char **cds = PHYSFS_getCdRomDirs(); + char **i; + for (i = cds; *i != NULL; i++) + PHYSFS_mount(*i, NULL, 1); + PHYSFS_freeList(cds); + } /* if */ + + /* Root out archives, and add them to search path... */ + if (archiveExt != NULL) + { + setSaneCfgEnumData data; + memset(&data, '\0', sizeof (data)); + data.archiveExt = archiveExt; + data.archiveExtLen = strlen(archiveExt); + data.archivesFirst = archivesFirst; + data.errcode = PHYSFS_ERR_OK; + if (!PHYSFS_enumerate("/", setSaneCfgEnumCallback, &data)) + { + /* !!! FIXME: use this if we're reporting errors. + PHYSFS_ErrorCode errcode = currentErrorCode(); + if (errcode == PHYSFS_ERR_APP_CALLBACK) + errcode = data->errcode; */ + } /* if */ + } /* if */ + + return 1; +} /* PHYSFS_setSaneConfig */ + + +void PHYSFS_permitSymbolicLinks(int allow) +{ + allowSymLinks = allow; +} /* PHYSFS_permitSymbolicLinks */ + + +int PHYSFS_symbolicLinksPermitted(void) +{ + return allowSymLinks; +} /* PHYSFS_symbolicLinksPermitted */ + + +/* + * Verify that (fname) (in platform-independent notation), in relation + * to (h) is secure. That means that each element of fname is checked + * for symlinks (if they aren't permitted). This also allows for quick + * rejection of files that exist outside an archive's mountpoint. + * + * With some exceptions (like PHYSFS_mkdir(), which builds multiple subdirs + * at a time), you should always pass zero for "allowMissing" for efficiency. + * + * (fname) must point to an output from sanitizePlatformIndependentPath(), + * since it will make sure that path names are in the right format for + * passing certain checks. It will also do checks for "insecure" pathnames + * like ".." which should be done once instead of once per archive. This also + * gives us license to treat (fname) as scratch space in this function. + * + * Returns non-zero if string is safe, zero if there's a security issue. + * PHYSFS_getLastError() will specify what was wrong. (*fname) will be + * updated to point past any mount point elements so it is prepared to + * be used with the archiver directly. + */ +static int verifyPath(DirHandle *h, char **_fname, int allowMissing) +{ + char *fname = *_fname; + int retval = 1; + char *start; + char *end; + + if (*fname == '\0') /* quick rejection. */ + return 1; + + /* !!! FIXME: This codeblock sucks. */ + if (h->mountPoint != NULL) /* NULL mountpoint means "/". */ + { + size_t mntpntlen = strlen(h->mountPoint); + size_t len = strlen(fname); + assert(mntpntlen > 1); /* root mount points should be NULL. */ + /* not under the mountpoint, so skip this archive. */ + BAIL_IF(len < mntpntlen-1, PHYSFS_ERR_NOT_FOUND, 0); + /* !!! FIXME: Case insensitive? */ + retval = strncmp(h->mountPoint, fname, mntpntlen-1); + BAIL_IF(retval != 0, PHYSFS_ERR_NOT_FOUND, 0); + if (len > mntpntlen-1) /* corner case... */ + BAIL_IF(fname[mntpntlen-1]!='/', PHYSFS_ERR_NOT_FOUND, 0); + fname += mntpntlen-1; /* move to start of actual archive path. */ + if (*fname == '/') + fname++; + *_fname = fname; /* skip mountpoint for later use. */ + retval = 1; /* may be reset, below. */ + } /* if */ + + start = fname; + if (!allowSymLinks) + { + while (1) + { + PHYSFS_Stat statbuf; + int rc = 0; + end = strchr(start, '/'); + + if (end != NULL) *end = '\0'; + rc = h->funcs->stat(h->opaque, fname, &statbuf); + if (rc) + rc = (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK); + else if (currentErrorCode() == PHYSFS_ERR_NOT_FOUND) + retval = 0; + + if (end != NULL) *end = '/'; + + /* insecure path (has a disallowed symlink in it)? */ + BAIL_IF(rc, PHYSFS_ERR_SYMLINK_FORBIDDEN, 0); + + /* break out early if path element is missing. */ + if (!retval) + { + /* + * We need to clear it if it's the last element of the path, + * since this might be a non-existant file we're opening + * for writing... + */ + if ((end == NULL) || (allowMissing)) + retval = 1; + break; + } /* if */ + + if (end == NULL) + break; + + start = end + 1; + } /* while */ + } /* if */ + + return retval; +} /* verifyPath */ + + +static int doMkdir(const char *_dname, char *dname) +{ + DirHandle *h; + char *start; + char *end; + int retval = 0; + int exists = 1; /* force existance check on first path element. */ + + BAIL_IF_ERRPASS(!sanitizePlatformIndependentPath(_dname, dname), 0); + + __PHYSFS_platformGrabMutex(stateLock); + BAIL_IF_MUTEX(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, stateLock, 0); + h = writeDir; + BAIL_IF_MUTEX_ERRPASS(!verifyPath(h, &dname, 1), stateLock, 0); + + start = dname; + while (1) + { + end = strchr(start, '/'); + if (end != NULL) + *end = '\0'; + + /* only check for existance if all parent dirs existed, too... */ + if (exists) + { + PHYSFS_Stat statbuf; + const int rc = h->funcs->stat(h->opaque, dname, &statbuf); + if ((!rc) && (currentErrorCode() == PHYSFS_ERR_NOT_FOUND)) + exists = 0; + retval = ((rc) && (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY)); + } /* if */ + + if (!exists) + retval = h->funcs->mkdir(h->opaque, dname); + + if (!retval) + break; + + if (end == NULL) + break; + + *end = '/'; + start = end + 1; + } /* while */ + + __PHYSFS_platformReleaseMutex(stateLock); + return retval; +} /* doMkdir */ + + +int PHYSFS_mkdir(const char *_dname) +{ + int retval = 0; + char *dname; + size_t len; + + BAIL_IF(!_dname, PHYSFS_ERR_INVALID_ARGUMENT, 0); + len = strlen(_dname) + 1; + dname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF(!dname, PHYSFS_ERR_OUT_OF_MEMORY, 0); + retval = doMkdir(_dname, dname); + __PHYSFS_smallFree(dname); + return retval; +} /* PHYSFS_mkdir */ + + +static int doDelete(const char *_fname, char *fname) +{ + int retval; + DirHandle *h; + BAIL_IF_ERRPASS(!sanitizePlatformIndependentPath(_fname, fname), 0); + + __PHYSFS_platformGrabMutex(stateLock); + + BAIL_IF_MUTEX(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, stateLock, 0); + h = writeDir; + BAIL_IF_MUTEX_ERRPASS(!verifyPath(h, &fname, 0), stateLock, 0); + retval = h->funcs->remove(h->opaque, fname); + + __PHYSFS_platformReleaseMutex(stateLock); + return retval; +} /* doDelete */ + + +int PHYSFS_delete(const char *_fname) +{ + int retval; + char *fname; + size_t len; + + BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0); + retval = doDelete(_fname, fname); + __PHYSFS_smallFree(fname); + return retval; +} /* PHYSFS_delete */ + + +static DirHandle *getRealDirHandle(const char *_fname) +{ + DirHandle *retval = NULL; + char *fname = NULL; + size_t len; + + BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, NULL); + len = strlen(_fname) + 1; + fname = __PHYSFS_smallAlloc(len); + BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + if (sanitizePlatformIndependentPath(_fname, fname)) + { + DirHandle *i; + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; i != NULL; i = i->next) + { + char *arcfname = fname; + if (partOfMountPoint(i, arcfname)) + { + retval = i; + break; + } /* if */ + else if (verifyPath(i, &arcfname, 0)) + { + PHYSFS_Stat statbuf; + if (i->funcs->stat(i->opaque, arcfname, &statbuf)) + { + retval = i; + break; + } /* if */ + } /* if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); + return retval; +} /* getRealDirHandle */ + +const char *PHYSFS_getRealDir(const char *fname) +{ + DirHandle *dh = getRealDirHandle(fname); + return dh ? dh->dirName : NULL; +} /* PHYSFS_getRealDir */ + + +static int locateInStringList(const char *str, + char **list, + PHYSFS_uint32 *pos) +{ + PHYSFS_uint32 len = *pos; + PHYSFS_uint32 half_len; + PHYSFS_uint32 lo = 0; + PHYSFS_uint32 middle; + int cmp; + + while (len > 0) + { + half_len = len >> 1; + middle = lo + half_len; + cmp = strcmp(list[middle], str); + + if (cmp == 0) /* it's in the list already. */ + return 1; + else if (cmp > 0) + len = half_len; + else + { + lo = middle + 1; + len -= half_len + 1; + } /* else */ + } /* while */ + + *pos = lo; + return 0; +} /* locateInStringList */ + + +static PHYSFS_EnumerateCallbackResult enumFilesCallback(void *data, + const char *origdir, const char *str) +{ + PHYSFS_uint32 pos; + void *ptr; + char *newstr; + EnumStringListCallbackData *pecd = (EnumStringListCallbackData *) data; + + /* + * See if file is in the list already, and if not, insert it in there + * alphabetically... + */ + pos = pecd->size; + if (locateInStringList(str, pecd->list, &pos)) + return PHYSFS_ENUM_OK; /* already in the list, but keep going. */ + + ptr = allocator.Realloc(pecd->list, (pecd->size + 2) * sizeof (char *)); + newstr = (char *) allocator.Malloc(strlen(str) + 1); + if (ptr != NULL) + pecd->list = (char **) ptr; + + if ((ptr == NULL) || (newstr == NULL)) + { + if (newstr) + allocator.Free(newstr); + + pecd->errcode = PHYSFS_ERR_OUT_OF_MEMORY; + return PHYSFS_ENUM_ERROR; /* better luck next time. */ + } /* if */ + + strcpy(newstr, str); + + if (pos != pecd->size) + { + memmove(&pecd->list[pos+1], &pecd->list[pos], + sizeof (char *) * ((pecd->size) - pos)); + } /* if */ + + pecd->list[pos] = newstr; + pecd->size++; + + return PHYSFS_ENUM_OK; +} /* enumFilesCallback */ + + +char **PHYSFS_enumerateFiles(const char *path) +{ + EnumStringListCallbackData ecd; + memset(&ecd, '\0', sizeof (ecd)); + ecd.list = (char **) allocator.Malloc(sizeof (char *)); + BAIL_IF(!ecd.list, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + if (!PHYSFS_enumerate(path, enumFilesCallback, &ecd)) + { + const PHYSFS_ErrorCode errcode = currentErrorCode(); + PHYSFS_uint32 i; + for (i = 0; i < ecd.size; i++) + allocator.Free(ecd.list[i]); + allocator.Free(ecd.list); + BAIL_IF(errcode == PHYSFS_ERR_APP_CALLBACK, ecd.errcode, NULL); + return NULL; + } /* if */ + + ecd.list[ecd.size] = NULL; + return ecd.list; +} /* PHYSFS_enumerateFiles */ + + +/* + * Broke out to seperate function so we can use stack allocation gratuitously. + */ +static PHYSFS_EnumerateCallbackResult enumerateFromMountPoint(DirHandle *i, + const char *arcfname, + PHYSFS_EnumerateCallback callback, + const char *_fname, void *data) +{ + PHYSFS_EnumerateCallbackResult retval; + const size_t len = strlen(arcfname); + char *ptr = NULL; + char *end = NULL; + const size_t slen = strlen(i->mountPoint) + 1; + char *mountPoint = (char *) __PHYSFS_smallAlloc(slen); + + BAIL_IF(!mountPoint, PHYSFS_ERR_OUT_OF_MEMORY, PHYSFS_ENUM_ERROR); + + strcpy(mountPoint, i->mountPoint); + ptr = mountPoint + ((len) ? len + 1 : 0); + end = strchr(ptr, '/'); + assert(end); /* should always find a terminating '/'. */ + *end = '\0'; + retval = callback(data, _fname, ptr); + __PHYSFS_smallFree(mountPoint); + + BAIL_IF(retval == PHYSFS_ENUM_ERROR, PHYSFS_ERR_APP_CALLBACK, retval); + return retval; +} /* enumerateFromMountPoint */ + + +typedef struct SymlinkFilterData +{ + PHYSFS_EnumerateCallback callback; + void *callbackData; + DirHandle *dirhandle; + const char *arcfname; + PHYSFS_ErrorCode errcode; +} SymlinkFilterData; + +static PHYSFS_EnumerateCallbackResult enumCallbackFilterSymLinks(void *_data, + const char *origdir, const char *fname) +{ + SymlinkFilterData *data = (SymlinkFilterData *) _data; + const DirHandle *dh = data->dirhandle; + const char *arcfname = data->arcfname; + PHYSFS_Stat statbuf; + const char *trimmedDir = (*arcfname == '/') ? (arcfname + 1) : arcfname; + const size_t slen = strlen(trimmedDir) + strlen(fname) + 2; + char *path = (char *) __PHYSFS_smallAlloc(slen); + PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK; + + if (path == NULL) + { + data->errcode = PHYSFS_ERR_OUT_OF_MEMORY; + return PHYSFS_ENUM_ERROR; + } /* if */ + + snprintf(path, slen, "%s%s%s", trimmedDir, *trimmedDir ? "/" : "", fname); + + if (!dh->funcs->stat(dh->opaque, path, &statbuf)) + { + data->errcode = currentErrorCode(); + retval = PHYSFS_ENUM_ERROR; + } /* if */ + else + { + /* Pass it on to the application if it's not a symlink. */ + if (statbuf.filetype != PHYSFS_FILETYPE_SYMLINK) + { + retval = data->callback(data->callbackData, origdir, fname); + if (retval == PHYSFS_ENUM_ERROR) + data->errcode = PHYSFS_ERR_APP_CALLBACK; + } /* if */ + } /* else */ + + __PHYSFS_smallFree(path); + + return retval; +} /* enumCallbackFilterSymLinks */ + + +int PHYSFS_enumerate(const char *_fn, PHYSFS_EnumerateCallback cb, void *data) +{ + PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK; + size_t len; + char *fname; + + BAIL_IF(!_fn, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!cb, PHYSFS_ERR_INVALID_ARGUMENT, 0); + + len = strlen(_fn) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0); + + if (!sanitizePlatformIndependentPath(_fn, fname)) + retval = PHYSFS_ENUM_STOP; + else + { + DirHandle *i; + SymlinkFilterData filterdata; + + __PHYSFS_platformGrabMutex(stateLock); + + if (!allowSymLinks) + { + memset(&filterdata, '\0', sizeof (filterdata)); + filterdata.callback = cb; + filterdata.callbackData = data; + } /* if */ + + for (i = searchPath; (retval == PHYSFS_ENUM_OK) && i; i = i->next) + { + char *arcfname = fname; + + if (partOfMountPoint(i, arcfname)) + retval = enumerateFromMountPoint(i, arcfname, cb, _fn, data); + + else if (verifyPath(i, &arcfname, 0)) + { + PHYSFS_Stat statbuf; + if (!i->funcs->stat(i->opaque, arcfname, &statbuf)) + { + if (currentErrorCode() == PHYSFS_ERR_NOT_FOUND) + continue; /* no such dir in this archive, skip it. */ + } /* if */ + + if (statbuf.filetype != PHYSFS_FILETYPE_DIRECTORY) + continue; /* not a directory in this archive, skip it. */ + + else if ((!allowSymLinks) && (i->funcs->info.supportsSymlinks)) + { + filterdata.dirhandle = i; + filterdata.arcfname = arcfname; + filterdata.errcode = PHYSFS_ERR_OK; + retval = i->funcs->enumerate(i->opaque, arcfname, + enumCallbackFilterSymLinks, + _fn, &filterdata); + if (retval == PHYSFS_ENUM_ERROR) + { + if (currentErrorCode() == PHYSFS_ERR_APP_CALLBACK) + PHYSFS_setErrorCode(filterdata.errcode); + } /* if */ + } /* else if */ + else + { + retval = i->funcs->enumerate(i->opaque, arcfname, + cb, _fn, data); + } /* else */ + } /* else if */ + } /* for */ + + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); + + return (retval == PHYSFS_ENUM_ERROR) ? 0 : 1; +} /* PHYSFS_enumerate */ + + +typedef struct +{ + PHYSFS_EnumFilesCallback callback; + void *data; +} LegacyEnumFilesCallbackData; + +static PHYSFS_EnumerateCallbackResult enumFilesCallbackAlwaysSucceed(void *d, + const char *origdir, const char *fname) +{ + LegacyEnumFilesCallbackData *cbdata = (LegacyEnumFilesCallbackData *) d; + cbdata->callback(cbdata->data, origdir, fname); + return PHYSFS_ENUM_OK; +} /* enumFilesCallbackAlwaysSucceed */ + +void PHYSFS_enumerateFilesCallback(const char *fname, + PHYSFS_EnumFilesCallback callback, + void *data) +{ + LegacyEnumFilesCallbackData cbdata; + cbdata.callback = callback; + cbdata.data = data; + (void) PHYSFS_enumerate(fname, enumFilesCallbackAlwaysSucceed, &cbdata); +} /* PHYSFS_enumerateFilesCallback */ + + +int PHYSFS_exists(const char *fname) +{ + return (getRealDirHandle(fname) != NULL); +} /* PHYSFS_exists */ + + +PHYSFS_sint64 PHYSFS_getLastModTime(const char *fname) +{ + PHYSFS_Stat statbuf; + BAIL_IF_ERRPASS(!PHYSFS_stat(fname, &statbuf), -1); + return statbuf.modtime; +} /* PHYSFS_getLastModTime */ + + +int PHYSFS_isDirectory(const char *fname) +{ + PHYSFS_Stat statbuf; + BAIL_IF_ERRPASS(!PHYSFS_stat(fname, &statbuf), 0); + return (statbuf.filetype == PHYSFS_FILETYPE_DIRECTORY); +} /* PHYSFS_isDirectory */ + + +int PHYSFS_isSymbolicLink(const char *fname) +{ + PHYSFS_Stat statbuf; + BAIL_IF_ERRPASS(!PHYSFS_stat(fname, &statbuf), 0); + return (statbuf.filetype == PHYSFS_FILETYPE_SYMLINK); +} /* PHYSFS_isSymbolicLink */ + + +static PHYSFS_File *doOpenWrite(const char *_fname, int appending) +{ + FileHandle *fh = NULL; + size_t len; + char *fname; + + BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0); + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + PHYSFS_Io *io = NULL; + DirHandle *h = NULL; + const PHYSFS_Archiver *f; + + __PHYSFS_platformGrabMutex(stateLock); + + GOTO_IF(!writeDir, PHYSFS_ERR_NO_WRITE_DIR, doOpenWriteEnd); + + h = writeDir; + GOTO_IF_ERRPASS(!verifyPath(h, &fname, 0), doOpenWriteEnd); + + f = h->funcs; + if (appending) + io = f->openAppend(h->opaque, fname); + else + io = f->openWrite(h->opaque, fname); + + GOTO_IF_ERRPASS(!io, doOpenWriteEnd); + + fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle)); + if (fh == NULL) + { + io->destroy(io); + GOTO(PHYSFS_ERR_OUT_OF_MEMORY, doOpenWriteEnd); + } /* if */ + else + { + memset(fh, '\0', sizeof (FileHandle)); + fh->io = io; + fh->dirHandle = h; + fh->next = openWriteList; + openWriteList = fh; + } /* else */ + + doOpenWriteEnd: + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); + return ((PHYSFS_File *) fh); +} /* doOpenWrite */ + + +PHYSFS_File *PHYSFS_openWrite(const char *filename) +{ + return doOpenWrite(filename, 0); +} /* PHYSFS_openWrite */ + + +PHYSFS_File *PHYSFS_openAppend(const char *filename) +{ + return doOpenWrite(filename, 1); +} /* PHYSFS_openAppend */ + + +PHYSFS_File *PHYSFS_openRead(const char *_fname) +{ + FileHandle *fh = NULL; + char *fname; + size_t len; + + BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0); + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + DirHandle *i = NULL; + PHYSFS_Io *io = NULL; + + __PHYSFS_platformGrabMutex(stateLock); + + GOTO_IF(!searchPath, PHYSFS_ERR_NOT_FOUND, openReadEnd); + + for (i = searchPath; i != NULL; i = i->next) + { + char *arcfname = fname; + if (verifyPath(i, &arcfname, 0)) + { + io = i->funcs->openRead(i->opaque, arcfname); + if (io) + break; + } /* if */ + } /* for */ + + GOTO_IF_ERRPASS(!io, openReadEnd); + + fh = (FileHandle *) allocator.Malloc(sizeof (FileHandle)); + if (fh == NULL) + { + io->destroy(io); + GOTO(PHYSFS_ERR_OUT_OF_MEMORY, openReadEnd); + } /* if */ + + memset(fh, '\0', sizeof (FileHandle)); + fh->io = io; + fh->forReading = 1; + fh->dirHandle = i; + fh->next = openReadList; + openReadList = fh; + + openReadEnd: + __PHYSFS_platformReleaseMutex(stateLock); + } /* if */ + + __PHYSFS_smallFree(fname); + return ((PHYSFS_File *) fh); +} /* PHYSFS_openRead */ + + +static int closeHandleInOpenList(FileHandle **list, FileHandle *handle) +{ + FileHandle *prev = NULL; + FileHandle *i; + int rc = 1; + + for (i = *list; i != NULL; i = i->next) + { + if (i == handle) /* handle is in this list? */ + { + PHYSFS_Io *io = handle->io; + PHYSFS_uint8 *tmp = handle->buffer; + rc = PHYSFS_flush((PHYSFS_File *) handle); + if (!rc) + return -1; + io->destroy(io); + + if (tmp != NULL) /* free any associated buffer. */ + allocator.Free(tmp); + + if (prev == NULL) + *list = handle->next; + else + prev->next = handle->next; + + allocator.Free(handle); + return 1; + } /* if */ + prev = i; + } /* for */ + + return 0; +} /* closeHandleInOpenList */ + + +int PHYSFS_close(PHYSFS_File *_handle) +{ + FileHandle *handle = (FileHandle *) _handle; + int rc; + + __PHYSFS_platformGrabMutex(stateLock); + + /* -1 == close failure. 0 == not found. 1 == success. */ + rc = closeHandleInOpenList(&openReadList, handle); + BAIL_IF_MUTEX_ERRPASS(rc == -1, stateLock, 0); + if (!rc) + { + rc = closeHandleInOpenList(&openWriteList, handle); + BAIL_IF_MUTEX_ERRPASS(rc == -1, stateLock, 0); + } /* if */ + + __PHYSFS_platformReleaseMutex(stateLock); + BAIL_IF(!rc, PHYSFS_ERR_INVALID_ARGUMENT, 0); + return 1; +} /* PHYSFS_close */ + + +static PHYSFS_sint64 doBufferedRead(FileHandle *fh, void *_buffer, size_t len) +{ + PHYSFS_uint8 *buffer = (PHYSFS_uint8 *) _buffer; + PHYSFS_sint64 retval = 0; + + while (len > 0) + { + const size_t avail = fh->buffill - fh->bufpos; + if (avail > 0) /* data available in the buffer. */ + { + const size_t cpy = (len < avail) ? len : avail; + memcpy(buffer, fh->buffer + fh->bufpos, cpy); + assert(len >= cpy); + buffer += cpy; + len -= cpy; + fh->bufpos += cpy; + retval += cpy; + } /* if */ + + else /* buffer is empty, refill it. */ + { + PHYSFS_Io *io = fh->io; + const PHYSFS_sint64 rc = io->read(io, fh->buffer, fh->bufsize); + fh->bufpos = 0; + if (rc > 0) + fh->buffill = (size_t) rc; + else + { + fh->buffill = 0; + if (retval == 0) /* report already-read data, or failure. */ + retval = rc; + break; + } /* else */ + } /* else */ + } /* while */ + + return retval; +} /* doBufferedRead */ + + +PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + const PHYSFS_uint64 len = ((PHYSFS_uint64) size) * ((PHYSFS_uint64) count); + const PHYSFS_sint64 retval = PHYSFS_readBytes(handle, buffer, len); + return ( (retval <= 0) ? retval : (retval / ((PHYSFS_sint64) size)) ); +} /* PHYSFS_read */ + + +PHYSFS_sint64 PHYSFS_readBytes(PHYSFS_File *handle, void *buffer, + PHYSFS_uint64 _len) +{ + const size_t len = (size_t) _len; + FileHandle *fh = (FileHandle *) handle; + +#ifdef PHYSFS_NO_64BIT_SUPPORT + const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFF); +#else + const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFFFFFFFFFF); +#endif + + if (!__PHYSFS_ui64FitsAddressSpace(_len)) + BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1); + + BAIL_IF(_len > maxlen, PHYSFS_ERR_INVALID_ARGUMENT, -1); + BAIL_IF(!fh->forReading, PHYSFS_ERR_OPEN_FOR_WRITING, -1); + BAIL_IF_ERRPASS(len == 0, 0); + if (fh->buffer) + return doBufferedRead(fh, buffer, len); + + return fh->io->read(fh->io, buffer, len); +} /* PHYSFS_readBytes */ + + +static PHYSFS_sint64 doBufferedWrite(PHYSFS_File *handle, const void *buffer, + const size_t len) +{ + FileHandle *fh = (FileHandle *) handle; + + /* whole thing fits in the buffer? */ + if ((fh->buffill + len) < fh->bufsize) + { + memcpy(fh->buffer + fh->buffill, buffer, len); + fh->buffill += len; + return (PHYSFS_sint64) len; + } /* if */ + + /* would overflow buffer. Flush and then write the new objects, too. */ + BAIL_IF_ERRPASS(!PHYSFS_flush(handle), -1); + return fh->io->write(fh->io, buffer, len); +} /* doBufferedWrite */ + + +PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, const void *buffer, + PHYSFS_uint32 size, PHYSFS_uint32 count) +{ + const PHYSFS_uint64 len = ((PHYSFS_uint64) size) * ((PHYSFS_uint64) count); + const PHYSFS_sint64 retval = PHYSFS_writeBytes(handle, buffer, len); + return ( (retval <= 0) ? retval : (retval / ((PHYSFS_sint64) size)) ); +} /* PHYSFS_write */ + + +PHYSFS_sint64 PHYSFS_writeBytes(PHYSFS_File *handle, const void *buffer, + PHYSFS_uint64 _len) +{ + const size_t len = (size_t) _len; + FileHandle *fh = (FileHandle *) handle; + +#ifdef PHYSFS_NO_64BIT_SUPPORT + const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFF); +#else + const PHYSFS_uint64 maxlen = __PHYSFS_UI64(0x7FFFFFFFFFFFFFFF); +#endif + + if (!__PHYSFS_ui64FitsAddressSpace(_len)) + BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1); + + BAIL_IF(_len > maxlen, PHYSFS_ERR_INVALID_ARGUMENT, -1); + BAIL_IF(fh->forReading, PHYSFS_ERR_OPEN_FOR_READING, -1); + BAIL_IF_ERRPASS(len == 0, 0); + if (fh->buffer) + return doBufferedWrite(handle, buffer, len); + + return fh->io->write(fh->io, buffer, len); +} /* PHYSFS_write */ + + +int PHYSFS_eof(PHYSFS_File *handle) +{ + FileHandle *fh = (FileHandle *) handle; + + if (!fh->forReading) /* never EOF on files opened for write/append. */ + return 0; + + /* can't be eof if buffer isn't empty */ + if (fh->bufpos == fh->buffill) + { + /* check the Io. */ + PHYSFS_Io *io = fh->io; + const PHYSFS_sint64 pos = io->tell(io); + const PHYSFS_sint64 len = io->length(io); + if ((pos < 0) || (len < 0)) + return 0; /* beats me. */ + return (pos >= len); + } /* if */ + + return 0; +} /* PHYSFS_eof */ + + +PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle) +{ + FileHandle *fh = (FileHandle *) handle; + const PHYSFS_sint64 pos = fh->io->tell(fh->io); + const PHYSFS_sint64 retval = fh->forReading ? + (pos - fh->buffill) + fh->bufpos : + (pos + fh->buffill); + return retval; +} /* PHYSFS_tell */ + + +int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos) +{ + FileHandle *fh = (FileHandle *) handle; + BAIL_IF_ERRPASS(!PHYSFS_flush(handle), 0); + + if (fh->buffer && fh->forReading) + { + /* avoid throwing away our precious buffer if seeking within it. */ + PHYSFS_sint64 offset = pos - PHYSFS_tell(handle); + if ( /* seeking within the already-buffered range? */ + /* forward? */ + ((offset >= 0) && (((size_t)offset) <= fh->buffill-fh->bufpos)) || + /* backward? */ + ((offset < 0) && (((size_t) -offset) <= fh->bufpos)) ) + { + fh->bufpos = (size_t) (((PHYSFS_sint64) fh->bufpos) + offset); + return 1; /* successful seek */ + } /* if */ + } /* if */ + + /* we have to fall back to a 'raw' seek. */ + fh->buffill = fh->bufpos = 0; + return fh->io->seek(fh->io, pos); +} /* PHYSFS_seek */ + + +PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle) +{ + PHYSFS_Io *io = ((FileHandle *) handle)->io; + return io->length(io); +} /* PHYSFS_filelength */ + + +int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 _bufsize) +{ + FileHandle *fh = (FileHandle *) handle; + const size_t bufsize = (size_t) _bufsize; + + if (!__PHYSFS_ui64FitsAddressSpace(_bufsize)) + BAIL(PHYSFS_ERR_INVALID_ARGUMENT, 0); + + BAIL_IF_ERRPASS(!PHYSFS_flush(handle), 0); + + /* + * For reads, we need to move the file pointer to where it would be + * if we weren't buffering, so that the next read will get the + * right chunk of stuff from the file. PHYSFS_flush() handles writes. + */ + if ((fh->forReading) && (fh->buffill != fh->bufpos)) + { + PHYSFS_uint64 pos; + const PHYSFS_sint64 curpos = fh->io->tell(fh->io); + BAIL_IF_ERRPASS(curpos == -1, 0); + pos = ((curpos - fh->buffill) + fh->bufpos); + BAIL_IF_ERRPASS(!fh->io->seek(fh->io, pos), 0); + } /* if */ + + if (bufsize == 0) /* delete existing buffer. */ + { + if (fh->buffer) + { + allocator.Free(fh->buffer); + fh->buffer = NULL; + } /* if */ + } /* if */ + + else + { + PHYSFS_uint8 *newbuf; + newbuf = (PHYSFS_uint8 *) allocator.Realloc(fh->buffer, bufsize); + BAIL_IF(!newbuf, PHYSFS_ERR_OUT_OF_MEMORY, 0); + fh->buffer = newbuf; + } /* else */ + + fh->bufsize = bufsize; + fh->buffill = fh->bufpos = 0; + return 1; +} /* PHYSFS_setBuffer */ + + +int PHYSFS_flush(PHYSFS_File *handle) +{ + FileHandle *fh = (FileHandle *) handle; + PHYSFS_Io *io; + PHYSFS_sint64 rc; + + if ((fh->forReading) || (fh->bufpos == fh->buffill)) + return 1; /* open for read or buffer empty are successful no-ops. */ + + /* dump buffer to disk. */ + io = fh->io; + rc = io->write(io, fh->buffer + fh->bufpos, fh->buffill - fh->bufpos); + BAIL_IF_ERRPASS(rc <= 0, 0); + fh->bufpos = fh->buffill = 0; + return io->flush ? io->flush(io) : 1; +} /* PHYSFS_flush */ + + +int PHYSFS_stat(const char *_fname, PHYSFS_Stat *stat) +{ + int retval = 0; + char *fname; + size_t len; + + BAIL_IF(!_fname, PHYSFS_ERR_INVALID_ARGUMENT, 0); + BAIL_IF(!stat, PHYSFS_ERR_INVALID_ARGUMENT, 0); + len = strlen(_fname) + 1; + fname = (char *) __PHYSFS_smallAlloc(len); + BAIL_IF(!fname, PHYSFS_ERR_OUT_OF_MEMORY, 0); + + /* set some sane defaults... */ + stat->filesize = -1; + stat->modtime = -1; + stat->createtime = -1; + stat->accesstime = -1; + stat->filetype = PHYSFS_FILETYPE_OTHER; + stat->readonly = 1; + + if (sanitizePlatformIndependentPath(_fname, fname)) + { + if (*fname == '\0') + { + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + stat->readonly = !writeDir; /* Writeable if we have a writeDir */ + retval = 1; + } /* if */ + else + { + DirHandle *i; + int exists = 0; + __PHYSFS_platformGrabMutex(stateLock); + for (i = searchPath; ((i != NULL) && (!exists)); i = i->next) + { + char *arcfname = fname; + exists = partOfMountPoint(i, arcfname); + if (exists) + { + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + stat->readonly = 1; + retval = 1; + } /* if */ + else if (verifyPath(i, &arcfname, 0)) + { + retval = i->funcs->stat(i->opaque, arcfname, stat); + if ((retval) || (currentErrorCode() != PHYSFS_ERR_NOT_FOUND)) + exists = 1; + } /* else if */ + } /* for */ + __PHYSFS_platformReleaseMutex(stateLock); + } /* else */ + } /* if */ + + __PHYSFS_smallFree(fname); + return retval; +} /* PHYSFS_stat */ + + +int __PHYSFS_readAll(PHYSFS_Io *io, void *buf, const size_t _len) +{ + const PHYSFS_uint64 len = (PHYSFS_uint64) _len; + return (io->read(io, buf, len) == len); +} /* __PHYSFS_readAll */ + + +void *__PHYSFS_initSmallAlloc(void *ptr, const size_t len) +{ + void *useHeap = ((ptr == NULL) ? ((void *) 1) : ((void *) 0)); + if (useHeap) /* too large for stack allocation or alloca() failed. */ + ptr = allocator.Malloc(len+sizeof (void *)); + + if (ptr != NULL) + { + void **retval = (void **) ptr; + /*printf("%s alloc'd (%lld) bytes at (%p).\n", + useHeap ? "heap" : "stack", (long long) len, ptr);*/ + *retval = useHeap; + return retval + 1; + } /* if */ + + return NULL; /* allocation failed. */ +} /* __PHYSFS_initSmallAlloc */ + + +void __PHYSFS_smallFree(void *ptr) +{ + if (ptr != NULL) + { + void **block = ((void **) ptr) - 1; + const int useHeap = (*block != NULL); + if (useHeap) + allocator.Free(block); + /*printf("%s free'd (%p).\n", useHeap ? "heap" : "stack", block);*/ + } /* if */ +} /* __PHYSFS_smallFree */ + + +int PHYSFS_setAllocator(const PHYSFS_Allocator *a) +{ + BAIL_IF(initialized, PHYSFS_ERR_IS_INITIALIZED, 0); + externalAllocator = (a != NULL); + if (externalAllocator) + memcpy(&allocator, a, sizeof (PHYSFS_Allocator)); + + return 1; +} /* PHYSFS_setAllocator */ + + +const PHYSFS_Allocator *PHYSFS_getAllocator(void) +{ + BAIL_IF(!initialized, PHYSFS_ERR_NOT_INITIALIZED, NULL); + return &allocator; +} /* PHYSFS_getAllocator */ + + +static void *mallocAllocatorMalloc(PHYSFS_uint64 s) +{ + if (!__PHYSFS_ui64FitsAddressSpace(s)) + BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); + #undef malloc + return malloc((size_t) s); +} /* mallocAllocatorMalloc */ + + +static void *mallocAllocatorRealloc(void *ptr, PHYSFS_uint64 s) +{ + if (!__PHYSFS_ui64FitsAddressSpace(s)) + BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); + #undef realloc + return realloc(ptr, (size_t) s); +} /* mallocAllocatorRealloc */ + + +static void mallocAllocatorFree(void *ptr) +{ + #undef free + free(ptr); +} /* mallocAllocatorFree */ + + +static void setDefaultAllocator(void) +{ + assert(!externalAllocator); + allocator.Init = NULL; + allocator.Deinit = NULL; + allocator.Malloc = mallocAllocatorMalloc; + allocator.Realloc = mallocAllocatorRealloc; + allocator.Free = mallocAllocatorFree; +} /* setDefaultAllocator */ + + +int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen) +{ + static char rootpath[2] = { '/', '\0' }; + size_t alloclen; + + assert(entrylen >= sizeof (__PHYSFS_DirTreeEntry)); + + memset(dt, '\0', sizeof (*dt)); + + dt->root = (__PHYSFS_DirTreeEntry *) allocator.Malloc(entrylen); + BAIL_IF(!dt->root, PHYSFS_ERR_OUT_OF_MEMORY, 0); + memset(dt->root, '\0', entrylen); + dt->root->name = rootpath; + dt->root->isdir = 1; + dt->hashBuckets = 64; + if (!dt->hashBuckets) + dt->hashBuckets = 1; + dt->entrylen = entrylen; + + alloclen = dt->hashBuckets * sizeof (__PHYSFS_DirTreeEntry *); + dt->hash = (__PHYSFS_DirTreeEntry **) allocator.Malloc(alloclen); + BAIL_IF(!dt->hash, PHYSFS_ERR_OUT_OF_MEMORY, 0); + memset(dt->hash, '\0', alloclen); + + return 1; +} /* __PHYSFS_DirTreeInit */ + + +static inline PHYSFS_uint32 hashPathName(__PHYSFS_DirTree *dt, const char *name) +{ + return __PHYSFS_hashString(name, strlen(name)) % dt->hashBuckets; +} /* hashPathName */ + + +/* Fill in missing parent directories. */ +static __PHYSFS_DirTreeEntry *addAncestors(__PHYSFS_DirTree *dt, char *name) +{ + __PHYSFS_DirTreeEntry *retval = dt->root; + char *sep = strrchr(name, '/'); + + if (sep) + { + *sep = '\0'; /* chop off last piece. */ + retval = (__PHYSFS_DirTreeEntry *) __PHYSFS_DirTreeFind(dt, name); + + if (retval != NULL) + { + *sep = '/'; + BAIL_IF(!retval->isdir, PHYSFS_ERR_CORRUPT, NULL); + return retval; /* already hashed. */ + } /* if */ + + /* okay, this is a new dir. Build and hash us. */ + retval = (__PHYSFS_DirTreeEntry*)__PHYSFS_DirTreeAdd(dt, name, 1); + *sep = '/'; + } /* if */ + + return retval; +} /* addAncestors */ + + +void *__PHYSFS_DirTreeAdd(__PHYSFS_DirTree *dt, char *name, const int isdir) +{ + __PHYSFS_DirTreeEntry *retval = __PHYSFS_DirTreeFind(dt, name); + if (!retval) + { + const size_t alloclen = strlen(name) + 1 + dt->entrylen; + PHYSFS_uint32 hashval; + __PHYSFS_DirTreeEntry *parent = addAncestors(dt, name); + BAIL_IF_ERRPASS(!parent, NULL); + assert(dt->entrylen >= sizeof (__PHYSFS_DirTreeEntry)); + retval = (__PHYSFS_DirTreeEntry *) allocator.Malloc(alloclen); + BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + memset(retval, '\0', dt->entrylen); + retval->name = ((char *) retval) + dt->entrylen; + strcpy(retval->name, name); + hashval = hashPathName(dt, name); + retval->hashnext = dt->hash[hashval]; + dt->hash[hashval] = retval; + retval->sibling = parent->children; + retval->isdir = isdir; + parent->children = retval; + } /* if */ + + return retval; +} /* __PHYSFS_DirTreeAdd */ + + +/* Find the __PHYSFS_DirTreeEntry for a path in platform-independent notation. */ +void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path) +{ + PHYSFS_uint32 hashval; + __PHYSFS_DirTreeEntry *prev = NULL; + __PHYSFS_DirTreeEntry *retval; + + if (*path == '\0') + return dt->root; + + hashval = hashPathName(dt, path); + for (retval = dt->hash[hashval]; retval; retval = retval->hashnext) + { + if (strcmp(retval->name, path) == 0) + { + if (prev != NULL) /* move this to the front of the list */ + { + prev->hashnext = retval->hashnext; + retval->hashnext = dt->hash[hashval]; + dt->hash[hashval] = retval; + } /* if */ + + return retval; + } /* if */ + + prev = retval; + } /* for */ + + BAIL(PHYSFS_ERR_NOT_FOUND, NULL); +} /* __PHYSFS_DirTreeFind */ + +PHYSFS_EnumerateCallbackResult __PHYSFS_DirTreeEnumerate(void *opaque, + const char *dname, PHYSFS_EnumerateCallback cb, + const char *origdir, void *callbackdata) +{ + PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK; + __PHYSFS_DirTree *tree = (__PHYSFS_DirTree *) opaque; + const __PHYSFS_DirTreeEntry *entry = __PHYSFS_DirTreeFind(tree, dname); + BAIL_IF(!entry, PHYSFS_ERR_NOT_FOUND, PHYSFS_ENUM_ERROR); + + entry = entry->children; + + while (entry && (retval == PHYSFS_ENUM_OK)) + { + const char *name = entry->name; + const char *ptr = strrchr(name, '/'); + retval = cb(callbackdata, origdir, ptr ? ptr + 1 : name); + BAIL_IF(retval == PHYSFS_ENUM_ERROR, PHYSFS_ERR_APP_CALLBACK, retval); + entry = entry->sibling; + } /* while */ + + return retval; +} /* __PHYSFS_DirTreeEnumerate */ + + +void __PHYSFS_DirTreeDeinit(__PHYSFS_DirTree *dt) +{ + if (!dt) + return; + + if (dt->root) + { + assert(dt->root->sibling == NULL); + assert(dt->hash || (dt->root->children == NULL)); + allocator.Free(dt->root); + } /* if */ + + if (dt->hash) + { + size_t i; + for (i = 0; i < dt->hashBuckets; i++) + { + __PHYSFS_DirTreeEntry *entry; + __PHYSFS_DirTreeEntry *next; + for (entry = dt->hash[i]; entry; entry = next) + { + next = entry->hashnext; + allocator.Free(entry); + } /* for */ + } /* for */ + allocator.Free(dt->hash); + } /* if */ +} /* __PHYSFS_DirTreeDeinit */ + +/* end of physfs.c ... */ + diff --git a/Source/3rdParty/physfs/physfs.h b/Source/3rdParty/physfs/physfs.h new file mode 100644 index 0000000..9169607 --- /dev/null +++ b/Source/3rdParty/physfs/physfs.h @@ -0,0 +1,3854 @@ +/** + * \file physfs.h + * + * Main header file for PhysicsFS. + */ + +/** + * \mainpage PhysicsFS + * + * The latest version of PhysicsFS can be found at: + * https://icculus.org/physfs/ + * + * PhysicsFS; a portable, flexible file i/o abstraction. + * + * This API gives you access to a system file system in ways superior to the + * stdio or system i/o calls. The brief benefits: + * + * - It's portable. + * - It's safe. No file access is permitted outside the specified dirs. + * - It's flexible. Archives (.ZIP files) can be used transparently as + * directory structures. + * + * With PhysicsFS, you have a single writing directory and multiple + * directories (the "search path") for reading. You can think of this as a + * filesystem within a filesystem. If (on Windows) you were to set the + * writing directory to "C:\MyGame\MyWritingDirectory", then no PHYSFS calls + * could touch anything above this directory, including the "C:\MyGame" and + * "C:\" directories. This prevents an application's internal scripting + * language from piddling over c:\\config.sys, for example. If you'd rather + * give PHYSFS full access to the system's REAL file system, set the writing + * dir to "C:\", but that's generally A Bad Thing for several reasons. + * + * Drive letters are hidden in PhysicsFS once you set up your initial paths. + * The search path creates a single, hierarchical directory structure. + * Not only does this lend itself well to general abstraction with archives, + * it also gives better support to operating systems like MacOS and Unix. + * Generally speaking, you shouldn't ever hardcode a drive letter; not only + * does this hurt portability to non-Microsoft OSes, but it limits your win32 + * users to a single drive, too. Use the PhysicsFS abstraction functions and + * allow user-defined configuration options, too. When opening a file, you + * specify it like it was on a Unix filesystem: if you want to write to + * "C:\MyGame\MyConfigFiles\game.cfg", then you might set the write dir to + * "C:\MyGame" and then open "MyConfigFiles/game.cfg". This gives an + * abstraction across all platforms. Specifying a file in this way is termed + * "platform-independent notation" in this documentation. Specifying a + * a filename in a form such as "C:\mydir\myfile" or + * "MacOS hard drive:My Directory:My File" is termed "platform-dependent + * notation". The only time you use platform-dependent notation is when + * setting up your write directory and search path; after that, all file + * access into those directories are done with platform-independent notation. + * + * All files opened for writing are opened in relation to the write directory, + * which is the root of the writable filesystem. When opening a file for + * reading, PhysicsFS goes through the search path. This is NOT the + * same thing as the PATH environment variable. An application using + * PhysicsFS specifies directories to be searched which may be actual + * directories, or archive files that contain files and subdirectories of + * their own. See the end of these docs for currently supported archive + * formats. + * + * Once the search path is defined, you may open files for reading. If you've + * got the following search path defined (to use a win32 example again): + * + * - C:\\mygame + * - C:\\mygame\\myuserfiles + * - D:\\mygamescdromdatafiles + * - C:\\mygame\\installeddatafiles.zip + * + * Then a call to PHYSFS_openRead("textfiles/myfile.txt") (note the directory + * separator, lack of drive letter, and lack of dir separator at the start of + * the string; this is platform-independent notation) will check for + * C:\\mygame\\textfiles\\myfile.txt, then + * C:\\mygame\\myuserfiles\\textfiles\\myfile.txt, then + * D:\\mygamescdromdatafiles\\textfiles\\myfile.txt, then, finally, for + * textfiles\\myfile.txt inside of C:\\mygame\\installeddatafiles.zip. + * Remember that most archive types and platform filesystems store their + * filenames in a case-sensitive manner, so you should be careful to specify + * it correctly. + * + * Files opened through PhysicsFS may NOT contain "." or ".." or ":" as dir + * elements. Not only are these meaningless on MacOS Classic and/or Unix, + * they are a security hole. Also, symbolic links (which can be found in + * some archive types and directly in the filesystem on Unix platforms) are + * NOT followed until you call PHYSFS_permitSymbolicLinks(). That's left to + * your own discretion, as following a symlink can allow for access outside + * the write dir and search paths. For portability, there is no mechanism for + * creating new symlinks in PhysicsFS. + * + * The write dir is not included in the search path unless you specifically + * add it. While you CAN change the write dir as many times as you like, + * you should probably set it once and stick to it. Remember that your + * program will not have permission to write in every directory on Unix and + * NT systems. + * + * All files are opened in binary mode; there is no endline conversion for + * textfiles. Other than that, PhysicsFS has some convenience functions for + * platform-independence. There is a function to tell you the current + * platform's dir separator ("\\" on windows, "/" on Unix, ":" on MacOS), + * which is needed only to set up your search/write paths. There is a + * function to tell you what CD-ROM drives contain accessible discs, and a + * function to recommend a good search path, etc. + * + * A recommended order for the search path is the write dir, then the base dir, + * then the cdrom dir, then any archives discovered. Quake 3 does something + * like this, but moves the archives to the start of the search path. Build + * Engine games, like Duke Nukem 3D and Blood, place the archives last, and + * use the base dir for both searching and writing. There is a helper + * function (PHYSFS_setSaneConfig()) that puts together a basic configuration + * for you, based on a few parameters. Also see the comments on + * PHYSFS_getBaseDir(), and PHYSFS_getPrefDir() for info on what those + * are and how they can help you determine an optimal search path. + * + * PhysicsFS 2.0 adds the concept of "mounting" archives to arbitrary points + * in the search path. If a zipfile contains "maps/level.map" and you mount + * that archive at "mods/mymod", then you would have to open + * "mods/mymod/maps/level.map" to access the file, even though "mods/mymod" + * isn't actually specified in the .zip file. Unlike the Unix mentality of + * mounting a filesystem, "mods/mymod" doesn't actually have to exist when + * mounting the zipfile. It's a "virtual" directory. The mounting mechanism + * allows the developer to seperate archives in the tree and avoid trampling + * over files when added new archives, such as including mod support in a + * game...keeping external content on a tight leash in this manner can be of + * utmost importance to some applications. + * + * PhysicsFS is mostly thread safe. The errors returned by + * PHYSFS_getLastErrorCode() are unique by thread, and library-state-setting + * functions are mutex'd. For efficiency, individual file accesses are + * not locked, so you can not safely read/write/seek/close/etc the same + * file from two threads at the same time. Other race conditions are bugs + * that should be reported/patched. + * + * While you CAN use stdio/syscall file access in a program that has PHYSFS_* + * calls, doing so is not recommended, and you can not directly use system + * filehandles with PhysicsFS and vice versa (but as of PhysicsFS 2.1, you + * can wrap them in a PHYSFS_Io interface yourself if you wanted to). + * + * Note that archives need not be named as such: if you have a ZIP file and + * rename it with a .PKG extension, the file will still be recognized as a + * ZIP archive by PhysicsFS; the file's contents are used to determine its + * type where possible. + * + * Currently supported archive types: + * - .ZIP (pkZip/WinZip/Info-ZIP compatible) + * - .7Z (7zip archives) + * - .ISO (ISO9660 files, CD-ROM images) + * - .GRP (Build Engine groupfile archives) + * - .PAK (Quake I/II archive format) + * - .HOG (Descent I/II HOG file archives) + * - .MVL (Descent II movielib archives) + * - .WAD (DOOM engine archives) + * - .VDF (Gothic I/II engine archives) + * - .SLB (Independence War archives) + * + * String policy for PhysicsFS 2.0 and later: + * + * PhysicsFS 1.0 could only deal with null-terminated ASCII strings. All high + * ASCII chars resulted in undefined behaviour, and there was no Unicode + * support at all. PhysicsFS 2.0 supports Unicode without breaking binary + * compatibility with the 1.0 API by using UTF-8 encoding of all strings + * passed in and out of the library. + * + * All strings passed through PhysicsFS are in null-terminated UTF-8 format. + * This means that if all you care about is English (ASCII characters <= 127) + * then you just use regular C strings. If you care about Unicode (and you + * should!) then you need to figure out what your platform wants, needs, and + * offers. If you are on Windows before Win2000 and build with Unicode + * support, your TCHAR strings are two bytes per character (this is called + * "UCS-2 encoding"). Any modern Windows uses UTF-16, which is two bytes + * per character for most characters, but some characters are four. You + * should convert them to UTF-8 before handing them to PhysicsFS with + * PHYSFS_utf8FromUtf16(), which handles both UTF-16 and UCS-2. If you're + * using Unix or Mac OS X, your wchar_t strings are four bytes per character + * ("UCS-4 encoding", sometimes called "UTF-32"). Use PHYSFS_utf8FromUcs4(). + * Mac OS X can give you UTF-8 directly from a CFString or NSString, and many + * Unixes generally give you C strings in UTF-8 format everywhere. If you + * have a single-byte high ASCII charset, like so-many European "codepages" + * you may be out of luck. We'll convert from "Latin1" to UTF-8 only, and + * never back to Latin1. If you're above ASCII 127, all bets are off: move + * to Unicode or use your platform's facilities. Passing a C string with + * high-ASCII data that isn't UTF-8 encoded will NOT do what you expect! + * + * Naturally, there's also PHYSFS_utf8ToUcs2(), PHYSFS_utf8ToUtf16(), and + * PHYSFS_utf8ToUcs4() to get data back into a format you like. Behind the + * scenes, PhysicsFS will use Unicode where possible: the UTF-8 strings on + * Windows will be converted and used with the multibyte Windows APIs, for + * example. + * + * PhysicsFS offers basic encoding conversion support, but not a whole string + * library. Get your stuff into whatever format you can work with. + * + * Most platforms supported by PhysicsFS 2.1 and later fully support Unicode. + * Some older platforms have been dropped (Windows 95, Mac OS 9). Some, like + * OS/2, might be able to convert to a local codepage or will just fail to + * open/create the file. Modern OSes (macOS, Linux, Windows, etc) should all + * be fine. + * + * Many game-specific archivers are seriously unprepared for Unicode (the + * Descent HOG/MVL and Build Engine GRP archivers, for example, only offer a + * DOS 8.3 filename, for example). Nothing can be done for these, but they + * tend to be legacy formats for existing content that was all ASCII (and + * thus, valid UTF-8) anyhow. Other formats, like .ZIP, don't explicitly + * offer Unicode support, but unofficially expect filenames to be UTF-8 + * encoded, and thus Just Work. Most everything does the right thing without + * bothering you, but it's good to be aware of these nuances in case they + * don't. + * + * + * Other stuff: + * + * Please see the file LICENSE.txt in the source's root directory for + * licensing and redistribution rights. + * + * Please see the file CREDITS.txt in the source's "docs" directory for + * a more or less complete list of who's responsible for this. + * + * \author Ryan C. Gordon. + */ + +#ifndef _INCLUDE_PHYSFS_H_ +#define _INCLUDE_PHYSFS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(PHYSFS_DECL) +/* do nothing. */ +#elif (defined _MSC_VER) +#define PHYSFS_DECL __declspec(dllexport) +#elif (defined __SUNPRO_C) +#define PHYSFS_DECL __global +#elif ((__GNUC__ >= 3) && (!__EMX__) && (!sun)) +#define PHYSFS_DECL __attribute__((visibility("default"))) +#else +#define PHYSFS_DECL +#endif + +#if defined(PHYSFS_DEPRECATED) +/* do nothing. */ +#elif (__GNUC__ >= 4) /* technically, this arrived in gcc 3.1, but oh well. */ +#define PHYSFS_DEPRECATED __attribute__((deprecated)) +#else +#define PHYSFS_DEPRECATED +#endif + +#if 0 /* !!! FIXME: look into this later. */ +#if defined(PHYSFS_CALL) +/* do nothing. */ +#elif defined(__WIN32__) && !defined(__GNUC__) +#define PHYSFS_CALL __cdecl +#elif defined(__OS2__) || defined(OS2) /* should work across all compilers. */ +#define PHYSFS_CALL _System +#else +#define PHYSFS_CALL +#endif +#endif + +/** + * \typedef PHYSFS_uint8 + * \brief An unsigned, 8-bit integer type. + */ +typedef unsigned char PHYSFS_uint8; + +/** + * \typedef PHYSFS_sint8 + * \brief A signed, 8-bit integer type. + */ +typedef signed char PHYSFS_sint8; + +/** + * \typedef PHYSFS_uint16 + * \brief An unsigned, 16-bit integer type. + */ +typedef unsigned short PHYSFS_uint16; + +/** + * \typedef PHYSFS_sint16 + * \brief A signed, 16-bit integer type. + */ +typedef signed short PHYSFS_sint16; + +/** + * \typedef PHYSFS_uint32 + * \brief An unsigned, 32-bit integer type. + */ +typedef unsigned int PHYSFS_uint32; + +/** + * \typedef PHYSFS_sint32 + * \brief A signed, 32-bit integer type. + */ +typedef signed int PHYSFS_sint32; + +/** + * \typedef PHYSFS_uint64 + * \brief An unsigned, 64-bit integer type. + * \warning on platforms without any sort of 64-bit datatype, this is + * equivalent to PHYSFS_uint32! + */ + +/** + * \typedef PHYSFS_sint64 + * \brief A signed, 64-bit integer type. + * \warning on platforms without any sort of 64-bit datatype, this is + * equivalent to PHYSFS_sint32! + */ + + +#if (defined PHYSFS_NO_64BIT_SUPPORT) /* oh well. */ +typedef PHYSFS_uint32 PHYSFS_uint64; +typedef PHYSFS_sint32 PHYSFS_sint64; +#elif (defined _MSC_VER) +typedef signed __int64 PHYSFS_sint64; +typedef unsigned __int64 PHYSFS_uint64; +#else +typedef unsigned long long PHYSFS_uint64; +typedef signed long long PHYSFS_sint64; +#endif + + +#ifndef DOXYGEN_SHOULD_IGNORE_THIS +/* Make sure the types really have the right sizes */ +#define PHYSFS_COMPILE_TIME_ASSERT(name, x) \ + typedef int PHYSFS_compile_time_assert_##name[(x) * 2 - 1] + +PHYSFS_COMPILE_TIME_ASSERT(uint8IsOneByte, sizeof(PHYSFS_uint8) == 1); +PHYSFS_COMPILE_TIME_ASSERT(sint8IsOneByte, sizeof(PHYSFS_sint8) == 1); +PHYSFS_COMPILE_TIME_ASSERT(uint16IsTwoBytes, sizeof(PHYSFS_uint16) == 2); +PHYSFS_COMPILE_TIME_ASSERT(sint16IsTwoBytes, sizeof(PHYSFS_sint16) == 2); +PHYSFS_COMPILE_TIME_ASSERT(uint32IsFourBytes, sizeof(PHYSFS_uint32) == 4); +PHYSFS_COMPILE_TIME_ASSERT(sint32IsFourBytes, sizeof(PHYSFS_sint32) == 4); + +#ifndef PHYSFS_NO_64BIT_SUPPORT +PHYSFS_COMPILE_TIME_ASSERT(uint64IsEightBytes, sizeof(PHYSFS_uint64) == 8); +PHYSFS_COMPILE_TIME_ASSERT(sint64IsEightBytes, sizeof(PHYSFS_sint64) == 8); +#endif + +#undef PHYSFS_COMPILE_TIME_ASSERT + +#endif /* DOXYGEN_SHOULD_IGNORE_THIS */ + + +/** + * \struct PHYSFS_File + * \brief A PhysicsFS file handle. + * + * You get a pointer to one of these when you open a file for reading, + * writing, or appending via PhysicsFS. + * + * As you can see from the lack of meaningful fields, you should treat this + * as opaque data. Don't try to manipulate the file handle, just pass the + * pointer you got, unmolested, to various PhysicsFS APIs. + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openWrite + * \sa PHYSFS_openAppend + * \sa PHYSFS_close + * \sa PHYSFS_read + * \sa PHYSFS_write + * \sa PHYSFS_seek + * \sa PHYSFS_tell + * \sa PHYSFS_eof + * \sa PHYSFS_setBuffer + * \sa PHYSFS_flush + */ +typedef struct PHYSFS_File +{ + void *opaque; /**< That's all you get. Don't touch. */ +} PHYSFS_File; + + +/** + * \def PHYSFS_file + * \brief 1.0 API compatibility define. + * + * PHYSFS_file is identical to PHYSFS_File. This #define is here for backwards + * compatibility with the 1.0 API, which had an inconsistent capitalization + * convention in this case. New code should use PHYSFS_File, as this #define + * may go away someday. + * + * \sa PHYSFS_File + */ +#define PHYSFS_file PHYSFS_File + + +/** + * \struct PHYSFS_ArchiveInfo + * \brief Information on various PhysicsFS-supported archives. + * + * This structure gives you details on what sort of archives are supported + * by this implementation of PhysicsFS. Archives tend to be things like + * ZIP files and such. + * + * \warning Not all binaries are created equal! PhysicsFS can be built with + * or without support for various archives. You can check with + * PHYSFS_supportedArchiveTypes() to see if your archive type is + * supported. + * + * \sa PHYSFS_supportedArchiveTypes + * \sa PHYSFS_registerArchiver + * \sa PHYSFS_deregisterArchiver + */ +typedef struct PHYSFS_ArchiveInfo +{ + const char *extension; /**< Archive file extension: "ZIP", for example. */ + const char *description; /**< Human-readable archive description. */ + const char *author; /**< Person who did support for this archive. */ + const char *url; /**< URL related to this archive */ + int supportsSymlinks; /**< non-zero if archive offers symbolic links. */ +} PHYSFS_ArchiveInfo; + + +/** + * \struct PHYSFS_Version + * \brief Information the version of PhysicsFS in use. + * + * Represents the library's version as three levels: major revision + * (increments with massive changes, additions, and enhancements), + * minor revision (increments with backwards-compatible changes to the + * major revision), and patchlevel (increments with fixes to the minor + * revision). + * + * \sa PHYSFS_VERSION + * \sa PHYSFS_getLinkedVersion + */ +typedef struct PHYSFS_Version +{ + PHYSFS_uint8 major; /**< major revision */ + PHYSFS_uint8 minor; /**< minor revision */ + PHYSFS_uint8 patch; /**< patchlevel */ +} PHYSFS_Version; + + +#ifndef DOXYGEN_SHOULD_IGNORE_THIS +#define PHYSFS_VER_MAJOR 3 +#define PHYSFS_VER_MINOR 0 +#define PHYSFS_VER_PATCH 1 +#endif /* DOXYGEN_SHOULD_IGNORE_THIS */ + + +/* PhysicsFS state stuff ... */ + +/** + * \def PHYSFS_VERSION(x) + * \brief Macro to determine PhysicsFS version program was compiled against. + * + * This macro fills in a PHYSFS_Version structure with the version of the + * library you compiled against. This is determined by what header the + * compiler uses. Note that if you dynamically linked the library, you might + * have a slightly newer or older version at runtime. That version can be + * determined with PHYSFS_getLinkedVersion(), which, unlike PHYSFS_VERSION, + * is not a macro. + * + * \param x A pointer to a PHYSFS_Version struct to initialize. + * + * \sa PHYSFS_Version + * \sa PHYSFS_getLinkedVersion + */ +#define PHYSFS_VERSION(x) \ +{ \ + (x)->major = PHYSFS_VER_MAJOR; \ + (x)->minor = PHYSFS_VER_MINOR; \ + (x)->patch = PHYSFS_VER_PATCH; \ +} + + +/** + * \fn void PHYSFS_getLinkedVersion(PHYSFS_Version *ver) + * \brief Get the version of PhysicsFS that is linked against your program. + * + * If you are using a shared library (DLL) version of PhysFS, then it is + * possible that it will be different than the version you compiled against. + * + * This is a real function; the macro PHYSFS_VERSION tells you what version + * of PhysFS you compiled against: + * + * \code + * PHYSFS_Version compiled; + * PHYSFS_Version linked; + * + * PHYSFS_VERSION(&compiled); + * PHYSFS_getLinkedVersion(&linked); + * printf("We compiled against PhysFS version %d.%d.%d ...\n", + * compiled.major, compiled.minor, compiled.patch); + * printf("But we linked against PhysFS version %d.%d.%d.\n", + * linked.major, linked.minor, linked.patch); + * \endcode + * + * This function may be called safely at any time, even before PHYSFS_init(). + * + * \sa PHYSFS_VERSION + */ +PHYSFS_DECL void PHYSFS_getLinkedVersion(PHYSFS_Version *ver); + + +/** + * \fn int PHYSFS_init(const char *argv0) + * \brief Initialize the PhysicsFS library. + * + * This must be called before any other PhysicsFS function. + * + * This should be called prior to any attempts to change your process's + * current working directory. + * + * \param argv0 the argv[0] string passed to your program's mainline. + * This may be NULL on most platforms (such as ones without a + * standard main() function), but you should always try to pass + * something in here. Unix-like systems such as Linux _need_ to + * pass argv[0] from main() in here. + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). + * + * \sa PHYSFS_deinit + * \sa PHYSFS_isInit + */ +PHYSFS_DECL int PHYSFS_init(const char *argv0); + + +/** + * \fn int PHYSFS_deinit(void) + * \brief Deinitialize the PhysicsFS library. + * + * This closes any files opened via PhysicsFS, blanks the search/write paths, + * frees memory, and invalidates all of your file handles. + * + * Note that this call can FAIL if there's a file open for writing that + * refuses to close (for example, the underlying operating system was + * buffering writes to network filesystem, and the fileserver has crashed, + * or a hard drive has failed, etc). It is usually best to close all write + * handles yourself before calling this function, so that you can gracefully + * handle a specific failure. + * + * Once successfully deinitialized, PHYSFS_init() can be called again to + * restart the subsystem. All default API states are restored at this + * point, with the exception of any custom allocator you might have + * specified, which survives between initializations. + * + * \return nonzero on success, zero on error. Specifics of the error can be + * gleaned from PHYSFS_getLastError(). If failure, state of PhysFS is + * undefined, and probably badly screwed up. + * + * \sa PHYSFS_init + * \sa PHYSFS_isInit + */ +PHYSFS_DECL int PHYSFS_deinit(void); + + +/** + * \fn const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void) + * \brief Get a list of supported archive types. + * + * Get a list of archive types supported by this implementation of PhysicFS. + * These are the file formats usable for search path entries. This is for + * informational purposes only. Note that the extension listed is merely + * convention: if we list "ZIP", you can open a PkZip-compatible archive + * with an extension of "XYZ", if you like. + * + * The returned value is an array of pointers to PHYSFS_ArchiveInfo structures, + * with a NULL entry to signify the end of the list: + * + * \code + * PHYSFS_ArchiveInfo **i; + * + * for (i = PHYSFS_supportedArchiveTypes(); *i != NULL; i++) + * { + * printf("Supported archive: [%s], which is [%s].\n", + * (*i)->extension, (*i)->description); + * } + * \endcode + * + * The return values are pointers to internal memory, and should + * be considered READ ONLY, and never freed. The returned values are + * valid until the next call to PHYSFS_deinit(), PHYSFS_registerArchiver(), + * or PHYSFS_deregisterArchiver(). + * + * \return READ ONLY Null-terminated array of READ ONLY structures. + * + * \sa PHYSFS_registerArchiver + * \sa PHYSFS_deregisterArchiver + */ +PHYSFS_DECL const PHYSFS_ArchiveInfo **PHYSFS_supportedArchiveTypes(void); + + +/** + * \fn void PHYSFS_freeList(void *listVar) + * \brief Deallocate resources of lists returned by PhysicsFS. + * + * Certain PhysicsFS functions return lists of information that are + * dynamically allocated. Use this function to free those resources. + * + * It is safe to pass a NULL here, but doing so will cause a crash in versions + * before PhysicsFS 2.1.0. + * + * \param listVar List of information specified as freeable by this function. + * Passing NULL is safe; it is a valid no-op. + * + * \sa PHYSFS_getCdRomDirs + * \sa PHYSFS_enumerateFiles + * \sa PHYSFS_getSearchPath + */ +PHYSFS_DECL void PHYSFS_freeList(void *listVar); + + +/** + * \fn const char *PHYSFS_getLastError(void) + * \brief Get human-readable error information. + * + * \deprecated Use PHYSFS_getLastErrorCode() and PHYSFS_getErrorByCode() instead. + * + * \warning As of PhysicsFS 2.1, this function has been nerfed. + * Before PhysicsFS 2.1, this function was the only way to get + * error details beyond a given function's basic return value. + * This was meant to be a human-readable string in one of several + * languages, and was not useful for application parsing. This was + * a problem, because the developer and not the user chose the + * language at compile time, and the PhysicsFS maintainers had + * to (poorly) maintain a significant amount of localization work. + * The app couldn't parse the strings, even if they counted on a + * specific language, since some were dynamically generated. + * In 2.1 and later, this always returns a static string in + * English; you may use it as a key string for your own + * localizations if you like, as we'll promise not to change + * existing error strings. Also, if your application wants to + * look at specific errors, we now offer a better option: + * use PHYSFS_getLastErrorCode() instead. + * + * Get the last PhysicsFS error message as a human-readable, null-terminated + * string. This will return NULL if there's been no error since the last call + * to this function. The pointer returned by this call points to an internal + * buffer. Each thread has a unique error state associated with it, but each + * time a new error message is set, it will overwrite the previous one + * associated with that thread. It is safe to call this function at anytime, + * even before PHYSFS_init(). + * + * PHYSFS_getLastError() and PHYSFS_getLastErrorCode() both reset the same + * thread-specific error state. Calling one will wipe out the other's + * data. If you need both, call PHYSFS_getLastErrorCode(), then pass that + * value to PHYSFS_getErrorByCode(). + * + * As of PhysicsFS 2.1, this function only presents text in the English + * language, but the strings are static, so you can use them as keys into + * your own localization dictionary. These strings are meant to be passed on + * directly to the user. + * + * Generally, applications should only concern themselves with whether a + * given function failed; however, if your code require more specifics, you + * should use PHYSFS_getLastErrorCode() instead of this function. + * + * \return READ ONLY string of last error message. + * + * \sa PHYSFS_getLastErrorCode + * \sa PHYSFS_getErrorByCode + */ +PHYSFS_DECL const char *PHYSFS_getLastError(void) PHYSFS_DEPRECATED; + + +/** + * \fn const char *PHYSFS_getDirSeparator(void) + * \brief Get platform-dependent dir separator string. + * + * This returns "\\" on win32, "/" on Unix, and ":" on MacOS. It may be more + * than one character, depending on the platform, and your code should take + * that into account. Note that this is only useful for setting up the + * search/write paths, since access into those dirs always use '/' + * (platform-independent notation) to separate directories. This is also + * handy for getting platform-independent access when using stdio calls. + * + * \return READ ONLY null-terminated string of platform's dir separator. + */ +PHYSFS_DECL const char *PHYSFS_getDirSeparator(void); + + +/** + * \fn void PHYSFS_permitSymbolicLinks(int allow) + * \brief Enable or disable following of symbolic links. + * + * Some physical filesystems and archives contain files that are just pointers + * to other files. On the physical filesystem, opening such a link will + * (transparently) open the file that is pointed to. + * + * By default, PhysicsFS will check if a file is really a symlink during open + * calls and fail if it is. Otherwise, the link could take you outside the + * write and search paths, and compromise security. + * + * If you want to take that risk, call this function with a non-zero parameter. + * Note that this is more for sandboxing a program's scripting language, in + * case untrusted scripts try to compromise the system. Generally speaking, + * a user could very well have a legitimate reason to set up a symlink, so + * unless you feel there's a specific danger in allowing them, you should + * permit them. + * + * Symlinks are only explicitly checked when dealing with filenames + * in platform-independent notation. That is, when setting up your + * search and write paths, etc, symlinks are never checked for. + * + * Please note that PHYSFS_stat() will always check the path specified; if + * that path is a symlink, it will not be followed in any case. If symlinks + * aren't permitted through this function, PHYSFS_stat() ignores them, and + * would treat the query as if the path didn't exist at all. + * + * Symbolic link permission can be enabled or disabled at any time after + * you've called PHYSFS_init(), and is disabled by default. + * + * \param allow nonzero to permit symlinks, zero to deny linking. + * + * \sa PHYSFS_symbolicLinksPermitted + */ +PHYSFS_DECL void PHYSFS_permitSymbolicLinks(int allow); + + +/** + * \fn char **PHYSFS_getCdRomDirs(void) + * \brief Get an array of paths to available CD-ROM drives. + * + * The dirs returned are platform-dependent ("D:\" on Win32, "/cdrom" or + * whatnot on Unix). Dirs are only returned if there is a disc ready and + * accessible in the drive. So if you've got two drives (D: and E:), and only + * E: has a disc in it, then that's all you get. If the user inserts a disc + * in D: and you call this function again, you get both drives. If, on a + * Unix box, the user unmounts a disc and remounts it elsewhere, the next + * call to this function will reflect that change. + * + * This function refers to "CD-ROM" media, but it really means "inserted disc + * media," such as DVD-ROM, HD-DVD, CDRW, and Blu-Ray discs. It looks for + * filesystems, and as such won't report an audio CD, unless there's a + * mounted filesystem track on it. + * + * The returned value is an array of strings, with a NULL entry to signify the + * end of the list: + * + * \code + * char **cds = PHYSFS_getCdRomDirs(); + * char **i; + * + * for (i = cds; *i != NULL; i++) + * printf("cdrom dir [%s] is available.\n", *i); + * + * PHYSFS_freeList(cds); + * \endcode + * + * This call may block while drives spin up. Be forewarned. + * + * When you are done with the returned information, you may dispose of the + * resources by calling PHYSFS_freeList() with the returned pointer. + * + * \return Null-terminated array of null-terminated strings. + * + * \sa PHYSFS_getCdRomDirsCallback + */ +PHYSFS_DECL char **PHYSFS_getCdRomDirs(void); + + +/** + * \fn const char *PHYSFS_getBaseDir(void) + * \brief Get the path where the application resides. + * + * Helper function. + * + * Get the "base dir". This is the directory where the application was run + * from, which is probably the installation directory, and may or may not + * be the process's current working directory. + * + * You should probably use the base dir in your search path. + * + * \return READ ONLY string of base dir in platform-dependent notation. + * + * \sa PHYSFS_getPrefDir + */ +PHYSFS_DECL const char *PHYSFS_getBaseDir(void); + + +/** + * \fn const char *PHYSFS_getUserDir(void) + * \brief Get the path where user's home directory resides. + * + * \deprecated As of PhysicsFS 2.1, you probably want PHYSFS_getPrefDir(). + * + * Helper function. + * + * Get the "user dir". This is meant to be a suggestion of where a specific + * user of the system can store files. On Unix, this is her home directory. + * On systems with no concept of multiple home directories (MacOS, win95), + * this will default to something like "C:\mybasedir\users\username" + * where "username" will either be the login name, or "default" if the + * platform doesn't support multiple users, either. + * + * \return READ ONLY string of user dir in platform-dependent notation. + * + * \sa PHYSFS_getBaseDir + * \sa PHYSFS_getPrefDir + */ +PHYSFS_DECL const char *PHYSFS_getUserDir(void) PHYSFS_DEPRECATED; + + +/** + * \fn const char *PHYSFS_getWriteDir(void) + * \brief Get path where PhysicsFS will allow file writing. + * + * Get the current write dir. The default write dir is NULL. + * + * \return READ ONLY string of write dir in platform-dependent notation, + * OR NULL IF NO WRITE PATH IS CURRENTLY SET. + * + * \sa PHYSFS_setWriteDir + */ +PHYSFS_DECL const char *PHYSFS_getWriteDir(void); + + +/** + * \fn int PHYSFS_setWriteDir(const char *newDir) + * \brief Tell PhysicsFS where it may write files. + * + * Set a new write dir. This will override the previous setting. + * + * This call will fail (and fail to change the write dir) if the current + * write dir still has files open in it. + * + * \param newDir The new directory to be the root of the write dir, + * specified in platform-dependent notation. Setting to NULL + * disables the write dir, so no files can be opened for + * writing via PhysicsFS. + * \return non-zero on success, zero on failure. All attempts to open a file + * for writing via PhysicsFS will fail until this call succeeds. + * Use PHYSFS_getLastErrorCode() to obtain the specific error. + * + * \sa PHYSFS_getWriteDir + */ +PHYSFS_DECL int PHYSFS_setWriteDir(const char *newDir); + + +/** + * \fn int PHYSFS_addToSearchPath(const char *newDir, int appendToPath) + * \brief Add an archive or directory to the search path. + * + * \deprecated As of PhysicsFS 2.0, use PHYSFS_mount() instead. This + * function just wraps it anyhow. + * + * This function is equivalent to: + * + * \code + * PHYSFS_mount(newDir, NULL, appendToPath); + * \endcode + * + * You must use this and not PHYSFS_mount if binary compatibility with + * PhysicsFS 1.0 is important (which it may not be for many people). + * + * \sa PHYSFS_mount + * \sa PHYSFS_removeFromSearchPath + * \sa PHYSFS_getSearchPath + */ +PHYSFS_DECL int PHYSFS_addToSearchPath(const char *newDir, int appendToPath) + PHYSFS_DEPRECATED; + +/** + * \fn int PHYSFS_removeFromSearchPath(const char *oldDir) + * \brief Remove a directory or archive from the search path. + * + * \deprecated As of PhysicsFS 2.1, use PHYSFS_unmount() instead. This + * function just wraps it anyhow. There's no functional difference + * except the vocabulary changed from "adding to the search path" + * to "mounting" when that functionality was extended, and thus + * the preferred way to accomplish this function's work is now + * called "unmounting." + * + * This function is equivalent to: + * + * \code + * PHYSFS_unmount(oldDir); + * \endcode + * + * You must use this and not PHYSFS_unmount if binary compatibility with + * PhysicsFS 1.0 is important (which it may not be for many people). + * + * \sa PHYSFS_addToSearchPath + * \sa PHYSFS_getSearchPath + * \sa PHYSFS_unmount + */ +PHYSFS_DECL int PHYSFS_removeFromSearchPath(const char *oldDir) + PHYSFS_DEPRECATED; + + +/** + * \fn char **PHYSFS_getSearchPath(void) + * \brief Get the current search path. + * + * The default search path is an empty list. + * + * The returned value is an array of strings, with a NULL entry to signify the + * end of the list: + * + * \code + * char **i; + * + * for (i = PHYSFS_getSearchPath(); *i != NULL; i++) + * printf("[%s] is in the search path.\n", *i); + * \endcode + * + * When you are done with the returned information, you may dispose of the + * resources by calling PHYSFS_freeList() with the returned pointer. + * + * \return Null-terminated array of null-terminated strings. NULL if there + * was a problem (read: OUT OF MEMORY). + * + * \sa PHYSFS_getSearchPathCallback + * \sa PHYSFS_addToSearchPath + * \sa PHYSFS_removeFromSearchPath + */ +PHYSFS_DECL char **PHYSFS_getSearchPath(void); + + +/** + * \fn int PHYSFS_setSaneConfig(const char *organization, const char *appName, const char *archiveExt, int includeCdRoms, int archivesFirst) + * \brief Set up sane, default paths. + * + * Helper function. + * + * The write dir will be set to the pref dir returned by + * \code PHYSFS_getPrefDir(organization, appName) \endcode, which is + * created if it doesn't exist. + * + * The above is sufficient to make sure your program's configuration directory + * is separated from other clutter, and platform-independent. + * + * The search path will be: + * + * - The Write Dir (created if it doesn't exist) + * - The Base Dir (PHYSFS_getBaseDir()) + * - All found CD-ROM dirs (optionally) + * + * These directories are then searched for files ending with the extension + * (archiveExt), which, if they are valid and supported archives, will also + * be added to the search path. If you specified "PKG" for (archiveExt), and + * there's a file named data.PKG in the base dir, it'll be checked. Archives + * can either be appended or prepended to the search path in alphabetical + * order, regardless of which directories they were found in. All archives + * are mounted in the root of the virtual file system ("/"). + * + * All of this can be accomplished from the application, but this just does it + * all for you. Feel free to add more to the search path manually, too. + * + * \param organization Name of your company/group/etc to be used as a + * dirname, so keep it small, and no-frills. + * + * \param appName Program-specific name of your program, to separate it + * from other programs using PhysicsFS. + * + * \param archiveExt File extension used by your program to specify an + * archive. For example, Quake 3 uses "pk3", even though + * they are just zipfiles. Specify NULL to not dig out + * archives automatically. Do not specify the '.' char; + * If you want to look for ZIP files, specify "ZIP" and + * not ".ZIP" ... the archive search is case-insensitive. + * + * \param includeCdRoms Non-zero to include CD-ROMs in the search path, and + * (if (archiveExt) != NULL) search them for archives. + * This may cause a significant amount of blocking + * while discs are accessed, and if there are no discs + * in the drive (or even not mounted on Unix systems), + * then they may not be made available anyhow. You may + * want to specify zero and handle the disc setup + * yourself. + * + * \param archivesFirst Non-zero to prepend the archives to the search path. + * Zero to append them. Ignored if !(archiveExt). + * + * \return nonzero on success, zero on error. Use PHYSFS_getLastErrorCode() + * to obtain the specific error. + */ +PHYSFS_DECL int PHYSFS_setSaneConfig(const char *organization, + const char *appName, + const char *archiveExt, + int includeCdRoms, + int archivesFirst); + + +/* Directory management stuff ... */ + +/** + * \fn int PHYSFS_mkdir(const char *dirName) + * \brief Create a directory. + * + * This is specified in platform-independent notation in relation to the + * write dir. All missing parent directories are also created if they + * don't exist. + * + * So if you've got the write dir set to "C:\mygame\writedir" and call + * PHYSFS_mkdir("downloads/maps") then the directories + * "C:\mygame\writedir\downloads" and "C:\mygame\writedir\downloads\maps" + * will be created if possible. If the creation of "maps" fails after we + * have successfully created "downloads", then the function leaves the + * created directory behind and reports failure. + * + * \param dirName New dir to create. + * \return nonzero on success, zero on error. Use + * PHYSFS_getLastErrorCode() to obtain the specific error. + * + * \sa PHYSFS_delete + */ +PHYSFS_DECL int PHYSFS_mkdir(const char *dirName); + + +/** + * \fn int PHYSFS_delete(const char *filename) + * \brief Delete a file or directory. + * + * (filename) is specified in platform-independent notation in relation to the + * write dir. + * + * A directory must be empty before this call can delete it. + * + * Deleting a symlink will remove the link, not what it points to, regardless + * of whether you "permitSymLinks" or not. + * + * So if you've got the write dir set to "C:\mygame\writedir" and call + * PHYSFS_delete("downloads/maps/level1.map") then the file + * "C:\mygame\writedir\downloads\maps\level1.map" is removed from the + * physical filesystem, if it exists and the operating system permits the + * deletion. + * + * Note that on Unix systems, deleting a file may be successful, but the + * actual file won't be removed until all processes that have an open + * filehandle to it (including your program) close their handles. + * + * Chances are, the bits that make up the file still exist, they are just + * made available to be written over at a later point. Don't consider this + * a security method or anything. :) + * + * \param filename Filename to delete. + * \return nonzero on success, zero on error. Use PHYSFS_getLastErrorCode() + * to obtain the specific error. + */ +PHYSFS_DECL int PHYSFS_delete(const char *filename); + + +/** + * \fn const char *PHYSFS_getRealDir(const char *filename) + * \brief Figure out where in the search path a file resides. + * + * The file is specified in platform-independent notation. The returned + * filename will be the element of the search path where the file was found, + * which may be a directory, or an archive. Even if there are multiple + * matches in different parts of the search path, only the first one found + * is used, just like when opening a file. + * + * So, if you look for "maps/level1.map", and C:\\mygame is in your search + * path and C:\\mygame\\maps\\level1.map exists, then "C:\mygame" is returned. + * + * If a any part of a match is a symbolic link, and you've not explicitly + * permitted symlinks, then it will be ignored, and the search for a match + * will continue. + * + * If you specify a fake directory that only exists as a mount point, it'll + * be associated with the first archive mounted there, even though that + * directory isn't necessarily contained in a real archive. + * + * \warning This will return NULL if there is no real directory associated + * with (filename). Specifically, PHYSFS_mountIo(), + * PHYSFS_mountMemory(), and PHYSFS_mountHandle() will return NULL + * even if the filename is found in the search path. Plan accordingly. + * + * \param filename file to look for. + * \return READ ONLY string of element of search path containing the + * the file in question. NULL if not found. + */ +PHYSFS_DECL const char *PHYSFS_getRealDir(const char *filename); + + +/** + * \fn char **PHYSFS_enumerateFiles(const char *dir) + * \brief Get a file listing of a search path's directory. + * + * \warning In PhysicsFS versions prior to 2.1, this function would return + * as many items as it could in the face of a failure condition + * (out of memory, disk i/o error, etc). Since this meant apps + * couldn't distinguish between complete success and partial failure, + * and since the function could always return NULL to report + * catastrophic failures anyway, in PhysicsFS 2.1 this function's + * policy changed: it will either return a list of complete results + * or it will return NULL for any failure of any kind, so we can + * guarantee that the enumeration ran to completion and has no gaps + * in its results. + * + * Matching directories are interpolated. That is, if "C:\mydir" is in the + * search path and contains a directory "savegames" that contains "x.sav", + * "y.sav", and "z.sav", and there is also a "C:\userdir" in the search path + * that has a "savegames" subdirectory with "w.sav", then the following code: + * + * \code + * char **rc = PHYSFS_enumerateFiles("savegames"); + * char **i; + * + * for (i = rc; *i != NULL; i++) + * printf(" * We've got [%s].\n", *i); + * + * PHYSFS_freeList(rc); + * \endcode + * + * \...will print: + * + * \verbatim + * We've got [x.sav]. + * We've got [y.sav]. + * We've got [z.sav]. + * We've got [w.sav].\endverbatim + * + * Feel free to sort the list however you like. However, the returned data + * will always contain no duplicates, and will be always sorted in alphabetic + * (rather: case-sensitive Unicode) order for you. + * + * Don't forget to call PHYSFS_freeList() with the return value from this + * function when you are done with it. + * + * \param dir directory in platform-independent notation to enumerate. + * \return Null-terminated array of null-terminated strings, or NULL for + * failure cases. + * + * \sa PHYSFS_enumerate + */ +PHYSFS_DECL char **PHYSFS_enumerateFiles(const char *dir); + + +/** + * \fn int PHYSFS_exists(const char *fname) + * \brief Determine if a file exists in the search path. + * + * Reports true if there is an entry anywhere in the search path by the + * name of (fname). + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, so you + * might end up further down in the search path than expected. + * + * \param fname filename in platform-independent notation. + * \return non-zero if filename exists. zero otherwise. + */ +PHYSFS_DECL int PHYSFS_exists(const char *fname); + + +/** + * \fn int PHYSFS_isDirectory(const char *fname) + * \brief Determine if a file in the search path is really a directory. + * + * \deprecated As of PhysicsFS 2.1, use PHYSFS_stat() instead. This + * function just wraps it anyhow. + * + * Determine if the first occurence of (fname) in the search path is + * really a directory entry. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, so you + * might end up further down in the search path than expected. + * + * \param fname filename in platform-independent notation. + * \return non-zero if filename exists and is a directory. zero otherwise. + * + * \sa PHYSFS_stat + * \sa PHYSFS_exists + */ +PHYSFS_DECL int PHYSFS_isDirectory(const char *fname) PHYSFS_DEPRECATED; + + +/** + * \fn int PHYSFS_isSymbolicLink(const char *fname) + * \brief Determine if a file in the search path is really a symbolic link. + * + * \deprecated As of PhysicsFS 2.1, use PHYSFS_stat() instead. This + * function just wraps it anyhow. + * + * Determine if the first occurence of (fname) in the search path is + * really a symbolic link. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and as such, + * this function will always return 0 in that case. + * + * \param fname filename in platform-independent notation. + * \return non-zero if filename exists and is a symlink. zero otherwise. + * + * \sa PHYSFS_stat + * \sa PHYSFS_exists + */ +PHYSFS_DECL int PHYSFS_isSymbolicLink(const char *fname) PHYSFS_DEPRECATED; + + +/** + * \fn PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename) + * \brief Get the last modification time of a file. + * + * \deprecated As of PhysicsFS 2.1, use PHYSFS_stat() instead. This + * function just wraps it anyhow. + * + * The modtime is returned as a number of seconds since the Unix epoch + * (midnight, Jan 1, 1970). The exact derivation and accuracy of this time + * depends on the particular archiver. If there is no reasonable way to + * obtain this information for a particular archiver, or there was some sort + * of error, this function returns (-1). + * + * You must use this and not PHYSFS_stat() if binary compatibility with + * PhysicsFS 2.0 is important (which it may not be for many people). + * + * \param filename filename to check, in platform-independent notation. + * \return last modified time of the file. -1 if it can't be determined. + * + * \sa PHYSFS_stat + */ +PHYSFS_DECL PHYSFS_sint64 PHYSFS_getLastModTime(const char *filename) + PHYSFS_DEPRECATED; + + +/* i/o stuff... */ + +/** + * \fn PHYSFS_File *PHYSFS_openWrite(const char *filename) + * \brief Open a file for writing. + * + * Open a file for writing, in platform-independent notation and in relation + * to the write dir as the root of the writable filesystem. The specified + * file is created if it doesn't exist. If it does exist, it is truncated to + * zero bytes, and the writing offset is set to the start. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * + * \param filename File to open. + * \return A valid PhysicsFS filehandle on success, NULL on error. Use + * PHYSFS_getLastErrorCode() to obtain the specific error. + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openAppend + * \sa PHYSFS_write + * \sa PHYSFS_close + */ +PHYSFS_DECL PHYSFS_File *PHYSFS_openWrite(const char *filename); + + +/** + * \fn PHYSFS_File *PHYSFS_openAppend(const char *filename) + * \brief Open a file for appending. + * + * Open a file for writing, in platform-independent notation and in relation + * to the write dir as the root of the writable filesystem. The specified + * file is created if it doesn't exist. If it does exist, the writing offset + * is set to the end of the file, so the first write will be the byte after + * the end. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * + * \param filename File to open. + * \return A valid PhysicsFS filehandle on success, NULL on error. Use + * PHYSFS_getLastErrorCode() to obtain the specific error. + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openWrite + * \sa PHYSFS_write + * \sa PHYSFS_close + */ +PHYSFS_DECL PHYSFS_File *PHYSFS_openAppend(const char *filename); + + +/** + * \fn PHYSFS_File *PHYSFS_openRead(const char *filename) + * \brief Open a file for reading. + * + * Open a file for reading, in platform-independent notation. The search path + * is checked one at a time until a matching file is found, in which case an + * abstract filehandle is associated with it, and reading may be done. + * The reading offset is set to the first byte of the file. + * + * Note that entries that are symlinks are ignored if + * PHYSFS_permitSymbolicLinks(1) hasn't been called, and opening a + * symlink with this function will fail in such a case. + * + * \param filename File to open. + * \return A valid PhysicsFS filehandle on success, NULL on error. + * Use PHYSFS_getLastErrorCode() to obtain the specific error. + * + * \sa PHYSFS_openWrite + * \sa PHYSFS_openAppend + * \sa PHYSFS_read + * \sa PHYSFS_close + */ +PHYSFS_DECL PHYSFS_File *PHYSFS_openRead(const char *filename); + + +/** + * \fn int PHYSFS_close(PHYSFS_File *handle) + * \brief Close a PhysicsFS filehandle. + * + * This call is capable of failing if the operating system was buffering + * writes to the physical media, and, now forced to write those changes to + * physical media, can not store the data for some reason. In such a case, + * the filehandle stays open. A well-written program should ALWAYS check the + * return value from the close call in addition to every writing call! + * + * \param handle handle returned from PHYSFS_open*(). + * \return nonzero on success, zero on error. Use PHYSFS_getLastErrorCode() + * to obtain the specific error. + * + * \sa PHYSFS_openRead + * \sa PHYSFS_openWrite + * \sa PHYSFS_openAppend + */ +PHYSFS_DECL int PHYSFS_close(PHYSFS_File *handle); + + +/** + * \fn PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) + * \brief Read data from a PhysicsFS filehandle + * + * The file must be opened for reading. + * + * \deprecated As of PhysicsFS 2.1, use PHYSFS_readBytes() instead. This + * function just wraps it anyhow. This function never clarified + * what would happen if you managed to read a partial object, so + * working at the byte level makes this cleaner for everyone, + * especially now that PHYSFS_Io interfaces can be supplied by the + * application. + * + * \param handle handle returned from PHYSFS_openRead(). + * \param buffer buffer to store read data into. + * \param objSize size in bytes of objects being read from (handle). + * \param objCount number of (objSize) objects to read from (handle). + * \return number of objects read. PHYSFS_getLastErrorCode() can shed light + * on the reason this might be < (objCount), as can PHYSFS_eof(). + * -1 if complete failure. + * + * \sa PHYSFS_readBytes + * \sa PHYSFS_eof + */ +PHYSFS_DECL PHYSFS_sint64 PHYSFS_read(PHYSFS_File *handle, + void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount) + PHYSFS_DEPRECATED; + +/** + * \fn PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, const void *buffer, PHYSFS_uint32 objSize, PHYSFS_uint32 objCount) + * \brief Write data to a PhysicsFS filehandle + * + * The file must be opened for writing. + * + * \deprecated As of PhysicsFS 2.1, use PHYSFS_writeBytes() instead. This + * function just wraps it anyhow. This function never clarified + * what would happen if you managed to write a partial object, so + * working at the byte level makes this cleaner for everyone, + * especially now that PHYSFS_Io interfaces can be supplied by the + * application. + * + * \param handle retval from PHYSFS_openWrite() or PHYSFS_openAppend(). + * \param buffer buffer of bytes to write to (handle). + * \param objSize size in bytes of objects being written to (handle). + * \param objCount number of (objSize) objects to write to (handle). + * \return number of objects written. PHYSFS_getLastErrorCode() can shed + * light on the reason this might be < (objCount). -1 if complete + * failure. + * + * \sa PHYSFS_writeBytes + */ +PHYSFS_DECL PHYSFS_sint64 PHYSFS_write(PHYSFS_File *handle, + const void *buffer, + PHYSFS_uint32 objSize, + PHYSFS_uint32 objCount) + PHYSFS_DEPRECATED; + + +/* File position stuff... */ + +/** + * \fn int PHYSFS_eof(PHYSFS_File *handle) + * \brief Check for end-of-file state on a PhysicsFS filehandle. + * + * Determine if the end of file has been reached in a PhysicsFS filehandle. + * + * \param handle handle returned from PHYSFS_openRead(). + * \return nonzero if EOF, zero if not. + * + * \sa PHYSFS_read + * \sa PHYSFS_tell + */ +PHYSFS_DECL int PHYSFS_eof(PHYSFS_File *handle); + + +/** + * \fn PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle) + * \brief Determine current position within a PhysicsFS filehandle. + * + * \param handle handle returned from PHYSFS_open*(). + * \return offset in bytes from start of file. -1 if error occurred. + * Use PHYSFS_getLastErrorCode() to obtain the specific error. + * + * \sa PHYSFS_seek + */ +PHYSFS_DECL PHYSFS_sint64 PHYSFS_tell(PHYSFS_File *handle); + + +/** + * \fn int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos) + * \brief Seek to a new position within a PhysicsFS filehandle. + * + * The next read or write will occur at that place. Seeking past the + * beginning or end of the file is not allowed, and causes an error. + * + * \param handle handle returned from PHYSFS_open*(). + * \param pos number of bytes from start of file to seek to. + * \return nonzero on success, zero on error. Use PHYSFS_getLastErrorCode() + * to obtain the specific error. + * + * \sa PHYSFS_tell + */ +PHYSFS_DECL int PHYSFS_seek(PHYSFS_File *handle, PHYSFS_uint64 pos); + + +/** + * \fn PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle) + * \brief Get total length of a file in bytes. + * + * Note that if another process/thread is writing to this file at the same + * time, then the information this function supplies could be incorrect + * before you get it. Use with caution, or better yet, don't use at all. + * + * \param handle handle returned from PHYSFS_open*(). + * \return size in bytes of the file. -1 if can't be determined. + * + * \sa PHYSFS_tell + * \sa PHYSFS_seek + */ +PHYSFS_DECL PHYSFS_sint64 PHYSFS_fileLength(PHYSFS_File *handle); + + +/* Buffering stuff... */ + +/** + * \fn int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 bufsize) + * \brief Set up buffering for a PhysicsFS file handle. + * + * Define an i/o buffer for a file handle. A memory block of (bufsize) bytes + * will be allocated and associated with (handle). + * + * For files opened for reading, up to (bufsize) bytes are read from (handle) + * and stored in the internal buffer. Calls to PHYSFS_read() will pull + * from this buffer until it is empty, and then refill it for more reading. + * Note that compressed files, like ZIP archives, will decompress while + * buffering, so this can be handy for offsetting CPU-intensive operations. + * The buffer isn't filled until you do your next read. + * + * For files opened for writing, data will be buffered to memory until the + * buffer is full or the buffer is flushed. Closing a handle implicitly + * causes a flush...check your return values! + * + * Seeking, etc transparently accounts for buffering. + * + * You can resize an existing buffer by calling this function more than once + * on the same file. Setting the buffer size to zero will free an existing + * buffer. + * + * PhysicsFS file handles are unbuffered by default. + * + * Please check the return value of this function! Failures can include + * not being able to seek backwards in a read-only file when removing the + * buffer, not being able to allocate the buffer, and not being able to + * flush the buffer to disk, among other unexpected problems. + * + * \param handle handle returned from PHYSFS_open*(). + * \param bufsize size, in bytes, of buffer to allocate. + * \return nonzero if successful, zero on error. + * + * \sa PHYSFS_flush + * \sa PHYSFS_read + * \sa PHYSFS_write + * \sa PHYSFS_close + */ +PHYSFS_DECL int PHYSFS_setBuffer(PHYSFS_File *handle, PHYSFS_uint64 bufsize); + + +/** + * \fn int PHYSFS_flush(PHYSFS_File *handle) + * \brief Flush a buffered PhysicsFS file handle. + * + * For buffered files opened for writing, this will put the current contents + * of the buffer to disk and flag the buffer as empty if possible. + * + * For buffered files opened for reading or unbuffered files, this is a safe + * no-op, and will report success. + * + * \param handle handle returned from PHYSFS_open*(). + * \return nonzero if successful, zero on error. + * + * \sa PHYSFS_setBuffer + * \sa PHYSFS_close + */ +PHYSFS_DECL int PHYSFS_flush(PHYSFS_File *handle); + + +/* Byteorder stuff... */ + +/** + * \fn PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 val) + * \brief Swap littleendian signed 16 to platform's native byte order. + * + * Take a 16-bit signed value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +PHYSFS_DECL PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 val); + + +/** + * \fn PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 val) + * \brief Swap littleendian unsigned 16 to platform's native byte order. + * + * Take a 16-bit unsigned value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +PHYSFS_DECL PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 val); + +/** + * \fn PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 val) + * \brief Swap littleendian signed 32 to platform's native byte order. + * + * Take a 32-bit signed value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +PHYSFS_DECL PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 val); + + +/** + * \fn PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 val) + * \brief Swap littleendian unsigned 32 to platform's native byte order. + * + * Take a 32-bit unsigned value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +PHYSFS_DECL PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 val); + +/** + * \fn PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 val) + * \brief Swap littleendian signed 64 to platform's native byte order. + * + * Take a 64-bit signed value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 val); + + +/** + * \fn PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 val) + * \brief Swap littleendian unsigned 64 to platform's native byte order. + * + * Take a 64-bit unsigned value in littleendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 val); + + +/** + * \fn PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 val) + * \brief Swap bigendian signed 16 to platform's native byte order. + * + * Take a 16-bit signed value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +PHYSFS_DECL PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 val); + + +/** + * \fn PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 val) + * \brief Swap bigendian unsigned 16 to platform's native byte order. + * + * Take a 16-bit unsigned value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +PHYSFS_DECL PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 val); + +/** + * \fn PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 val) + * \brief Swap bigendian signed 32 to platform's native byte order. + * + * Take a 32-bit signed value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +PHYSFS_DECL PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 val); + + +/** + * \fn PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 val) + * \brief Swap bigendian unsigned 32 to platform's native byte order. + * + * Take a 32-bit unsigned value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + */ +PHYSFS_DECL PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 val); + + +/** + * \fn PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 val) + * \brief Swap bigendian signed 64 to platform's native byte order. + * + * Take a 64-bit signed value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 val); + + +/** + * \fn PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 val) + * \brief Swap bigendian unsigned 64 to platform's native byte order. + * + * Take a 64-bit unsigned value in bigendian format and convert it to + * the platform's native byte order. + * + * \param val value to convert + * \return converted value. + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 val); + + +/** + * \fn int PHYSFS_readSLE16(PHYSFS_File *file, PHYSFS_sint16 *val) + * \brief Read and convert a signed 16-bit littleendian value. + * + * Convenience function. Read a signed 16-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_readSLE16(PHYSFS_File *file, PHYSFS_sint16 *val); + + +/** + * \fn int PHYSFS_readULE16(PHYSFS_File *file, PHYSFS_uint16 *val) + * \brief Read and convert an unsigned 16-bit littleendian value. + * + * Convenience function. Read an unsigned 16-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + * + */ +PHYSFS_DECL int PHYSFS_readULE16(PHYSFS_File *file, PHYSFS_uint16 *val); + + +/** + * \fn int PHYSFS_readSBE16(PHYSFS_File *file, PHYSFS_sint16 *val) + * \brief Read and convert a signed 16-bit bigendian value. + * + * Convenience function. Read a signed 16-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_readSBE16(PHYSFS_File *file, PHYSFS_sint16 *val); + + +/** + * \fn int PHYSFS_readUBE16(PHYSFS_File *file, PHYSFS_uint16 *val) + * \brief Read and convert an unsigned 16-bit bigendian value. + * + * Convenience function. Read an unsigned 16-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + * + */ +PHYSFS_DECL int PHYSFS_readUBE16(PHYSFS_File *file, PHYSFS_uint16 *val); + + +/** + * \fn int PHYSFS_readSLE32(PHYSFS_File *file, PHYSFS_sint32 *val) + * \brief Read and convert a signed 32-bit littleendian value. + * + * Convenience function. Read a signed 32-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_readSLE32(PHYSFS_File *file, PHYSFS_sint32 *val); + + +/** + * \fn int PHYSFS_readULE32(PHYSFS_File *file, PHYSFS_uint32 *val) + * \brief Read and convert an unsigned 32-bit littleendian value. + * + * Convenience function. Read an unsigned 32-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + * + */ +PHYSFS_DECL int PHYSFS_readULE32(PHYSFS_File *file, PHYSFS_uint32 *val); + + +/** + * \fn int PHYSFS_readSBE32(PHYSFS_File *file, PHYSFS_sint32 *val) + * \brief Read and convert a signed 32-bit bigendian value. + * + * Convenience function. Read a signed 32-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_readSBE32(PHYSFS_File *file, PHYSFS_sint32 *val); + + +/** + * \fn int PHYSFS_readUBE32(PHYSFS_File *file, PHYSFS_uint32 *val) + * \brief Read and convert an unsigned 32-bit bigendian value. + * + * Convenience function. Read an unsigned 32-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + * + */ +PHYSFS_DECL int PHYSFS_readUBE32(PHYSFS_File *file, PHYSFS_uint32 *val); + + +/** + * \fn int PHYSFS_readSLE64(PHYSFS_File *file, PHYSFS_sint64 *val) + * \brief Read and convert a signed 64-bit littleendian value. + * + * Convenience function. Read a signed 64-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + * + * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL int PHYSFS_readSLE64(PHYSFS_File *file, PHYSFS_sint64 *val); + + +/** + * \fn int PHYSFS_readULE64(PHYSFS_File *file, PHYSFS_uint64 *val) + * \brief Read and convert an unsigned 64-bit littleendian value. + * + * Convenience function. Read an unsigned 64-bit littleendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL int PHYSFS_readULE64(PHYSFS_File *file, PHYSFS_uint64 *val); + + +/** + * \fn int PHYSFS_readSBE64(PHYSFS_File *file, PHYSFS_sint64 *val) + * \brief Read and convert a signed 64-bit bigendian value. + * + * Convenience function. Read a signed 64-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + * + * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL int PHYSFS_readSBE64(PHYSFS_File *file, PHYSFS_sint64 *val); + + +/** + * \fn int PHYSFS_readUBE64(PHYSFS_File *file, PHYSFS_uint64 *val) + * \brief Read and convert an unsigned 64-bit bigendian value. + * + * Convenience function. Read an unsigned 64-bit bigendian value from a + * file and convert it to the platform's native byte order. + * + * \param file PhysicsFS file handle from which to read. + * \param val pointer to where value should be stored. + * \return zero on failure, non-zero on success. If successful, (*val) will + * store the result. On failure, you can find out what went wrong + * from PHYSFS_getLastErrorCode(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL int PHYSFS_readUBE64(PHYSFS_File *file, PHYSFS_uint64 *val); + + +/** + * \fn int PHYSFS_writeSLE16(PHYSFS_File *file, PHYSFS_sint16 val) + * \brief Convert and write a signed 16-bit littleendian value. + * + * Convenience function. Convert a signed 16-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_writeSLE16(PHYSFS_File *file, PHYSFS_sint16 val); + + +/** + * \fn int PHYSFS_writeULE16(PHYSFS_File *file, PHYSFS_uint16 val) + * \brief Convert and write an unsigned 16-bit littleendian value. + * + * Convenience function. Convert an unsigned 16-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_writeULE16(PHYSFS_File *file, PHYSFS_uint16 val); + + +/** + * \fn int PHYSFS_writeSBE16(PHYSFS_File *file, PHYSFS_sint16 val) + * \brief Convert and write a signed 16-bit bigendian value. + * + * Convenience function. Convert a signed 16-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_writeSBE16(PHYSFS_File *file, PHYSFS_sint16 val); + + +/** + * \fn int PHYSFS_writeUBE16(PHYSFS_File *file, PHYSFS_uint16 val) + * \brief Convert and write an unsigned 16-bit bigendian value. + * + * Convenience function. Convert an unsigned 16-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_writeUBE16(PHYSFS_File *file, PHYSFS_uint16 val); + + +/** + * \fn int PHYSFS_writeSLE32(PHYSFS_File *file, PHYSFS_sint32 val) + * \brief Convert and write a signed 32-bit littleendian value. + * + * Convenience function. Convert a signed 32-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_writeSLE32(PHYSFS_File *file, PHYSFS_sint32 val); + + +/** + * \fn int PHYSFS_writeULE32(PHYSFS_File *file, PHYSFS_uint32 val) + * \brief Convert and write an unsigned 32-bit littleendian value. + * + * Convenience function. Convert an unsigned 32-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_writeULE32(PHYSFS_File *file, PHYSFS_uint32 val); + + +/** + * \fn int PHYSFS_writeSBE32(PHYSFS_File *file, PHYSFS_sint32 val) + * \brief Convert and write a signed 32-bit bigendian value. + * + * Convenience function. Convert a signed 32-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_writeSBE32(PHYSFS_File *file, PHYSFS_sint32 val); + + +/** + * \fn int PHYSFS_writeUBE32(PHYSFS_File *file, PHYSFS_uint32 val) + * \brief Convert and write an unsigned 32-bit bigendian value. + * + * Convenience function. Convert an unsigned 32-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + */ +PHYSFS_DECL int PHYSFS_writeUBE32(PHYSFS_File *file, PHYSFS_uint32 val); + + +/** + * \fn int PHYSFS_writeSLE64(PHYSFS_File *file, PHYSFS_sint64 val) + * \brief Convert and write a signed 64-bit littleendian value. + * + * Convenience function. Convert a signed 64-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + * + * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL int PHYSFS_writeSLE64(PHYSFS_File *file, PHYSFS_sint64 val); + + +/** + * \fn int PHYSFS_writeULE64(PHYSFS_File *file, PHYSFS_uint64 val) + * \brief Convert and write an unsigned 64-bit littleendian value. + * + * Convenience function. Convert an unsigned 64-bit value from the platform's + * native byte order to littleendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL int PHYSFS_writeULE64(PHYSFS_File *file, PHYSFS_uint64 val); + + +/** + * \fn int PHYSFS_writeSBE64(PHYSFS_File *file, PHYSFS_sint64 val) + * \brief Convert and write a signed 64-bit bigending value. + * + * Convenience function. Convert a signed 64-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + * + * \warning Remember, PHYSFS_sint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL int PHYSFS_writeSBE64(PHYSFS_File *file, PHYSFS_sint64 val); + + +/** + * \fn int PHYSFS_writeUBE64(PHYSFS_File *file, PHYSFS_uint64 val) + * \brief Convert and write an unsigned 64-bit bigendian value. + * + * Convenience function. Convert an unsigned 64-bit value from the platform's + * native byte order to bigendian and write it to a file. + * + * \param file PhysicsFS file handle to which to write. + * \param val Value to convert and write. + * \return zero on failure, non-zero on success. On failure, you can + * find out what went wrong from PHYSFS_getLastErrorCode(). + * + * \warning Remember, PHYSFS_uint64 is only 32 bits on platforms without + * any sort of 64-bit support. + */ +PHYSFS_DECL int PHYSFS_writeUBE64(PHYSFS_File *file, PHYSFS_uint64 val); + + +/* Everything above this line is part of the PhysicsFS 1.0 API. */ + +/** + * \fn int PHYSFS_isInit(void) + * \brief Determine if the PhysicsFS library is initialized. + * + * Once PHYSFS_init() returns successfully, this will return non-zero. + * Before a successful PHYSFS_init() and after PHYSFS_deinit() returns + * successfully, this will return zero. This function is safe to call at + * any time. + * + * \return non-zero if library is initialized, zero if library is not. + * + * \sa PHYSFS_init + * \sa PHYSFS_deinit + */ +PHYSFS_DECL int PHYSFS_isInit(void); + + +/** + * \fn int PHYSFS_symbolicLinksPermitted(void) + * \brief Determine if the symbolic links are permitted. + * + * This reports the setting from the last call to PHYSFS_permitSymbolicLinks(). + * If PHYSFS_permitSymbolicLinks() hasn't been called since the library was + * last initialized, symbolic links are implicitly disabled. + * + * \return non-zero if symlinks are permitted, zero if not. + * + * \sa PHYSFS_permitSymbolicLinks + */ +PHYSFS_DECL int PHYSFS_symbolicLinksPermitted(void); + + +/** + * \struct PHYSFS_Allocator + * \brief PhysicsFS allocation function pointers. + * + * (This is for limited, hardcore use. If you don't immediately see a need + * for it, you can probably ignore this forever.) + * + * You create one of these structures for use with PHYSFS_setAllocator. + * Allocators are assumed to be reentrant by the caller; please mutex + * accordingly. + * + * Allocations are always discussed in 64-bits, for future expansion...we're + * on the cusp of a 64-bit transition, and we'll probably be allocating 6 + * gigabytes like it's nothing sooner or later, and I don't want to change + * this again at that point. If you're on a 32-bit platform and have to + * downcast, it's okay to return NULL if the allocation is greater than + * 4 gigabytes, since you'd have to do so anyhow. + * + * \sa PHYSFS_setAllocator + */ +typedef struct PHYSFS_Allocator +{ + int (*Init)(void); /**< Initialize. Can be NULL. Zero on failure. */ + void (*Deinit)(void); /**< Deinitialize your allocator. Can be NULL. */ + void *(*Malloc)(PHYSFS_uint64); /**< Allocate like malloc(). */ + void *(*Realloc)(void *, PHYSFS_uint64); /**< Reallocate like realloc(). */ + void (*Free)(void *); /**< Free memory from Malloc or Realloc. */ +} PHYSFS_Allocator; + + +/** + * \fn int PHYSFS_setAllocator(const PHYSFS_Allocator *allocator) + * \brief Hook your own allocation routines into PhysicsFS. + * + * (This is for limited, hardcore use. If you don't immediately see a need + * for it, you can probably ignore this forever.) + * + * By default, PhysicsFS will use whatever is reasonable for a platform + * to manage dynamic memory (usually ANSI C malloc/realloc/free, but + * some platforms might use something else), but in some uncommon cases, the + * app might want more control over the library's memory management. This + * lets you redirect PhysicsFS to use your own allocation routines instead. + * You can only call this function before PHYSFS_init(); if the library is + * initialized, it'll reject your efforts to change the allocator mid-stream. + * You may call this function after PHYSFS_deinit() if you are willing to + * shut down the library and restart it with a new allocator; this is a safe + * and supported operation. The allocator remains intact between deinit/init + * calls. If you want to return to the platform's default allocator, pass a + * NULL in here. + * + * If you aren't immediately sure what to do with this function, you can + * safely ignore it altogether. + * + * \param allocator Structure containing your allocator's entry points. + * \return zero on failure, non-zero on success. This call only fails + * when used between PHYSFS_init() and PHYSFS_deinit() calls. + */ +PHYSFS_DECL int PHYSFS_setAllocator(const PHYSFS_Allocator *allocator); + + +/** + * \fn int PHYSFS_mount(const char *newDir, const char *mountPoint, int appendToPath) + * \brief Add an archive or directory to the search path. + * + * If this is a duplicate, the entry is not added again, even though the + * function succeeds. You may not add the same archive to two different + * mountpoints: duplicate checking is done against the archive and not the + * mountpoint. + * + * When you mount an archive, it is added to a virtual file system...all files + * in all of the archives are interpolated into a single hierachical file + * tree. Two archives mounted at the same place (or an archive with files + * overlapping another mountpoint) may have overlapping files: in such a case, + * the file earliest in the search path is selected, and the other files are + * inaccessible to the application. This allows archives to be used to + * override previous revisions; you can use the mounting mechanism to place + * archives at a specific point in the file tree and prevent overlap; this + * is useful for downloadable mods that might trample over application data + * or each other, for example. + * + * The mountpoint does not need to exist prior to mounting, which is different + * than those familiar with the Unix concept of "mounting" may expect. + * As well, more than one archive can be mounted to the same mountpoint, or + * mountpoints and archive contents can overlap...the interpolation mechanism + * still functions as usual. + * + * Specifying a symbolic link to an archive or directory is allowed here, + * regardless of the state of PHYSFS_permitSymbolicLinks(). That function + * only deals with symlinks inside the mounted directory or archive. + * + * \param newDir directory or archive to add to the path, in + * platform-dependent notation. + * \param mountPoint Location in the interpolated tree that this archive + * will be "mounted", in platform-independent notation. + * NULL or "" is equivalent to "/". + * \param appendToPath nonzero to append to search path, zero to prepend. + * \return nonzero if added to path, zero on failure (bogus archive, dir + * missing, etc). Use PHYSFS_getLastErrorCode() to obtain + * the specific error. + * + * \sa PHYSFS_removeFromSearchPath + * \sa PHYSFS_getSearchPath + * \sa PHYSFS_getMountPoint + * \sa PHYSFS_mountIo + */ +PHYSFS_DECL int PHYSFS_mount(const char *newDir, + const char *mountPoint, + int appendToPath); + +/** + * \fn int PHYSFS_getMountPoint(const char *dir) + * \brief Determine a mounted archive's mountpoint. + * + * You give this function the name of an archive or dir you successfully + * added to the search path, and it reports the location in the interpolated + * tree where it is mounted. Files mounted with a NULL mountpoint or through + * PHYSFS_addToSearchPath() will report "/". The return value is READ ONLY + * and valid until the archive is removed from the search path. + * + * \param dir directory or archive previously added to the path, in + * platform-dependent notation. This must match the string + * used when adding, even if your string would also reference + * the same file with a different string of characters. + * \return READ-ONLY string of mount point if added to path, NULL on failure + * (bogus archive, etc). Use PHYSFS_getLastErrorCode() to obtain the + * specific error. + * + * \sa PHYSFS_removeFromSearchPath + * \sa PHYSFS_getSearchPath + * \sa PHYSFS_getMountPoint + */ +PHYSFS_DECL const char *PHYSFS_getMountPoint(const char *dir); + + +/** + * \typedef PHYSFS_StringCallback + * \brief Function signature for callbacks that report strings. + * + * These are used to report a list of strings to an original caller, one + * string per callback. All strings are UTF-8 encoded. Functions should not + * try to modify or free the string's memory. + * + * These callbacks are used, starting in PhysicsFS 1.1, as an alternative to + * functions that would return lists that need to be cleaned up with + * PHYSFS_freeList(). The callback means that the library doesn't need to + * allocate an entire list and all the strings up front. + * + * Be aware that promises data ordering in the list versions are not + * necessarily so in the callback versions. Check the documentation on + * specific APIs, but strings may not be sorted as you expect. + * + * \param data User-defined data pointer, passed through from the API + * that eventually called the callback. + * \param str The string data about which the callback is meant to inform. + * + * \sa PHYSFS_getCdRomDirsCallback + * \sa PHYSFS_getSearchPathCallback + */ +typedef void (*PHYSFS_StringCallback)(void *data, const char *str); + + +/** + * \typedef PHYSFS_EnumFilesCallback + * \brief Function signature for callbacks that enumerate files. + * + * \warning As of PhysicsFS 2.1, Use PHYSFS_EnumerateCallback with + * PHYSFS_enumerate() instead; it gives you more control over the process. + * + * These are used to report a list of directory entries to an original caller, + * one file/dir/symlink per callback. All strings are UTF-8 encoded. + * Functions should not try to modify or free any string's memory. + * + * These callbacks are used, starting in PhysicsFS 1.1, as an alternative to + * functions that would return lists that need to be cleaned up with + * PHYSFS_freeList(). The callback means that the library doesn't need to + * allocate an entire list and all the strings up front. + * + * Be aware that promised data ordering in the list versions are not + * necessarily so in the callback versions. Check the documentation on + * specific APIs, but strings may not be sorted as you expect and you might + * get duplicate strings. + * + * \param data User-defined data pointer, passed through from the API + * that eventually called the callback. + * \param origdir A string containing the full path, in platform-independent + * notation, of the directory containing this file. In most + * cases, this is the directory on which you requested + * enumeration, passed in the callback for your convenience. + * \param fname The filename that is being enumerated. It may not be in + * alphabetical order compared to other callbacks that have + * fired, and it will not contain the full path. You can + * recreate the fullpath with $origdir/$fname ... The file + * can be a subdirectory, a file, a symlink, etc. + * + * \sa PHYSFS_enumerateFilesCallback + */ +typedef void (*PHYSFS_EnumFilesCallback)(void *data, const char *origdir, + const char *fname); + + +/** + * \fn void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback c, void *d) + * \brief Enumerate CD-ROM directories, using an application-defined callback. + * + * Internally, PHYSFS_getCdRomDirs() just calls this function and then builds + * a list before returning to the application, so functionality is identical + * except for how the information is represented to the application. + * + * Unlike PHYSFS_getCdRomDirs(), this function does not return an array. + * Rather, it calls a function specified by the application once per + * detected disc: + * + * \code + * + * static void foundDisc(void *data, const char *cddir) + * { + * printf("cdrom dir [%s] is available.\n", cddir); + * } + * + * // ... + * PHYSFS_getCdRomDirsCallback(foundDisc, NULL); + * \endcode + * + * This call may block while drives spin up. Be forewarned. + * + * \param c Callback function to notify about detected drives. + * \param d Application-defined data passed to callback. Can be NULL. + * + * \sa PHYSFS_StringCallback + * \sa PHYSFS_getCdRomDirs + */ +PHYSFS_DECL void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback c, void *d); + + +/** + * \fn void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback c, void *d) + * \brief Enumerate the search path, using an application-defined callback. + * + * Internally, PHYSFS_getSearchPath() just calls this function and then builds + * a list before returning to the application, so functionality is identical + * except for how the information is represented to the application. + * + * Unlike PHYSFS_getSearchPath(), this function does not return an array. + * Rather, it calls a function specified by the application once per + * element of the search path: + * + * \code + * + * static void printSearchPath(void *data, const char *pathItem) + * { + * printf("[%s] is in the search path.\n", pathItem); + * } + * + * // ... + * PHYSFS_getSearchPathCallback(printSearchPath, NULL); + * \endcode + * + * Elements of the search path are reported in order search priority, so the + * first archive/dir that would be examined when looking for a file is the + * first element passed through the callback. + * + * \param c Callback function to notify about search path elements. + * \param d Application-defined data passed to callback. Can be NULL. + * + * \sa PHYSFS_StringCallback + * \sa PHYSFS_getSearchPath + */ +PHYSFS_DECL void PHYSFS_getSearchPathCallback(PHYSFS_StringCallback c, void *d); + + +/** + * \fn void PHYSFS_enumerateFilesCallback(const char *dir, PHYSFS_EnumFilesCallback c, void *d) + * \brief Get a file listing of a search path's directory, using an application-defined callback. + * + * \deprecated As of PhysicsFS 2.1, use PHYSFS_enumerate() instead. This + * function has no way to report errors (or to have the callback signal an + * error or request a stop), so if data will be lost, your callback has no + * way to direct the process, and your calling app has no way to know. + * + * As of PhysicsFS 2.1, this function just wraps PHYSFS_enumerate() and + * ignores errors. Consider using PHYSFS_enumerate() or + * PHYSFS_enumerateFiles() instead. + * + * \sa PHYSFS_enumerate + * \sa PHYSFS_enumerateFiles + * \sa PHYSFS_EnumFilesCallback + */ +PHYSFS_DECL void PHYSFS_enumerateFilesCallback(const char *dir, + PHYSFS_EnumFilesCallback c, + void *d) PHYSFS_DEPRECATED; + +/** + * \fn void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, PHYSFS_uint64 len) + * \brief Convert a UCS-4 string to a UTF-8 string. + * + * \warning This function will not report an error if there are invalid UCS-4 + * values in the source string. It will replace them with a '?' + * character and continue on. + * + * UCS-4 (aka UTF-32) strings are 32-bits per character: \c wchar_t on Unix. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is the same size as the source buffer. UTF-8 + * never uses more than 32-bits per character, so while it may shrink a UCS-4 + * string, it will never expand it. + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UTF-8 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * \param src Null-terminated source string in UCS-4 format. + * \param dst Buffer to store converted UTF-8 string. + * \param len Size, in bytes, of destination buffer. + */ +PHYSFS_DECL void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, + PHYSFS_uint64 len); + +/** + * \fn void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, PHYSFS_uint64 len) + * \brief Convert a UTF-8 string to a UCS-4 string. + * + * \warning This function will not report an error if there are invalid UTF-8 + * sequences in the source string. It will replace them with a '?' + * character and continue on. + * + * UCS-4 (aka UTF-32) strings are 32-bits per character: \c wchar_t on Unix. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is four times the size of the source buffer. + * UTF-8 uses from one to four bytes per character, but UCS-4 always uses + * four, so an entirely low-ASCII string will quadruple in size! + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UCS-4 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * \param src Null-terminated source string in UTF-8 format. + * \param dst Buffer to store converted UCS-4 string. + * \param len Size, in bytes, of destination buffer. + */ +PHYSFS_DECL void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, + PHYSFS_uint64 len); + +/** + * \fn void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len) + * \brief Convert a UCS-2 string to a UTF-8 string. + * + * \warning you almost certainly should use PHYSFS_utf8FromUtf16(), which + * became available in PhysicsFS 2.1, unless you know what you're doing. + * + * \warning This function will not report an error if there are invalid UCS-2 + * values in the source string. It will replace them with a '?' + * character and continue on. + * + * UCS-2 strings are 16-bits per character: \c TCHAR on Windows, when building + * with Unicode support. Please note that modern versions of Windows use + * UTF-16, which is an extended form of UCS-2, and not UCS-2 itself. You + * almost certainly want PHYSFS_utf8FromUtf16() instead. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is double the size of the source buffer. + * UTF-8 never uses more than 32-bits per character, so while it may shrink + * a UCS-2 string, it may also expand it. + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UTF-8 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * \param src Null-terminated source string in UCS-2 format. + * \param dst Buffer to store converted UTF-8 string. + * \param len Size, in bytes, of destination buffer. + * + * \sa PHYSFS_utf8FromUtf16 + */ +PHYSFS_DECL void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, + PHYSFS_uint64 len); + +/** + * \fn PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len) + * \brief Convert a UTF-8 string to a UCS-2 string. + * + * \warning you almost certainly should use PHYSFS_utf8ToUtf16(), which + * became available in PhysicsFS 2.1, unless you know what you're doing. + * + * \warning This function will not report an error if there are invalid UTF-8 + * sequences in the source string. It will replace them with a '?' + * character and continue on. + * + * UCS-2 strings are 16-bits per character: \c TCHAR on Windows, when building + * with Unicode support. Please note that modern versions of Windows use + * UTF-16, which is an extended form of UCS-2, and not UCS-2 itself. You + * almost certainly want PHYSFS_utf8ToUtf16() instead, but you need to + * understand how that changes things, too. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is double the size of the source buffer. + * UTF-8 uses from one to four bytes per character, but UCS-2 always uses + * two, so an entirely low-ASCII string will double in size! + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UCS-2 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * \param src Null-terminated source string in UTF-8 format. + * \param dst Buffer to store converted UCS-2 string. + * \param len Size, in bytes, of destination buffer. + * + * \sa PHYSFS_utf8ToUtf16 + */ +PHYSFS_DECL void PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, + PHYSFS_uint64 len); + +/** + * \fn void PHYSFS_utf8FromLatin1(const char *src, char *dst, PHYSFS_uint64 len) + * \brief Convert a UTF-8 string to a Latin1 string. + * + * Latin1 strings are 8-bits per character: a popular "high ASCII" encoding. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is double the size of the source buffer. + * UTF-8 expands latin1 codepoints over 127 from 1 to 2 bytes, so the string + * may grow in some cases. + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UTF-8 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * Please note that we do not supply a UTF-8 to Latin1 converter, since Latin1 + * can't express most Unicode codepoints. It's a legacy encoding; you should + * be converting away from it at all times. + * + * \param src Null-terminated source string in Latin1 format. + * \param dst Buffer to store converted UTF-8 string. + * \param len Size, in bytes, of destination buffer. + */ +PHYSFS_DECL void PHYSFS_utf8FromLatin1(const char *src, char *dst, + PHYSFS_uint64 len); + +/* Everything above this line is part of the PhysicsFS 2.0 API. */ + +/** + * \fn int PHYSFS_caseFold(const PHYSFS_uint32 from, PHYSFS_uint32 *to) + * \brief "Fold" a Unicode codepoint to a lowercase equivalent. + * + * (This is for limited, hardcore use. If you don't immediately see a need + * for it, you can probably ignore this forever.) + * + * This will convert a Unicode codepoint into its lowercase equivalent. + * Bogus codepoints and codepoints without a lowercase equivalent will + * be returned unconverted. + * + * Note that you might get multiple codepoints in return! The German Eszett, + * for example, will fold down to two lowercase latin 's' codepoints. The + * theory is that if you fold two strings, one with an Eszett and one with + * "SS" down, they will match. + * + * \warning Anyone that is a student of Unicode knows about the "Turkish I" + * problem. This API does not handle it. Assume this one letter + * in all of Unicode will definitely fold sort of incorrectly. If + * you don't know what this is about, you can probably ignore this + * problem for most of the planet, but perfection is impossible. + * + * \param from The codepoint to fold. + * \param to Buffer to store the folded codepoint values into. This should + * point to space for at least 3 PHYSFS_uint32 slots. + * \return The number of codepoints the folding produced. Between 1 and 3. + */ +PHYSFS_DECL int PHYSFS_caseFold(const PHYSFS_uint32 from, PHYSFS_uint32 *to); + + +/** + * \fn int PHYSFS_utf8stricmp(const char *str1, const char *str2) + * \brief Case-insensitive compare of two UTF-8 strings. + * + * This is a strcasecmp/stricmp replacement that expects both strings + * to be in UTF-8 encoding. It will do "case folding" to decide if the + * Unicode codepoints in the strings match. + * + * If both strings are exclusively low-ASCII characters, this will do the + * right thing, as that is also valid UTF-8. If there are any high-ASCII + * chars, this will not do what you expect! + * + * It will report which string is "greater than" the other, but be aware that + * this doesn't necessarily mean anything: 'a' may be "less than" 'b', but + * a Japanese kuten has no meaningful alphabetically relationship to + * a Greek lambda, but being able to assign a reliable "value" makes sorting + * algorithms possible, if not entirely sane. Most cases should treat the + * return value as "equal" or "not equal". + * + * Like stricmp, this expects both strings to be NULL-terminated. + * + * \param str1 First string to compare. + * \param str2 Second string to compare. + * \return -1 if str1 is "less than" str2, 1 if "greater than", 0 if equal. + */ +PHYSFS_DECL int PHYSFS_utf8stricmp(const char *str1, const char *str2); + +/** + * \fn int PHYSFS_utf16stricmp(const PHYSFS_uint16 *str1, const PHYSFS_uint16 *str2) + * \brief Case-insensitive compare of two UTF-16 strings. + * + * This is a strcasecmp/stricmp replacement that expects both strings + * to be in UTF-16 encoding. It will do "case folding" to decide if the + * Unicode codepoints in the strings match. + * + * It will report which string is "greater than" the other, but be aware that + * this doesn't necessarily mean anything: 'a' may be "less than" 'b', but + * a Japanese kuten has no meaningful alphabetically relationship to + * a Greek lambda, but being able to assign a reliable "value" makes sorting + * algorithms possible, if not entirely sane. Most cases should treat the + * return value as "equal" or "not equal". + * + * Like stricmp, this expects both strings to be NULL-terminated. + * + * \param str1 First string to compare. + * \param str2 Second string to compare. + * \return -1 if str1 is "less than" str2, 1 if "greater than", 0 if equal. + */ +PHYSFS_DECL int PHYSFS_utf16stricmp(const PHYSFS_uint16 *str1, + const PHYSFS_uint16 *str2); + +/** + * \fn int PHYSFS_ucs4stricmp(const PHYSFS_uint32 *str1, const PHYSFS_uint32 *str2) + * \brief Case-insensitive compare of two UCS-4 strings. + * + * This is a strcasecmp/stricmp replacement that expects both strings + * to be in UCS-4 (aka UTF-32) encoding. It will do "case folding" to decide + * if the Unicode codepoints in the strings match. + * + * It will report which string is "greater than" the other, but be aware that + * this doesn't necessarily mean anything: 'a' may be "less than" 'b', but + * a Japanese kuten has no meaningful alphabetically relationship to + * a Greek lambda, but being able to assign a reliable "value" makes sorting + * algorithms possible, if not entirely sane. Most cases should treat the + * return value as "equal" or "not equal". + * + * Like stricmp, this expects both strings to be NULL-terminated. + * + * \param str1 First string to compare. + * \param str2 Second string to compare. + * \return -1 if str1 is "less than" str2, 1 if "greater than", 0 if equal. + */ +PHYSFS_DECL int PHYSFS_ucs4stricmp(const PHYSFS_uint32 *str1, + const PHYSFS_uint32 *str2); + + +/** + * \typedef PHYSFS_EnumerateCallback + * \brief Possible return values from PHYSFS_EnumerateCallback. + * + * These values dictate if an enumeration callback should continue to fire, + * or stop (and why it is stopping). + * + * \sa PHYSFS_EnumerateCallback + * \sa PHYSFS_enumerate + */ +typedef enum PHYSFS_EnumerateCallbackResult +{ + PHYSFS_ENUM_ERROR = -1, /**< Stop enumerating, report error to app. */ + PHYSFS_ENUM_STOP = 0, /**< Stop enumerating, report success to app. */ + PHYSFS_ENUM_OK = 1 /**< Keep enumerating, no problems */ +} PHYSFS_EnumerateCallbackResult; + +/** + * \typedef PHYSFS_EnumerateCallback + * \brief Function signature for callbacks that enumerate and return results. + * + * This is the same thing as PHYSFS_EnumFilesCallback from PhysicsFS 2.0, + * except it can return a result from the callback: namely: if you're looking + * for something specific, once you find it, you can tell PhysicsFS to stop + * enumerating further. This is used with PHYSFS_enumerate(), which we + * hopefully got right this time. :) + * + * \param data User-defined data pointer, passed through from the API + * that eventually called the callback. + * \param origdir A string containing the full path, in platform-independent + * notation, of the directory containing this file. In most + * cases, this is the directory on which you requested + * enumeration, passed in the callback for your convenience. + * \param fname The filename that is being enumerated. It may not be in + * alphabetical order compared to other callbacks that have + * fired, and it will not contain the full path. You can + * recreate the fullpath with $origdir/$fname ... The file + * can be a subdirectory, a file, a symlink, etc. + * \return A value from PHYSFS_EnumerateCallbackResult. + * All other values are (currently) undefined; don't use them. + * + * \sa PHYSFS_enumerate + * \sa PHYSFS_EnumerateCallbackResult + */ +typedef PHYSFS_EnumerateCallbackResult (*PHYSFS_EnumerateCallback)(void *data, + const char *origdir, const char *fname); + +/** + * \fn int PHYSFS_enumerate(const char *dir, PHYSFS_EnumerateCallback c, void *d) + * \brief Get a file listing of a search path's directory, using an application-defined callback, with errors reported. + * + * Internally, PHYSFS_enumerateFiles() just calls this function and then builds + * a list before returning to the application, so functionality is identical + * except for how the information is represented to the application. + * + * Unlike PHYSFS_enumerateFiles(), this function does not return an array. + * Rather, it calls a function specified by the application once per + * element of the search path: + * + * \code + * + * static int printDir(void *data, const char *origdir, const char *fname) + * { + * printf(" * We've got [%s] in [%s].\n", fname, origdir); + * return 1; // give me more data, please. + * } + * + * // ... + * PHYSFS_enumerate("/some/path", printDir, NULL); + * \endcode + * + * Items sent to the callback are not guaranteed to be in any order whatsoever. + * There is no sorting done at this level, and if you need that, you should + * probably use PHYSFS_enumerateFiles() instead, which guarantees + * alphabetical sorting. This form reports whatever is discovered in each + * archive before moving on to the next. Even within one archive, we can't + * guarantee what order it will discover data. <em>Any sorting you find in + * these callbacks is just pure luck. Do not rely on it.</em> As this walks + * the entire list of archives, you may receive duplicate filenames. + * + * This API and the callbacks themselves are capable of reporting errors. + * Prior to this API, callbacks had to accept every enumerated item, even if + * they were only looking for a specific thing and wanted to stop after that, + * or had a serious error and couldn't alert anyone. Furthermore, if + * PhysicsFS itself had a problem (disk error or whatnot), it couldn't report + * it to the calling app, it would just have to skip items or stop + * enumerating outright, and the caller wouldn't know it had lost some data + * along the way. + * + * Now the caller can be sure it got a complete data set, and its callback has + * control if it wants enumeration to stop early. See the documentation for + * PHYSFS_EnumerateCallback for details on how your callback should behave. + * + * \param dir Directory, in platform-independent notation, to enumerate. + * \param c Callback function to notify about search path elements. + * \param d Application-defined data passed to callback. Can be NULL. + * \return non-zero on success, zero on failure. Use + * PHYSFS_getLastErrorCode() to obtain the specific error. If the + * callback returns PHYSFS_ENUM_STOP to stop early, this will be + * considered success. Callbacks returning PHYSFS_ENUM_ERROR will + * make this function return zero and set the error code to + * PHYSFS_ERR_APP_CALLBACK. + * + * \sa PHYSFS_EnumerateCallback + * \sa PHYSFS_enumerateFiles + */ +PHYSFS_DECL int PHYSFS_enumerate(const char *dir, PHYSFS_EnumerateCallback c, + void *d); + + +/** + * \fn int PHYSFS_unmount(const char *oldDir) + * \brief Remove a directory or archive from the search path. + * + * This is functionally equivalent to PHYSFS_removeFromSearchPath(), but that + * function is deprecated to keep the vocabulary paired with PHYSFS_mount(). + * + * This must be a (case-sensitive) match to a dir or archive already in the + * search path, specified in platform-dependent notation. + * + * This call will fail (and fail to remove from the path) if the element still + * has files open in it. + * + * \warning This function wants the path to the archive or directory that was + * mounted (the same string used for the "newDir" argument of + * PHYSFS_addToSearchPath or any of the mount functions), not the + * path where it is mounted in the tree (the "mountPoint" argument + * to any of the mount functions). + * + * \param oldDir dir/archive to remove. + * \return nonzero on success, zero on failure. Use + * PHYSFS_getLastErrorCode() to obtain the specific error. + * + * \sa PHYSFS_getSearchPath + * \sa PHYSFS_mount + */ +PHYSFS_DECL int PHYSFS_unmount(const char *oldDir); + + +/** + * \fn const PHYSFS_Allocator *PHYSFS_getAllocator(void) + * \brief Discover the current allocator. + * + * (This is for limited, hardcore use. If you don't immediately see a need + * for it, you can probably ignore this forever.) + * + * This function exposes the function pointers that make up the currently used + * allocator. This can be useful for apps that want to access PhysicsFS's + * internal, default allocation routines, as well as for external code that + * wants to share the same allocator, even if the application specified their + * own. + * + * This call is only valid between PHYSFS_init() and PHYSFS_deinit() calls; + * it will return NULL if the library isn't initialized. As we can't + * guarantee the state of the internal allocators unless the library is + * initialized, you shouldn't use any allocator returned here after a call + * to PHYSFS_deinit(). + * + * Do not call the returned allocator's Init() or Deinit() methods under any + * circumstances. + * + * If you aren't immediately sure what to do with this function, you can + * safely ignore it altogether. + * + * \return Current allocator, as set by PHYSFS_setAllocator(), or PhysicsFS's + * internal, default allocator if no application defined allocator + * is currently set. Will return NULL if the library is not + * initialized. + * + * \sa PHYSFS_Allocator + * \sa PHYSFS_setAllocator + */ +PHYSFS_DECL const PHYSFS_Allocator *PHYSFS_getAllocator(void); + + +/** + * \enum PHYSFS_FileType + * \brief Type of a File + * + * Possible types of a file. + * + * \sa PHYSFS_stat + */ +typedef enum PHYSFS_FileType +{ + PHYSFS_FILETYPE_REGULAR, /**< a normal file */ + PHYSFS_FILETYPE_DIRECTORY, /**< a directory */ + PHYSFS_FILETYPE_SYMLINK, /**< a symlink */ + PHYSFS_FILETYPE_OTHER /**< something completely different like a device */ +} PHYSFS_FileType; + +/** + * \struct PHYSFS_Stat + * \brief Meta data for a file or directory + * + * Container for various meta data about a file in the virtual file system. + * PHYSFS_stat() uses this structure for returning the information. The time + * data will be either the number of seconds since the Unix epoch (midnight, + * Jan 1, 1970), or -1 if the information isn't available or applicable. + * The (filesize) field is measured in bytes. + * The (readonly) field tells you whether the archive thinks a file is + * not writable, but tends to be only an estimate (for example, your write + * dir might overlap with a .zip file, meaning you _can_ successfully open + * that path for writing, as it gets created elsewhere. + * + * \sa PHYSFS_stat + * \sa PHYSFS_FileType + */ +typedef struct PHYSFS_Stat +{ + PHYSFS_sint64 filesize; /**< size in bytes, -1 for non-files and unknown */ + PHYSFS_sint64 modtime; /**< last modification time */ + PHYSFS_sint64 createtime; /**< like modtime, but for file creation time */ + PHYSFS_sint64 accesstime; /**< like modtime, but for file access time */ + PHYSFS_FileType filetype; /**< File? Directory? Symlink? */ + int readonly; /**< non-zero if read only, zero if writable. */ +} PHYSFS_Stat; + +/** + * \fn int PHYSFS_stat(const char *fname, PHYSFS_Stat *stat) + * \brief Get various information about a directory or a file. + * + * Obtain various information about a file or directory from the meta data. + * + * This function will never follow symbolic links. If you haven't enabled + * symlinks with PHYSFS_permitSymbolicLinks(), stat'ing a symlink will be + * treated like stat'ing a non-existant file. If symlinks are enabled, + * stat'ing a symlink will give you information on the link itself and not + * what it points to. + * + * \param fname filename to check, in platform-indepedent notation. + * \param stat pointer to structure to fill in with data about (fname). + * \return non-zero on success, zero on failure. On failure, (stat)'s + * contents are undefined. + * + * \sa PHYSFS_Stat + */ +PHYSFS_DECL int PHYSFS_stat(const char *fname, PHYSFS_Stat *stat); + + +/** + * \fn void PHYSFS_utf8FromUtf16(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len) + * \brief Convert a UTF-16 string to a UTF-8 string. + * + * \warning This function will not report an error if there are invalid UTF-16 + * sequences in the source string. It will replace them with a '?' + * character and continue on. + * + * UTF-16 strings are 16-bits per character (except some chars, which are + * 32-bits): \c TCHAR on Windows, when building with Unicode support. Modern + * Windows releases use UTF-16. Windows releases before 2000 used TCHAR, but + * only handled UCS-2. UTF-16 _is_ UCS-2, except for the characters that + * are 4 bytes, which aren't representable in UCS-2 at all anyhow. If you + * aren't sure, you should be using UTF-16 at this point on Windows. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is double the size of the source buffer. + * UTF-8 never uses more than 32-bits per character, so while it may shrink + * a UTF-16 string, it may also expand it. + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UTF-8 + * sequence at the end. If the buffer length is 0, this function does nothing. + * + * \param src Null-terminated source string in UTF-16 format. + * \param dst Buffer to store converted UTF-8 string. + * \param len Size, in bytes, of destination buffer. + */ +PHYSFS_DECL void PHYSFS_utf8FromUtf16(const PHYSFS_uint16 *src, char *dst, + PHYSFS_uint64 len); + +/** + * \fn PHYSFS_utf8ToUtf16(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len) + * \brief Convert a UTF-8 string to a UTF-16 string. + * + * \warning This function will not report an error if there are invalid UTF-8 + * sequences in the source string. It will replace them with a '?' + * character and continue on. + * + * UTF-16 strings are 16-bits per character (except some chars, which are + * 32-bits): \c TCHAR on Windows, when building with Unicode support. Modern + * Windows releases use UTF-16. Windows releases before 2000 used TCHAR, but + * only handled UCS-2. UTF-16 _is_ UCS-2, except for the characters that + * are 4 bytes, which aren't representable in UCS-2 at all anyhow. If you + * aren't sure, you should be using UTF-16 at this point on Windows. + * + * To ensure that the destination buffer is large enough for the conversion, + * please allocate a buffer that is double the size of the source buffer. + * UTF-8 uses from one to four bytes per character, but UTF-16 always uses + * two to four, so an entirely low-ASCII string will double in size! The + * UTF-16 characters that would take four bytes also take four bytes in UTF-8, + * so you don't need to allocate 4x the space just in case: double will do. + * + * Strings that don't fit in the destination buffer will be truncated, but + * will always be null-terminated and never have an incomplete UTF-16 + * surrogate pair at the end. If the buffer length is 0, this function does + * nothing. + * + * \param src Null-terminated source string in UTF-8 format. + * \param dst Buffer to store converted UTF-16 string. + * \param len Size, in bytes, of destination buffer. + * + * \sa PHYSFS_utf8ToUtf16 + */ +PHYSFS_DECL void PHYSFS_utf8ToUtf16(const char *src, PHYSFS_uint16 *dst, + PHYSFS_uint64 len); + + +/** + * \fn PHYSFS_sint64 PHYSFS_readBytes(PHYSFS_File *handle, void *buffer, PHYSFS_uint64 len) + * \brief Read bytes from a PhysicsFS filehandle + * + * The file must be opened for reading. + * + * \param handle handle returned from PHYSFS_openRead(). + * \param buffer buffer of at least (len) bytes to store read data into. + * \param len number of bytes being read from (handle). + * \return number of bytes read. This may be less than (len); this does not + * signify an error, necessarily (a short read may mean EOF). + * PHYSFS_getLastErrorCode() can shed light on the reason this might + * be < (len), as can PHYSFS_eof(). -1 if complete failure. + * + * \sa PHYSFS_eof + */ +PHYSFS_DECL PHYSFS_sint64 PHYSFS_readBytes(PHYSFS_File *handle, void *buffer, + PHYSFS_uint64 len); + +/** + * \fn PHYSFS_sint64 PHYSFS_writeBytes(PHYSFS_File *handle, const void *buffer, PHYSFS_uint64 len) + * \brief Write data to a PhysicsFS filehandle + * + * The file must be opened for writing. + * + * Please note that while (len) is an unsigned 64-bit integer, you are limited + * to 63 bits (9223372036854775807 bytes), so we can return a negative value + * on error. If length is greater than 0x7FFFFFFFFFFFFFFF, this function will + * immediately fail. For systems without a 64-bit datatype, you are limited + * to 31 bits (0x7FFFFFFF, or 2147483647 bytes). We trust most things won't + * need to do multiple gigabytes of i/o in one call anyhow, but why limit + * things? + * + * \param handle retval from PHYSFS_openWrite() or PHYSFS_openAppend(). + * \param buffer buffer of (len) bytes to write to (handle). + * \param len number of bytes being written to (handle). + * \return number of bytes written. This may be less than (len); in the case + * of an error, the system may try to write as many bytes as possible, + * so an incomplete write might occur. PHYSFS_getLastErrorCode() can + * shed light on the reason this might be < (len). -1 if complete + * failure. + */ +PHYSFS_DECL PHYSFS_sint64 PHYSFS_writeBytes(PHYSFS_File *handle, + const void *buffer, + PHYSFS_uint64 len); + + +/** + * \struct PHYSFS_Io + * \brief An abstract i/o interface. + * + * \warning This is advanced, hardcore stuff. You don't need this unless you + * really know what you're doing. Most apps will not need this. + * + * Historically, PhysicsFS provided access to the physical filesystem and + * archives within that filesystem. However, sometimes you need more power + * than this. Perhaps you need to provide an archive that is entirely + * contained in RAM, or you need to bridge some other file i/o API to + * PhysicsFS, or you need to translate the bits (perhaps you have a + * a standard .zip file that's encrypted, and you need to decrypt on the fly + * for the unsuspecting zip archiver). + * + * A PHYSFS_Io is the interface that Archivers use to get archive data. + * Historically, this has mapped to file i/o to the physical filesystem, but + * as of PhysicsFS 2.1, applications can provide their own i/o implementations + * at runtime. + * + * This interface isn't necessarily a good universal fit for i/o. There are a + * few requirements of note: + * + * - They only do blocking i/o (at least, for now). + * - They need to be able to duplicate. If you have a file handle from + * fopen(), you need to be able to create a unique clone of it (so we + * have two handles to the same file that can both seek/read/etc without + * stepping on each other). + * - They need to know the size of their entire data set. + * - They need to be able to seek and rewind on demand. + * + * ...in short, you're probably not going to write an HTTP implementation. + * + * Thread safety: PHYSFS_Io implementations are not guaranteed to be thread + * safe in themselves. Under the hood where PhysicsFS uses them, the library + * provides its own locks. If you plan to use them directly from separate + * threads, you should either use mutexes to protect them, or don't use the + * same PHYSFS_Io from two threads at the same time. + * + * \sa PHYSFS_mountIo + */ +typedef struct PHYSFS_Io +{ + /** + * \brief Binary compatibility information. + * + * This must be set to zero at this time. Future versions of this + * struct will increment this field, so we know what a given + * implementation supports. We'll presumably keep supporting older + * versions as we offer new features, though. + */ + PHYSFS_uint32 version; + + /** + * \brief Instance data for this struct. + * + * Each instance has a pointer associated with it that can be used to + * store anything it likes. This pointer is per-instance of the stream, + * so presumably it will change when calling duplicate(). This can be + * deallocated during the destroy() method. + */ + void *opaque; + + /** + * \brief Read more data. + * + * Read (len) bytes from the interface, at the current i/o position, and + * store them in (buffer). The current i/o position should move ahead + * by the number of bytes successfully read. + * + * You don't have to implement this; set it to NULL if not implemented. + * This will only be used if the file is opened for reading. If set to + * NULL, a default implementation that immediately reports failure will + * be used. + * + * \param io The i/o instance to read from. + * \param buf The buffer to store data into. It must be at least + * (len) bytes long and can't be NULL. + * \param len The number of bytes to read from the interface. + * \return number of bytes read from file, 0 on EOF, -1 if complete + * failure. + */ + PHYSFS_sint64 (*read)(struct PHYSFS_Io *io, void *buf, PHYSFS_uint64 len); + + /** + * \brief Write more data. + * + * Write (len) bytes from (buffer) to the interface at the current i/o + * position. The current i/o position should move ahead by the number of + * bytes successfully written. + * + * You don't have to implement this; set it to NULL if not implemented. + * This will only be used if the file is opened for writing. If set to + * NULL, a default implementation that immediately reports failure will + * be used. + * + * You are allowed to buffer; a write can succeed here and then later + * fail when flushing. Note that PHYSFS_setBuffer() may be operating a + * level above your i/o, so you should usually not implement your + * own buffering routines. + * + * \param io The i/o instance to write to. + * \param buffer The buffer to read data from. It must be at least + * (len) bytes long and can't be NULL. + * \param len The number of bytes to read from (buffer). + * \return number of bytes written to file, -1 if complete failure. + */ + PHYSFS_sint64 (*write)(struct PHYSFS_Io *io, const void *buffer, + PHYSFS_uint64 len); + + /** + * \brief Move i/o position to a given byte offset from start. + * + * This method moves the i/o position, so the next read/write will + * be of the byte at (offset) offset. Seeks past the end of file should + * be treated as an error condition. + * + * \param io The i/o instance to seek. + * \param offset The new byte offset for the i/o position. + * \return non-zero on success, zero on error. + */ + int (*seek)(struct PHYSFS_Io *io, PHYSFS_uint64 offset); + + /** + * \brief Report current i/o position. + * + * Return bytes offset, or -1 if you aren't able to determine. A failure + * will almost certainly be fatal to further use of this stream, so you + * may not leave this unimplemented. + * + * \param io The i/o instance to query. + * \return The current byte offset for the i/o position, -1 if unknown. + */ + PHYSFS_sint64 (*tell)(struct PHYSFS_Io *io); + + /** + * \brief Determine size of the i/o instance's dataset. + * + * Return number of bytes available in the file, or -1 if you + * aren't able to determine. A failure will almost certainly be fatal + * to further use of this stream, so you may not leave this unimplemented. + * + * \param io The i/o instance to query. + * \return Total size, in bytes, of the dataset. + */ + PHYSFS_sint64 (*length)(struct PHYSFS_Io *io); + + /** + * \brief Duplicate this i/o instance. + * + * This needs to result in a full copy of this PHYSFS_Io, that can live + * completely independently. The copy needs to be able to perform all + * its operations without altering the original, including either object + * being destroyed separately (so, for example: they can't share a file + * handle; they each need their own). + * + * If you can't duplicate a handle, it's legal to return NULL, but you + * almost certainly need this functionality if you want to use this to + * PHYSFS_Io to back an archive. + * + * \param io The i/o instance to duplicate. + * \return A new value for a stream's (opaque) field, or NULL on error. + */ + struct PHYSFS_Io *(*duplicate)(struct PHYSFS_Io *io); + + /** + * \brief Flush resources to media, or wherever. + * + * This is the chance to report failure for writes that had claimed + * success earlier, but still had a chance to actually fail. This method + * can be NULL if flushing isn't necessary. + * + * This function may be called before destroy(), as it can report failure + * and destroy() can not. It may be called at other times, too. + * + * \param io The i/o instance to flush. + * \return Zero on error, non-zero on success. + */ + int (*flush)(struct PHYSFS_Io *io); + + /** + * \brief Cleanup and deallocate i/o instance. + * + * Free associated resources, including (opaque) if applicable. + * + * This function must always succeed: as such, it returns void. The + * system may call your flush() method before this. You may report + * failure there if necessary. This method may still be called if + * flush() fails, in which case you'll have to abandon unflushed data + * and other failing conditions and clean up. + * + * Once this method is called for a given instance, the system will assume + * it is unsafe to touch that instance again and will discard any + * references to it. + * + * \param s The i/o instance to destroy. + */ + void (*destroy)(struct PHYSFS_Io *io); +} PHYSFS_Io; + + +/** + * \fn int PHYSFS_mountIo(PHYSFS_Io *io, const char *newDir, const char *mountPoint, int appendToPath) + * \brief Add an archive, built on a PHYSFS_Io, to the search path. + * + * \warning Unless you have some special, low-level need, you should be using + * PHYSFS_mount() instead of this. + * + * This function operates just like PHYSFS_mount(), but takes a PHYSFS_Io + * instead of a pathname. Behind the scenes, PHYSFS_mount() calls this + * function with a physical-filesystem-based PHYSFS_Io. + * + * (newDir) must be a unique string to identify this archive. It is used + * to optimize archiver selection (if you name it XXXXX.zip, we might try + * the ZIP archiver first, for example, or directly choose an archiver that + * can only trust the data is valid by filename extension). It doesn't + * need to refer to a real file at all. If the filename extension isn't + * helpful, the system will try every archiver until one works or none + * of them do. This filename must be unique, as the system won't allow you + * to have two archives with the same name. + * + * (io) must remain until the archive is unmounted. When the archive is + * unmounted, the system will call (io)->destroy(io), which will give you + * a chance to free your resources. + * + * If this function fails, (io)->destroy(io) is not called. + * + * \param io i/o instance for archive to add to the path. + * \param newDir Filename that can represent this stream. + * \param mountPoint Location in the interpolated tree that this archive + * will be "mounted", in platform-independent notation. + * NULL or "" is equivalent to "/". + * \param appendToPath nonzero to append to search path, zero to prepend. + * \return nonzero if added to path, zero on failure (bogus archive, stream + * i/o issue, etc). Use PHYSFS_getLastErrorCode() to obtain + * the specific error. + * + * \sa PHYSFS_unmount + * \sa PHYSFS_getSearchPath + * \sa PHYSFS_getMountPoint + */ +PHYSFS_DECL int PHYSFS_mountIo(PHYSFS_Io *io, const char *newDir, + const char *mountPoint, int appendToPath); + + +/** + * \fn int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len, void (*del)(void *), const char *newDir, const char *mountPoint, int appendToPath) + * \brief Add an archive, contained in a memory buffer, to the search path. + * + * \warning Unless you have some special, low-level need, you should be using + * PHYSFS_mount() instead of this. + * + * This function operates just like PHYSFS_mount(), but takes a memory buffer + * instead of a pathname. This buffer contains all the data of the archive, + * and is used instead of a real file in the physical filesystem. + * + * (newDir) must be a unique string to identify this archive. It is used + * to optimize archiver selection (if you name it XXXXX.zip, we might try + * the ZIP archiver first, for example, or directly choose an archiver that + * can only trust the data is valid by filename extension). It doesn't + * need to refer to a real file at all. If the filename extension isn't + * helpful, the system will try every archiver until one works or none + * of them do. This filename must be unique, as the system won't allow you + * to have two archives with the same name. + * + * (ptr) must remain until the archive is unmounted. When the archive is + * unmounted, the system will call (del)(ptr), which will notify you that + * the system is done with the buffer, and give you a chance to free your + * resources. (del) can be NULL, in which case the system will make no + * attempt to free the buffer. + * + * If this function fails, (del) is not called. + * + * \param buf Address of the memory buffer containing the archive data. + * \param len Size of memory buffer, in bytes. + * \param del A callback that triggers upon unmount. Can be NULL. + * \param newDir Filename that can represent this stream. + * \param mountPoint Location in the interpolated tree that this archive + * will be "mounted", in platform-independent notation. + * NULL or "" is equivalent to "/". + * \param appendToPath nonzero to append to search path, zero to prepend. + * \return nonzero if added to path, zero on failure (bogus archive, etc). + * Use PHYSFS_getLastErrorCode() to obtain the specific error. + * + * \sa PHYSFS_unmount + * \sa PHYSFS_getSearchPath + * \sa PHYSFS_getMountPoint + */ +PHYSFS_DECL int PHYSFS_mountMemory(const void *buf, PHYSFS_uint64 len, + void (*del)(void *), const char *newDir, + const char *mountPoint, int appendToPath); + + +/** + * \fn int PHYSFS_mountHandle(PHYSFS_File *file, const char *newDir, const char *mountPoint, int appendToPath) + * \brief Add an archive, contained in a PHYSFS_File handle, to the search path. + * + * \warning Unless you have some special, low-level need, you should be using + * PHYSFS_mount() instead of this. + * + * \warning Archives-in-archives may be very slow! While a PHYSFS_File can + * seek even when the data is compressed, it may do so by rewinding + * to the start and decompressing everything before the seek point. + * Normal archive usage may do a lot of seeking behind the scenes. + * As such, you might find normal archive usage extremely painful + * if mounted this way. Plan accordingly: if you, say, have a + * self-extracting .zip file, and want to mount something in it, + * compress the contents of the inner archive and make sure the outer + * .zip file doesn't compress the inner archive too. + * + * This function operates just like PHYSFS_mount(), but takes a PHYSFS_File + * handle instead of a pathname. This handle contains all the data of the + * archive, and is used instead of a real file in the physical filesystem. + * The PHYSFS_File may be backed by a real file in the physical filesystem, + * but isn't necessarily. The most popular use for this is likely to mount + * archives stored inside other archives. + * + * (newDir) must be a unique string to identify this archive. It is used + * to optimize archiver selection (if you name it XXXXX.zip, we might try + * the ZIP archiver first, for example, or directly choose an archiver that + * can only trust the data is valid by filename extension). It doesn't + * need to refer to a real file at all. If the filename extension isn't + * helpful, the system will try every archiver until one works or none + * of them do. This filename must be unique, as the system won't allow you + * to have two archives with the same name. + * + * (file) must remain until the archive is unmounted. When the archive is + * unmounted, the system will call PHYSFS_close(file). If you need this + * handle to survive, you will have to wrap this in a PHYSFS_Io and use + * PHYSFS_mountIo() instead. + * + * If this function fails, PHYSFS_close(file) is not called. + * + * \param file The PHYSFS_File handle containing archive data. + * \param newDir Filename that can represent this stream. + * \param mountPoint Location in the interpolated tree that this archive + * will be "mounted", in platform-independent notation. + * NULL or "" is equivalent to "/". + * \param appendToPath nonzero to append to search path, zero to prepend. + * \return nonzero if added to path, zero on failure (bogus archive, etc). + * Use PHYSFS_getLastErrorCode() to obtain the specific error. + * + * \sa PHYSFS_unmount + * \sa PHYSFS_getSearchPath + * \sa PHYSFS_getMountPoint + */ +PHYSFS_DECL int PHYSFS_mountHandle(PHYSFS_File *file, const char *newDir, + const char *mountPoint, int appendToPath); + + +/** + * \enum PHYSFS_ErrorCode + * \brief Values that represent specific causes of failure. + * + * Most of the time, you should only concern yourself with whether a given + * operation failed or not, but there may be occasions where you plan to + * handle a specific failure case gracefully, so we provide specific error + * codes. + * + * Most of these errors are a little vague, and most aren't things you can + * fix...if there's a permission error, for example, all you can really do + * is pass that information on to the user and let them figure out how to + * handle it. In most these cases, your program should only care that it + * failed to accomplish its goals, and not care specifically why. + * + * \sa PHYSFS_getLastErrorCode + * \sa PHYSFS_getErrorByCode + */ +typedef enum PHYSFS_ErrorCode +{ + PHYSFS_ERR_OK, /**< Success; no error. */ + PHYSFS_ERR_OTHER_ERROR, /**< Error not otherwise covered here. */ + PHYSFS_ERR_OUT_OF_MEMORY, /**< Memory allocation failed. */ + PHYSFS_ERR_NOT_INITIALIZED, /**< PhysicsFS is not initialized. */ + PHYSFS_ERR_IS_INITIALIZED, /**< PhysicsFS is already initialized. */ + PHYSFS_ERR_ARGV0_IS_NULL, /**< Needed argv[0], but it is NULL. */ + PHYSFS_ERR_UNSUPPORTED, /**< Operation or feature unsupported. */ + PHYSFS_ERR_PAST_EOF, /**< Attempted to access past end of file. */ + PHYSFS_ERR_FILES_STILL_OPEN, /**< Files still open. */ + PHYSFS_ERR_INVALID_ARGUMENT, /**< Bad parameter passed to an function. */ + PHYSFS_ERR_NOT_MOUNTED, /**< Requested archive/dir not mounted. */ + PHYSFS_ERR_NOT_FOUND, /**< File (or whatever) not found. */ + PHYSFS_ERR_SYMLINK_FORBIDDEN,/**< Symlink seen when not permitted. */ + PHYSFS_ERR_NO_WRITE_DIR, /**< No write dir has been specified. */ + PHYSFS_ERR_OPEN_FOR_READING, /**< Wrote to a file opened for reading. */ + PHYSFS_ERR_OPEN_FOR_WRITING, /**< Read from a file opened for writing. */ + PHYSFS_ERR_NOT_A_FILE, /**< Needed a file, got a directory (etc). */ + PHYSFS_ERR_READ_ONLY, /**< Wrote to a read-only filesystem. */ + PHYSFS_ERR_CORRUPT, /**< Corrupted data encountered. */ + PHYSFS_ERR_SYMLINK_LOOP, /**< Infinite symbolic link loop. */ + PHYSFS_ERR_IO, /**< i/o error (hardware failure, etc). */ + PHYSFS_ERR_PERMISSION, /**< Permission denied. */ + PHYSFS_ERR_NO_SPACE, /**< No space (disk full, over quota, etc) */ + PHYSFS_ERR_BAD_FILENAME, /**< Filename is bogus/insecure. */ + PHYSFS_ERR_BUSY, /**< Tried to modify a file the OS needs. */ + PHYSFS_ERR_DIR_NOT_EMPTY, /**< Tried to delete dir with files in it. */ + PHYSFS_ERR_OS_ERROR, /**< Unspecified OS-level error. */ + PHYSFS_ERR_DUPLICATE, /**< Duplicate entry. */ + PHYSFS_ERR_BAD_PASSWORD, /**< Bad password. */ + PHYSFS_ERR_APP_CALLBACK /**< Application callback reported error. */ +} PHYSFS_ErrorCode; + + +/** + * \fn PHYSFS_ErrorCode PHYSFS_getLastErrorCode(void) + * \brief Get machine-readable error information. + * + * Get the last PhysicsFS error message as an integer value. This will return + * PHYSFS_ERR_OK if there's been no error since the last call to this + * function. Each thread has a unique error state associated with it, but + * each time a new error message is set, it will overwrite the previous one + * associated with that thread. It is safe to call this function at anytime, + * even before PHYSFS_init(). + * + * PHYSFS_getLastError() and PHYSFS_getLastErrorCode() both reset the same + * thread-specific error state. Calling one will wipe out the other's + * data. If you need both, call PHYSFS_getLastErrorCode(), then pass that + * value to PHYSFS_getErrorByCode(). + * + * Generally, applications should only concern themselves with whether a + * given function failed; however, if you require more specifics, you can + * try this function to glean information, if there's some specific problem + * you're expecting and plan to handle. But with most things that involve + * file systems, the best course of action is usually to give up, report the + * problem to the user, and let them figure out what should be done about it. + * For that, you might prefer PHYSFS_getErrorByCode() instead. + * + * \return Enumeration value that represents last reported error. + * + * \sa PHYSFS_getErrorByCode + */ +PHYSFS_DECL PHYSFS_ErrorCode PHYSFS_getLastErrorCode(void); + + +/** + * \fn const char *PHYSFS_getErrorByCode(PHYSFS_ErrorCode code) + * \brief Get human-readable description string for a given error code. + * + * Get a static string, in UTF-8 format, that represents an English + * description of a given error code. + * + * This string is guaranteed to never change (although we may add new strings + * for new error codes in later versions of PhysicsFS), so you can use it + * for keying a localization dictionary. + * + * It is safe to call this function at anytime, even before PHYSFS_init(). + * + * These strings are meant to be passed on directly to the user. + * Generally, applications should only concern themselves with whether a + * given function failed, but not care about the specifics much. + * + * Do not attempt to free the returned strings; they are read-only and you + * don't own their memory pages. + * + * \param code Error code to convert to a string. + * \return READ ONLY string of requested error message, NULL if this + * is not a valid PhysicsFS error code. Always check for NULL if + * you might be looking up an error code that didn't exist in an + * earlier version of PhysicsFS. + * + * \sa PHYSFS_getLastErrorCode + */ +PHYSFS_DECL const char *PHYSFS_getErrorByCode(PHYSFS_ErrorCode code); + +/** + * \fn void PHYSFS_setErrorCode(PHYSFS_ErrorCode code) + * \brief Set the current thread's error code. + * + * This lets you set the value that will be returned by the next call to + * PHYSFS_getLastErrorCode(). This will replace any existing error code, + * whether set by your application or internally by PhysicsFS. + * + * Error codes are stored per-thread; what you set here will not be + * accessible to another thread. + * + * Any call into PhysicsFS may change the current error code, so any code you + * set here is somewhat fragile, and thus you shouldn't build any serious + * error reporting framework on this function. The primary goal of this + * function is to allow PHYSFS_Io implementations to set the error state, + * which generally will be passed back to your application when PhysicsFS + * makes a PHYSFS_Io call that fails internally. + * + * This function doesn't care if the error code is a value known to PhysicsFS + * or not (but PHYSFS_getErrorByCode() will return NULL for unknown values). + * The value will be reported unmolested by PHYSFS_getLastErrorCode(). + * + * \param code Error code to become the current thread's new error state. + * + * \sa PHYSFS_getLastErrorCode + * \sa PHYSFS_getErrorByCode + */ +PHYSFS_DECL void PHYSFS_setErrorCode(PHYSFS_ErrorCode code); + + +/** + * \fn const char *PHYSFS_getPrefDir(const char *org, const char *app) + * \brief Get the user-and-app-specific path where files can be written. + * + * Helper function. + * + * Get the "pref dir". This is meant to be where users can write personal + * files (preferences and save games, etc) that are specific to your + * application. This directory is unique per user, per application. + * + * This function will decide the appropriate location in the native filesystem, + * create the directory if necessary, and return a string in + * platform-dependent notation, suitable for passing to PHYSFS_setWriteDir(). + * + * On Windows, this might look like: + * "C:\\Users\\bob\\AppData\\Roaming\\My Company\\My Program Name" + * + * On Linux, this might look like: + * "/home/bob/.local/share/My Program Name" + * + * On Mac OS X, this might look like: + * "/Users/bob/Library/Application Support/My Program Name" + * + * (etc.) + * + * You should probably use the pref dir for your write dir, and also put it + * near the beginning of your search path. Older versions of PhysicsFS + * offered only PHYSFS_getUserDir() and left you to figure out where the + * files should go under that tree. This finds the correct location + * for whatever platform, which not only changes between operating systems, + * but also versions of the same operating system. + * + * You specify the name of your organization (if it's not a real organization, + * your name or an Internet domain you own might do) and the name of your + * application. These should be proper names. + * + * Both the (org) and (app) strings may become part of a directory name, so + * please follow these rules: + * + * - Try to use the same org string (including case-sensitivity) for + * all your applications that use this function. + * - Always use a unique app string for each one, and make sure it never + * changes for an app once you've decided on it. + * - Unicode characters are legal, as long as it's UTF-8 encoded, but... + * - ...only use letters, numbers, and spaces. Avoid punctuation like + * "Game Name 2: Bad Guy's Revenge!" ... "Game Name 2" is sufficient. + * + * The pointer returned by this function remains valid until you call this + * function again, or call PHYSFS_deinit(). This is not necessarily a fast + * call, though, so you should call this once at startup and copy the string + * if you need it. + * + * You should assume the path returned by this function is the only safe + * place to write files (and that PHYSFS_getUserDir() and PHYSFS_getBaseDir(), + * while they might be writable, or even parents of the returned path, aren't + * where you should be writing things). + * + * \param org The name of your organization. + * \param app The name of your application. + * \return READ ONLY string of user dir in platform-dependent notation. NULL + * if there's a problem (creating directory failed, etc). + * + * \sa PHYSFS_getBaseDir + * \sa PHYSFS_getUserDir + */ +PHYSFS_DECL const char *PHYSFS_getPrefDir(const char *org, const char *app); + + +/** + * \struct PHYSFS_Archiver + * \brief Abstract interface to provide support for user-defined archives. + * + * \warning This is advanced, hardcore stuff. You don't need this unless you + * really know what you're doing. Most apps will not need this. + * + * Historically, PhysicsFS provided a means to mount various archive file + * formats, and physical directories in the native filesystem. However, + * applications have been limited to the file formats provided by the + * library. This interface allows an application to provide their own + * archive file types. + * + * Conceptually, a PHYSFS_Archiver provides directory entries, while + * PHYSFS_Io provides data streams for those directory entries. The most + * obvious use of PHYSFS_Archiver is to provide support for an archive + * file type that isn't provided by PhysicsFS directly: perhaps some + * proprietary format that only your application needs to understand. + * + * Internally, all the built-in archive support uses this interface, so the + * best examples for building a PHYSFS_Archiver is the source code to + * PhysicsFS itself. + * + * An archiver is added to the system with PHYSFS_registerArchiver(), and then + * it will be available for use automatically with PHYSFS_mount(); if a + * given archive can be handled with your archiver, it will be given control + * as appropriate. + * + * These methods deal with dir handles. You have one instance of your + * archiver, and it generates a unique, opaque handle for each opened + * archive in its openArchive() method. Since the lifetime of an Archiver + * (not an archive) is generally the entire lifetime of the process, and it's + * assumed to be a singleton, we do not provide any instance data for the + * archiver itself; the app can just use some static variables if necessary. + * + * Symlinks should always be followed (except in stat()); PhysicsFS will + * use the stat() method to check for symlinks and make a judgement on + * whether to continue to call other methods based on that. + * + * Archivers, when necessary, should set the PhysicsFS error state with + * PHYSFS_setErrorCode() before returning. PhysicsFS will pass these errors + * back to the application unmolested in most cases. + * + * Thread safety: PHYSFS_Archiver implementations are not guaranteed to be + * thread safe in themselves. PhysicsFS provides thread safety when it calls + * into a given archiver inside the library, but it does not promise that + * using the same PHYSFS_File from two threads at once is thread-safe; as + * such, your PHYSFS_Archiver can assume that locking is handled for you + * so long as the PHYSFS_Io you return from PHYSFS_open* doesn't change any + * of your Archiver state, as the PHYSFS_Io won't be as aggressively + * protected. + * + * \sa PHYSFS_registerArchiver + * \sa PHYSFS_deregisterArchiver + * \sa PHYSFS_supportedArchiveTypes + */ +typedef struct PHYSFS_Archiver +{ + /** + * \brief Binary compatibility information. + * + * This must be set to zero at this time. Future versions of this + * struct will increment this field, so we know what a given + * implementation supports. We'll presumably keep supporting older + * versions as we offer new features, though. + */ + PHYSFS_uint32 version; + + /** + * \brief Basic info about this archiver. + * + * This is used to identify your archive, and is returned in + * PHYSFS_supportedArchiveTypes(). + */ + PHYSFS_ArchiveInfo info; + + /** + * \brief Open an archive provided by (io). + * + * This is where resources are allocated and data is parsed when mounting + * an archive. + * (name) is a filename associated with (io), but doesn't necessarily + * map to anything, let alone a real filename. This possibly- + * meaningless name is in platform-dependent notation. + * (forWrite) is non-zero if this is to be used for + * the write directory, and zero if this is to be used for an + * element of the search path. + * (claimed) should be set to 1 if this is definitely an archive your + * archiver implementation can handle, even if it fails. We use to + * decide if we should stop trying other archivers if you fail to open + * it. For example: the .zip archiver will set this to 1 for something + * that's got a .zip file signature, even if it failed because the file + * was also truncated. No sense in trying other archivers here, we + * already tried to handle it with the appropriate implementation!. + * Return NULL on failure and set (claimed) appropriately. If no archiver + * opened the archive or set (claimed), PHYSFS_mount() will report + * PHYSFS_ERR_UNSUPPORTED. Otherwise, it will report the error from the + * archiver that claimed the data through (claimed). + * Return non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later calls. + */ + void *(*openArchive)(PHYSFS_Io *io, const char *name, + int forWrite, int *claimed); + + /** + * \brief List all files in (dirname). + * + * Each file is passed to (cb), where a copy is made if appropriate, so + * you can dispose of it upon return from the callback. (dirname) is in + * platform-independent notation. + * If you have a failure, call PHYSFS_SetErrorCode() with whatever code + * seem appropriate and return PHYSFS_ENUM_ERROR. + * If the callback returns PHYSFS_ENUM_ERROR, please call + * PHYSFS_SetErrorCode(PHYSFS_ERR_APP_CALLBACK) and then return + * PHYSFS_ENUM_ERROR as well. Don't call the callback again in any + * circumstances. + * If the callback returns PHYSFS_ENUM_STOP, stop enumerating and return + * PHYSFS_ENUM_STOP as well. Don't call the callback again in any + * circumstances. Don't set an error code in this case. + * Callbacks are only supposed to return a value from + * PHYSFS_EnumerateCallbackResult. Any other result has undefined + * behavior. + * As long as the callback returned PHYSFS_ENUM_OK and you haven't + * experienced any errors of your own, keep enumerating until you're done + * and then return PHYSFS_ENUM_OK without setting an error code. + * + * \warning PHYSFS_enumerate returns zero or non-zero (success or failure), + * so be aware this function pointer returns different values! + */ + PHYSFS_EnumerateCallbackResult (*enumerate)(void *opaque, + const char *dirname, PHYSFS_EnumerateCallback cb, + const char *origdir, void *callbackdata); + + /** + * \brief Open a file in this archive for reading. + * + * This filename, (fnm), is in platform-independent notation. + * Fail if the file does not exist. + * Returns NULL on failure, and calls PHYSFS_setErrorCode(). + * Returns non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later file calls. + */ + PHYSFS_Io *(*openRead)(void *opaque, const char *fnm); + + /** + * \brief Open a file in this archive for writing. + * + * If the file does not exist, it should be created. If it exists, + * it should be truncated to zero bytes. The writing offset should + * be the start of the file. + * If the archive is read-only, this operation should fail. + * This filename is in platform-independent notation. + * Returns NULL on failure, and calls PHYSFS_setErrorCode(). + * Returns non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later file calls. + */ + PHYSFS_Io *(*openWrite)(void *opaque, const char *filename); + + /** + * \brief Open a file in this archive for appending. + * + * If the file does not exist, it should be created. The writing + * offset should be the end of the file. + * If the archive is read-only, this operation should fail. + * This filename is in platform-independent notation. + * Returns NULL on failure, and calls PHYSFS_setErrorCode(). + * Returns non-NULL on success. The pointer returned will be + * passed as the "opaque" parameter for later file calls. + */ + PHYSFS_Io *(*openAppend)(void *opaque, const char *filename); + + /** + * \brief Delete a file or directory in the archive. + * + * This same call is used for both files and directories; there is not a + * separate rmdir() call. Directories are only meant to be removed if + * they are empty. + * If the archive is read-only, this operation should fail. + * + * Return non-zero on success, zero on failure. + * This filename is in platform-independent notation. + * On failure, call PHYSFS_setErrorCode(). + */ + int (*remove)(void *opaque, const char *filename); + + /** + * \brief Create a directory in the archive. + * + * If the application is trying to make multiple dirs, PhysicsFS + * will split them up into multiple calls before passing them to + * your driver. + * If the archive is read-only, this operation should fail. + * Return non-zero on success, zero on failure. + * This filename is in platform-independent notation. + * On failure, call PHYSFS_setErrorCode(). + */ + int (*mkdir)(void *opaque, const char *filename); + + /** + * \brief Obtain basic file metadata. + * + * On success, fill in all the fields in (stat), using + * reasonable defaults for fields that apply to your archive. + * + * Returns non-zero on success, zero on failure. + * This filename is in platform-independent notation. + * On failure, call PHYSFS_setErrorCode(). + */ + int (*stat)(void *opaque, const char *fn, PHYSFS_Stat *stat); + + /** + * \brief Destruct a previously-opened archive. + * + * Close this archive, and free any associated memory, + * including the original PHYSFS_Io and (opaque) itself, if + * applicable. Implementation can assume that it won't be called if + * there are still files open from this archive. + */ + void (*closeArchive)(void *opaque); +} PHYSFS_Archiver; + +/** + * \fn int PHYSFS_registerArchiver(const PHYSFS_Archiver *archiver) + * \brief Add a new archiver to the system. + * + * \warning This is advanced, hardcore stuff. You don't need this unless you + * really know what you're doing. Most apps will not need this. + * + * If you want to provide your own archiver (for example, a custom archive + * file format, or some virtual thing you want to make look like a filesystem + * that you can access through the usual PhysicsFS APIs), this is where you + * start. Once an archiver is successfully registered, then you can use + * PHYSFS_mount() to add archives that your archiver supports to the + * search path, or perhaps use it as the write dir. Internally, PhysicsFS + * uses this function to register its own built-in archivers, like .zip + * support, etc. + * + * You may not have two archivers that handle the same extension. If you are + * going to have a clash, you can deregister the other archiver (including + * built-in ones) with PHYSFS_deregisterArchiver(). + * + * The data in (archiver) is copied; you may free this pointer when this + * function returns. + * + * Once this function returns successfully, PhysicsFS will be able to support + * archives of this type until you deregister the archiver again. + * + * \param archiver The archiver to register. + * \return Zero on error, non-zero on success. + * + * \sa PHYSFS_Archiver + * \sa PHYSFS_deregisterArchiver + */ +PHYSFS_DECL int PHYSFS_registerArchiver(const PHYSFS_Archiver *archiver); + +/** + * \fn int PHYSFS_deregisterArchiver(const char *ext) + * \brief Remove an archiver from the system. + * + * If for some reason, you only need your previously-registered archiver to + * live for a portion of your app's lifetime, you can remove it from the + * system once you're done with it through this function. + * + * This fails if there are any archives still open that use this archiver. + * + * This function can also remove internally-supplied archivers, like .zip + * support or whatnot. This could be useful in some situations, like + * disabling support for them outright or overriding them with your own + * implementation. Once an internal archiver is disabled like this, + * PhysicsFS provides no mechanism to recover them, short of calling + * PHYSFS_deinit() and PHYSFS_init() again. + * + * PHYSFS_deinit() will automatically deregister all archivers, so you don't + * need to explicitly deregister yours if you otherwise shut down cleanly. + * + * \param ext Filename extension that the archiver handles. + * \return Zero on error, non-zero on success. + * + * \sa PHYSFS_Archiver + * \sa PHYSFS_registerArchiver + */ +PHYSFS_DECL int PHYSFS_deregisterArchiver(const char *ext); + + +/* Everything above this line is part of the PhysicsFS 2.1 API. */ + +#ifdef __cplusplus +} +#endif + +#endif /* !defined _INCLUDE_PHYSFS_H_ */ + +/* end of physfs.h ... */ + diff --git a/Source/3rdParty/physfs/physfs_archiver_7z.c b/Source/3rdParty/physfs/physfs_archiver_7z.c new file mode 100644 index 0000000..f35a0b1 --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_7z.c @@ -0,0 +1,428 @@ +/* + * 7zip support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file was written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if PHYSFS_SUPPORTS_7Z + +#include "physfs_lzmasdk.h" + +typedef struct +{ + ISeekInStream seekStream; /* lzma sdk i/o interface (lower level). */ + PHYSFS_Io *io; /* physfs i/o interface for this archive. */ + CLookToRead lookStream; /* lzma sdk i/o interface (higher level). */ +} SZIPLookToRead; + +/* One SZIPentry is kept for each file in an open 7zip archive. */ +typedef struct +{ + __PHYSFS_DirTreeEntry tree; /* manages directory tree */ + PHYSFS_uint32 dbidx; /* index into lzma sdk database */ +} SZIPentry; + +/* One SZIPinfo is kept for each open 7zip archive. */ +typedef struct +{ + __PHYSFS_DirTree tree; /* manages directory tree. */ + PHYSFS_Io *io; /* physfs i/o interface for this archive. */ + CSzArEx db; /* lzma sdk archive database object. */ +} SZIPinfo; + + +static PHYSFS_ErrorCode szipErrorCode(const SRes rc) +{ + switch (rc) + { + case SZ_OK: return PHYSFS_ERR_OK; + case SZ_ERROR_DATA: return PHYSFS_ERR_CORRUPT; + case SZ_ERROR_MEM: return PHYSFS_ERR_OUT_OF_MEMORY; + case SZ_ERROR_CRC: return PHYSFS_ERR_CORRUPT; + case SZ_ERROR_UNSUPPORTED: return PHYSFS_ERR_UNSUPPORTED; + case SZ_ERROR_INPUT_EOF: return PHYSFS_ERR_CORRUPT; + case SZ_ERROR_OUTPUT_EOF: return PHYSFS_ERR_IO; + case SZ_ERROR_READ: return PHYSFS_ERR_IO; + case SZ_ERROR_WRITE: return PHYSFS_ERR_IO; + case SZ_ERROR_ARCHIVE: return PHYSFS_ERR_CORRUPT; + case SZ_ERROR_NO_ARCHIVE: return PHYSFS_ERR_UNSUPPORTED; + default: break; + } /* switch */ + + return PHYSFS_ERR_OTHER_ERROR; +} /* szipErrorCode */ + + +/* LZMA SDK's ISzAlloc interface ... */ + +static void *SZIP_ISzAlloc_Alloc(void *p, size_t size) +{ + return allocator.Malloc(size ? size : 1); +} /* SZIP_ISzAlloc_Alloc */ + +static void SZIP_ISzAlloc_Free(void *p, void *address) +{ + if (address) + allocator.Free(address); +} /* SZIP_ISzAlloc_Free */ + +static ISzAlloc SZIP_SzAlloc = { + SZIP_ISzAlloc_Alloc, SZIP_ISzAlloc_Free +}; + + +/* we implement ISeekInStream, and then wrap that in LZMA SDK's CLookToRead, + which implements the higher-level ILookInStream on top of that, handling + buffering and such for us. */ + +/* LZMA SDK's ISeekInStream interface ... */ + +static SRes SZIP_ISeekInStream_Read(void *p, void *buf, size_t *size) +{ + SZIPLookToRead *stream = (SZIPLookToRead *) p; + PHYSFS_Io *io = stream->io; + const PHYSFS_uint64 len = (PHYSFS_uint64) *size; + const PHYSFS_sint64 rc = (len == 0) ? 0 : io->read(io, buf, len); + + if (rc < 0) + { + *size = 0; + return SZ_ERROR_READ; + } /* if */ + + *size = (size_t) rc; + return SZ_OK; +} /* SZIP_ISeekInStream_Read */ + +static SRes SZIP_ISeekInStream_Seek(void *p, Int64 *pos, ESzSeek origin) +{ + SZIPLookToRead *stream = (SZIPLookToRead *) p; + PHYSFS_Io *io = stream->io; + PHYSFS_sint64 base; + PHYSFS_uint64 newpos; + + switch (origin) + { + case SZ_SEEK_SET: + base = 0; + break; + + case SZ_SEEK_CUR: + base = io->tell(io); + break; + + case SZ_SEEK_END: + base = io->length(io); + break; + + default: + return SZ_ERROR_FAIL; + } /* switch */ + + if (base < 0) + return SZ_ERROR_FAIL; + else if ((*pos < 0) && (((Int64) base) < -*pos)) + return SZ_ERROR_FAIL; + + newpos = (PHYSFS_uint64) (((Int64) base) + *pos); + if (!io->seek(io, newpos)) + return SZ_ERROR_FAIL; + + *pos = (Int64) newpos; + return SZ_OK; +} /* SZIP_ISeekInStream_Seek */ + + +static void szipInitStream(SZIPLookToRead *stream, PHYSFS_Io *io) +{ + stream->seekStream.Read = SZIP_ISeekInStream_Read; + stream->seekStream.Seek = SZIP_ISeekInStream_Seek; + + stream->io = io; + + /* !!! FIXME: can we use lookahead? Is there value to it? */ + LookToRead_Init(&stream->lookStream); + LookToRead_CreateVTable(&stream->lookStream, False); + stream->lookStream.realStream = &stream->seekStream; +} /* szipInitStream */ + + +/* Do this in a separate function so we can smallAlloc without looping. */ +static int szipLoadEntry(SZIPinfo *info, const PHYSFS_uint32 idx) +{ + const size_t utf16len = SzArEx_GetFileNameUtf16(&info->db, idx, NULL); + const size_t utf16buflen = utf16len * 2; + PHYSFS_uint16 *utf16 = (PHYSFS_uint16 *) __PHYSFS_smallAlloc(utf16buflen); + const size_t utf8buflen = utf16len * 4; + char *utf8 = (char *) __PHYSFS_smallAlloc(utf8buflen); + int retval = 0; + + if (utf16 && utf8) + { + const int isdir = SzArEx_IsDir(&info->db, idx) != 0; + SZIPentry *entry; + SzArEx_GetFileNameUtf16(&info->db, idx, (UInt16 *) utf16); + PHYSFS_utf8FromUtf16(utf16, utf8, utf8buflen); + entry = (SZIPentry*) __PHYSFS_DirTreeAdd(&info->tree, utf8, isdir); + retval = (entry != NULL); + if (retval) + entry->dbidx = idx; + } /* if */ + + __PHYSFS_smallFree(utf8); + __PHYSFS_smallFree(utf16); + + return retval; +} /* szipLoadEntry */ + + +static int szipLoadEntries(SZIPinfo *info) +{ + int retval = 0; + + if (__PHYSFS_DirTreeInit(&info->tree, sizeof (SZIPentry))) + { + const PHYSFS_uint32 count = info->db.NumFiles; + PHYSFS_uint32 i; + for (i = 0; i < count; i++) + BAIL_IF_ERRPASS(!szipLoadEntry(info, i), 0); + retval = 1; + } /* if */ + + return retval; +} /* szipLoadEntries */ + + +static void SZIP_closeArchive(void *opaque) +{ + SZIPinfo *info = (SZIPinfo *) opaque; + if (info) + { + SzArEx_Free(&info->db, &SZIP_SzAlloc); + __PHYSFS_DirTreeDeinit(&info->tree); + allocator.Free(info); + } /* if */ +} /* SZIP_closeArchive */ + + +static void *SZIP_openArchive(PHYSFS_Io *io, const char *name, + int forWriting, int *claimed) +{ + static const PHYSFS_uint8 wantedsig[] = { '7','z',0xBC,0xAF,0x27,0x1C }; + SZIPLookToRead stream; + ISzAlloc *alloc = &SZIP_SzAlloc; + SZIPinfo *info = NULL; + SRes rc; + PHYSFS_uint8 sig[6]; + PHYSFS_sint64 pos; + + BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); + pos = io->tell(io); + BAIL_IF_ERRPASS(pos == -1, NULL); + BAIL_IF_ERRPASS(io->read(io, sig, 6) != 6, NULL); + *claimed = (memcmp(sig, wantedsig, 6) == 0); + BAIL_IF_ERRPASS(!io->seek(io, pos), NULL); + + info = (SZIPinfo *) allocator.Malloc(sizeof (SZIPinfo)); + BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + memset(info, '\0', sizeof (*info)); + + SzArEx_Init(&info->db); + + info->io = io; + + szipInitStream(&stream, io); + rc = SzArEx_Open(&info->db, &stream.lookStream.s, alloc, alloc); + GOTO_IF(rc != SZ_OK, szipErrorCode(rc), failed); + + GOTO_IF_ERRPASS(!szipLoadEntries(info), failed); + + return info; + +failed: + info->io = NULL; /* don't let cleanup destroy the PHYSFS_Io. */ + SZIP_closeArchive(info); + return NULL; +} /* SZIP_openArchive */ + + +static PHYSFS_Io *SZIP_openRead(void *opaque, const char *path) +{ + /* !!! FIXME: the current lzma sdk C API only allows you to decompress + !!! FIXME: the entire file at once, which isn't ideal. Fix this in the + !!! FIXME: SDK and then convert this all to a streaming interface. */ + + SZIPinfo *info = (SZIPinfo *) opaque; + SZIPentry *entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path); + ISzAlloc *alloc = &SZIP_SzAlloc; + SZIPLookToRead stream; + PHYSFS_Io *retval = NULL; + PHYSFS_Io *io = NULL; + UInt32 blockIndex = 0xFFFFFFFF; + Byte *outBuffer = NULL; + size_t outBufferSize = 0; + size_t offset = 0; + size_t outSizeProcessed = 0; + void *buf = NULL; + SRes rc; + + BAIL_IF_ERRPASS(!entry, NULL); + BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL); + + io = info->io->duplicate(info->io); + GOTO_IF_ERRPASS(!io, SZIP_openRead_failed); + + szipInitStream(&stream, io); + + rc = SzArEx_Extract(&info->db, &stream.lookStream.s, entry->dbidx, + &blockIndex, &outBuffer, &outBufferSize, &offset, + &outSizeProcessed, alloc, alloc); + GOTO_IF(rc != SZ_OK, szipErrorCode(rc), SZIP_openRead_failed); + + io->destroy(io); + io = NULL; + + buf = allocator.Malloc(outSizeProcessed); + GOTO_IF(rc != SZ_OK, PHYSFS_ERR_OUT_OF_MEMORY, SZIP_openRead_failed); + memcpy(buf, outBuffer + offset, outSizeProcessed); + + alloc->Free(alloc, outBuffer); + outBuffer = NULL; + + retval = __PHYSFS_createMemoryIo(buf, outSizeProcessed, allocator.Free); + GOTO_IF_ERRPASS(!retval, SZIP_openRead_failed); + + return retval; + +SZIP_openRead_failed: + if (io != NULL) + io->destroy(io); + + if (buf) + allocator.Free(buf); + + if (outBuffer) + alloc->Free(alloc, outBuffer); + + return NULL; +} /* SZIP_openRead */ + + +static PHYSFS_Io *SZIP_openWrite(void *opaque, const char *filename) +{ + BAIL(PHYSFS_ERR_READ_ONLY, NULL); +} /* SZIP_openWrite */ + + +static PHYSFS_Io *SZIP_openAppend(void *opaque, const char *filename) +{ + BAIL(PHYSFS_ERR_READ_ONLY, NULL); +} /* SZIP_openAppend */ + + +static int SZIP_remove(void *opaque, const char *name) +{ + BAIL(PHYSFS_ERR_READ_ONLY, 0); +} /* SZIP_remove */ + + +static int SZIP_mkdir(void *opaque, const char *name) +{ + BAIL(PHYSFS_ERR_READ_ONLY, 0); +} /* SZIP_mkdir */ + + +static inline PHYSFS_uint64 lzmasdkTimeToPhysfsTime(const CNtfsFileTime *t) +{ + const PHYSFS_uint64 winEpochToUnixEpoch = __PHYSFS_UI64(0x019DB1DED53E8000); + const PHYSFS_uint64 nanosecToMillisec = __PHYSFS_UI64(10000000); + const PHYSFS_uint64 quad = (((PHYSFS_uint64) t->High) << 32) | t->Low; + return (quad - winEpochToUnixEpoch) / nanosecToMillisec; +} /* lzmasdkTimeToPhysfsTime */ + + +static int SZIP_stat(void *opaque, const char *path, PHYSFS_Stat *stat) +{ + SZIPinfo *info = (SZIPinfo *) opaque; + SZIPentry *entry; + PHYSFS_uint32 idx; + + entry = (SZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path); + BAIL_IF_ERRPASS(!entry, 0); + idx = entry->dbidx; + + if (entry->tree.isdir) + { + stat->filesize = -1; + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + } /* if */ + else + { + stat->filesize = (PHYSFS_sint64) SzArEx_GetFileSize(&info->db, idx); + stat->filetype = PHYSFS_FILETYPE_REGULAR; + } /* else */ + + if (info->db.MTime.Vals != NULL) + stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]); + else if (info->db.CTime.Vals != NULL) + stat->modtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]); + else + stat->modtime = -1; + + if (info->db.CTime.Vals != NULL) + stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.CTime.Vals[idx]); + else if (info->db.MTime.Vals != NULL) + stat->createtime = lzmasdkTimeToPhysfsTime(&info->db.MTime.Vals[idx]); + else + stat->createtime = -1; + + stat->accesstime = -1; + stat->readonly = 1; + + return 1; +} /* SZIP_stat */ + + +void SZIP_global_init(void) +{ + /* this just needs to calculate some things, so it only ever + has to run once, even after a deinit. */ + static int generatedTable = 0; + if (!generatedTable) + { + generatedTable = 1; + CrcGenerateTable(); + } /* if */ +} /* SZIP_global_init */ + + +const PHYSFS_Archiver __PHYSFS_Archiver_7Z = +{ + CURRENT_PHYSFS_ARCHIVER_API_VERSION, + { + "7Z", + "7zip archives", + "Ryan C. Gordon <icculus@icculus.org>", + "https://icculus.org/physfs/", + 0, /* supportsSymlinks */ + }, + SZIP_openArchive, + __PHYSFS_DirTreeEnumerate, + SZIP_openRead, + SZIP_openWrite, + SZIP_openAppend, + SZIP_remove, + SZIP_mkdir, + SZIP_stat, + SZIP_closeArchive +}; + +#endif /* defined PHYSFS_SUPPORTS_7Z */ + +/* end of physfs_archiver_7z.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_archiver_dir.c b/Source/3rdParty/physfs/physfs_archiver_dir.c new file mode 100644 index 0000000..61c0da3 --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_dir.c @@ -0,0 +1,196 @@ +/* + * Standard directory I/O support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +/* There's no PHYSFS_Io interface here. Use __PHYSFS_createNativeIo(). */ + + + +static char *cvtToDependent(const char *prepend, const char *path, + char *buf, const size_t buflen) +{ + BAIL_IF(buf == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + snprintf(buf, buflen, "%s%s", prepend ? prepend : "", path); + + #if !__PHYSFS_STANDARD_DIRSEP + assert(__PHYSFS_platformDirSeparator != '/'); + { + char *p; + for (p = strchr(buf, '/'); p != NULL; p = strchr(p + 1, '/')) + *p = __PHYSFS_platformDirSeparator; + } /* if */ + #endif + + return buf; +} /* cvtToDependent */ + + +#define CVT_TO_DEPENDENT(buf, pre, dir) { \ + const size_t len = ((pre) ? strlen((char *) pre) : 0) + strlen(dir) + 1; \ + buf = cvtToDependent((char*)pre,dir,(char*)__PHYSFS_smallAlloc(len),len); \ +} + + + +static void *DIR_openArchive(PHYSFS_Io *io, const char *name, + int forWriting, int *claimed) +{ + PHYSFS_Stat st; + const char dirsep = __PHYSFS_platformDirSeparator; + char *retval = NULL; + const size_t namelen = strlen(name); + const size_t seplen = 1; + + assert(io == NULL); /* shouldn't create an Io for these. */ + BAIL_IF_ERRPASS(!__PHYSFS_platformStat(name, &st, 1), NULL); + + if (st.filetype != PHYSFS_FILETYPE_DIRECTORY) + BAIL(PHYSFS_ERR_UNSUPPORTED, NULL); + + *claimed = 1; + retval = allocator.Malloc(namelen + seplen + 1); + BAIL_IF(retval == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + + strcpy(retval, name); + + /* make sure there's a dir separator at the end of the string */ + if (retval[namelen - 1] != dirsep) + { + retval[namelen] = dirsep; + retval[namelen + 1] = '\0'; + } /* if */ + + return retval; +} /* DIR_openArchive */ + + +static PHYSFS_EnumerateCallbackResult DIR_enumerate(void *opaque, + const char *dname, PHYSFS_EnumerateCallback cb, + const char *origdir, void *callbackdata) +{ + char *d; + PHYSFS_EnumerateCallbackResult retval; + CVT_TO_DEPENDENT(d, opaque, dname); + BAIL_IF_ERRPASS(!d, PHYSFS_ENUM_ERROR); + retval = __PHYSFS_platformEnumerate(d, cb, origdir, callbackdata); + __PHYSFS_smallFree(d); + return retval; +} /* DIR_enumerate */ + + +static PHYSFS_Io *doOpen(void *opaque, const char *name, const int mode) +{ + PHYSFS_Io *io = NULL; + char *f = NULL; + + CVT_TO_DEPENDENT(f, opaque, name); + BAIL_IF_ERRPASS(!f, NULL); + + io = __PHYSFS_createNativeIo(f, mode); + if (io == NULL) + { + const PHYSFS_ErrorCode err = PHYSFS_getLastErrorCode(); + PHYSFS_Stat statbuf; + __PHYSFS_platformStat(f, &statbuf, 0); /* !!! FIXME: why are we stating here? */ + PHYSFS_setErrorCode(err); + } /* if */ + + __PHYSFS_smallFree(f); + + return io; +} /* doOpen */ + + +static PHYSFS_Io *DIR_openRead(void *opaque, const char *filename) +{ + return doOpen(opaque, filename, 'r'); +} /* DIR_openRead */ + + +static PHYSFS_Io *DIR_openWrite(void *opaque, const char *filename) +{ + return doOpen(opaque, filename, 'w'); +} /* DIR_openWrite */ + + +static PHYSFS_Io *DIR_openAppend(void *opaque, const char *filename) +{ + return doOpen(opaque, filename, 'a'); +} /* DIR_openAppend */ + + +static int DIR_remove(void *opaque, const char *name) +{ + int retval; + char *f; + + CVT_TO_DEPENDENT(f, opaque, name); + BAIL_IF_ERRPASS(!f, 0); + retval = __PHYSFS_platformDelete(f); + __PHYSFS_smallFree(f); + return retval; +} /* DIR_remove */ + + +static int DIR_mkdir(void *opaque, const char *name) +{ + int retval; + char *f; + + CVT_TO_DEPENDENT(f, opaque, name); + BAIL_IF_ERRPASS(!f, 0); + retval = __PHYSFS_platformMkDir(f); + __PHYSFS_smallFree(f); + return retval; +} /* DIR_mkdir */ + + +static void DIR_closeArchive(void *opaque) +{ + allocator.Free(opaque); +} /* DIR_closeArchive */ + + +static int DIR_stat(void *opaque, const char *name, PHYSFS_Stat *stat) +{ + int retval = 0; + char *d; + + CVT_TO_DEPENDENT(d, opaque, name); + BAIL_IF_ERRPASS(!d, 0); + retval = __PHYSFS_platformStat(d, stat, 0); + __PHYSFS_smallFree(d); + return retval; +} /* DIR_stat */ + + +const PHYSFS_Archiver __PHYSFS_Archiver_DIR = +{ + CURRENT_PHYSFS_ARCHIVER_API_VERSION, + { + "", + "Non-archive, direct filesystem I/O", + "Ryan C. Gordon <icculus@icculus.org>", + "https://icculus.org/physfs/", + 1, /* supportsSymlinks */ + }, + DIR_openArchive, + DIR_enumerate, + DIR_openRead, + DIR_openWrite, + DIR_openAppend, + DIR_remove, + DIR_mkdir, + DIR_stat, + DIR_closeArchive +}; + +/* end of physfs_archiver_dir.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_archiver_grp.c b/Source/3rdParty/physfs/physfs_archiver_grp.c new file mode 100644 index 0000000..758475e --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_grp.c @@ -0,0 +1,116 @@ +/* + * GRP support routines for PhysicsFS. + * + * This driver handles BUILD engine archives ("groupfiles"). This format + * (but not this driver) was put together by Ken Silverman. + * + * The format is simple enough. In Ken's words: + * + * What's the .GRP file format? + * + * The ".grp" file format is just a collection of a lot of files stored + * into 1 big one. I tried to make the format as simple as possible: The + * first 12 bytes contains my name, "KenSilverman". The next 4 bytes is + * the number of files that were compacted into the group file. Then for + * each file, there is a 16 byte structure, where the first 12 bytes are + * the filename, and the last 4 bytes are the file's size. The rest of + * the group file is just the raw data packed one after the other in the + * same order as the list of files. + * + * (That info is from http://www.advsys.net/ken/build.htm ...) + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if PHYSFS_SUPPORTS_GRP + +static int grpLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) +{ + PHYSFS_uint32 pos = 16 + (16 * count); /* past sig+metadata. */ + PHYSFS_uint32 i; + + for (i = 0; i < count; i++) + { + char *ptr; + char name[13]; + PHYSFS_uint32 size; + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 12), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); + + name[12] = '\0'; /* name isn't null-terminated in file. */ + if ((ptr = strchr(name, ' ')) != NULL) + *ptr = '\0'; /* trim extra spaces. */ + + size = PHYSFS_swapULE32(size); + BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); + + pos += size; + } /* for */ + + return 1; +} /* grpLoadEntries */ + + +static void *GRP_openArchive(PHYSFS_Io *io, const char *name, + int forWriting, int *claimed) +{ + PHYSFS_uint8 buf[12]; + PHYSFS_uint32 count = 0; + void *unpkarc = NULL; + + assert(io != NULL); /* shouldn't ever happen. */ + + BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, sizeof (buf)), NULL); + if (memcmp(buf, "KenSilverman", sizeof (buf)) != 0) + BAIL(PHYSFS_ERR_UNSUPPORTED, NULL); + + *claimed = 1; + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL); + count = PHYSFS_swapULE32(count); + + unpkarc = UNPK_openArchive(io); + BAIL_IF_ERRPASS(!unpkarc, NULL); + + if (!grpLoadEntries(io, count, unpkarc)) + { + UNPK_abandonArchive(unpkarc); + return NULL; + } /* if */ + + return unpkarc; +} /* GRP_openArchive */ + + +const PHYSFS_Archiver __PHYSFS_Archiver_GRP = +{ + CURRENT_PHYSFS_ARCHIVER_API_VERSION, + { + "GRP", + "Build engine Groupfile format", + "Ryan C. Gordon <icculus@icculus.org>", + "https://icculus.org/physfs/", + 0, /* supportsSymlinks */ + }, + GRP_openArchive, + UNPK_enumerate, + UNPK_openRead, + UNPK_openWrite, + UNPK_openAppend, + UNPK_remove, + UNPK_mkdir, + UNPK_stat, + UNPK_closeArchive +}; + +#endif /* defined PHYSFS_SUPPORTS_GRP */ + +/* end of physfs_archiver_grp.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_archiver_hog.c b/Source/3rdParty/physfs/physfs_archiver_hog.c new file mode 100644 index 0000000..832209f --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_hog.c @@ -0,0 +1,114 @@ +/* + * HOG support routines for PhysicsFS. + * + * This driver handles Descent I/II HOG archives. + * + * The format is very simple: + * + * The file always starts with the 3-byte signature "DHF" (Descent + * HOG file). After that the files of a HOG are just attached after + * another, divided by a 17 bytes header, which specifies the name + * and length (in bytes) of the forthcoming file! So you just read + * the header with its information of how big the following file is, + * and then skip exact that number of bytes to get to the next file + * in that HOG. + * + * char sig[3] = {'D', 'H', 'F'}; // "DHF"=Descent HOG File + * + * struct { + * char file_name[13]; // Filename, padded to 13 bytes with 0s + * int file_size; // filesize in bytes + * char data[file_size]; // The file data + * } FILE_STRUCT; // Repeated until the end of the file. + * + * (That info is from http://www.descent2.com/ddn/specs/hog/) + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Bradley Bell. + * Based on grp.c by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if PHYSFS_SUPPORTS_HOG + +static int hogLoadEntries(PHYSFS_Io *io, void *arc) +{ + const PHYSFS_uint64 iolen = io->length(io); + PHYSFS_uint32 pos = 3; + + while (pos < iolen) + { + PHYSFS_uint32 size; + char name[13]; + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 13), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); + name[12] = '\0'; /* just in case. */ + pos += 13 + 4; + + size = PHYSFS_swapULE32(size); + BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); + pos += size; + + /* skip over entry */ + BAIL_IF_ERRPASS(!io->seek(io, pos), 0); + } /* while */ + + return 1; +} /* hogLoadEntries */ + + +static void *HOG_openArchive(PHYSFS_Io *io, const char *name, + int forWriting, int *claimed) +{ + PHYSFS_uint8 buf[3]; + void *unpkarc = NULL; + + assert(io != NULL); /* shouldn't ever happen. */ + BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 3), NULL); + BAIL_IF(memcmp(buf, "DHF", 3) != 0, PHYSFS_ERR_UNSUPPORTED, NULL); + + *claimed = 1; + + unpkarc = UNPK_openArchive(io); + BAIL_IF_ERRPASS(!unpkarc, NULL); + + if (!hogLoadEntries(io, unpkarc)) + { + UNPK_abandonArchive(unpkarc); + return NULL; + } /* if */ + + return unpkarc; +} /* HOG_openArchive */ + + +const PHYSFS_Archiver __PHYSFS_Archiver_HOG = +{ + CURRENT_PHYSFS_ARCHIVER_API_VERSION, + { + "HOG", + "Descent I/II HOG file format", + "Bradley Bell <btb@icculus.org>", + "https://icculus.org/physfs/", + 0, /* supportsSymlinks */ + }, + HOG_openArchive, + UNPK_enumerate, + UNPK_openRead, + UNPK_openWrite, + UNPK_openAppend, + UNPK_remove, + UNPK_mkdir, + UNPK_stat, + UNPK_closeArchive +}; + +#endif /* defined PHYSFS_SUPPORTS_HOG */ + +/* end of physfs_archiver_hog.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_archiver_iso9660.c b/Source/3rdParty/physfs/physfs_archiver_iso9660.c new file mode 100644 index 0000000..965c83f --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_iso9660.c @@ -0,0 +1,386 @@ +/* + * ISO9660 support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file originally written by Christoph Nelles, but was largely + * rewritten by Ryan C. Gordon (so please harass Ryan about bugs and not + * Christoph). + */ + +/* + * Handles CD-ROM disk images (and raw CD-ROM devices). + * + * Not supported: + * - Rock Ridge (needed for sparse files, device nodes and symlinks, etc). + * - Non 2048 Sectors + * - TRANS.TBL (maps 8.3 filenames on old discs to long filenames). + * - Multiextents (4gb max file size without it). + * - UDF + * + * Deviations from the standard + * - Ignores mandatory sort order + * - Allows various invalid file names + * + * Problems + * - Ambiguities in the standard + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if PHYSFS_SUPPORTS_ISO9660 + +#include <time.h> + +/* ISO9660 often stores values in both big and little endian formats: little + first, followed by big. While technically there might be different values + in each, we just always use the littleendian ones and swap ourselves. The + fields aren't aligned anyhow, so you have to serialize them in any case + to avoid crashes on many CPU archs in any case. */ + +static int iso9660LoadEntries(PHYSFS_Io *io, const int joliet, + const char *base, const PHYSFS_uint64 dirstart, + const PHYSFS_uint64 dirend, void *unpkarc); + +static int iso9660AddEntry(PHYSFS_Io *io, const int joliet, const int isdir, + const char *base, PHYSFS_uint8 *fname, + const int fnamelen, const PHYSFS_sint64 ts, + const PHYSFS_uint64 pos, const PHYSFS_uint64 len, + void *unpkarc) +{ + char *fullpath; + char *fnamecpy; + size_t baselen; + size_t fullpathlen; + void *entry; + int i; + + if (fnamelen == 1 && ((fname[0] == 0) || (fname[0] == 1))) + return 1; /* Magic that represents "." and "..", ignore */ + + BAIL_IF(fnamelen == 0, PHYSFS_ERR_CORRUPT, 0); + assert(fnamelen > 0); + assert(fnamelen <= 255); + BAIL_IF(joliet && (fnamelen % 2), PHYSFS_ERR_CORRUPT, 0); + + /* Joliet is UCS-2, so at most UTF-8 will double the byte size */ + baselen = strlen(base); + fullpathlen = baselen + (fnamelen * (joliet ? 2 : 1)) + 2; + fullpath = (char *) __PHYSFS_smallAlloc(fullpathlen); + BAIL_IF(!fullpath, PHYSFS_ERR_OUT_OF_MEMORY, 0); + fnamecpy = fullpath; + if (baselen > 0) + { + snprintf(fullpath, fullpathlen, "%s/", base); + fnamecpy += baselen + 1; + fullpathlen -= baselen - 1; + } /* if */ + + if (joliet) + { + PHYSFS_uint16 *ucs2 = (PHYSFS_uint16 *) fname; + int total = fnamelen / 2; + for (i = 0; i < total; i++) + ucs2[i] = PHYSFS_swapUBE16(ucs2[i]); + ucs2[total] = '\0'; + PHYSFS_utf8FromUcs2(ucs2, fnamecpy, fullpathlen); + } /* if */ + else + { + for (i = 0; i < fnamelen; i++) + { + /* We assume the filenames are low-ASCII; consider the archive + corrupt if we see something above 127, since we don't know the + encoding. (We can change this later if we find out these exist + and are intended to be, say, latin-1 or UTF-8 encoding). */ + BAIL_IF(fname[i] > 127, PHYSFS_ERR_CORRUPT, 0); + fnamecpy[i] = fname[i]; + } /* for */ + fnamecpy[fnamelen] = '\0'; + + if (!isdir) + { + /* find last SEPARATOR2 */ + char *ptr = strrchr(fnamecpy, ';'); + if (ptr && (ptr != fnamecpy)) + *(ptr--) = '\0'; + else + ptr = fnamecpy + (fnamelen - 1); + + /* chop out any trailing '.', as done in all implementations */ + if (*ptr == '.') + *ptr = '\0'; + } /* if */ + } /* else */ + + entry = UNPK_addEntry(unpkarc, fullpath, isdir, ts, ts, pos, len); + if ((entry) && (isdir)) + { + if (!iso9660LoadEntries(io, joliet, fullpath, pos, pos + len, unpkarc)) + entry = NULL; /* so we report a failure later. */ + } /* if */ + + __PHYSFS_smallFree(fullpath); + return entry != NULL; +} /* iso9660AddEntry */ + +static int iso9660LoadEntries(PHYSFS_Io *io, const int joliet, + const char *base, const PHYSFS_uint64 dirstart, + const PHYSFS_uint64 dirend, void *unpkarc) +{ + PHYSFS_uint64 readpos = dirstart; + + while (1) + { + PHYSFS_uint8 recordlen; + PHYSFS_uint8 extattrlen; + PHYSFS_uint32 extent; + PHYSFS_uint32 datalen; + PHYSFS_uint8 ignore[4]; + PHYSFS_uint8 year, month, day, hour, minute, second, offset; + PHYSFS_uint8 flags; + PHYSFS_uint8 fnamelen; + PHYSFS_uint8 fname[256]; + PHYSFS_sint64 timestamp; + struct tm t; + int isdir; + int multiextent; + + BAIL_IF_ERRPASS(!io->seek(io, readpos), 0); + + /* recordlen = 0 -> no more entries or fill entry */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &recordlen, 1), 0); + if (recordlen > 0) + readpos += recordlen; /* ready to seek to next record. */ + else + { + PHYSFS_uint64 nextpos; + + /* if we are in the last sector of the directory & it's 0 -> end */ + if ((dirend - 2048) <= (readpos - 1)) + break; /* finished */ + + /* else skip to the next sector & continue; */ + nextpos = (((readpos - 1) / 2048) + 1) * 2048; + + /* whoops, can't make forward progress! */ + BAIL_IF(nextpos == readpos, PHYSFS_ERR_CORRUPT, 0); + + readpos = nextpos; + continue; /* start back at upper loop. */ + } /* else */ + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &extattrlen, 1), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &extent, 4), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* extent be */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &datalen, 4), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* datalen be */ + + /* record timestamp */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &year, 1), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &month, 1), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &day, 1), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &hour, 1), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &minute, 1), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &second, 1), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &offset, 1), 0); + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &flags, 1), 0); + isdir = (flags & (1 << 1)) != 0; + multiextent = (flags & (1 << 7)) != 0; + BAIL_IF(multiextent, PHYSFS_ERR_UNSUPPORTED, 0); /* !!! FIXME */ + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* unit size */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* interleave gap */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seqnum le */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seqnum be */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &fnamelen, 1), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, fname, fnamelen), 0); + + t.tm_sec = second; + t.tm_min = minute; + t.tm_hour = hour; + t.tm_mday = day; + t.tm_mon = month - 1; + t.tm_year = year; + t.tm_wday = 0; + t.tm_yday = 0; + t.tm_isdst = -1; + timestamp = (PHYSFS_sint64) mktime(&t); + + extent += extattrlen; /* skip extended attribute record. */ + + /* infinite loop, corrupt file? */ + BAIL_IF((extent * 2048) == dirstart, PHYSFS_ERR_CORRUPT, 0); + + if (!iso9660AddEntry(io, joliet, isdir, base, fname, fnamelen, + timestamp, extent * 2048, datalen, unpkarc)) + { + return 0; + } /* if */ + } /* while */ + + return 1; +} /* iso9660LoadEntries */ + + +static int parseVolumeDescriptor(PHYSFS_Io *io, PHYSFS_uint64 *_rootpos, + PHYSFS_uint64 *_rootlen, int *_joliet, + int *_claimed) +{ + PHYSFS_uint64 pos = 32768; /* start at the Primary Volume Descriptor */ + int found = 0; + int done = 0; + + *_joliet = 0; + + while (!done) + { + PHYSFS_uint8 type; + PHYSFS_uint8 identifier[5]; + PHYSFS_uint8 version; + PHYSFS_uint8 flags; + PHYSFS_uint8 escapeseqs[32]; + PHYSFS_uint8 ignore[32]; + PHYSFS_uint16 blocksize; + PHYSFS_uint32 extent; + PHYSFS_uint32 datalen; + + BAIL_IF_ERRPASS(!io->seek(io, pos), 0); + pos += 2048; /* each volume descriptor is 2048 bytes */ + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &type, 1), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &identifier, 5), 0); + + if (memcmp(identifier, "CD001", 5) != 0) /* maybe not an iso? */ + { + BAIL_IF(!*_claimed, PHYSFS_ERR_UNSUPPORTED, 0); + continue; /* just skip this one */ + } /* if */ + + *_claimed = 1; /* okay, this is probably an iso. */ + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &version, 1), 0); /* version */ + BAIL_IF(version != 1, PHYSFS_ERR_UNSUPPORTED, 0); + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &flags, 1), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 32), 0); /* system id */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 32), 0); /* volume id */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 8), 0); /* reserved */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* space le */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* space be */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, escapeseqs, 32), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* setsize le */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* setsize be */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seq num le */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* seq num be */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &blocksize, 2), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 2), 0); /* blocklen be */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtablen le */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtablen be */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtabpos le */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* optpthtabpos le */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* pthtabpos be */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* optpthtabpos be */ + + /* root directory record... */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* len */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 1), 0); /* attr len */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &extent, 4), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* extent be */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &datalen, 4), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), 0); /* datalen be */ + + /* !!! FIXME: deal with this properly. */ + blocksize = PHYSFS_swapULE32(blocksize); + BAIL_IF(blocksize && (blocksize != 2048), PHYSFS_ERR_UNSUPPORTED, 0); + + switch (type) + { + case 1: /* Primary Volume Descriptor */ + case 2: /* Supplementary Volume Descriptor */ + if (found < type) + { + *_rootpos = PHYSFS_swapULE32(extent) * 2048; + *_rootlen = PHYSFS_swapULE32(datalen); + found = type; + + if (found == 2) /* possible Joliet volume */ + { + const PHYSFS_uint8 *s = escapeseqs; + *_joliet = !(flags & 1) && + (s[0] == 0x25) && (s[1] == 0x2F) && + ((s[2] == 0x40) || (s[2] == 0x43) || (s[2] == 0x45)); + } /* if */ + } /* if */ + break; + + case 255: /* type 255 terminates the volume descriptor list */ + done = 1; + break; + + default: + break; /* skip unknown types. */ + } /* switch */ + } /* while */ + + BAIL_IF(!found, PHYSFS_ERR_CORRUPT, 0); + + return 1; +} /* parseVolumeDescriptor */ + + +static void *ISO9660_openArchive(PHYSFS_Io *io, const char *filename, + int forWriting, int *claimed) +{ + PHYSFS_uint64 rootpos = 0; + PHYSFS_uint64 len = 0; + int joliet = 0; + void *unpkarc = NULL; + + assert(io != NULL); /* shouldn't ever happen. */ + + BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); + + if (!parseVolumeDescriptor(io, &rootpos, &len, &joliet, claimed)) + return NULL; + + unpkarc = UNPK_openArchive(io); + BAIL_IF_ERRPASS(!unpkarc, NULL); + + if (!iso9660LoadEntries(io, joliet, "", rootpos, rootpos + len, unpkarc)) + { + UNPK_abandonArchive(unpkarc); + return NULL; + } /* if */ + + return unpkarc; +} /* ISO9660_openArchive */ + + +const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660 = +{ + CURRENT_PHYSFS_ARCHIVER_API_VERSION, + { + "ISO", + "ISO9660 image file", + "Ryan C. Gordon <icculus@icculus.org>", + "https://icculus.org/physfs/", + 0, /* supportsSymlinks */ + }, + ISO9660_openArchive, + UNPK_enumerate, + UNPK_openRead, + UNPK_openWrite, + UNPK_openAppend, + UNPK_remove, + UNPK_mkdir, + UNPK_stat, + UNPK_closeArchive +}; + +#endif /* defined PHYSFS_SUPPORTS_ISO9660 */ + +/* end of physfs_archiver_iso9660.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_archiver_mvl.c b/Source/3rdParty/physfs/physfs_archiver_mvl.c new file mode 100644 index 0000000..78b59f1 --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_mvl.c @@ -0,0 +1,110 @@ +/* + * MVL support routines for PhysicsFS. + * + * This driver handles Descent II Movielib archives. + * + * The file format of MVL is quite easy... + * + * //MVL File format - Written by Heiko Herrmann + * char sig[4] = {'D','M', 'V', 'L'}; // "DMVL"=Descent MoVie Library + * + * int num_files; // the number of files in this MVL + * + * struct { + * char file_name[13]; // Filename, padded to 13 bytes with 0s + * int file_size; // filesize in bytes + * }DIR_STRUCT[num_files]; + * + * struct { + * char data[file_size]; // The file data + * }FILE_STRUCT[num_files]; + * + * (That info is from http://www.descent2.com/ddn/specs/mvl/) + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Bradley Bell. + * Based on grp.c by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if PHYSFS_SUPPORTS_MVL + +static int mvlLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) +{ + PHYSFS_uint32 pos = 8 + (17 * count); /* past sig+metadata. */ + PHYSFS_uint32 i; + + for (i = 0; i < count; i++) + { + PHYSFS_uint32 size; + char name[13]; + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 13), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); + name[12] = '\0'; /* just in case. */ + size = PHYSFS_swapULE32(size); + BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); + pos += size; + } /* for */ + + return 1; +} /* mvlLoadEntries */ + + +static void *MVL_openArchive(PHYSFS_Io *io, const char *name, + int forWriting, int *claimed) +{ + PHYSFS_uint8 buf[4]; + PHYSFS_uint32 count = 0; + void *unpkarc; + + assert(io != NULL); /* shouldn't ever happen. */ + BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, 4), NULL); + BAIL_IF(memcmp(buf, "DMVL", 4) != 0, PHYSFS_ERR_UNSUPPORTED, NULL); + + *claimed = 1; + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof(count)), NULL); + count = PHYSFS_swapULE32(count); + + unpkarc = UNPK_openArchive(io); + BAIL_IF_ERRPASS(!unpkarc, NULL); + + if (!mvlLoadEntries(io, count, unpkarc)) + { + UNPK_abandonArchive(unpkarc); + return NULL; + } /* if */ + + return unpkarc; +} /* MVL_openArchive */ + + +const PHYSFS_Archiver __PHYSFS_Archiver_MVL = +{ + CURRENT_PHYSFS_ARCHIVER_API_VERSION, + { + "MVL", + "Descent II Movielib format", + "Bradley Bell <btb@icculus.org>", + "https://icculus.org/physfs/", + 0, /* supportsSymlinks */ + }, + MVL_openArchive, + UNPK_enumerate, + UNPK_openRead, + UNPK_openWrite, + UNPK_openAppend, + UNPK_remove, + UNPK_mkdir, + UNPK_stat, + UNPK_closeArchive +}; + +#endif /* defined PHYSFS_SUPPORTS_MVL */ + +/* end of physfs_archiver_mvl.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_archiver_qpak.c b/Source/3rdParty/physfs/physfs_archiver_qpak.c new file mode 100644 index 0000000..15a5f2d --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_qpak.c @@ -0,0 +1,126 @@ +/* + * QPAK support routines for PhysicsFS. + * + * This archiver handles the archive format utilized by Quake 1 and 2. + * Quake3-based games use the PkZip/Info-Zip format (which our + * physfs_archiver_zip.c handles). + * + * ======================================================================== + * + * This format info (in more detail) comes from: + * https://web.archive.org/web/20040209101748/http://debian.fmi.uni-sofia.bg/~sergei/cgsr/docs/pak.txt + * + * Quake PAK Format + * + * Header + * (4 bytes) signature = 'PACK' + * (4 bytes) directory offset + * (4 bytes) directory length + * + * Directory + * (56 bytes) file name + * (4 bytes) file position + * (4 bytes) file length + * + * ======================================================================== + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if PHYSFS_SUPPORTS_QPAK + +#define QPAK_SIG 0x4B434150 /* "PACK" in ASCII. */ + +static int qpakLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) +{ + PHYSFS_uint32 i; + for (i = 0; i < count; i++) + { + PHYSFS_uint32 size; + PHYSFS_uint32 pos; + char name[56]; + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 56), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); + size = PHYSFS_swapULE32(size); + pos = PHYSFS_swapULE32(pos); + BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); + } /* for */ + + return 1; +} /* qpakLoadEntries */ + + +static void *QPAK_openArchive(PHYSFS_Io *io, const char *name, + int forWriting, int *claimed) +{ + PHYSFS_uint32 val = 0; + PHYSFS_uint32 pos = 0; + PHYSFS_uint32 count = 0; + void *unpkarc; + + assert(io != NULL); /* shouldn't ever happen. */ + + BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL); + if (PHYSFS_swapULE32(val) != QPAK_SIG) + BAIL(PHYSFS_ERR_UNSUPPORTED, NULL); + + *claimed = 1; + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL); + pos = PHYSFS_swapULE32(val); /* directory table offset. */ + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &val, 4), NULL); + count = PHYSFS_swapULE32(val); + + /* corrupted archive? */ + BAIL_IF((count % 64) != 0, PHYSFS_ERR_CORRUPT, NULL); + count /= 64; + + BAIL_IF_ERRPASS(!io->seek(io, pos), NULL); + + unpkarc = UNPK_openArchive(io); + BAIL_IF_ERRPASS(!unpkarc, NULL); + + if (!qpakLoadEntries(io, count, unpkarc)) + { + UNPK_abandonArchive(unpkarc); + return NULL; + } /* if */ + + return unpkarc; +} /* QPAK_openArchive */ + + +const PHYSFS_Archiver __PHYSFS_Archiver_QPAK = +{ + CURRENT_PHYSFS_ARCHIVER_API_VERSION, + { + "PAK", + "Quake I/II format", + "Ryan C. Gordon <icculus@icculus.org>", + "https://icculus.org/physfs/", + 0, /* supportsSymlinks */ + }, + QPAK_openArchive, + UNPK_enumerate, + UNPK_openRead, + UNPK_openWrite, + UNPK_openAppend, + UNPK_remove, + UNPK_mkdir, + UNPK_stat, + UNPK_closeArchive +}; + +#endif /* defined PHYSFS_SUPPORTS_QPAK */ + +/* end of physfs_archiver_qpak.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_archiver_slb.c b/Source/3rdParty/physfs/physfs_archiver_slb.c new file mode 100644 index 0000000..4fc28d4 --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_slb.c @@ -0,0 +1,135 @@ +/* + * SLB support routines for PhysicsFS. + * + * This driver handles SLB archives ("slab files"). This uncompressed format + * is used in I-War / Independence War and Independence War: Defiance. + * + * The format begins with four zero bytes (version?), the file count and the + * location of the table of contents. Each ToC entry contains a 64-byte buffer + * containing a zero-terminated filename, the offset of the data, and its size. + * All the filenames begin with the separator character '\'. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Aleksi Nurmi, based on the GRP archiver by + * Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if PHYSFS_SUPPORTS_SLB + +static int slbLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) +{ + PHYSFS_uint32 i; + for (i = 0; i < count; i++) + { + PHYSFS_uint32 pos; + PHYSFS_uint32 size; + char name[64]; + char backslash; + char *ptr; + + /* don't include the '\' in the beginning */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &backslash, 1), 0); + BAIL_IF(backslash != '\\', PHYSFS_ERR_CORRUPT, 0); + + /* read the rest of the buffer, 63 bytes */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &name, 63), 0); + name[63] = '\0'; /* in case the name lacks the null terminator */ + + /* convert backslashes */ + for (ptr = name; *ptr; ptr++) + { + if (*ptr == '\\') + *ptr = '/'; + } /* for */ + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0); + pos = PHYSFS_swapULE32(pos); + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); + size = PHYSFS_swapULE32(size); + + BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); + } /* for */ + + return 1; +} /* slbLoadEntries */ + + +static void *SLB_openArchive(PHYSFS_Io *io, const char *name, + int forWriting, int *claimed) +{ + PHYSFS_uint32 version; + PHYSFS_uint32 count; + PHYSFS_uint32 tocPos; + void *unpkarc; + + /* There's no identifier on an SLB file, so we assume it's _not_ if the + file count or tocPos is zero. Beyond that, we'll assume it's + bogus/corrupt if the entries' filenames don't start with '\' or the + tocPos is past the end of the file (seek will fail). This probably + covers all meaningful cases where we would accidentally accept a non-SLB + file with this archiver. */ + + assert(io != NULL); /* shouldn't ever happen. */ + + BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &version, sizeof (version)), NULL); + version = PHYSFS_swapULE32(version); + BAIL_IF(version != 0, PHYSFS_ERR_UNSUPPORTED, NULL); + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof (count)), NULL); + count = PHYSFS_swapULE32(count); + BAIL_IF(!count, PHYSFS_ERR_UNSUPPORTED, NULL); + + /* offset of the table of contents */ + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &tocPos, sizeof (tocPos)), NULL); + tocPos = PHYSFS_swapULE32(tocPos); + BAIL_IF(!tocPos, PHYSFS_ERR_UNSUPPORTED, NULL); + + /* seek to the table of contents */ + BAIL_IF_ERRPASS(!io->seek(io, tocPos), NULL); + + unpkarc = UNPK_openArchive(io); + BAIL_IF_ERRPASS(!unpkarc, NULL); + + if (!slbLoadEntries(io, count, unpkarc)) + { + UNPK_abandonArchive(unpkarc); + return NULL; + } /* if */ + + *claimed = 1; /* oh well. */ + + return unpkarc; +} /* SLB_openArchive */ + + +const PHYSFS_Archiver __PHYSFS_Archiver_SLB = +{ + CURRENT_PHYSFS_ARCHIVER_API_VERSION, + { + "SLB", + "I-War / Independence War Slab file", + "Aleksi Nurmi <aleksi.nurmi@gmail.com>", + "https://bitbucket.org/ahnurmi/", + 0, /* supportsSymlinks */ + }, + SLB_openArchive, + UNPK_enumerate, + UNPK_openRead, + UNPK_openWrite, + UNPK_openAppend, + UNPK_remove, + UNPK_mkdir, + UNPK_stat, + UNPK_closeArchive +}; + +#endif /* defined PHYSFS_SUPPORTS_SLB */ + +/* end of physfs_archiver_slb.c ... */ diff --git a/Source/3rdParty/physfs/physfs_archiver_unpacked.c b/Source/3rdParty/physfs/physfs_archiver_unpacked.c new file mode 100644 index 0000000..575efef --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_unpacked.c @@ -0,0 +1,305 @@ +/* + * High-level PhysicsFS archiver for simple unpacked file formats. + * + * This is a framework that basic archivers build on top of. It's for simple + * formats that can just hand back a list of files and the offsets of their + * uncompressed data. There are an alarming number of formats like this. + * + * RULES: Archive entries must be uncompressed. Dirs and files allowed, but no + * symlinks, etc. We can relax some of these rules as necessary. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +typedef struct +{ + __PHYSFS_DirTree tree; + PHYSFS_Io *io; +} UNPKinfo; + +typedef struct +{ + __PHYSFS_DirTreeEntry tree; + PHYSFS_uint64 startPos; + PHYSFS_uint64 size; + PHYSFS_sint64 ctime; + PHYSFS_sint64 mtime; +} UNPKentry; + +typedef struct +{ + PHYSFS_Io *io; + UNPKentry *entry; + PHYSFS_uint32 curPos; +} UNPKfileinfo; + + +void UNPK_closeArchive(void *opaque) +{ + UNPKinfo *info = ((UNPKinfo *) opaque); + if (info) + { + __PHYSFS_DirTreeDeinit(&info->tree); + + if (info->io) + info->io->destroy(info->io); + + allocator.Free(info); + } /* if */ +} /* UNPK_closeArchive */ + +void UNPK_abandonArchive(void *opaque) +{ + UNPKinfo *info = ((UNPKinfo *) opaque); + if (info) + { + info->io = NULL; + UNPK_closeArchive(info); + } /* if */ +} /* UNPK_abandonArchive */ + +static PHYSFS_sint64 UNPK_read(PHYSFS_Io *io, void *buffer, PHYSFS_uint64 len) +{ + UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque; + const UNPKentry *entry = finfo->entry; + const PHYSFS_uint64 bytesLeft = (PHYSFS_uint64)(entry->size-finfo->curPos); + PHYSFS_sint64 rc; + + if (bytesLeft < len) + len = bytesLeft; + + rc = finfo->io->read(finfo->io, buffer, len); + if (rc > 0) + finfo->curPos += (PHYSFS_uint32) rc; + + return rc; +} /* UNPK_read */ + + +static PHYSFS_sint64 UNPK_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len) +{ + BAIL(PHYSFS_ERR_READ_ONLY, -1); +} /* UNPK_write */ + + +static PHYSFS_sint64 UNPK_tell(PHYSFS_Io *io) +{ + return ((UNPKfileinfo *) io->opaque)->curPos; +} /* UNPK_tell */ + + +static int UNPK_seek(PHYSFS_Io *io, PHYSFS_uint64 offset) +{ + UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque; + const UNPKentry *entry = finfo->entry; + int rc; + + BAIL_IF(offset >= entry->size, PHYSFS_ERR_PAST_EOF, 0); + rc = finfo->io->seek(finfo->io, entry->startPos + offset); + if (rc) + finfo->curPos = (PHYSFS_uint32) offset; + + return rc; +} /* UNPK_seek */ + + +static PHYSFS_sint64 UNPK_length(PHYSFS_Io *io) +{ + const UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque; + return ((PHYSFS_sint64) finfo->entry->size); +} /* UNPK_length */ + + +static PHYSFS_Io *UNPK_duplicate(PHYSFS_Io *_io) +{ + UNPKfileinfo *origfinfo = (UNPKfileinfo *) _io->opaque; + PHYSFS_Io *io = NULL; + PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); + UNPKfileinfo *finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo)); + GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed); + GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_duplicate_failed); + + io = origfinfo->io->duplicate(origfinfo->io); + if (!io) goto UNPK_duplicate_failed; + finfo->io = io; + finfo->entry = origfinfo->entry; + finfo->curPos = 0; + memcpy(retval, _io, sizeof (PHYSFS_Io)); + retval->opaque = finfo; + return retval; + +UNPK_duplicate_failed: + if (finfo != NULL) allocator.Free(finfo); + if (retval != NULL) allocator.Free(retval); + if (io != NULL) io->destroy(io); + return NULL; +} /* UNPK_duplicate */ + +static int UNPK_flush(PHYSFS_Io *io) { return 1; /* no write support. */ } + +static void UNPK_destroy(PHYSFS_Io *io) +{ + UNPKfileinfo *finfo = (UNPKfileinfo *) io->opaque; + finfo->io->destroy(finfo->io); + allocator.Free(finfo); + allocator.Free(io); +} /* UNPK_destroy */ + + +static const PHYSFS_Io UNPK_Io = +{ + CURRENT_PHYSFS_IO_API_VERSION, NULL, + UNPK_read, + UNPK_write, + UNPK_seek, + UNPK_tell, + UNPK_length, + UNPK_duplicate, + UNPK_flush, + UNPK_destroy +}; + + +static inline UNPKentry *findEntry(UNPKinfo *info, const char *path) +{ + return (UNPKentry *) __PHYSFS_DirTreeFind(&info->tree, path); +} /* findEntry */ + + +PHYSFS_Io *UNPK_openRead(void *opaque, const char *name) +{ + PHYSFS_Io *retval = NULL; + UNPKinfo *info = (UNPKinfo *) opaque; + UNPKfileinfo *finfo = NULL; + UNPKentry *entry = findEntry(info, name); + + BAIL_IF_ERRPASS(!entry, NULL); + BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL); + + retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); + GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed); + + finfo = (UNPKfileinfo *) allocator.Malloc(sizeof (UNPKfileinfo)); + GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, UNPK_openRead_failed); + + finfo->io = info->io->duplicate(info->io); + GOTO_IF_ERRPASS(!finfo->io, UNPK_openRead_failed); + + if (!finfo->io->seek(finfo->io, entry->startPos)) + goto UNPK_openRead_failed; + + finfo->curPos = 0; + finfo->entry = entry; + + memcpy(retval, &UNPK_Io, sizeof (*retval)); + retval->opaque = finfo; + return retval; + +UNPK_openRead_failed: + if (finfo != NULL) + { + if (finfo->io != NULL) + finfo->io->destroy(finfo->io); + allocator.Free(finfo); + } /* if */ + + if (retval != NULL) + allocator.Free(retval); + + return NULL; +} /* UNPK_openRead */ + + +PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name) +{ + BAIL(PHYSFS_ERR_READ_ONLY, NULL); +} /* UNPK_openWrite */ + + +PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name) +{ + BAIL(PHYSFS_ERR_READ_ONLY, NULL); +} /* UNPK_openAppend */ + + +int UNPK_remove(void *opaque, const char *name) +{ + BAIL(PHYSFS_ERR_READ_ONLY, 0); +} /* UNPK_remove */ + + +int UNPK_mkdir(void *opaque, const char *name) +{ + BAIL(PHYSFS_ERR_READ_ONLY, 0); +} /* UNPK_mkdir */ + + +int UNPK_stat(void *opaque, const char *path, PHYSFS_Stat *stat) +{ + UNPKinfo *info = (UNPKinfo *) opaque; + const UNPKentry *entry = findEntry(info, path); + + BAIL_IF_ERRPASS(!entry, 0); + + if (entry->tree.isdir) + { + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + stat->filesize = 0; + } /* if */ + else + { + stat->filetype = PHYSFS_FILETYPE_REGULAR; + stat->filesize = entry->size; + } /* else */ + + stat->modtime = entry->mtime; + stat->createtime = entry->ctime; + stat->accesstime = -1; + stat->readonly = 1; + + return 1; +} /* UNPK_stat */ + + +void *UNPK_addEntry(void *opaque, char *name, const int isdir, + const PHYSFS_sint64 ctime, const PHYSFS_sint64 mtime, + const PHYSFS_uint64 pos, const PHYSFS_uint64 len) +{ + UNPKinfo *info = (UNPKinfo *) opaque; + UNPKentry *entry; + + entry = (UNPKentry *) __PHYSFS_DirTreeAdd(&info->tree, name, isdir); + BAIL_IF_ERRPASS(!entry, NULL); + + entry->startPos = isdir ? 0 : pos; + entry->size = isdir ? 0 : len; + entry->ctime = ctime; + entry->mtime = mtime; + + return entry; +} /* UNPK_addEntry */ + + +void *UNPK_openArchive(PHYSFS_Io *io) +{ + UNPKinfo *info = (UNPKinfo *) allocator.Malloc(sizeof (UNPKinfo)); + BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + + if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (UNPKentry))) + { + allocator.Free(info); + return NULL; + } /* if */ + + info->io = io; + + return info; +} /* UNPK_openArchive */ + +/* end of physfs_archiver_unpacked.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_archiver_vdf.c b/Source/3rdParty/physfs/physfs_archiver_vdf.c new file mode 100644 index 0000000..99bbb2a --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_vdf.c @@ -0,0 +1,168 @@ +/* + * VDF support routines for PhysicsFS. + * + * This driver handles Gothic I/II VDF archives. + * This format (but not this driver) was designed by Piranha Bytes for + * use wih the ZenGin engine. + * + * This file was written by Francesco Bertolaccini, based on the UNPK archiver + * by Ryan C. Gordon and the works of degenerated1123 and Nico Bendlin. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if PHYSFS_SUPPORTS_VDF + +#include <time.h> + +#define VDF_COMMENT_LENGTH 256 +#define VDF_SIGNATURE_LENGTH 16 +#define VDF_ENTRY_NAME_LENGTH 64 +#define VDF_ENTRY_DIR 0x80000000 + +static const char* VDF_SIGNATURE_G1 = "PSVDSC_V2.00\r\n\r\n"; +static const char* VDF_SIGNATURE_G2 = "PSVDSC_V2.00\n\r\n\r"; + + +static inline int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val) +{ + PHYSFS_uint32 v; + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0); + *val = PHYSFS_swapULE32(v); + return 1; +} /* readui32 */ + + +static PHYSFS_sint64 vdfDosTimeToEpoch(const PHYSFS_uint32 dostime) +{ + /* VDF stores timestamps as 32bit DOS dates: the seconds are counted in + 2-seconds intervals and the years are counted since 1 Jan. 1980 */ + struct tm t; + memset(&t, '\0', sizeof (t)); + t.tm_year = ((int) ((dostime >> 25) & 0x7F)) + 80; /* 1980 to 1900 */ + t.tm_mon = ((int) ((dostime >> 21) & 0xF)) - 1; /* 1-12 to 0-11 */ + t.tm_mday = (int) ((dostime >> 16) & 0x1F); + t.tm_hour = (int) ((dostime >> 11) & 0x1F); + t.tm_min = (int) ((dostime >> 5) & 0x3F); + t.tm_sec = ((int) ((dostime >> 0) & 0x1F)) * 2; /* 2 seconds to 1. */ + return (PHYSFS_sint64) mktime(&t); +} /* vdfDosTimeToEpoch */ + + +static int vdfLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, + const PHYSFS_sint64 ts, void *arc) +{ + PHYSFS_uint32 i; + + for (i = 0; i < count; i++) + { + char name[VDF_ENTRY_NAME_LENGTH + 1]; + int namei; + PHYSFS_uint32 jump, size, type, attr; + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, sizeof (name) - 1), 0); + BAIL_IF_ERRPASS(!readui32(io, &jump), 0); + BAIL_IF_ERRPASS(!readui32(io, &size), 0); + BAIL_IF_ERRPASS(!readui32(io, &type), 0); + BAIL_IF_ERRPASS(!readui32(io, &attr), 0); + + /* Trim whitespace off the end of the filename */ + name[VDF_ENTRY_NAME_LENGTH] = '\0'; /* always null-terminated. */ + for (namei = VDF_ENTRY_NAME_LENGTH - 1; namei >= 0; namei--) + { + /* We assume the filenames are low-ASCII; consider the archive + corrupt if we see something above 127, since we don't know the + encoding. (We can change this later if we find out these exist + and are intended to be, say, latin-1 or UTF-8 encoding). */ + BAIL_IF(((PHYSFS_uint8) name[namei]) > 127, PHYSFS_ERR_CORRUPT, 0); + + if (name[namei] == ' ') + name[namei] = '\0'; + else + break; + } /* for */ + + BAIL_IF(!name[0], PHYSFS_ERR_CORRUPT, 0); + if (!(type & VDF_ENTRY_DIR)) { + BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, ts, ts, jump, size), 0); + } + } /* for */ + + return 1; +} /* vdfLoadEntries */ + + +static void *VDF_openArchive(PHYSFS_Io *io, const char *name, + int forWriting, int *claimed) +{ + PHYSFS_uint8 ignore[16]; + PHYSFS_uint8 sig[VDF_SIGNATURE_LENGTH]; + PHYSFS_uint32 count, timestamp, version, dataSize, rootCatOffset; + void *unpkarc; + + assert(io != NULL); /* shouldn't ever happen. */ + + BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); + + /* skip the 256-byte comment field. */ + BAIL_IF_ERRPASS(!io->seek(io, VDF_COMMENT_LENGTH), NULL); + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, sig, sizeof (sig)), NULL); + + if ((memcmp(sig, VDF_SIGNATURE_G1, VDF_SIGNATURE_LENGTH) != 0) && + (memcmp(sig, VDF_SIGNATURE_G2, VDF_SIGNATURE_LENGTH) != 0)) + { + BAIL(PHYSFS_ERR_UNSUPPORTED, NULL); + } /* if */ + + *claimed = 1; + + BAIL_IF_ERRPASS(!readui32(io, &count), NULL); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, ignore, 4), NULL); /* numFiles */ + BAIL_IF_ERRPASS(!readui32(io, ×tamp), NULL); + BAIL_IF_ERRPASS(!readui32(io, &dataSize), NULL); /* dataSize */ + BAIL_IF_ERRPASS(!readui32(io, &rootCatOffset), NULL); /* rootCatOff */ + BAIL_IF_ERRPASS(!readui32(io, &version), NULL); + + BAIL_IF(version != 0x50, PHYSFS_ERR_UNSUPPORTED, NULL); + + BAIL_IF_ERRPASS(!io->seek(io, rootCatOffset), NULL); + + unpkarc = UNPK_openArchive(io); + BAIL_IF_ERRPASS(!unpkarc, NULL); + + if (!vdfLoadEntries(io, count, vdfDosTimeToEpoch(timestamp), unpkarc)) + { + UNPK_abandonArchive(unpkarc); + return NULL; + } /* if */ + + return unpkarc; +} /* VDF_openArchive */ + + +const PHYSFS_Archiver __PHYSFS_Archiver_VDF = +{ + CURRENT_PHYSFS_ARCHIVER_API_VERSION, + { + "VDF", + "Gothic I/II engine format", + "Francesco Bertolaccini <bertolaccinifrancesco@gmail.com>", + "https://github.com/frabert", + 0, /* supportsSymlinks */ + }, + VDF_openArchive, + UNPK_enumerate, + UNPK_openRead, + UNPK_openWrite, + UNPK_openAppend, + UNPK_remove, + UNPK_mkdir, + UNPK_stat, + UNPK_closeArchive +}; + +#endif /* defined PHYSFS_SUPPORTS_VDF */ + +/* end of physfs_archiver_vdf.c ... */ diff --git a/Source/3rdParty/physfs/physfs_archiver_wad.c b/Source/3rdParty/physfs/physfs_archiver_wad.c new file mode 100644 index 0000000..b094c5b --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_wad.c @@ -0,0 +1,135 @@ +/* + * WAD support routines for PhysicsFS. + * + * This driver handles DOOM engine archives ("wads"). + * This format (but not this driver) was designed by id Software for use + * with the DOOM engine. + * The specs of the format are from the unofficial doom specs v1.666 + * found here: http://www.gamers.org/dhs/helpdocs/dmsp1666.html + * The format of the archive: (from the specs) + * + * A WAD file has three parts: + * (1) a twelve-byte header + * (2) one or more "lumps" + * (3) a directory or "info table" that contains the names, offsets, and + * sizes of all the lumps in the WAD + * + * The header consists of three four-byte parts: + * (a) an ASCII string which must be either "IWAD" or "PWAD" + * (b) a uint32 which is the number of lumps in the wad + * (c) a uint32 which is the file offset to the start of + * the directory + * + * The directory has one 16-byte entry for every lump. Each entry consists + * of three parts: + * + * (a) a uint32, the file offset to the start of the lump + * (b) a uint32, the size of the lump in bytes + * (c) an 8-byte ASCII string, the name of the lump, padded with zeros. + * For example, the "DEMO1" entry in hexadecimal would be + * (44 45 4D 4F 31 00 00 00) + * + * Note that there is no way to tell if an opened WAD archive is a + * IWAD or PWAD with this archiver. + * I couldn't think of a way to provide that information, without being too + * hacky. + * I don't think it's really that important though. + * + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Travis Wells, based on the GRP archiver by + * Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if PHYSFS_SUPPORTS_WAD + +static int wadLoadEntries(PHYSFS_Io *io, const PHYSFS_uint32 count, void *arc) +{ + PHYSFS_uint32 i; + for (i = 0; i < count; i++) + { + PHYSFS_uint32 pos; + PHYSFS_uint32 size; + char name[9]; + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &pos, 4), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &size, 4), 0); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, name, 8), 0); + + name[8] = '\0'; /* name might not be null-terminated in file. */ + size = PHYSFS_swapULE32(size); + pos = PHYSFS_swapULE32(pos); + BAIL_IF_ERRPASS(!UNPK_addEntry(arc, name, 0, -1, -1, pos, size), 0); + } /* for */ + + return 1; +} /* wadLoadEntries */ + + +static void *WAD_openArchive(PHYSFS_Io *io, const char *name, + int forWriting, int *claimed) +{ + PHYSFS_uint8 buf[4]; + PHYSFS_uint32 count; + PHYSFS_uint32 directoryOffset; + void *unpkarc; + + assert(io != NULL); /* shouldn't ever happen. */ + + BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, buf, sizeof (buf)), NULL); + if ((memcmp(buf, "IWAD", 4) != 0) && (memcmp(buf, "PWAD", 4) != 0)) + BAIL(PHYSFS_ERR_UNSUPPORTED, NULL); + + *claimed = 1; + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &count, sizeof (count)), NULL); + count = PHYSFS_swapULE32(count); + + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &directoryOffset, 4), 0); + directoryOffset = PHYSFS_swapULE32(directoryOffset); + + BAIL_IF_ERRPASS(!io->seek(io, directoryOffset), 0); + + unpkarc = UNPK_openArchive(io); + BAIL_IF_ERRPASS(!unpkarc, NULL); + + if (!wadLoadEntries(io, count, unpkarc)) + { + UNPK_abandonArchive(unpkarc); + return NULL; + } /* if */ + + return unpkarc; +} /* WAD_openArchive */ + + +const PHYSFS_Archiver __PHYSFS_Archiver_WAD = +{ + CURRENT_PHYSFS_ARCHIVER_API_VERSION, + { + "WAD", + "DOOM engine format", + "Travis Wells <traviswells@mchsi.com>", + "http://www.3dmm2.com/doom/", + 0, /* supportsSymlinks */ + }, + WAD_openArchive, + UNPK_enumerate, + UNPK_openRead, + UNPK_openWrite, + UNPK_openAppend, + UNPK_remove, + UNPK_mkdir, + UNPK_stat, + UNPK_closeArchive +}; + +#endif /* defined PHYSFS_SUPPORTS_WAD */ + +/* end of physfs_archiver_wad.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_archiver_zip.c b/Source/3rdParty/physfs/physfs_archiver_zip.c new file mode 100644 index 0000000..9972628 --- /dev/null +++ b/Source/3rdParty/physfs/physfs_archiver_zip.c @@ -0,0 +1,1710 @@ +/* + * ZIP support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon, with some peeking at "unzip.c" + * by Gilles Vollant. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#if PHYSFS_SUPPORTS_ZIP + +#include <errno.h> +#include <time.h> + +#include "physfs_miniz.h" + +/* + * A buffer of ZIP_READBUFSIZE is allocated for each compressed file opened, + * and is freed when you close the file; compressed data is read into + * this buffer, and then is decompressed into the buffer passed to + * PHYSFS_read(). + * + * Uncompressed entries in a zipfile do not allocate this buffer; they just + * read data directly into the buffer passed to PHYSFS_read(). + * + * Depending on your speed and memory requirements, you should tweak this + * value. + */ +#define ZIP_READBUFSIZE (16 * 1024) + + +/* + * Entries are "unresolved" until they are first opened. At that time, + * local file headers parsed/validated, data offsets will be updated to look + * at the actual file data instead of the header, and symlinks will be + * followed and optimized. This means that we don't seek and read around the + * archive until forced to do so, and after the first time, we had to do + * less reading and parsing, which is very CD-ROM friendly. + */ +typedef enum +{ + ZIP_UNRESOLVED_FILE, + ZIP_UNRESOLVED_SYMLINK, + ZIP_RESOLVING, + ZIP_RESOLVED, + ZIP_DIRECTORY, + ZIP_BROKEN_FILE, + ZIP_BROKEN_SYMLINK +} ZipResolveType; + + +/* + * One ZIPentry is kept for each file in an open ZIP archive. + */ +typedef struct _ZIPentry +{ + __PHYSFS_DirTreeEntry tree; /* manages directory tree */ + struct _ZIPentry *symlink; /* NULL or file we symlink to */ + ZipResolveType resolved; /* Have we resolved file/symlink? */ + PHYSFS_uint64 offset; /* offset of data in archive */ + PHYSFS_uint16 version; /* version made by */ + PHYSFS_uint16 version_needed; /* version needed to extract */ + PHYSFS_uint16 general_bits; /* general purpose bits */ + PHYSFS_uint16 compression_method; /* compression method */ + PHYSFS_uint32 crc; /* crc-32 */ + PHYSFS_uint64 compressed_size; /* compressed size */ + PHYSFS_uint64 uncompressed_size; /* uncompressed size */ + PHYSFS_sint64 last_mod_time; /* last file mod time */ + PHYSFS_uint32 dos_mod_time; /* original MS-DOS style mod time */ +} ZIPentry; + +/* + * One ZIPinfo is kept for each open ZIP archive. + */ +typedef struct +{ + __PHYSFS_DirTree tree; /* manages directory tree. */ + PHYSFS_Io *io; /* the i/o interface for this archive. */ + int zip64; /* non-zero if this is a Zip64 archive. */ + int has_crypto; /* non-zero if any entry uses encryption. */ +} ZIPinfo; + +/* + * One ZIPfileinfo is kept for each open file in a ZIP archive. + */ +typedef struct +{ + ZIPentry *entry; /* Info on file. */ + PHYSFS_Io *io; /* physical file handle. */ + PHYSFS_uint32 compressed_position; /* offset in compressed data. */ + PHYSFS_uint32 uncompressed_position; /* tell() position. */ + PHYSFS_uint8 *buffer; /* decompression buffer. */ + PHYSFS_uint32 crypto_keys[3]; /* for "traditional" crypto. */ + PHYSFS_uint32 initial_crypto_keys[3]; /* for "traditional" crypto. */ + z_stream stream; /* zlib stream state. */ +} ZIPfileinfo; + + +/* Magic numbers... */ +#define ZIP_LOCAL_FILE_SIG 0x04034b50 +#define ZIP_CENTRAL_DIR_SIG 0x02014b50 +#define ZIP_END_OF_CENTRAL_DIR_SIG 0x06054b50 +#define ZIP64_END_OF_CENTRAL_DIR_SIG 0x06064b50 +#define ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG 0x07064b50 +#define ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG 0x0001 + +/* compression methods... */ +#define COMPMETH_NONE 0 +/* ...and others... */ + + +#define UNIX_FILETYPE_MASK 0170000 +#define UNIX_FILETYPE_SYMLINK 0120000 + +#define ZIP_GENERAL_BITS_TRADITIONAL_CRYPTO (1 << 0) +#define ZIP_GENERAL_BITS_IGNORE_LOCAL_HEADER (1 << 3) + +/* support for "traditional" PKWARE encryption. */ +static int zip_entry_is_tradional_crypto(const ZIPentry *entry) +{ + return (entry->general_bits & ZIP_GENERAL_BITS_TRADITIONAL_CRYPTO) != 0; +} /* zip_entry_is_traditional_crypto */ + +static int zip_entry_ignore_local_header(const ZIPentry *entry) +{ + return (entry->general_bits & ZIP_GENERAL_BITS_IGNORE_LOCAL_HEADER) != 0; +} /* zip_entry_is_traditional_crypto */ + +static PHYSFS_uint32 zip_crypto_crc32(const PHYSFS_uint32 crc, const PHYSFS_uint8 val) +{ + int i; + PHYSFS_uint32 xorval = (crc ^ ((PHYSFS_uint32) val)) & 0xFF; + for (i = 0; i < 8; i++) + xorval = ((xorval & 1) ? (0xEDB88320 ^ (xorval >> 1)) : (xorval >> 1)); + return xorval ^ (crc >> 8); +} /* zip_crc32 */ + +static void zip_update_crypto_keys(PHYSFS_uint32 *keys, const PHYSFS_uint8 val) +{ + keys[0] = zip_crypto_crc32(keys[0], val); + keys[1] = keys[1] + (keys[0] & 0x000000FF); + keys[1] = (keys[1] * 134775813) + 1; + keys[2] = zip_crypto_crc32(keys[2], (PHYSFS_uint8) ((keys[1] >> 24) & 0xFF)); +} /* zip_update_crypto_keys */ + +static PHYSFS_uint8 zip_decrypt_byte(const PHYSFS_uint32 *keys) +{ + const PHYSFS_uint16 tmp = keys[2] | 2; + return (PHYSFS_uint8) ((tmp * (tmp ^ 1)) >> 8); +} /* zip_decrypt_byte */ + +static PHYSFS_sint64 zip_read_decrypt(ZIPfileinfo *finfo, void *buf, PHYSFS_uint64 len) +{ + PHYSFS_Io *io = finfo->io; + const PHYSFS_sint64 br = io->read(io, buf, len); + + /* Decompression the new data if necessary. */ + if (zip_entry_is_tradional_crypto(finfo->entry) && (br > 0)) + { + PHYSFS_uint32 *keys = finfo->crypto_keys; + PHYSFS_uint8 *ptr = (PHYSFS_uint8 *) buf; + PHYSFS_sint64 i; + for (i = 0; i < br; i++, ptr++) + { + const PHYSFS_uint8 ch = *ptr ^ zip_decrypt_byte(keys); + zip_update_crypto_keys(keys, ch); + *ptr = ch; + } /* for */ + } /* if */ + + return br; +} /* zip_read_decrypt */ + +static int zip_prep_crypto_keys(ZIPfileinfo *finfo, const PHYSFS_uint8 *crypto_header, const PHYSFS_uint8 *password) +{ + /* It doesn't appear to be documented in PKWare's APPNOTE.TXT, but you + need to use a different byte in the header to verify the password + if general purpose bit 3 is set. Discovered this from Info-Zip. + That's what the (verifier) value is doing, below. */ + + PHYSFS_uint32 *keys = finfo->crypto_keys; + const ZIPentry *entry = finfo->entry; + const int usedate = zip_entry_ignore_local_header(entry); + const PHYSFS_uint8 verifier = (PHYSFS_uint8) ((usedate ? (entry->dos_mod_time >> 8) : (entry->crc >> 24)) & 0xFF); + PHYSFS_uint8 finalbyte = 0; + int i = 0; + + /* initialize vector with defaults, then password, then header. */ + keys[0] = 305419896; + keys[1] = 591751049; + keys[2] = 878082192; + + while (*password) + zip_update_crypto_keys(keys, *(password++)); + + for (i = 0; i < 12; i++) + { + const PHYSFS_uint8 c = crypto_header[i] ^ zip_decrypt_byte(keys); + zip_update_crypto_keys(keys, c); + finalbyte = c; + } /* for */ + + /* you have a 1/256 chance of passing this test incorrectly. :/ */ + if (finalbyte != verifier) + BAIL(PHYSFS_ERR_BAD_PASSWORD, 0); + + /* save the initial vector for seeking purposes. Not secure!! */ + memcpy(finfo->initial_crypto_keys, finfo->crypto_keys, 12); + return 1; +} /* zip_prep_crypto_keys */ + + +/* + * Bridge physfs allocation functions to zlib's format... + */ +static voidpf zlibPhysfsAlloc(voidpf opaque, uInt items, uInt size) +{ + return ((PHYSFS_Allocator *) opaque)->Malloc(items * size); +} /* zlibPhysfsAlloc */ + +/* + * Bridge physfs allocation functions to zlib's format... + */ +static void zlibPhysfsFree(voidpf opaque, voidpf address) +{ + ((PHYSFS_Allocator *) opaque)->Free(address); +} /* zlibPhysfsFree */ + + +/* + * Construct a new z_stream to a sane state. + */ +static void initializeZStream(z_stream *pstr) +{ + memset(pstr, '\0', sizeof (z_stream)); + pstr->zalloc = zlibPhysfsAlloc; + pstr->zfree = zlibPhysfsFree; + pstr->opaque = &allocator; +} /* initializeZStream */ + + +static PHYSFS_ErrorCode zlib_error_code(int rc) +{ + switch (rc) + { + case Z_OK: return PHYSFS_ERR_OK; /* not an error. */ + case Z_STREAM_END: return PHYSFS_ERR_OK; /* not an error. */ + case Z_ERRNO: return PHYSFS_ERR_IO; + case Z_MEM_ERROR: return PHYSFS_ERR_OUT_OF_MEMORY; + default: return PHYSFS_ERR_CORRUPT; + } /* switch */ +} /* zlib_error_string */ + + +/* + * Wrap all zlib calls in this, so the physfs error state is set appropriately. + */ +static int zlib_err(const int rc) +{ + PHYSFS_setErrorCode(zlib_error_code(rc)); + return rc; +} /* zlib_err */ + +/* + * Read an unsigned 64-bit int and swap to native byte order. + */ +static int readui64(PHYSFS_Io *io, PHYSFS_uint64 *val) +{ + PHYSFS_uint64 v; + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0); + *val = PHYSFS_swapULE64(v); + return 1; +} /* readui64 */ + +/* + * Read an unsigned 32-bit int and swap to native byte order. + */ +static int readui32(PHYSFS_Io *io, PHYSFS_uint32 *val) +{ + PHYSFS_uint32 v; + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0); + *val = PHYSFS_swapULE32(v); + return 1; +} /* readui32 */ + + +/* + * Read an unsigned 16-bit int and swap to native byte order. + */ +static int readui16(PHYSFS_Io *io, PHYSFS_uint16 *val) +{ + PHYSFS_uint16 v; + BAIL_IF_ERRPASS(!__PHYSFS_readAll(io, &v, sizeof (v)), 0); + *val = PHYSFS_swapULE16(v); + return 1; +} /* readui16 */ + + +static PHYSFS_sint64 ZIP_read(PHYSFS_Io *_io, void *buf, PHYSFS_uint64 len) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque; + ZIPentry *entry = finfo->entry; + PHYSFS_sint64 retval = 0; + PHYSFS_sint64 maxread = (PHYSFS_sint64) len; + PHYSFS_sint64 avail = entry->uncompressed_size - + finfo->uncompressed_position; + + if (avail < maxread) + maxread = avail; + + BAIL_IF_ERRPASS(maxread == 0, 0); /* quick rejection. */ + + if (entry->compression_method == COMPMETH_NONE) + retval = zip_read_decrypt(finfo, buf, maxread); + else + { + finfo->stream.next_out = buf; + finfo->stream.avail_out = (uInt) maxread; + + while (retval < maxread) + { + const PHYSFS_uint32 before = (PHYSFS_uint32) finfo->stream.total_out; + int rc; + + if (finfo->stream.avail_in == 0) + { + PHYSFS_sint64 br; + + br = entry->compressed_size - finfo->compressed_position; + if (br > 0) + { + if (br > ZIP_READBUFSIZE) + br = ZIP_READBUFSIZE; + + br = zip_read_decrypt(finfo, finfo->buffer, (PHYSFS_uint64) br); + if (br <= 0) + break; + + finfo->compressed_position += (PHYSFS_uint32) br; + finfo->stream.next_in = finfo->buffer; + finfo->stream.avail_in = (unsigned int) br; + } /* if */ + } /* if */ + + rc = zlib_err(inflate(&finfo->stream, Z_SYNC_FLUSH)); + retval += (finfo->stream.total_out - before); + + if (rc != Z_OK) + break; + } /* while */ + } /* else */ + + if (retval > 0) + finfo->uncompressed_position += (PHYSFS_uint32) retval; + + return retval; +} /* ZIP_read */ + + +static PHYSFS_sint64 ZIP_write(PHYSFS_Io *io, const void *b, PHYSFS_uint64 len) +{ + BAIL(PHYSFS_ERR_READ_ONLY, -1); +} /* ZIP_write */ + + +static PHYSFS_sint64 ZIP_tell(PHYSFS_Io *io) +{ + return ((ZIPfileinfo *) io->opaque)->uncompressed_position; +} /* ZIP_tell */ + + +static int ZIP_seek(PHYSFS_Io *_io, PHYSFS_uint64 offset) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) _io->opaque; + ZIPentry *entry = finfo->entry; + PHYSFS_Io *io = finfo->io; + const int encrypted = zip_entry_is_tradional_crypto(entry); + + BAIL_IF(offset > entry->uncompressed_size, PHYSFS_ERR_PAST_EOF, 0); + + if (!encrypted && (entry->compression_method == COMPMETH_NONE)) + { + PHYSFS_sint64 newpos = offset + entry->offset; + BAIL_IF_ERRPASS(!io->seek(io, newpos), 0); + finfo->uncompressed_position = (PHYSFS_uint32) offset; + } /* if */ + + else + { + /* + * If seeking backwards, we need to redecode the file + * from the start and throw away the compressed bits until we hit + * the offset we need. If seeking forward, we still need to + * decode, but we don't rewind first. + */ + if (offset < finfo->uncompressed_position) + { + /* we do a copy so state is sane if inflateInit2() fails. */ + z_stream str; + initializeZStream(&str); + if (zlib_err(inflateInit2(&str, -MAX_WBITS)) != Z_OK) + return 0; + + if (!io->seek(io, entry->offset + (encrypted ? 12 : 0))) + return 0; + + inflateEnd(&finfo->stream); + memcpy(&finfo->stream, &str, sizeof (z_stream)); + finfo->uncompressed_position = finfo->compressed_position = 0; + + if (encrypted) + memcpy(finfo->crypto_keys, finfo->initial_crypto_keys, 12); + } /* if */ + + while (finfo->uncompressed_position != offset) + { + PHYSFS_uint8 buf[512]; + PHYSFS_uint32 maxread; + + maxread = (PHYSFS_uint32) (offset - finfo->uncompressed_position); + if (maxread > sizeof (buf)) + maxread = sizeof (buf); + + if (ZIP_read(_io, buf, maxread) != maxread) + return 0; + } /* while */ + } /* else */ + + return 1; +} /* ZIP_seek */ + + +static PHYSFS_sint64 ZIP_length(PHYSFS_Io *io) +{ + const ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque; + return (PHYSFS_sint64) finfo->entry->uncompressed_size; +} /* ZIP_length */ + + +static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry); + +static PHYSFS_Io *ZIP_duplicate(PHYSFS_Io *io) +{ + ZIPfileinfo *origfinfo = (ZIPfileinfo *) io->opaque; + PHYSFS_Io *retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); + ZIPfileinfo *finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo)); + GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, failed); + GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, failed); + memset(finfo, '\0', sizeof (*finfo)); + + finfo->entry = origfinfo->entry; + finfo->io = zip_get_io(origfinfo->io, NULL, finfo->entry); + GOTO_IF_ERRPASS(!finfo->io, failed); + + initializeZStream(&finfo->stream); + if (finfo->entry->compression_method != COMPMETH_NONE) + { + finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE); + GOTO_IF(!finfo->buffer, PHYSFS_ERR_OUT_OF_MEMORY, failed); + if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK) + goto failed; + } /* if */ + + memcpy(retval, io, sizeof (PHYSFS_Io)); + retval->opaque = finfo; + return retval; + +failed: + if (finfo != NULL) + { + if (finfo->io != NULL) + finfo->io->destroy(finfo->io); + + if (finfo->buffer != NULL) + { + allocator.Free(finfo->buffer); + inflateEnd(&finfo->stream); + } /* if */ + + allocator.Free(finfo); + } /* if */ + + if (retval != NULL) + allocator.Free(retval); + + return NULL; +} /* ZIP_duplicate */ + +static int ZIP_flush(PHYSFS_Io *io) { return 1; /* no write support. */ } + +static void ZIP_destroy(PHYSFS_Io *io) +{ + ZIPfileinfo *finfo = (ZIPfileinfo *) io->opaque; + finfo->io->destroy(finfo->io); + + if (finfo->entry->compression_method != COMPMETH_NONE) + inflateEnd(&finfo->stream); + + if (finfo->buffer != NULL) + allocator.Free(finfo->buffer); + + allocator.Free(finfo); + allocator.Free(io); +} /* ZIP_destroy */ + + +static const PHYSFS_Io ZIP_Io = +{ + CURRENT_PHYSFS_IO_API_VERSION, NULL, + ZIP_read, + ZIP_write, + ZIP_seek, + ZIP_tell, + ZIP_length, + ZIP_duplicate, + ZIP_flush, + ZIP_destroy +}; + + + +static PHYSFS_sint64 zip_find_end_of_central_dir(PHYSFS_Io *io, PHYSFS_sint64 *len) +{ + PHYSFS_uint8 buf[256]; + PHYSFS_uint8 extra[4] = { 0, 0, 0, 0 }; + PHYSFS_sint32 i = 0; + PHYSFS_sint64 filelen; + PHYSFS_sint64 filepos; + PHYSFS_sint32 maxread; + PHYSFS_sint32 totalread = 0; + int found = 0; + + filelen = io->length(io); + BAIL_IF_ERRPASS(filelen == -1, -1); + + /* + * Jump to the end of the file and start reading backwards. + * The last thing in the file is the zipfile comment, which is variable + * length, and the field that specifies its size is before it in the + * file (argh!)...this means that we need to scan backwards until we + * hit the end-of-central-dir signature. We can then sanity check that + * the comment was as big as it should be to make sure we're in the + * right place. The comment length field is 16 bits, so we can stop + * searching for that signature after a little more than 64k at most, + * and call it a corrupted zipfile. + */ + + if (sizeof (buf) < filelen) + { + filepos = filelen - sizeof (buf); + maxread = sizeof (buf); + } /* if */ + else + { + filepos = 0; + maxread = (PHYSFS_uint32) filelen; + } /* else */ + + while ((totalread < filelen) && (totalread < 65557)) + { + BAIL_IF_ERRPASS(!io->seek(io, filepos), -1); + + /* make sure we catch a signature between buffers. */ + if (totalread != 0) + { + if (!__PHYSFS_readAll(io, buf, maxread - 4)) + return -1; + memcpy(&buf[maxread - 4], &extra, sizeof (extra)); + totalread += maxread - 4; + } /* if */ + else + { + if (!__PHYSFS_readAll(io, buf, maxread)) + return -1; + totalread += maxread; + } /* else */ + + memcpy(&extra, buf, sizeof (extra)); + + for (i = maxread - 4; i > 0; i--) + { + if ((buf[i + 0] == 0x50) && + (buf[i + 1] == 0x4B) && + (buf[i + 2] == 0x05) && + (buf[i + 3] == 0x06) ) + { + found = 1; /* that's the signature! */ + break; + } /* if */ + } /* for */ + + if (found) + break; + + filepos -= (maxread - 4); + if (filepos < 0) + filepos = 0; + } /* while */ + + BAIL_IF(!found, PHYSFS_ERR_UNSUPPORTED, -1); + + if (len != NULL) + *len = filelen; + + return (filepos + i); +} /* zip_find_end_of_central_dir */ + + +static int isZip(PHYSFS_Io *io) +{ + PHYSFS_uint32 sig = 0; + int retval = 0; + + /* + * The first thing in a zip file might be the signature of the + * first local file record, so it makes for a quick determination. + */ + if (readui32(io, &sig)) + { + retval = (sig == ZIP_LOCAL_FILE_SIG); + if (!retval) + { + /* + * No sig...might be a ZIP with data at the start + * (a self-extracting executable, etc), so we'll have to do + * it the hard way... + */ + retval = (zip_find_end_of_central_dir(io, NULL) != -1); + } /* if */ + } /* if */ + + return retval; +} /* isZip */ + + +/* Convert paths from old, buggy DOS zippers... */ +static void zip_convert_dos_path(const PHYSFS_uint16 entryversion, char *path) +{ + const PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((entryversion >> 8) & 0xFF); + if (hosttype == 0) /* FS_FAT_ */ + { + while (*path) + { + if (*path == '\\') + *path = '/'; + path++; + } /* while */ + } /* if */ +} /* zip_convert_dos_path */ + + +static void zip_expand_symlink_path(char *path) +{ + char *ptr = path; + char *prevptr = path; + + while (1) + { + ptr = strchr(ptr, '/'); + if (ptr == NULL) + break; + + if (*(ptr + 1) == '.') + { + if (*(ptr + 2) == '/') + { + /* current dir in middle of string: ditch it. */ + memmove(ptr, ptr + 2, strlen(ptr + 2) + 1); + } /* else if */ + + else if (*(ptr + 2) == '\0') + { + /* current dir at end of string: ditch it. */ + *ptr = '\0'; + } /* else if */ + + else if (*(ptr + 2) == '.') + { + if (*(ptr + 3) == '/') + { + /* parent dir in middle: move back one, if possible. */ + memmove(prevptr, ptr + 4, strlen(ptr + 4) + 1); + ptr = prevptr; + while (prevptr != path) + { + prevptr--; + if (*prevptr == '/') + { + prevptr++; + break; + } /* if */ + } /* while */ + } /* if */ + + if (*(ptr + 3) == '\0') + { + /* parent dir at end: move back one, if possible. */ + *prevptr = '\0'; + } /* if */ + } /* if */ + } /* if */ + else + { + prevptr = ptr; + ptr++; + } /* else */ + } /* while */ +} /* zip_expand_symlink_path */ + + +static inline ZIPentry *zip_find_entry(ZIPinfo *info, const char *path) +{ + return (ZIPentry *) __PHYSFS_DirTreeFind(&info->tree, path); +} /* zip_find_entry */ + +/* (forward reference: zip_follow_symlink and zip_resolve call each other.) */ +static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry); + +/* + * Look for the entry named by (path). If it exists, resolve it, and return + * a pointer to that entry. If it's another symlink, keep resolving until you + * hit a real file and then return a pointer to the final non-symlink entry. + * If there's a problem, return NULL. + */ +static ZIPentry *zip_follow_symlink(PHYSFS_Io *io, ZIPinfo *info, char *path) +{ + ZIPentry *entry; + + zip_expand_symlink_path(path); + entry = zip_find_entry(info, path); + if (entry != NULL) + { + if (!zip_resolve(io, info, entry)) /* recursive! */ + entry = NULL; + else + { + if (entry->symlink != NULL) + entry = entry->symlink; + } /* else */ + } /* if */ + + return entry; +} /* zip_follow_symlink */ + + +static int zip_resolve_symlink(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry) +{ + const size_t size = (size_t) entry->uncompressed_size; + char *path = NULL; + int rc = 0; + + /* + * We've already parsed the local file header of the symlink at this + * point. Now we need to read the actual link from the file data and + * follow it. + */ + + BAIL_IF_ERRPASS(!io->seek(io, entry->offset), 0); + + path = (char *) __PHYSFS_smallAlloc(size + 1); + BAIL_IF(!path, PHYSFS_ERR_OUT_OF_MEMORY, 0); + + if (entry->compression_method == COMPMETH_NONE) + rc = __PHYSFS_readAll(io, path, size); + + else /* symlink target path is compressed... */ + { + z_stream stream; + const size_t complen = (size_t) entry->compressed_size; + PHYSFS_uint8 *compressed = (PHYSFS_uint8*) __PHYSFS_smallAlloc(complen); + if (compressed != NULL) + { + if (__PHYSFS_readAll(io, compressed, complen)) + { + initializeZStream(&stream); + stream.next_in = compressed; + stream.avail_in = (unsigned int) complen; + stream.next_out = (unsigned char *) path; + stream.avail_out = (unsigned int) size; + if (zlib_err(inflateInit2(&stream, -MAX_WBITS)) == Z_OK) + { + rc = zlib_err(inflate(&stream, Z_FINISH)); + inflateEnd(&stream); + + /* both are acceptable outcomes... */ + rc = ((rc == Z_OK) || (rc == Z_STREAM_END)); + } /* if */ + } /* if */ + __PHYSFS_smallFree(compressed); + } /* if */ + } /* else */ + + if (rc) + { + path[entry->uncompressed_size] = '\0'; /* null-terminate it. */ + zip_convert_dos_path(entry->version, path); + entry->symlink = zip_follow_symlink(io, info, path); + } /* else */ + + __PHYSFS_smallFree(path); + + return (entry->symlink != NULL); +} /* zip_resolve_symlink */ + + +/* + * Parse the local file header of an entry, and update entry->offset. + */ +static int zip_parse_local(PHYSFS_Io *io, ZIPentry *entry) +{ + PHYSFS_uint32 ui32; + PHYSFS_uint16 ui16; + PHYSFS_uint16 fnamelen; + PHYSFS_uint16 extralen; + + /* + * crc and (un)compressed_size are always zero if this is a "JAR" + * archive created with Sun's Java tools, apparently. We only + * consider this archive corrupted if those entries don't match and + * aren't zero. That seems to work well. + * We also ignore a mismatch if the value is 0xFFFFFFFF here, since it's + * possible that's a Zip64 thing. + */ + + /* !!! FIXME: apparently these are zero if general purpose bit 3 is set, + !!! FIXME: which is probably true for Jar files, fwiw, but we don't + !!! FIXME: care about these values anyhow. */ + + BAIL_IF_ERRPASS(!io->seek(io, entry->offset), 0); + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + BAIL_IF(ui32 != ZIP_LOCAL_FILE_SIG, PHYSFS_ERR_CORRUPT, 0); + BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); + BAIL_IF(ui16 != entry->version_needed, PHYSFS_ERR_CORRUPT, 0); + BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); /* general bits. */ + BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); + BAIL_IF(ui16 != entry->compression_method, PHYSFS_ERR_CORRUPT, 0); + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); /* date/time */ + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + BAIL_IF(ui32 && (ui32 != entry->crc), PHYSFS_ERR_CORRUPT, 0); + + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + BAIL_IF(ui32 && (ui32 != 0xFFFFFFFF) && + (ui32 != entry->compressed_size), PHYSFS_ERR_CORRUPT, 0); + + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + BAIL_IF(ui32 && (ui32 != 0xFFFFFFFF) && + (ui32 != entry->uncompressed_size), PHYSFS_ERR_CORRUPT, 0); + + BAIL_IF_ERRPASS(!readui16(io, &fnamelen), 0); + BAIL_IF_ERRPASS(!readui16(io, &extralen), 0); + + entry->offset += fnamelen + extralen + 30; + return 1; +} /* zip_parse_local */ + + +static int zip_resolve(PHYSFS_Io *io, ZIPinfo *info, ZIPentry *entry) +{ + int retval = 1; + const ZipResolveType resolve_type = entry->resolved; + + if (resolve_type == ZIP_DIRECTORY) + return 1; /* we're good. */ + + /* Don't bother if we've failed to resolve this entry before. */ + BAIL_IF(resolve_type == ZIP_BROKEN_FILE, PHYSFS_ERR_CORRUPT, 0); + BAIL_IF(resolve_type == ZIP_BROKEN_SYMLINK, PHYSFS_ERR_CORRUPT, 0); + + /* uhoh...infinite symlink loop! */ + BAIL_IF(resolve_type == ZIP_RESOLVING, PHYSFS_ERR_SYMLINK_LOOP, 0); + + /* + * We fix up the offset to point to the actual data on the + * first open, since we don't want to seek across the whole file on + * archive open (can be SLOW on large, CD-stored files), but we + * need to check the local file header...not just for corruption, + * but since it stores offset info the central directory does not. + */ + if (resolve_type != ZIP_RESOLVED) + { + if (entry->tree.isdir) /* an ancestor dir that DirTree filled in? */ + { + entry->resolved = ZIP_DIRECTORY; + return 1; + } /* if */ + + retval = zip_parse_local(io, entry); + if (retval) + { + /* + * If it's a symlink, find the original file. This will cause + * resolution of other entries (other symlinks and, eventually, + * the real file) if all goes well. + */ + if (resolve_type == ZIP_UNRESOLVED_SYMLINK) + retval = zip_resolve_symlink(io, info, entry); + } /* if */ + + if (resolve_type == ZIP_UNRESOLVED_SYMLINK) + entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_SYMLINK); + else if (resolve_type == ZIP_UNRESOLVED_FILE) + entry->resolved = ((retval) ? ZIP_RESOLVED : ZIP_BROKEN_FILE); + } /* if */ + + return retval; +} /* zip_resolve */ + + +static int zip_entry_is_symlink(const ZIPentry *entry) +{ + return ((entry->resolved == ZIP_UNRESOLVED_SYMLINK) || + (entry->resolved == ZIP_BROKEN_SYMLINK) || + (entry->symlink)); +} /* zip_entry_is_symlink */ + + +static int zip_version_does_symlinks(PHYSFS_uint32 version) +{ + int retval = 0; + PHYSFS_uint8 hosttype = (PHYSFS_uint8) ((version >> 8) & 0xFF); + + switch (hosttype) + { + /* + * These are the platforms that can NOT build an archive with + * symlinks, according to the Info-ZIP project. + */ + case 0: /* FS_FAT_ */ + case 1: /* AMIGA_ */ + case 2: /* VMS_ */ + case 4: /* VM_CSM_ */ + case 6: /* FS_HPFS_ */ + case 11: /* FS_NTFS_ */ + case 14: /* FS_VFAT_ */ + case 13: /* ACORN_ */ + case 15: /* MVS_ */ + case 18: /* THEOS_ */ + break; /* do nothing. */ + + default: /* assume the rest to be unix-like. */ + retval = 1; + break; + } /* switch */ + + return retval; +} /* zip_version_does_symlinks */ + + +static inline int zip_has_symlink_attr(const ZIPentry *entry, + const PHYSFS_uint32 extern_attr) +{ + PHYSFS_uint16 xattr = ((extern_attr >> 16) & 0xFFFF); + return ( (zip_version_does_symlinks(entry->version)) && + (entry->uncompressed_size > 0) && + ((xattr & UNIX_FILETYPE_MASK) == UNIX_FILETYPE_SYMLINK) ); +} /* zip_has_symlink_attr */ + + +static PHYSFS_sint64 zip_dos_time_to_physfs_time(PHYSFS_uint32 dostime) +{ + PHYSFS_uint32 dosdate; + struct tm unixtime; + memset(&unixtime, '\0', sizeof (unixtime)); + + dosdate = (PHYSFS_uint32) ((dostime >> 16) & 0xFFFF); + dostime &= 0xFFFF; + + /* dissect date */ + unixtime.tm_year = ((dosdate >> 9) & 0x7F) + 80; + unixtime.tm_mon = ((dosdate >> 5) & 0x0F) - 1; + unixtime.tm_mday = ((dosdate ) & 0x1F); + + /* dissect time */ + unixtime.tm_hour = ((dostime >> 11) & 0x1F); + unixtime.tm_min = ((dostime >> 5) & 0x3F); + unixtime.tm_sec = ((dostime << 1) & 0x3E); + + /* let mktime calculate daylight savings time. */ + unixtime.tm_isdst = -1; + + return ((PHYSFS_sint64) mktime(&unixtime)); +} /* zip_dos_time_to_physfs_time */ + + +static ZIPentry *zip_load_entry(ZIPinfo *info, const int zip64, + const PHYSFS_uint64 ofs_fixup) +{ + PHYSFS_Io *io = info->io; + ZIPentry entry; + ZIPentry *retval = NULL; + PHYSFS_uint16 fnamelen, extralen, commentlen; + PHYSFS_uint32 external_attr; + PHYSFS_uint32 starting_disk; + PHYSFS_uint64 offset; + PHYSFS_uint16 ui16; + PHYSFS_uint32 ui32; + PHYSFS_sint64 si64; + char *name = NULL; + int isdir = 0; + + /* sanity check with central directory signature... */ + BAIL_IF_ERRPASS(!readui32(io, &ui32), NULL); + BAIL_IF(ui32 != ZIP_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, NULL); + + memset(&entry, '\0', sizeof (entry)); + + /* Get the pertinent parts of the record... */ + BAIL_IF_ERRPASS(!readui16(io, &entry.version), NULL); + BAIL_IF_ERRPASS(!readui16(io, &entry.version_needed), NULL); + BAIL_IF_ERRPASS(!readui16(io, &entry.general_bits), NULL); /* general bits */ + BAIL_IF_ERRPASS(!readui16(io, &entry.compression_method), NULL); + BAIL_IF_ERRPASS(!readui32(io, &entry.dos_mod_time), NULL); + entry.last_mod_time = zip_dos_time_to_physfs_time(entry.dos_mod_time); + BAIL_IF_ERRPASS(!readui32(io, &entry.crc), NULL); + BAIL_IF_ERRPASS(!readui32(io, &ui32), NULL); + entry.compressed_size = (PHYSFS_uint64) ui32; + BAIL_IF_ERRPASS(!readui32(io, &ui32), NULL); + entry.uncompressed_size = (PHYSFS_uint64) ui32; + BAIL_IF_ERRPASS(!readui16(io, &fnamelen), NULL); + BAIL_IF_ERRPASS(!readui16(io, &extralen), NULL); + BAIL_IF_ERRPASS(!readui16(io, &commentlen), NULL); + BAIL_IF_ERRPASS(!readui16(io, &ui16), NULL); + starting_disk = (PHYSFS_uint32) ui16; + BAIL_IF_ERRPASS(!readui16(io, &ui16), NULL); /* internal file attribs */ + BAIL_IF_ERRPASS(!readui32(io, &external_attr), NULL); + BAIL_IF_ERRPASS(!readui32(io, &ui32), NULL); + offset = (PHYSFS_uint64) ui32; + + name = (char *) __PHYSFS_smallAlloc(fnamelen + 1); + BAIL_IF(!name, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + if (!__PHYSFS_readAll(io, name, fnamelen)) + { + __PHYSFS_smallFree(name); + return NULL; + } /* if */ + + if (name[fnamelen - 1] == '/') + { + name[fnamelen - 1] = '\0'; + isdir = 1; + } /* if */ + name[fnamelen] = '\0'; /* null-terminate the filename. */ + + zip_convert_dos_path(entry.version, name); + + retval = (ZIPentry *) __PHYSFS_DirTreeAdd(&info->tree, name, isdir); + __PHYSFS_smallFree(name); + + BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + + /* It's okay to BAIL without freeing retval, because it's stored in the + __PHYSFS_DirTree and will be freed later anyhow. */ + BAIL_IF(retval->last_mod_time != 0, PHYSFS_ERR_CORRUPT, NULL); /* dupe? */ + + /* Move the data we already read into place in the official object. */ + memcpy(((PHYSFS_uint8 *) retval) + sizeof (__PHYSFS_DirTreeEntry), + ((PHYSFS_uint8 *) &entry) + sizeof (__PHYSFS_DirTreeEntry), + sizeof (*retval) - sizeof (__PHYSFS_DirTreeEntry)); + + retval->symlink = NULL; /* will be resolved later, if necessary. */ + + if (isdir) + retval->resolved = ZIP_DIRECTORY; + else + { + retval->resolved = (zip_has_symlink_attr(retval, external_attr)) ? + ZIP_UNRESOLVED_SYMLINK : ZIP_UNRESOLVED_FILE; + } /* else */ + + si64 = io->tell(io); + BAIL_IF_ERRPASS(si64 == -1, NULL); + + /* If the actual sizes didn't fit in 32-bits, look for the Zip64 + extended information extra field... */ + if ( (zip64) && + ((offset == 0xFFFFFFFF) || + (starting_disk == 0xFFFFFFFF) || + (retval->compressed_size == 0xFFFFFFFF) || + (retval->uncompressed_size == 0xFFFFFFFF)) ) + { + int found = 0; + PHYSFS_uint16 sig = 0; + PHYSFS_uint16 len = 0; + while (extralen > 4) + { + BAIL_IF_ERRPASS(!readui16(io, &sig), NULL); + BAIL_IF_ERRPASS(!readui16(io, &len), NULL); + + si64 += 4 + len; + extralen -= 4 + len; + if (sig != ZIP64_EXTENDED_INFO_EXTRA_FIELD_SIG) + { + BAIL_IF_ERRPASS(!io->seek(io, si64), NULL); + continue; + } /* if */ + + found = 1; + break; + } /* while */ + + BAIL_IF(!found, PHYSFS_ERR_CORRUPT, NULL); + + if (retval->uncompressed_size == 0xFFFFFFFF) + { + BAIL_IF(len < 8, PHYSFS_ERR_CORRUPT, NULL); + BAIL_IF_ERRPASS(!readui64(io, &retval->uncompressed_size), NULL); + len -= 8; + } /* if */ + + if (retval->compressed_size == 0xFFFFFFFF) + { + BAIL_IF(len < 8, PHYSFS_ERR_CORRUPT, NULL); + BAIL_IF_ERRPASS(!readui64(io, &retval->compressed_size), NULL); + len -= 8; + } /* if */ + + if (offset == 0xFFFFFFFF) + { + BAIL_IF(len < 8, PHYSFS_ERR_CORRUPT, NULL); + BAIL_IF_ERRPASS(!readui64(io, &offset), NULL); + len -= 8; + } /* if */ + + if (starting_disk == 0xFFFFFFFF) + { + BAIL_IF(len < 8, PHYSFS_ERR_CORRUPT, NULL); + BAIL_IF_ERRPASS(!readui32(io, &starting_disk), NULL); + len -= 4; + } /* if */ + + BAIL_IF(len != 0, PHYSFS_ERR_CORRUPT, NULL); + } /* if */ + + BAIL_IF(starting_disk != 0, PHYSFS_ERR_CORRUPT, NULL); + + retval->offset = offset + ofs_fixup; + + /* seek to the start of the next entry in the central directory... */ + BAIL_IF_ERRPASS(!io->seek(io, si64 + extralen + commentlen), NULL); + + return retval; /* success. */ +} /* zip_load_entry */ + + +/* This leaves things allocated on error; the caller will clean up the mess. */ +static int zip_load_entries(ZIPinfo *info, + const PHYSFS_uint64 data_ofs, + const PHYSFS_uint64 central_ofs, + const PHYSFS_uint64 entry_count) +{ + PHYSFS_Io *io = info->io; + const int zip64 = info->zip64; + PHYSFS_uint64 i; + + BAIL_IF_ERRPASS(!io->seek(io, central_ofs), 0); + + for (i = 0; i < entry_count; i++) + { + ZIPentry *entry = zip_load_entry(info, zip64, data_ofs); + BAIL_IF_ERRPASS(!entry, 0); + if (zip_entry_is_tradional_crypto(entry)) + info->has_crypto = 1; + } /* for */ + + return 1; +} /* zip_load_entries */ + + +static PHYSFS_sint64 zip64_find_end_of_central_dir(PHYSFS_Io *io, + PHYSFS_sint64 _pos, + PHYSFS_uint64 offset) +{ + /* + * Naturally, the offset is useless to us; it is the offset from the + * start of file, which is meaningless if we've appended this .zip to + * a self-extracting .exe. We need to find this on our own. It should + * be directly before the locator record, but the record in question, + * like the original end-of-central-directory record, ends with a + * variable-length field. Unlike the original, which has to store the + * size of that variable-length field in a 16-bit int and thus has to be + * within 64k, the new one gets 64-bits. + * + * Fortunately, the only currently-specified record for that variable + * length block is some weird proprietary thing that deals with EBCDIC + * and tape backups or something. So we don't seek far. + */ + + PHYSFS_uint32 ui32; + const PHYSFS_uint64 pos = (PHYSFS_uint64) _pos; + + assert(_pos > 0); + + /* Try offset specified in the Zip64 end of central directory locator. */ + /* This works if the entire PHYSFS_Io is the zip file. */ + BAIL_IF_ERRPASS(!io->seek(io, offset), -1); + BAIL_IF_ERRPASS(!readui32(io, &ui32), -1); + if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG) + return offset; + + /* Try 56 bytes before the Zip64 end of central directory locator. */ + /* This works if the record isn't variable length and is version 1. */ + if (pos > 56) + { + BAIL_IF_ERRPASS(!io->seek(io, pos-56), -1); + BAIL_IF_ERRPASS(!readui32(io, &ui32), -1); + if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG) + return pos-56; + } /* if */ + + /* Try 84 bytes before the Zip64 end of central directory locator. */ + /* This works if the record isn't variable length and is version 2. */ + if (pos > 84) + { + BAIL_IF_ERRPASS(!io->seek(io, pos-84), -1); + BAIL_IF_ERRPASS(!readui32(io, &ui32), -1); + if (ui32 == ZIP64_END_OF_CENTRAL_DIR_SIG) + return pos-84; + } /* if */ + + /* Ok, brute force: we know it's between (offset) and (pos) somewhere. */ + /* Just try moving back at most 256k. Oh well. */ + if ((offset < pos) && (pos > 4)) + { + const size_t maxbuflen = 256 * 1024; + size_t len = (size_t) (pos - offset); + PHYSFS_uint8 *buf = NULL; + PHYSFS_sint32 i; + + if (len > maxbuflen) + len = maxbuflen; + + buf = (PHYSFS_uint8 *) __PHYSFS_smallAlloc(len); + BAIL_IF(!buf, PHYSFS_ERR_OUT_OF_MEMORY, -1); + + if (!io->seek(io, pos - len) || !__PHYSFS_readAll(io, buf, len)) + { + __PHYSFS_smallFree(buf); + return -1; /* error was set elsewhere. */ + } /* if */ + + for (i = (PHYSFS_sint32) (len - 4); i >= 0; i--) + { + if ( (buf[i] == 0x50) && (buf[i+1] == 0x4b) && + (buf[i+2] == 0x06) && (buf[i+3] == 0x06) ) + { + __PHYSFS_smallFree(buf); + return pos - ((PHYSFS_sint64) (len - i)); + } /* if */ + } /* for */ + + __PHYSFS_smallFree(buf); + } /* if */ + + BAIL(PHYSFS_ERR_CORRUPT, -1); /* didn't find it. */ +} /* zip64_find_end_of_central_dir */ + + +static int zip64_parse_end_of_central_dir(ZIPinfo *info, + PHYSFS_uint64 *data_start, + PHYSFS_uint64 *dir_ofs, + PHYSFS_uint64 *entry_count, + PHYSFS_sint64 pos) +{ + PHYSFS_Io *io = info->io; + PHYSFS_uint64 ui64; + PHYSFS_uint32 ui32; + PHYSFS_uint16 ui16; + + /* We should be positioned right past the locator signature. */ + + if ((pos < 0) || (!io->seek(io, pos))) + return 0; + + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + if (ui32 != ZIP64_END_OF_CENTRAL_DIRECTORY_LOCATOR_SIG) + return -1; /* it's not a Zip64 archive. Not an error, though! */ + + info->zip64 = 1; + + /* number of the disk with the start of the central directory. */ + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + BAIL_IF(ui32 != 0, PHYSFS_ERR_CORRUPT, 0); + + /* offset of Zip64 end of central directory record. */ + BAIL_IF_ERRPASS(!readui64(io, &ui64), 0); + + /* total number of disks */ + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + BAIL_IF(ui32 != 1, PHYSFS_ERR_CORRUPT, 0); + + pos = zip64_find_end_of_central_dir(io, pos, ui64); + if (pos < 0) + return 0; /* oh well. */ + + /* + * For self-extracting archives, etc, there's crapola in the file + * before the zipfile records; we calculate how much data there is + * prepended by determining how far the zip64-end-of-central-directory + * offset is from where it is supposed to be...the difference in bytes + * is how much arbitrary data is at the start of the physical file. + */ + assert(((PHYSFS_uint64) pos) >= ui64); + *data_start = ((PHYSFS_uint64) pos) - ui64; + + BAIL_IF_ERRPASS(!io->seek(io, pos), 0); + + /* check signature again, just in case. */ + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + BAIL_IF(ui32 != ZIP64_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0); + + /* size of Zip64 end of central directory record. */ + BAIL_IF_ERRPASS(!readui64(io, &ui64), 0); + + /* version made by. */ + BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); + + /* version needed to extract. */ + BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); + + /* number of this disk. */ + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + BAIL_IF(ui32 != 0, PHYSFS_ERR_CORRUPT, 0); + + /* number of disk with start of central directory record. */ + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + BAIL_IF(ui32 != 0, PHYSFS_ERR_CORRUPT, 0); + + /* total number of entries in the central dir on this disk */ + BAIL_IF_ERRPASS(!readui64(io, &ui64), 0); + + /* total number of entries in the central dir */ + BAIL_IF_ERRPASS(!readui64(io, entry_count), 0); + BAIL_IF(ui64 != *entry_count, PHYSFS_ERR_CORRUPT, 0); + + /* size of the central directory */ + BAIL_IF_ERRPASS(!readui64(io, &ui64), 0); + + /* offset of central directory */ + BAIL_IF_ERRPASS(!readui64(io, dir_ofs), 0); + + /* Since we know the difference, fix up the central dir offset... */ + *dir_ofs += *data_start; + + /* + * There are more fields here, for encryption and feature-specific things, + * but we don't care about any of them at the moment. + */ + + return 1; /* made it. */ +} /* zip64_parse_end_of_central_dir */ + + +static int zip_parse_end_of_central_dir(ZIPinfo *info, + PHYSFS_uint64 *data_start, + PHYSFS_uint64 *dir_ofs, + PHYSFS_uint64 *entry_count) +{ + PHYSFS_Io *io = info->io; + PHYSFS_uint16 entryCount16; + PHYSFS_uint32 offset32; + PHYSFS_uint32 ui32; + PHYSFS_uint16 ui16; + PHYSFS_sint64 len; + PHYSFS_sint64 pos; + int rc; + + /* find the end-of-central-dir record, and seek to it. */ + pos = zip_find_end_of_central_dir(io, &len); + BAIL_IF_ERRPASS(pos == -1, 0); + BAIL_IF_ERRPASS(!io->seek(io, pos), 0); + + /* check signature again, just in case. */ + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + BAIL_IF(ui32 != ZIP_END_OF_CENTRAL_DIR_SIG, PHYSFS_ERR_CORRUPT, 0); + + /* Seek back to see if "Zip64 end of central directory locator" exists. */ + /* this record is 20 bytes before end-of-central-dir */ + rc = zip64_parse_end_of_central_dir(info, data_start, dir_ofs, + entry_count, pos - 20); + + /* Error or success? Bounce out of here. Keep going if not zip64. */ + if ((rc == 0) || (rc == 1)) + return rc; + + assert(rc == -1); /* no error, just not a Zip64 archive. */ + + /* Not Zip64? Seek back to where we were and keep processing. */ + BAIL_IF_ERRPASS(!io->seek(io, pos + 4), 0); + + /* number of this disk */ + BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); + BAIL_IF(ui16 != 0, PHYSFS_ERR_CORRUPT, 0); + + /* number of the disk with the start of the central directory */ + BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); + BAIL_IF(ui16 != 0, PHYSFS_ERR_CORRUPT, 0); + + /* total number of entries in the central dir on this disk */ + BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); + + /* total number of entries in the central dir */ + BAIL_IF_ERRPASS(!readui16(io, &entryCount16), 0); + BAIL_IF(ui16 != entryCount16, PHYSFS_ERR_CORRUPT, 0); + + *entry_count = entryCount16; + + /* size of the central directory */ + BAIL_IF_ERRPASS(!readui32(io, &ui32), 0); + + /* offset of central directory */ + BAIL_IF_ERRPASS(!readui32(io, &offset32), 0); + *dir_ofs = (PHYSFS_uint64) offset32; + BAIL_IF(((PHYSFS_uint64) pos) < (*dir_ofs + ui32), PHYSFS_ERR_CORRUPT, 0); + + /* + * For self-extracting archives, etc, there's crapola in the file + * before the zipfile records; we calculate how much data there is + * prepended by determining how far the central directory offset is + * from where it is supposed to be (start of end-of-central-dir minus + * sizeof central dir)...the difference in bytes is how much arbitrary + * data is at the start of the physical file. + */ + *data_start = (PHYSFS_uint64) (pos - (*dir_ofs + ui32)); + + /* Now that we know the difference, fix up the central dir offset... */ + *dir_ofs += *data_start; + + /* zipfile comment length */ + BAIL_IF_ERRPASS(!readui16(io, &ui16), 0); + + /* + * Make sure that the comment length matches to the end of file... + * If it doesn't, we're either in the wrong part of the file, or the + * file is corrupted, but we give up either way. + */ + BAIL_IF((pos + 22 + ui16) != len, PHYSFS_ERR_CORRUPT, 0); + + return 1; /* made it. */ +} /* zip_parse_end_of_central_dir */ + + +static void ZIP_closeArchive(void *opaque) +{ + ZIPinfo *info = (ZIPinfo *) (opaque); + + if (!info) + return; + + if (info->io) + info->io->destroy(info->io); + + __PHYSFS_DirTreeDeinit(&info->tree); + + allocator.Free(info); +} /* ZIP_closeArchive */ + + +static void *ZIP_openArchive(PHYSFS_Io *io, const char *name, + int forWriting, int *claimed) +{ + ZIPinfo *info = NULL; + ZIPentry *root = NULL; + PHYSFS_uint64 dstart = 0; /* data start */ + PHYSFS_uint64 cdir_ofs; /* central dir offset */ + PHYSFS_uint64 count; + + assert(io != NULL); /* shouldn't ever happen. */ + + BAIL_IF(forWriting, PHYSFS_ERR_READ_ONLY, NULL); + BAIL_IF_ERRPASS(!isZip(io), NULL); + + *claimed = 1; + + info = (ZIPinfo *) allocator.Malloc(sizeof (ZIPinfo)); + BAIL_IF(!info, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + memset(info, '\0', sizeof (ZIPinfo)); + + info->io = io; + + if (!zip_parse_end_of_central_dir(info, &dstart, &cdir_ofs, &count)) + goto ZIP_openarchive_failed; + else if (!__PHYSFS_DirTreeInit(&info->tree, sizeof (ZIPentry))) + goto ZIP_openarchive_failed; + + root = (ZIPentry *) info->tree.root; + root->resolved = ZIP_DIRECTORY; + + if (!zip_load_entries(info, dstart, cdir_ofs, count)) + goto ZIP_openarchive_failed; + + assert(info->tree.root->sibling == NULL); + return info; + +ZIP_openarchive_failed: + info->io = NULL; /* don't let ZIP_closeArchive destroy (io). */ + ZIP_closeArchive(info); + return NULL; +} /* ZIP_openArchive */ + + +static PHYSFS_Io *zip_get_io(PHYSFS_Io *io, ZIPinfo *inf, ZIPentry *entry) +{ + int success; + PHYSFS_Io *retval = io->duplicate(io); + BAIL_IF_ERRPASS(!retval, NULL); + + assert(!entry->tree.isdir); /* should have been checked before calling. */ + + /* (inf) can be NULL if we already resolved. */ + success = (inf == NULL) || zip_resolve(retval, inf, entry); + if (success) + { + PHYSFS_sint64 offset; + offset = ((entry->symlink) ? entry->symlink->offset : entry->offset); + success = retval->seek(retval, offset); + } /* if */ + + if (!success) + { + retval->destroy(retval); + retval = NULL; + } /* if */ + + return retval; +} /* zip_get_io */ + + +static PHYSFS_Io *ZIP_openRead(void *opaque, const char *filename) +{ + PHYSFS_Io *retval = NULL; + ZIPinfo *info = (ZIPinfo *) opaque; + ZIPentry *entry = zip_find_entry(info, filename); + ZIPfileinfo *finfo = NULL; + PHYSFS_Io *io = NULL; + PHYSFS_uint8 *password = NULL; + + /* if not found, see if maybe "$PASSWORD" is appended. */ + if ((!entry) && (info->has_crypto)) + { + const char *ptr = strrchr(filename, '$'); + if (ptr != NULL) + { + const size_t len = (size_t) (ptr - filename); + char *str = (char *) __PHYSFS_smallAlloc(len + 1); + BAIL_IF(!str, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + memcpy(str, filename, len); + str[len] = '\0'; + entry = zip_find_entry(info, str); + __PHYSFS_smallFree(str); + password = (PHYSFS_uint8 *) (ptr + 1); + } /* if */ + } /* if */ + + BAIL_IF_ERRPASS(!entry, NULL); + + BAIL_IF_ERRPASS(!zip_resolve(info->io, info, entry), NULL); + + BAIL_IF(entry->tree.isdir, PHYSFS_ERR_NOT_A_FILE, NULL); + + retval = (PHYSFS_Io *) allocator.Malloc(sizeof (PHYSFS_Io)); + GOTO_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed); + + finfo = (ZIPfileinfo *) allocator.Malloc(sizeof (ZIPfileinfo)); + GOTO_IF(!finfo, PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed); + memset(finfo, '\0', sizeof (ZIPfileinfo)); + + io = zip_get_io(info->io, info, entry); + GOTO_IF_ERRPASS(!io, ZIP_openRead_failed); + finfo->io = io; + finfo->entry = ((entry->symlink != NULL) ? entry->symlink : entry); + initializeZStream(&finfo->stream); + + if (finfo->entry->compression_method != COMPMETH_NONE) + { + finfo->buffer = (PHYSFS_uint8 *) allocator.Malloc(ZIP_READBUFSIZE); + if (!finfo->buffer) + GOTO(PHYSFS_ERR_OUT_OF_MEMORY, ZIP_openRead_failed); + else if (zlib_err(inflateInit2(&finfo->stream, -MAX_WBITS)) != Z_OK) + goto ZIP_openRead_failed; + } /* if */ + + if (!zip_entry_is_tradional_crypto(entry)) + GOTO_IF(password != NULL, PHYSFS_ERR_BAD_PASSWORD, ZIP_openRead_failed); + else + { + PHYSFS_uint8 crypto_header[12]; + GOTO_IF(password == NULL, PHYSFS_ERR_BAD_PASSWORD, ZIP_openRead_failed); + if (io->read(io, crypto_header, 12) != 12) + goto ZIP_openRead_failed; + else if (!zip_prep_crypto_keys(finfo, crypto_header, password)) + goto ZIP_openRead_failed; + } /* if */ + + memcpy(retval, &ZIP_Io, sizeof (PHYSFS_Io)); + retval->opaque = finfo; + + return retval; + +ZIP_openRead_failed: + if (finfo != NULL) + { + if (finfo->io != NULL) + finfo->io->destroy(finfo->io); + + if (finfo->buffer != NULL) + { + allocator.Free(finfo->buffer); + inflateEnd(&finfo->stream); + } /* if */ + + allocator.Free(finfo); + } /* if */ + + if (retval != NULL) + allocator.Free(retval); + + return NULL; +} /* ZIP_openRead */ + + +static PHYSFS_Io *ZIP_openWrite(void *opaque, const char *filename) +{ + BAIL(PHYSFS_ERR_READ_ONLY, NULL); +} /* ZIP_openWrite */ + + +static PHYSFS_Io *ZIP_openAppend(void *opaque, const char *filename) +{ + BAIL(PHYSFS_ERR_READ_ONLY, NULL); +} /* ZIP_openAppend */ + + +static int ZIP_remove(void *opaque, const char *name) +{ + BAIL(PHYSFS_ERR_READ_ONLY, 0); +} /* ZIP_remove */ + + +static int ZIP_mkdir(void *opaque, const char *name) +{ + BAIL(PHYSFS_ERR_READ_ONLY, 0); +} /* ZIP_mkdir */ + + +static int ZIP_stat(void *opaque, const char *filename, PHYSFS_Stat *stat) +{ + ZIPinfo *info = (ZIPinfo *) opaque; + ZIPentry *entry = zip_find_entry(info, filename); + + if (entry == NULL) + return 0; + + else if (!zip_resolve(info->io, info, entry)) + return 0; + + else if (entry->resolved == ZIP_DIRECTORY) + { + stat->filesize = 0; + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + } /* if */ + + else if (zip_entry_is_symlink(entry)) + { + stat->filesize = 0; + stat->filetype = PHYSFS_FILETYPE_SYMLINK; + } /* else if */ + + else + { + stat->filesize = (PHYSFS_sint64) entry->uncompressed_size; + stat->filetype = PHYSFS_FILETYPE_REGULAR; + } /* else */ + + stat->modtime = ((entry) ? entry->last_mod_time : 0); + stat->createtime = stat->modtime; + stat->accesstime = -1; + stat->readonly = 1; /* .zip files are always read only */ + + return 1; +} /* ZIP_stat */ + + +const PHYSFS_Archiver __PHYSFS_Archiver_ZIP = +{ + CURRENT_PHYSFS_ARCHIVER_API_VERSION, + { + "ZIP", + "PkZip/WinZip/Info-Zip compatible", + "Ryan C. Gordon <icculus@icculus.org>", + "https://icculus.org/physfs/", + 1, /* supportsSymlinks */ + }, + ZIP_openArchive, + __PHYSFS_DirTreeEnumerate, + ZIP_openRead, + ZIP_openWrite, + ZIP_openAppend, + ZIP_remove, + ZIP_mkdir, + ZIP_stat, + ZIP_closeArchive +}; + +#endif /* defined PHYSFS_SUPPORTS_ZIP */ + +/* end of physfs_archiver_zip.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_byteorder.c b/Source/3rdParty/physfs/physfs_byteorder.c new file mode 100644 index 0000000..b2d3a2c --- /dev/null +++ b/Source/3rdParty/physfs/physfs_byteorder.c @@ -0,0 +1,137 @@ +/** + * PhysicsFS; a portable, flexible file i/o abstraction. + * + * Documentation is in physfs.h. It's verbose, honest. :) + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#ifndef PHYSFS_Swap16 +static inline PHYSFS_uint16 PHYSFS_Swap16(PHYSFS_uint16 D) +{ + return ((D<<8)|(D>>8)); +} +#endif +#ifndef PHYSFS_Swap32 +static inline PHYSFS_uint32 PHYSFS_Swap32(PHYSFS_uint32 D) +{ + return ((D<<24)|((D<<8)&0x00FF0000)|((D>>8)&0x0000FF00)|(D>>24)); +} +#endif +#ifndef PHYSFS_NO_64BIT_SUPPORT +#ifndef PHYSFS_Swap64 +static inline PHYSFS_uint64 PHYSFS_Swap64(PHYSFS_uint64 val) { + PHYSFS_uint32 hi, lo; + + /* Separate into high and low 32-bit values and swap them */ + lo = (PHYSFS_uint32)(val&0xFFFFFFFF); + val >>= 32; + hi = (PHYSFS_uint32)(val&0xFFFFFFFF); + val = PHYSFS_Swap32(lo); + val <<= 32; + val |= PHYSFS_Swap32(hi); + return val; +} +#endif +#else +#ifndef PHYSFS_Swap64 +/* This is mainly to keep compilers from complaining in PHYSFS code. + If there is no real 64-bit datatype, then compilers will complain about + the fake 64-bit datatype that PHYSFS provides when it compiles user code. +*/ +#define PHYSFS_Swap64(X) (X) +#endif +#endif /* PHYSFS_NO_64BIT_SUPPORT */ + + +/* Byteswap item from the specified endianness to the native endianness */ +#if PHYSFS_BYTEORDER == PHYSFS_LIL_ENDIAN +PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return x; } +PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return x; } +PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return x; } +PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return x; } +PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return x; } +PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return x; } + +PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return PHYSFS_Swap16(x); } +PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return PHYSFS_Swap16(x); } +PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return PHYSFS_Swap32(x); } +PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return PHYSFS_Swap32(x); } +PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return PHYSFS_Swap64(x); } +PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return PHYSFS_Swap64(x); } +#else +PHYSFS_uint16 PHYSFS_swapULE16(PHYSFS_uint16 x) { return PHYSFS_Swap16(x); } +PHYSFS_sint16 PHYSFS_swapSLE16(PHYSFS_sint16 x) { return PHYSFS_Swap16(x); } +PHYSFS_uint32 PHYSFS_swapULE32(PHYSFS_uint32 x) { return PHYSFS_Swap32(x); } +PHYSFS_sint32 PHYSFS_swapSLE32(PHYSFS_sint32 x) { return PHYSFS_Swap32(x); } +PHYSFS_uint64 PHYSFS_swapULE64(PHYSFS_uint64 x) { return PHYSFS_Swap64(x); } +PHYSFS_sint64 PHYSFS_swapSLE64(PHYSFS_sint64 x) { return PHYSFS_Swap64(x); } + +PHYSFS_uint16 PHYSFS_swapUBE16(PHYSFS_uint16 x) { return x; } +PHYSFS_sint16 PHYSFS_swapSBE16(PHYSFS_sint16 x) { return x; } +PHYSFS_uint32 PHYSFS_swapUBE32(PHYSFS_uint32 x) { return x; } +PHYSFS_sint32 PHYSFS_swapSBE32(PHYSFS_sint32 x) { return x; } +PHYSFS_uint64 PHYSFS_swapUBE64(PHYSFS_uint64 x) { return x; } +PHYSFS_sint64 PHYSFS_swapSBE64(PHYSFS_sint64 x) { return x; } +#endif + +static inline int readAll(PHYSFS_File *file, void *val, const size_t len) +{ + return (PHYSFS_readBytes(file, val, len) == len); +} /* readAll */ + +#define PHYSFS_BYTEORDER_READ(datatype, swaptype) \ + int PHYSFS_read##swaptype(PHYSFS_File *file, PHYSFS_##datatype *val) { \ + PHYSFS_##datatype in; \ + BAIL_IF(val == NULL, PHYSFS_ERR_INVALID_ARGUMENT, 0); \ + BAIL_IF_ERRPASS(!readAll(file, &in, sizeof (in)), 0); \ + *val = PHYSFS_swap##swaptype(in); \ + return 1; \ + } + +PHYSFS_BYTEORDER_READ(sint16, SLE16) +PHYSFS_BYTEORDER_READ(uint16, ULE16) +PHYSFS_BYTEORDER_READ(sint16, SBE16) +PHYSFS_BYTEORDER_READ(uint16, UBE16) +PHYSFS_BYTEORDER_READ(sint32, SLE32) +PHYSFS_BYTEORDER_READ(uint32, ULE32) +PHYSFS_BYTEORDER_READ(sint32, SBE32) +PHYSFS_BYTEORDER_READ(uint32, UBE32) +PHYSFS_BYTEORDER_READ(sint64, SLE64) +PHYSFS_BYTEORDER_READ(uint64, ULE64) +PHYSFS_BYTEORDER_READ(sint64, SBE64) +PHYSFS_BYTEORDER_READ(uint64, UBE64) + + +static inline int writeAll(PHYSFS_File *f, const void *val, const size_t len) +{ + return (PHYSFS_writeBytes(f, val, len) == len); +} /* writeAll */ + +#define PHYSFS_BYTEORDER_WRITE(datatype, swaptype) \ + int PHYSFS_write##swaptype(PHYSFS_File *file, PHYSFS_##datatype val) { \ + const PHYSFS_##datatype out = PHYSFS_swap##swaptype(val); \ + BAIL_IF_ERRPASS(!writeAll(file, &out, sizeof (out)), 0); \ + return 1; \ + } + +PHYSFS_BYTEORDER_WRITE(sint16, SLE16) +PHYSFS_BYTEORDER_WRITE(uint16, ULE16) +PHYSFS_BYTEORDER_WRITE(sint16, SBE16) +PHYSFS_BYTEORDER_WRITE(uint16, UBE16) +PHYSFS_BYTEORDER_WRITE(sint32, SLE32) +PHYSFS_BYTEORDER_WRITE(uint32, ULE32) +PHYSFS_BYTEORDER_WRITE(sint32, SBE32) +PHYSFS_BYTEORDER_WRITE(uint32, UBE32) +PHYSFS_BYTEORDER_WRITE(sint64, SLE64) +PHYSFS_BYTEORDER_WRITE(uint64, ULE64) +PHYSFS_BYTEORDER_WRITE(sint64, SBE64) +PHYSFS_BYTEORDER_WRITE(uint64, UBE64) + +/* end of physfs_byteorder.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_casefolding.h b/Source/3rdParty/physfs/physfs_casefolding.h new file mode 100644 index 0000000..bb6ac18 --- /dev/null +++ b/Source/3rdParty/physfs/physfs_casefolding.h @@ -0,0 +1,2572 @@ +/* + * This file is part of PhysicsFS (https://icculus.org/physfs/) + * + * This data generated by physfs/extras/makecasefoldhashtable.pl ... + * Do not manually edit this file! + * + * Please see the file LICENSE.txt in the source's root directory. + */ + +#ifndef _INCLUDE_PHYSFS_CASEFOLDING_H_ +#define _INCLUDE_PHYSFS_CASEFOLDING_H_ + +#ifndef __PHYSICSFS_INTERNAL__ +#error Do not include this header from your applications. +#endif + +/* We build three simple hashmaps here: one that maps Unicode codepoints to +a one, two, or three lowercase codepoints. To retrieve this info: look at +case_fold_hashX, where X is 1, 2, or 3. Most foldable codepoints fold to one, +a few dozen fold to two, and a handful fold to three. If the codepoint isn't +in any of these hashes, it doesn't fold (no separate upper and lowercase). + +Almost all these codepoints fit into 16 bits, so we hash them as such to save +memory. If a codepoint is > 0xFFFF, we have separate hashes for them, +since there are (currently) only about 120 of them and (currently) all of them +map to a single lowercase codepoint. */ + +typedef struct CaseFoldMapping1_32 +{ + PHYSFS_uint32 from; + PHYSFS_uint32 to0; +} CaseFoldMapping1_32; + +typedef struct CaseFoldMapping1_16 +{ + PHYSFS_uint16 from; + PHYSFS_uint16 to0; +} CaseFoldMapping1_16; + +typedef struct CaseFoldMapping2_16 +{ + PHYSFS_uint16 from; + PHYSFS_uint16 to0; + PHYSFS_uint16 to1; +} CaseFoldMapping2_16; + +typedef struct CaseFoldMapping3_16 +{ + PHYSFS_uint16 from; + PHYSFS_uint16 to0; + PHYSFS_uint16 to1; + PHYSFS_uint16 to2; +} CaseFoldMapping3_16; + +typedef struct CaseFoldHashBucket1_16 +{ + const CaseFoldMapping1_16 *list; + const PHYSFS_uint8 count; +} CaseFoldHashBucket1_16; + +typedef struct CaseFoldHashBucket1_32 +{ + const CaseFoldMapping1_32 *list; + const PHYSFS_uint8 count; +} CaseFoldHashBucket1_32; + +typedef struct CaseFoldHashBucket2_16 +{ + const CaseFoldMapping2_16 *list; + const PHYSFS_uint8 count; +} CaseFoldHashBucket2_16; + +typedef struct CaseFoldHashBucket3_16 +{ + const CaseFoldMapping3_16 *list; + const PHYSFS_uint8 count; +} CaseFoldHashBucket3_16; + +static const CaseFoldMapping1_16 case_fold1_16_000[] = { + { 0x0202, 0x0203 }, + { 0x0404, 0x0454 }, + { 0x1E1E, 0x1E1F }, + { 0x2C2C, 0x2C5C }, + { 0xABAB, 0x13DB } +}; + +static const CaseFoldMapping1_16 case_fold1_16_001[] = { + { 0x0100, 0x0101 }, + { 0x0405, 0x0455 }, + { 0x0504, 0x0505 }, + { 0x2C2D, 0x2C5D }, + { 0xA7A6, 0xA7A7 }, + { 0xABAA, 0x13DA } +}; + +static const CaseFoldMapping1_16 case_fold1_16_002[] = { + { 0x0200, 0x0201 }, + { 0x0406, 0x0456 }, + { 0x1E1C, 0x1E1D }, + { 0x1F1D, 0x1F15 }, + { 0x2C2E, 0x2C5E }, + { 0xABA9, 0x13D9 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_003[] = { + { 0x0102, 0x0103 }, + { 0x0407, 0x0457 }, + { 0x0506, 0x0507 }, + { 0x1F1C, 0x1F14 }, + { 0xA7A4, 0xA7A5 }, + { 0xABA8, 0x13D8 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_004[] = { + { 0x0206, 0x0207 }, + { 0x0400, 0x0450 }, + { 0x1E1A, 0x1E1B }, + { 0x1F1B, 0x1F13 }, + { 0x2C28, 0x2C58 }, + { 0xABAF, 0x13DF } +}; + +static const CaseFoldMapping1_16 case_fold1_16_005[] = { + { 0x0104, 0x0105 }, + { 0x0401, 0x0451 }, + { 0x0500, 0x0501 }, + { 0x1F1A, 0x1F12 }, + { 0x2C29, 0x2C59 }, + { 0xA7A2, 0xA7A3 }, + { 0xABAE, 0x13DE } +}; + +static const CaseFoldMapping1_16 case_fold1_16_006[] = { + { 0x0204, 0x0205 }, + { 0x0402, 0x0452 }, + { 0x1E18, 0x1E19 }, + { 0x1F19, 0x1F11 }, + { 0x2C2A, 0x2C5A }, + { 0xABAD, 0x13DD } +}; + +static const CaseFoldMapping1_16 case_fold1_16_007[] = { + { 0x0106, 0x0107 }, + { 0x0403, 0x0453 }, + { 0x0502, 0x0503 }, + { 0x1F18, 0x1F10 }, + { 0x2126, 0x03C9 }, + { 0x2C2B, 0x2C5B }, + { 0xA7A0, 0xA7A1 }, + { 0xABAC, 0x13DC } +}; + +static const CaseFoldMapping1_16 case_fold1_16_008[] = { + { 0x020A, 0x020B }, + { 0x040C, 0x045C }, + { 0x1E16, 0x1E17 }, + { 0x2C24, 0x2C54 }, + { 0xABA3, 0x13D3 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_009[] = { + { 0x0108, 0x0109 }, + { 0x040D, 0x045D }, + { 0x050C, 0x050D }, + { 0x2C25, 0x2C55 }, + { 0xABA2, 0x13D2 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_010[] = { + { 0x0208, 0x0209 }, + { 0x040E, 0x045E }, + { 0x1E14, 0x1E15 }, + { 0x212B, 0x00E5 }, + { 0x2C26, 0x2C56 }, + { 0xA7AD, 0x026C }, + { 0xABA1, 0x13D1 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_011[] = { + { 0x010A, 0x010B }, + { 0x040F, 0x045F }, + { 0x050E, 0x050F }, + { 0x212A, 0x006B }, + { 0x2C27, 0x2C57 }, + { 0xA7AC, 0x0261 }, + { 0xABA0, 0x13D0 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_012[] = { + { 0x020E, 0x020F }, + { 0x0408, 0x0458 }, + { 0x1E12, 0x1E13 }, + { 0x2C20, 0x2C50 }, + { 0xA7AB, 0x025C }, + { 0xABA7, 0x13D7 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_013[] = { + { 0x010C, 0x010D }, + { 0x0409, 0x0459 }, + { 0x0508, 0x0509 }, + { 0x2C21, 0x2C51 }, + { 0xA7AA, 0x0266 }, + { 0xABA6, 0x13D6 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_014[] = { + { 0x020C, 0x020D }, + { 0x040A, 0x045A }, + { 0x1E10, 0x1E11 }, + { 0x2C22, 0x2C52 }, + { 0xABA5, 0x13D5 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_015[] = { + { 0x010E, 0x010F }, + { 0x040B, 0x045B }, + { 0x050A, 0x050B }, + { 0x2C23, 0x2C53 }, + { 0xA7A8, 0xA7A9 }, + { 0xABA4, 0x13D4 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_016[] = { + { 0x0212, 0x0213 }, + { 0x0414, 0x0434 }, + { 0x1E0E, 0x1E0F }, + { 0x1F0F, 0x1F07 }, + { 0xABBB, 0x13EB } +}; + +static const CaseFoldMapping1_16 case_fold1_16_017[] = { + { 0x0110, 0x0111 }, + { 0x0415, 0x0435 }, + { 0x0514, 0x0515 }, + { 0x1F0E, 0x1F06 }, + { 0xA7B6, 0xA7B7 }, + { 0xABBA, 0x13EA } +}; + +static const CaseFoldMapping1_16 case_fold1_16_018[] = { + { 0x0210, 0x0211 }, + { 0x0416, 0x0436 }, + { 0x1E0C, 0x1E0D }, + { 0x1F0D, 0x1F05 }, + { 0xABB9, 0x13E9 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_019[] = { + { 0x0112, 0x0113 }, + { 0x0417, 0x0437 }, + { 0x0516, 0x0517 }, + { 0x1F0C, 0x1F04 }, + { 0x2132, 0x214E }, + { 0xA7B4, 0xA7B5 }, + { 0xABB8, 0x13E8 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_020[] = { + { 0x0216, 0x0217 }, + { 0x0410, 0x0430 }, + { 0x1E0A, 0x1E0B }, + { 0x1F0B, 0x1F03 }, + { 0xA7B3, 0xAB53 }, + { 0xABBF, 0x13EF } +}; + +static const CaseFoldMapping1_16 case_fold1_16_021[] = { + { 0x0114, 0x0115 }, + { 0x0411, 0x0431 }, + { 0x0510, 0x0511 }, + { 0x1F0A, 0x1F02 }, + { 0xA7B2, 0x029D }, + { 0xABBE, 0x13EE } +}; + +static const CaseFoldMapping1_16 case_fold1_16_022[] = { + { 0x0214, 0x0215 }, + { 0x0412, 0x0432 }, + { 0x1E08, 0x1E09 }, + { 0x1F09, 0x1F01 }, + { 0xA7B1, 0x0287 }, + { 0xABBD, 0x13ED } +}; + +static const CaseFoldMapping1_16 case_fold1_16_023[] = { + { 0x0116, 0x0117 }, + { 0x0413, 0x0433 }, + { 0x0512, 0x0513 }, + { 0x1F08, 0x1F00 }, + { 0xA7B0, 0x029E }, + { 0xABBC, 0x13EC } +}; + +static const CaseFoldMapping1_16 case_fold1_16_024[] = { + { 0x021A, 0x021B }, + { 0x041C, 0x043C }, + { 0x1E06, 0x1E07 }, + { 0xABB3, 0x13E3 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_025[] = { + { 0x0118, 0x0119 }, + { 0x041D, 0x043D }, + { 0x051C, 0x051D }, + { 0xABB2, 0x13E2 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_026[] = { + { 0x0218, 0x0219 }, + { 0x041E, 0x043E }, + { 0x1E04, 0x1E05 }, + { 0xABB1, 0x13E1 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_027[] = { + { 0x011A, 0x011B }, + { 0x041F, 0x043F }, + { 0x051E, 0x051F }, + { 0xABB0, 0x13E0 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_028[] = { + { 0x021E, 0x021F }, + { 0x0418, 0x0438 }, + { 0x1E02, 0x1E03 }, + { 0xABB7, 0x13E7 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_029[] = { + { 0x011C, 0x011D }, + { 0x0419, 0x0439 }, + { 0x0518, 0x0519 }, + { 0xABB6, 0x13E6 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_030[] = { + { 0x021C, 0x021D }, + { 0x041A, 0x043A }, + { 0x1E00, 0x1E01 }, + { 0xABB5, 0x13E5 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_031[] = { + { 0x011E, 0x011F }, + { 0x041B, 0x043B }, + { 0x051A, 0x051B }, + { 0xABB4, 0x13E4 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_032[] = { + { 0x0222, 0x0223 }, + { 0x0424, 0x0444 }, + { 0x1E3E, 0x1E3F }, + { 0x1F3F, 0x1F37 }, + { 0x2C0C, 0x2C3C }, + { 0xA686, 0xA687 }, + { 0xAB8B, 0x13BB } +}; + +static const CaseFoldMapping1_16 case_fold1_16_033[] = { + { 0x0120, 0x0121 }, + { 0x0425, 0x0445 }, + { 0x0524, 0x0525 }, + { 0x1F3E, 0x1F36 }, + { 0x2C0D, 0x2C3D }, + { 0xA786, 0xA787 }, + { 0xAB8A, 0x13BA } +}; + +static const CaseFoldMapping1_16 case_fold1_16_034[] = { + { 0x0220, 0x019E }, + { 0x0426, 0x0446 }, + { 0x1E3C, 0x1E3D }, + { 0x1F3D, 0x1F35 }, + { 0x2C0E, 0x2C3E }, + { 0xA684, 0xA685 }, + { 0xAB89, 0x13B9 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_035[] = { + { 0x0122, 0x0123 }, + { 0x0427, 0x0447 }, + { 0x0526, 0x0527 }, + { 0x1F3C, 0x1F34 }, + { 0x2C0F, 0x2C3F }, + { 0xA784, 0xA785 }, + { 0xAB88, 0x13B8 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_036[] = { + { 0x0226, 0x0227 }, + { 0x0420, 0x0440 }, + { 0x1E3A, 0x1E3B }, + { 0x1F3B, 0x1F33 }, + { 0x2C08, 0x2C38 }, + { 0xA682, 0xA683 }, + { 0xAB8F, 0x13BF } +}; + +static const CaseFoldMapping1_16 case_fold1_16_037[] = { + { 0x0124, 0x0125 }, + { 0x0421, 0x0441 }, + { 0x0520, 0x0521 }, + { 0x1F3A, 0x1F32 }, + { 0x2C09, 0x2C39 }, + { 0xA782, 0xA783 }, + { 0xAB8E, 0x13BE } +}; + +static const CaseFoldMapping1_16 case_fold1_16_038[] = { + { 0x0224, 0x0225 }, + { 0x0422, 0x0442 }, + { 0x1E38, 0x1E39 }, + { 0x1F39, 0x1F31 }, + { 0x2C0A, 0x2C3A }, + { 0xA680, 0xA681 }, + { 0xAB8D, 0x13BD } +}; + +static const CaseFoldMapping1_16 case_fold1_16_039[] = { + { 0x0126, 0x0127 }, + { 0x0423, 0x0443 }, + { 0x0522, 0x0523 }, + { 0x1F38, 0x1F30 }, + { 0x2C0B, 0x2C3B }, + { 0xA780, 0xA781 }, + { 0xAB8C, 0x13BC } +}; + +static const CaseFoldMapping1_16 case_fold1_16_040[] = { + { 0x022A, 0x022B }, + { 0x042C, 0x044C }, + { 0x1E36, 0x1E37 }, + { 0x2C04, 0x2C34 }, + { 0xA68E, 0xA68F }, + { 0xAB83, 0x13B3 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_041[] = { + { 0x0128, 0x0129 }, + { 0x042D, 0x044D }, + { 0x052C, 0x052D }, + { 0x2C05, 0x2C35 }, + { 0xAB82, 0x13B2 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_042[] = { + { 0x0228, 0x0229 }, + { 0x042E, 0x044E }, + { 0x1E34, 0x1E35 }, + { 0x2C06, 0x2C36 }, + { 0xA68C, 0xA68D }, + { 0xA78D, 0x0265 }, + { 0xAB81, 0x13B1 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_043[] = { + { 0x012A, 0x012B }, + { 0x042F, 0x044F }, + { 0x052E, 0x052F }, + { 0x2C07, 0x2C37 }, + { 0xAB80, 0x13B0 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_044[] = { + { 0x022E, 0x022F }, + { 0x0428, 0x0448 }, + { 0x1E32, 0x1E33 }, + { 0x2C00, 0x2C30 }, + { 0xA68A, 0xA68B }, + { 0xA78B, 0xA78C }, + { 0xAB87, 0x13B7 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_045[] = { + { 0x012C, 0x012D }, + { 0x0429, 0x0449 }, + { 0x0528, 0x0529 }, + { 0x2C01, 0x2C31 }, + { 0xAB86, 0x13B6 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_046[] = { + { 0x022C, 0x022D }, + { 0x042A, 0x044A }, + { 0x1E30, 0x1E31 }, + { 0x2C02, 0x2C32 }, + { 0xA688, 0xA689 }, + { 0xAB85, 0x13B5 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_047[] = { + { 0x012E, 0x012F }, + { 0x042B, 0x044B }, + { 0x052A, 0x052B }, + { 0x2C03, 0x2C33 }, + { 0xAB84, 0x13B4 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_048[] = { + { 0x0232, 0x0233 }, + { 0x0535, 0x0565 }, + { 0x1E2E, 0x1E2F }, + { 0x1F2F, 0x1F27 }, + { 0x2C1C, 0x2C4C }, + { 0xA696, 0xA697 }, + { 0xAB9B, 0x13CB } +}; + +static const CaseFoldMapping1_16 case_fold1_16_049[] = { + { 0x0534, 0x0564 }, + { 0x1F2E, 0x1F26 }, + { 0x2C1D, 0x2C4D }, + { 0xA796, 0xA797 }, + { 0xAB9A, 0x13CA } +}; + +static const CaseFoldMapping1_16 case_fold1_16_050[] = { + { 0x0230, 0x0231 }, + { 0x0537, 0x0567 }, + { 0x1E2C, 0x1E2D }, + { 0x1F2D, 0x1F25 }, + { 0x2C1E, 0x2C4E }, + { 0xA694, 0xA695 }, + { 0xAB99, 0x13C9 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_051[] = { + { 0x0132, 0x0133 }, + { 0x0536, 0x0566 }, + { 0x1F2C, 0x1F24 }, + { 0x2C1F, 0x2C4F }, + { 0xAB98, 0x13C8 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_052[] = { + { 0x0531, 0x0561 }, + { 0x1E2A, 0x1E2B }, + { 0x1F2B, 0x1F23 }, + { 0x2C18, 0x2C48 }, + { 0xA692, 0xA693 }, + { 0xAB9F, 0x13CF } +}; + +static const CaseFoldMapping1_16 case_fold1_16_053[] = { + { 0x0134, 0x0135 }, + { 0x1F2A, 0x1F22 }, + { 0x2C19, 0x2C49 }, + { 0xA792, 0xA793 }, + { 0xAB9E, 0x13CE } +}; + +static const CaseFoldMapping1_16 case_fold1_16_054[] = { + { 0x0533, 0x0563 }, + { 0x1E28, 0x1E29 }, + { 0x1F29, 0x1F21 }, + { 0x2C1A, 0x2C4A }, + { 0xA690, 0xA691 }, + { 0xAB9D, 0x13CD } +}; + +static const CaseFoldMapping1_16 case_fold1_16_055[] = { + { 0x0136, 0x0137 }, + { 0x0532, 0x0562 }, + { 0x1F28, 0x1F20 }, + { 0x2C1B, 0x2C4B }, + { 0xA790, 0xA791 }, + { 0xAB9C, 0x13CC } +}; + +static const CaseFoldMapping1_16 case_fold1_16_056[] = { + { 0x0139, 0x013A }, + { 0x023A, 0x2C65 }, + { 0x053D, 0x056D }, + { 0x1E26, 0x1E27 }, + { 0x2C14, 0x2C44 }, + { 0xAB93, 0x13C3 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_057[] = { + { 0x023B, 0x023C }, + { 0x053C, 0x056C }, + { 0x2C15, 0x2C45 }, + { 0xA79E, 0xA79F }, + { 0xAB92, 0x13C2 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_058[] = { + { 0x013B, 0x013C }, + { 0x053F, 0x056F }, + { 0x1E24, 0x1E25 }, + { 0x2C16, 0x2C46 }, + { 0xAB91, 0x13C1 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_059[] = { + { 0x053E, 0x056E }, + { 0x2C17, 0x2C47 }, + { 0xA79C, 0xA79D }, + { 0xAB90, 0x13C0 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_060[] = { + { 0x013D, 0x013E }, + { 0x023E, 0x2C66 }, + { 0x0539, 0x0569 }, + { 0x1E22, 0x1E23 }, + { 0x2C10, 0x2C40 }, + { 0xA69A, 0xA69B }, + { 0xAB97, 0x13C7 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_061[] = { + { 0x0538, 0x0568 }, + { 0x2C11, 0x2C41 }, + { 0xA79A, 0xA79B }, + { 0xAB96, 0x13C6 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_062[] = { + { 0x013F, 0x0140 }, + { 0x053B, 0x056B }, + { 0x1E20, 0x1E21 }, + { 0x2C12, 0x2C42 }, + { 0xA698, 0xA699 }, + { 0xAB95, 0x13C5 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_063[] = { + { 0x023D, 0x019A }, + { 0x053A, 0x056A }, + { 0x2C13, 0x2C43 }, + { 0xA798, 0xA799 }, + { 0xAB94, 0x13C4 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_064[] = { + { 0x0141, 0x0142 }, + { 0x0545, 0x0575 }, + { 0x1E5E, 0x1E5F }, + { 0x1F5F, 0x1F57 }, + { 0x2161, 0x2171 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_065[] = { + { 0x0041, 0x0061 }, + { 0x0243, 0x0180 }, + { 0x0544, 0x0574 }, + { 0x2160, 0x2170 }, + { 0x2C6D, 0x0251 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_066[] = { + { 0x0042, 0x0062 }, + { 0x0143, 0x0144 }, + { 0x0547, 0x0577 }, + { 0x1E5C, 0x1E5D }, + { 0x1F5D, 0x1F55 }, + { 0x2163, 0x2173 }, + { 0x2C6E, 0x0271 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_067[] = { + { 0x0043, 0x0063 }, + { 0x0241, 0x0242 }, + { 0x0546, 0x0576 }, + { 0x2162, 0x2172 }, + { 0x2C6F, 0x0250 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_068[] = { + { 0x0044, 0x0064 }, + { 0x0145, 0x0146 }, + { 0x0246, 0x0247 }, + { 0x0541, 0x0571 }, + { 0x1E5A, 0x1E5B }, + { 0x1F5B, 0x1F53 }, + { 0x2165, 0x2175 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_069[] = { + { 0x0045, 0x0065 }, + { 0x0540, 0x0570 }, + { 0x2164, 0x2174 }, + { 0x2C69, 0x2C6A } +}; + +static const CaseFoldMapping1_16 case_fold1_16_070[] = { + { 0x0046, 0x0066 }, + { 0x0147, 0x0148 }, + { 0x0244, 0x0289 }, + { 0x0345, 0x03B9 }, + { 0x0543, 0x0573 }, + { 0x1E58, 0x1E59 }, + { 0x1F59, 0x1F51 }, + { 0x2167, 0x2177 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_071[] = { + { 0x0047, 0x0067 }, + { 0x0245, 0x028C }, + { 0x0542, 0x0572 }, + { 0x2166, 0x2176 }, + { 0x2C6B, 0x2C6C } +}; + +static const CaseFoldMapping1_16 case_fold1_16_072[] = { + { 0x0048, 0x0068 }, + { 0x024A, 0x024B }, + { 0x054D, 0x057D }, + { 0x1E56, 0x1E57 }, + { 0x2169, 0x2179 }, + { 0x2C64, 0x027D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_073[] = { + { 0x0049, 0x0069 }, + { 0x054C, 0x057C }, + { 0x2168, 0x2178 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_074[] = { + { 0x004A, 0x006A }, + { 0x0248, 0x0249 }, + { 0x054F, 0x057F }, + { 0x1E54, 0x1E55 }, + { 0x216B, 0x217B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_075[] = { + { 0x004B, 0x006B }, + { 0x014A, 0x014B }, + { 0x054E, 0x057E }, + { 0x216A, 0x217A }, + { 0x2C67, 0x2C68 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_076[] = { + { 0x004C, 0x006C }, + { 0x024E, 0x024F }, + { 0x0549, 0x0579 }, + { 0x1E52, 0x1E53 }, + { 0x216D, 0x217D }, + { 0x2C60, 0x2C61 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_077[] = { + { 0x004D, 0x006D }, + { 0x014C, 0x014D }, + { 0x0548, 0x0578 }, + { 0x216C, 0x217C } +}; + +static const CaseFoldMapping1_16 case_fold1_16_078[] = { + { 0x004E, 0x006E }, + { 0x024C, 0x024D }, + { 0x054B, 0x057B }, + { 0x1E50, 0x1E51 }, + { 0x216F, 0x217F }, + { 0x2C62, 0x026B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_079[] = { + { 0x004F, 0x006F }, + { 0x014E, 0x014F }, + { 0x054A, 0x057A }, + { 0x216E, 0x217E }, + { 0x2C63, 0x1D7D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_080[] = { + { 0x0050, 0x0070 }, + { 0x0555, 0x0585 }, + { 0x1E4E, 0x1E4F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_081[] = { + { 0x0051, 0x0071 }, + { 0x0150, 0x0151 }, + { 0x0554, 0x0584 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_082[] = { + { 0x0052, 0x0072 }, + { 0x1E4C, 0x1E4D }, + { 0x1F4D, 0x1F45 }, + { 0x2C7E, 0x023F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_083[] = { + { 0x0053, 0x0073 }, + { 0x0152, 0x0153 }, + { 0x0556, 0x0586 }, + { 0x1F4C, 0x1F44 }, + { 0x2C7F, 0x0240 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_084[] = { + { 0x0054, 0x0074 }, + { 0x0551, 0x0581 }, + { 0x1E4A, 0x1E4B }, + { 0x1F4B, 0x1F43 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_085[] = { + { 0x0055, 0x0075 }, + { 0x0154, 0x0155 }, + { 0x0550, 0x0580 }, + { 0x1F4A, 0x1F42 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_086[] = { + { 0x0056, 0x0076 }, + { 0x0553, 0x0583 }, + { 0x1E48, 0x1E49 }, + { 0x1F49, 0x1F41 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_087[] = { + { 0x0057, 0x0077 }, + { 0x0156, 0x0157 }, + { 0x0552, 0x0582 }, + { 0x1F48, 0x1F40 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_088[] = { + { 0x0058, 0x0078 }, + { 0x1E46, 0x1E47 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_089[] = { + { 0x0059, 0x0079 }, + { 0x0158, 0x0159 }, + { 0x2C75, 0x2C76 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_090[] = { + { 0x005A, 0x007A }, + { 0x1E44, 0x1E45 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_091[] = { + { 0x015A, 0x015B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_092[] = { + { 0x1E42, 0x1E43 }, + { 0x2C70, 0x0252 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_093[] = { + { 0x015C, 0x015D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_094[] = { + { 0x1E40, 0x1E41 }, + { 0x2C72, 0x2C73 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_095[] = { + { 0x015E, 0x015F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_096[] = { + { 0x0464, 0x0465 }, + { 0x1E7E, 0x1E7F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_097[] = { + { 0x0160, 0x0161 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_098[] = { + { 0x0466, 0x0467 }, + { 0x1E7C, 0x1E7D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_099[] = { + { 0x0162, 0x0163 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_100[] = { + { 0x0460, 0x0461 }, + { 0x1E7A, 0x1E7B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_101[] = { + { 0x0164, 0x0165 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_102[] = { + { 0x0462, 0x0463 }, + { 0x1E78, 0x1E79 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_103[] = { + { 0x0166, 0x0167 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_104[] = { + { 0x046C, 0x046D }, + { 0x1E76, 0x1E77 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_105[] = { + { 0x0168, 0x0169 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_106[] = { + { 0x046E, 0x046F }, + { 0x1E74, 0x1E75 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_107[] = { + { 0x016A, 0x016B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_108[] = { + { 0x0468, 0x0469 }, + { 0x1E72, 0x1E73 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_109[] = { + { 0x016C, 0x016D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_110[] = { + { 0x046A, 0x046B }, + { 0x1E70, 0x1E71 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_111[] = { + { 0x016E, 0x016F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_112[] = { + { 0x0474, 0x0475 }, + { 0x1E6E, 0x1E6F }, + { 0x1F6F, 0x1F67 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_113[] = { + { 0x0170, 0x0171 }, + { 0x0372, 0x0373 }, + { 0x1F6E, 0x1F66 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_114[] = { + { 0x0476, 0x0477 }, + { 0x1E6C, 0x1E6D }, + { 0x1F6D, 0x1F65 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_115[] = { + { 0x0172, 0x0173 }, + { 0x0370, 0x0371 }, + { 0x1F6C, 0x1F64 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_116[] = { + { 0x0470, 0x0471 }, + { 0x1E6A, 0x1E6B }, + { 0x1F6B, 0x1F63 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_117[] = { + { 0x0174, 0x0175 }, + { 0x0376, 0x0377 }, + { 0x1F6A, 0x1F62 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_118[] = { + { 0x0472, 0x0473 }, + { 0x1E68, 0x1E69 }, + { 0x1F69, 0x1F61 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_119[] = { + { 0x0176, 0x0177 }, + { 0x1F68, 0x1F60 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_120[] = { + { 0x0179, 0x017A }, + { 0x047C, 0x047D }, + { 0x1E66, 0x1E67 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_121[] = { + { 0x0178, 0x00FF } +}; + +static const CaseFoldMapping1_16 case_fold1_16_122[] = { + { 0x017B, 0x017C }, + { 0x047E, 0x047F }, + { 0x1E64, 0x1E65 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_124[] = { + { 0x017D, 0x017E }, + { 0x037F, 0x03F3 }, + { 0x0478, 0x0479 }, + { 0x1E62, 0x1E63 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_126[] = { + { 0x017F, 0x0073 }, + { 0x047A, 0x047B }, + { 0x1E60, 0x1E61 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_128[] = { + { 0x0181, 0x0253 }, + { 0x2CAC, 0x2CAD } +}; + +static const CaseFoldMapping1_16 case_fold1_16_129[] = { + { 0xA726, 0xA727 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_130[] = { + { 0x2CAE, 0x2CAF } +}; + +static const CaseFoldMapping1_16 case_fold1_16_131[] = { + { 0x0182, 0x0183 }, + { 0xA724, 0xA725 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_132[] = { + { 0x0480, 0x0481 }, + { 0x2CA8, 0x2CA9 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_133[] = { + { 0x0184, 0x0185 }, + { 0x0386, 0x03AC }, + { 0x1E9B, 0x1E61 }, + { 0xA722, 0xA723 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_134[] = { + { 0x0187, 0x0188 }, + { 0x2CAA, 0x2CAB } +}; + +static const CaseFoldMapping1_16 case_fold1_16_135[] = { + { 0x0186, 0x0254 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_136[] = { + { 0x0189, 0x0256 }, + { 0x048C, 0x048D }, + { 0x2CA4, 0x2CA5 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_137[] = { + { 0x038A, 0x03AF }, + { 0xA72E, 0xA72F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_138[] = { + { 0x018B, 0x018C }, + { 0x0389, 0x03AE }, + { 0x048E, 0x048F }, + { 0x1E94, 0x1E95 }, + { 0x2CA6, 0x2CA7 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_139[] = { + { 0x018A, 0x0257 }, + { 0x0388, 0x03AD }, + { 0xA72C, 0xA72D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_140[] = { + { 0x038F, 0x03CE }, + { 0x1E92, 0x1E93 }, + { 0x2CA0, 0x2CA1 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_141[] = { + { 0x038E, 0x03CD }, + { 0xA72A, 0xA72B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_142[] = { + { 0x018F, 0x0259 }, + { 0x048A, 0x048B }, + { 0x1E90, 0x1E91 }, + { 0x2CA2, 0x2CA3 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_143[] = { + { 0x018E, 0x01DD }, + { 0x038C, 0x03CC }, + { 0xA728, 0xA729 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_144[] = { + { 0x0191, 0x0192 }, + { 0x0393, 0x03B3 }, + { 0x0494, 0x0495 }, + { 0x1E8E, 0x1E8F }, + { 0x2CBC, 0x2CBD } +}; + +static const CaseFoldMapping1_16 case_fold1_16_145[] = { + { 0x0190, 0x025B }, + { 0x0392, 0x03B2 }, + { 0xA736, 0xA737 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_146[] = { + { 0x0193, 0x0260 }, + { 0x0391, 0x03B1 }, + { 0x0496, 0x0497 }, + { 0x1E8C, 0x1E8D }, + { 0x24B6, 0x24D0 }, + { 0x2CBE, 0x2CBF } +}; + +static const CaseFoldMapping1_16 case_fold1_16_147[] = { + { 0x24B7, 0x24D1 }, + { 0xA734, 0xA735 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_148[] = { + { 0x0397, 0x03B7 }, + { 0x0490, 0x0491 }, + { 0x1E8A, 0x1E8B }, + { 0x2CB8, 0x2CB9 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_149[] = { + { 0x0194, 0x0263 }, + { 0x0396, 0x03B6 }, + { 0xA732, 0xA733 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_150[] = { + { 0x0197, 0x0268 }, + { 0x0395, 0x03B5 }, + { 0x0492, 0x0493 }, + { 0x1E88, 0x1E89 }, + { 0x2CBA, 0x2CBB } +}; + +static const CaseFoldMapping1_16 case_fold1_16_151[] = { + { 0x0196, 0x0269 }, + { 0x0394, 0x03B4 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_152[] = { + { 0x039B, 0x03BB }, + { 0x049C, 0x049D }, + { 0x1E86, 0x1E87 }, + { 0x24BC, 0x24D6 }, + { 0x2CB4, 0x2CB5 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_153[] = { + { 0x0198, 0x0199 }, + { 0x039A, 0x03BA }, + { 0x24BD, 0x24D7 }, + { 0xA73E, 0xA73F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_154[] = { + { 0x0399, 0x03B9 }, + { 0x049E, 0x049F }, + { 0x1E84, 0x1E85 }, + { 0x24BE, 0x24D8 }, + { 0x2CB6, 0x2CB7 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_155[] = { + { 0x0398, 0x03B8 }, + { 0x24BF, 0x24D9 }, + { 0xA73C, 0xA73D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_156[] = { + { 0x019D, 0x0272 }, + { 0x039F, 0x03BF }, + { 0x0498, 0x0499 }, + { 0x1E82, 0x1E83 }, + { 0x24B8, 0x24D2 }, + { 0x2CB0, 0x2CB1 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_157[] = { + { 0x019C, 0x026F }, + { 0x039E, 0x03BE }, + { 0x24B9, 0x24D3 }, + { 0xA73A, 0xA73B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_158[] = { + { 0x019F, 0x0275 }, + { 0x039D, 0x03BD }, + { 0x049A, 0x049B }, + { 0x1E80, 0x1E81 }, + { 0x24BA, 0x24D4 }, + { 0x2CB2, 0x2CB3 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_159[] = { + { 0x039C, 0x03BC }, + { 0x24BB, 0x24D5 }, + { 0xA738, 0xA739 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_160[] = { + { 0x03A3, 0x03C3 }, + { 0x04A4, 0x04A5 }, + { 0x10B0, 0x2D10 }, + { 0x1EBE, 0x1EBF }, + { 0x2C8C, 0x2C8D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_161[] = { + { 0x01A0, 0x01A1 }, + { 0x10B1, 0x2D11 }, + { 0x1FBE, 0x03B9 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_162[] = { + { 0x03A1, 0x03C1 }, + { 0x04A6, 0x04A7 }, + { 0x10B2, 0x2D12 }, + { 0x1EBC, 0x1EBD }, + { 0x2183, 0x2184 }, + { 0x2C8E, 0x2C8F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_163[] = { + { 0x01A2, 0x01A3 }, + { 0x03A0, 0x03C0 }, + { 0x10B3, 0x2D13 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_164[] = { + { 0x03A7, 0x03C7 }, + { 0x04A0, 0x04A1 }, + { 0x10B4, 0x2D14 }, + { 0x1EBA, 0x1EBB }, + { 0x1FBB, 0x1F71 }, + { 0x2C88, 0x2C89 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_165[] = { + { 0x01A4, 0x01A5 }, + { 0x03A6, 0x03C6 }, + { 0x10B5, 0x2D15 }, + { 0x1FBA, 0x1F70 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_166[] = { + { 0x01A7, 0x01A8 }, + { 0x03A5, 0x03C5 }, + { 0x04A2, 0x04A3 }, + { 0x10B6, 0x2D16 }, + { 0x1EB8, 0x1EB9 }, + { 0x1FB9, 0x1FB1 }, + { 0x2C8A, 0x2C8B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_167[] = { + { 0x01A6, 0x0280 }, + { 0x03A4, 0x03C4 }, + { 0x10B7, 0x2D17 }, + { 0x1FB8, 0x1FB0 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_168[] = { + { 0x01A9, 0x0283 }, + { 0x03AB, 0x03CB }, + { 0x04AC, 0x04AD }, + { 0x10B8, 0x2D18 }, + { 0x1EB6, 0x1EB7 }, + { 0x2C84, 0x2C85 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_169[] = { + { 0x03AA, 0x03CA }, + { 0x10B9, 0x2D19 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_170[] = { + { 0x03A9, 0x03C9 }, + { 0x04AE, 0x04AF }, + { 0x10BA, 0x2D1A }, + { 0x1EB4, 0x1EB5 }, + { 0x2C86, 0x2C87 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_171[] = { + { 0x03A8, 0x03C8 }, + { 0x10BB, 0x2D1B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_172[] = { + { 0x04A8, 0x04A9 }, + { 0x10BC, 0x2D1C }, + { 0x1EB2, 0x1EB3 }, + { 0x2C80, 0x2C81 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_173[] = { + { 0x01AC, 0x01AD }, + { 0x10BD, 0x2D1D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_174[] = { + { 0x01AF, 0x01B0 }, + { 0x04AA, 0x04AB }, + { 0x10BE, 0x2D1E }, + { 0x1EB0, 0x1EB1 }, + { 0x2C82, 0x2C83 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_175[] = { + { 0x01AE, 0x0288 }, + { 0x10BF, 0x2D1F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_176[] = { + { 0x01B1, 0x028A }, + { 0x04B4, 0x04B5 }, + { 0x10A0, 0x2D00 }, + { 0x1EAE, 0x1EAF }, + { 0x2C9C, 0x2C9D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_177[] = { + { 0x10A1, 0x2D01 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_178[] = { + { 0x01B3, 0x01B4 }, + { 0x04B6, 0x04B7 }, + { 0x10A2, 0x2D02 }, + { 0x1EAC, 0x1EAD }, + { 0x2C9E, 0x2C9F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_179[] = { + { 0x01B2, 0x028B }, + { 0x10A3, 0x2D03 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_180[] = { + { 0x01B5, 0x01B6 }, + { 0x04B0, 0x04B1 }, + { 0x10A4, 0x2D04 }, + { 0x1EAA, 0x1EAB }, + { 0x2C98, 0x2C99 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_181[] = { + { 0x00B5, 0x03BC }, + { 0x10A5, 0x2D05 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_182[] = { + { 0x01B7, 0x0292 }, + { 0x04B2, 0x04B3 }, + { 0x10A6, 0x2D06 }, + { 0x1EA8, 0x1EA9 }, + { 0x2C9A, 0x2C9B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_183[] = { + { 0x10A7, 0x2D07 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_184[] = { + { 0x04BC, 0x04BD }, + { 0x10A8, 0x2D08 }, + { 0x1EA6, 0x1EA7 }, + { 0x2C94, 0x2C95 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_185[] = { + { 0x01B8, 0x01B9 }, + { 0x10A9, 0x2D09 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_186[] = { + { 0x04BE, 0x04BF }, + { 0x10AA, 0x2D0A }, + { 0x1EA4, 0x1EA5 }, + { 0x2C96, 0x2C97 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_187[] = { + { 0x10AB, 0x2D0B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_188[] = { + { 0x04B8, 0x04B9 }, + { 0x10AC, 0x2D0C }, + { 0x1EA2, 0x1EA3 }, + { 0x2C90, 0x2C91 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_189[] = { + { 0x01BC, 0x01BD }, + { 0x10AD, 0x2D0D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_190[] = { + { 0x04BA, 0x04BB }, + { 0x10AE, 0x2D0E }, + { 0x1EA0, 0x1EA1 }, + { 0x2C92, 0x2C93 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_191[] = { + { 0x10AF, 0x2D0F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_192[] = { + { 0x00C0, 0x00E0 }, + { 0x1EDE, 0x1EDF }, + { 0xA666, 0xA667 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_193[] = { + { 0x00C1, 0x00E1 }, + { 0x03C2, 0x03C3 }, + { 0x04C5, 0x04C6 }, + { 0x2CED, 0x2CEE }, + { 0xA766, 0xA767 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_194[] = { + { 0x00C2, 0x00E2 }, + { 0x1EDC, 0x1EDD }, + { 0xA664, 0xA665 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_195[] = { + { 0x00C3, 0x00E3 }, + { 0x04C7, 0x04C8 }, + { 0xA764, 0xA765 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_196[] = { + { 0x00C4, 0x00E4 }, + { 0x01C5, 0x01C6 }, + { 0x04C0, 0x04CF }, + { 0x1EDA, 0x1EDB }, + { 0x1FDB, 0x1F77 }, + { 0xA662, 0xA663 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_197[] = { + { 0x00C5, 0x00E5 }, + { 0x01C4, 0x01C6 }, + { 0x04C1, 0x04C2 }, + { 0x1FDA, 0x1F76 }, + { 0xA762, 0xA763 }, + { 0xFF3A, 0xFF5A } +}; + +static const CaseFoldMapping1_16 case_fold1_16_198[] = { + { 0x00C6, 0x00E6 }, + { 0x01C7, 0x01C9 }, + { 0x1ED8, 0x1ED9 }, + { 0x1FD9, 0x1FD1 }, + { 0xA660, 0xA661 }, + { 0xFF39, 0xFF59 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_199[] = { + { 0x00C7, 0x00E7 }, + { 0x04C3, 0x04C4 }, + { 0x1FD8, 0x1FD0 }, + { 0x2CEB, 0x2CEC }, + { 0xA760, 0xA761 }, + { 0xFF38, 0xFF58 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_200[] = { + { 0x00C8, 0x00E8 }, + { 0x1ED6, 0x1ED7 }, + { 0xFF37, 0xFF57 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_201[] = { + { 0x00C9, 0x00E9 }, + { 0x01C8, 0x01C9 }, + { 0x04CD, 0x04CE }, + { 0xA76E, 0xA76F }, + { 0xFF36, 0xFF56 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_202[] = { + { 0x00CA, 0x00EA }, + { 0x01CB, 0x01CC }, + { 0x1ED4, 0x1ED5 }, + { 0xA66C, 0xA66D }, + { 0xFF35, 0xFF55 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_203[] = { + { 0x00CB, 0x00EB }, + { 0x01CA, 0x01CC }, + { 0xA76C, 0xA76D }, + { 0xFF34, 0xFF54 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_204[] = { + { 0x00CC, 0x00EC }, + { 0x01CD, 0x01CE }, + { 0x03CF, 0x03D7 }, + { 0x1ED2, 0x1ED3 }, + { 0x2CE0, 0x2CE1 }, + { 0xA66A, 0xA66B }, + { 0xFF33, 0xFF53 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_205[] = { + { 0x00CD, 0x00ED }, + { 0x04C9, 0x04CA }, + { 0xA76A, 0xA76B }, + { 0xFF32, 0xFF52 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_206[] = { + { 0x00CE, 0x00EE }, + { 0x01CF, 0x01D0 }, + { 0x1ED0, 0x1ED1 }, + { 0x2CE2, 0x2CE3 }, + { 0xA668, 0xA669 }, + { 0xFF31, 0xFF51 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_207[] = { + { 0x00CF, 0x00EF }, + { 0x04CB, 0x04CC }, + { 0xA768, 0xA769 }, + { 0xFF30, 0xFF50 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_208[] = { + { 0x00D0, 0x00F0 }, + { 0x01D1, 0x01D2 }, + { 0x04D4, 0x04D5 }, + { 0x10C0, 0x2D20 }, + { 0x1ECE, 0x1ECF }, + { 0xAB7B, 0x13AB }, + { 0xFF2F, 0xFF4F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_209[] = { + { 0x00D1, 0x00F1 }, + { 0x10C1, 0x2D21 }, + { 0xAB7A, 0x13AA }, + { 0xFF2E, 0xFF4E } +}; + +static const CaseFoldMapping1_16 case_fold1_16_210[] = { + { 0x00D2, 0x00F2 }, + { 0x01D3, 0x01D4 }, + { 0x03D1, 0x03B8 }, + { 0x04D6, 0x04D7 }, + { 0x10C2, 0x2D22 }, + { 0x1ECC, 0x1ECD }, + { 0xAB79, 0x13A9 }, + { 0xFF2D, 0xFF4D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_211[] = { + { 0x00D3, 0x00F3 }, + { 0x03D0, 0x03B2 }, + { 0x10C3, 0x2D23 }, + { 0xAB78, 0x13A8 }, + { 0xFF2C, 0xFF4C } +}; + +static const CaseFoldMapping1_16 case_fold1_16_212[] = { + { 0x00D4, 0x00F4 }, + { 0x01D5, 0x01D6 }, + { 0x04D0, 0x04D1 }, + { 0x10C4, 0x2D24 }, + { 0x1ECA, 0x1ECB }, + { 0x1FCB, 0x1F75 }, + { 0xAB7F, 0x13AF }, + { 0xFF2B, 0xFF4B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_213[] = { + { 0x00D5, 0x00F5 }, + { 0x03D6, 0x03C0 }, + { 0x10C5, 0x2D25 }, + { 0x1FCA, 0x1F74 }, + { 0xAB7E, 0x13AE }, + { 0xFF2A, 0xFF4A } +}; + +static const CaseFoldMapping1_16 case_fold1_16_214[] = { + { 0x00D6, 0x00F6 }, + { 0x01D7, 0x01D8 }, + { 0x03D5, 0x03C6 }, + { 0x04D2, 0x04D3 }, + { 0x1EC8, 0x1EC9 }, + { 0x1FC9, 0x1F73 }, + { 0xAB7D, 0x13AD }, + { 0xFF29, 0xFF49 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_215[] = { + { 0x10C7, 0x2D27 }, + { 0x1FC8, 0x1F72 }, + { 0xAB7C, 0x13AC }, + { 0xFF28, 0xFF48 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_216[] = { + { 0x00D8, 0x00F8 }, + { 0x01D9, 0x01DA }, + { 0x04DC, 0x04DD }, + { 0x1EC6, 0x1EC7 }, + { 0xAB73, 0x13A3 }, + { 0xFF27, 0xFF47 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_217[] = { + { 0x00D9, 0x00F9 }, + { 0x03DA, 0x03DB }, + { 0xA77E, 0xA77F }, + { 0xAB72, 0x13A2 }, + { 0xFF26, 0xFF46 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_218[] = { + { 0x00DA, 0x00FA }, + { 0x01DB, 0x01DC }, + { 0x04DE, 0x04DF }, + { 0x1EC4, 0x1EC5 }, + { 0xA77D, 0x1D79 }, + { 0xAB71, 0x13A1 }, + { 0xFF25, 0xFF45 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_219[] = { + { 0x00DB, 0x00FB }, + { 0x03D8, 0x03D9 }, + { 0xAB70, 0x13A0 }, + { 0xFF24, 0xFF44 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_220[] = { + { 0x00DC, 0x00FC }, + { 0x04D8, 0x04D9 }, + { 0x1EC2, 0x1EC3 }, + { 0xA77B, 0xA77C }, + { 0xAB77, 0x13A7 }, + { 0xFF23, 0xFF43 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_221[] = { + { 0x00DD, 0x00FD }, + { 0x03DE, 0x03DF }, + { 0x10CD, 0x2D2D }, + { 0xAB76, 0x13A6 }, + { 0xFF22, 0xFF42 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_222[] = { + { 0x00DE, 0x00FE }, + { 0x04DA, 0x04DB }, + { 0x1EC0, 0x1EC1 }, + { 0x2CF2, 0x2CF3 }, + { 0xA779, 0xA77A }, + { 0xAB75, 0x13A5 }, + { 0xFF21, 0xFF41 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_223[] = { + { 0x01DE, 0x01DF }, + { 0x03DC, 0x03DD }, + { 0xAB74, 0x13A4 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_224[] = { + { 0x04E4, 0x04E5 }, + { 0x1EFE, 0x1EFF }, + { 0x24C4, 0x24DE }, + { 0x2CCC, 0x2CCD }, + { 0xA646, 0xA647 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_225[] = { + { 0x01E0, 0x01E1 }, + { 0x03E2, 0x03E3 }, + { 0x24C5, 0x24DF }, + { 0xA746, 0xA747 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_226[] = { + { 0x04E6, 0x04E7 }, + { 0x1EFC, 0x1EFD }, + { 0x24C6, 0x24E0 }, + { 0x2CCE, 0x2CCF }, + { 0xA644, 0xA645 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_227[] = { + { 0x01E2, 0x01E3 }, + { 0x03E0, 0x03E1 }, + { 0x24C7, 0x24E1 }, + { 0xA744, 0xA745 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_228[] = { + { 0x04E0, 0x04E1 }, + { 0x1EFA, 0x1EFB }, + { 0x1FFB, 0x1F7D }, + { 0x24C0, 0x24DA }, + { 0x2CC8, 0x2CC9 }, + { 0xA642, 0xA643 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_229[] = { + { 0x01E4, 0x01E5 }, + { 0x03E6, 0x03E7 }, + { 0x1FFA, 0x1F7C }, + { 0x24C1, 0x24DB }, + { 0xA742, 0xA743 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_230[] = { + { 0x04E2, 0x04E3 }, + { 0x1EF8, 0x1EF9 }, + { 0x1FF9, 0x1F79 }, + { 0x24C2, 0x24DC }, + { 0x2CCA, 0x2CCB }, + { 0xA640, 0xA641 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_231[] = { + { 0x01E6, 0x01E7 }, + { 0x03E4, 0x03E5 }, + { 0x1FF8, 0x1F78 }, + { 0x24C3, 0x24DD }, + { 0xA740, 0xA741 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_232[] = { + { 0x04EC, 0x04ED }, + { 0x13FB, 0x13F3 }, + { 0x1EF6, 0x1EF7 }, + { 0x24CC, 0x24E6 }, + { 0x2CC4, 0x2CC5 }, + { 0xA64E, 0xA64F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_233[] = { + { 0x01E8, 0x01E9 }, + { 0x03EA, 0x03EB }, + { 0x13FA, 0x13F2 }, + { 0x24CD, 0x24E7 }, + { 0xA74E, 0xA74F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_234[] = { + { 0x04EE, 0x04EF }, + { 0x13F9, 0x13F1 }, + { 0x1EF4, 0x1EF5 }, + { 0x24CE, 0x24E8 }, + { 0x2CC6, 0x2CC7 }, + { 0xA64C, 0xA64D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_235[] = { + { 0x01EA, 0x01EB }, + { 0x03E8, 0x03E9 }, + { 0x13F8, 0x13F0 }, + { 0x24CF, 0x24E9 }, + { 0xA74C, 0xA74D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_236[] = { + { 0x04E8, 0x04E9 }, + { 0x1EF2, 0x1EF3 }, + { 0x24C8, 0x24E2 }, + { 0x2CC0, 0x2CC1 }, + { 0xA64A, 0xA64B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_237[] = { + { 0x01EC, 0x01ED }, + { 0x03EE, 0x03EF }, + { 0x24C9, 0x24E3 }, + { 0xA74A, 0xA74B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_238[] = { + { 0x04EA, 0x04EB }, + { 0x13FD, 0x13F5 }, + { 0x1EF0, 0x1EF1 }, + { 0x24CA, 0x24E4 }, + { 0x2CC2, 0x2CC3 }, + { 0xA648, 0xA649 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_239[] = { + { 0x01EE, 0x01EF }, + { 0x03EC, 0x03ED }, + { 0x13FC, 0x13F4 }, + { 0x24CB, 0x24E5 }, + { 0xA748, 0xA749 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_240[] = { + { 0x01F1, 0x01F3 }, + { 0x04F4, 0x04F5 }, + { 0x1EEE, 0x1EEF }, + { 0x2CDC, 0x2CDD }, + { 0xA656, 0xA657 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_241[] = { + { 0xA756, 0xA757 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_242[] = { + { 0x03F1, 0x03C1 }, + { 0x04F6, 0x04F7 }, + { 0x1EEC, 0x1EED }, + { 0x2CDE, 0x2CDF }, + { 0xA654, 0xA655 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_243[] = { + { 0x01F2, 0x01F3 }, + { 0x03F0, 0x03BA }, + { 0x1FEC, 0x1FE5 }, + { 0xA754, 0xA755 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_244[] = { + { 0x03F7, 0x03F8 }, + { 0x04F0, 0x04F1 }, + { 0x1EEA, 0x1EEB }, + { 0x1FEB, 0x1F7B }, + { 0x2CD8, 0x2CD9 }, + { 0xA652, 0xA653 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_245[] = { + { 0x01F4, 0x01F5 }, + { 0x1FEA, 0x1F7A }, + { 0xA752, 0xA753 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_246[] = { + { 0x01F7, 0x01BF }, + { 0x03F5, 0x03B5 }, + { 0x04F2, 0x04F3 }, + { 0x1EE8, 0x1EE9 }, + { 0x1FE9, 0x1FE1 }, + { 0x2CDA, 0x2CDB }, + { 0xA650, 0xA651 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_247[] = { + { 0x01F6, 0x0195 }, + { 0x03F4, 0x03B8 }, + { 0x1FE8, 0x1FE0 }, + { 0xA750, 0xA751 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_248[] = { + { 0x04FC, 0x04FD }, + { 0x1EE6, 0x1EE7 }, + { 0x2CD4, 0x2CD5 }, + { 0xA65E, 0xA65F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_249[] = { + { 0x01F8, 0x01F9 }, + { 0x03FA, 0x03FB }, + { 0xA75E, 0xA75F } +}; + +static const CaseFoldMapping1_16 case_fold1_16_250[] = { + { 0x03F9, 0x03F2 }, + { 0x04FE, 0x04FF }, + { 0x1EE4, 0x1EE5 }, + { 0x2CD6, 0x2CD7 }, + { 0xA65C, 0xA65D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_251[] = { + { 0x01FA, 0x01FB }, + { 0xA75C, 0xA75D } +}; + +static const CaseFoldMapping1_16 case_fold1_16_252[] = { + { 0x03FF, 0x037D }, + { 0x04F8, 0x04F9 }, + { 0x1EE2, 0x1EE3 }, + { 0x2CD0, 0x2CD1 }, + { 0xA65A, 0xA65B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_253[] = { + { 0x01FC, 0x01FD }, + { 0x03FE, 0x037C }, + { 0xA75A, 0xA75B } +}; + +static const CaseFoldMapping1_16 case_fold1_16_254[] = { + { 0x03FD, 0x037B }, + { 0x04FA, 0x04FB }, + { 0x1EE0, 0x1EE1 }, + { 0x2CD2, 0x2CD3 }, + { 0xA658, 0xA659 } +}; + +static const CaseFoldMapping1_16 case_fold1_16_255[] = { + { 0x01FE, 0x01FF }, + { 0xA758, 0xA759 } +}; + +static const CaseFoldMapping1_32 case_fold1_32_000[] = { + { 0x10404, 0x1042C }, + { 0x10414, 0x1043C }, + { 0x10424, 0x1044C }, + { 0x10C8C, 0x10CCC }, + { 0x10C9C, 0x10CDC }, + { 0x10CAC, 0x10CEC }, + { 0x118A8, 0x118C8 }, + { 0x118B8, 0x118D8 } +}; + +static const CaseFoldMapping1_32 case_fold1_32_001[] = { + { 0x10405, 0x1042D }, + { 0x10415, 0x1043D }, + { 0x10425, 0x1044D }, + { 0x10C8D, 0x10CCD }, + { 0x10C9D, 0x10CDD }, + { 0x10CAD, 0x10CED }, + { 0x118A9, 0x118C9 }, + { 0x118B9, 0x118D9 } +}; + +static const CaseFoldMapping1_32 case_fold1_32_002[] = { + { 0x10406, 0x1042E }, + { 0x10416, 0x1043E }, + { 0x10426, 0x1044E }, + { 0x10C8E, 0x10CCE }, + { 0x10C9E, 0x10CDE }, + { 0x10CAE, 0x10CEE }, + { 0x118AA, 0x118CA }, + { 0x118BA, 0x118DA } +}; + +static const CaseFoldMapping1_32 case_fold1_32_003[] = { + { 0x10407, 0x1042F }, + { 0x10417, 0x1043F }, + { 0x10427, 0x1044F }, + { 0x10C8F, 0x10CCF }, + { 0x10C9F, 0x10CDF }, + { 0x10CAF, 0x10CEF }, + { 0x118AB, 0x118CB }, + { 0x118BB, 0x118DB } +}; + +static const CaseFoldMapping1_32 case_fold1_32_004[] = { + { 0x10400, 0x10428 }, + { 0x10410, 0x10438 }, + { 0x10420, 0x10448 }, + { 0x10C88, 0x10CC8 }, + { 0x10C98, 0x10CD8 }, + { 0x10CA8, 0x10CE8 }, + { 0x118AC, 0x118CC }, + { 0x118BC, 0x118DC } +}; + +static const CaseFoldMapping1_32 case_fold1_32_005[] = { + { 0x10401, 0x10429 }, + { 0x10411, 0x10439 }, + { 0x10421, 0x10449 }, + { 0x10C89, 0x10CC9 }, + { 0x10C99, 0x10CD9 }, + { 0x10CA9, 0x10CE9 }, + { 0x118AD, 0x118CD }, + { 0x118BD, 0x118DD } +}; + +static const CaseFoldMapping1_32 case_fold1_32_006[] = { + { 0x10402, 0x1042A }, + { 0x10412, 0x1043A }, + { 0x10422, 0x1044A }, + { 0x10C8A, 0x10CCA }, + { 0x10C9A, 0x10CDA }, + { 0x10CAA, 0x10CEA }, + { 0x118AE, 0x118CE }, + { 0x118BE, 0x118DE } +}; + +static const CaseFoldMapping1_32 case_fold1_32_007[] = { + { 0x10403, 0x1042B }, + { 0x10413, 0x1043B }, + { 0x10423, 0x1044B }, + { 0x10C8B, 0x10CCB }, + { 0x10C9B, 0x10CDB }, + { 0x10CAB, 0x10CEB }, + { 0x118AF, 0x118CF }, + { 0x118BF, 0x118DF } +}; + +static const CaseFoldMapping1_32 case_fold1_32_008[] = { + { 0x1040C, 0x10434 }, + { 0x1041C, 0x10444 }, + { 0x10C84, 0x10CC4 }, + { 0x10C94, 0x10CD4 }, + { 0x10CA4, 0x10CE4 }, + { 0x118A0, 0x118C0 }, + { 0x118B0, 0x118D0 } +}; + +static const CaseFoldMapping1_32 case_fold1_32_009[] = { + { 0x1040D, 0x10435 }, + { 0x1041D, 0x10445 }, + { 0x10C85, 0x10CC5 }, + { 0x10C95, 0x10CD5 }, + { 0x10CA5, 0x10CE5 }, + { 0x118A1, 0x118C1 }, + { 0x118B1, 0x118D1 } +}; + +static const CaseFoldMapping1_32 case_fold1_32_010[] = { + { 0x1040E, 0x10436 }, + { 0x1041E, 0x10446 }, + { 0x10C86, 0x10CC6 }, + { 0x10C96, 0x10CD6 }, + { 0x10CA6, 0x10CE6 }, + { 0x118A2, 0x118C2 }, + { 0x118B2, 0x118D2 } +}; + +static const CaseFoldMapping1_32 case_fold1_32_011[] = { + { 0x1040F, 0x10437 }, + { 0x1041F, 0x10447 }, + { 0x10C87, 0x10CC7 }, + { 0x10C97, 0x10CD7 }, + { 0x10CA7, 0x10CE7 }, + { 0x118A3, 0x118C3 }, + { 0x118B3, 0x118D3 } +}; + +static const CaseFoldMapping1_32 case_fold1_32_012[] = { + { 0x10408, 0x10430 }, + { 0x10418, 0x10440 }, + { 0x10C80, 0x10CC0 }, + { 0x10C90, 0x10CD0 }, + { 0x10CA0, 0x10CE0 }, + { 0x10CB0, 0x10CF0 }, + { 0x118A4, 0x118C4 }, + { 0x118B4, 0x118D4 } +}; + +static const CaseFoldMapping1_32 case_fold1_32_013[] = { + { 0x10409, 0x10431 }, + { 0x10419, 0x10441 }, + { 0x10C81, 0x10CC1 }, + { 0x10C91, 0x10CD1 }, + { 0x10CA1, 0x10CE1 }, + { 0x10CB1, 0x10CF1 }, + { 0x118A5, 0x118C5 }, + { 0x118B5, 0x118D5 } +}; + +static const CaseFoldMapping1_32 case_fold1_32_014[] = { + { 0x1040A, 0x10432 }, + { 0x1041A, 0x10442 }, + { 0x10C82, 0x10CC2 }, + { 0x10C92, 0x10CD2 }, + { 0x10CA2, 0x10CE2 }, + { 0x10CB2, 0x10CF2 }, + { 0x118A6, 0x118C6 }, + { 0x118B6, 0x118D6 } +}; + +static const CaseFoldMapping1_32 case_fold1_32_015[] = { + { 0x1040B, 0x10433 }, + { 0x1041B, 0x10443 }, + { 0x10C83, 0x10CC3 }, + { 0x10C93, 0x10CD3 }, + { 0x10CA3, 0x10CE3 }, + { 0x118A7, 0x118C7 }, + { 0x118B7, 0x118D7 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_000[] = { + { 0x1E9E, 0x0073, 0x0073 }, + { 0x1F8F, 0x1F07, 0x03B9 }, + { 0x1F9F, 0x1F27, 0x03B9 }, + { 0x1FAF, 0x1F67, 0x03B9 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_001[] = { + { 0x0130, 0x0069, 0x0307 }, + { 0x01F0, 0x006A, 0x030C }, + { 0x1F8E, 0x1F06, 0x03B9 }, + { 0x1F9E, 0x1F26, 0x03B9 }, + { 0x1FAE, 0x1F66, 0x03B9 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_002[] = { + { 0x0587, 0x0565, 0x0582 }, + { 0x1F8D, 0x1F05, 0x03B9 }, + { 0x1F9D, 0x1F25, 0x03B9 }, + { 0x1FAD, 0x1F65, 0x03B9 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_003[] = { + { 0x1F8C, 0x1F04, 0x03B9 }, + { 0x1F9C, 0x1F24, 0x03B9 }, + { 0x1FAC, 0x1F64, 0x03B9 }, + { 0x1FBC, 0x03B1, 0x03B9 }, + { 0x1FCC, 0x03B7, 0x03B9 }, + { 0x1FFC, 0x03C9, 0x03B9 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_004[] = { + { 0x1E9A, 0x0061, 0x02BE }, + { 0x1F8B, 0x1F03, 0x03B9 }, + { 0x1F9B, 0x1F23, 0x03B9 }, + { 0x1FAB, 0x1F63, 0x03B9 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_005[] = { + { 0x1F8A, 0x1F02, 0x03B9 }, + { 0x1F9A, 0x1F22, 0x03B9 }, + { 0x1FAA, 0x1F62, 0x03B9 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_006[] = { + { 0x1E98, 0x0077, 0x030A }, + { 0x1F89, 0x1F01, 0x03B9 }, + { 0x1F99, 0x1F21, 0x03B9 }, + { 0x1FA9, 0x1F61, 0x03B9 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_007[] = { + { 0x1E99, 0x0079, 0x030A }, + { 0x1F88, 0x1F00, 0x03B9 }, + { 0x1F98, 0x1F20, 0x03B9 }, + { 0x1FA8, 0x1F60, 0x03B9 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_008[] = { + { 0x0149, 0x02BC, 0x006E }, + { 0x1E96, 0x0068, 0x0331 }, + { 0x1F87, 0x1F07, 0x03B9 }, + { 0x1F97, 0x1F27, 0x03B9 }, + { 0x1FA7, 0x1F67, 0x03B9 }, + { 0xFB13, 0x0574, 0x0576 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_009[] = { + { 0x1E97, 0x0074, 0x0308 }, + { 0x1F86, 0x1F06, 0x03B9 }, + { 0x1F96, 0x1F26, 0x03B9 }, + { 0x1FA6, 0x1F66, 0x03B9 }, + { 0x1FB6, 0x03B1, 0x0342 }, + { 0x1FC6, 0x03B7, 0x0342 }, + { 0x1FD6, 0x03B9, 0x0342 }, + { 0x1FE6, 0x03C5, 0x0342 }, + { 0x1FF6, 0x03C9, 0x0342 }, + { 0xFB02, 0x0066, 0x006C } +}; + +static const CaseFoldMapping2_16 case_fold2_16_010[] = { + { 0x1F85, 0x1F05, 0x03B9 }, + { 0x1F95, 0x1F25, 0x03B9 }, + { 0x1FA5, 0x1F65, 0x03B9 }, + { 0xFB01, 0x0066, 0x0069 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_011[] = { + { 0x1F84, 0x1F04, 0x03B9 }, + { 0x1F94, 0x1F24, 0x03B9 }, + { 0x1FA4, 0x1F64, 0x03B9 }, + { 0x1FB4, 0x03AC, 0x03B9 }, + { 0x1FC4, 0x03AE, 0x03B9 }, + { 0x1FE4, 0x03C1, 0x0313 }, + { 0x1FF4, 0x03CE, 0x03B9 }, + { 0xFB00, 0x0066, 0x0066 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_012[] = { + { 0x1F83, 0x1F03, 0x03B9 }, + { 0x1F93, 0x1F23, 0x03B9 }, + { 0x1FA3, 0x1F63, 0x03B9 }, + { 0x1FB3, 0x03B1, 0x03B9 }, + { 0x1FC3, 0x03B7, 0x03B9 }, + { 0x1FF3, 0x03C9, 0x03B9 }, + { 0xFB17, 0x0574, 0x056D } +}; + +static const CaseFoldMapping2_16 case_fold2_16_013[] = { + { 0x1F82, 0x1F02, 0x03B9 }, + { 0x1F92, 0x1F22, 0x03B9 }, + { 0x1FA2, 0x1F62, 0x03B9 }, + { 0x1FB2, 0x1F70, 0x03B9 }, + { 0x1FC2, 0x1F74, 0x03B9 }, + { 0x1FF2, 0x1F7C, 0x03B9 }, + { 0xFB06, 0x0073, 0x0074 }, + { 0xFB16, 0x057E, 0x0576 } +}; + +static const CaseFoldMapping2_16 case_fold2_16_014[] = { + { 0x1F81, 0x1F01, 0x03B9 }, + { 0x1F91, 0x1F21, 0x03B9 }, + { 0x1FA1, 0x1F61, 0x03B9 }, + { 0xFB05, 0x0073, 0x0074 }, + { 0xFB15, 0x0574, 0x056B } +}; + +static const CaseFoldMapping2_16 case_fold2_16_015[] = { + { 0x00DF, 0x0073, 0x0073 }, + { 0x1F50, 0x03C5, 0x0313 }, + { 0x1F80, 0x1F00, 0x03B9 }, + { 0x1F90, 0x1F20, 0x03B9 }, + { 0x1FA0, 0x1F60, 0x03B9 }, + { 0xFB14, 0x0574, 0x0565 } +}; + +static const CaseFoldMapping3_16 case_fold3_16_000[] = { + { 0x1FB7, 0x03B1, 0x0342, 0x03B9 }, + { 0x1FC7, 0x03B7, 0x0342, 0x03B9 }, + { 0x1FD3, 0x03B9, 0x0308, 0x0301 }, + { 0x1FD7, 0x03B9, 0x0308, 0x0342 }, + { 0x1FE3, 0x03C5, 0x0308, 0x0301 }, + { 0x1FE7, 0x03C5, 0x0308, 0x0342 }, + { 0x1FF7, 0x03C9, 0x0342, 0x03B9 }, + { 0xFB03, 0x0066, 0x0066, 0x0069 } +}; + +static const CaseFoldMapping3_16 case_fold3_16_001[] = { + { 0x1F52, 0x03C5, 0x0313, 0x0300 }, + { 0x1F56, 0x03C5, 0x0313, 0x0342 }, + { 0x1FD2, 0x03B9, 0x0308, 0x0300 }, + { 0x1FE2, 0x03C5, 0x0308, 0x0300 } +}; + +static const CaseFoldMapping3_16 case_fold3_16_003[] = { + { 0x0390, 0x03B9, 0x0308, 0x0301 }, + { 0x03B0, 0x03C5, 0x0308, 0x0301 }, + { 0x1F54, 0x03C5, 0x0313, 0x0301 }, + { 0xFB04, 0x0066, 0x0066, 0x006C } +}; + +static const CaseFoldHashBucket1_16 case_fold_hash1_16[] = { + { case_fold1_16_000, __PHYSFS_ARRAYLEN(case_fold1_16_000) }, + { case_fold1_16_001, __PHYSFS_ARRAYLEN(case_fold1_16_001) }, + { case_fold1_16_002, __PHYSFS_ARRAYLEN(case_fold1_16_002) }, + { case_fold1_16_003, __PHYSFS_ARRAYLEN(case_fold1_16_003) }, + { case_fold1_16_004, __PHYSFS_ARRAYLEN(case_fold1_16_004) }, + { case_fold1_16_005, __PHYSFS_ARRAYLEN(case_fold1_16_005) }, + { case_fold1_16_006, __PHYSFS_ARRAYLEN(case_fold1_16_006) }, + { case_fold1_16_007, __PHYSFS_ARRAYLEN(case_fold1_16_007) }, + { case_fold1_16_008, __PHYSFS_ARRAYLEN(case_fold1_16_008) }, + { case_fold1_16_009, __PHYSFS_ARRAYLEN(case_fold1_16_009) }, + { case_fold1_16_010, __PHYSFS_ARRAYLEN(case_fold1_16_010) }, + { case_fold1_16_011, __PHYSFS_ARRAYLEN(case_fold1_16_011) }, + { case_fold1_16_012, __PHYSFS_ARRAYLEN(case_fold1_16_012) }, + { case_fold1_16_013, __PHYSFS_ARRAYLEN(case_fold1_16_013) }, + { case_fold1_16_014, __PHYSFS_ARRAYLEN(case_fold1_16_014) }, + { case_fold1_16_015, __PHYSFS_ARRAYLEN(case_fold1_16_015) }, + { case_fold1_16_016, __PHYSFS_ARRAYLEN(case_fold1_16_016) }, + { case_fold1_16_017, __PHYSFS_ARRAYLEN(case_fold1_16_017) }, + { case_fold1_16_018, __PHYSFS_ARRAYLEN(case_fold1_16_018) }, + { case_fold1_16_019, __PHYSFS_ARRAYLEN(case_fold1_16_019) }, + { case_fold1_16_020, __PHYSFS_ARRAYLEN(case_fold1_16_020) }, + { case_fold1_16_021, __PHYSFS_ARRAYLEN(case_fold1_16_021) }, + { case_fold1_16_022, __PHYSFS_ARRAYLEN(case_fold1_16_022) }, + { case_fold1_16_023, __PHYSFS_ARRAYLEN(case_fold1_16_023) }, + { case_fold1_16_024, __PHYSFS_ARRAYLEN(case_fold1_16_024) }, + { case_fold1_16_025, __PHYSFS_ARRAYLEN(case_fold1_16_025) }, + { case_fold1_16_026, __PHYSFS_ARRAYLEN(case_fold1_16_026) }, + { case_fold1_16_027, __PHYSFS_ARRAYLEN(case_fold1_16_027) }, + { case_fold1_16_028, __PHYSFS_ARRAYLEN(case_fold1_16_028) }, + { case_fold1_16_029, __PHYSFS_ARRAYLEN(case_fold1_16_029) }, + { case_fold1_16_030, __PHYSFS_ARRAYLEN(case_fold1_16_030) }, + { case_fold1_16_031, __PHYSFS_ARRAYLEN(case_fold1_16_031) }, + { case_fold1_16_032, __PHYSFS_ARRAYLEN(case_fold1_16_032) }, + { case_fold1_16_033, __PHYSFS_ARRAYLEN(case_fold1_16_033) }, + { case_fold1_16_034, __PHYSFS_ARRAYLEN(case_fold1_16_034) }, + { case_fold1_16_035, __PHYSFS_ARRAYLEN(case_fold1_16_035) }, + { case_fold1_16_036, __PHYSFS_ARRAYLEN(case_fold1_16_036) }, + { case_fold1_16_037, __PHYSFS_ARRAYLEN(case_fold1_16_037) }, + { case_fold1_16_038, __PHYSFS_ARRAYLEN(case_fold1_16_038) }, + { case_fold1_16_039, __PHYSFS_ARRAYLEN(case_fold1_16_039) }, + { case_fold1_16_040, __PHYSFS_ARRAYLEN(case_fold1_16_040) }, + { case_fold1_16_041, __PHYSFS_ARRAYLEN(case_fold1_16_041) }, + { case_fold1_16_042, __PHYSFS_ARRAYLEN(case_fold1_16_042) }, + { case_fold1_16_043, __PHYSFS_ARRAYLEN(case_fold1_16_043) }, + { case_fold1_16_044, __PHYSFS_ARRAYLEN(case_fold1_16_044) }, + { case_fold1_16_045, __PHYSFS_ARRAYLEN(case_fold1_16_045) }, + { case_fold1_16_046, __PHYSFS_ARRAYLEN(case_fold1_16_046) }, + { case_fold1_16_047, __PHYSFS_ARRAYLEN(case_fold1_16_047) }, + { case_fold1_16_048, __PHYSFS_ARRAYLEN(case_fold1_16_048) }, + { case_fold1_16_049, __PHYSFS_ARRAYLEN(case_fold1_16_049) }, + { case_fold1_16_050, __PHYSFS_ARRAYLEN(case_fold1_16_050) }, + { case_fold1_16_051, __PHYSFS_ARRAYLEN(case_fold1_16_051) }, + { case_fold1_16_052, __PHYSFS_ARRAYLEN(case_fold1_16_052) }, + { case_fold1_16_053, __PHYSFS_ARRAYLEN(case_fold1_16_053) }, + { case_fold1_16_054, __PHYSFS_ARRAYLEN(case_fold1_16_054) }, + { case_fold1_16_055, __PHYSFS_ARRAYLEN(case_fold1_16_055) }, + { case_fold1_16_056, __PHYSFS_ARRAYLEN(case_fold1_16_056) }, + { case_fold1_16_057, __PHYSFS_ARRAYLEN(case_fold1_16_057) }, + { case_fold1_16_058, __PHYSFS_ARRAYLEN(case_fold1_16_058) }, + { case_fold1_16_059, __PHYSFS_ARRAYLEN(case_fold1_16_059) }, + { case_fold1_16_060, __PHYSFS_ARRAYLEN(case_fold1_16_060) }, + { case_fold1_16_061, __PHYSFS_ARRAYLEN(case_fold1_16_061) }, + { case_fold1_16_062, __PHYSFS_ARRAYLEN(case_fold1_16_062) }, + { case_fold1_16_063, __PHYSFS_ARRAYLEN(case_fold1_16_063) }, + { case_fold1_16_064, __PHYSFS_ARRAYLEN(case_fold1_16_064) }, + { case_fold1_16_065, __PHYSFS_ARRAYLEN(case_fold1_16_065) }, + { case_fold1_16_066, __PHYSFS_ARRAYLEN(case_fold1_16_066) }, + { case_fold1_16_067, __PHYSFS_ARRAYLEN(case_fold1_16_067) }, + { case_fold1_16_068, __PHYSFS_ARRAYLEN(case_fold1_16_068) }, + { case_fold1_16_069, __PHYSFS_ARRAYLEN(case_fold1_16_069) }, + { case_fold1_16_070, __PHYSFS_ARRAYLEN(case_fold1_16_070) }, + { case_fold1_16_071, __PHYSFS_ARRAYLEN(case_fold1_16_071) }, + { case_fold1_16_072, __PHYSFS_ARRAYLEN(case_fold1_16_072) }, + { case_fold1_16_073, __PHYSFS_ARRAYLEN(case_fold1_16_073) }, + { case_fold1_16_074, __PHYSFS_ARRAYLEN(case_fold1_16_074) }, + { case_fold1_16_075, __PHYSFS_ARRAYLEN(case_fold1_16_075) }, + { case_fold1_16_076, __PHYSFS_ARRAYLEN(case_fold1_16_076) }, + { case_fold1_16_077, __PHYSFS_ARRAYLEN(case_fold1_16_077) }, + { case_fold1_16_078, __PHYSFS_ARRAYLEN(case_fold1_16_078) }, + { case_fold1_16_079, __PHYSFS_ARRAYLEN(case_fold1_16_079) }, + { case_fold1_16_080, __PHYSFS_ARRAYLEN(case_fold1_16_080) }, + { case_fold1_16_081, __PHYSFS_ARRAYLEN(case_fold1_16_081) }, + { case_fold1_16_082, __PHYSFS_ARRAYLEN(case_fold1_16_082) }, + { case_fold1_16_083, __PHYSFS_ARRAYLEN(case_fold1_16_083) }, + { case_fold1_16_084, __PHYSFS_ARRAYLEN(case_fold1_16_084) }, + { case_fold1_16_085, __PHYSFS_ARRAYLEN(case_fold1_16_085) }, + { case_fold1_16_086, __PHYSFS_ARRAYLEN(case_fold1_16_086) }, + { case_fold1_16_087, __PHYSFS_ARRAYLEN(case_fold1_16_087) }, + { case_fold1_16_088, __PHYSFS_ARRAYLEN(case_fold1_16_088) }, + { case_fold1_16_089, __PHYSFS_ARRAYLEN(case_fold1_16_089) }, + { case_fold1_16_090, __PHYSFS_ARRAYLEN(case_fold1_16_090) }, + { case_fold1_16_091, __PHYSFS_ARRAYLEN(case_fold1_16_091) }, + { case_fold1_16_092, __PHYSFS_ARRAYLEN(case_fold1_16_092) }, + { case_fold1_16_093, __PHYSFS_ARRAYLEN(case_fold1_16_093) }, + { case_fold1_16_094, __PHYSFS_ARRAYLEN(case_fold1_16_094) }, + { case_fold1_16_095, __PHYSFS_ARRAYLEN(case_fold1_16_095) }, + { case_fold1_16_096, __PHYSFS_ARRAYLEN(case_fold1_16_096) }, + { case_fold1_16_097, __PHYSFS_ARRAYLEN(case_fold1_16_097) }, + { case_fold1_16_098, __PHYSFS_ARRAYLEN(case_fold1_16_098) }, + { case_fold1_16_099, __PHYSFS_ARRAYLEN(case_fold1_16_099) }, + { case_fold1_16_100, __PHYSFS_ARRAYLEN(case_fold1_16_100) }, + { case_fold1_16_101, __PHYSFS_ARRAYLEN(case_fold1_16_101) }, + { case_fold1_16_102, __PHYSFS_ARRAYLEN(case_fold1_16_102) }, + { case_fold1_16_103, __PHYSFS_ARRAYLEN(case_fold1_16_103) }, + { case_fold1_16_104, __PHYSFS_ARRAYLEN(case_fold1_16_104) }, + { case_fold1_16_105, __PHYSFS_ARRAYLEN(case_fold1_16_105) }, + { case_fold1_16_106, __PHYSFS_ARRAYLEN(case_fold1_16_106) }, + { case_fold1_16_107, __PHYSFS_ARRAYLEN(case_fold1_16_107) }, + { case_fold1_16_108, __PHYSFS_ARRAYLEN(case_fold1_16_108) }, + { case_fold1_16_109, __PHYSFS_ARRAYLEN(case_fold1_16_109) }, + { case_fold1_16_110, __PHYSFS_ARRAYLEN(case_fold1_16_110) }, + { case_fold1_16_111, __PHYSFS_ARRAYLEN(case_fold1_16_111) }, + { case_fold1_16_112, __PHYSFS_ARRAYLEN(case_fold1_16_112) }, + { case_fold1_16_113, __PHYSFS_ARRAYLEN(case_fold1_16_113) }, + { case_fold1_16_114, __PHYSFS_ARRAYLEN(case_fold1_16_114) }, + { case_fold1_16_115, __PHYSFS_ARRAYLEN(case_fold1_16_115) }, + { case_fold1_16_116, __PHYSFS_ARRAYLEN(case_fold1_16_116) }, + { case_fold1_16_117, __PHYSFS_ARRAYLEN(case_fold1_16_117) }, + { case_fold1_16_118, __PHYSFS_ARRAYLEN(case_fold1_16_118) }, + { case_fold1_16_119, __PHYSFS_ARRAYLEN(case_fold1_16_119) }, + { case_fold1_16_120, __PHYSFS_ARRAYLEN(case_fold1_16_120) }, + { case_fold1_16_121, __PHYSFS_ARRAYLEN(case_fold1_16_121) }, + { case_fold1_16_122, __PHYSFS_ARRAYLEN(case_fold1_16_122) }, + { NULL, 0 }, + { case_fold1_16_124, __PHYSFS_ARRAYLEN(case_fold1_16_124) }, + { NULL, 0 }, + { case_fold1_16_126, __PHYSFS_ARRAYLEN(case_fold1_16_126) }, + { NULL, 0 }, + { case_fold1_16_128, __PHYSFS_ARRAYLEN(case_fold1_16_128) }, + { case_fold1_16_129, __PHYSFS_ARRAYLEN(case_fold1_16_129) }, + { case_fold1_16_130, __PHYSFS_ARRAYLEN(case_fold1_16_130) }, + { case_fold1_16_131, __PHYSFS_ARRAYLEN(case_fold1_16_131) }, + { case_fold1_16_132, __PHYSFS_ARRAYLEN(case_fold1_16_132) }, + { case_fold1_16_133, __PHYSFS_ARRAYLEN(case_fold1_16_133) }, + { case_fold1_16_134, __PHYSFS_ARRAYLEN(case_fold1_16_134) }, + { case_fold1_16_135, __PHYSFS_ARRAYLEN(case_fold1_16_135) }, + { case_fold1_16_136, __PHYSFS_ARRAYLEN(case_fold1_16_136) }, + { case_fold1_16_137, __PHYSFS_ARRAYLEN(case_fold1_16_137) }, + { case_fold1_16_138, __PHYSFS_ARRAYLEN(case_fold1_16_138) }, + { case_fold1_16_139, __PHYSFS_ARRAYLEN(case_fold1_16_139) }, + { case_fold1_16_140, __PHYSFS_ARRAYLEN(case_fold1_16_140) }, + { case_fold1_16_141, __PHYSFS_ARRAYLEN(case_fold1_16_141) }, + { case_fold1_16_142, __PHYSFS_ARRAYLEN(case_fold1_16_142) }, + { case_fold1_16_143, __PHYSFS_ARRAYLEN(case_fold1_16_143) }, + { case_fold1_16_144, __PHYSFS_ARRAYLEN(case_fold1_16_144) }, + { case_fold1_16_145, __PHYSFS_ARRAYLEN(case_fold1_16_145) }, + { case_fold1_16_146, __PHYSFS_ARRAYLEN(case_fold1_16_146) }, + { case_fold1_16_147, __PHYSFS_ARRAYLEN(case_fold1_16_147) }, + { case_fold1_16_148, __PHYSFS_ARRAYLEN(case_fold1_16_148) }, + { case_fold1_16_149, __PHYSFS_ARRAYLEN(case_fold1_16_149) }, + { case_fold1_16_150, __PHYSFS_ARRAYLEN(case_fold1_16_150) }, + { case_fold1_16_151, __PHYSFS_ARRAYLEN(case_fold1_16_151) }, + { case_fold1_16_152, __PHYSFS_ARRAYLEN(case_fold1_16_152) }, + { case_fold1_16_153, __PHYSFS_ARRAYLEN(case_fold1_16_153) }, + { case_fold1_16_154, __PHYSFS_ARRAYLEN(case_fold1_16_154) }, + { case_fold1_16_155, __PHYSFS_ARRAYLEN(case_fold1_16_155) }, + { case_fold1_16_156, __PHYSFS_ARRAYLEN(case_fold1_16_156) }, + { case_fold1_16_157, __PHYSFS_ARRAYLEN(case_fold1_16_157) }, + { case_fold1_16_158, __PHYSFS_ARRAYLEN(case_fold1_16_158) }, + { case_fold1_16_159, __PHYSFS_ARRAYLEN(case_fold1_16_159) }, + { case_fold1_16_160, __PHYSFS_ARRAYLEN(case_fold1_16_160) }, + { case_fold1_16_161, __PHYSFS_ARRAYLEN(case_fold1_16_161) }, + { case_fold1_16_162, __PHYSFS_ARRAYLEN(case_fold1_16_162) }, + { case_fold1_16_163, __PHYSFS_ARRAYLEN(case_fold1_16_163) }, + { case_fold1_16_164, __PHYSFS_ARRAYLEN(case_fold1_16_164) }, + { case_fold1_16_165, __PHYSFS_ARRAYLEN(case_fold1_16_165) }, + { case_fold1_16_166, __PHYSFS_ARRAYLEN(case_fold1_16_166) }, + { case_fold1_16_167, __PHYSFS_ARRAYLEN(case_fold1_16_167) }, + { case_fold1_16_168, __PHYSFS_ARRAYLEN(case_fold1_16_168) }, + { case_fold1_16_169, __PHYSFS_ARRAYLEN(case_fold1_16_169) }, + { case_fold1_16_170, __PHYSFS_ARRAYLEN(case_fold1_16_170) }, + { case_fold1_16_171, __PHYSFS_ARRAYLEN(case_fold1_16_171) }, + { case_fold1_16_172, __PHYSFS_ARRAYLEN(case_fold1_16_172) }, + { case_fold1_16_173, __PHYSFS_ARRAYLEN(case_fold1_16_173) }, + { case_fold1_16_174, __PHYSFS_ARRAYLEN(case_fold1_16_174) }, + { case_fold1_16_175, __PHYSFS_ARRAYLEN(case_fold1_16_175) }, + { case_fold1_16_176, __PHYSFS_ARRAYLEN(case_fold1_16_176) }, + { case_fold1_16_177, __PHYSFS_ARRAYLEN(case_fold1_16_177) }, + { case_fold1_16_178, __PHYSFS_ARRAYLEN(case_fold1_16_178) }, + { case_fold1_16_179, __PHYSFS_ARRAYLEN(case_fold1_16_179) }, + { case_fold1_16_180, __PHYSFS_ARRAYLEN(case_fold1_16_180) }, + { case_fold1_16_181, __PHYSFS_ARRAYLEN(case_fold1_16_181) }, + { case_fold1_16_182, __PHYSFS_ARRAYLEN(case_fold1_16_182) }, + { case_fold1_16_183, __PHYSFS_ARRAYLEN(case_fold1_16_183) }, + { case_fold1_16_184, __PHYSFS_ARRAYLEN(case_fold1_16_184) }, + { case_fold1_16_185, __PHYSFS_ARRAYLEN(case_fold1_16_185) }, + { case_fold1_16_186, __PHYSFS_ARRAYLEN(case_fold1_16_186) }, + { case_fold1_16_187, __PHYSFS_ARRAYLEN(case_fold1_16_187) }, + { case_fold1_16_188, __PHYSFS_ARRAYLEN(case_fold1_16_188) }, + { case_fold1_16_189, __PHYSFS_ARRAYLEN(case_fold1_16_189) }, + { case_fold1_16_190, __PHYSFS_ARRAYLEN(case_fold1_16_190) }, + { case_fold1_16_191, __PHYSFS_ARRAYLEN(case_fold1_16_191) }, + { case_fold1_16_192, __PHYSFS_ARRAYLEN(case_fold1_16_192) }, + { case_fold1_16_193, __PHYSFS_ARRAYLEN(case_fold1_16_193) }, + { case_fold1_16_194, __PHYSFS_ARRAYLEN(case_fold1_16_194) }, + { case_fold1_16_195, __PHYSFS_ARRAYLEN(case_fold1_16_195) }, + { case_fold1_16_196, __PHYSFS_ARRAYLEN(case_fold1_16_196) }, + { case_fold1_16_197, __PHYSFS_ARRAYLEN(case_fold1_16_197) }, + { case_fold1_16_198, __PHYSFS_ARRAYLEN(case_fold1_16_198) }, + { case_fold1_16_199, __PHYSFS_ARRAYLEN(case_fold1_16_199) }, + { case_fold1_16_200, __PHYSFS_ARRAYLEN(case_fold1_16_200) }, + { case_fold1_16_201, __PHYSFS_ARRAYLEN(case_fold1_16_201) }, + { case_fold1_16_202, __PHYSFS_ARRAYLEN(case_fold1_16_202) }, + { case_fold1_16_203, __PHYSFS_ARRAYLEN(case_fold1_16_203) }, + { case_fold1_16_204, __PHYSFS_ARRAYLEN(case_fold1_16_204) }, + { case_fold1_16_205, __PHYSFS_ARRAYLEN(case_fold1_16_205) }, + { case_fold1_16_206, __PHYSFS_ARRAYLEN(case_fold1_16_206) }, + { case_fold1_16_207, __PHYSFS_ARRAYLEN(case_fold1_16_207) }, + { case_fold1_16_208, __PHYSFS_ARRAYLEN(case_fold1_16_208) }, + { case_fold1_16_209, __PHYSFS_ARRAYLEN(case_fold1_16_209) }, + { case_fold1_16_210, __PHYSFS_ARRAYLEN(case_fold1_16_210) }, + { case_fold1_16_211, __PHYSFS_ARRAYLEN(case_fold1_16_211) }, + { case_fold1_16_212, __PHYSFS_ARRAYLEN(case_fold1_16_212) }, + { case_fold1_16_213, __PHYSFS_ARRAYLEN(case_fold1_16_213) }, + { case_fold1_16_214, __PHYSFS_ARRAYLEN(case_fold1_16_214) }, + { case_fold1_16_215, __PHYSFS_ARRAYLEN(case_fold1_16_215) }, + { case_fold1_16_216, __PHYSFS_ARRAYLEN(case_fold1_16_216) }, + { case_fold1_16_217, __PHYSFS_ARRAYLEN(case_fold1_16_217) }, + { case_fold1_16_218, __PHYSFS_ARRAYLEN(case_fold1_16_218) }, + { case_fold1_16_219, __PHYSFS_ARRAYLEN(case_fold1_16_219) }, + { case_fold1_16_220, __PHYSFS_ARRAYLEN(case_fold1_16_220) }, + { case_fold1_16_221, __PHYSFS_ARRAYLEN(case_fold1_16_221) }, + { case_fold1_16_222, __PHYSFS_ARRAYLEN(case_fold1_16_222) }, + { case_fold1_16_223, __PHYSFS_ARRAYLEN(case_fold1_16_223) }, + { case_fold1_16_224, __PHYSFS_ARRAYLEN(case_fold1_16_224) }, + { case_fold1_16_225, __PHYSFS_ARRAYLEN(case_fold1_16_225) }, + { case_fold1_16_226, __PHYSFS_ARRAYLEN(case_fold1_16_226) }, + { case_fold1_16_227, __PHYSFS_ARRAYLEN(case_fold1_16_227) }, + { case_fold1_16_228, __PHYSFS_ARRAYLEN(case_fold1_16_228) }, + { case_fold1_16_229, __PHYSFS_ARRAYLEN(case_fold1_16_229) }, + { case_fold1_16_230, __PHYSFS_ARRAYLEN(case_fold1_16_230) }, + { case_fold1_16_231, __PHYSFS_ARRAYLEN(case_fold1_16_231) }, + { case_fold1_16_232, __PHYSFS_ARRAYLEN(case_fold1_16_232) }, + { case_fold1_16_233, __PHYSFS_ARRAYLEN(case_fold1_16_233) }, + { case_fold1_16_234, __PHYSFS_ARRAYLEN(case_fold1_16_234) }, + { case_fold1_16_235, __PHYSFS_ARRAYLEN(case_fold1_16_235) }, + { case_fold1_16_236, __PHYSFS_ARRAYLEN(case_fold1_16_236) }, + { case_fold1_16_237, __PHYSFS_ARRAYLEN(case_fold1_16_237) }, + { case_fold1_16_238, __PHYSFS_ARRAYLEN(case_fold1_16_238) }, + { case_fold1_16_239, __PHYSFS_ARRAYLEN(case_fold1_16_239) }, + { case_fold1_16_240, __PHYSFS_ARRAYLEN(case_fold1_16_240) }, + { case_fold1_16_241, __PHYSFS_ARRAYLEN(case_fold1_16_241) }, + { case_fold1_16_242, __PHYSFS_ARRAYLEN(case_fold1_16_242) }, + { case_fold1_16_243, __PHYSFS_ARRAYLEN(case_fold1_16_243) }, + { case_fold1_16_244, __PHYSFS_ARRAYLEN(case_fold1_16_244) }, + { case_fold1_16_245, __PHYSFS_ARRAYLEN(case_fold1_16_245) }, + { case_fold1_16_246, __PHYSFS_ARRAYLEN(case_fold1_16_246) }, + { case_fold1_16_247, __PHYSFS_ARRAYLEN(case_fold1_16_247) }, + { case_fold1_16_248, __PHYSFS_ARRAYLEN(case_fold1_16_248) }, + { case_fold1_16_249, __PHYSFS_ARRAYLEN(case_fold1_16_249) }, + { case_fold1_16_250, __PHYSFS_ARRAYLEN(case_fold1_16_250) }, + { case_fold1_16_251, __PHYSFS_ARRAYLEN(case_fold1_16_251) }, + { case_fold1_16_252, __PHYSFS_ARRAYLEN(case_fold1_16_252) }, + { case_fold1_16_253, __PHYSFS_ARRAYLEN(case_fold1_16_253) }, + { case_fold1_16_254, __PHYSFS_ARRAYLEN(case_fold1_16_254) }, + { case_fold1_16_255, __PHYSFS_ARRAYLEN(case_fold1_16_255) }, +}; + +static const CaseFoldHashBucket1_32 case_fold_hash1_32[] = { + { case_fold1_32_000, __PHYSFS_ARRAYLEN(case_fold1_32_000) }, + { case_fold1_32_001, __PHYSFS_ARRAYLEN(case_fold1_32_001) }, + { case_fold1_32_002, __PHYSFS_ARRAYLEN(case_fold1_32_002) }, + { case_fold1_32_003, __PHYSFS_ARRAYLEN(case_fold1_32_003) }, + { case_fold1_32_004, __PHYSFS_ARRAYLEN(case_fold1_32_004) }, + { case_fold1_32_005, __PHYSFS_ARRAYLEN(case_fold1_32_005) }, + { case_fold1_32_006, __PHYSFS_ARRAYLEN(case_fold1_32_006) }, + { case_fold1_32_007, __PHYSFS_ARRAYLEN(case_fold1_32_007) }, + { case_fold1_32_008, __PHYSFS_ARRAYLEN(case_fold1_32_008) }, + { case_fold1_32_009, __PHYSFS_ARRAYLEN(case_fold1_32_009) }, + { case_fold1_32_010, __PHYSFS_ARRAYLEN(case_fold1_32_010) }, + { case_fold1_32_011, __PHYSFS_ARRAYLEN(case_fold1_32_011) }, + { case_fold1_32_012, __PHYSFS_ARRAYLEN(case_fold1_32_012) }, + { case_fold1_32_013, __PHYSFS_ARRAYLEN(case_fold1_32_013) }, + { case_fold1_32_014, __PHYSFS_ARRAYLEN(case_fold1_32_014) }, + { case_fold1_32_015, __PHYSFS_ARRAYLEN(case_fold1_32_015) }, +}; + +static const CaseFoldHashBucket2_16 case_fold_hash2_16[] = { + { case_fold2_16_000, __PHYSFS_ARRAYLEN(case_fold2_16_000) }, + { case_fold2_16_001, __PHYSFS_ARRAYLEN(case_fold2_16_001) }, + { case_fold2_16_002, __PHYSFS_ARRAYLEN(case_fold2_16_002) }, + { case_fold2_16_003, __PHYSFS_ARRAYLEN(case_fold2_16_003) }, + { case_fold2_16_004, __PHYSFS_ARRAYLEN(case_fold2_16_004) }, + { case_fold2_16_005, __PHYSFS_ARRAYLEN(case_fold2_16_005) }, + { case_fold2_16_006, __PHYSFS_ARRAYLEN(case_fold2_16_006) }, + { case_fold2_16_007, __PHYSFS_ARRAYLEN(case_fold2_16_007) }, + { case_fold2_16_008, __PHYSFS_ARRAYLEN(case_fold2_16_008) }, + { case_fold2_16_009, __PHYSFS_ARRAYLEN(case_fold2_16_009) }, + { case_fold2_16_010, __PHYSFS_ARRAYLEN(case_fold2_16_010) }, + { case_fold2_16_011, __PHYSFS_ARRAYLEN(case_fold2_16_011) }, + { case_fold2_16_012, __PHYSFS_ARRAYLEN(case_fold2_16_012) }, + { case_fold2_16_013, __PHYSFS_ARRAYLEN(case_fold2_16_013) }, + { case_fold2_16_014, __PHYSFS_ARRAYLEN(case_fold2_16_014) }, + { case_fold2_16_015, __PHYSFS_ARRAYLEN(case_fold2_16_015) }, +}; + +static const CaseFoldHashBucket3_16 case_fold_hash3_16[] = { + { case_fold3_16_000, __PHYSFS_ARRAYLEN(case_fold3_16_000) }, + { case_fold3_16_001, __PHYSFS_ARRAYLEN(case_fold3_16_001) }, + { NULL, 0 }, + { case_fold3_16_003, __PHYSFS_ARRAYLEN(case_fold3_16_003) }, +}; + + +#endif /* _INCLUDE_PHYSFS_CASEFOLDING_H_ */ + +/* end of physfs_casefolding.h ... */ + diff --git a/Source/3rdParty/physfs/physfs_internal.h b/Source/3rdParty/physfs/physfs_internal.h new file mode 100644 index 0000000..003ecc5 --- /dev/null +++ b/Source/3rdParty/physfs/physfs_internal.h @@ -0,0 +1,720 @@ +/* + * Internal function/structure declaration. Do NOT include in your + * application. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#ifndef _INCLUDE_PHYSFS_INTERNAL_H_ +#define _INCLUDE_PHYSFS_INTERNAL_H_ + +#ifndef __PHYSICSFS_INTERNAL__ +#error Do not include this header from your applications. +#endif + +/* Turn off MSVC warnings that are aggressively anti-portability. */ +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS 1 +#endif + +#include "physfs.h" + +/* The holy trinity. */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "physfs_platforms.h" + +#include <assert.h> + +#define __PHYSFS_COMPILE_TIME_ASSERT(name, x) \ + typedef int __PHYSFS_compile_time_assert_##name[(x) * 2 - 1] + +/* !!! FIXME: remove this when revamping stack allocation code... */ +#if defined(_MSC_VER) || defined(__MINGW32__) || defined(__WATCOMC__) +#include <malloc.h> +#endif + +#ifdef PHYSFS_PLATFORM_SOLARIS +#include <alloca.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __GNUC__ +#define PHYSFS_MINIMUM_GCC_VERSION(major, minor) \ + ( ((__GNUC__ << 16) + __GNUC_MINOR__) >= (((major) << 16) + (minor)) ) +#else +#define PHYSFS_MINIMUM_GCC_VERSION(major, minor) (0) +#endif + +#ifdef __cplusplus + /* C++ always has a real inline keyword. */ +#elif (defined macintosh) && !(defined __MWERKS__) +# define inline +#elif (defined _MSC_VER) +# define inline __inline +#endif + +#if defined(PHYSFS_PLATFORM_LINUX) && !defined(_FILE_OFFSET_BITS) +#define _FILE_OFFSET_BITS 64 +#endif + +/* All public APIs need to be in physfs.h with a PHYSFS_DECL. + All file-private symbols need to be marked "static". + Everything shared between PhysicsFS sources needs to be in this + file between the visibility pragma blocks. */ +#if PHYSFS_MINIMUM_GCC_VERSION(4,0) || defined(__clang__) +#define PHYSFS_HAVE_PRAGMA_VISIBILITY 1 +#endif + +#if PHYSFS_HAVE_PRAGMA_VISIBILITY +#pragma GCC visibility push(hidden) +#endif + +/* These are the build-in archivers. We list them all as "extern" here without + #ifdefs to keep it tidy, but obviously you need to make sure these are + wrapped in PHYSFS_SUPPORTS_* checks before actually referencing them. */ +extern const PHYSFS_Archiver __PHYSFS_Archiver_DIR; +extern const PHYSFS_Archiver __PHYSFS_Archiver_ZIP; +extern const PHYSFS_Archiver __PHYSFS_Archiver_7Z; +extern const PHYSFS_Archiver __PHYSFS_Archiver_GRP; +extern const PHYSFS_Archiver __PHYSFS_Archiver_QPAK; +extern const PHYSFS_Archiver __PHYSFS_Archiver_HOG; +extern const PHYSFS_Archiver __PHYSFS_Archiver_MVL; +extern const PHYSFS_Archiver __PHYSFS_Archiver_WAD; +extern const PHYSFS_Archiver __PHYSFS_Archiver_SLB; +extern const PHYSFS_Archiver __PHYSFS_Archiver_ISO9660; +extern const PHYSFS_Archiver __PHYSFS_Archiver_VDF; + +/* a real C99-compliant snprintf() is in Visual Studio 2015, + but just use this everywhere for binary compatibility. */ +#if defined(_MSC_VER) +int __PHYSFS_msvc_vsnprintf(char *outBuf, size_t size, const char *format, va_list ap); +int __PHYSFS_msvc_snprintf(char *outBuf, size_t size, const char *format, ...); +#define vsnprintf __PHYSFS_msvc_vsnprintf +#define snprintf __PHYSFS_msvc_snprintf +#endif + +/* Some simple wrappers around WinRT C++ interfaces we can call from C. */ +#ifdef PHYSFS_PLATFORM_WINRT +const void *__PHYSFS_winrtCalcBaseDir(void); +const void *__PHYSFS_winrtCalcPrefDir(void); +#endif + +/* atomic operations. */ +#if defined(_MSC_VER) && (_MSC_VER >= 1500) +#include <intrin.h> +__PHYSFS_COMPILE_TIME_ASSERT(LongEqualsInt, sizeof (int) == sizeof (long)); +#define __PHYSFS_ATOMIC_INCR(ptrval) _InterlockedIncrement((long*)(ptrval)) +#define __PHYSFS_ATOMIC_DECR(ptrval) _InterlockedDecrement((long*)(ptrval)) +#elif defined(__clang__) || (defined(__GNUC__) && (((__GNUC__ * 10000) + (__GNUC_MINOR__ * 100)) >= 40100)) +#define __PHYSFS_ATOMIC_INCR(ptrval) __sync_fetch_and_add(ptrval, 1) +#define __PHYSFS_ATOMIC_DECR(ptrval) __sync_fetch_and_add(ptrval, -1) +#else +#define PHYSFS_NEED_ATOMIC_OP_FALLBACK 1 +int __PHYSFS_ATOMIC_INCR(int *ptrval); +int __PHYSFS_ATOMIC_DECR(int *ptrval); +#endif + + +/* + * Interface for small allocations. If you need a little scratch space for + * a throwaway buffer or string, use this. It will make small allocations + * on the stack if possible, and use allocator.Malloc() if they are too + * large. This helps reduce malloc pressure. + * There are some rules, though: + * NEVER return a pointer from this, as stack-allocated buffers go away + * when your function returns. + * NEVER allocate in a loop, as stack-allocated pointers will pile up. Call + * a function that uses smallAlloc from your loop, so the allocation can + * free each time. + * NEVER call smallAlloc with any complex expression (it's a macro that WILL + * have side effects...it references the argument multiple times). Use a + * variable or a literal. + * NEVER free a pointer from this with anything but smallFree. It will not + * be a valid pointer to the allocator, regardless of where the memory came + * from. + * NEVER realloc a pointer from this. + * NEVER forget to use smallFree: it may not be a pointer from the stack. + * NEVER forget to check for NULL...allocation can fail here, of course! + */ +#define __PHYSFS_SMALLALLOCTHRESHOLD 256 +void *__PHYSFS_initSmallAlloc(void *ptr, const size_t len); + +#define __PHYSFS_smallAlloc(bytes) ( \ + __PHYSFS_initSmallAlloc( \ + (((bytes) < __PHYSFS_SMALLALLOCTHRESHOLD) ? \ + alloca((size_t)((bytes)+sizeof(void*))) : NULL), (bytes)) \ +) + +void __PHYSFS_smallFree(void *ptr); + + +/* Use the allocation hooks. */ +#define malloc(x) Do not use malloc() directly. +#define realloc(x, y) Do not use realloc() directly. +#define free(x) Do not use free() directly. +/* !!! FIXME: add alloca check here. */ + +#ifndef PHYSFS_SUPPORTS_ZIP +#define PHYSFS_SUPPORTS_ZIP 1 +#endif +#ifndef PHYSFS_SUPPORTS_7Z +#define PHYSFS_SUPPORTS_7Z 1 +#endif +#ifndef PHYSFS_SUPPORTS_GRP +#define PHYSFS_SUPPORTS_GRP 1 +#endif +#ifndef PHYSFS_SUPPORTS_HOG +#define PHYSFS_SUPPORTS_HOG 1 +#endif +#ifndef PHYSFS_SUPPORTS_MVL +#define PHYSFS_SUPPORTS_MVL 1 +#endif +#ifndef PHYSFS_SUPPORTS_WAD +#define PHYSFS_SUPPORTS_WAD 1 +#endif +#ifndef PHYSFS_SUPPORTS_QPAK +#define PHYSFS_SUPPORTS_QPAK 1 +#endif +#ifndef PHYSFS_SUPPORTS_SLB +#define PHYSFS_SUPPORTS_SLB 1 +#endif +#ifndef PHYSFS_SUPPORTS_ISO9660 +#define PHYSFS_SUPPORTS_ISO9660 1 +#endif +#ifndef PHYSFS_SUPPORTS_VDF +#define PHYSFS_SUPPORTS_VDF 1 +#endif + +#if PHYSFS_SUPPORTS_7Z +/* 7zip support needs a global init function called at startup (no deinit). */ +extern void SZIP_global_init(void); +#endif + +/* The latest supported PHYSFS_Io::version value. */ +#define CURRENT_PHYSFS_IO_API_VERSION 0 + +/* The latest supported PHYSFS_Archiver::version value. */ +#define CURRENT_PHYSFS_ARCHIVER_API_VERSION 0 + +/* This byteorder stuff was lifted from SDL. https://www.libsdl.org/ */ +#define PHYSFS_LIL_ENDIAN 1234 +#define PHYSFS_BIG_ENDIAN 4321 + +#ifdef __linux__ +#include <endian.h> +#define PHYSFS_BYTEORDER __BYTE_ORDER +#else /* __linux__ */ +#if defined(__hppa__) || \ + defined(__m68k__) || defined(mc68000) || defined(_M_M68K) || \ + (defined(__MIPS__) && defined(__MISPEB__)) || \ + defined(__ppc__) || defined(__POWERPC__) || defined(_M_PPC) || \ + defined(__sparc__) +#define PHYSFS_BYTEORDER PHYSFS_BIG_ENDIAN +#else +#define PHYSFS_BYTEORDER PHYSFS_LIL_ENDIAN +#endif +#endif /* __linux__ */ + + +/* + * When sorting the entries in an archive, we use a modified QuickSort. + * When there are less then PHYSFS_QUICKSORT_THRESHOLD entries left to sort, + * we switch over to a BubbleSort for the remainder. Tweak to taste. + * + * You can override this setting by defining PHYSFS_QUICKSORT_THRESHOLD + * before #including "physfs_internal.h". + */ +#ifndef PHYSFS_QUICKSORT_THRESHOLD +#define PHYSFS_QUICKSORT_THRESHOLD 4 +#endif + +/* + * Sort an array (or whatever) of (max) elements. This uses a mixture of + * a QuickSort and BubbleSort internally. + * (cmpfn) is used to determine ordering, and (swapfn) does the actual + * swapping of elements in the list. + */ +void __PHYSFS_sort(void *entries, size_t max, + int (*cmpfn)(void *, size_t, size_t), + void (*swapfn)(void *, size_t, size_t)); + +/* These get used all over for lessening code clutter. */ +/* "ERRPASS" means "something else just set the error state for us" and is + just to make it clear where the responsibility for the error state lays. */ +#define BAIL(e, r) do { if (e) PHYSFS_setErrorCode(e); return r; } while (0) +#define BAIL_ERRPASS(r) do { return r; } while (0) +#define BAIL_IF(c, e, r) do { if (c) { if (e) PHYSFS_setErrorCode(e); return r; } } while (0) +#define BAIL_IF_ERRPASS(c, r) do { if (c) { return r; } } while (0) +#define BAIL_MUTEX(e, m, r) do { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); return r; } while (0) +#define BAIL_MUTEX_ERRPASS(m, r) do { __PHYSFS_platformReleaseMutex(m); return r; } while (0) +#define BAIL_IF_MUTEX(c, e, m, r) do { if (c) { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); return r; } } while (0) +#define BAIL_IF_MUTEX_ERRPASS(c, m, r) do { if (c) { __PHYSFS_platformReleaseMutex(m); return r; } } while (0) +#define GOTO(e, g) do { if (e) PHYSFS_setErrorCode(e); goto g; } while (0) +#define GOTO_ERRPASS(g) do { goto g; } while (0) +#define GOTO_IF(c, e, g) do { if (c) { if (e) PHYSFS_setErrorCode(e); goto g; } } while (0) +#define GOTO_IF_ERRPASS(c, g) do { if (c) { goto g; } } while (0) +#define GOTO_MUTEX(e, m, g) do { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); goto g; } while (0) +#define GOTO_MUTEX_ERRPASS(m, g) do { __PHYSFS_platformReleaseMutex(m); goto g; } while (0) +#define GOTO_IF_MUTEX(c, e, m, g) do { if (c) { if (e) PHYSFS_setErrorCode(e); __PHYSFS_platformReleaseMutex(m); goto g; } } while (0) +#define GOTO_IF_MUTEX_ERRPASS(c, m, g) do { if (c) { __PHYSFS_platformReleaseMutex(m); goto g; } } while (0) + +#define __PHYSFS_ARRAYLEN(x) ( (sizeof (x)) / (sizeof (x[0])) ) + +#ifdef PHYSFS_NO_64BIT_SUPPORT +#define __PHYSFS_SI64(x) ((PHYSFS_sint64) (x)) +#define __PHYSFS_UI64(x) ((PHYSFS_uint64) (x)) +#elif (defined __GNUC__) +#define __PHYSFS_SI64(x) x##LL +#define __PHYSFS_UI64(x) x##ULL +#elif (defined _MSC_VER) +#define __PHYSFS_SI64(x) x##i64 +#define __PHYSFS_UI64(x) x##ui64 +#else +#define __PHYSFS_SI64(x) ((PHYSFS_sint64) (x)) +#define __PHYSFS_UI64(x) ((PHYSFS_uint64) (x)) +#endif + + +/* + * Check if a ui64 will fit in the platform's address space. + * The initial sizeof check will optimize this macro out entirely on + * 64-bit (and larger?!) platforms, and the other condition will + * return zero or non-zero if the variable will fit in the platform's + * size_t, suitable to pass to malloc. This is kinda messy, but effective. + */ +#define __PHYSFS_ui64FitsAddressSpace(s) ( \ + (sizeof (PHYSFS_uint64) <= sizeof (size_t)) || \ + ((s) < (__PHYSFS_UI64(0xFFFFFFFFFFFFFFFF) >> (64-(sizeof(size_t)*8)))) \ +) + +/* + * Like strdup(), but uses the current PhysicsFS allocator. + */ +char *__PHYSFS_strdup(const char *str); + +/* + * Give a hash value for a C string (uses djb's xor hashing algorithm). + */ +PHYSFS_uint32 __PHYSFS_hashString(const char *str, size_t len); + + +/* + * The current allocator. Not valid before PHYSFS_init is called! + */ +extern PHYSFS_Allocator __PHYSFS_AllocatorHooks; + +/* convenience macro to make this less cumbersome internally... */ +#define allocator __PHYSFS_AllocatorHooks + +/* + * Create a PHYSFS_Io for a file in the physical filesystem. + * This path is in platform-dependent notation. (mode) must be 'r', 'w', or + * 'a' for Read, Write, or Append. + */ +PHYSFS_Io *__PHYSFS_createNativeIo(const char *path, const int mode); + +/* + * Create a PHYSFS_Io for a buffer of memory (READ-ONLY). If you already + * have one of these, just use its duplicate() method, and it'll increment + * its refcount without allocating a copy of the buffer. + */ +PHYSFS_Io *__PHYSFS_createMemoryIo(const void *buf, PHYSFS_uint64 len, + void (*destruct)(void *)); + + +/* + * Read (len) bytes from (io) into (buf). Returns non-zero on success, + * zero on i/o error. Literally: "return (io->read(io, buf, len) == len);" + */ +int __PHYSFS_readAll(PHYSFS_Io *io, void *buf, const size_t len); + + +/* These are shared between some archivers. */ + +void UNPK_abandonArchive(void *opaque); +void UNPK_closeArchive(void *opaque); +void *UNPK_openArchive(PHYSFS_Io *io); +void *UNPK_addEntry(void *opaque, char *name, const int isdir, + const PHYSFS_sint64 ctime, const PHYSFS_sint64 mtime, + const PHYSFS_uint64 pos, const PHYSFS_uint64 len); +PHYSFS_Io *UNPK_openRead(void *opaque, const char *name); +PHYSFS_Io *UNPK_openWrite(void *opaque, const char *name); +PHYSFS_Io *UNPK_openAppend(void *opaque, const char *name); +int UNPK_remove(void *opaque, const char *name); +int UNPK_mkdir(void *opaque, const char *name); +int UNPK_stat(void *opaque, const char *fn, PHYSFS_Stat *st); +#define UNPK_enumerate __PHYSFS_DirTreeEnumerate + + + +/* Optional API many archivers use this to manage their directory tree. */ +/* !!! FIXME: document this better. */ + +typedef struct __PHYSFS_DirTreeEntry +{ + char *name; /* Full path in archive. */ + struct __PHYSFS_DirTreeEntry *hashnext; /* next item in hash bucket. */ + struct __PHYSFS_DirTreeEntry *children; /* linked list of kids, if dir. */ + struct __PHYSFS_DirTreeEntry *sibling; /* next item in same dir. */ + int isdir; +} __PHYSFS_DirTreeEntry; + +typedef struct __PHYSFS_DirTree +{ + __PHYSFS_DirTreeEntry *root; /* root of directory tree. */ + __PHYSFS_DirTreeEntry **hash; /* all entries hashed for fast lookup. */ + size_t hashBuckets; /* number of buckets in hash. */ + size_t entrylen; /* size in bytes of entries (including subclass). */ +} __PHYSFS_DirTree; + + +int __PHYSFS_DirTreeInit(__PHYSFS_DirTree *dt, const size_t entrylen); +void *__PHYSFS_DirTreeAdd(__PHYSFS_DirTree *dt, char *name, const int isdir); +void *__PHYSFS_DirTreeFind(__PHYSFS_DirTree *dt, const char *path); +PHYSFS_EnumerateCallbackResult __PHYSFS_DirTreeEnumerate(void *opaque, + const char *dname, PHYSFS_EnumerateCallback cb, + const char *origdir, void *callbackdata); +void __PHYSFS_DirTreeDeinit(__PHYSFS_DirTree *dt); + + + +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ +/*------------ ----------------*/ +/*------------ You MUST implement the following functions ----------------*/ +/*------------ if porting to a new platform. ----------------*/ +/*------------ (see platform/unix.c for an example) ----------------*/ +/*------------ ----------------*/ +/*--------------------------------------------------------------------------*/ +/*--------------------------------------------------------------------------*/ + + +/* + * The dir separator; '/' on unix, '\\' on win32, ":" on MacOS, etc... + * Obviously, this isn't a function. If you need more than one char for this, + * you'll need to pull some old pieces of PhysicsFS out of revision control. + */ +#if defined(PHYSFS_PLATFORM_WINDOWS) || defined(PHYSFS_PLATFORM_OS2) +#define __PHYSFS_platformDirSeparator '\\' +#else +#define __PHYSFS_STANDARD_DIRSEP 1 +#define __PHYSFS_platformDirSeparator '/' +#endif + +/* + * Initialize the platform. This is called when PHYSFS_init() is called from + * the application. + * + * Return zero if there was a catastrophic failure (which prevents you from + * functioning at all), and non-zero otherwise. + */ +int __PHYSFS_platformInit(void); + + +/* + * Deinitialize the platform. This is called when PHYSFS_deinit() is called + * from the application. You can use this to clean up anything you've + * allocated in your platform driver. + */ +void __PHYSFS_platformDeinit(void); + + +/* + * Open a file for reading. (filename) is in platform-dependent notation. The + * file pointer should be positioned on the first byte of the file. + * + * The return value will be some platform-specific datatype that is opaque to + * the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32. + * + * The same file can be opened for read multiple times, and each should have + * a unique file handle; this is frequently employed to prevent race + * conditions in the archivers. + * + * Call PHYSFS_setErrorCode() and return (NULL) if the file can't be opened. + */ +void *__PHYSFS_platformOpenRead(const char *filename); + + +/* + * Open a file for writing. (filename) is in platform-dependent notation. If + * the file exists, it should be truncated to zero bytes, and if it doesn't + * exist, it should be created as a zero-byte file. The file pointer should + * be positioned on the first byte of the file. + * + * The return value will be some platform-specific datatype that is opaque to + * the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32, + * etc. + * + * Opening a file for write multiple times has undefined results. + * + * Call PHYSFS_setErrorCode() and return (NULL) if the file can't be opened. + */ +void *__PHYSFS_platformOpenWrite(const char *filename); + + +/* + * Open a file for appending. (filename) is in platform-dependent notation. If + * the file exists, the file pointer should be place just past the end of the + * file, so that the first write will be one byte after the current end of + * the file. If the file doesn't exist, it should be created as a zero-byte + * file. The file pointer should be positioned on the first byte of the file. + * + * The return value will be some platform-specific datatype that is opaque to + * the caller; it could be a (FILE *) under Unix, or a (HANDLE *) under win32, + * etc. + * + * Opening a file for append multiple times has undefined results. + * + * Call PHYSFS_setErrorCode() and return (NULL) if the file can't be opened. + */ +void *__PHYSFS_platformOpenAppend(const char *filename); + +/* + * Read more data from a platform-specific file handle. (opaque) should be + * cast to whatever data type your platform uses. Read a maximum of (len) + * 8-bit bytes to the area pointed to by (buf). If there isn't enough data + * available, return the number of bytes read, and position the file pointer + * immediately after those bytes. + * On success, return (len) and position the file pointer immediately past + * the end of the last read byte. Return (-1) if there is a catastrophic + * error, and call PHYSFS_setErrorCode() to describe the problem; the file + * pointer should not move in such a case. A partial read is success; only + * return (-1) on total failure; presumably, the next read call after a + * partial read will fail as such. + */ +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len); + +/* + * Write more data to a platform-specific file handle. (opaque) should be + * cast to whatever data type your platform uses. Write a maximum of (len) + * 8-bit bytes from the area pointed to by (buffer). If there is a problem, + * return the number of bytes written, and position the file pointer + * immediately after those bytes. Return (-1) if there is a catastrophic + * error, and call PHYSFS_setErrorCode() to describe the problem; the file + * pointer should not move in such a case. A partial write is success; only + * return (-1) on total failure; presumably, the next write call after a + * partial write will fail as such. + */ +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint64 len); + +/* + * Set the file pointer to a new position. (opaque) should be cast to + * whatever data type your platform uses. (pos) specifies the number + * of 8-bit bytes to seek to from the start of the file. Seeking past the + * end of the file is an error condition, and you should check for it. + * + * Not all file types can seek; this is to be expected by the caller. + * + * On error, call PHYSFS_setErrorCode() and return zero. On success, return + * a non-zero value. + */ +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos); + + +/* + * Get the file pointer's position, in an 8-bit byte offset from the start of + * the file. (opaque) should be cast to whatever data type your platform + * uses. + * + * Not all file types can "tell"; this is to be expected by the caller. + * + * On error, call PHYSFS_setErrorCode() and return -1. On success, return >= 0. + */ +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque); + + +/* + * Determine the current size of a file, in 8-bit bytes, from an open file. + * + * The caller expects that this information may not be available for all + * file types on all platforms. + * + * Return -1 if you can't do it, and call PHYSFS_setErrorCode(). Otherwise, + * return the file length in 8-bit bytes. + */ +PHYSFS_sint64 __PHYSFS_platformFileLength(void *handle); + + +/* + * Read filesystem metadata for a specific path. + * + * This needs to fill in all the fields of (stat). For fields that might not + * mean anything on a platform (access time, perhaps), choose a reasonable + * default. if (follow), we want to follow symlinks and stat what they + * link to and not the link itself. + * + * Return zero on failure, non-zero on success. + */ +int __PHYSFS_platformStat(const char *fn, PHYSFS_Stat *stat, const int follow); + +/* + * Flush any pending writes to disk. (opaque) should be cast to whatever data + * type your platform uses. Be sure to check for errors; the caller expects + * that this function can fail if there was a flushing error, etc. + * + * Return zero on failure, non-zero on success. + */ +int __PHYSFS_platformFlush(void *opaque); + +/* + * Close file and deallocate resources. (opaque) should be cast to whatever + * data type your platform uses. This should close the file in any scenario: + * flushing is a separate function call, and this function should never fail. + * + * You should clean up all resources associated with (opaque); the pointer + * will be considered invalid after this call. + */ +void __PHYSFS_platformClose(void *opaque); + +/* + * Platform implementation of PHYSFS_getCdRomDirsCallback()... + * CD directories are discovered and reported to the callback one at a time. + * Pointers passed to the callback are assumed to be invalid to the + * application after the callback returns, so you can free them or whatever. + * Callback does not assume results will be sorted in any meaningful way. + */ +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data); + +/* + * Calculate the base dir, if your platform needs special consideration. + * Just return NULL if the standard routines will suffice. (see + * calculateBaseDir() in physfs.c ...) + * Your string must end with a dir separator if you don't return NULL. + * Caller will allocator.Free() the retval if it's not NULL. + */ +char *__PHYSFS_platformCalcBaseDir(const char *argv0); + +/* + * Get the platform-specific user dir. + * As of PhysicsFS 2.1, returning NULL means fatal error. + * Your string must end with a dir separator if you don't return NULL. + * Caller will allocator.Free() the retval if it's not NULL. + */ +char *__PHYSFS_platformCalcUserDir(void); + + +/* This is the cached version from PHYSFS_init(). This is a fast call. */ +const char *__PHYSFS_getUserDir(void); /* not deprecated internal version. */ + + +/* + * Get the platform-specific pref dir. + * Returning NULL means fatal error. + * Your string must end with a dir separator if you don't return NULL. + * Caller will allocator.Free() the retval if it's not NULL. + * Caller will make missing directories if necessary; this just reports + * the final path. + */ +char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app); + + +/* + * Return a pointer that uniquely identifies the current thread. + * On a platform without threading, (0x1) will suffice. These numbers are + * arbitrary; the only requirement is that no two threads have the same + * pointer. + */ +void *__PHYSFS_platformGetThreadID(void); + + +/* + * Enumerate a directory of files. This follows the rules for the + * PHYSFS_Archiver::enumerate() method, except that the (dirName) that is + * passed to this function is converted to platform-DEPENDENT notation by + * the caller. The PHYSFS_Archiver version uses platform-independent + * notation. Note that ".", "..", and other meta-entries should always + * be ignored. + */ +PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname, + PHYSFS_EnumerateCallback callback, + const char *origdir, void *callbackdata); + +/* + * Make a directory in the actual filesystem. (path) is specified in + * platform-dependent notation. On error, return zero and set the error + * message. Return non-zero on success. + */ +int __PHYSFS_platformMkDir(const char *path); + + +/* + * Remove a file or directory entry in the actual filesystem. (path) is + * specified in platform-dependent notation. Note that this deletes files + * _and_ directories, so you might need to do some determination. + * Non-empty directories should report an error and not delete themselves + * or their contents. + * + * Deleting a symlink should remove the link, not what it points to. + * + * On error, return zero and set the error message. Return non-zero on success. + */ +int __PHYSFS_platformDelete(const char *path); + + +/* + * Create a platform-specific mutex. This can be whatever datatype your + * platform uses for mutexes, but it is cast to a (void *) for abstractness. + * + * Return (NULL) if you couldn't create one. Systems without threads can + * return any arbitrary non-NULL value. + */ +void *__PHYSFS_platformCreateMutex(void); + +/* + * Destroy a platform-specific mutex, and clean up any resources associated + * with it. (mutex) is a value previously returned by + * __PHYSFS_platformCreateMutex(). This can be a no-op on single-threaded + * platforms. + */ +void __PHYSFS_platformDestroyMutex(void *mutex); + +/* + * Grab possession of a platform-specific mutex. Mutexes should be recursive; + * that is, the same thread should be able to call this function multiple + * times in a row without causing a deadlock. This function should block + * until a thread can gain possession of the mutex. + * + * Return non-zero if the mutex was grabbed, zero if there was an + * unrecoverable problem grabbing it (this should not be a matter of + * timing out! We're talking major system errors; block until the mutex + * is available otherwise.) + * + * _DO NOT_ call PHYSFS_setErrorCode() in here! Since setErrorCode calls this + * function, you'll cause an infinite recursion. This means you can't + * use the BAIL_*MACRO* macros, either. + */ +int __PHYSFS_platformGrabMutex(void *mutex); + +/* + * Relinquish possession of the mutex when this method has been called + * once for each time that platformGrabMutex was called. Once possession has + * been released, the next thread in line to grab the mutex (if any) may + * proceed. + * + * _DO NOT_ call PHYSFS_setErrorCode() in here! Since setErrorCode calls this + * function, you'll cause an infinite recursion. This means you can't + * use the BAIL_*MACRO* macros, either. + */ +void __PHYSFS_platformReleaseMutex(void *mutex); + +#if PHYSFS_HAVE_PRAGMA_VISIBILITY +#pragma GCC visibility pop +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/* end of physfs_internal.h ... */ + diff --git a/Source/3rdParty/physfs/physfs_lzmasdk.h b/Source/3rdParty/physfs/physfs_lzmasdk.h new file mode 100644 index 0000000..c86972e --- /dev/null +++ b/Source/3rdParty/physfs/physfs_lzmasdk.h @@ -0,0 +1,6028 @@ +#ifndef _INCLUDE_PHYSFS_LZMASDK_H_ +#define _INCLUDE_PHYSFS_LZMASDK_H_ + +/* This is just a bunch of the LZMA SDK mushed together into one header. +This code is all public domain, and mostly (if not entirely) written by +Igor Pavlov. http://www.7-zip.org/sdk.html +--ryan. */ + + + +/* 7zTypes.h -- Basic types +2013-11-12 : Igor Pavlov : Public domain */ + +#ifndef __7Z_TYPES_H +#define __7Z_TYPES_H + +#ifdef _WIN32 +/* #include <windows.h> */ +#endif + +#include <stddef.h> + +#ifndef EXTERN_C_BEGIN +#ifdef __cplusplus +#define EXTERN_C_BEGIN extern "C" { +#define EXTERN_C_END } +#else +#define EXTERN_C_BEGIN +#define EXTERN_C_END +#endif +#endif + +EXTERN_C_BEGIN + +#define SZ_OK 0 + +#define SZ_ERROR_DATA 1 +#define SZ_ERROR_MEM 2 +#define SZ_ERROR_CRC 3 +#define SZ_ERROR_UNSUPPORTED 4 +#define SZ_ERROR_PARAM 5 +#define SZ_ERROR_INPUT_EOF 6 +#define SZ_ERROR_OUTPUT_EOF 7 +#define SZ_ERROR_READ 8 +#define SZ_ERROR_WRITE 9 +#define SZ_ERROR_PROGRESS 10 +#define SZ_ERROR_FAIL 11 +#define SZ_ERROR_THREAD 12 + +#define SZ_ERROR_ARCHIVE 16 +#define SZ_ERROR_NO_ARCHIVE 17 + +typedef int SRes; + +#ifdef _WIN32 +/* typedef DWORD WRes; */ +typedef unsigned WRes; +#else +typedef int WRes; +#endif + +#ifndef RINOK +#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } +#endif + +typedef unsigned char Byte; +typedef short Int16; +typedef unsigned short UInt16; + +#ifdef _LZMA_UINT32_IS_ULONG +typedef long Int32; +typedef unsigned long UInt32; +#else +typedef int Int32; +typedef unsigned int UInt32; +#endif + +#ifdef _SZ_NO_INT_64 + +/* define _SZ_NO_INT_64, if your compiler doesn't support 64-bit integers. + NOTES: Some code will work incorrectly in that case! */ + +typedef long Int64; +typedef unsigned long UInt64; + +#else + +#if defined(_MSC_VER) || defined(__BORLANDC__) || defined(__WATCOMC__) +typedef __int64 Int64; +typedef unsigned __int64 UInt64; +#define UINT64_CONST(n) n ## ui64 +#else +typedef long long int Int64; +typedef unsigned long long int UInt64; +#define UINT64_CONST(n) n ## ULL +#endif + +#endif + +#ifdef _LZMA_NO_SYSTEM_SIZE_T +typedef UInt32 SizeT; +#else +typedef size_t SizeT; +#endif + +typedef int Bool; +#define True 1 +#define False 0 + + +#ifdef _WIN32 +#define MY_STD_CALL __stdcall +#else +#define MY_STD_CALL +#endif + +#ifdef _MSC_VER + +#if _MSC_VER >= 1300 +#define MY_NO_INLINE __declspec(noinline) +#else +#define MY_NO_INLINE +#endif + +#define MY_CDECL __cdecl +#define MY_FAST_CALL __fastcall + +#else + +#define MY_NO_INLINE +#define MY_CDECL +#define MY_FAST_CALL + +#endif + + +/* The following interfaces use first parameter as pointer to structure */ + +typedef struct +{ + Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ +} IByteIn; + +typedef struct +{ + void (*Write)(void *p, Byte b); +} IByteOut; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) < input(*size)) is allowed */ +} ISeqInStream; + +typedef struct +{ + size_t (*Write)(void *p, const void *buf, size_t size); + /* Returns: result - the number of actually written bytes. + (result < size) means error */ +} ISeqOutStream; + +typedef enum +{ + SZ_SEEK_SET = 0, + SZ_SEEK_CUR = 1, + SZ_SEEK_END = 2 +} ESzSeek; + +typedef struct +{ + SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ISeekInStream; + +typedef struct +{ + SRes (*Look)(void *p, const void **buf, size_t *size); + /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. + (output(*size) > input(*size)) is not allowed + (output(*size) < input(*size)) is allowed */ + SRes (*Skip)(void *p, size_t offset); + /* offset must be <= output(*size) of Look */ + + SRes (*Read)(void *p, void *buf, size_t *size); + /* reads directly (without buffer). It's same as ISeqInStream::Read */ + SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); +} ILookInStream; + +static SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); + +/* reads via ILookInStream::Read */ +static SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); +static SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); + +#define LookToRead_BUF_SIZE (1 << 14) + +typedef struct +{ + ILookInStream s; + ISeekInStream *realStream; + size_t pos; + size_t size; + Byte buf[LookToRead_BUF_SIZE]; +} CLookToRead; + +static void LookToRead_CreateVTable(CLookToRead *p, int lookahead); +static void LookToRead_Init(CLookToRead *p); + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToLook; + +typedef struct +{ + ISeqInStream s; + ILookInStream *realStream; +} CSecToRead; + +typedef struct +{ + SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); + /* Returns: result. (result != SZ_OK) means break. + Value (UInt64)(Int64)-1 for size means unknown value. */ +} ICompressProgress; + +typedef struct +{ + void *(*Alloc)(void *p, size_t size); + void (*Free)(void *p, void *address); /* address can be 0 */ +} ISzAlloc; + +#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) +#define IAlloc_Free(p, a) (p)->Free((p), a) + +#ifdef _WIN32 + +#define CHAR_PATH_SEPARATOR '\\' +#define WCHAR_PATH_SEPARATOR L'\\' +#define STRING_PATH_SEPARATOR "\\" +#define WSTRING_PATH_SEPARATOR L"\\" + +#else + +#define CHAR_PATH_SEPARATOR '/' +#define WCHAR_PATH_SEPARATOR L'/' +#define STRING_PATH_SEPARATOR "/" +#define WSTRING_PATH_SEPARATOR L"/" + +#endif + +EXTERN_C_END + +#endif + +/* 7z.h -- 7z interface +2015-11-18 : Igor Pavlov : Public domain */ + +#ifndef __7Z_H +#define __7Z_H + +/*#include "7zTypes.h"*/ + +EXTERN_C_BEGIN + +#define k7zStartHeaderSize 0x20 +#define k7zSignatureSize 6 + +static const Byte k7zSignature[k7zSignatureSize]; + +typedef struct +{ + const Byte *Data; + size_t Size; +} CSzData; + +/* CSzCoderInfo & CSzFolder support only default methods */ + +typedef struct +{ + size_t PropsOffset; + UInt32 MethodID; + Byte NumStreams; + Byte PropsSize; +} CSzCoderInfo; + +typedef struct +{ + UInt32 InIndex; + UInt32 OutIndex; +} CSzBond; + +#define SZ_NUM_CODERS_IN_FOLDER_MAX 4 +#define SZ_NUM_BONDS_IN_FOLDER_MAX 3 +#define SZ_NUM_PACK_STREAMS_IN_FOLDER_MAX 4 + +typedef struct +{ + UInt32 NumCoders; + UInt32 NumBonds; + UInt32 NumPackStreams; + UInt32 UnpackStream; + UInt32 PackStreams[SZ_NUM_PACK_STREAMS_IN_FOLDER_MAX]; + CSzBond Bonds[SZ_NUM_BONDS_IN_FOLDER_MAX]; + CSzCoderInfo Coders[SZ_NUM_CODERS_IN_FOLDER_MAX]; +} CSzFolder; + + +static SRes SzGetNextFolderItem(CSzFolder *f, CSzData *sd); + +typedef struct +{ + UInt32 Low; + UInt32 High; +} CNtfsFileTime; + +typedef struct +{ + Byte *Defs; /* MSB 0 bit numbering */ + UInt32 *Vals; +} CSzBitUi32s; + +typedef struct +{ + Byte *Defs; /* MSB 0 bit numbering */ + /* UInt64 *Vals; */ + CNtfsFileTime *Vals; +} CSzBitUi64s; + +#define SzBitArray_Check(p, i) (((p)[(i) >> 3] & (0x80 >> ((i) & 7))) != 0) + +#define SzBitWithVals_Check(p, i) ((p)->Defs && ((p)->Defs[(i) >> 3] & (0x80 >> ((i) & 7))) != 0) + +typedef struct +{ + UInt32 NumPackStreams; + UInt32 NumFolders; + + UInt64 *PackPositions; /* NumPackStreams + 1 */ + CSzBitUi32s FolderCRCs; /* NumFolders */ + + size_t *FoCodersOffsets; /* NumFolders + 1 */ + UInt32 *FoStartPackStreamIndex; /* NumFolders + 1 */ + UInt32 *FoToCoderUnpackSizes; /* NumFolders + 1 */ + Byte *FoToMainUnpackSizeIndex; /* NumFolders */ + UInt64 *CoderUnpackSizes; /* for all coders in all folders */ + + Byte *CodersData; +} CSzAr; + +static UInt64 SzAr_GetFolderUnpackSize(const CSzAr *p, UInt32 folderIndex); + +static SRes SzAr_DecodeFolder(const CSzAr *p, UInt32 folderIndex, + ILookInStream *stream, UInt64 startPos, + Byte *outBuffer, size_t outSize, + ISzAlloc *allocMain); + +typedef struct +{ + CSzAr db; + + UInt64 startPosAfterHeader; + UInt64 dataPos; + + UInt32 NumFiles; + + UInt64 *UnpackPositions; /* NumFiles + 1 */ + /* Byte *IsEmptyFiles; */ + Byte *IsDirs; + CSzBitUi32s CRCs; + + CSzBitUi32s Attribs; + /* CSzBitUi32s Parents; */ + CSzBitUi64s MTime; + CSzBitUi64s CTime; + + UInt32 *FolderToFile; /* NumFolders + 1 */ + UInt32 *FileToFolder; /* NumFiles */ + + size_t *FileNameOffsets; /* in 2-byte steps */ + Byte *FileNames; /* UTF-16-LE */ +} CSzArEx; + +#define SzArEx_IsDir(p, i) (SzBitArray_Check((p)->IsDirs, i)) + +#define SzArEx_GetFileSize(p, i) ((p)->UnpackPositions[(i) + 1] - (p)->UnpackPositions[i]) + +static void SzArEx_Init(CSzArEx *p); +static void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc); + +/* +if dest == NULL, the return value specifies the required size of the buffer, + in 16-bit characters, including the null-terminating character. +if dest != NULL, the return value specifies the number of 16-bit characters that + are written to the dest, including the null-terminating character. */ + +static size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest); + +/* +size_t SzArEx_GetFullNameLen(const CSzArEx *p, size_t fileIndex); +UInt16 *SzArEx_GetFullNameUtf16_Back(const CSzArEx *p, size_t fileIndex, UInt16 *dest); +*/ + + + +/* + SzArEx_Extract extracts file from archive + + *outBuffer must be 0 before first call for each new archive. + + Extracting cache: + If you need to decompress more than one file, you can send + these values from previous call: + *blockIndex, + *outBuffer, + *outBufferSize + You can consider "*outBuffer" as cache of solid block. If your archive is solid, + it will increase decompression speed. + + If you use external function, you can declare these 3 cache variables + (blockIndex, outBuffer, outBufferSize) as static in that external function. + + Free *outBuffer and set *outBuffer to 0, if you want to flush cache. +*/ + +static SRes SzArEx_Extract( + const CSzArEx *db, + ILookInStream *inStream, + UInt32 fileIndex, /* index of file */ + UInt32 *blockIndex, /* index of solid block */ + Byte **outBuffer, /* pointer to pointer to output buffer (allocated with allocMain) */ + size_t *outBufferSize, /* buffer size for output buffer */ + size_t *offset, /* offset of stream for required file in *outBuffer */ + size_t *outSizeProcessed, /* size of file in *outBuffer */ + ISzAlloc *allocMain, + ISzAlloc *allocTemp); + + +/* +SzArEx_Open Errors: +SZ_ERROR_NO_ARCHIVE +SZ_ERROR_ARCHIVE +SZ_ERROR_UNSUPPORTED +SZ_ERROR_MEM +SZ_ERROR_CRC +SZ_ERROR_INPUT_EOF +SZ_ERROR_FAIL +*/ + +static SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, + ISzAlloc *allocMain, ISzAlloc *allocTemp); + +EXTERN_C_END + +#endif + +/* 7zCrc.h -- CRC32 calculation +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __7Z_CRC_H +#define __7Z_CRC_H + +/*#include "7zTypes.h" */ + +EXTERN_C_BEGIN + +/* Call CrcGenerateTable one time before other CRC functions */ +static void MY_FAST_CALL CrcGenerateTable(void); + +#define CRC_INIT_VAL 0xFFFFFFFF +#define CRC_GET_DIGEST(crc) ((crc) ^ CRC_INIT_VAL) +#define CRC_UPDATE_BYTE(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +static UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size); + +EXTERN_C_END + +#endif + +/* CpuArch.h -- CPU specific code +2016-06-09: Igor Pavlov : Public domain */ + +#ifndef __CPU_ARCH_H +#define __CPU_ARCH_H + +/*#include "7zTypes.h"*/ + +EXTERN_C_BEGIN + +/* +MY_CPU_LE means that CPU is LITTLE ENDIAN. +MY_CPU_BE means that CPU is BIG ENDIAN. +If MY_CPU_LE and MY_CPU_BE are not defined, we don't know about ENDIANNESS of platform. + +MY_CPU_LE_UNALIGN means that CPU is LITTLE ENDIAN and CPU supports unaligned memory accesses. +*/ + +#if defined(_M_X64) \ + || defined(_M_AMD64) \ + || defined(__x86_64__) \ + || defined(__AMD64__) \ + || defined(__amd64__) + #define MY_CPU_AMD64 +#endif + +#if defined(MY_CPU_AMD64) \ + || defined(_M_IA64) \ + || defined(__AARCH64EL__) \ + || defined(__AARCH64EB__) + #define MY_CPU_64BIT +#endif + +#if defined(_M_IX86) || defined(__i386__) +#define MY_CPU_X86 +#endif + +#if defined(MY_CPU_X86) || defined(MY_CPU_AMD64) +#define MY_CPU_X86_OR_AMD64 +#endif + +#if defined(MY_CPU_X86) \ + || defined(_M_ARM) \ + || defined(__ARMEL__) \ + || defined(__THUMBEL__) \ + || defined(__ARMEB__) \ + || defined(__THUMBEB__) + #define MY_CPU_32BIT +#endif + +#if defined(_WIN32) && defined(_M_ARM) +#define MY_CPU_ARM_LE +#endif + +#if defined(_WIN32) && defined(_M_IA64) +#define MY_CPU_IA64_LE +#endif + +#if defined(MY_CPU_X86_OR_AMD64) \ + || defined(MY_CPU_ARM_LE) \ + || defined(MY_CPU_IA64_LE) \ + || defined(__LITTLE_ENDIAN__) \ + || defined(__ARMEL__) \ + || defined(__THUMBEL__) \ + || defined(__AARCH64EL__) \ + || defined(__MIPSEL__) \ + || defined(__MIPSEL) \ + || defined(_MIPSEL) \ + || defined(__BFIN__) \ + || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)) + #define MY_CPU_LE +#endif + +#if defined(__BIG_ENDIAN__) \ + || defined(__ARMEB__) \ + || defined(__THUMBEB__) \ + || defined(__AARCH64EB__) \ + || defined(__MIPSEB__) \ + || defined(__MIPSEB) \ + || defined(_MIPSEB) \ + || defined(__m68k__) \ + || defined(__s390__) \ + || defined(__s390x__) \ + || defined(__zarch__) \ + || (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) + #define MY_CPU_BE +#endif + +#if defined(MY_CPU_LE) && defined(MY_CPU_BE) +Stop_Compiling_Bad_Endian +#endif + + +#ifdef MY_CPU_LE + #if defined(MY_CPU_X86_OR_AMD64) \ + /* || defined(__AARCH64EL__) */ + /*#define MY_CPU_LE_UNALIGN*/ + #endif +#endif + + +#ifdef MY_CPU_LE_UNALIGN + +#define GetUi16(p) (*(const UInt16 *)(const void *)(p)) +#define GetUi32(p) (*(const UInt32 *)(const void *)(p)) +#define GetUi64(p) (*(const UInt64 *)(const void *)(p)) + +#define SetUi16(p, v) { *(UInt16 *)(p) = (v); } +#define SetUi32(p, v) { *(UInt32 *)(p) = (v); } +#define SetUi64(p, v) { *(UInt64 *)(p) = (v); } + +#else + +#define GetUi16(p) ( (UInt16) ( \ + ((const Byte *)(p))[0] | \ + ((UInt16)((const Byte *)(p))[1] << 8) )) + +#define GetUi32(p) ( \ + ((const Byte *)(p))[0] | \ + ((UInt32)((const Byte *)(p))[1] << 8) | \ + ((UInt32)((const Byte *)(p))[2] << 16) | \ + ((UInt32)((const Byte *)(p))[3] << 24)) + +#define GetUi64(p) (GetUi32(p) | ((UInt64)GetUi32(((const Byte *)(p)) + 4) << 32)) + +#define SetUi16(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ + _ppp_[0] = (Byte)_vvv_; \ + _ppp_[1] = (Byte)(_vvv_ >> 8); } + +#define SetUi32(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ + _ppp_[0] = (Byte)_vvv_; \ + _ppp_[1] = (Byte)(_vvv_ >> 8); \ + _ppp_[2] = (Byte)(_vvv_ >> 16); \ + _ppp_[3] = (Byte)(_vvv_ >> 24); } + +#define SetUi64(p, v) { Byte *_ppp2_ = (Byte *)(p); UInt64 _vvv2_ = (v); \ + SetUi32(_ppp2_ , (UInt32)_vvv2_); \ + SetUi32(_ppp2_ + 4, (UInt32)(_vvv2_ >> 32)); } + +#endif + + +#if defined(MY_CPU_LE_UNALIGN) && /* defined(_WIN64) && */ (_MSC_VER >= 1300) + +/* Note: we use bswap instruction, that is unsupported in 386 cpu */ + +#include <stdlib.h> + +#pragma intrinsic(_byteswap_ulong) +#pragma intrinsic(_byteswap_uint64) +#define GetBe32(p) _byteswap_ulong(*(const UInt32 *)(const Byte *)(p)) +#define GetBe64(p) _byteswap_uint64(*(const UInt64 *)(const Byte *)(p)) + +#define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = _byteswap_ulong(v) + +#elif defined(MY_CPU_LE_UNALIGN) && defined (__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) + +#define GetBe32(p) __builtin_bswap32(*(const UInt32 *)(const Byte *)(p)) +#define GetBe64(p) __builtin_bswap64(*(const UInt64 *)(const Byte *)(p)) + +#define SetBe32(p, v) (*(UInt32 *)(void *)(p)) = __builtin_bswap32(v) + +#else + +#define GetBe32(p) ( \ + ((UInt32)((const Byte *)(p))[0] << 24) | \ + ((UInt32)((const Byte *)(p))[1] << 16) | \ + ((UInt32)((const Byte *)(p))[2] << 8) | \ + ((const Byte *)(p))[3] ) + +#define GetBe64(p) (((UInt64)GetBe32(p) << 32) | GetBe32(((const Byte *)(p)) + 4)) + +#define SetBe32(p, v) { Byte *_ppp_ = (Byte *)(p); UInt32 _vvv_ = (v); \ + _ppp_[0] = (Byte)(_vvv_ >> 24); \ + _ppp_[1] = (Byte)(_vvv_ >> 16); \ + _ppp_[2] = (Byte)(_vvv_ >> 8); \ + _ppp_[3] = (Byte)_vvv_; } + +#endif + + +#define GetBe16(p) ( (UInt16) ( \ + ((UInt16)((const Byte *)(p))[0] << 8) | \ + ((const Byte *)(p))[1] )) + + + +#ifdef MY_CPU_X86_OR_AMD64 + +typedef struct +{ + UInt32 maxFunc; + UInt32 vendor[3]; + UInt32 ver; + UInt32 b; + UInt32 c; + UInt32 d; +} Cx86cpuid; + +enum +{ + CPU_FIRM_INTEL, + CPU_FIRM_AMD, + CPU_FIRM_VIA +}; + +static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d); + +static Bool x86cpuid_CheckAndRead(Cx86cpuid *p); +static int x86cpuid_GetFirm(const Cx86cpuid *p); + +#define x86cpuid_GetFamily(ver) (((ver >> 16) & 0xFF0) | ((ver >> 8) & 0xF)) +#define x86cpuid_GetModel(ver) (((ver >> 12) & 0xF0) | ((ver >> 4) & 0xF)) +#define x86cpuid_GetStepping(ver) (ver & 0xF) + +static Bool CPU_Is_InOrder(); + +#endif + +EXTERN_C_END + +#endif + +/* 7zBuf.h -- Byte Buffer +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __7Z_BUF_H +#define __7Z_BUF_H + +/*#include "7zTypes.h" */ + +EXTERN_C_BEGIN + +typedef struct +{ + Byte *data; + size_t size; +} CBuf; + +static void Buf_Init(CBuf *p); +static int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc); +static void Buf_Free(CBuf *p, ISzAlloc *alloc); + +EXTERN_C_END + +#endif + + +/* Bcj2.h -- BCJ2 Converter for x86 code +2014-11-10 : Igor Pavlov : Public domain */ + +#ifndef __BCJ2_H +#define __BCJ2_H + +/*#include "7zTypes.h" */ + +EXTERN_C_BEGIN + +#define BCJ2_NUM_STREAMS 4 + +enum +{ + BCJ2_STREAM_MAIN, + BCJ2_STREAM_CALL, + BCJ2_STREAM_JUMP, + BCJ2_STREAM_RC +}; + +enum +{ + BCJ2_DEC_STATE_ORIG_0 = BCJ2_NUM_STREAMS, + BCJ2_DEC_STATE_ORIG_1, + BCJ2_DEC_STATE_ORIG_2, + BCJ2_DEC_STATE_ORIG_3, + + BCJ2_DEC_STATE_ORIG, + BCJ2_DEC_STATE_OK +}; + +enum +{ + BCJ2_ENC_STATE_ORIG = BCJ2_NUM_STREAMS, + BCJ2_ENC_STATE_OK +}; + + +#define BCJ2_IS_32BIT_STREAM(s) ((s) == BCJ2_STREAM_CALL || (s) == BCJ2_STREAM_JUMP) + +/* +CBcj2Dec / CBcj2Enc +bufs sizes: + BUF_SIZE(n) = lims[n] - bufs[n] +bufs sizes for BCJ2_STREAM_CALL and BCJ2_STREAM_JUMP must be mutliply of 4: + (BUF_SIZE(BCJ2_STREAM_CALL) & 3) == 0 + (BUF_SIZE(BCJ2_STREAM_JUMP) & 3) == 0 +*/ + +/* +CBcj2Dec: +dest is allowed to overlap with bufs[BCJ2_STREAM_MAIN], with the following conditions: + bufs[BCJ2_STREAM_MAIN] >= dest && + bufs[BCJ2_STREAM_MAIN] - dest >= tempReserv + + BUF_SIZE(BCJ2_STREAM_CALL) + + BUF_SIZE(BCJ2_STREAM_JUMP) + tempReserv = 0 : for first call of Bcj2Dec_Decode + tempReserv = 4 : for any other calls of Bcj2Dec_Decode + overlap with offset = 1 is not allowed +*/ + +typedef struct +{ + const Byte *bufs[BCJ2_NUM_STREAMS]; + const Byte *lims[BCJ2_NUM_STREAMS]; + Byte *dest; + const Byte *destLim; + + unsigned state; /* BCJ2_STREAM_MAIN has more priority than BCJ2_STATE_ORIG */ + + UInt32 ip; + Byte temp[4]; + UInt32 range; + UInt32 code; + UInt16 probs[2 + 256]; +} CBcj2Dec; + +static void Bcj2Dec_Init(CBcj2Dec *p); + +/* Returns: SZ_OK or SZ_ERROR_DATA */ +static SRes Bcj2Dec_Decode(CBcj2Dec *p); + +#define Bcj2Dec_IsFinished(_p_) ((_p_)->code == 0) + +#define BCJ2_RELAT_LIMIT_NUM_BITS 26 +#define BCJ2_RELAT_LIMIT ((UInt32)1 << BCJ2_RELAT_LIMIT_NUM_BITS) + +/* limit for CBcj2Enc::fileSize variable */ +#define BCJ2_FileSize_MAX ((UInt32)1 << 31) + +EXTERN_C_END + +#endif + +/* Bra.h -- Branch converters for executables +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __BRA_H +#define __BRA_H + +/*#include "7zTypes.h"*/ + +EXTERN_C_BEGIN + +/* +These functions convert relative addresses to absolute addresses +in CALL instructions to increase the compression ratio. + + In: + data - data buffer + size - size of data + ip - current virtual Instruction Pinter (IP) value + state - state variable for x86 converter + encoding - 0 (for decoding), 1 (for encoding) + + Out: + state - state variable for x86 converter + + Returns: + The number of processed bytes. If you call these functions with multiple calls, + you must start next call with first byte after block of processed bytes. + + Type Endian Alignment LookAhead + + x86 little 1 4 + ARMT little 2 2 + ARM little 4 0 + PPC big 4 0 + SPARC big 4 0 + IA64 little 16 0 + + size must be >= Alignment + LookAhead, if it's not last block. + If (size < Alignment + LookAhead), converter returns 0. + + Example: + + UInt32 ip = 0; + for () + { + ; size must be >= Alignment + LookAhead, if it's not last block + SizeT processed = Convert(data, size, ip, 1); + data += processed; + size -= processed; + ip += processed; + } +*/ + +#define x86_Convert_Init(state) { state = 0; } +static SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding); +static SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +static SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +static SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +static SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); +static SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding); + +EXTERN_C_END + +#endif + +/* Delta.h -- Delta converter +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __DELTA_H +#define __DELTA_H + +/*#include "7zTypes.h" */ + +EXTERN_C_BEGIN + +#define DELTA_STATE_SIZE 256 + +static void Delta_Init(Byte *state); +static void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size); + +EXTERN_C_END + +#endif + +/* LzmaDec.h -- LZMA Decoder +2013-01-18 : Igor Pavlov : Public domain */ + +#ifndef __LZMA_DEC_H +#define __LZMA_DEC_H + +/*#include "7zTypes.h"*/ + +EXTERN_C_BEGIN + +/* #define _LZMA_PROB32 */ +/* _LZMA_PROB32 can increase the speed on some CPUs, + but memory usage for CLzmaDec::probs will be doubled in that case */ + +#ifdef _LZMA_PROB32 +#define CLzmaProb UInt32 +#else +#define CLzmaProb UInt16 +#endif + + +/* ---------- LZMA Properties ---------- */ + +#define LZMA_PROPS_SIZE 5 + +typedef struct _CLzmaProps +{ + unsigned lc, lp, pb; + UInt32 dicSize; +} CLzmaProps; + +/* LzmaProps_Decode - decodes properties +Returns: + SZ_OK + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +static SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size); + + +/* ---------- LZMA Decoder state ---------- */ + +/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case. + Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */ + +#define LZMA_REQUIRED_INPUT_MAX 20 + +typedef struct +{ + CLzmaProps prop; + CLzmaProb *probs; + Byte *dic; + const Byte *buf; + UInt32 range, code; + SizeT dicPos; + SizeT dicBufSize; + UInt32 processedPos; + UInt32 checkDicSize; + unsigned state; + UInt32 reps[4]; + unsigned remainLen; + int needFlush; + int needInitState; + UInt32 numProbs; + unsigned tempBufSize; + Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; +} CLzmaDec; + +#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } + +static void LzmaDec_Init(CLzmaDec *p); + +/* There are two types of LZMA streams: + 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. + 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + +typedef enum +{ + LZMA_FINISH_ANY, /* finish at any point */ + LZMA_FINISH_END /* block must be finished at the end */ +} ELzmaFinishMode; + +/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!! + + You must use LZMA_FINISH_END, when you know that current output buffer + covers last bytes of block. In other cases you must use LZMA_FINISH_ANY. + + If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK, + and output value of destLen will be less than output buffer size limit. + You can check status result also. + + You can use multiple checks to test data integrity after full decompression: + 1) Check Result and "status" variable. + 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize. + 3) Check that output(srcLen) = compressedSize, if you know real compressedSize. + You must use correct finish mode in that case. */ + +typedef enum +{ + LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */ + LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */ + LZMA_STATUS_NOT_FINISHED, /* stream was not finished */ + LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */ + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */ +} ELzmaStatus; + +/* ELzmaStatus is used only as output value for function call */ + + +/* ---------- Interfaces ---------- */ + +/* There are 3 levels of interfaces: + 1) Dictionary Interface + 2) Buffer Interface + 3) One Call Interface + You can select any of these interfaces, but don't mix functions from different + groups for same object. */ + + +/* There are two variants to allocate state for Dictionary Interface: + 1) LzmaDec_Allocate / LzmaDec_Free + 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs + You can use variant 2, if you set dictionary buffer manually. + For Buffer Interface you must always use variant 1. + +LzmaDec_Allocate* can return: + SZ_OK + SZ_ERROR_MEM - Memory allocation error + SZ_ERROR_UNSUPPORTED - Unsupported properties +*/ + +static SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); +static void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); + +/* ---------- Dictionary Interface ---------- */ + +/* You can use it, if you want to eliminate the overhead for data copying from + dictionary to some other external buffer. + You must work with CLzmaDec variables directly in this interface. + + STEPS: + LzmaDec_Constr() + LzmaDec_Allocate() + for (each new stream) + { + LzmaDec_Init() + while (it needs more decompression) + { + LzmaDec_DecodeToDic() + use data from CLzmaDec::dic and update CLzmaDec::dicPos + } + } + LzmaDec_Free() +*/ + +/* LzmaDec_DecodeToDic + + The decoding to internal dictionary buffer (CLzmaDec::dic). + You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!! + +finishMode: + It has meaning only if the decoding reaches output limit (dicLimit). + LZMA_FINISH_ANY - Decode just dicLimit bytes. + LZMA_FINISH_END - Stream must be finished after dicLimit. + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + SZ_ERROR_DATA - Data error +*/ + +static SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + +EXTERN_C_END + +#endif + +/* Lzma2Dec.h -- LZMA2 Decoder +2015-05-13 : Igor Pavlov : Public domain */ + +#ifndef __LZMA2_DEC_H +#define __LZMA2_DEC_H + +/*#include "LzmaDec.h"*/ + +EXTERN_C_BEGIN + +/* ---------- State Interface ---------- */ + +typedef struct +{ + CLzmaDec decoder; + UInt32 packSize; + UInt32 unpackSize; + unsigned state; + Byte control; + Bool needInitDic; + Bool needInitState; + Bool needInitProp; +} CLzma2Dec; + +#define Lzma2Dec_Construct(p) LzmaDec_Construct(&(p)->decoder) +#define Lzma2Dec_FreeProbs(p, alloc) LzmaDec_FreeProbs(&(p)->decoder, alloc); +#define Lzma2Dec_Free(p, alloc) LzmaDec_Free(&(p)->decoder, alloc); + +static SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc); +static void Lzma2Dec_Init(CLzma2Dec *p); + + +/* +finishMode: + It has meaning only if the decoding reaches output limit (*destLen or dicLimit). + LZMA_FINISH_ANY - use smallest number of input bytes + LZMA_FINISH_END - read EndOfStream marker after decoding + +Returns: + SZ_OK + status: + LZMA_STATUS_FINISHED_WITH_MARK + LZMA_STATUS_NOT_FINISHED + LZMA_STATUS_NEEDS_MORE_INPUT + SZ_ERROR_DATA - Data error +*/ + +static SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status); + + +EXTERN_C_END + +#endif + + +/* END HEADERS */ + + +/* 7zCrc.c -- CRC32 init +2015-03-10 : Igor Pavlov : Public domain */ + +/* +#include "Precomp.h" + +#include "7zCrc.h" +#include "CpuArch.h" +*/ +#define UNUSED_VAR(x) (void)x; + +#define kCrcPoly 0xEDB88320 + +#ifdef MY_CPU_LE + #define CRC_NUM_TABLES 8 +#else + #define CRC_NUM_TABLES 9 + + #define CRC_UINT32_SWAP(v) ((v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24)) + + static UInt32 MY_FAST_CALL CrcUpdateT1_BeT4(UInt32 v, const void *data, size_t size, const UInt32 *table); + static UInt32 MY_FAST_CALL CrcUpdateT1_BeT8(UInt32 v, const void *data, size_t size, const UInt32 *table); +#endif + +#ifndef MY_CPU_BE + static UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table); + static UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table); +#endif + +typedef UInt32 (MY_FAST_CALL *CRC_FUNC)(UInt32 v, const void *data, size_t size, const UInt32 *table); + +static CRC_FUNC g_CrcUpdateT4; +static CRC_FUNC g_CrcUpdateT8; +static CRC_FUNC g_CrcUpdate; + +static UInt32 g_CrcTable[256 * CRC_NUM_TABLES]; + +static UInt32 MY_FAST_CALL CrcCalc(const void *data, size_t size) +{ + return g_CrcUpdate(CRC_INIT_VAL, data, size, g_CrcTable) ^ CRC_INIT_VAL; +} + +#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +#if CRC_NUM_TABLES < 4 +static UInt32 MY_FAST_CALL CrcUpdateT1(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + const Byte *pEnd = p + size; + for (; p != pEnd; p++) + v = CRC_UPDATE_BYTE_2(v, *p); + return v; +} +#endif + +static void MY_FAST_CALL CrcGenerateTable() +{ + UInt32 i; + for (i = 0; i < 256; i++) + { + UInt32 r = i; + unsigned j; + for (j = 0; j < 8; j++) + r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + g_CrcTable[i] = r; + } + for (; i < 256 * CRC_NUM_TABLES; i++) + { + UInt32 r = g_CrcTable[i - 256]; + g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8); + } + + #if CRC_NUM_TABLES < 4 + + g_CrcUpdate = CrcUpdateT1; + + #else + + #ifdef MY_CPU_LE + + g_CrcUpdateT4 = CrcUpdateT4; + g_CrcUpdate = CrcUpdateT4; + + #if CRC_NUM_TABLES >= 8 + g_CrcUpdateT8 = CrcUpdateT8; + + #ifdef MY_CPU_X86_OR_AMD64 + if (!CPU_Is_InOrder()) + g_CrcUpdate = CrcUpdateT8; + #endif + #endif + + #else + { + #ifndef MY_CPU_BE + UInt32 k = 0x01020304; + const Byte *p = (const Byte *)&k; + if (p[0] == 4 && p[1] == 3) + { + g_CrcUpdateT4 = CrcUpdateT4; + g_CrcUpdate = CrcUpdateT4; + #if CRC_NUM_TABLES >= 8 + g_CrcUpdateT8 = CrcUpdateT8; + /* g_CrcUpdate = CrcUpdateT8; */ + #endif + } + else if (p[0] != 1 || p[1] != 2) + g_CrcUpdate = CrcUpdateT1; + else + #endif + { + for (i = 256 * CRC_NUM_TABLES - 1; i >= 256; i--) + { + UInt32 x = g_CrcTable[i - 256]; + g_CrcTable[i] = CRC_UINT32_SWAP(x); + } + g_CrcUpdateT4 = CrcUpdateT1_BeT4; + g_CrcUpdate = CrcUpdateT1_BeT4; + #if CRC_NUM_TABLES >= 8 + g_CrcUpdateT8 = CrcUpdateT1_BeT8; + /* g_CrcUpdate = CrcUpdateT1_BeT8; */ + #endif + } + } + #endif + + #endif +} + +/* 7zCrcOpt.c -- CRC32 calculation +2015-03-01 : Igor Pavlov : Public domain */ + +/* +#include "Precomp.h" + +#include "CpuArch.h" +*/ + +#ifndef MY_CPU_BE + +#define CRC_UPDATE_BYTE_2(crc, b) (table[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) + +static UInt32 MY_FAST_CALL CrcUpdateT4(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + for (; size >= 4; size -= 4, p += 4) + { + v ^= *(const UInt32 *)p; + v = + table[0x300 + ((v ) & 0xFF)] + ^ table[0x200 + ((v >> 8) & 0xFF)] + ^ table[0x100 + ((v >> 16) & 0xFF)] + ^ table[0x000 + ((v >> 24))]; + } + for (; size > 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + return v; +} + +static UInt32 MY_FAST_CALL CrcUpdateT8(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + for (; size > 0 && ((unsigned)(ptrdiff_t)p & 7) != 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + for (; size >= 8; size -= 8, p += 8) + { + UInt32 d; + v ^= *(const UInt32 *)p; + v = + table[0x700 + ((v ) & 0xFF)] + ^ table[0x600 + ((v >> 8) & 0xFF)] + ^ table[0x500 + ((v >> 16) & 0xFF)] + ^ table[0x400 + ((v >> 24))]; + d = *((const UInt32 *)p + 1); + v ^= + table[0x300 + ((d ) & 0xFF)] + ^ table[0x200 + ((d >> 8) & 0xFF)] + ^ table[0x100 + ((d >> 16) & 0xFF)] + ^ table[0x000 + ((d >> 24))]; + } + for (; size > 0; size--, p++) + v = CRC_UPDATE_BYTE_2(v, *p); + return v; +} + +#endif + + +#ifndef MY_CPU_LE + +#define CRC_UINT32_SWAP(v) ((v >> 24) | ((v >> 8) & 0xFF00) | ((v << 8) & 0xFF0000) | (v << 24)) + +#define CRC_UPDATE_BYTE_2_BE(crc, b) (table[(((crc) >> 24) ^ (b))] ^ ((crc) << 8)) + +static UInt32 MY_FAST_CALL CrcUpdateT1_BeT4(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + table += 0x100; + v = CRC_UINT32_SWAP(v); + for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) + v = CRC_UPDATE_BYTE_2_BE(v, *p); + for (; size >= 4; size -= 4, p += 4) + { + v ^= *(const UInt32 *)p; + v = + table[0x000 + ((v ) & 0xFF)] + ^ table[0x100 + ((v >> 8) & 0xFF)] + ^ table[0x200 + ((v >> 16) & 0xFF)] + ^ table[0x300 + ((v >> 24))]; + } + for (; size > 0; size--, p++) + v = CRC_UPDATE_BYTE_2_BE(v, *p); + return CRC_UINT32_SWAP(v); +} + +static UInt32 MY_FAST_CALL CrcUpdateT1_BeT8(UInt32 v, const void *data, size_t size, const UInt32 *table) +{ + const Byte *p = (const Byte *)data; + table += 0x100; + v = CRC_UINT32_SWAP(v); + for (; size > 0 && ((unsigned)(ptrdiff_t)p & 7) != 0; size--, p++) + v = CRC_UPDATE_BYTE_2_BE(v, *p); + for (; size >= 8; size -= 8, p += 8) + { + UInt32 d; + v ^= *(const UInt32 *)p; + v = + table[0x400 + ((v ) & 0xFF)] + ^ table[0x500 + ((v >> 8) & 0xFF)] + ^ table[0x600 + ((v >> 16) & 0xFF)] + ^ table[0x700 + ((v >> 24))]; + d = *((const UInt32 *)p + 1); + v ^= + table[0x000 + ((d ) & 0xFF)] + ^ table[0x100 + ((d >> 8) & 0xFF)] + ^ table[0x200 + ((d >> 16) & 0xFF)] + ^ table[0x300 + ((d >> 24))]; + } + for (; size > 0; size--, p++) + v = CRC_UPDATE_BYTE_2_BE(v, *p); + return CRC_UINT32_SWAP(v); +} + +#endif + +/* CpuArch.c -- CPU specific code +2016-02-25: Igor Pavlov : Public domain */ + +/* +#include "Precomp.h" + +#include "CpuArch.h" +*/ + +#ifdef MY_CPU_X86_OR_AMD64 + +#if (defined(_MSC_VER) && !defined(MY_CPU_AMD64)) || defined(__GNUC__) +#define USE_ASM +#endif + +#if !defined(USE_ASM) && _MSC_VER >= 1500 +#include <intrin.h> +#endif + +#if defined(USE_ASM) && !defined(MY_CPU_AMD64) +static UInt32 CheckFlag(UInt32 flag) +{ + #ifdef _MSC_VER + __asm pushfd; + __asm pop EAX; + __asm mov EDX, EAX; + __asm xor EAX, flag; + __asm push EAX; + __asm popfd; + __asm pushfd; + __asm pop EAX; + __asm xor EAX, EDX; + __asm push EDX; + __asm popfd; + __asm and flag, EAX; + #else + __asm__ __volatile__ ( + "pushf\n\t" + "pop %%EAX\n\t" + "movl %%EAX,%%EDX\n\t" + "xorl %0,%%EAX\n\t" + "push %%EAX\n\t" + "popf\n\t" + "pushf\n\t" + "pop %%EAX\n\t" + "xorl %%EDX,%%EAX\n\t" + "push %%EDX\n\t" + "popf\n\t" + "andl %%EAX, %0\n\t": + "=c" (flag) : "c" (flag) : + "%eax", "%edx"); + #endif + return flag; +} +#define CHECK_CPUID_IS_SUPPORTED if (CheckFlag(1 << 18) == 0 || CheckFlag(1 << 21) == 0) return False; +#else +#define CHECK_CPUID_IS_SUPPORTED +#endif + +#if defined(__WATCOMC__) +static void __cpuid(int *cpuinfo, const UInt32 infotype); +#pragma aux __cpuid = \ + ".586" \ + "cpuid" \ + "mov [esi+0],eax" \ + "mov [esi+4],ebx" \ + "mov [esi+8],ecx" \ + "mov [esi+12],edx" \ + parm [esi] [eax] modify [ebx ecx edx]; +#endif + + +static void MyCPUID(UInt32 function, UInt32 *a, UInt32 *b, UInt32 *c, UInt32 *d) +{ + #ifdef USE_ASM + + #ifdef _MSC_VER + + UInt32 a2, b2, c2, d2; + __asm xor EBX, EBX; + __asm xor ECX, ECX; + __asm xor EDX, EDX; + __asm mov EAX, function; + __asm cpuid; + __asm mov a2, EAX; + __asm mov b2, EBX; + __asm mov c2, ECX; + __asm mov d2, EDX; + + *a = a2; + *b = b2; + *c = c2; + *d = d2; + + #else + + __asm__ __volatile__ ( + #if defined(MY_CPU_AMD64) && defined(__PIC__) + "mov %%rbx, %%rdi;" + "cpuid;" + "xchg %%rbx, %%rdi;" + : "=a" (*a) , + "=D" (*b) , + #elif defined(MY_CPU_X86) && defined(__PIC__) + "mov %%ebx, %%edi;" + "cpuid;" + "xchgl %%ebx, %%edi;" + : "=a" (*a) , + "=D" (*b) , + #else + "cpuid" + : "=a" (*a) , + "=b" (*b) , + #endif + "=c" (*c) , + "=d" (*d) + : "0" (function)) ; + + #endif + + #else + + int CPUInfo[4]; + __cpuid(CPUInfo, function); + *a = CPUInfo[0]; + *b = CPUInfo[1]; + *c = CPUInfo[2]; + *d = CPUInfo[3]; + + #endif +} + +static Bool x86cpuid_CheckAndRead(Cx86cpuid *p) +{ + CHECK_CPUID_IS_SUPPORTED + MyCPUID(0, &p->maxFunc, &p->vendor[0], &p->vendor[2], &p->vendor[1]); + MyCPUID(1, &p->ver, &p->b, &p->c, &p->d); + return True; +} + +static const UInt32 kVendors[][3] = +{ + { 0x756E6547, 0x49656E69, 0x6C65746E}, + { 0x68747541, 0x69746E65, 0x444D4163}, + { 0x746E6543, 0x48727561, 0x736C7561} +}; + +static int x86cpuid_GetFirm(const Cx86cpuid *p) +{ + unsigned i; + for (i = 0; i < sizeof(kVendors) / sizeof(kVendors[i]); i++) + { + const UInt32 *v = kVendors[i]; + if (v[0] == p->vendor[0] && + v[1] == p->vendor[1] && + v[2] == p->vendor[2]) + return (int)i; + } + return -1; +} + +static Bool CPU_Is_InOrder() +{ + Cx86cpuid p; + int firm; + UInt32 family, model; + if (!x86cpuid_CheckAndRead(&p)) + return True; + + family = x86cpuid_GetFamily(p.ver); + model = x86cpuid_GetModel(p.ver); + + firm = x86cpuid_GetFirm(&p); + + switch (firm) + { + case CPU_FIRM_INTEL: return (family < 6 || (family == 6 && ( + /* In-Order Atom CPU */ + model == 0x1C /* 45 nm, N4xx, D4xx, N5xx, D5xx, 230, 330 */ + || model == 0x26 /* 45 nm, Z6xx */ + || model == 0x27 /* 32 nm, Z2460 */ + || model == 0x35 /* 32 nm, Z2760 */ + || model == 0x36 /* 32 nm, N2xxx, D2xxx */ + ))); + case CPU_FIRM_AMD: return (family < 5 || (family == 5 && (model < 6 || model == 0xA))); + case CPU_FIRM_VIA: return (family < 6 || (family == 6 && model < 0xF)); + } + return True; +} + +#endif + +/* 7zStream.c -- 7z Stream functions +2013-11-12 : Igor Pavlov : Public domain */ + +/*#include "Precomp.h"*/ + +#include <string.h> + +/*#include "7zTypes.h"*/ + +static SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset) +{ + Int64 t = offset; + return stream->Seek(stream, &t, SZ_SEEK_SET); +} + +static SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType) +{ + while (size != 0) + { + size_t processed = size; + RINOK(stream->Read(stream, buf, &processed)); + if (processed == 0) + return errorType; + buf = (void *)((Byte *)buf + processed); + size -= processed; + } + return SZ_OK; +} + +static SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size) +{ + return LookInStream_Read2(stream, buf, size, SZ_ERROR_INPUT_EOF); +} + +static SRes LookToRead_Look_Lookahead(void *pp, const void **buf, size_t *size) +{ + SRes res = SZ_OK; + CLookToRead *p = (CLookToRead *)pp; + size_t size2 = p->size - p->pos; + if (size2 == 0 && *size > 0) + { + p->pos = 0; + size2 = LookToRead_BUF_SIZE; + res = p->realStream->Read(p->realStream, p->buf, &size2); + p->size = size2; + } + if (size2 < *size) + *size = size2; + *buf = p->buf + p->pos; + return res; +} + +static SRes LookToRead_Look_Exact(void *pp, const void **buf, size_t *size) +{ + SRes res = SZ_OK; + CLookToRead *p = (CLookToRead *)pp; + size_t size2 = p->size - p->pos; + if (size2 == 0 && *size > 0) + { + p->pos = 0; + if (*size > LookToRead_BUF_SIZE) + *size = LookToRead_BUF_SIZE; + res = p->realStream->Read(p->realStream, p->buf, size); + size2 = p->size = *size; + } + if (size2 < *size) + *size = size2; + *buf = p->buf + p->pos; + return res; +} + +static SRes LookToRead_Skip(void *pp, size_t offset) +{ + CLookToRead *p = (CLookToRead *)pp; + p->pos += offset; + return SZ_OK; +} + +static SRes LookToRead_Read(void *pp, void *buf, size_t *size) +{ + CLookToRead *p = (CLookToRead *)pp; + size_t rem = p->size - p->pos; + if (rem == 0) + return p->realStream->Read(p->realStream, buf, size); + if (rem > *size) + rem = *size; + memcpy(buf, p->buf + p->pos, rem); + p->pos += rem; + *size = rem; + return SZ_OK; +} + +static SRes LookToRead_Seek(void *pp, Int64 *pos, ESzSeek origin) +{ + CLookToRead *p = (CLookToRead *)pp; + p->pos = p->size = 0; + return p->realStream->Seek(p->realStream, pos, origin); +} + +static void LookToRead_CreateVTable(CLookToRead *p, int lookahead) +{ + p->s.Look = lookahead ? + LookToRead_Look_Lookahead : + LookToRead_Look_Exact; + p->s.Skip = LookToRead_Skip; + p->s.Read = LookToRead_Read; + p->s.Seek = LookToRead_Seek; +} + +static void LookToRead_Init(CLookToRead *p) +{ + p->pos = p->size = 0; +} + + +/* 7zArcIn.c -- 7z Input functions +2016-05-16 : Igor Pavlov : Public domain */ + +/* +#include "Precomp.h" + +#include <string.h> + +#include "7z.h" +#include "7zBuf.h" +#include "7zCrc.h" +#include "CpuArch.h" +*/ + +#define MY_ALLOC(T, p, size, alloc) { \ + if ((p = (T *)IAlloc_Alloc(alloc, (size) * sizeof(T))) == NULL) return SZ_ERROR_MEM; } + +#define MY_ALLOC_ZE(T, p, size, alloc) { if ((size) == 0) p = NULL; else MY_ALLOC(T, p, size, alloc) } + +#define MY_ALLOC_AND_CPY(to, size, from, alloc) \ + { MY_ALLOC(Byte, to, size, alloc); memcpy(to, from, size); } + +#define MY_ALLOC_ZE_AND_CPY(to, size, from, alloc) \ + { if ((size) == 0) p = NULL; else { MY_ALLOC_AND_CPY(to, size, from, alloc) } } + +#define k7zMajorVersion 0 + +enum EIdEnum +{ + k7zIdEnd, + k7zIdHeader, + k7zIdArchiveProperties, + k7zIdAdditionalStreamsInfo, + k7zIdMainStreamsInfo, + k7zIdFilesInfo, + k7zIdPackInfo, + k7zIdUnpackInfo, + k7zIdSubStreamsInfo, + k7zIdSize, + k7zIdCRC, + k7zIdFolder, + k7zIdCodersUnpackSize, + k7zIdNumUnpackStream, + k7zIdEmptyStream, + k7zIdEmptyFile, + k7zIdAnti, + k7zIdName, + k7zIdCTime, + k7zIdATime, + k7zIdMTime, + k7zIdWinAttrib, + k7zIdComment, + k7zIdEncodedHeader, + k7zIdStartPos, + k7zIdDummy + /* k7zNtSecure, */ + /* k7zParent, */ + /* k7zIsReal */ +}; + +static const Byte k7zSignature[k7zSignatureSize] = {'7', 'z', 0xBC, 0xAF, 0x27, 0x1C}; + +#define SzBitUi32s_Init(p) { (p)->Defs = NULL; (p)->Vals = NULL; } + +static SRes SzBitUi32s_Alloc(CSzBitUi32s *p, size_t num, ISzAlloc *alloc) +{ + if (num == 0) + { + p->Defs = NULL; + p->Vals = NULL; + } + else + { + MY_ALLOC(Byte, p->Defs, (num + 7) >> 3, alloc); + MY_ALLOC(UInt32, p->Vals, num, alloc); + } + return SZ_OK; +} + +static void SzBitUi32s_Free(CSzBitUi32s *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->Defs); p->Defs = NULL; + IAlloc_Free(alloc, p->Vals); p->Vals = NULL; +} + +#define SzBitUi64s_Init(p) { (p)->Defs = NULL; (p)->Vals = NULL; } + +static void SzBitUi64s_Free(CSzBitUi64s *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->Defs); p->Defs = NULL; + IAlloc_Free(alloc, p->Vals); p->Vals = NULL; +} + + +static void SzAr_Init(CSzAr *p) +{ + p->NumPackStreams = 0; + p->NumFolders = 0; + + p->PackPositions = NULL; + SzBitUi32s_Init(&p->FolderCRCs); + + p->FoCodersOffsets = NULL; + p->FoStartPackStreamIndex = NULL; + p->FoToCoderUnpackSizes = NULL; + p->FoToMainUnpackSizeIndex = NULL; + p->CoderUnpackSizes = NULL; + + p->CodersData = NULL; +} + +static void SzAr_Free(CSzAr *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->PackPositions); + SzBitUi32s_Free(&p->FolderCRCs, alloc); + + IAlloc_Free(alloc, p->FoCodersOffsets); + IAlloc_Free(alloc, p->FoStartPackStreamIndex); + IAlloc_Free(alloc, p->FoToCoderUnpackSizes); + IAlloc_Free(alloc, p->FoToMainUnpackSizeIndex); + IAlloc_Free(alloc, p->CoderUnpackSizes); + + IAlloc_Free(alloc, p->CodersData); + + SzAr_Init(p); +} + + +static void SzArEx_Init(CSzArEx *p) +{ + SzAr_Init(&p->db); + + p->NumFiles = 0; + p->dataPos = 0; + + p->UnpackPositions = NULL; + p->IsDirs = NULL; + + p->FolderToFile = NULL; + p->FileToFolder = NULL; + + p->FileNameOffsets = NULL; + p->FileNames = NULL; + + SzBitUi32s_Init(&p->CRCs); + SzBitUi32s_Init(&p->Attribs); + /* SzBitUi32s_Init(&p->Parents); */ + SzBitUi64s_Init(&p->MTime); + SzBitUi64s_Init(&p->CTime); +} + +static void SzArEx_Free(CSzArEx *p, ISzAlloc *alloc) +{ + IAlloc_Free(alloc, p->UnpackPositions); + IAlloc_Free(alloc, p->IsDirs); + + IAlloc_Free(alloc, p->FolderToFile); + IAlloc_Free(alloc, p->FileToFolder); + + IAlloc_Free(alloc, p->FileNameOffsets); + IAlloc_Free(alloc, p->FileNames); + + SzBitUi32s_Free(&p->CRCs, alloc); + SzBitUi32s_Free(&p->Attribs, alloc); + /* SzBitUi32s_Free(&p->Parents, alloc); */ + SzBitUi64s_Free(&p->MTime, alloc); + SzBitUi64s_Free(&p->CTime, alloc); + + SzAr_Free(&p->db, alloc); + SzArEx_Init(p); +} + + +static int TestSignatureCandidate(const Byte *testBytes) +{ + unsigned i; + for (i = 0; i < k7zSignatureSize; i++) + if (testBytes[i] != k7zSignature[i]) + return 0; + return 1; +} + +#define SzData_Clear(p) { (p)->Data = NULL; (p)->Size = 0; } + +#define SZ_READ_BYTE_SD(_sd_, dest) if ((_sd_)->Size == 0) return SZ_ERROR_ARCHIVE; (_sd_)->Size--; dest = *(_sd_)->Data++; +#define SZ_READ_BYTE(dest) SZ_READ_BYTE_SD(sd, dest) +#define SZ_READ_BYTE_2(dest) if (sd.Size == 0) return SZ_ERROR_ARCHIVE; sd.Size--; dest = *sd.Data++; + +#define SKIP_DATA(sd, size) { sd->Size -= (size_t)(size); sd->Data += (size_t)(size); } +#define SKIP_DATA2(sd, size) { sd.Size -= (size_t)(size); sd.Data += (size_t)(size); } + +#define SZ_READ_32(dest) if (sd.Size < 4) return SZ_ERROR_ARCHIVE; \ + dest = GetUi32(sd.Data); SKIP_DATA2(sd, 4); + +static MY_NO_INLINE SRes ReadNumber(CSzData *sd, UInt64 *value) +{ + Byte firstByte, mask; + unsigned i; + UInt32 v; + + SZ_READ_BYTE(firstByte); + if ((firstByte & 0x80) == 0) + { + *value = firstByte; + return SZ_OK; + } + SZ_READ_BYTE(v); + if ((firstByte & 0x40) == 0) + { + *value = (((UInt32)firstByte & 0x3F) << 8) | v; + return SZ_OK; + } + SZ_READ_BYTE(mask); + *value = v | ((UInt32)mask << 8); + mask = 0x20; + for (i = 2; i < 8; i++) + { + Byte b; + if ((firstByte & mask) == 0) + { + UInt64 highPart = (unsigned)firstByte & (unsigned)(mask - 1); + *value |= (highPart << (8 * i)); + return SZ_OK; + } + SZ_READ_BYTE(b); + *value |= ((UInt64)b << (8 * i)); + mask >>= 1; + } + return SZ_OK; +} + + +static MY_NO_INLINE SRes SzReadNumber32(CSzData *sd, UInt32 *value) +{ + Byte firstByte; + UInt64 value64; + if (sd->Size == 0) + return SZ_ERROR_ARCHIVE; + firstByte = *sd->Data; + if ((firstByte & 0x80) == 0) + { + *value = firstByte; + sd->Data++; + sd->Size--; + return SZ_OK; + } + RINOK(ReadNumber(sd, &value64)); + if (value64 >= (UInt32)0x80000000 - 1) + return SZ_ERROR_UNSUPPORTED; + if (value64 >= ((UInt64)(1) << ((sizeof(size_t) - 1) * 8 + 4))) + return SZ_ERROR_UNSUPPORTED; + *value = (UInt32)value64; + return SZ_OK; +} + +#define ReadID(sd, value) ReadNumber(sd, value) + +static SRes SkipData(CSzData *sd) +{ + UInt64 size; + RINOK(ReadNumber(sd, &size)); + if (size > sd->Size) + return SZ_ERROR_ARCHIVE; + SKIP_DATA(sd, size); + return SZ_OK; +} + +static SRes WaitId(CSzData *sd, UInt32 id) +{ + for (;;) + { + UInt64 type; + RINOK(ReadID(sd, &type)); + if (type == id) + return SZ_OK; + if (type == k7zIdEnd) + return SZ_ERROR_ARCHIVE; + RINOK(SkipData(sd)); + } +} + +static SRes RememberBitVector(CSzData *sd, UInt32 numItems, const Byte **v) +{ + UInt32 numBytes = (numItems + 7) >> 3; + if (numBytes > sd->Size) + return SZ_ERROR_ARCHIVE; + *v = sd->Data; + SKIP_DATA(sd, numBytes); + return SZ_OK; +} + +static UInt32 CountDefinedBits(const Byte *bits, UInt32 numItems) +{ + Byte b = 0; + unsigned m = 0; + UInt32 sum = 0; + for (; numItems != 0; numItems--) + { + if (m == 0) + { + b = *bits++; + m = 8; + } + m--; + sum += ((b >> m) & 1); + } + return sum; +} + +static MY_NO_INLINE SRes ReadBitVector(CSzData *sd, UInt32 numItems, Byte **v, ISzAlloc *alloc) +{ + Byte allAreDefined; + Byte *v2; + UInt32 numBytes = (numItems + 7) >> 3; + *v = NULL; + SZ_READ_BYTE(allAreDefined); + if (numBytes == 0) + return SZ_OK; + if (allAreDefined == 0) + { + if (numBytes > sd->Size) + return SZ_ERROR_ARCHIVE; + MY_ALLOC_AND_CPY(*v, numBytes, sd->Data, alloc); + SKIP_DATA(sd, numBytes); + return SZ_OK; + } + MY_ALLOC(Byte, *v, numBytes, alloc); + v2 = *v; + memset(v2, 0xFF, (size_t)numBytes); + { + unsigned numBits = (unsigned)numItems & 7; + if (numBits != 0) + v2[numBytes - 1] = (Byte)((((UInt32)1 << numBits) - 1) << (8 - numBits)); + } + return SZ_OK; +} + +static MY_NO_INLINE SRes ReadUi32s(CSzData *sd2, UInt32 numItems, CSzBitUi32s *crcs, ISzAlloc *alloc) +{ + UInt32 i; + CSzData sd; + UInt32 *vals; + const Byte *defs; + MY_ALLOC_ZE(UInt32, crcs->Vals, numItems, alloc); + sd = *sd2; + defs = crcs->Defs; + vals = crcs->Vals; + for (i = 0; i < numItems; i++) + if (SzBitArray_Check(defs, i)) + { + SZ_READ_32(vals[i]); + } + else + vals[i] = 0; + *sd2 = sd; + return SZ_OK; +} + +static SRes ReadBitUi32s(CSzData *sd, UInt32 numItems, CSzBitUi32s *crcs, ISzAlloc *alloc) +{ + SzBitUi32s_Free(crcs, alloc); + RINOK(ReadBitVector(sd, numItems, &crcs->Defs, alloc)); + return ReadUi32s(sd, numItems, crcs, alloc); +} + +static SRes SkipBitUi32s(CSzData *sd, UInt32 numItems) +{ + Byte allAreDefined; + UInt32 numDefined = numItems; + SZ_READ_BYTE(allAreDefined); + if (!allAreDefined) + { + size_t numBytes = (numItems + 7) >> 3; + if (numBytes > sd->Size) + return SZ_ERROR_ARCHIVE; + numDefined = CountDefinedBits(sd->Data, numItems); + SKIP_DATA(sd, numBytes); + } + if (numDefined > (sd->Size >> 2)) + return SZ_ERROR_ARCHIVE; + SKIP_DATA(sd, (size_t)numDefined * 4); + return SZ_OK; +} + +static SRes ReadPackInfo(CSzAr *p, CSzData *sd, ISzAlloc *alloc) +{ + RINOK(SzReadNumber32(sd, &p->NumPackStreams)); + + RINOK(WaitId(sd, k7zIdSize)); + MY_ALLOC(UInt64, p->PackPositions, (size_t)p->NumPackStreams + 1, alloc); + { + UInt64 sum = 0; + UInt32 i; + UInt32 numPackStreams = p->NumPackStreams; + for (i = 0; i < numPackStreams; i++) + { + UInt64 packSize; + p->PackPositions[i] = sum; + RINOK(ReadNumber(sd, &packSize)); + sum += packSize; + if (sum < packSize) + return SZ_ERROR_ARCHIVE; + } + p->PackPositions[i] = sum; + } + + for (;;) + { + UInt64 type; + RINOK(ReadID(sd, &type)); + if (type == k7zIdEnd) + return SZ_OK; + if (type == k7zIdCRC) + { + /* CRC of packed streams is unused now */ + RINOK(SkipBitUi32s(sd, p->NumPackStreams)); + continue; + } + RINOK(SkipData(sd)); + } +} + +/* +static SRes SzReadSwitch(CSzData *sd) +{ + Byte external; + RINOK(SzReadByte(sd, &external)); + return (external == 0) ? SZ_OK: SZ_ERROR_UNSUPPORTED; +} +*/ + +#define k_NumCodersStreams_in_Folder_MAX (SZ_NUM_BONDS_IN_FOLDER_MAX + SZ_NUM_PACK_STREAMS_IN_FOLDER_MAX) + +static SRes SzGetNextFolderItem(CSzFolder *f, CSzData *sd) +{ + UInt32 numCoders, i; + UInt32 numInStreams = 0; + const Byte *dataStart = sd->Data; + + f->NumCoders = 0; + f->NumBonds = 0; + f->NumPackStreams = 0; + f->UnpackStream = 0; + + RINOK(SzReadNumber32(sd, &numCoders)); + if (numCoders == 0 || numCoders > SZ_NUM_CODERS_IN_FOLDER_MAX) + return SZ_ERROR_UNSUPPORTED; + + for (i = 0; i < numCoders; i++) + { + Byte mainByte; + CSzCoderInfo *coder = f->Coders + i; + unsigned idSize, j; + UInt64 id; + + SZ_READ_BYTE(mainByte); + if ((mainByte & 0xC0) != 0) + return SZ_ERROR_UNSUPPORTED; + + idSize = (unsigned)(mainByte & 0xF); + if (idSize > sizeof(id)) + return SZ_ERROR_UNSUPPORTED; + if (idSize > sd->Size) + return SZ_ERROR_ARCHIVE; + id = 0; + for (j = 0; j < idSize; j++) + { + id = ((id << 8) | *sd->Data); + sd->Data++; + sd->Size--; + } + if (id > UINT64_CONST(0xFFFFFFFF)) + return SZ_ERROR_UNSUPPORTED; + coder->MethodID = (UInt32)id; + + coder->NumStreams = 1; + coder->PropsOffset = 0; + coder->PropsSize = 0; + + if ((mainByte & 0x10) != 0) + { + UInt32 numStreams; + + RINOK(SzReadNumber32(sd, &numStreams)); + if (numStreams > k_NumCodersStreams_in_Folder_MAX) + return SZ_ERROR_UNSUPPORTED; + coder->NumStreams = (Byte)numStreams; + + RINOK(SzReadNumber32(sd, &numStreams)); + if (numStreams != 1) + return SZ_ERROR_UNSUPPORTED; + } + + numInStreams += coder->NumStreams; + + if (numInStreams > k_NumCodersStreams_in_Folder_MAX) + return SZ_ERROR_UNSUPPORTED; + + if ((mainByte & 0x20) != 0) + { + UInt32 propsSize = 0; + RINOK(SzReadNumber32(sd, &propsSize)); + if (propsSize > sd->Size) + return SZ_ERROR_ARCHIVE; + if (propsSize >= 0x80) + return SZ_ERROR_UNSUPPORTED; + coder->PropsOffset = sd->Data - dataStart; + coder->PropsSize = (Byte)propsSize; + sd->Data += (size_t)propsSize; + sd->Size -= (size_t)propsSize; + } + } + + /* + if (numInStreams == 1 && numCoders == 1) + { + f->NumPackStreams = 1; + f->PackStreams[0] = 0; + } + else + */ + { + Byte streamUsed[k_NumCodersStreams_in_Folder_MAX]; + UInt32 numBonds, numPackStreams; + + numBonds = numCoders - 1; + if (numInStreams < numBonds) + return SZ_ERROR_ARCHIVE; + if (numBonds > SZ_NUM_BONDS_IN_FOLDER_MAX) + return SZ_ERROR_UNSUPPORTED; + f->NumBonds = numBonds; + + numPackStreams = numInStreams - numBonds; + if (numPackStreams > SZ_NUM_PACK_STREAMS_IN_FOLDER_MAX) + return SZ_ERROR_UNSUPPORTED; + f->NumPackStreams = numPackStreams; + + for (i = 0; i < numInStreams; i++) + streamUsed[i] = False; + + if (numBonds != 0) + { + Byte coderUsed[SZ_NUM_CODERS_IN_FOLDER_MAX]; + + for (i = 0; i < numCoders; i++) + coderUsed[i] = False; + + for (i = 0; i < numBonds; i++) + { + CSzBond *bp = f->Bonds + i; + + RINOK(SzReadNumber32(sd, &bp->InIndex)); + if (bp->InIndex >= numInStreams || streamUsed[bp->InIndex]) + return SZ_ERROR_ARCHIVE; + streamUsed[bp->InIndex] = True; + + RINOK(SzReadNumber32(sd, &bp->OutIndex)); + if (bp->OutIndex >= numCoders || coderUsed[bp->OutIndex]) + return SZ_ERROR_ARCHIVE; + coderUsed[bp->OutIndex] = True; + } + + for (i = 0; i < numCoders; i++) + if (!coderUsed[i]) + { + f->UnpackStream = i; + break; + } + + if (i == numCoders) + return SZ_ERROR_ARCHIVE; + } + + if (numPackStreams == 1) + { + for (i = 0; i < numInStreams; i++) + if (!streamUsed[i]) + break; + if (i == numInStreams) + return SZ_ERROR_ARCHIVE; + f->PackStreams[0] = i; + } + else + for (i = 0; i < numPackStreams; i++) + { + UInt32 index; + RINOK(SzReadNumber32(sd, &index)); + if (index >= numInStreams || streamUsed[index]) + return SZ_ERROR_ARCHIVE; + streamUsed[index] = True; + f->PackStreams[i] = index; + } + } + + f->NumCoders = numCoders; + + return SZ_OK; +} + + +static MY_NO_INLINE SRes SkipNumbers(CSzData *sd2, UInt32 num) +{ + CSzData sd; + sd = *sd2; + for (; num != 0; num--) + { + Byte firstByte, mask; + unsigned i; + SZ_READ_BYTE_2(firstByte); + if ((firstByte & 0x80) == 0) + continue; + if ((firstByte & 0x40) == 0) + { + if (sd.Size == 0) + return SZ_ERROR_ARCHIVE; + sd.Size--; + sd.Data++; + continue; + } + mask = 0x20; + for (i = 2; i < 8 && (firstByte & mask) != 0; i++) + mask >>= 1; + if (i > sd.Size) + return SZ_ERROR_ARCHIVE; + SKIP_DATA2(sd, i); + } + *sd2 = sd; + return SZ_OK; +} + + +#define k_Scan_NumCoders_MAX 64 +#define k_Scan_NumCodersStreams_in_Folder_MAX 64 + + +static SRes ReadUnpackInfo(CSzAr *p, + CSzData *sd2, + UInt32 numFoldersMax, + const CBuf *tempBufs, UInt32 numTempBufs, + ISzAlloc *alloc) +{ + CSzData sd; + + UInt32 fo, numFolders, numCodersOutStreams, packStreamIndex; + const Byte *startBufPtr; + Byte external; + + RINOK(WaitId(sd2, k7zIdFolder)); + + RINOK(SzReadNumber32(sd2, &numFolders)); + if (numFolders > numFoldersMax) + return SZ_ERROR_UNSUPPORTED; + p->NumFolders = numFolders; + + SZ_READ_BYTE_SD(sd2, external); + if (external == 0) + sd = *sd2; + else + { + UInt32 index; + RINOK(SzReadNumber32(sd2, &index)); + if (index >= numTempBufs) + return SZ_ERROR_ARCHIVE; + sd.Data = tempBufs[index].data; + sd.Size = tempBufs[index].size; + } + + MY_ALLOC(size_t, p->FoCodersOffsets, (size_t)numFolders + 1, alloc); + MY_ALLOC(UInt32, p->FoStartPackStreamIndex, (size_t)numFolders + 1, alloc); + MY_ALLOC(UInt32, p->FoToCoderUnpackSizes, (size_t)numFolders + 1, alloc); + MY_ALLOC(Byte, p->FoToMainUnpackSizeIndex, (size_t)numFolders, alloc); + + startBufPtr = sd.Data; + + packStreamIndex = 0; + numCodersOutStreams = 0; + + for (fo = 0; fo < numFolders; fo++) + { + UInt32 numCoders, ci, numInStreams = 0; + + p->FoCodersOffsets[fo] = sd.Data - startBufPtr; + + RINOK(SzReadNumber32(&sd, &numCoders)); + if (numCoders == 0 || numCoders > k_Scan_NumCoders_MAX) + return SZ_ERROR_UNSUPPORTED; + + for (ci = 0; ci < numCoders; ci++) + { + Byte mainByte; + unsigned idSize; + UInt32 coderInStreams; + + SZ_READ_BYTE_2(mainByte); + if ((mainByte & 0xC0) != 0) + return SZ_ERROR_UNSUPPORTED; + idSize = (mainByte & 0xF); + if (idSize > 8) + return SZ_ERROR_UNSUPPORTED; + if (idSize > sd.Size) + return SZ_ERROR_ARCHIVE; + SKIP_DATA2(sd, idSize); + + coderInStreams = 1; + + if ((mainByte & 0x10) != 0) + { + UInt32 coderOutStreams; + RINOK(SzReadNumber32(&sd, &coderInStreams)); + RINOK(SzReadNumber32(&sd, &coderOutStreams)); + if (coderInStreams > k_Scan_NumCodersStreams_in_Folder_MAX || coderOutStreams != 1) + return SZ_ERROR_UNSUPPORTED; + } + + numInStreams += coderInStreams; + + if ((mainByte & 0x20) != 0) + { + UInt32 propsSize; + RINOK(SzReadNumber32(&sd, &propsSize)); + if (propsSize > sd.Size) + return SZ_ERROR_ARCHIVE; + SKIP_DATA2(sd, propsSize); + } + } + + { + UInt32 indexOfMainStream = 0; + UInt32 numPackStreams = 1; + + if (numCoders != 1 || numInStreams != 1) + { + Byte streamUsed[k_Scan_NumCodersStreams_in_Folder_MAX]; + Byte coderUsed[k_Scan_NumCoders_MAX]; + + UInt32 i; + UInt32 numBonds = numCoders - 1; + if (numInStreams < numBonds) + return SZ_ERROR_ARCHIVE; + + if (numInStreams > k_Scan_NumCodersStreams_in_Folder_MAX) + return SZ_ERROR_UNSUPPORTED; + + for (i = 0; i < numInStreams; i++) + streamUsed[i] = False; + for (i = 0; i < numCoders; i++) + coderUsed[i] = False; + + for (i = 0; i < numBonds; i++) + { + UInt32 index; + + RINOK(SzReadNumber32(&sd, &index)); + if (index >= numInStreams || streamUsed[index]) + return SZ_ERROR_ARCHIVE; + streamUsed[index] = True; + + RINOK(SzReadNumber32(&sd, &index)); + if (index >= numCoders || coderUsed[index]) + return SZ_ERROR_ARCHIVE; + coderUsed[index] = True; + } + + numPackStreams = numInStreams - numBonds; + + if (numPackStreams != 1) + for (i = 0; i < numPackStreams; i++) + { + UInt32 index; + RINOK(SzReadNumber32(&sd, &index)); + if (index >= numInStreams || streamUsed[index]) + return SZ_ERROR_ARCHIVE; + streamUsed[index] = True; + } + + for (i = 0; i < numCoders; i++) + if (!coderUsed[i]) + { + indexOfMainStream = i; + break; + } + + if (i == numCoders) + return SZ_ERROR_ARCHIVE; + } + + p->FoStartPackStreamIndex[fo] = packStreamIndex; + p->FoToCoderUnpackSizes[fo] = numCodersOutStreams; + p->FoToMainUnpackSizeIndex[fo] = (Byte)indexOfMainStream; + numCodersOutStreams += numCoders; + if (numCodersOutStreams < numCoders) + return SZ_ERROR_UNSUPPORTED; + if (numPackStreams > p->NumPackStreams - packStreamIndex) + return SZ_ERROR_ARCHIVE; + packStreamIndex += numPackStreams; + } + } + + p->FoToCoderUnpackSizes[fo] = numCodersOutStreams; + + { + size_t dataSize = sd.Data - startBufPtr; + p->FoStartPackStreamIndex[fo] = packStreamIndex; + p->FoCodersOffsets[fo] = dataSize; + MY_ALLOC_ZE_AND_CPY(p->CodersData, dataSize, startBufPtr, alloc); + } + + if (external != 0) + { + if (sd.Size != 0) + return SZ_ERROR_ARCHIVE; + sd = *sd2; + } + + RINOK(WaitId(&sd, k7zIdCodersUnpackSize)); + + MY_ALLOC_ZE(UInt64, p->CoderUnpackSizes, (size_t)numCodersOutStreams, alloc); + { + UInt32 i; + for (i = 0; i < numCodersOutStreams; i++) + { + RINOK(ReadNumber(&sd, p->CoderUnpackSizes + i)); + } + } + + for (;;) + { + UInt64 type; + RINOK(ReadID(&sd, &type)); + if (type == k7zIdEnd) + { + *sd2 = sd; + return SZ_OK; + } + if (type == k7zIdCRC) + { + RINOK(ReadBitUi32s(&sd, numFolders, &p->FolderCRCs, alloc)); + continue; + } + RINOK(SkipData(&sd)); + } +} + + +static UInt64 SzAr_GetFolderUnpackSize(const CSzAr *p, UInt32 folderIndex) +{ + return p->CoderUnpackSizes[p->FoToCoderUnpackSizes[folderIndex] + p->FoToMainUnpackSizeIndex[folderIndex]]; +} + + +typedef struct +{ + UInt32 NumTotalSubStreams; + UInt32 NumSubDigests; + CSzData sdNumSubStreams; + CSzData sdSizes; + CSzData sdCRCs; +} CSubStreamInfo; + + +static SRes ReadSubStreamsInfo(CSzAr *p, CSzData *sd, CSubStreamInfo *ssi) +{ + UInt64 type = 0; + UInt32 numSubDigests = 0; + UInt32 numFolders = p->NumFolders; + UInt32 numUnpackStreams = numFolders; + UInt32 numUnpackSizesInData = 0; + + for (;;) + { + RINOK(ReadID(sd, &type)); + if (type == k7zIdNumUnpackStream) + { + UInt32 i; + ssi->sdNumSubStreams.Data = sd->Data; + numUnpackStreams = 0; + numSubDigests = 0; + for (i = 0; i < numFolders; i++) + { + UInt32 numStreams; + RINOK(SzReadNumber32(sd, &numStreams)); + if (numUnpackStreams > numUnpackStreams + numStreams) + return SZ_ERROR_UNSUPPORTED; + numUnpackStreams += numStreams; + if (numStreams != 0) + numUnpackSizesInData += (numStreams - 1); + if (numStreams != 1 || !SzBitWithVals_Check(&p->FolderCRCs, i)) + numSubDigests += numStreams; + } + ssi->sdNumSubStreams.Size = sd->Data - ssi->sdNumSubStreams.Data; + continue; + } + if (type == k7zIdCRC || type == k7zIdSize || type == k7zIdEnd) + break; + RINOK(SkipData(sd)); + } + + if (!ssi->sdNumSubStreams.Data) + { + numSubDigests = numFolders; + if (p->FolderCRCs.Defs) + numSubDigests = numFolders - CountDefinedBits(p->FolderCRCs.Defs, numFolders); + } + + ssi->NumTotalSubStreams = numUnpackStreams; + ssi->NumSubDigests = numSubDigests; + + if (type == k7zIdSize) + { + ssi->sdSizes.Data = sd->Data; + RINOK(SkipNumbers(sd, numUnpackSizesInData)); + ssi->sdSizes.Size = sd->Data - ssi->sdSizes.Data; + RINOK(ReadID(sd, &type)); + } + + for (;;) + { + if (type == k7zIdEnd) + return SZ_OK; + if (type == k7zIdCRC) + { + ssi->sdCRCs.Data = sd->Data; + RINOK(SkipBitUi32s(sd, numSubDigests)); + ssi->sdCRCs.Size = sd->Data - ssi->sdCRCs.Data; + } + else + { + RINOK(SkipData(sd)); + } + RINOK(ReadID(sd, &type)); + } +} + +static SRes SzReadStreamsInfo(CSzAr *p, + CSzData *sd, + UInt32 numFoldersMax, const CBuf *tempBufs, UInt32 numTempBufs, + UInt64 *dataOffset, + CSubStreamInfo *ssi, + ISzAlloc *alloc) +{ + UInt64 type; + + SzData_Clear(&ssi->sdSizes); + SzData_Clear(&ssi->sdCRCs); + SzData_Clear(&ssi->sdNumSubStreams); + + *dataOffset = 0; + RINOK(ReadID(sd, &type)); + if (type == k7zIdPackInfo) + { + RINOK(ReadNumber(sd, dataOffset)); + RINOK(ReadPackInfo(p, sd, alloc)); + RINOK(ReadID(sd, &type)); + } + if (type == k7zIdUnpackInfo) + { + RINOK(ReadUnpackInfo(p, sd, numFoldersMax, tempBufs, numTempBufs, alloc)); + RINOK(ReadID(sd, &type)); + } + if (type == k7zIdSubStreamsInfo) + { + RINOK(ReadSubStreamsInfo(p, sd, ssi)); + RINOK(ReadID(sd, &type)); + } + else + { + ssi->NumTotalSubStreams = p->NumFolders; + /* ssi->NumSubDigests = 0; */ + } + + return (type == k7zIdEnd ? SZ_OK : SZ_ERROR_UNSUPPORTED); +} + +static SRes SzReadAndDecodePackedStreams( + ILookInStream *inStream, + CSzData *sd, + CBuf *tempBufs, + UInt32 numFoldersMax, + UInt64 baseOffset, + CSzAr *p, + ISzAlloc *allocTemp) +{ + UInt64 dataStartPos = 0; + UInt32 fo; + CSubStreamInfo ssi; + UInt32 numFolders; + + RINOK(SzReadStreamsInfo(p, sd, numFoldersMax, NULL, 0, &dataStartPos, &ssi, allocTemp)); + + numFolders = p->NumFolders; + if (numFolders == 0) + return SZ_ERROR_ARCHIVE; + else if (numFolders > numFoldersMax) + return SZ_ERROR_UNSUPPORTED; + + dataStartPos += baseOffset; + + for (fo = 0; fo < numFolders; fo++) + Buf_Init(tempBufs + fo); + + for (fo = 0; fo < numFolders; fo++) + { + CBuf *tempBuf = tempBufs + fo; + UInt64 unpackSize = SzAr_GetFolderUnpackSize(p, fo); + if ((size_t)unpackSize != unpackSize) + return SZ_ERROR_MEM; + if (!Buf_Create(tempBuf, (size_t)unpackSize, allocTemp)) + return SZ_ERROR_MEM; + } + + for (fo = 0; fo < numFolders; fo++) + { + const CBuf *tempBuf = tempBufs + fo; + RINOK(LookInStream_SeekTo(inStream, dataStartPos)); + RINOK(SzAr_DecodeFolder(p, fo, inStream, dataStartPos, tempBuf->data, tempBuf->size, allocTemp)); + } + + return SZ_OK; +} + +static SRes SzReadFileNames(const Byte *data, size_t size, UInt32 numFiles, size_t *offsets) +{ + size_t pos = 0; + *offsets++ = 0; + if (numFiles == 0) + return (size == 0) ? SZ_OK : SZ_ERROR_ARCHIVE; + if (size < 2) + return SZ_ERROR_ARCHIVE; + if (data[size - 2] != 0 || data[size - 1] != 0) + return SZ_ERROR_ARCHIVE; + do + { + const Byte *p; + if (pos == size) + return SZ_ERROR_ARCHIVE; + for (p = data + pos; + #ifdef _WIN32 + *(const UInt16 *)p != 0 + #else + p[0] != 0 || p[1] != 0 + #endif + ; p += 2); + pos = p - data + 2; + *offsets++ = (pos >> 1); + } + while (--numFiles); + return (pos == size) ? SZ_OK : SZ_ERROR_ARCHIVE; +} + +static MY_NO_INLINE SRes ReadTime(CSzBitUi64s *p, UInt32 num, + CSzData *sd2, + const CBuf *tempBufs, UInt32 numTempBufs, + ISzAlloc *alloc) +{ + CSzData sd; + UInt32 i; + CNtfsFileTime *vals; + Byte *defs; + Byte external; + + RINOK(ReadBitVector(sd2, num, &p->Defs, alloc)); + + SZ_READ_BYTE_SD(sd2, external); + if (external == 0) + sd = *sd2; + else + { + UInt32 index; + RINOK(SzReadNumber32(sd2, &index)); + if (index >= numTempBufs) + return SZ_ERROR_ARCHIVE; + sd.Data = tempBufs[index].data; + sd.Size = tempBufs[index].size; + } + + MY_ALLOC_ZE(CNtfsFileTime, p->Vals, num, alloc); + vals = p->Vals; + defs = p->Defs; + for (i = 0; i < num; i++) + if (SzBitArray_Check(defs, i)) + { + if (sd.Size < 8) + return SZ_ERROR_ARCHIVE; + vals[i].Low = GetUi32(sd.Data); + vals[i].High = GetUi32(sd.Data + 4); + SKIP_DATA2(sd, 8); + } + else + vals[i].High = vals[i].Low = 0; + + if (external == 0) + *sd2 = sd; + + return SZ_OK; +} + + +#define NUM_ADDITIONAL_STREAMS_MAX 8 + + +static SRes SzReadHeader2( + CSzArEx *p, /* allocMain */ + CSzData *sd, + ILookInStream *inStream, + CBuf *tempBufs, UInt32 *numTempBufs, + ISzAlloc *allocMain, + ISzAlloc *allocTemp + ) +{ + CSubStreamInfo ssi; + +{ + UInt64 type; + + SzData_Clear(&ssi.sdSizes); + SzData_Clear(&ssi.sdCRCs); + SzData_Clear(&ssi.sdNumSubStreams); + + ssi.NumSubDigests = 0; + ssi.NumTotalSubStreams = 0; + + RINOK(ReadID(sd, &type)); + + if (type == k7zIdArchiveProperties) + { + for (;;) + { + UInt64 type2; + RINOK(ReadID(sd, &type2)); + if (type2 == k7zIdEnd) + break; + RINOK(SkipData(sd)); + } + RINOK(ReadID(sd, &type)); + } + + if (type == k7zIdAdditionalStreamsInfo) + { + CSzAr tempAr; + SRes res; + + SzAr_Init(&tempAr); + res = SzReadAndDecodePackedStreams(inStream, sd, tempBufs, NUM_ADDITIONAL_STREAMS_MAX, + p->startPosAfterHeader, &tempAr, allocTemp); + *numTempBufs = tempAr.NumFolders; + SzAr_Free(&tempAr, allocTemp); + + if (res != SZ_OK) + return res; + RINOK(ReadID(sd, &type)); + } + + if (type == k7zIdMainStreamsInfo) + { + RINOK(SzReadStreamsInfo(&p->db, sd, (UInt32)1 << 30, tempBufs, *numTempBufs, + &p->dataPos, &ssi, allocMain)); + p->dataPos += p->startPosAfterHeader; + RINOK(ReadID(sd, &type)); + } + + if (type == k7zIdEnd) + { + return SZ_OK; + } + + if (type != k7zIdFilesInfo) + return SZ_ERROR_ARCHIVE; +} + +{ + UInt32 numFiles = 0; + UInt32 numEmptyStreams = 0; + const Byte *emptyStreams = NULL; + const Byte *emptyFiles = NULL; + + RINOK(SzReadNumber32(sd, &numFiles)); + p->NumFiles = numFiles; + + for (;;) + { + UInt64 type; + UInt64 size; + RINOK(ReadID(sd, &type)); + if (type == k7zIdEnd) + break; + RINOK(ReadNumber(sd, &size)); + if (size > sd->Size) + return SZ_ERROR_ARCHIVE; + + if (type >= ((UInt32)1 << 8)) + { + SKIP_DATA(sd, size); + } + else switch ((unsigned)type) + { + case k7zIdName: + { + size_t namesSize; + const Byte *namesData; + Byte external; + + SZ_READ_BYTE(external); + if (external == 0) + { + namesSize = (size_t)size - 1; + namesData = sd->Data; + } + else + { + UInt32 index; + RINOK(SzReadNumber32(sd, &index)); + if (index >= *numTempBufs) + return SZ_ERROR_ARCHIVE; + namesData = (tempBufs)[index].data; + namesSize = (tempBufs)[index].size; + } + + if ((namesSize & 1) != 0) + return SZ_ERROR_ARCHIVE; + MY_ALLOC(size_t, p->FileNameOffsets, numFiles + 1, allocMain); + MY_ALLOC_ZE_AND_CPY(p->FileNames, namesSize, namesData, allocMain); + RINOK(SzReadFileNames(p->FileNames, namesSize, numFiles, p->FileNameOffsets)) + if (external == 0) + { + SKIP_DATA(sd, namesSize); + } + break; + } + case k7zIdEmptyStream: + { + RINOK(RememberBitVector(sd, numFiles, &emptyStreams)); + numEmptyStreams = CountDefinedBits(emptyStreams, numFiles); + emptyFiles = NULL; + break; + } + case k7zIdEmptyFile: + { + RINOK(RememberBitVector(sd, numEmptyStreams, &emptyFiles)); + break; + } + case k7zIdWinAttrib: + { + Byte external; + CSzData sdSwitch; + CSzData *sdPtr; + SzBitUi32s_Free(&p->Attribs, allocMain); + RINOK(ReadBitVector(sd, numFiles, &p->Attribs.Defs, allocMain)); + + SZ_READ_BYTE(external); + if (external == 0) + sdPtr = sd; + else + { + UInt32 index; + RINOK(SzReadNumber32(sd, &index)); + if (index >= *numTempBufs) + return SZ_ERROR_ARCHIVE; + sdSwitch.Data = (tempBufs)[index].data; + sdSwitch.Size = (tempBufs)[index].size; + sdPtr = &sdSwitch; + } + RINOK(ReadUi32s(sdPtr, numFiles, &p->Attribs, allocMain)); + break; + } + /* + case k7zParent: + { + SzBitUi32s_Free(&p->Parents, allocMain); + RINOK(ReadBitVector(sd, numFiles, &p->Parents.Defs, allocMain)); + RINOK(SzReadSwitch(sd)); + RINOK(ReadUi32s(sd, numFiles, &p->Parents, allocMain)); + break; + } + */ + case k7zIdMTime: RINOK(ReadTime(&p->MTime, numFiles, sd, tempBufs, *numTempBufs, allocMain)); break; + case k7zIdCTime: RINOK(ReadTime(&p->CTime, numFiles, sd, tempBufs, *numTempBufs, allocMain)); break; + default: + { + SKIP_DATA(sd, size); + } + } + } + + if (numFiles - numEmptyStreams != ssi.NumTotalSubStreams) + return SZ_ERROR_ARCHIVE; + + for (;;) + { + UInt64 type; + RINOK(ReadID(sd, &type)); + if (type == k7zIdEnd) + break; + RINOK(SkipData(sd)); + } + + { + UInt32 i; + UInt32 emptyFileIndex = 0; + UInt32 folderIndex = 0; + UInt32 remSubStreams = 0; + UInt32 numSubStreams = 0; + UInt64 unpackPos = 0; + const Byte *digestsDefs = NULL; + const Byte *digestsVals = NULL; + UInt32 digestsValsIndex = 0; + UInt32 digestIndex; + Byte allDigestsDefined = 0; + Byte isDirMask = 0; + Byte crcMask = 0; + Byte mask = 0x80; + + MY_ALLOC(UInt32, p->FolderToFile, p->db.NumFolders + 1, allocMain); + MY_ALLOC_ZE(UInt32, p->FileToFolder, p->NumFiles, allocMain); + MY_ALLOC(UInt64, p->UnpackPositions, p->NumFiles + 1, allocMain); + MY_ALLOC_ZE(Byte, p->IsDirs, (p->NumFiles + 7) >> 3, allocMain); + + RINOK(SzBitUi32s_Alloc(&p->CRCs, p->NumFiles, allocMain)); + + if (ssi.sdCRCs.Size != 0) + { + SZ_READ_BYTE_SD(&ssi.sdCRCs, allDigestsDefined); + if (allDigestsDefined) + digestsVals = ssi.sdCRCs.Data; + else + { + size_t numBytes = (ssi.NumSubDigests + 7) >> 3; + digestsDefs = ssi.sdCRCs.Data; + digestsVals = digestsDefs + numBytes; + } + } + + digestIndex = 0; + + for (i = 0; i < numFiles; i++, mask >>= 1) + { + if (mask == 0) + { + UInt32 byteIndex = (i - 1) >> 3; + p->IsDirs[byteIndex] = isDirMask; + p->CRCs.Defs[byteIndex] = crcMask; + isDirMask = 0; + crcMask = 0; + mask = 0x80; + } + + p->UnpackPositions[i] = unpackPos; + p->CRCs.Vals[i] = 0; + + if (emptyStreams && SzBitArray_Check(emptyStreams, i)) + { + if (emptyFiles) + { + if (!SzBitArray_Check(emptyFiles, emptyFileIndex)) + isDirMask |= mask; + emptyFileIndex++; + } + else + isDirMask |= mask; + if (remSubStreams == 0) + { + p->FileToFolder[i] = (UInt32)-1; + continue; + } + } + + if (remSubStreams == 0) + { + for (;;) + { + if (folderIndex >= p->db.NumFolders) + return SZ_ERROR_ARCHIVE; + p->FolderToFile[folderIndex] = i; + numSubStreams = 1; + if (ssi.sdNumSubStreams.Data) + { + RINOK(SzReadNumber32(&ssi.sdNumSubStreams, &numSubStreams)); + } + remSubStreams = numSubStreams; + if (numSubStreams != 0) + break; + { + UInt64 folderUnpackSize = SzAr_GetFolderUnpackSize(&p->db, folderIndex); + unpackPos += folderUnpackSize; + if (unpackPos < folderUnpackSize) + return SZ_ERROR_ARCHIVE; + } + + folderIndex++; + } + } + + p->FileToFolder[i] = folderIndex; + + if (emptyStreams && SzBitArray_Check(emptyStreams, i)) + continue; + + if (--remSubStreams == 0) + { + UInt64 folderUnpackSize = SzAr_GetFolderUnpackSize(&p->db, folderIndex); + UInt64 startFolderUnpackPos = p->UnpackPositions[p->FolderToFile[folderIndex]]; + if (folderUnpackSize < unpackPos - startFolderUnpackPos) + return SZ_ERROR_ARCHIVE; + unpackPos = startFolderUnpackPos + folderUnpackSize; + if (unpackPos < folderUnpackSize) + return SZ_ERROR_ARCHIVE; + + if (numSubStreams == 1 && SzBitWithVals_Check(&p->db.FolderCRCs, i)) + { + p->CRCs.Vals[i] = p->db.FolderCRCs.Vals[folderIndex]; + crcMask |= mask; + } + else if (allDigestsDefined || (digestsDefs && SzBitArray_Check(digestsDefs, digestIndex))) + { + p->CRCs.Vals[i] = GetUi32(digestsVals + (size_t)digestsValsIndex * 4); + digestsValsIndex++; + crcMask |= mask; + } + + folderIndex++; + } + else + { + UInt64 v; + RINOK(ReadNumber(&ssi.sdSizes, &v)); + unpackPos += v; + if (unpackPos < v) + return SZ_ERROR_ARCHIVE; + if (allDigestsDefined || (digestsDefs && SzBitArray_Check(digestsDefs, digestIndex))) + { + p->CRCs.Vals[i] = GetUi32(digestsVals + (size_t)digestsValsIndex * 4); + digestsValsIndex++; + crcMask |= mask; + } + } + } + + if (mask != 0x80) + { + UInt32 byteIndex = (i - 1) >> 3; + p->IsDirs[byteIndex] = isDirMask; + p->CRCs.Defs[byteIndex] = crcMask; + } + + p->UnpackPositions[i] = unpackPos; + + if (remSubStreams != 0) + return SZ_ERROR_ARCHIVE; + + for (;;) + { + p->FolderToFile[folderIndex] = i; + if (folderIndex >= p->db.NumFolders) + break; + if (!ssi.sdNumSubStreams.Data) + return SZ_ERROR_ARCHIVE; + RINOK(SzReadNumber32(&ssi.sdNumSubStreams, &numSubStreams)); + if (numSubStreams != 0) + return SZ_ERROR_ARCHIVE; + /* + { + UInt64 folderUnpackSize = SzAr_GetFolderUnpackSize(&p->db, folderIndex); + unpackPos += folderUnpackSize; + if (unpackPos < folderUnpackSize) + return SZ_ERROR_ARCHIVE; + } + */ + folderIndex++; + } + + if (ssi.sdNumSubStreams.Data && ssi.sdNumSubStreams.Size != 0) + return SZ_ERROR_ARCHIVE; + } +} + return SZ_OK; +} + + +static SRes SzReadHeader( + CSzArEx *p, + CSzData *sd, + ILookInStream *inStream, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt32 i; + UInt32 numTempBufs = 0; + SRes res; + CBuf tempBufs[NUM_ADDITIONAL_STREAMS_MAX]; + + for (i = 0; i < NUM_ADDITIONAL_STREAMS_MAX; i++) + Buf_Init(tempBufs + i); + + res = SzReadHeader2(p, sd, inStream, + tempBufs, &numTempBufs, + allocMain, allocTemp); + + for (i = 0; i < NUM_ADDITIONAL_STREAMS_MAX; i++) + Buf_Free(tempBufs + i, allocTemp); + + RINOK(res); + + if (sd->Size != 0) + return SZ_ERROR_FAIL; + + return res; +} + +static SRes SzArEx_Open2( + CSzArEx *p, + ILookInStream *inStream, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + Byte header[k7zStartHeaderSize]; + Int64 startArcPos; + UInt64 nextHeaderOffset, nextHeaderSize; + size_t nextHeaderSizeT; + UInt32 nextHeaderCRC; + CBuf buf; + SRes res; + + startArcPos = 0; + RINOK(inStream->Seek(inStream, &startArcPos, SZ_SEEK_CUR)); + + RINOK(LookInStream_Read2(inStream, header, k7zStartHeaderSize, SZ_ERROR_NO_ARCHIVE)); + + if (!TestSignatureCandidate(header)) + return SZ_ERROR_NO_ARCHIVE; + if (header[6] != k7zMajorVersion) + return SZ_ERROR_UNSUPPORTED; + + nextHeaderOffset = GetUi64(header + 12); + nextHeaderSize = GetUi64(header + 20); + nextHeaderCRC = GetUi32(header + 28); + + p->startPosAfterHeader = startArcPos + k7zStartHeaderSize; + + if (CrcCalc(header + 12, 20) != GetUi32(header + 8)) + return SZ_ERROR_CRC; + + nextHeaderSizeT = (size_t)nextHeaderSize; + if (nextHeaderSizeT != nextHeaderSize) + return SZ_ERROR_MEM; + if (nextHeaderSizeT == 0) + return SZ_OK; + if (nextHeaderOffset > nextHeaderOffset + nextHeaderSize || + nextHeaderOffset > nextHeaderOffset + nextHeaderSize + k7zStartHeaderSize) + return SZ_ERROR_NO_ARCHIVE; + + { + Int64 pos = 0; + RINOK(inStream->Seek(inStream, &pos, SZ_SEEK_END)); + if ((UInt64)pos < startArcPos + nextHeaderOffset || + (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset || + (UInt64)pos < startArcPos + k7zStartHeaderSize + nextHeaderOffset + nextHeaderSize) + return SZ_ERROR_INPUT_EOF; + } + + RINOK(LookInStream_SeekTo(inStream, startArcPos + k7zStartHeaderSize + nextHeaderOffset)); + + if (!Buf_Create(&buf, nextHeaderSizeT, allocTemp)) + return SZ_ERROR_MEM; + + res = LookInStream_Read(inStream, buf.data, nextHeaderSizeT); + + if (res == SZ_OK) + { + res = SZ_ERROR_ARCHIVE; + if (CrcCalc(buf.data, nextHeaderSizeT) == nextHeaderCRC) + { + CSzData sd; + UInt64 type; + sd.Data = buf.data; + sd.Size = buf.size; + + res = ReadID(&sd, &type); + + if (res == SZ_OK && type == k7zIdEncodedHeader) + { + CSzAr tempAr; + CBuf tempBuf; + Buf_Init(&tempBuf); + + SzAr_Init(&tempAr); + res = SzReadAndDecodePackedStreams(inStream, &sd, &tempBuf, 1, p->startPosAfterHeader, &tempAr, allocTemp); + SzAr_Free(&tempAr, allocTemp); + + if (res != SZ_OK) + { + Buf_Free(&tempBuf, allocTemp); + } + else + { + Buf_Free(&buf, allocTemp); + buf.data = tempBuf.data; + buf.size = tempBuf.size; + sd.Data = buf.data; + sd.Size = buf.size; + res = ReadID(&sd, &type); + } + } + + if (res == SZ_OK) + { + if (type == k7zIdHeader) + { + /* + CSzData sd2; + unsigned ttt; + for (ttt = 0; ttt < 40000; ttt++) + { + SzArEx_Free(p, allocMain); + sd2 = sd; + res = SzReadHeader(p, &sd2, inStream, allocMain, allocTemp); + if (res != SZ_OK) + break; + } + */ + res = SzReadHeader(p, &sd, inStream, allocMain, allocTemp); + } + else + res = SZ_ERROR_UNSUPPORTED; + } + } + } + + Buf_Free(&buf, allocTemp); + return res; +} + + +static SRes SzArEx_Open(CSzArEx *p, ILookInStream *inStream, + ISzAlloc *allocMain, ISzAlloc *allocTemp) +{ + SRes res = SzArEx_Open2(p, inStream, allocMain, allocTemp); + if (res != SZ_OK) + SzArEx_Free(p, allocMain); + return res; +} + + +static SRes SzArEx_Extract( + const CSzArEx *p, + ILookInStream *inStream, + UInt32 fileIndex, + UInt32 *blockIndex, + Byte **tempBuf, + size_t *outBufferSize, + size_t *offset, + size_t *outSizeProcessed, + ISzAlloc *allocMain, + ISzAlloc *allocTemp) +{ + UInt32 folderIndex = p->FileToFolder[fileIndex]; + SRes res = SZ_OK; + + *offset = 0; + *outSizeProcessed = 0; + + if (folderIndex == (UInt32)-1) + { + IAlloc_Free(allocMain, *tempBuf); + *blockIndex = folderIndex; + *tempBuf = NULL; + *outBufferSize = 0; + return SZ_OK; + } + + if (*tempBuf == NULL || *blockIndex != folderIndex) + { + UInt64 unpackSizeSpec = SzAr_GetFolderUnpackSize(&p->db, folderIndex); + /* + UInt64 unpackSizeSpec = + p->UnpackPositions[p->FolderToFile[folderIndex + 1]] - + p->UnpackPositions[p->FolderToFile[folderIndex]]; + */ + size_t unpackSize = (size_t)unpackSizeSpec; + + if (unpackSize != unpackSizeSpec) + return SZ_ERROR_MEM; + *blockIndex = folderIndex; + IAlloc_Free(allocMain, *tempBuf); + *tempBuf = NULL; + + if (res == SZ_OK) + { + *outBufferSize = unpackSize; + if (unpackSize != 0) + { + *tempBuf = (Byte *)IAlloc_Alloc(allocMain, unpackSize); + if (*tempBuf == NULL) + res = SZ_ERROR_MEM; + } + + if (res == SZ_OK) + { + res = SzAr_DecodeFolder(&p->db, folderIndex, + inStream, p->dataPos, *tempBuf, unpackSize, allocTemp); + } + } + } + + if (res == SZ_OK) + { + UInt64 unpackPos = p->UnpackPositions[fileIndex]; + *offset = (size_t)(unpackPos - p->UnpackPositions[p->FolderToFile[folderIndex]]); + *outSizeProcessed = (size_t)(p->UnpackPositions[fileIndex + 1] - unpackPos); + if (*offset + *outSizeProcessed > *outBufferSize) + return SZ_ERROR_FAIL; + if (SzBitWithVals_Check(&p->CRCs, fileIndex)) + if (CrcCalc(*tempBuf + *offset, *outSizeProcessed) != p->CRCs.Vals[fileIndex]) + res = SZ_ERROR_CRC; + } + + return res; +} + + +static size_t SzArEx_GetFileNameUtf16(const CSzArEx *p, size_t fileIndex, UInt16 *dest) +{ + size_t offs = p->FileNameOffsets[fileIndex]; + size_t len = p->FileNameOffsets[fileIndex + 1] - offs; + if (dest != 0) + { + size_t i; + const Byte *src = p->FileNames + offs * 2; + for (i = 0; i < len; i++) + dest[i] = GetUi16(src + i * 2); + } + return len; +} + +/* +static size_t SzArEx_GetFullNameLen(const CSzArEx *p, size_t fileIndex) +{ + size_t len; + if (!p->FileNameOffsets) + return 1; + len = 0; + for (;;) + { + UInt32 parent = (UInt32)(Int32)-1; + len += p->FileNameOffsets[fileIndex + 1] - p->FileNameOffsets[fileIndex]; + if SzBitWithVals_Check(&p->Parents, fileIndex) + parent = p->Parents.Vals[fileIndex]; + if (parent == (UInt32)(Int32)-1) + return len; + fileIndex = parent; + } +} + +static UInt16 *SzArEx_GetFullNameUtf16_Back(const CSzArEx *p, size_t fileIndex, UInt16 *dest) +{ + Bool needSlash; + if (!p->FileNameOffsets) + { + *(--dest) = 0; + return dest; + } + needSlash = False; + for (;;) + { + UInt32 parent = (UInt32)(Int32)-1; + size_t curLen = p->FileNameOffsets[fileIndex + 1] - p->FileNameOffsets[fileIndex]; + SzArEx_GetFileNameUtf16(p, fileIndex, dest - curLen); + if (needSlash) + *(dest - 1) = '/'; + needSlash = True; + dest -= curLen; + + if SzBitWithVals_Check(&p->Parents, fileIndex) + parent = p->Parents.Vals[fileIndex]; + if (parent == (UInt32)(Int32)-1) + return dest; + fileIndex = parent; + } +} +*/ + +/* 7zBuf.c -- Byte Buffer +2013-01-21 : Igor Pavlov : Public domain */ + +/* +#include "Precomp.h" + +#include "7zBuf.h" +*/ + +static void Buf_Init(CBuf *p) +{ + p->data = 0; + p->size = 0; +} + +static int Buf_Create(CBuf *p, size_t size, ISzAlloc *alloc) +{ + p->size = 0; + if (size == 0) + { + p->data = 0; + return 1; + } + p->data = (Byte *)alloc->Alloc(alloc, size); + if (p->data != 0) + { + p->size = size; + return 1; + } + return 0; +} + +static void Buf_Free(CBuf *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->data); + p->data = 0; + p->size = 0; +} + +/* 7zDec.c -- Decoding from 7z folder +2015-11-18 : Igor Pavlov : Public domain */ + +/* #define _7ZIP_PPMD_SUPPPORT */ + +/* +#include "Precomp.h" + +#include <string.h> + +#include "7z.h" +#include "7zCrc.h" + +#include "Bcj2.h" +#include "Bra.h" +#include "CpuArch.h" +#include "Delta.h" +#include "LzmaDec.h" +#include "Lzma2Dec.h" +#ifdef _7ZIP_PPMD_SUPPPORT +#include "Ppmd7.h" +#endif +*/ + +#define k_Copy 0 +#define k_Delta 3 +#define k_LZMA2 0x21 +#define k_LZMA 0x30101 +#define k_BCJ 0x3030103 +#define k_BCJ2 0x303011B +#define k_PPC 0x3030205 +#define k_IA64 0x3030401 +#define k_ARM 0x3030501 +#define k_ARMT 0x3030701 +#define k_SPARC 0x3030805 + + +#ifdef _7ZIP_PPMD_SUPPPORT + +#define k_PPMD 0x30401 + +typedef struct +{ + IByteIn p; + const Byte *cur; + const Byte *end; + const Byte *begin; + UInt64 processed; + Bool extra; + SRes res; + ILookInStream *inStream; +} CByteInToLook; + +static Byte ReadByte(void *pp) +{ + CByteInToLook *p = (CByteInToLook *)pp; + if (p->cur != p->end) + return *p->cur++; + if (p->res == SZ_OK) + { + size_t size = p->cur - p->begin; + p->processed += size; + p->res = p->inStream->Skip(p->inStream, size); + size = (1 << 25); + p->res = p->inStream->Look(p->inStream, (const void **)&p->begin, &size); + p->cur = p->begin; + p->end = p->begin + size; + if (size != 0) + return *p->cur++;; + } + p->extra = True; + return 0; +} + +static SRes SzDecodePpmd(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) +{ + CPpmd7 ppmd; + CByteInToLook s; + SRes res = SZ_OK; + + s.p.Read = ReadByte; + s.inStream = inStream; + s.begin = s.end = s.cur = NULL; + s.extra = False; + s.res = SZ_OK; + s.processed = 0; + + if (propsSize != 5) + return SZ_ERROR_UNSUPPORTED; + + { + unsigned order = props[0]; + UInt32 memSize = GetUi32(props + 1); + if (order < PPMD7_MIN_ORDER || + order > PPMD7_MAX_ORDER || + memSize < PPMD7_MIN_MEM_SIZE || + memSize > PPMD7_MAX_MEM_SIZE) + return SZ_ERROR_UNSUPPORTED; + Ppmd7_Construct(&ppmd); + if (!Ppmd7_Alloc(&ppmd, memSize, allocMain)) + return SZ_ERROR_MEM; + Ppmd7_Init(&ppmd, order); + } + { + CPpmd7z_RangeDec rc; + Ppmd7z_RangeDec_CreateVTable(&rc); + rc.Stream = &s.p; + if (!Ppmd7z_RangeDec_Init(&rc)) + res = SZ_ERROR_DATA; + else if (s.extra) + res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); + else + { + SizeT i; + for (i = 0; i < outSize; i++) + { + int sym = Ppmd7_DecodeSymbol(&ppmd, &rc.p); + if (s.extra || sym < 0) + break; + outBuffer[i] = (Byte)sym; + } + if (i != outSize) + res = (s.res != SZ_OK ? s.res : SZ_ERROR_DATA); + else if (s.processed + (s.cur - s.begin) != inSize || !Ppmd7z_RangeDec_IsFinishedOK(&rc)) + res = SZ_ERROR_DATA; + } + } + Ppmd7_Free(&ppmd, allocMain); + return res; +} + +#endif + + +static SRes SzDecodeLzma(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) +{ + CLzmaDec state; + SRes res = SZ_OK; + + LzmaDec_Construct(&state); + RINOK(LzmaDec_AllocateProbs(&state, props, propsSize, allocMain)); + state.dic = outBuffer; + state.dicBufSize = outSize; + LzmaDec_Init(&state); + + for (;;) + { + const void *inBuf = NULL; + size_t lookahead = (1 << 18); + if (lookahead > inSize) + lookahead = (size_t)inSize; + res = inStream->Look(inStream, &inBuf, &lookahead); + if (res != SZ_OK) + break; + + { + SizeT inProcessed = (SizeT)lookahead, dicPos = state.dicPos; + ELzmaStatus status; + res = LzmaDec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status); + lookahead -= inProcessed; + inSize -= inProcessed; + if (res != SZ_OK) + break; + + if (status == LZMA_STATUS_FINISHED_WITH_MARK) + { + if (outSize != state.dicPos || inSize != 0) + res = SZ_ERROR_DATA; + break; + } + + if (outSize == state.dicPos && inSize == 0 && status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) + break; + + if (inProcessed == 0 && dicPos == state.dicPos) + { + res = SZ_ERROR_DATA; + break; + } + + res = inStream->Skip((void *)inStream, inProcessed); + if (res != SZ_OK) + break; + } + } + + LzmaDec_FreeProbs(&state, allocMain); + return res; +} + + +#ifndef _7Z_NO_METHOD_LZMA2 + +static SRes SzDecodeLzma2(const Byte *props, unsigned propsSize, UInt64 inSize, ILookInStream *inStream, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain) +{ + CLzma2Dec state; + SRes res = SZ_OK; + + Lzma2Dec_Construct(&state); + if (propsSize != 1) + return SZ_ERROR_DATA; + RINOK(Lzma2Dec_AllocateProbs(&state, props[0], allocMain)); + state.decoder.dic = outBuffer; + state.decoder.dicBufSize = outSize; + Lzma2Dec_Init(&state); + + for (;;) + { + const void *inBuf = NULL; + size_t lookahead = (1 << 18); + if (lookahead > inSize) + lookahead = (size_t)inSize; + res = inStream->Look(inStream, &inBuf, &lookahead); + if (res != SZ_OK) + break; + + { + SizeT inProcessed = (SizeT)lookahead, dicPos = state.decoder.dicPos; + ELzmaStatus status; + res = Lzma2Dec_DecodeToDic(&state, outSize, inBuf, &inProcessed, LZMA_FINISH_END, &status); + lookahead -= inProcessed; + inSize -= inProcessed; + if (res != SZ_OK) + break; + + if (status == LZMA_STATUS_FINISHED_WITH_MARK) + { + if (outSize != state.decoder.dicPos || inSize != 0) + res = SZ_ERROR_DATA; + break; + } + + if (inProcessed == 0 && dicPos == state.decoder.dicPos) + { + res = SZ_ERROR_DATA; + break; + } + + res = inStream->Skip((void *)inStream, inProcessed); + if (res != SZ_OK) + break; + } + } + + Lzma2Dec_FreeProbs(&state, allocMain); + return res; +} + +#endif + + +static SRes SzDecodeCopy(UInt64 inSize, ILookInStream *inStream, Byte *outBuffer) +{ + while (inSize > 0) + { + const void *inBuf; + size_t curSize = (1 << 18); + if (curSize > inSize) + curSize = (size_t)inSize; + RINOK(inStream->Look(inStream, &inBuf, &curSize)); + if (curSize == 0) + return SZ_ERROR_INPUT_EOF; + memcpy(outBuffer, inBuf, curSize); + outBuffer += curSize; + inSize -= curSize; + RINOK(inStream->Skip((void *)inStream, curSize)); + } + return SZ_OK; +} + +static Bool IS_MAIN_METHOD(UInt32 m) +{ + switch (m) + { + case k_Copy: + case k_LZMA: + #ifndef _7Z_NO_METHOD_LZMA2 + case k_LZMA2: + #endif + #ifdef _7ZIP_PPMD_SUPPPORT + case k_PPMD: + #endif + return True; + } + return False; +} + +static Bool IS_SUPPORTED_CODER(const CSzCoderInfo *c) +{ + return + c->NumStreams == 1 + /* && c->MethodID <= (UInt32)0xFFFFFFFF */ + && IS_MAIN_METHOD((UInt32)c->MethodID); +} + +#define IS_BCJ2(c) ((c)->MethodID == k_BCJ2 && (c)->NumStreams == 4) + +static SRes CheckSupportedFolder(const CSzFolder *f) +{ + if (f->NumCoders < 1 || f->NumCoders > 4) + return SZ_ERROR_UNSUPPORTED; + if (!IS_SUPPORTED_CODER(&f->Coders[0])) + return SZ_ERROR_UNSUPPORTED; + if (f->NumCoders == 1) + { + if (f->NumPackStreams != 1 || f->PackStreams[0] != 0 || f->NumBonds != 0) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + + + #ifndef _7Z_NO_METHODS_FILTERS + + if (f->NumCoders == 2) + { + const CSzCoderInfo *c = &f->Coders[1]; + if ( + /* c->MethodID > (UInt32)0xFFFFFFFF || */ + c->NumStreams != 1 + || f->NumPackStreams != 1 + || f->PackStreams[0] != 0 + || f->NumBonds != 1 + || f->Bonds[0].InIndex != 1 + || f->Bonds[0].OutIndex != 0) + return SZ_ERROR_UNSUPPORTED; + switch ((UInt32)c->MethodID) + { + case k_Delta: + case k_BCJ: + case k_PPC: + case k_IA64: + case k_SPARC: + case k_ARM: + case k_ARMT: + break; + default: + return SZ_ERROR_UNSUPPORTED; + } + return SZ_OK; + } + + #endif + + + if (f->NumCoders == 4) + { + if (!IS_SUPPORTED_CODER(&f->Coders[1]) + || !IS_SUPPORTED_CODER(&f->Coders[2]) + || !IS_BCJ2(&f->Coders[3])) + return SZ_ERROR_UNSUPPORTED; + if (f->NumPackStreams != 4 + || f->PackStreams[0] != 2 + || f->PackStreams[1] != 6 + || f->PackStreams[2] != 1 + || f->PackStreams[3] != 0 + || f->NumBonds != 3 + || f->Bonds[0].InIndex != 5 || f->Bonds[0].OutIndex != 0 + || f->Bonds[1].InIndex != 4 || f->Bonds[1].OutIndex != 1 + || f->Bonds[2].InIndex != 3 || f->Bonds[2].OutIndex != 2) + return SZ_ERROR_UNSUPPORTED; + return SZ_OK; + } + + return SZ_ERROR_UNSUPPORTED; +} + +#define CASE_BRA_CONV(isa) case k_ ## isa: isa ## _Convert(outBuffer, outSize, 0, 0); break; + +static SRes SzFolder_Decode2(const CSzFolder *folder, + const Byte *propsData, + const UInt64 *unpackSizes, + const UInt64 *packPositions, + ILookInStream *inStream, UInt64 startPos, + Byte *outBuffer, SizeT outSize, ISzAlloc *allocMain, + Byte *tempBuf[]) +{ + UInt32 ci; + SizeT tempSizes[3] = { 0, 0, 0}; + SizeT tempSize3 = 0; + Byte *tempBuf3 = 0; + + RINOK(CheckSupportedFolder(folder)); + + for (ci = 0; ci < folder->NumCoders; ci++) + { + const CSzCoderInfo *coder = &folder->Coders[ci]; + + if (IS_MAIN_METHOD((UInt32)coder->MethodID)) + { + UInt32 si = 0; + UInt64 offset; + UInt64 inSize; + Byte *outBufCur = outBuffer; + SizeT outSizeCur = outSize; + if (folder->NumCoders == 4) + { + UInt32 indices[] = { 3, 2, 0 }; + UInt64 unpackSize = unpackSizes[ci]; + si = indices[ci]; + if (ci < 2) + { + Byte *temp; + outSizeCur = (SizeT)unpackSize; + if (outSizeCur != unpackSize) + return SZ_ERROR_MEM; + temp = (Byte *)IAlloc_Alloc(allocMain, outSizeCur); + if (!temp && outSizeCur != 0) + return SZ_ERROR_MEM; + outBufCur = tempBuf[1 - ci] = temp; + tempSizes[1 - ci] = outSizeCur; + } + else if (ci == 2) + { + if (unpackSize > outSize) /* check it */ + return SZ_ERROR_PARAM; + tempBuf3 = outBufCur = outBuffer + (outSize - (size_t)unpackSize); + tempSize3 = outSizeCur = (SizeT)unpackSize; + } + else + return SZ_ERROR_UNSUPPORTED; + } + offset = packPositions[si]; + inSize = packPositions[si + 1] - offset; + RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + + if (coder->MethodID == k_Copy) + { + if (inSize != outSizeCur) /* check it */ + return SZ_ERROR_DATA; + RINOK(SzDecodeCopy(inSize, inStream, outBufCur)); + } + else if (coder->MethodID == k_LZMA) + { + RINOK(SzDecodeLzma(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); + } + #ifndef _7Z_NO_METHOD_LZMA2 + else if (coder->MethodID == k_LZMA2) + { + RINOK(SzDecodeLzma2(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); + } + #endif + #ifdef _7ZIP_PPMD_SUPPPORT + else if (coder->MethodID == k_PPMD) + { + RINOK(SzDecodePpmd(propsData + coder->PropsOffset, coder->PropsSize, inSize, inStream, outBufCur, outSizeCur, allocMain)); + } + #endif + else + return SZ_ERROR_UNSUPPORTED; + } + else if (coder->MethodID == k_BCJ2) + { + UInt64 offset = packPositions[1]; + UInt64 s3Size = packPositions[2] - offset; + + if (ci != 3) + return SZ_ERROR_UNSUPPORTED; + + tempSizes[2] = (SizeT)s3Size; + if (tempSizes[2] != s3Size) + return SZ_ERROR_MEM; + tempBuf[2] = (Byte *)IAlloc_Alloc(allocMain, tempSizes[2]); + if (!tempBuf[2] && tempSizes[2] != 0) + return SZ_ERROR_MEM; + + RINOK(LookInStream_SeekTo(inStream, startPos + offset)); + RINOK(SzDecodeCopy(s3Size, inStream, tempBuf[2])); + + if ((tempSizes[0] & 3) != 0 || + (tempSizes[1] & 3) != 0 || + tempSize3 + tempSizes[0] + tempSizes[1] != outSize) + return SZ_ERROR_DATA; + + { + CBcj2Dec p; + + p.bufs[0] = tempBuf3; p.lims[0] = tempBuf3 + tempSize3; + p.bufs[1] = tempBuf[0]; p.lims[1] = tempBuf[0] + tempSizes[0]; + p.bufs[2] = tempBuf[1]; p.lims[2] = tempBuf[1] + tempSizes[1]; + p.bufs[3] = tempBuf[2]; p.lims[3] = tempBuf[2] + tempSizes[2]; + + p.dest = outBuffer; + p.destLim = outBuffer + outSize; + + Bcj2Dec_Init(&p); + RINOK(Bcj2Dec_Decode(&p)); + + { + unsigned i; + for (i = 0; i < 4; i++) + if (p.bufs[i] != p.lims[i]) + return SZ_ERROR_DATA; + + if (!Bcj2Dec_IsFinished(&p)) + return SZ_ERROR_DATA; + + if (p.dest != p.destLim + || p.state != BCJ2_STREAM_MAIN) + return SZ_ERROR_DATA; + } + } + } + #ifndef _7Z_NO_METHODS_FILTERS + else if (ci == 1) + { + if (coder->MethodID == k_Delta) + { + if (coder->PropsSize != 1) + return SZ_ERROR_UNSUPPORTED; + { + Byte state[DELTA_STATE_SIZE]; + Delta_Init(state); + Delta_Decode(state, (unsigned)(propsData[coder->PropsOffset]) + 1, outBuffer, outSize); + } + } + else + { + if (coder->PropsSize != 0) + return SZ_ERROR_UNSUPPORTED; + switch (coder->MethodID) + { + case k_BCJ: + { + UInt32 state; + x86_Convert_Init(state); + x86_Convert(outBuffer, outSize, 0, &state, 0); + break; + } + CASE_BRA_CONV(PPC) + CASE_BRA_CONV(IA64) + CASE_BRA_CONV(SPARC) + CASE_BRA_CONV(ARM) + CASE_BRA_CONV(ARMT) + default: + return SZ_ERROR_UNSUPPORTED; + } + } + } + #endif + else + return SZ_ERROR_UNSUPPORTED; + } + + return SZ_OK; +} + + +static SRes SzAr_DecodeFolder(const CSzAr *p, UInt32 folderIndex, + ILookInStream *inStream, UInt64 startPos, + Byte *outBuffer, size_t outSize, + ISzAlloc *allocMain) +{ + SRes res; + CSzFolder folder; + CSzData sd; + + const Byte *data = p->CodersData + p->FoCodersOffsets[folderIndex]; + sd.Data = data; + sd.Size = p->FoCodersOffsets[folderIndex + 1] - p->FoCodersOffsets[folderIndex]; + + res = SzGetNextFolderItem(&folder, &sd); + + if (res != SZ_OK) + return res; + + if (sd.Size != 0 + || folder.UnpackStream != p->FoToMainUnpackSizeIndex[folderIndex] + || outSize != SzAr_GetFolderUnpackSize(p, folderIndex)) + return SZ_ERROR_FAIL; + { + unsigned i; + Byte *tempBuf[3] = { 0, 0, 0}; + + res = SzFolder_Decode2(&folder, data, + &p->CoderUnpackSizes[p->FoToCoderUnpackSizes[folderIndex]], + p->PackPositions + p->FoStartPackStreamIndex[folderIndex], + inStream, startPos, + outBuffer, (SizeT)outSize, allocMain, tempBuf); + + for (i = 0; i < 3; i++) + IAlloc_Free(allocMain, tempBuf[i]); + + if (res == SZ_OK) + if (SzBitWithVals_Check(&p->FolderCRCs, folderIndex)) + if (CrcCalc(outBuffer, outSize) != p->FolderCRCs.Vals[folderIndex]) + res = SZ_ERROR_CRC; + + return res; + } +} + +/* Bcj2.c -- BCJ2 Decoder (Converter for x86 code) +2015-08-01 : Igor Pavlov : Public domain */ + +/* +#include "Precomp.h" + +#include "Bcj2.h" +#include "CpuArch.h" +*/ + +#define CProb UInt16 + +#define kTopValue ((UInt32)1 << 24) +#define kNumModelBits 11 +#define kBitModelTotal (1 << kNumModelBits) +#define kNumMoveBits 5 + +#define _IF_BIT_0 ttt = *prob; bound = (p->range >> kNumModelBits) * ttt; if (p->code < bound) +#define _UPDATE_0 p->range = bound; *prob = (CProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define _UPDATE_1 p->range -= bound; p->code -= bound; *prob = (CProb)(ttt - (ttt >> kNumMoveBits)); + +static void Bcj2Dec_Init(CBcj2Dec *p) +{ + unsigned i; + + p->state = BCJ2_DEC_STATE_OK; + p->ip = 0; + p->temp[3] = 0; + p->range = 0; + p->code = 0; + for (i = 0; i < sizeof(p->probs) / sizeof(p->probs[0]); i++) + p->probs[i] = kBitModelTotal >> 1; +} + +static SRes Bcj2Dec_Decode(CBcj2Dec *p) +{ + if (p->range <= 5) + { + p->state = BCJ2_DEC_STATE_OK; + for (; p->range != 5; p->range++) + { + if (p->range == 1 && p->code != 0) + return SZ_ERROR_DATA; + + if (p->bufs[BCJ2_STREAM_RC] == p->lims[BCJ2_STREAM_RC]) + { + p->state = BCJ2_STREAM_RC; + return SZ_OK; + } + + p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++; + } + + if (p->code == 0xFFFFFFFF) + return SZ_ERROR_DATA; + + p->range = 0xFFFFFFFF; + } + else if (p->state >= BCJ2_DEC_STATE_ORIG_0) + { + while (p->state <= BCJ2_DEC_STATE_ORIG_3) + { + Byte *dest = p->dest; + if (dest == p->destLim) + return SZ_OK; + *dest = p->temp[p->state++ - BCJ2_DEC_STATE_ORIG_0]; + p->dest = dest + 1; + } + } + + /* + if (BCJ2_IS_32BIT_STREAM(p->state)) + { + const Byte *cur = p->bufs[p->state]; + if (cur == p->lims[p->state]) + return SZ_OK; + p->bufs[p->state] = cur + 4; + + { + UInt32 val; + Byte *dest; + SizeT rem; + + p->ip += 4; + val = GetBe32(cur) - p->ip; + dest = p->dest; + rem = p->destLim - dest; + if (rem < 4) + { + SizeT i; + SetUi32(p->temp, val); + for (i = 0; i < rem; i++) + dest[i] = p->temp[i]; + p->dest = dest + rem; + p->state = BCJ2_DEC_STATE_ORIG_0 + (unsigned)rem; + return SZ_OK; + } + SetUi32(dest, val); + p->temp[3] = (Byte)(val >> 24); + p->dest = dest + 4; + p->state = BCJ2_DEC_STATE_OK; + } + } + */ + + for (;;) + { + if (BCJ2_IS_32BIT_STREAM(p->state)) + p->state = BCJ2_DEC_STATE_OK; + else + { + if (p->range < kTopValue) + { + if (p->bufs[BCJ2_STREAM_RC] == p->lims[BCJ2_STREAM_RC]) + { + p->state = BCJ2_STREAM_RC; + return SZ_OK; + } + p->range <<= 8; + p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++; + } + + { + const Byte *src = p->bufs[BCJ2_STREAM_MAIN]; + const Byte *srcLim; + Byte *dest; + SizeT num = p->lims[BCJ2_STREAM_MAIN] - src; + + if (num == 0) + { + p->state = BCJ2_STREAM_MAIN; + return SZ_OK; + } + + dest = p->dest; + if (num > (SizeT)(p->destLim - dest)) + { + num = p->destLim - dest; + if (num == 0) + { + p->state = BCJ2_DEC_STATE_ORIG; + return SZ_OK; + } + } + + srcLim = src + num; + + if (p->temp[3] == 0x0F && (src[0] & 0xF0) == 0x80) + *dest = src[0]; + else for (;;) + { + Byte b = *src; + *dest = b; + if (b != 0x0F) + { + if ((b & 0xFE) == 0xE8) + break; + dest++; + if (++src != srcLim) + continue; + break; + } + dest++; + if (++src == srcLim) + break; + if ((*src & 0xF0) != 0x80) + continue; + *dest = *src; + break; + } + + num = src - p->bufs[BCJ2_STREAM_MAIN]; + + if (src == srcLim) + { + p->temp[3] = src[-1]; + p->bufs[BCJ2_STREAM_MAIN] = src; + p->ip += (UInt32)num; + p->dest += num; + p->state = + p->bufs[BCJ2_STREAM_MAIN] == + p->lims[BCJ2_STREAM_MAIN] ? + (unsigned)BCJ2_STREAM_MAIN : + (unsigned)BCJ2_DEC_STATE_ORIG; + return SZ_OK; + } + + { + UInt32 bound, ttt; + CProb *prob; + Byte b = src[0]; + Byte prev = (Byte)(num == 0 ? p->temp[3] : src[-1]); + + p->temp[3] = b; + p->bufs[BCJ2_STREAM_MAIN] = src + 1; + num++; + p->ip += (UInt32)num; + p->dest += num; + + prob = p->probs + (unsigned)(b == 0xE8 ? 2 + (unsigned)prev : (b == 0xE9 ? 1 : 0)); + + _IF_BIT_0 + { + _UPDATE_0 + continue; + } + _UPDATE_1 + + } + } + } + + { + UInt32 val; + unsigned cj = (p->temp[3] == 0xE8) ? BCJ2_STREAM_CALL : BCJ2_STREAM_JUMP; + const Byte *cur = p->bufs[cj]; + Byte *dest; + SizeT rem; + + if (cur == p->lims[cj]) + { + p->state = cj; + break; + } + + val = GetBe32(cur); + p->bufs[cj] = cur + 4; + + p->ip += 4; + val -= p->ip; + dest = p->dest; + rem = p->destLim - dest; + + if (rem < 4) + { + SizeT i; + SetUi32(p->temp, val); + for (i = 0; i < rem; i++) + dest[i] = p->temp[i]; + p->dest = dest + rem; + p->state = BCJ2_DEC_STATE_ORIG_0 + (unsigned)rem; + break; + } + + SetUi32(dest, val); + p->temp[3] = (Byte)(val >> 24); + p->dest = dest + 4; + } + } + + if (p->range < kTopValue && p->bufs[BCJ2_STREAM_RC] != p->lims[BCJ2_STREAM_RC]) + { + p->range <<= 8; + p->code = (p->code << 8) | *(p->bufs[BCJ2_STREAM_RC])++; + } + + return SZ_OK; +} + +#undef kTopValue /* reused later. --ryan. */ +#undef kBitModelTotal /* reused later. --ryan. */ + + +/* Bra.c -- Converters for RISC code +2010-04-16 : Igor Pavlov : Public domain */ + +/* +#include "Precomp.h" + +#include "Bra.h" +*/ + +static SizeT ARM_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + ip += 8; + for (i = 0; i <= size; i += 4) + { + if (data[i + 3] == 0xEB) + { + UInt32 dest; + UInt32 src = ((UInt32)data[i + 2] << 16) | ((UInt32)data[i + 1] << 8) | (data[i + 0]); + src <<= 2; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + dest >>= 2; + data[i + 2] = (Byte)(dest >> 16); + data[i + 1] = (Byte)(dest >> 8); + data[i + 0] = (Byte)dest; + } + } + return i; +} + +static SizeT ARMT_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + ip += 4; + for (i = 0; i <= size; i += 2) + { + if ((data[i + 1] & 0xF8) == 0xF0 && + (data[i + 3] & 0xF8) == 0xF8) + { + UInt32 dest; + UInt32 src = + (((UInt32)data[i + 1] & 0x7) << 19) | + ((UInt32)data[i + 0] << 11) | + (((UInt32)data[i + 3] & 0x7) << 8) | + (data[i + 2]); + + src <<= 1; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + dest >>= 1; + + data[i + 1] = (Byte)(0xF0 | ((dest >> 19) & 0x7)); + data[i + 0] = (Byte)(dest >> 11); + data[i + 3] = (Byte)(0xF8 | ((dest >> 8) & 0x7)); + data[i + 2] = (Byte)dest; + i += 2; + } + } + return i; +} + +static SizeT PPC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 4) + return 0; + size -= 4; + for (i = 0; i <= size; i += 4) + { + if ((data[i] >> 2) == 0x12 && (data[i + 3] & 3) == 1) + { + UInt32 src = ((UInt32)(data[i + 0] & 3) << 24) | + ((UInt32)data[i + 1] << 16) | + ((UInt32)data[i + 2] << 8) | + ((UInt32)data[i + 3] & (~3)); + + UInt32 dest; + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + data[i + 0] = (Byte)(0x48 | ((dest >> 24) & 0x3)); + data[i + 1] = (Byte)(dest >> 16); + data[i + 2] = (Byte)(dest >> 8); + data[i + 3] &= 0x3; + data[i + 3] |= dest; + } + } + return i; +} + +static SizeT SPARC_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + UInt32 i; + if (size < 4) + return 0; + size -= 4; + for (i = 0; i <= size; i += 4) + { + if ((data[i] == 0x40 && (data[i + 1] & 0xC0) == 0x00) || + (data[i] == 0x7F && (data[i + 1] & 0xC0) == 0xC0)) + { + UInt32 src = + ((UInt32)data[i + 0] << 24) | + ((UInt32)data[i + 1] << 16) | + ((UInt32)data[i + 2] << 8) | + ((UInt32)data[i + 3]); + UInt32 dest; + + src <<= 2; + if (encoding) + dest = ip + i + src; + else + dest = src - (ip + i); + dest >>= 2; + + dest = (((0 - ((dest >> 22) & 1)) << 22) & 0x3FFFFFFF) | (dest & 0x3FFFFF) | 0x40000000; + + data[i + 0] = (Byte)(dest >> 24); + data[i + 1] = (Byte)(dest >> 16); + data[i + 2] = (Byte)(dest >> 8); + data[i + 3] = (Byte)dest; + } + } + return i; +} + +/* Bra86.c -- Converter for x86 code (BCJ) +2013-11-12 : Igor Pavlov : Public domain */ + +/* +#include "Precomp.h" + +#include "Bra.h" +*/ + +#define Test86MSByte(b) ((((b) + 1) & 0xFE) == 0) + +static SizeT x86_Convert(Byte *data, SizeT size, UInt32 ip, UInt32 *state, int encoding) +{ + SizeT pos = 0; + UInt32 mask = *state & 7; + if (size < 5) + return 0; + size -= 4; + ip += 5; + + for (;;) + { + Byte *p = data + pos; + const Byte *limit = data + size; + for (; p < limit; p++) + if ((*p & 0xFE) == 0xE8) + break; + + { + SizeT d = (SizeT)(p - data - pos); + pos = (SizeT)(p - data); + if (p >= limit) + { + *state = (d > 2 ? 0 : mask >> (unsigned)d); + return pos; + } + if (d > 2) + mask = 0; + else + { + mask >>= (unsigned)d; + if (mask != 0 && (mask > 4 || mask == 3 || Test86MSByte(p[(mask >> 1) + 1]))) + { + mask = (mask >> 1) | 4; + pos++; + continue; + } + } + } + + if (Test86MSByte(p[4])) + { + UInt32 v = ((UInt32)p[4] << 24) | ((UInt32)p[3] << 16) | ((UInt32)p[2] << 8) | ((UInt32)p[1]); + UInt32 cur = ip + (UInt32)pos; + pos += 5; + if (encoding) + v += cur; + else + v -= cur; + if (mask != 0) + { + unsigned sh = (mask & 6) << 2; + if (Test86MSByte((Byte)(v >> sh))) + { + v ^= (((UInt32)0x100 << sh) - 1); + if (encoding) + v += cur; + else + v -= cur; + } + mask = 0; + } + p[1] = (Byte)v; + p[2] = (Byte)(v >> 8); + p[3] = (Byte)(v >> 16); + p[4] = (Byte)(0 - ((v >> 24) & 1)); + } + else + { + mask = (mask >> 1) | 4; + pos++; + } + } +} + + +/* BraIA64.c -- Converter for IA-64 code +2013-11-12 : Igor Pavlov : Public domain */ + +/* +#include "Precomp.h" + +#include "Bra.h" +*/ +static const Byte kBranchTable[32] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 4, 4, 6, 6, 0, 0, 7, 7, + 4, 4, 0, 0, 4, 4, 0, 0 +}; + +static SizeT IA64_Convert(Byte *data, SizeT size, UInt32 ip, int encoding) +{ + SizeT i; + if (size < 16) + return 0; + size -= 16; + for (i = 0; i <= size; i += 16) + { + UInt32 instrTemplate = data[i] & 0x1F; + UInt32 mask = kBranchTable[instrTemplate]; + UInt32 bitPos = 5; + int slot; + for (slot = 0; slot < 3; slot++, bitPos += 41) + { + UInt32 bytePos, bitRes; + UInt64 instruction, instNorm; + int j; + if (((mask >> slot) & 1) == 0) + continue; + bytePos = (bitPos >> 3); + bitRes = bitPos & 0x7; + instruction = 0; + for (j = 0; j < 6; j++) + instruction += (UInt64)data[i + j + bytePos] << (8 * j); + + instNorm = instruction >> bitRes; + if (((instNorm >> 37) & 0xF) == 0x5 && ((instNorm >> 9) & 0x7) == 0) + { + UInt32 src = (UInt32)((instNorm >> 13) & 0xFFFFF); + UInt32 dest; + src |= ((UInt32)(instNorm >> 36) & 1) << 20; + + src <<= 4; + + if (encoding) + dest = ip + (UInt32)i + src; + else + dest = src - (ip + (UInt32)i); + + dest >>= 4; + + instNorm &= ~((UInt64)(0x8FFFFF) << 13); + instNorm |= ((UInt64)(dest & 0xFFFFF) << 13); + instNorm |= ((UInt64)(dest & 0x100000) << (36 - 20)); + + instruction &= (1 << bitRes) - 1; + instruction |= (instNorm << bitRes); + for (j = 0; j < 6; j++) + data[i + j + bytePos] = (Byte)(instruction >> (8 * j)); + } + } + } + return i; +} + + +/* Delta.c -- Delta converter +2009-05-26 : Igor Pavlov : Public domain */ + +/* +#include "Precomp.h" + +#include "Delta.h" +*/ + +static void Delta_Init(Byte *state) +{ + unsigned i; + for (i = 0; i < DELTA_STATE_SIZE; i++) + state[i] = 0; +} + +static void MyMemCpy(Byte *dest, const Byte *src, unsigned size) +{ + unsigned i; + for (i = 0; i < size; i++) + dest[i] = src[i]; +} + +static void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size) +{ + Byte buf[DELTA_STATE_SIZE]; + unsigned j = 0; + MyMemCpy(buf, state, delta); + { + SizeT i; + for (i = 0; i < size;) + { + for (j = 0; j < delta && i < size; i++, j++) + { + buf[j] = data[i] = (Byte)(buf[j] + data[i]); + } + } + } + if (j == delta) + j = 0; + MyMemCpy(state, buf + j, delta - j); + MyMemCpy(state + delta - j, buf, j); +} + +/* LzmaDec.c -- LZMA Decoder +2016-05-16 : Igor Pavlov : Public domain */ + +/* +#include "Precomp.h" + +#include "LzmaDec.h" + +#include <string.h> +*/ + +#define kNumTopBits 24 +#define kTopValue ((UInt32)1 << kNumTopBits) + +#define kNumBitModelTotalBits 11 +#define kBitModelTotal (1 << kNumBitModelTotalBits) +#define kNumMoveBits 5 + +#define RC_INIT_SIZE 5 + +#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); +#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); +#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ + { UPDATE_0(p); i = (i + i); A0; } else \ + { UPDATE_1(p); i = (i + i) + 1; A1; } +#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) + +#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } +#define TREE_DECODE(probs, limit, i) \ + { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } + +/* #define _LZMA_SIZE_OPT */ + +#ifdef _LZMA_SIZE_OPT +#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i) +#else +#define TREE_6_DECODE(probs, i) \ + { i = 1; \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + TREE_GET_BIT(probs, i); \ + i -= 0x40; } +#endif + +#define NORMAL_LITER_DEC GET_BIT(prob + symbol, symbol) +#define MATCHED_LITER_DEC \ + matchByte <<= 1; \ + bit = (matchByte & offs); \ + probLit = prob + offs + bit + symbol; \ + GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + +#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } + +#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound) +#define UPDATE_0_CHECK range = bound; +#define UPDATE_1_CHECK range -= bound; code -= bound; +#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \ + { UPDATE_0_CHECK; i = (i + i); A0; } else \ + { UPDATE_1_CHECK; i = (i + i) + 1; A1; } +#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;) +#define TREE_DECODE_CHECK(probs, limit, i) \ + { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } + + +#define kNumPosBitsMax 4 +#define kNumPosStatesMax (1 << kNumPosBitsMax) + +#define kLenNumLowBits 3 +#define kLenNumLowSymbols (1 << kLenNumLowBits) +#define kLenNumMidBits 3 +#define kLenNumMidSymbols (1 << kLenNumMidBits) +#define kLenNumHighBits 8 +#define kLenNumHighSymbols (1 << kLenNumHighBits) + +#define LenChoice 0 +#define LenChoice2 (LenChoice + 1) +#define LenLow (LenChoice2 + 1) +#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) +#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define kNumLenProbs (LenHigh + kLenNumHighSymbols) + + +#define kNumStates 12 +#define kNumLitStates 7 + +#define kStartPosModelIndex 4 +#define kEndPosModelIndex 14 +#define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) + +#define kNumPosSlotBits 6 +#define kNumLenToPosStates 4 + +#define kNumAlignBits 4 +#define kAlignTableSize (1 << kNumAlignBits) + +#define kMatchMinLen 2 +#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) + +#define IsMatch 0 +#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define IsRepG0 (IsRep + kNumStates) +#define IsRepG1 (IsRepG0 + kNumStates) +#define IsRepG2 (IsRepG1 + kNumStates) +#define IsRep0Long (IsRepG2 + kNumStates) +#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) +#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) +#define LenCoder (Align + kAlignTableSize) +#define RepLenCoder (LenCoder + kNumLenProbs) +#define Literal (RepLenCoder + kNumLenProbs) + +#define LZMA_BASE_SIZE 1846 +#define LZMA_LIT_SIZE 0x300 + +#if Literal != LZMA_BASE_SIZE +StopCompilingDueBUG +#endif + +#define LzmaProps_GetNumProbs(p) (Literal + ((UInt32)LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#define LZMA_DIC_MIN (1 << 12) + +/* First LZMA-symbol is always decoded. +And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization +Out: + Result: + SZ_OK - OK + SZ_ERROR_DATA - Error + p->remainLen: + < kMatchSpecLenStart : normal remain + = kMatchSpecLenStart : finished + = kMatchSpecLenStart + 1 : Flush marker (unused now) + = kMatchSpecLenStart + 2 : State Init Marker (unused now) +*/ + +static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + CLzmaProb *probs = p->probs; + + unsigned state = p->state; + UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; + unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; + unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; + unsigned lc = p->prop.lc; + + Byte *dic = p->dic; + SizeT dicBufSize = p->dicBufSize; + SizeT dicPos = p->dicPos; + + UInt32 processedPos = p->processedPos; + UInt32 checkDicSize = p->checkDicSize; + unsigned len = 0; + + const Byte *buf = p->buf; + UInt32 range = p->range; + UInt32 code = p->code; + + do + { + CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = processedPos & pbMask; + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + unsigned symbol; + UPDATE_0(prob); + prob = probs + Literal; + if (processedPos != 0 || checkDicSize != 0) + prob += ((UInt32)LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + + (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + processedPos++; + + if (state < kNumLitStates) + { + state -= (state < 4) ? state : 3; + symbol = 1; + #ifdef _LZMA_SIZE_OPT + do { NORMAL_LITER_DEC } while (symbol < 0x100); + #else + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + NORMAL_LITER_DEC + #endif + } + else + { + unsigned matchByte = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; + unsigned offs = 0x100; + state -= (state < 10) ? 3 : 6; + symbol = 1; + #ifdef _LZMA_SIZE_OPT + do + { + unsigned bit; + CLzmaProb *probLit; + MATCHED_LITER_DEC + } + while (symbol < 0x100); + #else + { + unsigned bit; + CLzmaProb *probLit; + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + MATCHED_LITER_DEC + } + #endif + } + + dic[dicPos++] = (Byte)symbol; + continue; + } + + { + UPDATE_1(prob); + prob = probs + IsRep + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + state += kNumStates; + prob = probs + LenCoder; + } + else + { + UPDATE_1(prob); + if (checkDicSize == 0 && processedPos == 0) + return SZ_ERROR_DATA; + prob = probs + IsRepG0 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0(prob) + { + UPDATE_0(prob); + dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; + dicPos++; + processedPos++; + state = state < kNumLitStates ? 9 : 11; + continue; + } + UPDATE_1(prob); + } + else + { + UInt32 distance; + UPDATE_1(prob); + prob = probs + IsRepG1 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep1; + } + else + { + UPDATE_1(prob); + prob = probs + IsRepG2 + state; + IF_BIT_0(prob) + { + UPDATE_0(prob); + distance = rep2; + } + else + { + UPDATE_1(prob); + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + state = state < kNumLitStates ? 8 : 11; + prob = probs + RepLenCoder; + } + + #ifdef _LZMA_SIZE_OPT + { + unsigned lim, offset; + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + lim = (1 << kLenNumLowBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + lim = (1 << kLenNumMidBits); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + lim = (1 << kLenNumHighBits); + } + } + TREE_DECODE(probLen, lim, len); + len += offset; + } + #else + { + CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenLow + (posState << kLenNumLowBits); + len = 1; + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + len -= 8; + } + else + { + UPDATE_1(probLen); + probLen = prob + LenChoice2; + IF_BIT_0(probLen) + { + UPDATE_0(probLen); + probLen = prob + LenMid + (posState << kLenNumMidBits); + len = 1; + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + TREE_GET_BIT(probLen, len); + } + else + { + UPDATE_1(probLen); + probLen = prob + LenHigh; + TREE_DECODE(probLen, (1 << kLenNumHighBits), len); + len += kLenNumLowSymbols + kLenNumMidSymbols; + } + } + } + #endif + + if (state >= kNumStates) + { + UInt32 distance; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); + TREE_6_DECODE(prob, distance); + if (distance >= kStartPosModelIndex) + { + unsigned posSlot = (unsigned)distance; + unsigned numDirectBits = (unsigned)(((distance >> 1) - 1)); + distance = (2 | (distance & 1)); + if (posSlot < kEndPosModelIndex) + { + distance <<= numDirectBits; + prob = probs + SpecPos + distance - posSlot - 1; + { + UInt32 mask = 1; + unsigned i = 1; + do + { + GET_BIT2(prob + i, i, ; , distance |= mask); + mask <<= 1; + } + while (--numDirectBits != 0); + } + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE + range >>= 1; + + { + UInt32 t; + code -= range; + t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */ + distance = (distance << 1) + (t + 1); + code += range & t; + } + /* + distance <<= 1; + if (code >= range) + { + code -= range; + distance |= 1; + } + */ + } + while (--numDirectBits != 0); + prob = probs + Align; + distance <<= kNumAlignBits; + { + unsigned i = 1; + GET_BIT2(prob + i, i, ; , distance |= 1); + GET_BIT2(prob + i, i, ; , distance |= 2); + GET_BIT2(prob + i, i, ; , distance |= 4); + GET_BIT2(prob + i, i, ; , distance |= 8); + } + if (distance == (UInt32)0xFFFFFFFF) + { + len += kMatchSpecLenStart; + state -= kNumStates; + break; + } + } + } + + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + rep0 = distance + 1; + if (checkDicSize == 0) + { + if (distance >= processedPos) + { + p->dicPos = dicPos; + return SZ_ERROR_DATA; + } + } + else if (distance >= checkDicSize) + { + p->dicPos = dicPos; + return SZ_ERROR_DATA; + } + state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; + } + + len += kMatchMinLen; + + { + SizeT rem; + unsigned curLen; + SizeT pos; + + if ((rem = limit - dicPos) == 0) + { + p->dicPos = dicPos; + return SZ_ERROR_DATA; + } + + curLen = ((rem < len) ? (unsigned)rem : len); + pos = dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0); + + processedPos += curLen; + + len -= curLen; + if (curLen <= dicBufSize - pos) + { + Byte *dest = dic + dicPos; + ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos; + const Byte *lim = dest + curLen; + dicPos += curLen; + do + *(dest) = (Byte)*(dest + src); + while (++dest != lim); + } + else + { + do + { + dic[dicPos++] = dic[pos]; + if (++pos == dicBufSize) + pos = 0; + } + while (--curLen != 0); + } + } + } + } + while (dicPos < limit && buf < bufLimit); + + NORMALIZE; + + p->buf = buf; + p->range = range; + p->code = code; + p->remainLen = len; + p->dicPos = dicPos; + p->processedPos = processedPos; + p->reps[0] = rep0; + p->reps[1] = rep1; + p->reps[2] = rep2; + p->reps[3] = rep3; + p->state = state; + + return SZ_OK; +} + +static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) +{ + if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart) + { + Byte *dic = p->dic; + SizeT dicPos = p->dicPos; + SizeT dicBufSize = p->dicBufSize; + unsigned len = p->remainLen; + SizeT rep0 = p->reps[0]; /* we use SizeT to avoid the BUG of VC14 for AMD64 */ + SizeT rem = limit - dicPos; + if (rem < len) + len = (unsigned)(rem); + + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len) + p->checkDicSize = p->prop.dicSize; + + p->processedPos += len; + p->remainLen -= len; + while (len != 0) + { + len--; + dic[dicPos] = dic[dicPos - rep0 + (dicPos < rep0 ? dicBufSize : 0)]; + dicPos++; + } + p->dicPos = dicPos; + } +} + +static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + do + { + SizeT limit2 = limit; + if (p->checkDicSize == 0) + { + UInt32 rem = p->prop.dicSize - p->processedPos; + if (limit - p->dicPos > rem) + limit2 = p->dicPos + rem; + } + + RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); + + if (p->checkDicSize == 0 && p->processedPos >= p->prop.dicSize) + p->checkDicSize = p->prop.dicSize; + + LzmaDec_WriteRem(p, limit); + } + while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); + + if (p->remainLen > kMatchSpecLenStart) + p->remainLen = kMatchSpecLenStart; + + return 0; +} + +typedef enum +{ + DUMMY_ERROR, /* unexpected end of input stream */ + DUMMY_LIT, + DUMMY_MATCH, + DUMMY_REP +} ELzmaDummy; + +static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize) +{ + UInt32 range = p->range; + UInt32 code = p->code; + const Byte *bufLimit = buf + inSize; + const CLzmaProb *probs = p->probs; + unsigned state = p->state; + ELzmaDummy res; + + { + const CLzmaProb *prob; + UInt32 bound; + unsigned ttt; + unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + + prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK + + /* if (bufLimit - buf >= 7) return DUMMY_LIT; */ + + prob = probs + Literal; + if (p->checkDicSize != 0 || p->processedPos != 0) + prob += ((UInt32)LZMA_LIT_SIZE * + ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) + + (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc)))); + + if (state < kNumLitStates) + { + unsigned symbol = 1; + do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100); + } + else + { + unsigned matchByte = p->dic[p->dicPos - p->reps[0] + + (p->dicPos < p->reps[0] ? p->dicBufSize : 0)]; + unsigned offs = 0x100; + unsigned symbol = 1; + do + { + unsigned bit; + const CLzmaProb *probLit; + matchByte <<= 1; + bit = (matchByte & offs); + probLit = prob + offs + bit + symbol; + GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) + } + while (symbol < 0x100); + } + res = DUMMY_LIT; + } + else + { + unsigned len; + UPDATE_1_CHECK; + + prob = probs + IsRep + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + state = 0; + prob = probs + LenCoder; + res = DUMMY_MATCH; + } + else + { + UPDATE_1_CHECK; + res = DUMMY_REP; + prob = probs + IsRepG0 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + NORMALIZE_CHECK; + return DUMMY_REP; + } + else + { + UPDATE_1_CHECK; + } + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG1 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + prob = probs + IsRepG2 + state; + IF_BIT_0_CHECK(prob) + { + UPDATE_0_CHECK; + } + else + { + UPDATE_1_CHECK; + } + } + } + state = kNumStates; + prob = probs + RepLenCoder; + } + { + unsigned limit, offset; + const CLzmaProb *probLen = prob + LenChoice; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenLow + (posState << kLenNumLowBits); + offset = 0; + limit = 1 << kLenNumLowBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenChoice2; + IF_BIT_0_CHECK(probLen) + { + UPDATE_0_CHECK; + probLen = prob + LenMid + (posState << kLenNumMidBits); + offset = kLenNumLowSymbols; + limit = 1 << kLenNumMidBits; + } + else + { + UPDATE_1_CHECK; + probLen = prob + LenHigh; + offset = kLenNumLowSymbols + kLenNumMidSymbols; + limit = 1 << kLenNumHighBits; + } + } + TREE_DECODE_CHECK(probLen, limit, len); + len += offset; + } + + if (state < 4) + { + unsigned posSlot; + prob = probs + PosSlot + + ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + kNumPosSlotBits); + TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); + if (posSlot >= kStartPosModelIndex) + { + unsigned numDirectBits = ((posSlot >> 1) - 1); + + /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */ + + if (posSlot < kEndPosModelIndex) + { + prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; + } + else + { + numDirectBits -= kNumAlignBits; + do + { + NORMALIZE_CHECK + range >>= 1; + code -= range & (((code - range) >> 31) - 1); + /* if (code >= range) code -= range; */ + } + while (--numDirectBits != 0); + prob = probs + Align; + numDirectBits = kNumAlignBits; + } + { + unsigned i = 1; + do + { + GET_BIT_CHECK(prob + i, i); + } + while (--numDirectBits != 0); + } + } + } + } + } + NORMALIZE_CHECK; + return res; +} + + +static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) +{ + p->needFlush = 1; + p->remainLen = 0; + p->tempBufSize = 0; + + if (initDic) + { + p->processedPos = 0; + p->checkDicSize = 0; + p->needInitState = 1; + } + if (initState) + p->needInitState = 1; +} + +static void LzmaDec_Init(CLzmaDec *p) +{ + p->dicPos = 0; + LzmaDec_InitDicAndState(p, True, True); +} + +static void LzmaDec_InitStateReal(CLzmaDec *p) +{ + SizeT numProbs = LzmaProps_GetNumProbs(&p->prop); + SizeT i; + CLzmaProb *probs = p->probs; + for (i = 0; i < numProbs; i++) + probs[i] = kBitModelTotal >> 1; + p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; + p->state = 0; + p->needInitState = 0; +} + +static SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, + ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + (*srcLen) = 0; + LzmaDec_WriteRem(p, dicLimit); + + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->remainLen != kMatchSpecLenStart) + { + int checkEndMarkNow; + + if (p->needFlush) + { + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) + p->tempBuf[p->tempBufSize++] = *src++; + if (p->tempBufSize < RC_INIT_SIZE) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (p->tempBuf[0] != 0) + return SZ_ERROR_DATA; + p->code = + ((UInt32)p->tempBuf[1] << 24) + | ((UInt32)p->tempBuf[2] << 16) + | ((UInt32)p->tempBuf[3] << 8) + | ((UInt32)p->tempBuf[4]); + p->range = 0xFFFFFFFF; + p->needFlush = 0; + p->tempBufSize = 0; + } + + checkEndMarkNow = 0; + if (p->dicPos >= dicLimit) + { + if (p->remainLen == 0 && p->code == 0) + { + *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK; + return SZ_OK; + } + if (finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + if (p->remainLen != 0) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + checkEndMarkNow = 1; + } + + if (p->needInitState) + LzmaDec_InitStateReal(p); + + if (p->tempBufSize == 0) + { + SizeT processed; + const Byte *bufLimit; + if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, src, inSize); + if (dummyRes == DUMMY_ERROR) + { + memcpy(p->tempBuf, src, inSize); + p->tempBufSize = (unsigned)inSize; + (*srcLen) += inSize; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + bufLimit = src; + } + else + bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX; + p->buf = src; + if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0) + return SZ_ERROR_DATA; + processed = (SizeT)(p->buf - src); + (*srcLen) += processed; + src += processed; + inSize -= processed; + } + else + { + unsigned rem = p->tempBufSize, lookAhead = 0; + while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize) + p->tempBuf[rem++] = src[lookAhead++]; + p->tempBufSize = rem; + if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow) + { + int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem); + if (dummyRes == DUMMY_ERROR) + { + (*srcLen) += lookAhead; + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + if (checkEndMarkNow && dummyRes != DUMMY_MATCH) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_ERROR_DATA; + } + } + p->buf = p->tempBuf; + if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0) + return SZ_ERROR_DATA; + + { + unsigned kkk = (unsigned)(p->buf - p->tempBuf); + if (rem < kkk) + return SZ_ERROR_FAIL; /* some internal error */ + rem -= kkk; + if (lookAhead < rem) + return SZ_ERROR_FAIL; /* some internal error */ + lookAhead -= rem; + } + (*srcLen) += lookAhead; + src += lookAhead; + inSize -= lookAhead; + p->tempBufSize = 0; + } + } + if (p->code == 0) + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; +} + +static void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) +{ + alloc->Free(alloc, p->probs); + p->probs = NULL; +} + +static SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size) +{ + UInt32 dicSize; + Byte d; + + if (size < LZMA_PROPS_SIZE) + return SZ_ERROR_UNSUPPORTED; + else + dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24); + + if (dicSize < LZMA_DIC_MIN) + dicSize = LZMA_DIC_MIN; + p->dicSize = dicSize; + + d = data[0]; + if (d >= (9 * 5 * 5)) + return SZ_ERROR_UNSUPPORTED; + + p->lc = d % 9; + d /= 9; + p->pb = d / 5; + p->lp = d % 5; + + return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) +{ + UInt32 numProbs = LzmaProps_GetNumProbs(propNew); + if (!p->probs || numProbs != p->numProbs) + { + LzmaDec_FreeProbs(p, alloc); + p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); + p->numProbs = numProbs; + if (!p->probs) + return SZ_ERROR_MEM; + } + return SZ_OK; +} + +static SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +{ + CLzmaProps propNew; + RINOK(LzmaProps_Decode(&propNew, props, propsSize)); + RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc)); + p->prop = propNew; + return SZ_OK; +} + +/* Lzma2Dec.c -- LZMA2 Decoder +2015-11-09 : Igor Pavlov : Public domain */ + +/* #define SHOW_DEBUG_INFO */ + +/* +#include "Precomp.h" + +#ifdef SHOW_DEBUG_INFO +#include <stdio.h> +#endif + +#include <string.h> + +#include "Lzma2Dec.h" +*/ + +/* +00000000 - EOS +00000001 U U - Uncompressed Reset Dic +00000010 U U - Uncompressed No Reset +100uuuuu U U P P - LZMA no reset +101uuuuu U U P P - LZMA reset state +110uuuuu U U P P S - LZMA reset state + new prop +111uuuuu U U P P S - LZMA reset state + new prop + reset dic + + u, U - Unpack Size + P - Pack Size + S - Props +*/ + +#define LZMA2_CONTROL_LZMA (1 << 7) +#define LZMA2_CONTROL_COPY_NO_RESET 2 +#define LZMA2_CONTROL_COPY_RESET_DIC 1 +#define LZMA2_CONTROL_EOF 0 + +#define LZMA2_IS_UNCOMPRESSED_STATE(p) (((p)->control & LZMA2_CONTROL_LZMA) == 0) + +#define LZMA2_GET_LZMA_MODE(p) (((p)->control >> 5) & 3) +#define LZMA2_IS_THERE_PROP(mode) ((mode) >= 2) + +#define LZMA2_LCLP_MAX 4 +#define LZMA2_DIC_SIZE_FROM_PROP(p) (((UInt32)2 | ((p) & 1)) << ((p) / 2 + 11)) + +#ifdef SHOW_DEBUG_INFO +#define PRF(x) x +#else +#define PRF(x) +#endif + +typedef enum +{ + LZMA2_STATE_CONTROL, + LZMA2_STATE_UNPACK0, + LZMA2_STATE_UNPACK1, + LZMA2_STATE_PACK0, + LZMA2_STATE_PACK1, + LZMA2_STATE_PROP, + LZMA2_STATE_DATA, + LZMA2_STATE_DATA_CONT, + LZMA2_STATE_FINISHED, + LZMA2_STATE_ERROR +} ELzma2State; + +static SRes Lzma2Dec_GetOldProps(Byte prop, Byte *props) +{ + UInt32 dicSize; + if (prop > 40) + return SZ_ERROR_UNSUPPORTED; + dicSize = (prop == 40) ? 0xFFFFFFFF : LZMA2_DIC_SIZE_FROM_PROP(prop); + props[0] = (Byte)LZMA2_LCLP_MAX; + props[1] = (Byte)(dicSize); + props[2] = (Byte)(dicSize >> 8); + props[3] = (Byte)(dicSize >> 16); + props[4] = (Byte)(dicSize >> 24); + return SZ_OK; +} + +static SRes Lzma2Dec_AllocateProbs(CLzma2Dec *p, Byte prop, ISzAlloc *alloc) +{ + Byte props[LZMA_PROPS_SIZE]; + RINOK(Lzma2Dec_GetOldProps(prop, props)); + return LzmaDec_AllocateProbs(&p->decoder, props, LZMA_PROPS_SIZE, alloc); +} + +static void Lzma2Dec_Init(CLzma2Dec *p) +{ + p->state = LZMA2_STATE_CONTROL; + p->needInitDic = True; + p->needInitState = True; + p->needInitProp = True; + LzmaDec_Init(&p->decoder); +} + +static ELzma2State Lzma2Dec_UpdateState(CLzma2Dec *p, Byte b) +{ + switch (p->state) + { + case LZMA2_STATE_CONTROL: + p->control = b; + PRF(printf("\n %4X ", (unsigned)p->decoder.dicPos)); + PRF(printf(" %2X", (unsigned)b)); + if (p->control == 0) + return LZMA2_STATE_FINISHED; + if (LZMA2_IS_UNCOMPRESSED_STATE(p)) + { + if ((p->control & 0x7F) > 2) + return LZMA2_STATE_ERROR; + p->unpackSize = 0; + } + else + p->unpackSize = (UInt32)(p->control & 0x1F) << 16; + return LZMA2_STATE_UNPACK0; + + case LZMA2_STATE_UNPACK0: + p->unpackSize |= (UInt32)b << 8; + return LZMA2_STATE_UNPACK1; + + case LZMA2_STATE_UNPACK1: + p->unpackSize |= (UInt32)b; + p->unpackSize++; + PRF(printf(" %8u", (unsigned)p->unpackSize)); + return (LZMA2_IS_UNCOMPRESSED_STATE(p)) ? LZMA2_STATE_DATA : LZMA2_STATE_PACK0; + + case LZMA2_STATE_PACK0: + p->packSize = (UInt32)b << 8; + return LZMA2_STATE_PACK1; + + case LZMA2_STATE_PACK1: + p->packSize |= (UInt32)b; + p->packSize++; + PRF(printf(" %8u", (unsigned)p->packSize)); + return LZMA2_IS_THERE_PROP(LZMA2_GET_LZMA_MODE(p)) ? LZMA2_STATE_PROP: + (p->needInitProp ? LZMA2_STATE_ERROR : LZMA2_STATE_DATA); + + case LZMA2_STATE_PROP: + { + unsigned lc, lp; + if (b >= (9 * 5 * 5)) + return LZMA2_STATE_ERROR; + lc = b % 9; + b /= 9; + p->decoder.prop.pb = b / 5; + lp = b % 5; + if (lc + lp > LZMA2_LCLP_MAX) + return LZMA2_STATE_ERROR; + p->decoder.prop.lc = lc; + p->decoder.prop.lp = lp; + p->needInitProp = False; + return LZMA2_STATE_DATA; + } + } + return LZMA2_STATE_ERROR; +} + +static void LzmaDec_UpdateWithUncompressed(CLzmaDec *p, const Byte *src, SizeT size) +{ + memcpy(p->dic + p->dicPos, src, size); + p->dicPos += size; + if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= size) + p->checkDicSize = p->prop.dicSize; + p->processedPos += (UInt32)size; +} + +static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState); + +static SRes Lzma2Dec_DecodeToDic(CLzma2Dec *p, SizeT dicLimit, + const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) +{ + SizeT inSize = *srcLen; + *srcLen = 0; + *status = LZMA_STATUS_NOT_SPECIFIED; + + while (p->state != LZMA2_STATE_FINISHED) + { + SizeT dicPos = p->decoder.dicPos; + + if (p->state == LZMA2_STATE_ERROR) + return SZ_ERROR_DATA; + + if (dicPos == dicLimit && finishMode == LZMA_FINISH_ANY) + { + *status = LZMA_STATUS_NOT_FINISHED; + return SZ_OK; + } + + if (p->state != LZMA2_STATE_DATA && p->state != LZMA2_STATE_DATA_CONT) + { + if (*srcLen == inSize) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + (*srcLen)++; + p->state = Lzma2Dec_UpdateState(p, *src++); + + if (dicPos == dicLimit && p->state != LZMA2_STATE_FINISHED) + { + p->state = LZMA2_STATE_ERROR; + return SZ_ERROR_DATA; + } + continue; + } + + { + SizeT destSizeCur = dicLimit - dicPos; + SizeT srcSizeCur = inSize - *srcLen; + ELzmaFinishMode curFinishMode = LZMA_FINISH_ANY; + + if (p->unpackSize <= destSizeCur) + { + destSizeCur = (SizeT)p->unpackSize; + curFinishMode = LZMA_FINISH_END; + } + + if (LZMA2_IS_UNCOMPRESSED_STATE(p)) + { + if (*srcLen == inSize) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + + if (p->state == LZMA2_STATE_DATA) + { + Bool initDic = (p->control == LZMA2_CONTROL_COPY_RESET_DIC); + if (initDic) + p->needInitProp = p->needInitState = True; + else if (p->needInitDic) + { + p->state = LZMA2_STATE_ERROR; + return SZ_ERROR_DATA; + } + p->needInitDic = False; + LzmaDec_InitDicAndState(&p->decoder, initDic, False); + } + + if (srcSizeCur > destSizeCur) + srcSizeCur = destSizeCur; + + if (srcSizeCur == 0) + { + p->state = LZMA2_STATE_ERROR; + return SZ_ERROR_DATA; + } + + LzmaDec_UpdateWithUncompressed(&p->decoder, src, srcSizeCur); + + src += srcSizeCur; + *srcLen += srcSizeCur; + p->unpackSize -= (UInt32)srcSizeCur; + p->state = (p->unpackSize == 0) ? LZMA2_STATE_CONTROL : LZMA2_STATE_DATA_CONT; + } + else + { + SizeT outSizeProcessed; + SRes res; + + if (p->state == LZMA2_STATE_DATA) + { + unsigned mode = LZMA2_GET_LZMA_MODE(p); + Bool initDic = (mode == 3); + Bool initState = (mode != 0); + if ((!initDic && p->needInitDic) || (!initState && p->needInitState)) + { + p->state = LZMA2_STATE_ERROR; + return SZ_ERROR_DATA; + } + + LzmaDec_InitDicAndState(&p->decoder, initDic, initState); + p->needInitDic = False; + p->needInitState = False; + p->state = LZMA2_STATE_DATA_CONT; + } + + if (srcSizeCur > p->packSize) + srcSizeCur = (SizeT)p->packSize; + + res = LzmaDec_DecodeToDic(&p->decoder, dicPos + destSizeCur, src, &srcSizeCur, curFinishMode, status); + + src += srcSizeCur; + *srcLen += srcSizeCur; + p->packSize -= (UInt32)srcSizeCur; + + outSizeProcessed = p->decoder.dicPos - dicPos; + p->unpackSize -= (UInt32)outSizeProcessed; + + RINOK(res); + if (*status == LZMA_STATUS_NEEDS_MORE_INPUT) + return res; + + if (srcSizeCur == 0 && outSizeProcessed == 0) + { + if (*status != LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK + || p->unpackSize != 0 + || p->packSize != 0) + { + p->state = LZMA2_STATE_ERROR; + return SZ_ERROR_DATA; + } + p->state = LZMA2_STATE_CONTROL; + } + + if (*status == LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK) + *status = LZMA_STATUS_NOT_FINISHED; + } + } + } + + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return SZ_OK; +} + +#endif /* _INCLUDE_PHYSFS_LZMASDK_H_ */ + +/* end of physfs_lzmasdk.h ... */ + diff --git a/Source/3rdParty/physfs/physfs_miniz.h b/Source/3rdParty/physfs/physfs_miniz.h new file mode 100644 index 0000000..30d6f4c --- /dev/null +++ b/Source/3rdParty/physfs/physfs_miniz.h @@ -0,0 +1,701 @@ +/* tinfl.c v1.11 - public domain inflate with zlib header parsing/adler32 checking (inflate-only subset of miniz.c) + See "unlicense" statement at the end of this file. + Rich Geldreich <richgel99@gmail.com>, last updated May 20, 2011 + Implements RFC 1950: https://www.ietf.org/rfc/rfc1950.txt and RFC 1951: https://www.ietf.org/rfc/rfc1951.txt + + The entire decompressor coroutine is implemented in tinfl_decompress(). The other functions are optional high-level helpers. +*/ +#ifndef TINFL_HEADER_INCLUDED +#define TINFL_HEADER_INCLUDED + +typedef PHYSFS_uint8 mz_uint8; +typedef PHYSFS_sint16 mz_int16; +typedef PHYSFS_uint16 mz_uint16; +typedef PHYSFS_uint32 mz_uint32; +typedef unsigned int mz_uint; +typedef PHYSFS_uint64 mz_uint64; + +/* For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. */ +typedef unsigned long mz_ulong; + +/* Heap allocation callbacks. */ +typedef void *(*mz_alloc_func)(void *opaque, unsigned int items, unsigned int size); +typedef void (*mz_free_func)(void *opaque, void *address); + +#if defined(_M_IX86) || defined(_M_X64) +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 if integer loads and stores to unaligned addresses are acceptable on the target platform (slightly faster). */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +/* Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. */ +#define MINIZ_LITTLE_ENDIAN 1 +#endif + +#if defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) +/* Set MINIZ_HAS_64BIT_REGISTERS to 1 if the processor has 64-bit general purpose registers (enables 64-bit bitbuffer in inflator) */ +#define MINIZ_HAS_64BIT_REGISTERS 1 +#endif + +/* Works around MSVC's spammy "warning C4127: conditional expression is constant" message. */ +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +/* Decompression flags. */ +enum +{ + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; + +/* Max size of LZ dictionary. */ +#define TINFL_LZ_DICT_SIZE 32768 + +/* Return status. */ +typedef enum +{ + TINFL_STATUS_BAD_PARAM = -3, + TINFL_STATUS_ADLER32_MISMATCH = -2, + TINFL_STATUS_FAILED = -1, + TINFL_STATUS_DONE = 0, + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +/* Initializes the decompressor to its initial state. */ +#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +/* Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. */ +/* This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. */ +static tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); + +/* Internal/private bits follow. */ +enum +{ + TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct +{ + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS + #define TINFL_USE_64BIT_BITBUF 1 +#endif + +#if TINFL_USE_64BIT_BITBUF + typedef mz_uint64 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (64) +#else + typedef mz_uint32 tinfl_bit_buf_t; + #define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag +{ + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +#endif /* #ifdef TINFL_HEADER_INCLUDED */ + +/* ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) */ + +#ifndef TINFL_HEADER_FILE_ONLY + +#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) +#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) + #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else + #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) + #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN switch(r->m_state) { case 0: +#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END +#define TINFL_CR_FINISH } + +/* TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never */ +/* reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. */ +#define TINFL_GET_BYTE(state_index, c) do { \ + if (pIn_buf_cur >= pIn_buf_end) { \ + for ( ; ; ) { \ + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ + TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ + if (pIn_buf_cur < pIn_buf_end) { \ + c = *pIn_buf_cur++; \ + break; \ + } \ + } else { \ + c = 0; \ + break; \ + } \ + } \ + } else c = *pIn_buf_cur++; } MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END + +/* TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. */ +/* It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a */ +/* Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the */ +/* bit buffer contains >=15 bits (deflate's max. Huffman code size). */ +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ + } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ + } while (num_bits < 15); + +/* TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read */ +/* beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully */ +/* decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. */ +/* The slow path is only executed at the very end of the input buffer. */ +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ + int temp; mz_uint code_len, c; \ + if (num_bits < 15) { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } else { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ + code_len = temp >> 9, temp &= 511; \ + else { \ + code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ + } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END + +static tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) +{ + static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; + static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + static const int s_min_table_sizes[3] = { 257, 1, 4 }; + + tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; + + /* Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). */ + if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } + + num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } + } + + do + { + TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; + if (r->m_type == 0) + { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } + while ((counter) && (num_bits)) + { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) + { + size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } + while (pIn_buf_cur >= pIn_buf_end) + { + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) + { + TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); + } + else + { + TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); + } + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; + } + } + else if (r->m_type == 3) + { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } + else + { + if (r->m_type == 1) + { + mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; + r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for ( i = 0; i <= 143; ++i) *p++ = 8; + for ( ; i <= 255; ++i) *p++ = 9; + for ( ; i <= 279; ++i) *p++ = 7; + for ( ; i <= 287; ++i) *p++ = 8; + } + else + { + for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } + r->m_table_sizes[2] = 19; + } + for ( ; (int)r->m_type >= 0; r->m_type--) + { + int tree_next, tree_cur; tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } + if ((65536 != total) && (used_syms > 1)) + { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) + { + mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; + cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } + if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) + { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; + } + tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) + { + for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) + { + mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } + if ((dist == 16) && (!counter)) + { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) + { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); + } + } + for ( ; ; ) + { + mz_uint8 *pSrc; + for ( ; ; ) + { + if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) + { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = (mz_uint8)counter; + } + else + { + int sym2; mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } +#else + if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + counter = sym2; bit_buf >>= code_len; num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } +#endif + if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) + code_len = sym2 >> 9; + else + { + code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); + } + bit_buf >>= code_len; num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) + { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) break; + + num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; + if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) + { + while (counter--) + { + while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } + *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) + { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do + { + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) + { + if (counter) + { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do + { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) + { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) + { + TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + TINFL_CR_FINISH + +common_exit: + r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) + { + const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; + while (buf_len) + { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) + { + s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; + } + for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +/* Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other stuff is for advanced use. */ +enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; + +/* Return status codes. MZ_PARAM_ERROR is non-standard. */ +enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; + +/* Compression levels. */ +enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_DEFAULT_COMPRESSION = -1 }; + +/* Window bits */ +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +/* Compression/decompression stream struct. */ +typedef struct mz_stream_s +{ + const unsigned char *next_in; /* pointer to next byte to read */ + unsigned int avail_in; /* number of bytes available at next_in */ + mz_ulong total_in; /* total number of bytes consumed so far */ + + unsigned char *next_out; /* pointer to next byte to write */ + unsigned int avail_out; /* number of bytes that can be written to next_out */ + mz_ulong total_out; /* total number of bytes produced so far */ + + char *msg; /* error msg (unused) */ + struct mz_internal_state *state; /* internal state, allocated by zalloc/zfree */ + + mz_alloc_func zalloc; /* optional heap allocation function (defaults to malloc) */ + mz_free_func zfree; /* optional heap free function (defaults to free) */ + void *opaque; /* heap alloc function user pointer */ + + int data_type; /* data_type (unused) */ + mz_ulong adler; /* adler32 of the source or uncompressed data */ + mz_ulong reserved; /* not used */ +} mz_stream; + +typedef mz_stream *mz_streamp; + + +typedef struct +{ + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +static int mz_inflateInit2(mz_streamp pStream, int window_bits) +{ + inflate_state *pDecomp; + if (!pStream) return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + /* if (!pStream->zalloc) pStream->zalloc = def_alloc_func; */ + /* if (!pStream->zfree) pStream->zfree = def_free_func; */ + + pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); + if (!pDecomp) return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +static int mz_inflate(mz_streamp pStream, int flush) +{ + inflate_state* pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + + pState = (inflate_state*)pStream->state; + if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; pState->m_first_call = 0; + if (pState->m_last_status < 0) return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) + { + /* MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. */ + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) + { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + /* flush != MZ_FINISH then we must assume there's more input. */ + if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) + { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; + } + + for ( ; ; ) + { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; + pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; /* Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). */ + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; /* Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. */ + else if (flush == MZ_FINISH) + { + /* The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. */ + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + /* status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. */ + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } + else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; +} + +static int mz_inflateEnd(mz_streamp pStream) +{ + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) + { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +/* make this a drop-in replacement for zlib... */ + #define voidpf void* + #define uInt unsigned int + #define z_stream mz_stream + #define inflateInit2 mz_inflateInit2 + #define inflate mz_inflate + #define inflateEnd mz_inflateEnd + #define Z_SYNC_FLUSH MZ_SYNC_FLUSH + #define Z_FINISH MZ_FINISH + #define Z_OK MZ_OK + #define Z_STREAM_END MZ_STREAM_END + #define Z_NEED_DICT MZ_NEED_DICT + #define Z_ERRNO MZ_ERRNO + #define Z_STREAM_ERROR MZ_STREAM_ERROR + #define Z_DATA_ERROR MZ_DATA_ERROR + #define Z_MEM_ERROR MZ_MEM_ERROR + #define Z_BUF_ERROR MZ_BUF_ERROR + #define Z_VERSION_ERROR MZ_VERSION_ERROR + #define MAX_WBITS 15 + +#endif /* #ifndef TINFL_HEADER_FILE_ONLY */ + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to <https://unlicense.org/> +*/ diff --git a/Source/3rdParty/physfs/physfs_platform_apple.m b/Source/3rdParty/physfs/physfs_platform_apple.m new file mode 100644 index 0000000..fe50f4b --- /dev/null +++ b/Source/3rdParty/physfs/physfs_platform_apple.m @@ -0,0 +1,188 @@ +/* + * Apple platform (macOS, iOS, watchOS, etc) support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_APPLE + +#include <Foundation/Foundation.h> + +#include "physfs_internal.h" + +int __PHYSFS_platformInit(void) +{ + return 1; /* success. */ +} /* __PHYSFS_platformInit */ + + +void __PHYSFS_platformDeinit(void) +{ + /* no-op */ +} /* __PHYSFS_platformDeinit */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + @autoreleasepool + { + NSString *path = [[NSBundle mainBundle] bundlePath]; + BAIL_IF(!path, PHYSFS_ERR_OS_ERROR, NULL); + size_t len = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + char *retval = (char *) allocator.Malloc(len + 2); + BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + [path getCString:retval maxLength:len+1 encoding:NSUTF8StringEncoding]; + retval[len] = '/'; + retval[len+1] = '\0'; + return retval; /* whew. */ + } /* @autoreleasepool */ +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) +{ + @autoreleasepool + { + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, TRUE); + BAIL_IF(!paths, PHYSFS_ERR_OS_ERROR, NULL); + NSString *path = (NSString *) paths[0]; + BAIL_IF(!path, PHYSFS_ERR_OS_ERROR, NULL); + size_t len = [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + const size_t applen = strlen(app); + char *retval = (char *) allocator.Malloc(len + applen + 3); + BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + [path getCString:retval maxLength:len+1 encoding:NSUTF8StringEncoding]; + snprintf(retval + len, applen + 3, "/%s/", app); + return retval; /* whew. */ + } /* @autoreleasepool */ +} /* __PHYSFS_platformCalcPrefDir */ + + +/* CD-ROM detection code... */ + +/* + * Code based on sample from Apple Developer Connection: + * https://developer.apple.com/samplecode/Sample_Code/Devices_and_Hardware/Disks/VolumeToBSDNode/VolumeToBSDNode.c.htm + */ + +#if !defined(PHYSFS_NO_CDROM_SUPPORT) + +#include <IOKit/IOKitLib.h> +#include <IOKit/storage/IOMedia.h> +#include <IOKit/storage/IOCDMedia.h> +#include <IOKit/storage/IODVDMedia.h> +#include <sys/mount.h> + +static int darwinIsWholeMedia(io_service_t service) +{ + int retval = 0; + CFTypeRef wholeMedia; + + if (!IOObjectConformsTo(service, kIOMediaClass)) + return 0; + + wholeMedia = IORegistryEntryCreateCFProperty(service, + CFSTR(kIOMediaWholeKey), + NULL, 0); + if (wholeMedia == NULL) + return 0; + + retval = CFBooleanGetValue(wholeMedia); + CFRelease(wholeMedia); + + return retval; +} /* darwinIsWholeMedia */ + + +static int darwinIsMountedDisc(char *bsdName, mach_port_t masterPort) +{ + int retval = 0; + CFMutableDictionaryRef matchingDict; + kern_return_t rc; + io_iterator_t iter; + io_service_t service; + + if ((matchingDict = IOBSDNameMatching(masterPort, 0, bsdName)) == NULL) + return 0; + + rc = IOServiceGetMatchingServices(masterPort, matchingDict, &iter); + if ((rc != KERN_SUCCESS) || (!iter)) + return 0; + + service = IOIteratorNext(iter); + IOObjectRelease(iter); + if (!service) + return 0; + + rc = IORegistryEntryCreateIterator(service, kIOServicePlane, + kIORegistryIterateRecursively | kIORegistryIterateParents, &iter); + + if (!iter) + return 0; + + if (rc != KERN_SUCCESS) + { + IOObjectRelease(iter); + return 0; + } /* if */ + + IOObjectRetain(service); /* add an extra object reference... */ + + do + { + if (darwinIsWholeMedia(service)) + { + if ( (IOObjectConformsTo(service, kIOCDMediaClass)) || + (IOObjectConformsTo(service, kIODVDMediaClass)) ) + { + retval = 1; + } /* if */ + } /* if */ + IOObjectRelease(service); + } while ((service = IOIteratorNext(iter)) && (!retval)); + + IOObjectRelease(iter); + IOObjectRelease(service); + + return retval; +} /* darwinIsMountedDisc */ + +#endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ +#if !defined(PHYSFS_NO_CDROM_SUPPORT) + const char *devPrefix = "/dev/"; + const int prefixLen = strlen(devPrefix); + mach_port_t masterPort = 0; + struct statfs *mntbufp; + int i, mounts; + + if (IOMasterPort(MACH_PORT_NULL, &masterPort) != KERN_SUCCESS) + BAIL(PHYSFS_ERR_OS_ERROR, ) /*return void*/; + + mounts = getmntinfo(&mntbufp, MNT_WAIT); /* NOT THREAD SAFE! */ + for (i = 0; i < mounts; i++) + { + char *dev = mntbufp[i].f_mntfromname; + char *mnt = mntbufp[i].f_mntonname; + if (strncmp(dev, devPrefix, prefixLen) != 0) /* a virtual device? */ + continue; + + dev += prefixLen; + if (darwinIsMountedDisc(dev, masterPort)) + cb(data, mnt); + } /* for */ +#endif /* !defined(PHYSFS_NO_CDROM_SUPPORT) */ +} /* __PHYSFS_platformDetectAvailableCDs */ + +#endif /* PHYSFS_PLATFORM_APPLE */ + +/* end of physfs_platform_apple.m ... */ + diff --git a/Source/3rdParty/physfs/physfs_platform_haiku.cpp b/Source/3rdParty/physfs/physfs_platform_haiku.cpp new file mode 100644 index 0000000..fcf8ebd --- /dev/null +++ b/Source/3rdParty/physfs/physfs_platform_haiku.cpp @@ -0,0 +1,186 @@ +/* + * Haiku platform-dependent support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_HAIKU + +#include <os/kernel/OS.h> +#include <os/app/Roster.h> +#include <os/storage/Volume.h> +#include <os/storage/VolumeRoster.h> +#include <os/storage/Directory.h> +#include <os/storage/Entry.h> +#include <os/storage/Path.h> +#include <os/kernel/fs_info.h> +#include <os/device/scsi.h> + +#include <errno.h> +#include <unistd.h> + +#include "physfs_internal.h" + +int __PHYSFS_platformInit(void) +{ + return 1; /* always succeed. */ +} /* __PHYSFS_platformInit */ + + +void __PHYSFS_platformDeinit(void) +{ + /* no-op */ +} /* __PHYSFS_platformDeinit */ + + +static char *getMountPoint(const char *devname, char *buf, size_t bufsize) +{ + BVolumeRoster mounts; + BVolume vol; + + mounts.Rewind(); + while (mounts.GetNextVolume(&vol) == B_NO_ERROR) + { + fs_info fsinfo; + fs_stat_dev(vol.Device(), &fsinfo); + if (strcmp(devname, fsinfo.device_name) == 0) + { + BDirectory directory; + BEntry entry; + BPath path; + const char *str; + + if ( (vol.GetRootDirectory(&directory) < B_OK) || + (directory.GetEntry(&entry) < B_OK) || + (entry.GetPath(&path) < B_OK) || + ( (str = path.Path()) == NULL) ) + return NULL; + + strncpy(buf, str, bufsize-1); + buf[bufsize-1] = '\0'; + return buf; + } /* if */ + } /* while */ + + return NULL; +} /* getMountPoint */ + + + /* + * This function is lifted from Simple Directmedia Layer (SDL): + * https://www.libsdl.org/ ... this is zlib-licensed code, too. + */ +static void tryDir(const char *d, PHYSFS_StringCallback callback, void *data) +{ + BDirectory dir; + dir.SetTo(d); + if (dir.InitCheck() != B_NO_ERROR) + return; + + dir.Rewind(); + BEntry entry; + while (dir.GetNextEntry(&entry) >= 0) + { + BPath path; + const char *name; + entry_ref e; + + if (entry.GetPath(&path) != B_NO_ERROR) + continue; + + name = path.Path(); + + if (entry.GetRef(&e) != B_NO_ERROR) + continue; + + if (entry.IsDirectory()) + { + if (strcmp(e.name, "floppy") != 0) + tryDir(name, callback, data); + continue; + } /* if */ + + const int devfd = open(name, O_RDONLY); + if (devfd < 0) + continue; + + device_geometry g; + const int rc = ioctl(devfd, B_GET_GEOMETRY, &g, sizeof (g)); + close(devfd); + if (rc < 0) + continue; + + if (g.device_type != B_CD) + continue; + + char mntpnt[B_FILE_NAME_LENGTH]; + if (getMountPoint(name, mntpnt, sizeof (mntpnt))) + callback(data, mntpnt); + } /* while */ +} /* tryDir */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + tryDir("/dev/disk", cb, data); +} /* __PHYSFS_platformDetectAvailableCDs */ + + +static team_id getTeamID(void) +{ + thread_info info; + thread_id tid = find_thread(NULL); + get_thread_info(tid, &info); + return info.team; +} /* getTeamID */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + image_info info; + int32 cookie = 0; + + while (get_next_image_info(0, &cookie, &info) == B_OK) + { + if (info.type == B_APP_IMAGE) + break; + } /* while */ + + BEntry entry(info.name, true); + BPath path; + status_t rc = entry.GetPath(&path); /* (path) now has binary's path. */ + assert(rc == B_OK); + rc = path.GetParent(&path); /* chop filename, keep directory. */ + assert(rc == B_OK); + const char *str = path.Path(); + assert(str != NULL); + const size_t len = strlen(str); + char *retval = (char *) allocator.Malloc(len + 2); + BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + strcpy(retval, str); + retval[len] = '/'; + retval[len+1] = '\0'; + return retval; +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) +{ + const char *userdir = __PHYSFS_getUserDir(); + const char *append = "config/settings/"; + const size_t len = strlen(userdir) + strlen(append) + strlen(app) + 2; + char *retval = (char *) allocator.Malloc(len); + BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + snprintf(retval, len, "%s%s%s/", userdir, append, app); + return retval; +} /* __PHYSFS_platformCalcPrefDir */ + +#endif /* PHYSFS_PLATFORM_HAIKU */ + +/* end of physfs_platform_haiku.cpp ... */ + diff --git a/Source/3rdParty/physfs/physfs_platform_os2.c b/Source/3rdParty/physfs/physfs_platform_os2.c new file mode 100644 index 0000000..8cc8044 --- /dev/null +++ b/Source/3rdParty/physfs/physfs_platform_os2.c @@ -0,0 +1,812 @@ +/* + * OS/2 support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_OS2 + +#define INCL_DOSMODULEMGR +#define INCL_DOSSEMAPHORES +#define INCL_DOSDATETIME +#define INCL_DOSFILEMGR +#define INCL_DOSMODULEMGR +#define INCL_DOSERRORS +#define INCL_DOSPROCESS +#define INCL_DOSDEVICES +#define INCL_DOSDEVIOCTL +#define INCL_DOSMISC +#include <os2.h> +#include <uconv.h> + +#include <errno.h> +#include <time.h> +#include <ctype.h> + +#include "physfs_internal.h" + +static HMODULE uconvdll = 0; +static UconvObject uconv = 0; +static int (_System *pUniCreateUconvObject)(UniChar *, UconvObject *) = NULL; +static int (_System *pUniFreeUconvObject)(UconvObject *) = NULL; +static int (_System *pUniUconvToUcs)(UconvObject,void **,size_t *, UniChar**, size_t *, size_t *) = NULL; +static int (_System *pUniUconvFromUcs)(UconvObject,UniChar **,size_t *,void **,size_t *,size_t *) = NULL; + +static PHYSFS_ErrorCode errcodeFromAPIRET(const APIRET rc) +{ + switch (rc) + { + case NO_ERROR: return PHYSFS_ERR_OK; /* not an error. */ + case ERROR_INTERRUPT: return PHYSFS_ERR_OK; /* not an error. */ + case ERROR_TIMEOUT: return PHYSFS_ERR_OK; /* not an error. */ + case ERROR_NOT_ENOUGH_MEMORY: return PHYSFS_ERR_OUT_OF_MEMORY; + case ERROR_FILE_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND; + case ERROR_PATH_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND; + case ERROR_ACCESS_DENIED: return PHYSFS_ERR_PERMISSION; + case ERROR_NOT_DOS_DISK: return PHYSFS_ERR_NOT_FOUND; + case ERROR_SHARING_VIOLATION: return PHYSFS_ERR_PERMISSION; + case ERROR_CANNOT_MAKE: return PHYSFS_ERR_IO; /* maybe this is wrong? */ + case ERROR_DEVICE_IN_USE: return PHYSFS_ERR_BUSY; + case ERROR_OPEN_FAILED: return PHYSFS_ERR_IO; /* maybe this is wrong? */ + case ERROR_DISK_FULL: return PHYSFS_ERR_NO_SPACE; + case ERROR_PIPE_BUSY: return PHYSFS_ERR_BUSY; + case ERROR_SHARING_BUFFER_EXCEEDED: return PHYSFS_ERR_IO; + case ERROR_FILENAME_EXCED_RANGE: return PHYSFS_ERR_BAD_FILENAME; + case ERROR_META_EXPANSION_TOO_LONG: return PHYSFS_ERR_BAD_FILENAME; + case ERROR_TOO_MANY_HANDLES: return PHYSFS_ERR_IO; + case ERROR_TOO_MANY_OPEN_FILES: return PHYSFS_ERR_IO; + case ERROR_NO_MORE_SEARCH_HANDLES: return PHYSFS_ERR_IO; + case ERROR_SEEK_ON_DEVICE: return PHYSFS_ERR_IO; + case ERROR_NEGATIVE_SEEK: return PHYSFS_ERR_INVALID_ARGUMENT; + case ERROR_WRITE_PROTECT: return PHYSFS_ERR_PERMISSION; + case ERROR_WRITE_FAULT: return PHYSFS_ERR_IO; + case ERROR_UNCERTAIN_MEDIA: return PHYSFS_ERR_IO; + case ERROR_PROTECTION_VIOLATION: return PHYSFS_ERR_IO; + case ERROR_BROKEN_PIPE: return PHYSFS_ERR_IO; + + /* !!! FIXME: some of these might be PHYSFS_ERR_BAD_FILENAME, etc */ + case ERROR_LOCK_VIOLATION: + case ERROR_GEN_FAILURE: + case ERROR_INVALID_PARAMETER: + case ERROR_INVALID_NAME: + case ERROR_INVALID_DRIVE: + case ERROR_INVALID_HANDLE: + case ERROR_INVALID_FUNCTION: + case ERROR_INVALID_LEVEL: + case ERROR_INVALID_CATEGORY: + case ERROR_DUPLICATE_NAME: + case ERROR_BUFFER_OVERFLOW: + case ERROR_BAD_LENGTH: + case ERROR_BAD_DRIVER_LEVEL: + case ERROR_DIRECT_ACCESS_HANDLE: + case ERROR_NOT_OWNER: + return PHYSFS_ERR_OS_ERROR; + + default: break; + } /* switch */ + + return PHYSFS_ERR_OTHER_ERROR; +} /* errcodeFromAPIRET */ + +static char *cvtUtf8ToCodepage(const char *utf8str) +{ + const size_t len = strlen(utf8str) + 1; + const size_t uc2buflen = len * sizeof (UniChar); + UniChar *uc2ptr = (UniChar *) __PHYSFS_smallAlloc(uc2buflen); + UniChar *uc2str = uc2ptr; + char *cpptr = NULL; + char *cpstr = NULL; + size_t subs = 0; + size_t unilen; + + BAIL_IF(!uc2str, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + PHYSFS_utf8ToUcs2(utf8str, (PHYSFS_uint16 *) uc2str, uc2buflen); + for (unilen = 0; uc2str[unilen]; unilen++) { /* spin */ } + unilen++; /* null terminator. */ + + if (!uconvdll) + { + /* There's really not much we can do on older OS/2s except pray this + is latin1-compatible. */ + size_t i; + cpptr = (char *) allocator.Malloc(unilen); + cpstr = cpptr; + GOTO_IF(!cpptr, PHYSFS_ERR_OUT_OF_MEMORY, failed); + for (i = 0; i < unilen; i++) + { + const UniChar ch = uc2str[i]; + GOTO_IF(ch > 0xFF, PHYSFS_ERR_BAD_FILENAME, failed); + cpptr[i] = (char) ((unsigned char) ch); + } /* for */ + + __PHYSFS_smallFree(uc2ptr); + return cpstr; + } /* if */ + else + { + int rc; + size_t cplen = unilen * 4; /* overallocate, just in case. */ + cpptr = (char *) allocator.Malloc(cplen); + GOTO_IF(!cpptr, PHYSFS_ERR_OUT_OF_MEMORY, failed); + cpstr = cpptr; + + rc = pUniUconvFromUcs(uconv, &uc2str, &unilen, (void **) &cpstr, &cplen, &subs); + GOTO_IF(rc != ULS_SUCCESS, PHYSFS_ERR_BAD_FILENAME, failed); + GOTO_IF(subs > 0, PHYSFS_ERR_BAD_FILENAME, failed); + assert(unilen == 0); + + __PHYSFS_smallFree(uc2ptr); + return cpptr; + } /* else */ + +failed: + __PHYSFS_smallFree(uc2ptr); + allocator.Free(cpptr); + + return NULL; +} /* cvtUtf8ToCodepage */ + +static char *cvtCodepageToUtf8(const char *cpstr) +{ + const size_t len = strlen(cpstr) + 1; + char *retvalbuf = (char *) allocator.Malloc(len * 4); + char *retval = NULL; + + BAIL_IF(!retvalbuf, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + + if (!uconvdll) + { + /* There's really not much we can do on older OS/2s except pray this + is latin1-compatible. */ + retval = retvalbuf; + PHYSFS_utf8FromLatin1(cpstr, retval, len * 4); + } /* if */ + else + { + int rc; + size_t cplen = len; + size_t unilen = len; + size_t subs = 0; + UniChar *uc2ptr = __PHYSFS_smallAlloc(len * sizeof (UniChar)); + UniChar *uc2str = uc2ptr; + + BAIL_IF(!uc2ptr, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + rc = pUniUconvToUcs(uconv, (void **) &cpstr, &cplen, &uc2str, &unilen, &subs); + GOTO_IF(rc != ULS_SUCCESS, PHYSFS_ERR_BAD_FILENAME, done); + GOTO_IF(subs > 0, PHYSFS_ERR_BAD_FILENAME, done); + assert(cplen == 0); + retval = retvalbuf; + PHYSFS_utf8FromUcs2((const PHYSFS_uint16 *) uc2ptr, retval, len * 4); + done: + __PHYSFS_smallFree(uc2ptr); + } /* else */ + + return retval; +} /* cvtCodepageToUtf8 */ + + +/* (be gentle, this function isn't very robust.) */ +static char *cvtPathToCorrectCase(char *buf) +{ + char *retval = buf; + char *fname = buf + 3; /* point to first element. */ + char *ptr = strchr(fname, '\\'); /* find end of first element. */ + + buf[0] = toupper(buf[0]); /* capitalize drive letter. */ + + /* + * Go through each path element, and enumerate its parent dir until + * a case-insensitive match is found. If one is (and it SHOULD be) + * then overwrite the original element with the correct case. + * If there's an error, or the path has vanished for some reason, it + * won't hurt to have the original case, so we just keep going. + */ + while ((fname != NULL) && (*fname != '\0')) + { + char spec[CCHMAXPATH]; + FILEFINDBUF3 fb; + HDIR hdir = HDIR_CREATE; + ULONG count = 1; + APIRET rc; + + *(fname - 1) = '\0'; /* isolate parent dir string. */ + + strcpy(spec, buf); /* copy isolated parent dir... */ + strcat(spec, "\\*.*"); /* ...and add wildcard search spec. */ + + if (ptr != NULL) /* isolate element to find (fname is the start). */ + *ptr = '\0'; + + rc = DosFindFirst((unsigned char *) spec, &hdir, FILE_DIRECTORY, + &fb, sizeof (fb), &count, FIL_STANDARD); + if (rc == NO_ERROR) + { + while (count == 1) /* while still entries to enumerate... */ + { + int cmp; + char *utf8 = cvtCodepageToUtf8(fb.achName); + if (!utf8) /* ugh, maybe we'll get lucky with the C runtime. */ + cmp = stricmp(fb.achName, fname); + else + { + cmp = PHYSFS_utf8stricmp(utf8, fname); + allocator.Free(utf8); + } /* else */ + + if (cmp == 0) + { + strcpy(fname, fb.achName); + break; /* there it is. Overwrite and stop searching. */ + } /* if */ + + DosFindNext(hdir, &fb, sizeof (fb), &count); + } /* while */ + DosFindClose(hdir); + } /* if */ + + *(fname - 1) = '\\'; /* unisolate parent dir. */ + fname = ptr; /* point to next element. */ + if (ptr != NULL) + { + *ptr = '\\'; /* unisolate element. */ + ptr = strchr(++fname, '\\'); /* find next element. */ + } /* if */ + } /* while */ + + return retval; +} /* cvtPathToCorrectCase */ + +static void prepUnicodeSupport(void) +{ + /* really old OS/2 might not have Unicode support _at all_, so load + the system library and do without if it doesn't exist. */ + int ok = 0; + char buf[CCHMAXPATH]; + UniChar defstr[] = { 0 }; + if (DosLoadModule(buf, sizeof (buf) - 1, "uconv", &uconvdll) == NO_ERROR) + { + #define LOAD(x) (DosQueryProcAddr(uconvdll,0,#x,(PFN*)&p##x)==NO_ERROR) + ok = LOAD(UniCreateUconvObject) && + LOAD(UniFreeUconvObject) && + LOAD(UniUconvToUcs) && + LOAD(UniUconvFromUcs); + #undef LOAD + } /* else */ + + if (!ok || (pUniCreateUconvObject(defstr, &uconv) != ULS_SUCCESS)) + { + /* oh well, live without it. */ + if (uconvdll) + { + if (uconv) + pUniFreeUconvObject(uconv); + DosFreeModule(uconvdll); + uconvdll = 0; + } /* if */ + } /* if */ +} /* prepUnicodeSupport */ + + +int __PHYSFS_platformInit(void) +{ + prepUnicodeSupport(); + return 1; /* ready to go! */ +} /* __PHYSFS_platformInit */ + + +void __PHYSFS_platformDeinit(void) +{ + if (uconvdll) + { + pUniFreeUconvObject(uconv); + uconv = 0; + DosFreeModule(uconvdll); + uconvdll = 0; + } /* if */ +} /* __PHYSFS_platformDeinit */ + + +static int discIsInserted(ULONG drive) +{ + int rc; + char buf[20]; + DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION); + rc = DosQueryFSInfo(drive + 1, FSIL_VOLSER, buf, sizeof (buf)); + DosError(FERR_ENABLEHARDERR | FERR_ENABLEEXCEPTION); + return (rc == NO_ERROR); +} /* is_cdrom_inserted */ + + +/* looks like "CD01" in ASCII (littleendian)...used for an ioctl. */ +#define CD01 0x31304443 + +static int isCdRomDrive(ULONG drive) +{ + PHYSFS_uint32 param, data; + ULONG ul1, ul2; + APIRET rc; + HFILE hfile = NULLHANDLE; + unsigned char drivename[3] = { 0, ':', '\0' }; + + drivename[0] = 'A' + drive; + + rc = DosOpen(drivename, &hfile, &ul1, 0, 0, + OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, + OPEN_FLAGS_DASD | OPEN_FLAGS_FAIL_ON_ERROR | + OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYNONE, NULL); + if (rc != NO_ERROR) + return 0; + + data = 0; + param = PHYSFS_swapULE32(CD01); + ul1 = ul2 = sizeof (PHYSFS_uint32); + rc = DosDevIOCtl(hfile, IOCTL_CDROMDISK, CDROMDISK_GETDRIVER, + ¶m, sizeof (param), &ul1, &data, sizeof (data), &ul2); + + DosClose(hfile); + return ((rc == NO_ERROR) && (PHYSFS_swapULE32(data) == CD01)); +} /* isCdRomDrive */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + ULONG dummy = 0; + ULONG drivemap = 0; + ULONG i, bit; + const APIRET rc = DosQueryCurrentDisk(&dummy, &drivemap); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc),); + + for (i = 0, bit = 1; i < 26; i++, bit <<= 1) + { + if (drivemap & bit) /* this logical drive exists. */ + { + if ((isCdRomDrive(i)) && (discIsInserted(i))) + { + char drive[4] = "x:\\"; + drive[0] = ('A' + i); + cb(data, drive); + } /* if */ + } /* if */ + } /* for */ +} /* __PHYSFS_platformDetectAvailableCDs */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + char *retval = NULL; + char buf[CCHMAXPATH]; + APIRET rc; + PTIB ptib; + PPIB ppib; + PHYSFS_sint32 len; + + rc = DosGetInfoBlocks(&ptib, &ppib); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0); + rc = DosQueryModuleName(ppib->pib_hmte, sizeof (buf), (PCHAR) buf); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0); + retval = cvtCodepageToUtf8(buf); + BAIL_IF_ERRPASS(!retval, NULL); + + /* chop off filename, leave path. */ + for (len = strlen(retval) - 1; len >= 0; len--) + { + if (retval[len] == '\\') + { + retval[len + 1] = '\0'; + break; + } /* if */ + } /* for */ + + assert(len > 0); /* should have been a "x:\\" on the front on string. */ + + /* The string is capitalized! Figure out the REAL case... */ + return cvtPathToCorrectCase(retval); +} /* __PHYSFS_platformCalcBaseDir */ + +char *__PHYSFS_platformCalcUserDir(void) +{ + return __PHYSFS_platformCalcBaseDir(NULL); /* !!! FIXME: ? */ +} /* __PHYSFS_platformCalcUserDir */ + +char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) +{ + return __PHYSFS_platformCalcBaseDir(NULL); /* !!! FIXME: ? */ +} /* __PHYSFS_platformCalcPrefDir */ + +PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname, + PHYSFS_EnumerateCallback callback, + const char *origdir, void *callbackdata) +{ + PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK; + size_t utf8len = strlen(dirname); + char *utf8 = (char *) __PHYSFS_smallAlloc(utf8len + 5); + char *cpspec = NULL; + FILEFINDBUF3 fb; + HDIR hdir = HDIR_CREATE; + ULONG count = 1; + APIRET rc; + + BAIL_IF(!utf8, PHYSFS_ERR_OUT_OF_MEMORY, PHYSFS_ENUM_ERROR); + + strcpy(utf8, dirname); + if (utf8[utf8len - 1] != '\\') + strcpy(utf8 + utf8len, "\\*.*"); + else + strcpy(utf8 + utf8len, "*.*"); + + cpspec = cvtUtf8ToCodepage(utf8); + __PHYSFS_smallFree(utf8); + BAIL_IF_ERRPASS(!cpspec, PHYSFS_ENUM_ERROR); + + rc = DosFindFirst((unsigned char *) cpspec, &hdir, + FILE_DIRECTORY | FILE_ARCHIVED | + FILE_READONLY | FILE_HIDDEN | FILE_SYSTEM, + &fb, sizeof (fb), &count, FIL_STANDARD); + allocator.Free(cpspec); + + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), PHYSFS_ENUM_ERROR); + + while (count == 1) + { + if ((strcmp(fb.achName, ".") != 0) && (strcmp(fb.achName, "..") != 0)) + { + utf8 = cvtCodepageToUtf8(fb.achName); + if (!utf8) + retval = PHYSFS_ENUM_ERROR; + else + { + retval = callback(callbackdata, origdir, utf8); + allocator.Free(utf8); + if (retval == PHYSFS_ENUM_ERROR) + PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK); + } /* else */ + } /* if */ + + if (retval != PHYSFS_ENUM_OK) + break; + + DosFindNext(hdir, &fb, sizeof (fb), &count); + } /* while */ + + DosFindClose(hdir); + + return retval; +} /* __PHYSFS_platformEnumerate */ + + +char *__PHYSFS_platformCurrentDir(void) +{ + char *retval; + char *cpstr; + char *utf8; + ULONG currentDisk; + ULONG dummy; + ULONG pathSize = 0; + APIRET rc; + BYTE byte; + + rc = DosQueryCurrentDisk(¤tDisk, &dummy); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), NULL); + + /* The first call just tells us how much space we need for the string. */ + rc = DosQueryCurrentDir(currentDisk, &byte, &pathSize); + pathSize++; /* Add space for null terminator. */ + cpstr = (char *) __PHYSFS_smallAlloc(pathSize); + BAIL_IF(cpstr == NULL, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + + /* Actually get the string this time. */ + rc = DosQueryCurrentDir(currentDisk, (PBYTE) cpstr, &pathSize); + if (rc != NO_ERROR) + { + __PHYSFS_smallFree(cpstr); + BAIL(errcodeFromAPIRET(rc), NULL); + } /* if */ + + utf8 = cvtCodepageToUtf8(cpstr); + __PHYSFS_smallFree(cpstr); + BAIL_IF_ERRPASS(utf8 == NULL, NULL); + + /* +4 for "x:\\" drive selector and null terminator. */ + retval = (char *) allocator.Malloc(strlen(utf8) + 4); + if (retval == NULL) + { + allocator.Free(utf8); + BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + retval[0] = ('A' + (currentDisk - 1)); + retval[1] = ':'; + retval[2] = '\\'; + strcpy(retval + 3, utf8); + + allocator.Free(utf8); + + return retval; +} /* __PHYSFS_platformCurrentDir */ + + +int __PHYSFS_platformMkDir(const char *filename) +{ + APIRET rc; + char *cpstr = cvtUtf8ToCodepage(filename); + BAIL_IF_ERRPASS(!cpstr, 0); + rc = DosCreateDir((unsigned char *) cpstr, NULL); + allocator.Free(cpstr); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0); + return 1; +} /* __PHYSFS_platformMkDir */ + + +static HFILE openFile(const char *filename, const ULONG flags, const ULONG mode) +{ + char *cpfname = cvtUtf8ToCodepage(filename); + ULONG action = 0; + HFILE hfile = NULLHANDLE; + APIRET rc; + + BAIL_IF_ERRPASS(!cpfname, 0); + + rc = DosOpen(cpfname, &hfile, &action, 0, FILE_NORMAL, flags, mode, NULL); + allocator.Free(cpfname); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0); + + return hfile; +} /* openFile */ + +void *__PHYSFS_platformOpenRead(const char *filename) +{ + /* + * File must be opened SHARE_DENYWRITE and ACCESS_READONLY, otherwise + * DosQueryFileInfo() will fail if we try to get a file length, etc. + */ + return (void *) openFile(filename, + OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW, + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY | + OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE | + OPEN_ACCESS_READONLY); +} /* __PHYSFS_platformOpenRead */ + + +void *__PHYSFS_platformOpenWrite(const char *filename) +{ + return (void *) openFile(filename, + OPEN_ACTION_REPLACE_IF_EXISTS | + OPEN_ACTION_CREATE_IF_NEW, + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY | + OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE); +} /* __PHYSFS_platformOpenWrite */ + + +void *__PHYSFS_platformOpenAppend(const char *filename) +{ + APIRET rc; + ULONG dummy = 0; + HFILE hfile; + + /* + * File must be opened SHARE_DENYWRITE and ACCESS_READWRITE, otherwise + * DosQueryFileInfo() will fail if we try to get a file length, etc. + */ + hfile = openFile(filename, + OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW, + OPEN_FLAGS_FAIL_ON_ERROR | OPEN_FLAGS_NO_LOCALITY | + OPEN_FLAGS_NOINHERIT | OPEN_SHARE_DENYWRITE | + OPEN_ACCESS_READWRITE); + BAIL_IF_ERRPASS(!hfile, NULL); + + rc = DosSetFilePtr(hfile, 0, FILE_END, &dummy); + if (rc != NO_ERROR) + { + DosClose(hfile); + BAIL(errcodeFromAPIRET(rc), NULL); + } /* if */ + + return ((void *) hfile); +} /* __PHYSFS_platformOpenAppend */ + + +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len) +{ + ULONG br = 0; + APIRET rc; + BAIL_IF(!__PHYSFS_ui64FitsAddressSpace(len),PHYSFS_ERR_INVALID_ARGUMENT,-1); + rc = DosRead((HFILE) opaque, buf, (ULONG) len, &br); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), (br > 0) ? ((PHYSFS_sint64) br) : -1); + return (PHYSFS_sint64) br; +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buf, + PHYSFS_uint64 len) +{ + ULONG bw = 0; + APIRET rc; + BAIL_IF(!__PHYSFS_ui64FitsAddressSpace(len),PHYSFS_ERR_INVALID_ARGUMENT,-1); + rc = DosWrite((HFILE) opaque, (void *) buf, (ULONG) len, &bw); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), (bw > 0) ? ((PHYSFS_sint64) bw) : -1); + return (PHYSFS_sint64) bw; +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + ULONG dummy; + HFILE hfile = (HFILE) opaque; + LONG dist = (LONG) pos; + APIRET rc; + + /* hooray for 32-bit filesystem limits! :) */ + BAIL_IF((PHYSFS_uint64) dist != pos, PHYSFS_ERR_INVALID_ARGUMENT, 0); + rc = DosSetFilePtr(hfile, dist, FILE_BEGIN, &dummy); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0); + return 1; +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + ULONG pos; + HFILE hfile = (HFILE) opaque; + const APIRET rc = DosSetFilePtr(hfile, 0, FILE_CURRENT, &pos); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), -1); + return ((PHYSFS_sint64) pos); +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + FILESTATUS3 fs; + HFILE hfile = (HFILE) opaque; + const APIRET rc = DosQueryFileInfo(hfile, FIL_STANDARD, &fs, sizeof (fs)); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), -1); + return ((PHYSFS_sint64) fs.cbFile); +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + const APIRET rc = DosResetBuffer((HFILE) opaque); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0); + return 1; +} /* __PHYSFS_platformFlush */ + + +void __PHYSFS_platformClose(void *opaque) +{ + DosClose((HFILE) opaque); /* ignore errors. You should have flushed! */ +} /* __PHYSFS_platformClose */ + + +int __PHYSFS_platformDelete(const char *path) +{ + char *cppath = cvtUtf8ToCodepage(path); + FILESTATUS3 fs; + APIRET rc; + int retval = 0; + + BAIL_IF_ERRPASS(!cppath, 0); + rc = DosQueryPathInfo(cppath, FIL_STANDARD, &fs, sizeof (fs)); + GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done); + rc = (fs.attrFile & FILE_DIRECTORY) ? DosDeleteDir(path) : DosDelete(path); + GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done); + retval = 1; /* success */ + +done: + allocator.Free(cppath); + return retval; +} /* __PHYSFS_platformDelete */ + + +/* Convert to a format PhysicsFS can grok... */ +PHYSFS_sint64 os2TimeToUnixTime(const FDATE *date, const FTIME *time) +{ + struct tm tm; + + tm.tm_sec = ((PHYSFS_uint32) time->twosecs) * 2; + tm.tm_min = time->minutes; + tm.tm_hour = time->hours; + tm.tm_mday = date->day; + tm.tm_mon = date->month; + tm.tm_year = ((PHYSFS_uint32) date->year) + 80; + tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/; + tm.tm_yday = -1; + tm.tm_isdst = -1; + + return (PHYSFS_sint64) mktime(&tm); +} /* os2TimeToUnixTime */ + + +int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *stat, const int follow) +{ + char *cpfname = cvtUtf8ToCodepage(filename); + FILESTATUS3 fs; + int retval = 0; + APIRET rc; + + BAIL_IF_ERRPASS(!cpfname, 0); + + rc = DosQueryPathInfo(cpfname, FIL_STANDARD, &fs, sizeof (fs)); + GOTO_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), done); + + if (fs.attrFile & FILE_DIRECTORY) + { + stat->filetype = PHYSFS_FILETYPE_DIRECTORY; + stat->filesize = 0; + } /* if */ + else + { + stat->filetype = PHYSFS_FILETYPE_REGULAR; + stat->filesize = fs.cbFile; + } /* else */ + + stat->modtime = os2TimeToUnixTime(&fs.fdateLastWrite, &fs.ftimeLastWrite); + if (stat->modtime < 0) + stat->modtime = 0; + + stat->accesstime = os2TimeToUnixTime(&fs.fdateLastAccess, &fs.ftimeLastAccess); + if (stat->accesstime < 0) + stat->accesstime = 0; + + stat->createtime = os2TimeToUnixTime(&fs.fdateCreation, &fs.ftimeCreation); + if (stat->createtime < 0) + stat->createtime = 0; + + stat->readonly = ((fs.attrFile & FILE_READONLY) == FILE_READONLY); + return 1; /* success */ + +done: + allocator.Free(cpfname); + return retval; +} /* __PHYSFS_platformStat */ + + +void *__PHYSFS_platformGetThreadID(void) +{ + PTIB ptib; + PPIB ppib; + + /* + * Allegedly, this API never fails, but we'll punt and return a + * default value (zero might as well do) if it does. + */ + const APIRET rc = DosGetInfoBlocks(&ptib, &ppib); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), 0); + return ((void *) ptib->tib_ordinal); +} /* __PHYSFS_platformGetThreadID */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + HMTX hmtx = NULLHANDLE; + const APIRET rc = DosCreateMutexSem(NULL, &hmtx, 0, 0); + BAIL_IF(rc != NO_ERROR, errcodeFromAPIRET(rc), NULL); + return ((void *) hmtx); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + DosCloseMutexSem((HMTX) mutex); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + /* Do _NOT_ set the physfs error message in here! */ + return (DosRequestMutexSem((HMTX) mutex, SEM_INDEFINITE_WAIT) == NO_ERROR); +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + DosReleaseMutexSem((HMTX) mutex); +} /* __PHYSFS_platformReleaseMutex */ + +#endif /* PHYSFS_PLATFORM_OS2 */ + +/* end of physfs_platform_os2.c ... */ diff --git a/Source/3rdParty/physfs/physfs_platform_posix.c b/Source/3rdParty/physfs/physfs_platform_posix.c new file mode 100644 index 0000000..fa2159c --- /dev/null +++ b/Source/3rdParty/physfs/physfs_platform_posix.c @@ -0,0 +1,417 @@ +/* + * Posix-esque support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* !!! FIXME: check for EINTR? */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_POSIX + +#include <unistd.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <pwd.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <pthread.h> + +#include "physfs_internal.h" + + +static PHYSFS_ErrorCode errcodeFromErrnoError(const int err) +{ + switch (err) + { + case 0: return PHYSFS_ERR_OK; + case EACCES: return PHYSFS_ERR_PERMISSION; + case EPERM: return PHYSFS_ERR_PERMISSION; + case EDQUOT: return PHYSFS_ERR_NO_SPACE; + case EIO: return PHYSFS_ERR_IO; + case ELOOP: return PHYSFS_ERR_SYMLINK_LOOP; + case EMLINK: return PHYSFS_ERR_NO_SPACE; + case ENAMETOOLONG: return PHYSFS_ERR_BAD_FILENAME; + case ENOENT: return PHYSFS_ERR_NOT_FOUND; + case ENOSPC: return PHYSFS_ERR_NO_SPACE; + case ENOTDIR: return PHYSFS_ERR_NOT_FOUND; + case EISDIR: return PHYSFS_ERR_NOT_A_FILE; + case EROFS: return PHYSFS_ERR_READ_ONLY; + case ETXTBSY: return PHYSFS_ERR_BUSY; + case EBUSY: return PHYSFS_ERR_BUSY; + case ENOMEM: return PHYSFS_ERR_OUT_OF_MEMORY; + case ENOTEMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY; + default: return PHYSFS_ERR_OS_ERROR; + } /* switch */ +} /* errcodeFromErrnoError */ + + +static inline PHYSFS_ErrorCode errcodeFromErrno(void) +{ + return errcodeFromErrnoError(errno); +} /* errcodeFromErrno */ + + +static char *getUserDirByUID(void) +{ + uid_t uid = getuid(); + struct passwd *pw; + char *retval = NULL; + + pw = getpwuid(uid); + if ((pw != NULL) && (pw->pw_dir != NULL) && (*pw->pw_dir != '\0')) + { + const size_t dlen = strlen(pw->pw_dir); + const size_t add_dirsep = (pw->pw_dir[dlen-1] != '/') ? 1 : 0; + retval = (char *) allocator.Malloc(dlen + 1 + add_dirsep); + if (retval != NULL) + { + strcpy(retval, pw->pw_dir); + if (add_dirsep) + { + retval[dlen] = '/'; + retval[dlen+1] = '\0'; + } /* if */ + } /* if */ + } /* if */ + + return retval; +} /* getUserDirByUID */ + + +char *__PHYSFS_platformCalcUserDir(void) +{ + char *retval = NULL; + char *envr = getenv("HOME"); + + /* if the environment variable was set, make sure it's really a dir. */ + if (envr != NULL) + { + struct stat statbuf; + if ((stat(envr, &statbuf) != -1) && (S_ISDIR(statbuf.st_mode))) + { + const size_t envrlen = strlen(envr); + const size_t add_dirsep = (envr[envrlen-1] != '/') ? 1 : 0; + retval = allocator.Malloc(envrlen + 1 + add_dirsep); + if (retval) + { + strcpy(retval, envr); + if (add_dirsep) + { + retval[envrlen] = '/'; + retval[envrlen+1] = '\0'; + } /* if */ + } /* if */ + } /* if */ + } /* if */ + + if (retval == NULL) + retval = getUserDirByUID(); + + return retval; +} /* __PHYSFS_platformCalcUserDir */ + + +PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname, + PHYSFS_EnumerateCallback callback, + const char *origdir, void *callbackdata) +{ + DIR *dir; + struct dirent *ent; + PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK; + + dir = opendir(dirname); + BAIL_IF(dir == NULL, errcodeFromErrno(), PHYSFS_ENUM_ERROR); + + while ((retval == PHYSFS_ENUM_OK) && ((ent = readdir(dir)) != NULL)) + { + const char *name = ent->d_name; + if (name[0] == '.') /* ignore "." and ".." */ + { + if ((name[1] == '\0') || ((name[1] == '.') && (name[2] == '\0'))) + continue; + } /* if */ + + retval = callback(callbackdata, origdir, name); + if (retval == PHYSFS_ENUM_ERROR) + PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK); + } /* while */ + + closedir(dir); + + return retval; +} /* __PHYSFS_platformEnumerate */ + + +int __PHYSFS_platformMkDir(const char *path) +{ + const int rc = mkdir(path, S_IRWXU); + BAIL_IF(rc == -1, errcodeFromErrno(), 0); + return 1; +} /* __PHYSFS_platformMkDir */ + + +static void *doOpen(const char *filename, int mode) +{ + const int appending = (mode & O_APPEND); + int fd; + int *retval; + errno = 0; + + /* O_APPEND doesn't actually behave as we'd like. */ + mode &= ~O_APPEND; + + fd = open(filename, mode, S_IRUSR | S_IWUSR); + BAIL_IF(fd < 0, errcodeFromErrno(), NULL); + + if (appending) + { + if (lseek(fd, 0, SEEK_END) < 0) + { + const int err = errno; + close(fd); + BAIL(errcodeFromErrnoError(err), NULL); + } /* if */ + } /* if */ + + retval = (int *) allocator.Malloc(sizeof (int)); + if (!retval) + { + close(fd); + BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + *retval = fd; + return ((void *) retval); +} /* doOpen */ + + +void *__PHYSFS_platformOpenRead(const char *filename) +{ + return doOpen(filename, O_RDONLY); +} /* __PHYSFS_platformOpenRead */ + + +void *__PHYSFS_platformOpenWrite(const char *filename) +{ + return doOpen(filename, O_WRONLY | O_CREAT | O_TRUNC); +} /* __PHYSFS_platformOpenWrite */ + + +void *__PHYSFS_platformOpenAppend(const char *filename) +{ + return doOpen(filename, O_WRONLY | O_CREAT | O_APPEND); +} /* __PHYSFS_platformOpenAppend */ + + +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buffer, + PHYSFS_uint64 len) +{ + const int fd = *((int *) opaque); + ssize_t rc = 0; + + if (!__PHYSFS_ui64FitsAddressSpace(len)) + BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1); + + rc = read(fd, buffer, (size_t) len); + BAIL_IF(rc == -1, errcodeFromErrno(), -1); + assert(rc >= 0); + assert(rc <= len); + return (PHYSFS_sint64) rc; +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint64 len) +{ + const int fd = *((int *) opaque); + ssize_t rc = 0; + + if (!__PHYSFS_ui64FitsAddressSpace(len)) + BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1); + + rc = write(fd, (void *) buffer, (size_t) len); + BAIL_IF(rc == -1, errcodeFromErrno(), rc); + assert(rc >= 0); + assert(rc <= len); + return (PHYSFS_sint64) rc; +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + const int fd = *((int *) opaque); + const off_t rc = lseek(fd, (off_t) pos, SEEK_SET); + BAIL_IF(rc == -1, errcodeFromErrno(), 0); + return 1; +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + const int fd = *((int *) opaque); + PHYSFS_sint64 retval; + retval = (PHYSFS_sint64) lseek(fd, 0, SEEK_CUR); + BAIL_IF(retval == -1, errcodeFromErrno(), -1); + return retval; +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + const int fd = *((int *) opaque); + struct stat statbuf; + BAIL_IF(fstat(fd, &statbuf) == -1, errcodeFromErrno(), -1); + return ((PHYSFS_sint64) statbuf.st_size); +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + const int fd = *((int *) opaque); + if ((fcntl(fd, F_GETFL) & O_ACCMODE) != O_RDONLY) + BAIL_IF(fsync(fd) == -1, errcodeFromErrno(), 0); + return 1; +} /* __PHYSFS_platformFlush */ + + +void __PHYSFS_platformClose(void *opaque) +{ + const int fd = *((int *) opaque); + (void) close(fd); /* we don't check this. You should have used flush! */ + allocator.Free(opaque); +} /* __PHYSFS_platformClose */ + + +int __PHYSFS_platformDelete(const char *path) +{ + BAIL_IF(remove(path) == -1, errcodeFromErrno(), 0); + return 1; +} /* __PHYSFS_platformDelete */ + + +int __PHYSFS_platformStat(const char *fname, PHYSFS_Stat *st, const int follow) +{ + struct stat statbuf; + const int rc = follow ? stat(fname, &statbuf) : lstat(fname, &statbuf); + BAIL_IF(rc == -1, errcodeFromErrno(), 0); + + if (S_ISREG(statbuf.st_mode)) + { + st->filetype = PHYSFS_FILETYPE_REGULAR; + st->filesize = statbuf.st_size; + } /* if */ + + else if(S_ISDIR(statbuf.st_mode)) + { + st->filetype = PHYSFS_FILETYPE_DIRECTORY; + st->filesize = 0; + } /* else if */ + + else if(S_ISLNK(statbuf.st_mode)) + { + st->filetype = PHYSFS_FILETYPE_SYMLINK; + st->filesize = 0; + } /* else if */ + + else + { + st->filetype = PHYSFS_FILETYPE_OTHER; + st->filesize = statbuf.st_size; + } /* else */ + + st->modtime = statbuf.st_mtime; + st->createtime = statbuf.st_ctime; + st->accesstime = statbuf.st_atime; + + st->readonly = (access(fname, W_OK) == -1); + return 1; +} /* __PHYSFS_platformStat */ + + +typedef struct +{ + pthread_mutex_t mutex; + pthread_t owner; + PHYSFS_uint32 count; +} PthreadMutex; + + +void *__PHYSFS_platformGetThreadID(void) +{ + return ( (void *) ((size_t) pthread_self()) ); +} /* __PHYSFS_platformGetThreadID */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + int rc; + PthreadMutex *m = (PthreadMutex *) allocator.Malloc(sizeof (PthreadMutex)); + BAIL_IF(!m, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + rc = pthread_mutex_init(&m->mutex, NULL); + if (rc != 0) + { + allocator.Free(m); + BAIL(PHYSFS_ERR_OS_ERROR, NULL); + } /* if */ + + m->count = 0; + m->owner = (pthread_t) 0xDEADBEEF; + return ((void *) m); +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + PthreadMutex *m = (PthreadMutex *) mutex; + + /* Destroying a locked mutex is a bug, but we'll try to be helpful. */ + if ((m->owner == pthread_self()) && (m->count > 0)) + pthread_mutex_unlock(&m->mutex); + + pthread_mutex_destroy(&m->mutex); + allocator.Free(m); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + PthreadMutex *m = (PthreadMutex *) mutex; + pthread_t tid = pthread_self(); + if (m->owner != tid) + { + if (pthread_mutex_lock(&m->mutex) != 0) + return 0; + m->owner = tid; + } /* if */ + + m->count++; + return 1; +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + PthreadMutex *m = (PthreadMutex *) mutex; + assert(m->owner == pthread_self()); /* catch programming errors. */ + assert(m->count > 0); /* catch programming errors. */ + if (m->owner == pthread_self()) + { + if (--m->count == 0) + { + m->owner = (pthread_t) 0xDEADBEEF; + pthread_mutex_unlock(&m->mutex); + } /* if */ + } /* if */ +} /* __PHYSFS_platformReleaseMutex */ + +#endif /* PHYSFS_PLATFORM_POSIX */ + +/* end of physfs_platform_posix.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_platform_qnx.c b/Source/3rdParty/physfs/physfs_platform_qnx.c new file mode 100644 index 0000000..46f9aa7 --- /dev/null +++ b/Source/3rdParty/physfs/physfs_platform_qnx.c @@ -0,0 +1,169 @@ +/* + * QNX support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +/* This is tested against QNX 7 at the moment. */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_QNX + +#include <fcntl.h> +#include <unistd.h> +#include <sys/types.h> +#include <limits.h> + +#include "physfs_internal.h" + +int __PHYSFS_platformInit(void) +{ + return 1; /* always succeed. */ +} /* __PHYSFS_platformInit */ + + +void __PHYSFS_platformDeinit(void) +{ + /* no-op */ +} /* __PHYSFS_platformDeinit */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + char *retval = (char *) allocator.Malloc(PATH_MAX+1); + if (retval == NULL) + BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); + else + { + const int fd = open("/proc/self/exefile", O_RDONLY); + const ssize_t br = (fd == -1) ? -1 : read(fd, retval, PATH_MAX); + char *ptr; + + if (fd != -1) + close(fd); + + if ((br < 0) || (br > PATH_MAX)) + { + allocator.Free(retval); + BAIL(PHYSFS_ERR_OS_ERROR, NULL); + } /* if */ + + retval[br] = '\0'; + ptr = strrchr(retval, '/'); + if (ptr == NULL) /* uhoh! */ + { + allocator.Free(retval); + BAIL(PHYSFS_ERR_OS_ERROR, NULL); + } /* if */ + + ptr[1] = '\0'; /* chop off filename, leave dirs and '/' */ + + ptr = (char *) allocator.Realloc(retval, (ptr - retval) + 2); + if (ptr != NULL) /* just shrinking buffer; don't care if it failed. */ + retval = ptr; + } /* else */ + + return retval; +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) +{ + /* !!! FIXME: this might be wrong; I don't know if there's a better method + on QNX, or if it follows XDG specs, etc. */ + char *retval = NULL; + const char *home = __PHYSFS_getUserDir(); + if (home) + { + const size_t len = strlen(home) + strlen(app) + 3; + retval = (char *) allocator.Malloc(len); + BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + snprintf(retval, len, "%s.%s/", home, app); + } /* if */ + return retval; +} /* __PHYSFS_platformCalcPrefDir */ + + +#if !PHYSFS_NO_CDROM_SUPPORT +#include <devctl.h> +#include <sys/dcmd_blk.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/statvfs.h> + +static void checkPathForCD(const char *path, PHYSFS_StringCallback cb, void *d) +{ + struct stat statbuf; + int fd; + + /* The devctl() thing is QNX-specific. In this case, we query what is + probably the mountpoint for the device. statvfs() on that mountpoint + will tell use its filesystem type. */ + + if ( (stat(path, &statbuf) == 0) && + (S_ISBLK(statbuf.st_mode)) && + ((fd = open(path, O_RDONLY | O_NONBLOCK)) != -1) ) + { + char mnt[256] = { 0 }; + const int rc = devctl(fd, DCMD_FSYS_MOUNTED_BY, mnt, sizeof (mnt), 0); + close(fd); + if ( (rc == EOK) && (mnt[0]) ) + { + struct statvfs statvfsbuf; + if (statvfs(mnt, &statvfsbuf) == 0) + { + /* I don't know if this is a complete or accurate list. */ + const char *fstype = statvfsbuf.f_basetype; + const int iscd = ( (strcmp(fstype, "cd") == 0) || + (strcmp(fstype, "udf") == 0) ); + if (iscd) + cb(d, mnt); + } /* if */ + } /* if */ + } /* if */ +} /* checkPathForCD */ + +static void checkDevForCD(const char *dev, PHYSFS_StringCallback cb, void *d) +{ + size_t len; + char *path; + + if (dev[0] == '.') /* ignore "." and ".." */ + { + if ((dev[1] == '\0') || ((dev[1] == '.') && (dev[2] == '\0'))) + return; + } /* if */ + + len = strlen(dev) + 6; + path = (char *) __PHYSFS_smallAlloc(len); + if (!path) + return; /* oh well. */ + + snprintf(path, len, "/dev/%s", dev); + checkPathForCD(path, cb, d); + __PHYSFS_smallFree(path); +} /* checkDevForCD */ +#endif /* !PHYSFS_NO_CDROM_SUPPORT */ + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ +#if !PHYSFS_NO_CDROM_SUPPORT + DIR *dirp = opendir("/dev"); + if (dirp) + { + struct dirent *dent; + while ((dent = readdir(dirp)) != NULL) + checkDevForCD(dent->d_name, cb, data); + closedir(dirp); + } /* if */ +#endif +} /* __PHYSFS_platformDetectAvailableCDs */ + +#endif /* PHYSFS_PLATFORM_QNX */ + +/* end of physfs_platform_qnx.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_platform_unix.c b/Source/3rdParty/physfs/physfs_platform_unix.c new file mode 100644 index 0000000..10d93a7 --- /dev/null +++ b/Source/3rdParty/physfs/physfs_platform_unix.c @@ -0,0 +1,367 @@ +/* + * Unix support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_UNIX + +#include <ctype.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/types.h> +#include <pwd.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <dirent.h> +#include <time.h> +#include <errno.h> +#include <limits.h> + +#if PHYSFS_NO_CDROM_SUPPORT +#elif PHYSFS_PLATFORM_LINUX +# define PHYSFS_HAVE_MNTENT_H 1 +#elif defined __CYGWIN__ +# define PHYSFS_HAVE_MNTENT_H 1 +#elif PHYSFS_PLATFORM_SOLARIS +# define PHYSFS_HAVE_SYS_MNTTAB_H 1 +#elif PHYSFS_PLATFORM_BSD +# define PHYSFS_HAVE_SYS_UCRED_H 1 +#else +# warning No CD-ROM support included. Either define your platform here, +# warning or define PHYSFS_NO_CDROM_SUPPORT=1 to confirm this is intentional. +#endif + +#ifdef PHYSFS_HAVE_SYS_UCRED_H +# ifdef PHYSFS_HAVE_MNTENT_H +# undef PHYSFS_HAVE_MNTENT_H /* don't do both... */ +# endif +# include <sys/mount.h> +# include <sys/ucred.h> +#endif + +#ifdef PHYSFS_HAVE_MNTENT_H +#include <mntent.h> +#endif + +#ifdef PHYSFS_HAVE_SYS_MNTTAB_H +#include <sys/mnttab.h> +#endif + +#ifdef PHYSFS_PLATFORM_FREEBSD +#include <sys/sysctl.h> +#endif + + +#include "physfs_internal.h" + +int __PHYSFS_platformInit(void) +{ + return 1; /* always succeed. */ +} /* __PHYSFS_platformInit */ + + +void __PHYSFS_platformDeinit(void) +{ + /* no-op */ +} /* __PHYSFS_platformDeinit */ + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ +#if (defined PHYSFS_NO_CDROM_SUPPORT) + /* no-op. */ + +#elif (defined PHYSFS_HAVE_SYS_UCRED_H) + int i; + struct statfs *mntbufp = NULL; + int mounts = getmntinfo(&mntbufp, MNT_NOWAIT); + + for (i = 0; i < mounts; i++) + { + int add_it = 0; + + if (strcmp(mntbufp[i].f_fstypename, "iso9660") == 0) + add_it = 1; + else if (strcmp( mntbufp[i].f_fstypename, "cd9660") == 0) + add_it = 1; + + /* add other mount types here */ + + if (add_it) + cb(data, mntbufp[i].f_mntonname); + } /* for */ + +#elif (defined PHYSFS_HAVE_MNTENT_H) + FILE *mounts = NULL; + struct mntent *ent = NULL; + + mounts = setmntent("/etc/mtab", "r"); + BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/); + + while ( (ent = getmntent(mounts)) != NULL ) + { + int add_it = 0; + if (strcmp(ent->mnt_type, "iso9660") == 0) + add_it = 1; + else if (strcmp(ent->mnt_type, "udf") == 0) + add_it = 1; + + /* !!! FIXME: these might pick up floppy drives, right? */ + else if (strcmp(ent->mnt_type, "auto") == 0) + add_it = 1; + else if (strcmp(ent->mnt_type, "supermount") == 0) + add_it = 1; + + /* add other mount types here */ + + if (add_it) + cb(data, ent->mnt_dir); + } /* while */ + + endmntent(mounts); + +#elif (defined PHYSFS_HAVE_SYS_MNTTAB_H) + FILE *mounts = fopen(MNTTAB, "r"); + struct mnttab ent; + + BAIL_IF(mounts == NULL, PHYSFS_ERR_IO, /*return void*/); + while (getmntent(mounts, &ent) == 0) + { + int add_it = 0; + if (strcmp(ent.mnt_fstype, "hsfs") == 0) + add_it = 1; + + /* add other mount types here */ + + if (add_it) + cb(data, ent.mnt_mountp); + } /* while */ + + fclose(mounts); +#endif +} /* __PHYSFS_platformDetectAvailableCDs */ + + +/* + * See where program (bin) resides in the $PATH specified by (envr). + * returns a copy of the first element in envr that contains it, or NULL + * if it doesn't exist or there were other problems. PHYSFS_SetError() is + * called if we have a problem. + * + * (envr) will be scribbled over, and you are expected to allocator.Free() the + * return value when you're done with it. + */ +static char *findBinaryInPath(const char *bin, char *envr) +{ + size_t alloc_size = 0; + char *exe = NULL; + char *start = envr; + char *ptr; + + assert(bin != NULL); + assert(envr != NULL); + + do + { + size_t size; + size_t binlen; + + ptr = strchr(start, ':'); /* find next $PATH separator. */ + if (ptr) + *ptr = '\0'; + + binlen = strlen(bin); + size = strlen(start) + binlen + 2; + if (size >= alloc_size) + { + char *x = (char *) allocator.Realloc(exe, size); + if (!x) + { + if (exe != NULL) + allocator.Free(exe); + BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + alloc_size = size; + exe = x; + } /* if */ + + /* build full binary path... */ + strcpy(exe, start); + if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/')) + strcat(exe, "/"); + strcat(exe, bin); + + if (access(exe, X_OK) == 0) /* Exists as executable? We're done. */ + { + exe[(size - binlen) - 1] = '\0'; /* chop off filename, leave '/' */ + return exe; + } /* if */ + + start = ptr + 1; /* start points to beginning of next element. */ + } while (ptr != NULL); + + if (exe != NULL) + allocator.Free(exe); + + return NULL; /* doesn't exist in path. */ +} /* findBinaryInPath */ + + +static char *readSymLink(const char *path) +{ + ssize_t len = 64; + ssize_t rc = -1; + char *retval = NULL; + + while (1) + { + char *ptr = (char *) allocator.Realloc(retval, (size_t) len); + if (ptr == NULL) + break; /* out of memory. */ + retval = ptr; + + rc = readlink(path, retval, len); + if (rc == -1) + break; /* not a symlink, i/o error, etc. */ + + else if (rc < len) + { + retval[rc] = '\0'; /* readlink doesn't null-terminate. */ + return retval; /* we're good to go. */ + } /* else if */ + + len *= 2; /* grow buffer, try again. */ + } /* while */ + + if (retval != NULL) + allocator.Free(retval); + return NULL; +} /* readSymLink */ + + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ + char *retval = NULL; + const char *envr = NULL; + + /* Try to avoid using argv0 unless forced to. Try system-specific stuff. */ + + #if defined(PHYSFS_PLATFORM_FREEBSD) + { + char fullpath[PATH_MAX]; + size_t buflen = sizeof (fullpath); + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + if (sysctl(mib, 4, fullpath, &buflen, NULL, 0) != -1) + retval = __PHYSFS_strdup(fullpath); + } + #elif defined(PHYSFS_PLATFORM_SOLARIS) + { + const char *path = getexecname(); + if ((path != NULL) && (path[0] == '/')) /* must be absolute path... */ + retval = __PHYSFS_strdup(path); + } + #endif + + /* If there's a Linux-like /proc filesystem, you can get the full path to + * the current process from a symlink in there. + */ + + if (!retval && (access("/proc", F_OK) == 0)) + { + retval = readSymLink("/proc/self/exe"); + if (!retval) retval = readSymLink("/proc/curproc/file"); + if (!retval) retval = readSymLink("/proc/curproc/exe"); + if (retval == NULL) + { + /* older kernels don't have /proc/self ... try PID version... */ + const unsigned long long pid = (unsigned long long) getpid(); + char path[64]; + const int rc = (int) snprintf(path,sizeof(path),"/proc/%llu/exe",pid); + if ( (rc > 0) && (rc < sizeof(path)) ) + retval = readSymLink(path); + } /* if */ + } /* if */ + + if (retval != NULL) /* chop off filename. */ + { + char *ptr = strrchr(retval, '/'); + if (ptr != NULL) + *(ptr+1) = '\0'; + else /* shouldn't happen, but just in case... */ + { + allocator.Free(retval); + retval = NULL; + } /* else */ + } /* if */ + + /* No /proc/self/exe, etc, but we have an argv[0] we can parse? */ + if ((retval == NULL) && (argv0 != NULL)) + { + /* fast path: default behaviour can handle this. */ + if (strchr(argv0, '/') != NULL) + return NULL; /* higher level parses out real path from argv0. */ + + /* If there's no dirsep on argv0, then look through $PATH for it. */ + envr = getenv("PATH"); + if (envr != NULL) + { + char *path = (char *) __PHYSFS_smallAlloc(strlen(envr) + 1); + BAIL_IF(!path, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + strcpy(path, envr); + retval = findBinaryInPath(argv0, path); + __PHYSFS_smallFree(path); + } /* if */ + } /* if */ + + if (retval != NULL) + { + /* try to shrink buffer... */ + char *ptr = (char *) allocator.Realloc(retval, strlen(retval) + 1); + if (ptr != NULL) + retval = ptr; /* oh well if it failed. */ + } /* if */ + + return retval; +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) +{ + /* + * We use XDG's base directory spec, even if you're not on Linux. + * This isn't strictly correct, but the results are relatively sane + * in any case. + * + * https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html + */ + const char *envr = getenv("XDG_DATA_HOME"); + const char *append = "/"; + char *retval = NULL; + size_t len = 0; + + if (!envr) + { + /* You end up with "$HOME/.local/share/Game Name 2" */ + envr = __PHYSFS_getUserDir(); + BAIL_IF_ERRPASS(!envr, NULL); /* oh well. */ + append = ".local/share/"; + } /* if */ + + len = strlen(envr) + strlen(append) + strlen(app) + 2; + retval = (char *) allocator.Malloc(len); + BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + snprintf(retval, len, "%s%s%s/", envr, append, app); + return retval; +} /* __PHYSFS_platformCalcPrefDir */ + +#endif /* PHYSFS_PLATFORM_UNIX */ + +/* end of physfs_platform_unix.c ... */ + diff --git a/Source/3rdParty/physfs/physfs_platform_windows.c b/Source/3rdParty/physfs/physfs_platform_windows.c new file mode 100644 index 0000000..4f8c99a --- /dev/null +++ b/Source/3rdParty/physfs/physfs_platform_windows.c @@ -0,0 +1,1026 @@ +/* + * Windows support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file written by Ryan C. Gordon, and made sane by Gregory S. Read. + */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_WINDOWS + +/* Forcibly disable UNICODE macro, since we manage this ourselves. */ +#ifdef UNICODE +#undef UNICODE +#endif + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS 1 +#endif + +#define WIN32_LEAN_AND_MEAN 1 +#include <windows.h> + +#ifndef PHYSFS_PLATFORM_WINRT +#include <userenv.h> +#include <shlobj.h> +#endif + +#if !defined(PHYSFS_NO_CDROM_SUPPORT) +#include <dbt.h> +#endif + +#include <errno.h> +#include <ctype.h> +#include <time.h> + +#ifdef allocator /* apparently Windows 10 SDK conflicts here. */ +#undef allocator +#endif + +#include "physfs_internal.h" + +/* + * Users without the platform SDK don't have this defined. The original docs + * for SetFilePointer() just said to compare with 0xFFFFFFFF, so this should + * work as desired. + */ +#define PHYSFS_INVALID_SET_FILE_POINTER 0xFFFFFFFF + +/* just in case... */ +#define PHYSFS_INVALID_FILE_ATTRIBUTES 0xFFFFFFFF + +/* Not defined before the Vista SDK. */ +#define PHYSFS_FILE_ATTRIBUTE_REPARSE_POINT 0x400 +#define PHYSFS_IO_REPARSE_TAG_SYMLINK 0xA000000C + + +#define UTF8_TO_UNICODE_STACK(w_assignto, str) { \ + if (str == NULL) \ + w_assignto = NULL; \ + else { \ + const size_t len = (PHYSFS_uint64) ((strlen(str) + 1) * 2); \ + w_assignto = (WCHAR *) __PHYSFS_smallAlloc(len); \ + if (w_assignto != NULL) \ + PHYSFS_utf8ToUtf16(str, (PHYSFS_uint16 *) w_assignto, len); \ + } \ +} \ + +/* Note this counts WCHARs, not codepoints! */ +static PHYSFS_uint64 wStrLen(const WCHAR *wstr) +{ + PHYSFS_uint64 len = 0; + while (*(wstr++)) + len++; + return len; +} /* wStrLen */ + +static char *unicodeToUtf8Heap(const WCHAR *w_str) +{ + char *retval = NULL; + if (w_str != NULL) + { + void *ptr = NULL; + const PHYSFS_uint64 len = (wStrLen(w_str) * 4) + 1; + retval = allocator.Malloc(len); + BAIL_IF(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + PHYSFS_utf8FromUtf16((const PHYSFS_uint16 *) w_str, retval, len); + ptr = allocator.Realloc(retval, strlen(retval) + 1); /* shrink. */ + if (ptr != NULL) + retval = (char *) ptr; + } /* if */ + return retval; +} /* unicodeToUtf8Heap */ + + +/* Some older APIs aren't in WinRT (only the "Ex" version, etc). + Since non-WinRT might not have the "Ex" version, we tapdance to use + the perfectly-fine-and-available-even-on-Win95 API on non-WinRT targets. */ + +static inline HANDLE winFindFirstFileW(const WCHAR *path, LPWIN32_FIND_DATAW d) +{ + #ifdef PHYSFS_PLATFORM_WINRT + return FindFirstFileExW(path, FindExInfoStandard, d, + FindExSearchNameMatch, NULL, 0); + #else + return FindFirstFileW(path, d); + #endif +} /* winFindFirstFileW */ + +static inline BOOL winInitializeCriticalSection(LPCRITICAL_SECTION lpcs) +{ + #ifdef PHYSFS_PLATFORM_WINRT + return InitializeCriticalSectionEx(lpcs, 2000, 0); + #else + InitializeCriticalSection(lpcs); + return TRUE; + #endif +} /* winInitializeCriticalSection */ + +static inline HANDLE winCreateFileW(const WCHAR *wfname, const DWORD mode, + const DWORD creation) +{ + const DWORD share = FILE_SHARE_READ | FILE_SHARE_WRITE; + #ifdef PHYSFS_PLATFORM_WINRT + return CreateFile2(wfname, mode, share, creation, NULL); + #else + return CreateFileW(wfname, mode, share, NULL, creation, + FILE_ATTRIBUTE_NORMAL, NULL); + #endif +} /* winCreateFileW */ + +static BOOL winSetFilePointer(HANDLE h, const PHYSFS_sint64 pos, + PHYSFS_sint64 *_newpos, const DWORD whence) +{ + #ifdef PHYSFS_PLATFORM_WINRT + LARGE_INTEGER lipos; + LARGE_INTEGER linewpos; + BOOL rc; + lipos.QuadPart = (LONGLONG) pos; + rc = SetFilePointerEx(h, lipos, &linewpos, whence); + if (_newpos) + *_newpos = (PHYSFS_sint64) linewpos.QuadPart; + return rc; + #else + const LONG low = (LONG) (pos & 0xFFFFFFFF); + LONG high = (LONG) ((pos >> 32) & 0xFFFFFFFF); + const DWORD rc = SetFilePointer(h, low, &high, whence); + /* 0xFFFFFFFF could be valid, so you have to check GetLastError too! */ + if (_newpos) + *_newpos = ((PHYSFS_sint64) rc) | (((PHYSFS_sint64) high) << 32); + if ((rc == PHYSFS_INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)) + return FALSE; + return TRUE; + #endif +} /* winSetFilePointer */ + +static PHYSFS_sint64 winGetFileSize(HANDLE h) +{ + #ifdef PHYSFS_PLATFORM_WINRT + FILE_STANDARD_INFO info; + const BOOL rc = GetFileInformationByHandleEx(h, FileStandardInfo, + &info, sizeof (info)); + return rc ? (PHYSFS_sint64) info.EndOfFile.QuadPart : -1; + #else + DWORD high = 0; + const DWORD rc = GetFileSize(h, &high); + if ((rc == PHYSFS_INVALID_SET_FILE_POINTER) && (GetLastError() != NO_ERROR)) + return -1; + return (PHYSFS_sint64) ((((PHYSFS_uint64) high) << 32) | rc); + #endif +} /* winGetFileSize */ + + +static PHYSFS_ErrorCode errcodeFromWinApiError(const DWORD err) +{ + /* + * win32 error codes are sort of a tricky thing; Microsoft intentionally + * doesn't list which ones a given API might trigger, there are several + * with overlapping and unclear meanings...and there's 16 thousand of + * them in Windows 7. It looks like the ones we care about are in the + * first 500, but I can't say this list is perfect; we might miss + * important values or misinterpret others. + * + * Don't treat this list as anything other than a work in progress. + */ + switch (err) + { + case ERROR_SUCCESS: return PHYSFS_ERR_OK; + case ERROR_ACCESS_DENIED: return PHYSFS_ERR_PERMISSION; + case ERROR_NETWORK_ACCESS_DENIED: return PHYSFS_ERR_PERMISSION; + case ERROR_NOT_READY: return PHYSFS_ERR_IO; + case ERROR_CRC: return PHYSFS_ERR_IO; + case ERROR_SEEK: return PHYSFS_ERR_IO; + case ERROR_SECTOR_NOT_FOUND: return PHYSFS_ERR_IO; + case ERROR_NOT_DOS_DISK: return PHYSFS_ERR_IO; + case ERROR_WRITE_FAULT: return PHYSFS_ERR_IO; + case ERROR_READ_FAULT: return PHYSFS_ERR_IO; + case ERROR_DEV_NOT_EXIST: return PHYSFS_ERR_IO; + case ERROR_BUFFER_OVERFLOW: return PHYSFS_ERR_BAD_FILENAME; + case ERROR_INVALID_NAME: return PHYSFS_ERR_BAD_FILENAME; + case ERROR_BAD_PATHNAME: return PHYSFS_ERR_BAD_FILENAME; + case ERROR_DIRECTORY: return PHYSFS_ERR_BAD_FILENAME; + case ERROR_FILE_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND; + case ERROR_PATH_NOT_FOUND: return PHYSFS_ERR_NOT_FOUND; + case ERROR_DELETE_PENDING: return PHYSFS_ERR_NOT_FOUND; + case ERROR_INVALID_DRIVE: return PHYSFS_ERR_NOT_FOUND; + case ERROR_HANDLE_DISK_FULL: return PHYSFS_ERR_NO_SPACE; + case ERROR_DISK_FULL: return PHYSFS_ERR_NO_SPACE; + case ERROR_WRITE_PROTECT: return PHYSFS_ERR_READ_ONLY; + case ERROR_LOCK_VIOLATION: return PHYSFS_ERR_BUSY; + case ERROR_SHARING_VIOLATION: return PHYSFS_ERR_BUSY; + case ERROR_CURRENT_DIRECTORY: return PHYSFS_ERR_BUSY; + case ERROR_DRIVE_LOCKED: return PHYSFS_ERR_BUSY; + case ERROR_PATH_BUSY: return PHYSFS_ERR_BUSY; + case ERROR_BUSY: return PHYSFS_ERR_BUSY; + case ERROR_NOT_ENOUGH_MEMORY: return PHYSFS_ERR_OUT_OF_MEMORY; + case ERROR_OUTOFMEMORY: return PHYSFS_ERR_OUT_OF_MEMORY; + case ERROR_DIR_NOT_EMPTY: return PHYSFS_ERR_DIR_NOT_EMPTY; + default: return PHYSFS_ERR_OS_ERROR; + } /* switch */ +} /* errcodeFromWinApiError */ + +static inline PHYSFS_ErrorCode errcodeFromWinApi(void) +{ + return errcodeFromWinApiError(GetLastError()); +} /* errcodeFromWinApi */ + + +#if defined(PHYSFS_NO_CDROM_SUPPORT) +#define detectAvailableCDs(cb, data) +#define deinitCDThread() +#else +static HANDLE detectCDThreadHandle = NULL; +static HWND detectCDHwnd = NULL; +static volatile DWORD drivesWithMediaBitmap = 0; + +typedef BOOL (WINAPI *fnSTEM)(DWORD, LPDWORD b); + +static DWORD pollDiscDrives(void) +{ + /* Try to use SetThreadErrorMode(), which showed up in Windows 7. */ + HANDLE lib = LoadLibraryA("kernel32.dll"); + fnSTEM stem = NULL; + char drive[4] = { 'x', ':', '\\', '\0' }; + DWORD oldErrorMode = 0; + DWORD drives = 0; + DWORD i; + + if (lib) + stem = (fnSTEM) GetProcAddress(lib, "SetThreadErrorMode"); + + if (stem) + stem(SEM_FAILCRITICALERRORS, &oldErrorMode); + else + oldErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS); + + /* Do detection. This may block if a disc is spinning up. */ + for (i = 'A'; i <= 'Z'; i++) + { + DWORD tmp = 0; + drive[0] = (char) i; + if (GetDriveTypeA(drive) != DRIVE_CDROM) + continue; + + /* If this function succeeds, there's media in the drive */ + if (GetVolumeInformationA(drive, NULL, 0, NULL, NULL, &tmp, NULL, 0)) + drives |= (1 << (i - 'A')); + } /* for */ + + if (stem) + stem(oldErrorMode, NULL); + else + SetErrorMode(oldErrorMode); + + if (lib) + FreeLibrary(lib); + + return drives; +} /* pollDiscDrives */ + + +static LRESULT CALLBACK detectCDWndProc(HWND hwnd, UINT msg, + WPARAM wp, LPARAM lparam) +{ + PDEV_BROADCAST_HDR lpdb = (PDEV_BROADCAST_HDR) lparam; + PDEV_BROADCAST_VOLUME lpdbv = (PDEV_BROADCAST_VOLUME) lparam; + const int removed = (wp == DBT_DEVICEREMOVECOMPLETE); + + if (msg == WM_DESTROY) + return 0; + else if ((msg != WM_DEVICECHANGE) || + ((wp != DBT_DEVICEARRIVAL) && (wp != DBT_DEVICEREMOVECOMPLETE)) || + (lpdb->dbch_devicetype != DBT_DEVTYP_VOLUME) || + ((lpdbv->dbcv_flags & DBTF_MEDIA) == 0)) + { + return DefWindowProcW(hwnd, msg, wp, lparam); + } /* else if */ + + if (removed) + drivesWithMediaBitmap &= ~lpdbv->dbcv_unitmask; + else + drivesWithMediaBitmap |= lpdbv->dbcv_unitmask; + + return TRUE; +} /* detectCDWndProc */ + + +static DWORD WINAPI detectCDThread(LPVOID arg) +{ + HANDLE initialDiscDetectionComplete = *((HANDLE *) arg); + const char *classname = "PhysicsFSDetectCDCatcher"; + const char *winname = "PhysicsFSDetectCDMsgWindow"; + HINSTANCE hInstance = GetModuleHandleW(NULL); + ATOM class_atom = 0; + WNDCLASSEXA wce; + MSG msg; + + memset(&wce, '\0', sizeof (wce)); + wce.cbSize = sizeof (wce); + wce.lpfnWndProc = detectCDWndProc; + wce.lpszClassName = classname; + wce.hInstance = hInstance; + class_atom = RegisterClassExA(&wce); + if (class_atom == 0) + { + SetEvent(initialDiscDetectionComplete); /* let main thread go on. */ + return 0; + } /* if */ + + detectCDHwnd = CreateWindowExA(0, classname, winname, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, HWND_DESKTOP, NULL, hInstance, NULL); + + if (detectCDHwnd == NULL) + { + SetEvent(initialDiscDetectionComplete); /* let main thread go on. */ + UnregisterClassA(classname, hInstance); + return 0; + } /* if */ + + /* We'll get events when discs come and go from now on. */ + + /* Do initial detection, possibly blocking awhile... */ + drivesWithMediaBitmap = pollDiscDrives(); + + SetEvent(initialDiscDetectionComplete); /* let main thread go on. */ + + do + { + const BOOL rc = GetMessageW(&msg, detectCDHwnd, 0, 0); + if ((rc == 0) || (rc == -1)) + break; /* don't care if WM_QUIT or error break this loop. */ + TranslateMessage(&msg); + DispatchMessageW(&msg); + } while (1); + + /* we've been asked to quit. */ + DestroyWindow(detectCDHwnd); + UnregisterClassA(classname, hInstance); + return 0; +} /* detectCDThread */ + +static void detectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + char drive_str[4] = { 'x', ':', '\\', '\0' }; + DWORD drives = 0; + DWORD i; + + /* + * If you poll a drive while a user is inserting a disc, the OS will + * block this thread until the drive has spun up. So we swallow the risk + * once for initial detection, and spin a thread that will get device + * events thereafter, for apps that use this interface to poll for + * disc insertion. + */ + if (!detectCDThreadHandle) + { + HANDLE initialDetectDone = CreateEvent(NULL, TRUE, FALSE, NULL); + if (!initialDetectDone) + return; /* oh well. */ + + detectCDThreadHandle = CreateThread(NULL, 0, detectCDThread, + &initialDetectDone, 0, NULL); + if (detectCDThreadHandle) + WaitForSingleObject(initialDetectDone, INFINITE); + CloseHandle(initialDetectDone); + + if (!detectCDThreadHandle) + return; /* oh well. */ + } /* if */ + + drives = drivesWithMediaBitmap; /* whatever the thread has seen, we take. */ + for (i = 'A'; i <= 'Z'; i++) + { + if (drives & (1 << (i - 'A'))) + { + drive_str[0] = (char) i; + cb(data, drive_str); + } /* if */ + } /* for */ +} /* detectAvailableCDs */ + +static void deinitCDThread(void) +{ + if (detectCDThreadHandle) + { + if (detectCDHwnd) + PostMessageW(detectCDHwnd, WM_QUIT, 0, 0); + CloseHandle(detectCDThreadHandle); + detectCDThreadHandle = NULL; + drivesWithMediaBitmap = 0; + } /* if */ +} /* deinitCDThread */ +#endif + + +void __PHYSFS_platformDetectAvailableCDs(PHYSFS_StringCallback cb, void *data) +{ + detectAvailableCDs(cb, data); +} /* __PHYSFS_platformDetectAvailableCDs */ + +#ifdef PHYSFS_PLATFORM_WINRT +static char *calcDirAppendSep(const WCHAR *wdir) +{ + size_t len; + void *ptr; + char *retval; + BAIL_IF(!wdir, errcodeFromWinApi(), NULL); + retval = unicodeToUtf8Heap(wdir); + BAIL_IF_ERRPASS(!retval, NULL); + len = strlen(retval); + ptr = allocator.Realloc(retval, len + 2); + if (!ptr) + { + allocator.Free(retval); + BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); + } /* if */ + retval = (char *) ptr; + retval[len] = '\\'; + retval[len+1] = '\0'; + return retval; +} /* calcDirAppendSep */ +#endif + +char *__PHYSFS_platformCalcBaseDir(const char *argv0) +{ +#ifdef PHYSFS_PLATFORM_WINRT + return calcDirAppendSep((const WCHAR *) __PHYSFS_winrtCalcBaseDir()); +#else + char *retval = NULL; + DWORD buflen = 64; + LPWSTR modpath = NULL; + + while (1) + { + DWORD rc; + void *ptr; + + if ( (ptr = allocator.Realloc(modpath, buflen*sizeof(WCHAR))) == NULL ) + { + allocator.Free(modpath); + BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); + } /* if */ + modpath = (LPWSTR) ptr; + + rc = GetModuleFileNameW(NULL, modpath, buflen); + if (rc == 0) + { + allocator.Free(modpath); + BAIL(errcodeFromWinApi(), NULL); + } /* if */ + + if (rc < buflen) + { + buflen = rc; + break; + } /* if */ + + buflen *= 2; + } /* while */ + + if (buflen > 0) /* just in case... */ + { + WCHAR *ptr = (modpath + buflen) - 1; + while (ptr != modpath) + { + if (*ptr == '\\') + break; + ptr--; + } /* while */ + + if ((ptr == modpath) && (*ptr != '\\')) + PHYSFS_setErrorCode(PHYSFS_ERR_OTHER_ERROR); /* oh well. */ + else + { + *(ptr+1) = '\0'; /* chop off filename. */ + retval = unicodeToUtf8Heap(modpath); + } /* else */ + } /* else */ + allocator.Free(modpath); + + return retval; /* w00t. */ +#endif +} /* __PHYSFS_platformCalcBaseDir */ + + +char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app) +{ +#ifdef PHYSFS_PLATFORM_WINRT + return calcDirAppendSep((const WCHAR *) __PHYSFS_winrtCalcPrefDir()); +#else + /* + * Vista and later has a new API for this, but SHGetFolderPath works there, + * and apparently just wraps the new API. This is the new way to do it: + * + * SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE, + * NULL, &wszPath); + */ + + WCHAR path[MAX_PATH]; + char *utf8 = NULL; + size_t len = 0; + char *retval = NULL; + + if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, + NULL, 0, path))) + BAIL(PHYSFS_ERR_OS_ERROR, NULL); + + utf8 = unicodeToUtf8Heap(path); + BAIL_IF_ERRPASS(!utf8, NULL); + len = strlen(utf8) + strlen(org) + strlen(app) + 4; + retval = allocator.Malloc(len); + if (!retval) + { + allocator.Free(utf8); + BAIL(PHYSFS_ERR_OUT_OF_MEMORY, NULL); + } /* if */ + + snprintf(retval, len, "%s\\%s\\%s\\", utf8, org, app); + allocator.Free(utf8); + return retval; +#endif +} /* __PHYSFS_platformCalcPrefDir */ + + +char *__PHYSFS_platformCalcUserDir(void) +{ +#ifdef PHYSFS_PLATFORM_WINRT + return calcDirAppendSep((const WCHAR *) __PHYSFS_winrtCalcPrefDir()); +#else + typedef BOOL (WINAPI *fnGetUserProfDirW)(HANDLE, LPWSTR, LPDWORD); + fnGetUserProfDirW pGetDir = NULL; + HANDLE lib = NULL; + HANDLE accessToken = NULL; /* Security handle to process */ + char *retval = NULL; + + lib = LoadLibraryA("userenv.dll"); + BAIL_IF(!lib, errcodeFromWinApi(), NULL); + pGetDir=(fnGetUserProfDirW) GetProcAddress(lib,"GetUserProfileDirectoryW"); + GOTO_IF(!pGetDir, errcodeFromWinApi(), done); + + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &accessToken)) + GOTO(errcodeFromWinApi(), done); + else + { + DWORD psize = 0; + LPWSTR wstr = NULL; + BOOL rc = 0; + + /* + * Should fail. Will write the size of the profile path in + * psize. Also note that the second parameter can't be + * NULL or the function fails. + */ + /* + * EDIT: (03.10.2018) after Windows 10 Update 1809 psize will be zero + * if something other than NULL is passed for the second argument. + * Passing NULL now makes GetUserProfileDirectoryW fail (rc == 0) + * and psize receives the correct user directory length. + * It seems to work fine on Windows 7 and Windows 10 Update 1803 too + */ + rc = pGetDir(accessToken, NULL, &psize); + GOTO_IF(rc, PHYSFS_ERR_OS_ERROR, done); /* should have failed! */ + + /* Allocate memory for the profile directory */ + wstr = (LPWSTR) __PHYSFS_smallAlloc((psize + 1) * sizeof (WCHAR)); + if (wstr != NULL) + { + if (pGetDir(accessToken, wstr, &psize)) + { + /* Make sure it ends in a dirsep. We allocated +1 for this. */ + if (wstr[psize - 2] != '\\') + { + wstr[psize - 1] = '\\'; + wstr[psize - 0] = '\0'; + } /* if */ + retval = unicodeToUtf8Heap(wstr); + } /* if */ + __PHYSFS_smallFree(wstr); + } /* if */ + } /* if */ + +done: + if (accessToken) + CloseHandle(accessToken); + FreeLibrary(lib); + return retval; /* We made it: hit the showers. */ +#endif +} /* __PHYSFS_platformCalcUserDir */ + + +int __PHYSFS_platformInit(void) +{ + return 1; /* It's all good */ +} /* __PHYSFS_platformInit */ + + +void __PHYSFS_platformDeinit(void) +{ + deinitCDThread(); +} /* __PHYSFS_platformDeinit */ + + +void *__PHYSFS_platformGetThreadID(void) +{ + return ( (void *) ((size_t) GetCurrentThreadId()) ); +} /* __PHYSFS_platformGetThreadID */ + + +PHYSFS_EnumerateCallbackResult __PHYSFS_platformEnumerate(const char *dirname, + PHYSFS_EnumerateCallback callback, + const char *origdir, void *callbackdata) +{ + PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK; + HANDLE dir = INVALID_HANDLE_VALUE; + WIN32_FIND_DATAW entw; + size_t len = strlen(dirname); + char *searchPath = NULL; + WCHAR *wSearchPath = NULL; + + /* Allocate a new string for path, maybe '\\', "*", and NULL terminator */ + searchPath = (char *) __PHYSFS_smallAlloc(len + 3); + BAIL_IF(!searchPath, PHYSFS_ERR_OUT_OF_MEMORY, PHYSFS_ENUM_ERROR); + + /* Copy current dirname */ + strcpy(searchPath, dirname); + + /* if there's no '\\' at the end of the path, stick one in there. */ + if (searchPath[len - 1] != '\\') + { + searchPath[len++] = '\\'; + searchPath[len] = '\0'; + } /* if */ + + /* Append the "*" to the end of the string */ + strcat(searchPath, "*"); + + UTF8_TO_UNICODE_STACK(wSearchPath, searchPath); + __PHYSFS_smallFree(searchPath); + BAIL_IF_ERRPASS(!wSearchPath, PHYSFS_ENUM_ERROR); + + dir = winFindFirstFileW(wSearchPath, &entw); + __PHYSFS_smallFree(wSearchPath); + BAIL_IF(dir==INVALID_HANDLE_VALUE, errcodeFromWinApi(), PHYSFS_ENUM_ERROR); + + do + { + const WCHAR *fn = entw.cFileName; + char *utf8; + + if (fn[0] == '.') /* ignore "." and ".." */ + { + if ((fn[1] == '\0') || ((fn[1] == '.') && (fn[2] == '\0'))) + continue; + } /* if */ + + utf8 = unicodeToUtf8Heap(fn); + if (utf8 == NULL) + retval = -1; + else + { + retval = callback(callbackdata, origdir, utf8); + allocator.Free(utf8); + if (retval == PHYSFS_ENUM_ERROR) + PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK); + } /* else */ + } while ((retval == PHYSFS_ENUM_OK) && (FindNextFileW(dir, &entw) != 0)); + + FindClose(dir); + + return retval; +} /* __PHYSFS_platformEnumerate */ + + +int __PHYSFS_platformMkDir(const char *path) +{ + WCHAR *wpath; + DWORD rc; + UTF8_TO_UNICODE_STACK(wpath, path); + rc = CreateDirectoryW(wpath, NULL); + __PHYSFS_smallFree(wpath); + BAIL_IF(rc == 0, errcodeFromWinApi(), 0); + return 1; +} /* __PHYSFS_platformMkDir */ + + +static HANDLE doOpen(const char *fname, DWORD mode, DWORD creation) +{ + HANDLE fileh; + WCHAR *wfname; + + UTF8_TO_UNICODE_STACK(wfname, fname); + BAIL_IF(!wfname, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + + fileh = winCreateFileW(wfname, mode, creation); + __PHYSFS_smallFree(wfname); + + if (fileh == INVALID_HANDLE_VALUE) + BAIL(errcodeFromWinApi(), INVALID_HANDLE_VALUE); + + return fileh; +} /* doOpen */ + + +void *__PHYSFS_platformOpenRead(const char *filename) +{ + HANDLE h = doOpen(filename, GENERIC_READ, OPEN_EXISTING); + return (h == INVALID_HANDLE_VALUE) ? NULL : (void *) h; +} /* __PHYSFS_platformOpenRead */ + + +void *__PHYSFS_platformOpenWrite(const char *filename) +{ + HANDLE h = doOpen(filename, GENERIC_WRITE, CREATE_ALWAYS); + return (h == INVALID_HANDLE_VALUE) ? NULL : (void *) h; +} /* __PHYSFS_platformOpenWrite */ + + +void *__PHYSFS_platformOpenAppend(const char *filename) +{ + HANDLE h = doOpen(filename, GENERIC_WRITE, OPEN_ALWAYS); + BAIL_IF_ERRPASS(h == INVALID_HANDLE_VALUE, NULL); + + if (!winSetFilePointer(h, 0, NULL, FILE_END)) + { + const PHYSFS_ErrorCode err = errcodeFromWinApi(); + CloseHandle(h); + BAIL(err, NULL); + } /* if */ + + return (void *) h; +} /* __PHYSFS_platformOpenAppend */ + + +PHYSFS_sint64 __PHYSFS_platformRead(void *opaque, void *buf, PHYSFS_uint64 len) +{ + HANDLE h = (HANDLE) opaque; + PHYSFS_sint64 totalRead = 0; + + if (!__PHYSFS_ui64FitsAddressSpace(len)) + BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1); + + while (len > 0) + { + const DWORD thislen = (len > 0xFFFFFFFF) ? 0xFFFFFFFF : (DWORD) len; + DWORD numRead = 0; + if (!ReadFile(h, buf, thislen, &numRead, NULL)) + BAIL(errcodeFromWinApi(), -1); + len -= (PHYSFS_uint64) numRead; + totalRead += (PHYSFS_sint64) numRead; + if (numRead != thislen) + break; + } /* while */ + + return totalRead; +} /* __PHYSFS_platformRead */ + + +PHYSFS_sint64 __PHYSFS_platformWrite(void *opaque, const void *buffer, + PHYSFS_uint64 len) +{ + HANDLE h = (HANDLE) opaque; + PHYSFS_sint64 totalWritten = 0; + + if (!__PHYSFS_ui64FitsAddressSpace(len)) + BAIL(PHYSFS_ERR_INVALID_ARGUMENT, -1); + + while (len > 0) + { + const DWORD thislen = (len > 0xFFFFFFFF) ? 0xFFFFFFFF : (DWORD) len; + DWORD numWritten = 0; + if (!WriteFile(h, buffer, thislen, &numWritten, NULL)) + BAIL(errcodeFromWinApi(), -1); + len -= (PHYSFS_uint64) numWritten; + totalWritten += (PHYSFS_sint64) numWritten; + if (numWritten != thislen) + break; + } /* while */ + + return totalWritten; +} /* __PHYSFS_platformWrite */ + + +int __PHYSFS_platformSeek(void *opaque, PHYSFS_uint64 pos) +{ + HANDLE h = (HANDLE) opaque; + const PHYSFS_sint64 spos = (PHYSFS_sint64) pos; + BAIL_IF(!winSetFilePointer(h,spos,NULL,FILE_BEGIN), errcodeFromWinApi(), 0); + return 1; /* No error occured */ +} /* __PHYSFS_platformSeek */ + + +PHYSFS_sint64 __PHYSFS_platformTell(void *opaque) +{ + HANDLE h = (HANDLE) opaque; + PHYSFS_sint64 pos = 0; + BAIL_IF(!winSetFilePointer(h,0,&pos,FILE_CURRENT), errcodeFromWinApi(), -1); + return pos; +} /* __PHYSFS_platformTell */ + + +PHYSFS_sint64 __PHYSFS_platformFileLength(void *opaque) +{ + HANDLE h = (HANDLE) opaque; + const PHYSFS_sint64 retval = winGetFileSize(h); + BAIL_IF(retval < 0, errcodeFromWinApi(), -1); + return retval; +} /* __PHYSFS_platformFileLength */ + + +int __PHYSFS_platformFlush(void *opaque) +{ + HANDLE h = (HANDLE) opaque; + BAIL_IF(!FlushFileBuffers(h), errcodeFromWinApi(), 0); + return 1; +} /* __PHYSFS_platformFlush */ + + +void __PHYSFS_platformClose(void *opaque) +{ + HANDLE h = (HANDLE) opaque; + (void) CloseHandle(h); /* ignore errors. You should have flushed! */ +} /* __PHYSFS_platformClose */ + + +static int doPlatformDelete(LPWSTR wpath) +{ + WIN32_FILE_ATTRIBUTE_DATA info; + if (!GetFileAttributesExW(wpath, GetFileExInfoStandard, &info)) + BAIL(errcodeFromWinApi(), 0); + else + { + const int isdir = (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); + const BOOL rc = isdir ? RemoveDirectoryW(wpath) : DeleteFileW(wpath); + BAIL_IF(!rc, errcodeFromWinApi(), 0); + } /* else */ + return 1; /* if you made it here, it worked. */ +} /* doPlatformDelete */ + + +int __PHYSFS_platformDelete(const char *path) +{ + int retval = 0; + LPWSTR wpath = NULL; + UTF8_TO_UNICODE_STACK(wpath, path); + BAIL_IF(!wpath, PHYSFS_ERR_OUT_OF_MEMORY, 0); + retval = doPlatformDelete(wpath); + __PHYSFS_smallFree(wpath); + return retval; +} /* __PHYSFS_platformDelete */ + + +void *__PHYSFS_platformCreateMutex(void) +{ + LPCRITICAL_SECTION lpcs; + lpcs = (LPCRITICAL_SECTION) allocator.Malloc(sizeof (CRITICAL_SECTION)); + BAIL_IF(!lpcs, PHYSFS_ERR_OUT_OF_MEMORY, NULL); + + if (!winInitializeCriticalSection(lpcs)) + { + allocator.Free(lpcs); + BAIL(errcodeFromWinApi(), NULL); + } /* if */ + + return lpcs; +} /* __PHYSFS_platformCreateMutex */ + + +void __PHYSFS_platformDestroyMutex(void *mutex) +{ + DeleteCriticalSection((LPCRITICAL_SECTION) mutex); + allocator.Free(mutex); +} /* __PHYSFS_platformDestroyMutex */ + + +int __PHYSFS_platformGrabMutex(void *mutex) +{ + EnterCriticalSection((LPCRITICAL_SECTION) mutex); + return 1; +} /* __PHYSFS_platformGrabMutex */ + + +void __PHYSFS_platformReleaseMutex(void *mutex) +{ + LeaveCriticalSection((LPCRITICAL_SECTION) mutex); +} /* __PHYSFS_platformReleaseMutex */ + + +static PHYSFS_sint64 FileTimeToPhysfsTime(const FILETIME *ft) +{ + SYSTEMTIME st_utc; + SYSTEMTIME st_localtz; + TIME_ZONE_INFORMATION tzi; + DWORD tzid; + PHYSFS_sint64 retval; + struct tm tm; + BOOL rc; + + BAIL_IF(!FileTimeToSystemTime(ft, &st_utc), errcodeFromWinApi(), -1); + tzid = GetTimeZoneInformation(&tzi); + BAIL_IF(tzid == TIME_ZONE_ID_INVALID, errcodeFromWinApi(), -1); + rc = SystemTimeToTzSpecificLocalTime(&tzi, &st_utc, &st_localtz); + BAIL_IF(!rc, errcodeFromWinApi(), -1); + + /* Convert to a format that mktime() can grok... */ + tm.tm_sec = st_localtz.wSecond; + tm.tm_min = st_localtz.wMinute; + tm.tm_hour = st_localtz.wHour; + tm.tm_mday = st_localtz.wDay; + tm.tm_mon = st_localtz.wMonth - 1; + tm.tm_year = st_localtz.wYear - 1900; + tm.tm_wday = -1 /*st_localtz.wDayOfWeek*/; + tm.tm_yday = -1; + tm.tm_isdst = -1; + + /* Convert to a format PhysicsFS can grok... */ + retval = (PHYSFS_sint64) mktime(&tm); + BAIL_IF(retval == -1, PHYSFS_ERR_OS_ERROR, -1); + return retval; +} /* FileTimeToPhysfsTime */ + + +/* check for symlinks. These exist in NTFS 3.1 (WinXP), even though + they aren't really available to userspace before Vista. I wonder + what would happen if you put an NTFS disk with a symlink on it + into an XP machine, though; would this flag get set? + NTFS symlinks are a form of "reparse point" (junction, volume mount, + etc), so if the REPARSE_POINT attribute is set, check for the symlink + tag thereafter. This assumes you already read in the file attributes. */ +static int isSymlink(const WCHAR *wpath, const DWORD attr) +{ + WIN32_FIND_DATAW w32dw; + HANDLE h; + + if ((attr & PHYSFS_FILE_ATTRIBUTE_REPARSE_POINT) == 0) + return 0; /* not a reparse point? Definitely not a symlink. */ + + h = winFindFirstFileW(wpath, &w32dw); + if (h == INVALID_HANDLE_VALUE) + return 0; /* ...maybe the file just vanished...? */ + + FindClose(h); + return (w32dw.dwReserved0 == PHYSFS_IO_REPARSE_TAG_SYMLINK); +} /* isSymlink */ + + +int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *st, const int follow) +{ + WIN32_FILE_ATTRIBUTE_DATA winstat; + WCHAR *wstr = NULL; + DWORD err = 0; + BOOL rc = 0; + int issymlink = 0; + + UTF8_TO_UNICODE_STACK(wstr, filename); + BAIL_IF(!wstr, PHYSFS_ERR_OUT_OF_MEMORY, 0); + rc = GetFileAttributesExW(wstr, GetFileExInfoStandard, &winstat); + + if (!rc) + err = GetLastError(); + else /* check for symlink while wstr is still available */ + issymlink = !follow && isSymlink(wstr, winstat.dwFileAttributes); + + __PHYSFS_smallFree(wstr); + BAIL_IF(!rc, errcodeFromWinApiError(err), 0); + + st->modtime = FileTimeToPhysfsTime(&winstat.ftLastWriteTime); + st->accesstime = FileTimeToPhysfsTime(&winstat.ftLastAccessTime); + st->createtime = FileTimeToPhysfsTime(&winstat.ftCreationTime); + + if (issymlink) + { + st->filetype = PHYSFS_FILETYPE_SYMLINK; + st->filesize = 0; + } /* if */ + + else if (winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + st->filetype = PHYSFS_FILETYPE_DIRECTORY; + st->filesize = 0; + } /* else if */ + + else if (winstat.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_DEVICE)) + { + st->filetype = PHYSFS_FILETYPE_OTHER; + st->filesize = (((PHYSFS_uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow; + } /* else if */ + + else + { + st->filetype = PHYSFS_FILETYPE_REGULAR; + st->filesize = (((PHYSFS_uint64) winstat.nFileSizeHigh) << 32) | winstat.nFileSizeLow; + } /* else */ + + st->readonly = ((winstat.dwFileAttributes & FILE_ATTRIBUTE_READONLY) != 0); + + return 1; +} /* __PHYSFS_platformStat */ + +#endif /* PHYSFS_PLATFORM_WINDOWS */ + +/* end of physfs_platform_windows.c ... */ + + diff --git a/Source/3rdParty/physfs/physfs_platform_winrt.cpp b/Source/3rdParty/physfs/physfs_platform_winrt.cpp new file mode 100644 index 0000000..f50fc9d --- /dev/null +++ b/Source/3rdParty/physfs/physfs_platform_winrt.cpp @@ -0,0 +1,41 @@ +/* + * Windows Runtime (WinRT) support routines for PhysicsFS. + * + * Please see the file LICENSE.txt in the source's root directory. + * + * This file originally written by Martin "T-Bone" Ahrnbom, but was mostly + * merged into physfs_platform_windows.c by Ryan C. Gordon (so please harass + * Ryan about bugs and not Martin). + */ + +/* (There used to be instructions on how to make a WinRT project, but at + this point, either CMake will do it for you or you should just drop + PhysicsFS's sources into your existing project. --ryan.) */ + +#define __PHYSICSFS_INTERNAL__ +#include "physfs_platforms.h" + +#ifdef PHYSFS_PLATFORM_WINRT + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS 1 +#endif +#include <windows.h> + +#include "physfs_internal.h" + +const void *__PHYSFS_winrtCalcBaseDir(void) +{ + return Windows::ApplicationModel::Package::Current->InstalledLocation->Path->Data(); +} /* __PHYSFS_winrtCalcBaseDir */ + +const void *__PHYSFS_winrtCalcPrefDir(void) +{ + return Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data(); +} /* __PHYSFS_winrtCalcBaseDir */ + + +#endif /* PHYSFS_PLATFORM_WINRT */ + +/* end of physfs_platform_winrt.cpp ... */ + diff --git a/Source/3rdParty/physfs/physfs_platforms.h b/Source/3rdParty/physfs/physfs_platforms.h new file mode 100644 index 0000000..d4e4bfd --- /dev/null +++ b/Source/3rdParty/physfs/physfs_platforms.h @@ -0,0 +1,80 @@ +#ifndef _INCL_PHYSFS_PLATFORMS +#define _INCL_PHYSFS_PLATFORMS + +#ifndef __PHYSICSFS_INTERNAL__ +#error Do not include this header from your applications. +#endif + +/* + * These only define the platforms to determine which files in the platforms + * directory should be compiled. For example, technically BeOS can be called + * a "unix" system, but since it doesn't use unix.c, we don't define + * PHYSFS_PLATFORM_UNIX on that system. + */ + +#if (defined __HAIKU__) +# define PHYSFS_PLATFORM_HAIKU 1 +# define PHYSFS_PLATFORM_POSIX 1 +#elif ((defined __BEOS__) || (defined __beos__)) +# error BeOS support was dropped since PhysicsFS 2.1. Sorry. Try Haiku! +#elif (defined _WIN32_WCE) || (defined _WIN64_WCE) +# error PocketPC support was dropped since PhysicsFS 2.1. Sorry. Try WinRT! +#elif (defined(_MSC_VER) && (_MSC_VER >= 1700) && !_USING_V110_SDK71_) /* _MSC_VER==1700 for MSVC 2012 */ +# include <winapifamily.h> +# define PHYSFS_PLATFORM_WINDOWS 1 +# if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +# define PHYSFS_NO_CDROM_SUPPORT 1 +# define PHYSFS_PLATFORM_WINRT 1 +# endif +#elif (((defined _WIN32) || (defined _WIN64)) && (!defined __CYGWIN__)) +# define PHYSFS_PLATFORM_WINDOWS 1 +#elif defined(__OS2__) || defined(OS2) +# define PHYSFS_PLATFORM_OS2 1 +#elif ((defined __MACH__) && (defined __APPLE__)) +/* To check if iOS or not, we need to include this file */ +# include <TargetConditionals.h> +# if ((TARGET_IPHONE_SIMULATOR) || (TARGET_OS_IPHONE)) +# define PHYSFS_NO_CDROM_SUPPORT 1 +# endif +# define PHYSFS_PLATFORM_APPLE 1 +# define PHYSFS_PLATFORM_POSIX 1 +#elif defined(macintosh) +# error Classic Mac OS support was dropped from PhysicsFS 2.0. Move to OS X. +#elif defined(ANDROID) +# define PHYSFS_PLATFORM_LINUX 1 +# define PHYSFS_PLATFORM_UNIX 1 +# define PHYSFS_PLATFORM_POSIX 1 +# define PHYSFS_NO_CDROM_SUPPORT 1 +#elif defined(__linux) +# define PHYSFS_PLATFORM_LINUX 1 +# define PHYSFS_PLATFORM_UNIX 1 +# define PHYSFS_PLATFORM_POSIX 1 +#elif defined(__sun) || defined(sun) +# define PHYSFS_PLATFORM_SOLARIS 1 +# define PHYSFS_PLATFORM_UNIX 1 +# define PHYSFS_PLATFORM_POSIX 1 +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) +# define PHYSFS_PLATFORM_FREEBSD 1 +# define PHYSFS_PLATFORM_BSD 1 +# define PHYSFS_PLATFORM_UNIX 1 +# define PHYSFS_PLATFORM_POSIX 1 +#elif defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) +# define PHYSFS_PLATFORM_BSD 1 +# define PHYSFS_PLATFORM_UNIX 1 +# define PHYSFS_PLATFORM_POSIX 1 +#elif defined(__EMSCRIPTEN__) +# define PHYSFS_NO_CDROM_SUPPORT 1 +# define PHYSFS_PLATFORM_UNIX 1 +# define PHYSFS_PLATFORM_POSIX 1 +#elif defined(__QNX__) +# define PHYSFS_PLATFORM_QNX 1 +# define PHYSFS_PLATFORM_POSIX 1 +#elif defined(unix) || defined(__unix__) +# define PHYSFS_PLATFORM_UNIX 1 +# define PHYSFS_PLATFORM_POSIX 1 +#else +# error Unknown platform. +#endif + +#endif /* include-once blocker. */ + diff --git a/Source/3rdParty/physfs/physfs_unicode.c b/Source/3rdParty/physfs/physfs_unicode.c new file mode 100644 index 0000000..0e00602 --- /dev/null +++ b/Source/3rdParty/physfs/physfs_unicode.c @@ -0,0 +1,567 @@ +#define __PHYSICSFS_INTERNAL__ +#include "physfs_internal.h" + +#include "physfs_casefolding.h" + + +/* + * From rfc3629, the UTF-8 spec: + * https://www.ietf.org/rfc/rfc3629.txt + * + * Char. number range | UTF-8 octet sequence + * (hexadecimal) | (binary) + * --------------------+--------------------------------------------- + * 0000 0000-0000 007F | 0xxxxxxx + * 0000 0080-0000 07FF | 110xxxxx 10xxxxxx + * 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx + * 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + */ + + +/* + * This may not be the best value, but it's one that isn't represented + * in Unicode (0x10FFFF is the largest codepoint value). We return this + * value from utf8codepoint() if there's bogus bits in the + * stream. utf8codepoint() will turn this value into something + * reasonable (like a question mark), for text that wants to try to recover, + * whereas utf8valid() will use the value to determine if a string has bad + * bits. + */ +#define UNICODE_BOGUS_CHAR_VALUE 0xFFFFFFFF + +/* + * This is the codepoint we currently return when there was bogus bits in a + * UTF-8 string. May not fly in Asian locales? + */ +#define UNICODE_BOGUS_CHAR_CODEPOINT '?' + +static PHYSFS_uint32 utf8codepoint(const char **_str) +{ + const char *str = *_str; + PHYSFS_uint32 retval = 0; + PHYSFS_uint32 octet = (PHYSFS_uint32) ((PHYSFS_uint8) *str); + PHYSFS_uint32 octet2, octet3, octet4; + + if (octet == 0) /* null terminator, end of string. */ + return 0; + + else if (octet < 128) /* one octet char: 0 to 127 */ + { + (*_str)++; /* skip to next possible start of codepoint. */ + return octet; + } /* else if */ + + else if ((octet > 127) && (octet < 192)) /* bad (starts with 10xxxxxx). */ + { + /* + * Apparently each of these is supposed to be flagged as a bogus + * char, instead of just resyncing to the next valid codepoint. + */ + (*_str)++; /* skip to next possible start of codepoint. */ + return UNICODE_BOGUS_CHAR_VALUE; + } /* else if */ + + else if (octet < 224) /* two octets */ + { + (*_str)++; /* advance at least one byte in case of an error */ + octet -= (128+64); + octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 1; /* skip to next possible start of codepoint. */ + retval = ((octet << 6) | (octet2 - 128)); + if ((retval >= 0x80) && (retval <= 0x7FF)) + return retval; + } /* else if */ + + else if (octet < 240) /* three octets */ + { + (*_str)++; /* advance at least one byte in case of an error */ + octet -= (128+64+32); + octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet3 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet3 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 2; /* skip to next possible start of codepoint. */ + retval = ( ((octet << 12)) | ((octet2-128) << 6) | ((octet3-128)) ); + + /* There are seven "UTF-16 surrogates" that are illegal in UTF-8. */ + switch (retval) + { + case 0xD800: + case 0xDB7F: + case 0xDB80: + case 0xDBFF: + case 0xDC00: + case 0xDF80: + case 0xDFFF: + return UNICODE_BOGUS_CHAR_VALUE; + } /* switch */ + + /* 0xFFFE and 0xFFFF are illegal, too, so we check them at the edge. */ + if ((retval >= 0x800) && (retval <= 0xFFFD)) + return retval; + } /* else if */ + + else if (octet < 248) /* four octets */ + { + (*_str)++; /* advance at least one byte in case of an error */ + octet -= (128+64+32+16); + octet2 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet2 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet3 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet3 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet4 = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet4 & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 3; /* skip to next possible start of codepoint. */ + retval = ( ((octet << 18)) | ((octet2 - 128) << 12) | + ((octet3 - 128) << 6) | ((octet4 - 128)) ); + if ((retval >= 0x10000) && (retval <= 0x10FFFF)) + return retval; + } /* else if */ + + /* + * Five and six octet sequences became illegal in rfc3629. + * We throw the codepoint away, but parse them to make sure we move + * ahead the right number of bytes and don't overflow the buffer. + */ + + else if (octet < 252) /* five octets */ + { + (*_str)++; /* advance at least one byte in case of an error */ + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 4; /* skip to next possible start of codepoint. */ + return UNICODE_BOGUS_CHAR_VALUE; + } /* else if */ + + else /* six octets */ + { + (*_str)++; /* advance at least one byte in case of an error */ + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + octet = (PHYSFS_uint32) ((PHYSFS_uint8) *(++str)); + if ((octet & (128+64)) != 128) /* Format isn't 10xxxxxx? */ + return UNICODE_BOGUS_CHAR_VALUE; + + *_str += 6; /* skip to next possible start of codepoint. */ + return UNICODE_BOGUS_CHAR_VALUE; + } /* else if */ + + return UNICODE_BOGUS_CHAR_VALUE; +} /* utf8codepoint */ + +static PHYSFS_uint32 utf16codepoint(const PHYSFS_uint16 **_str) +{ + const PHYSFS_uint16 *src = *_str; + PHYSFS_uint32 cp = (PHYSFS_uint32) *(src++); + + if (cp == 0) /* null terminator, end of string. */ + return 0; + /* Orphaned second half of surrogate pair? */ + else if ((cp >= 0xDC00) && (cp <= 0xDFFF)) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + else if ((cp >= 0xD800) && (cp <= 0xDBFF)) /* start surrogate pair! */ + { + const PHYSFS_uint32 pair = (PHYSFS_uint32) *src; + if (pair == 0) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + else if ((pair < 0xDC00) || (pair > 0xDFFF)) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + else + { + src++; /* eat the other surrogate. */ + cp = (((cp - 0xD800) << 10) | (pair - 0xDC00)); + } /* else */ + } /* else if */ + + *_str = src; + return cp; +} /* utf16codepoint */ + +static PHYSFS_uint32 utf32codepoint(const PHYSFS_uint32 **_str) +{ + const PHYSFS_uint32 *src = *_str; + PHYSFS_uint32 cp = *(src++); + + if (cp == 0) /* null terminator, end of string. */ + return 0; + else if (cp > 0x10FFF) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + + *_str = src; + return cp; +} /* utf32codepoint */ + + +void PHYSFS_utf8ToUcs4(const char *src, PHYSFS_uint32 *dst, PHYSFS_uint64 len) +{ + len -= sizeof (PHYSFS_uint32); /* save room for null char. */ + while (len >= sizeof (PHYSFS_uint32)) + { + PHYSFS_uint32 cp = utf8codepoint(&src); + if (cp == 0) + break; + else if (cp == UNICODE_BOGUS_CHAR_VALUE) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + *(dst++) = cp; + len -= sizeof (PHYSFS_uint32); + } /* while */ + + *dst = 0; +} /* PHYSFS_utf8ToUcs4 */ + + +void PHYSFS_utf8ToUcs2(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len) +{ + len -= sizeof (PHYSFS_uint16); /* save room for null char. */ + while (len >= sizeof (PHYSFS_uint16)) + { + PHYSFS_uint32 cp = utf8codepoint(&src); + if (cp == 0) + break; + else if (cp == UNICODE_BOGUS_CHAR_VALUE) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + + if (cp > 0xFFFF) /* UTF-16 surrogates (bogus chars in UCS-2) */ + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + + *(dst++) = cp; + len -= sizeof (PHYSFS_uint16); + } /* while */ + + *dst = 0; +} /* PHYSFS_utf8ToUcs2 */ + + +void PHYSFS_utf8ToUtf16(const char *src, PHYSFS_uint16 *dst, PHYSFS_uint64 len) +{ + len -= sizeof (PHYSFS_uint16); /* save room for null char. */ + while (len >= sizeof (PHYSFS_uint16)) + { + PHYSFS_uint32 cp = utf8codepoint(&src); + if (cp == 0) + break; + else if (cp == UNICODE_BOGUS_CHAR_VALUE) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + + if (cp > 0xFFFF) /* encode as surrogate pair */ + { + if (len < (sizeof (PHYSFS_uint16) * 2)) + break; /* not enough room for the pair, stop now. */ + + cp -= 0x10000; /* Make this a 20-bit value */ + + *(dst++) = 0xD800 + ((cp >> 10) & 0x3FF); + len -= sizeof (PHYSFS_uint16); + + cp = 0xDC00 + (cp & 0x3FF); + } /* if */ + + *(dst++) = cp; + len -= sizeof (PHYSFS_uint16); + } /* while */ + + *dst = 0; +} /* PHYSFS_utf8ToUtf16 */ + +static void utf8fromcodepoint(PHYSFS_uint32 cp, char **_dst, PHYSFS_uint64 *_len) +{ + char *dst = *_dst; + PHYSFS_uint64 len = *_len; + + if (len == 0) + return; + + if (cp > 0x10FFFF) + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + else if ((cp == 0xFFFE) || (cp == 0xFFFF)) /* illegal values. */ + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + else + { + /* There are seven "UTF-16 surrogates" that are illegal in UTF-8. */ + switch (cp) + { + case 0xD800: + case 0xDB7F: + case 0xDB80: + case 0xDBFF: + case 0xDC00: + case 0xDF80: + case 0xDFFF: + cp = UNICODE_BOGUS_CHAR_CODEPOINT; + } /* switch */ + } /* else */ + + /* Do the encoding... */ + if (cp < 0x80) + { + *(dst++) = (char) cp; + len--; + } /* if */ + + else if (cp < 0x800) + { + if (len < 2) + len = 0; + else + { + *(dst++) = (char) ((cp >> 6) | 128 | 64); + *(dst++) = (char) (cp & 0x3F) | 128; + len -= 2; + } /* else */ + } /* else if */ + + else if (cp < 0x10000) + { + if (len < 3) + len = 0; + else + { + *(dst++) = (char) ((cp >> 12) | 128 | 64 | 32); + *(dst++) = (char) ((cp >> 6) & 0x3F) | 128; + *(dst++) = (char) (cp & 0x3F) | 128; + len -= 3; + } /* else */ + } /* else if */ + + else + { + if (len < 4) + len = 0; + else + { + *(dst++) = (char) ((cp >> 18) | 128 | 64 | 32 | 16); + *(dst++) = (char) ((cp >> 12) & 0x3F) | 128; + *(dst++) = (char) ((cp >> 6) & 0x3F) | 128; + *(dst++) = (char) (cp & 0x3F) | 128; + len -= 4; + } /* else if */ + } /* else */ + + *_dst = dst; + *_len = len; +} /* utf8fromcodepoint */ + +#define UTF8FROMTYPE(typ, src, dst, len) \ + if (len == 0) return; \ + len--; \ + while (len) \ + { \ + const PHYSFS_uint32 cp = (PHYSFS_uint32) ((typ) (*(src++))); \ + if (cp == 0) break; \ + utf8fromcodepoint(cp, &dst, &len); \ + } \ + *dst = '\0'; \ + +void PHYSFS_utf8FromUcs4(const PHYSFS_uint32 *src, char *dst, PHYSFS_uint64 len) +{ + UTF8FROMTYPE(PHYSFS_uint32, src, dst, len); +} /* PHYSFS_utf8FromUcs4 */ + +void PHYSFS_utf8FromUcs2(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len) +{ + UTF8FROMTYPE(PHYSFS_uint64, src, dst, len); +} /* PHYSFS_utf8FromUcs2 */ + +/* latin1 maps to unicode codepoints directly, we just utf-8 encode it. */ +void PHYSFS_utf8FromLatin1(const char *src, char *dst, PHYSFS_uint64 len) +{ + UTF8FROMTYPE(PHYSFS_uint8, src, dst, len); +} /* PHYSFS_utf8FromLatin1 */ + +#undef UTF8FROMTYPE + + +void PHYSFS_utf8FromUtf16(const PHYSFS_uint16 *src, char *dst, PHYSFS_uint64 len) +{ + if (len == 0) + return; + + len--; + while (len) + { + const PHYSFS_uint32 cp = utf16codepoint(&src); + if (!cp) + break; + utf8fromcodepoint(cp, &dst, &len); + } /* while */ + + *dst = '\0'; +} /* PHYSFS_utf8FromUtf16 */ + + +int PHYSFS_caseFold(const PHYSFS_uint32 from, PHYSFS_uint32 *to) +{ + int i; + + if (from < 128) /* low-ASCII, easy! */ + { + if ((from >= 'A') && (from <= 'Z')) + *to = from - ('A' - 'a'); + else + *to = from; + return 1; + } /* if */ + + else if (from <= 0xFFFF) + { + const PHYSFS_uint8 hash = ((from ^ (from >> 8)) & 0xFF); + const PHYSFS_uint16 from16 = (PHYSFS_uint16) from; + + { + const CaseFoldHashBucket1_16 *bucket = &case_fold_hash1_16[hash]; + const int count = (int) bucket->count; + for (i = 0; i < count; i++) + { + const CaseFoldMapping1_16 *mapping = &bucket->list[i]; + if (mapping->from == from16) + { + *to = mapping->to0; + return 1; + } /* if */ + } /* for */ + } + + { + const CaseFoldHashBucket2_16 *bucket = &case_fold_hash2_16[hash & 15]; + const int count = (int) bucket->count; + for (i = 0; i < count; i++) + { + const CaseFoldMapping2_16 *mapping = &bucket->list[i]; + if (mapping->from == from16) + { + to[0] = mapping->to0; + to[1] = mapping->to1; + return 2; + } /* if */ + } /* for */ + } + + { + const CaseFoldHashBucket3_16 *bucket = &case_fold_hash3_16[hash & 3]; + const int count = (int) bucket->count; + for (i = 0; i < count; i++) + { + const CaseFoldMapping3_16 *mapping = &bucket->list[i]; + if (mapping->from == from16) + { + to[0] = mapping->to0; + to[1] = mapping->to1; + to[2] = mapping->to2; + return 3; + } /* if */ + } /* for */ + } + } /* else if */ + + else /* codepoint that doesn't fit in 16 bits. */ + { + const PHYSFS_uint8 hash = ((from ^ (from >> 8)) & 0xFF); + const CaseFoldHashBucket1_32 *bucket = &case_fold_hash1_32[hash & 15]; + const int count = (int) bucket->count; + for (i = 0; i < count; i++) + { + const CaseFoldMapping1_32 *mapping = &bucket->list[i]; + if (mapping->from == from) + { + *to = mapping->to0; + return 1; + } /* if */ + } /* for */ + } /* else */ + + + /* Not found...there's no remapping for this codepoint. */ + *to = from; + return 1; +} /* PHYSFS_caseFold */ + + +#define UTFSTRICMP(bits) \ + PHYSFS_uint32 folded1[3], folded2[3]; \ + int head1 = 0, tail1 = 0, head2 = 0, tail2 = 0; \ + while (1) { \ + PHYSFS_uint32 cp1, cp2; \ + if (head1 != tail1) { \ + cp1 = folded1[tail1++]; \ + } else { \ + head1 = PHYSFS_caseFold(utf##bits##codepoint(&str1), folded1); \ + cp1 = folded1[0]; \ + tail1 = 1; \ + } \ + if (head2 != tail2) { \ + cp2 = folded2[tail2++]; \ + } else { \ + head2 = PHYSFS_caseFold(utf##bits##codepoint(&str2), folded2); \ + cp2 = folded2[0]; \ + tail2 = 1; \ + } \ + if (cp1 < cp2) { \ + return -1; \ + } else if (cp1 > cp2) { \ + return 1; \ + } else if (cp1 == 0) { \ + break; /* complete match. */ \ + } \ + } \ + return 0 + +int PHYSFS_utf8stricmp(const char *str1, const char *str2) +{ + UTFSTRICMP(8); +} /* PHYSFS_utf8stricmp */ + +int PHYSFS_utf16stricmp(const PHYSFS_uint16 *str1, const PHYSFS_uint16 *str2) +{ + UTFSTRICMP(16); +} /* PHYSFS_utf16stricmp */ + +int PHYSFS_ucs4stricmp(const PHYSFS_uint32 *str1, const PHYSFS_uint32 *str2) +{ + UTFSTRICMP(32); +} /* PHYSFS_ucs4stricmp */ + +#undef UTFSTRICMP + +/* end of physfs_unicode.c ... */ + diff --git a/Source/3rdParty/stb/stb_image.h b/Source/3rdParty/stb/stb_image.h new file mode 100644 index 0000000..4df317a --- /dev/null +++ b/Source/3rdParty/stb/stb_image.h @@ -0,0 +1,7176 @@ +/* stb_image - v2.15 - public domain image loader - http://nothings.org/stb_image.h + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) partial animated GIF support + limited 16-bit PSD support + minor bugs, code cleanup, and compiler warnings + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNetManager JPGs) + Optimizations & bugfixes + Fabian "ryg" Giesen + Arseny Kapoulkine + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan + Dave Moore Roy Eltham Hayaki Saito Nathan Reed + Won Chun Luke Graham Johan Duparc Nick Verigakis + the Horde3D community Thomas Ruf Ronny Chevalier Baldur Karlsson + Janez Zemva John Bartholomew Michal Cichon github:rlyeh + Jonathan Blow Ken Hamada Tero Hanninen github:romigrou + Laurent Gomila Cort Stratton Sergio Gonzalez github:svdijk + Aruelien Pocheville Thibault Reuille Cass Everitt github:snagar + Ryamond Barbiero Paul Du Bois Engin Manap github:Zelex + Michaelangel007@github Philipp Wiesemann Dale Weiler github:grim210 + Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:sammyhw + Blazej Dariusz Roszkowski Gregory Mullen github:phprus + +*/ +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 16-bit-per-channel PNG +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'req_comp' if req_comp is non-zero, or *comp otherwise. +// If req_comp is non-zero, *comp has the number of components that _would_ +// have been output otherwise. E.g. if you set req_comp to 4, you will always +// get RGBA output, but you can check *comp to see if it's trivially opaque +// because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *comp will be unchanged. The function stbi_failure_reason() +// can be queried for an extremely brief, end-user unfriendly explanation +// of why the load failed. Define STBI_NO_FAILURE_STRINGS to avoid +// compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy to use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// make more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// + + +#ifndef STBI_NO_STDIO +#include <stdio.h> +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for req_comp + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif +// @TODO the other variants + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include <stdarg.h> +#include <stddef.h> // ptrdiff_t on osx +#include <stdlib.h> +#include <string.h> +#include <limits.h> + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include <math.h> // ldexp +#endif + +#ifndef STBI_NO_STDIO +#include <stdio.h> +#endif + +#ifndef STBI_ASSERT +#include <assert.h> +#define STBI_ASSERT(x) assert(x) +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include <stdint.h> +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include <emmintrin.h> + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include <intrin.h> // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +static int stbi__sse2_available() +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +static int stbi__sse2_available() +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include <arm_neon.h> +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 8) { + STBI_ASSERT(ri.bits_per_channel == 16); + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int w = *x, h = *y; + int channels = req_comp ? req_comp : *comp; + int row,col,z; + stbi_uc *image = (stbi_uc *) result; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < channels; z++) { + stbi_uc temp = image[(row * w + col) * channels + z]; + image[(row * w + col) * channels + z] = image[((h - row - 1) * w + col) * channels + z]; + image[((h - row - 1) * w + col) * channels + z] = temp; + } + } + } + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 16) { + STBI_ASSERT(ri.bits_per_channel == 8); + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int w = *x, h = *y; + int channels = req_comp ? req_comp : *comp; + int row,col,z; + stbi__uint16 *image = (stbi__uint16 *) result; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < channels; z++) { + stbi__uint16 temp = image[(row * w + col) * channels + z]; + image[(row * w + col) * channels + z] = image[((h - row - 1) * w + col) * channels + z]; + image[((h - row - 1) * w + col) * channels + z] = temp; + } + } + } + } + + return (stbi__uint16 *) result; +} + +#ifndef STBI_NO_HDR +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int w = *x, h = *y; + int depth = req_comp ? req_comp : *comp; + int row,col,z; + float temp; + + // @OPTIMIZE: use a bigger temp buffer and memcpy multiple pixels at once + for (row = 0; row < (h>>1); row++) { + for (col = 0; col < w; col++) { + for (z = 0; z < depth; z++) { + temp = result[(row * w + col) * depth + z]; + result[(row * w + col) * depth + z] = result[((h - row - 1) * w + col) * depth + z]; + result[((h - row - 1) * w + col) * depth + z] = temp; + } + } + } + } +} +#endif + +#ifndef STBI_NO_STDIO + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_file(&s,f); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<<n) + 1 +static int const stbi__jbias[16] = {0,-1,-3,-7,-15,-31,-63,-127,-255,-511,-1023,-2047,-4095,-8191,-16383,-32767}; + +// combined JPEG 'receive' and JPEG 'extend', since baseline +// always extends everything it receives. +stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) +{ + unsigned int k; + int sgn; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = sixteen ? stbi__get16be(z->s) : stbi__get8(z->s); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4]; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc k = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], k); + out[1] = stbi__blinn_8x8(coutput[1][i], k); + out[2] = stbi__blinn_8x8(coutput[2][i], k); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc k = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], k); + out[1] = stbi__blinn_8x8(255 - out[1], k); + out[2] = stbi__blinn_8x8(255 - out[2], k); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc k = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], k); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], k); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], k); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + if (s->img_x == x && s->img_y == y) { + if (raw_len != img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } else { // interlaced: + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + } + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + p[0] = p[2] * 255 / a; + p[1] = p[1] * 255 / a; + p[2] = t * 255 / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int stbi__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if(is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // else: fall-through + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fall-through + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; y<height; ++y) { + int packet_idx; + + for(packet_idx=0; packet_idx < num_packets; ++packet_idx) { + stbi__pic_packet *packet = &packets[packet_idx]; + stbi_uc *dest = result+y*width*4; + + switch (packet->type) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;x<width;++x, dest+=4) + if (!stbi__readval(s,packet->channel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; i<count; ++i,dest+=4) + stbi__copyval(packet->channel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;i<count;++i, dest += 4) + stbi__copyval(packet->channel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;i<count;++i, dest+=4) + if (!stbi__readval(s,packet->channel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out, *old_out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags, delay; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[4096]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) +{ + int x, y; + stbi_uc *c = g->pal[g->bgindex]; + for (y = y0; y < y1; y += 4 * g->w) { + for (x = x0; x < x1; x += 4) { + stbi_uc *p = &g->out[y + x]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = 0; + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +{ + int i; + stbi_uc *prev_out = 0; + + if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) + return 0; // stbi__g_failure_reason set by stbi__gif_header + + if (!stbi__mad3sizes_valid(g->w, g->h, 4, 0)) + return stbi__errpuc("too large", "GIF too large"); + + prev_out = g->out; + g->out = (stbi_uc *) stbi__malloc_mad3(4, g->w, g->h, 0); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + + switch ((g->eflags & 0x1C) >> 2) { + case 0: // unspecified (also always used on 1st frame) + stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); + break; + case 1: // do not dispose + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + g->old_out = prev_out; + break; + case 2: // dispose to background + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); + break; + case 3: // dispose to previous + if (g->old_out) { + for (i = g->start_y; i < g->max_y; i += 4 * g->w) + memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); + } + break; + } + + for (;;) { + switch (stbi__get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int prev_trans = -1; + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + if (g->transparent >= 0 && (g->eflags & 0x01)) { + prev_trans = g->pal[g->transparent][3]; + g->pal[g->transparent][3] = 0; + } + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (prev_trans != -1) + g->pal[g->transparent][3] = (stbi_uc) prev_trans; + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = stbi__get16le(s); + g->transparent = stbi__get8(s); + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) + stbi__skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } + + STBI_NOTUSED(req_comp); +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + memset(g, 0, sizeof(*g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, g, comp, req_comp); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g->w; + *y = g->h; + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g->w, g->h); + } + else if (g->out) + STBI_FREE(g->out); + STBI_FREE(g); + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) *comp = info.ma ? 4 : 3; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + if (stbi__get16be(s) != 8) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/Source/Asura.Editor/Scripts/scene_view.lua b/Source/Asura.Editor/Scripts/scene_view.lua new file mode 100644 index 0000000..ff96b2c --- /dev/null +++ b/Source/Asura.Editor/Scripts/scene_view.lua @@ -0,0 +1,2 @@ +-- 场景窗口 + diff --git a/Source/Asura.Engine/Application.cpp b/Source/Asura.Engine/Application.cpp new file mode 100644 index 0000000..2f0b8d0 --- /dev/null +++ b/Source/Asura.Engine/Application.cpp @@ -0,0 +1,24 @@ +#include "Application.h" + +namespace AsuraEngine +{ + + Application::Application() + { + + } + + Application::~Application() + { + + } + + bool Application::Init(int flag) + { + if (flag & Asura_Graphics) + { + + } + } + +} diff --git a/Source/Asura.Engine/Application.h b/Source/Asura.Engine/Application.h new file mode 100644 index 0000000..174ab91 --- /dev/null +++ b/Source/Asura.Engine/Application.h @@ -0,0 +1,50 @@ +#ifndef __ASURA_ENGINE_APPLICATION_H__ +#define __ASURA_ENGINE_APPLICATION_H__ + +#include "Object.h" + +namespace AsuraEngine +{ + + /// + /// ģ + /// + enum SubModules + { + ASURA_NOMODULE = 0x00000000, + + ASURA_MODULE_GRAPHICS = 1 << 1, + ASURA_MODULE_AUDIO = 1 << 2, + ASURA_MODULE_FONT = 1 << 3, + ASURA_MODULE_INPUT = 1 << 4, + ASURA_MODULE_MATH = 1 << 5, + ASURA_MODULE_PHYSICS = 1 << 6, + ASURA_MODULE_TIME = 1 << 7, + ASURA_MODULE_WINDOW = 1 << 8, + + ASURAMODULE_ALL = 0XFFFFFFFF + }; + + /// + /// ѭ + /// + class Application : public Object + { + public: + + Application(); + + virtual ~Application(); + + /// + /// ʼǰϵͳ + /// + bool Init(int subsystems = Asura_All); + + virtual void Run(); + + }; + +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Asura.h b/Source/Asura.Engine/Asura.h index ae6f68e..e1d8940 100644 --- a/Source/Asura.Engine/Asura.h +++ b/Source/Asura.Engine/Asura.h @@ -1,12 +1,14 @@ #ifndef __ASURA_ENGINE_H__ #define __ASURA_ENGINE_H__ +#include "Application.h" + #include "Graphics/Shader.h" namespace AEGraphics = AsuraEngine::Graphics; namespace AEMath = AsuraEngine::Math; -namespace AETime = AsuraEngine::Time; -namespace AEInput = AsuraEngine::Input; -namespace AEProfiler = AsuraEngine::Profiler; +//namespace AETime = AsuraEngine::Time; +//namespace AEInput = AsuraEngine::Input; +//namespace AEProfiler = AsuraEngine::Profiler; #endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Audio/Sound.h b/Source/Asura.Engine/Audio/Sound.h index e69de29..3bacd09 100644 --- a/Source/Asura.Engine/Audio/Sound.h +++ b/Source/Asura.Engine/Audio/Sound.h @@ -0,0 +1,19 @@ +#ifndef __ASURA_ENGINE_SOUND_H__ +#define __ASURA_ENGINE_SOUND_H__ + +#include "Object.h" + +namespace AsuraEngine +{ + namespace Audio + { + + class Sound : virtual public Object, public Scripting::Portable + { + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Audio/SoundDecoder.h b/Source/Asura.Engine/Audio/SoundDecoder.h new file mode 100644 index 0000000..7b8eb59 --- /dev/null +++ b/Source/Asura.Engine/Audio/SoundDecoder.h @@ -0,0 +1,29 @@ +#ifndef __ASURA_ENGINE_SOUND_DECODER_H__ +#define __ASURA_ENGINE_SOUND_DECODER_H__ + +#include "Sound.h" +#include "FileSystem/DataBuffer.h" + +namespace AsuraEngine +{ + namespace Audio + { + + /// + /// Ƶļ + /// + class SoundDecoder : virtual public Object + { + public: + + SoundDecoder(); + virtual ~SoundDecoder(); + + virtual Sound* Decode(const Filesystem::DataBuffer* db); + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Exceptions/Exception.cpp b/Source/Asura.Engine/Exceptions/Exception.cpp index e69de29..dbb36ca 100644 --- a/Source/Asura.Engine/Exceptions/Exception.cpp +++ b/Source/Asura.Engine/Exceptions/Exception.cpp @@ -0,0 +1,47 @@ +#include "Exception.h" + +#include <cstdarg> +#include <iostream> + +namespace AsuraEngine +{ + + Exception::Exception(const char *fmt, ...) + { + va_list args; + int size_buffer = 256, size_out; + char *buffer; + while (true) + { + buffer = new char[size_buffer]; + memset(buffer, 0, size_buffer); + + va_start(args, fmt); + size_out = vsnprintf(buffer, size_buffer, fmt, args); + va_end(args); + + // see http://perfec.to/vsnprintf/pasprintf.c + // if size_out ... + // == -1 --> output was truncated + // == size_buffer --> output was truncated + // == size_buffer-1 --> ambiguous, /may/ have been truncated + // > size_buffer --> output was truncated, and size_out + // bytes would have been written + if (size_out == size_buffer || size_out == -1 || size_out == size_buffer - 1) + size_buffer *= 2; + else if (size_out > size_buffer) + size_buffer = size_out + 2; // to avoid the ambiguous case + else + break; + + delete[] buffer; + } + message = std::string(buffer); + delete[] buffer; + } + + Exception::~Exception() throw() + { + } + +} diff --git a/Source/Asura.Engine/Exceptions/Exception.h b/Source/Asura.Engine/Exceptions/Exception.h index e69de29..bed8a9a 100644 --- a/Source/Asura.Engine/Exceptions/Exception.h +++ b/Source/Asura.Engine/Exceptions/Exception.h @@ -0,0 +1,43 @@ +#ifndef __ASURA_ENGINE_EXCEPTION_H__ +#define __ASURA_ENGINE_EXCEPTION_H__ + +#include <exception> + +namespace AsuraEngine +{ + + /** + * A convenient vararg-enabled exception class. + **/ + class Exception : public std::exception + { + public: + + /** + * Creates a new Exception according to printf-rules. + * + * See: http://www.cplusplus.com/reference/clibrary/cstdio/printf/ + * + * @param fmt The format string (see printf). + **/ + Exception(const char *fmt, ...); + virtual ~Exception() throw(); + + /** + * Returns a string containing reason for the exception. + * @return A description of the exception. + **/ + inline virtual const char *what() const throw() + { + return message.c_str(); + } + + private: + + std::string message; + + }; // Exception + +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/FileSystem/DataBuffer.cpp b/Source/Asura.Engine/FileSystem/DataBuffer.cpp new file mode 100644 index 0000000..ec2995a --- /dev/null +++ b/Source/Asura.Engine/FileSystem/DataBuffer.cpp @@ -0,0 +1,14 @@ +#include "DataBuffer.h" + +namespace AsuraEngine +{ + namespace Filesystem + { + + DataBuffer::~DataBuffer() + { + delete[] data; + } + + } +}
\ No newline at end of file diff --git a/Source/Asura.Engine/FileSystem/DataBuffer.h b/Source/Asura.Engine/FileSystem/DataBuffer.h new file mode 100644 index 0000000..f7d8cba --- /dev/null +++ b/Source/Asura.Engine/FileSystem/DataBuffer.h @@ -0,0 +1,39 @@ +#ifndef __ASURA_ENGINE_DATABUFFER_H__ +#define __ASURA_ENGINE_DATABUFFER_H__ + +#include <cstdlib> + +#include "Scripting/Luax.hpp" +#include "Object.h" + +namespace AsuraEngine +{ + namespace Filesystem + { + + /// + /// ڴݵķװеʹData bufferװֱʹconst void*ͨresource managerȡ + /// + class DataBuffer final : virtual public Object, public Scripting::Portable + { + public: + + DataBuffer(const void* data, std::size_t size); + + virtual ~DataBuffer(); + + const void* data; + std::size_t size; + + //---------------------------------------------------------------------------------------------------------- + + LUAX_DECL_FACTORY(DataBuffer); + + //---------------------------------------------------------------------------------------------------------- + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/FileSystem/DecodedData.cpp b/Source/Asura.Engine/FileSystem/DecodedData.cpp new file mode 100644 index 0000000..90726cf --- /dev/null +++ b/Source/Asura.Engine/FileSystem/DecodedData.cpp @@ -0,0 +1,19 @@ +#include "DecodedData.h" + +namespace AsuraEngine +{ + namespace Filesystem + { + + DecodedData::DecodedData(const DataBuffer* databuffer) + { + Decode(databuffer); + } + + DecodedData::~DecodedData() + { + + } + + } +} diff --git a/Source/Asura.Engine/FileSystem/DecodedData.h b/Source/Asura.Engine/FileSystem/DecodedData.h new file mode 100644 index 0000000..927052f --- /dev/null +++ b/Source/Asura.Engine/FileSystem/DecodedData.h @@ -0,0 +1,41 @@ +#ifndef __ASURA_ENGINE_DATA_H__ +#define __ASURA_ENGINE_DATA_H__ + +#include <cstdlib> + +#include "DataBuffer.h" +#include "Object.h" + +namespace AsuraEngine +{ + namespace Filesystem + { + + /// + /// һ̹߳data̳дࡣͼƬݡƵݵȣһ߳нԭļڲݸʽ + /// ȡ + /// + class DecodedData : virtual public Object + { + public: + + /// + /// ڴйdataԷһ߳棬Դϵͳء + /// + DecodedData(const DataBuffer* databuffer); + + virtual ~DecodedData(); + + protected: + + /// + /// ڴеݡ + /// + virtual void Decode(const DataBuffer* buffer) = 0; + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/FileSystem/File.cpp b/Source/Asura.Engine/FileSystem/ResourceManager.cpp index e69de29..e69de29 100644 --- a/Source/Asura.Engine/FileSystem/File.cpp +++ b/Source/Asura.Engine/FileSystem/ResourceManager.cpp diff --git a/Source/Asura.Engine/FileSystem/ResourceManager.h b/Source/Asura.Engine/FileSystem/ResourceManager.h new file mode 100644 index 0000000..b4aef54 --- /dev/null +++ b/Source/Asura.Engine/FileSystem/ResourceManager.h @@ -0,0 +1,44 @@ +#ifndef __ASURA_ENGINE_RESOURCE_MANAGER_H__ +#define __ASURA_ENGINE_RESOURCE_MANAGER_H__ + +#include <string> + +#include "DataBuffer.h" +#include "Object.h" + +namespace AsuraEngine +{ + namespace Filesystem + { + + /// + /// Դء洢ԴָĿ¼ȡ + /// + class ResourceManager final : virtual public Object + { + public: + + ResourceManager(); + ~ResourceManager(); + + /// + /// װظĿ¼ + /// + void Mount(const std::string& root); + + /// + /// ȡļһdata bufferעҪȷȷڴ棬ڵôʹunique_ptr + /// + DataBuffer* LoadFile(const std::string& path); + + /// + /// data buffer + /// + void SaveFile(const std::string& path, const DataBuffer* buffer); + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Graphics/BlendMode.h b/Source/Asura.Engine/Graphics/BlendMode.h new file mode 100644 index 0000000..775cc45 --- /dev/null +++ b/Source/Asura.Engine/Graphics/BlendMode.h @@ -0,0 +1,17 @@ +#ifndef __ASURA_ENGINE_BLEND_MODE_H__ +#define __ASURA_ENGINE_BLEND_MODE_H__ + +namespace AsuraEngine +{ + namespace Graphics + { + + enum BlendMode + { + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Graphics/Canvas.cpp b/Source/Asura.Engine/Graphics/Canvas.cpp index 8ca6fb2..61787b6 100644 --- a/Source/Asura.Engine/Graphics/Canvas.cpp +++ b/Source/Asura.Engine/Graphics/Canvas.cpp @@ -5,7 +5,37 @@ namespace AsuraEngine namespace Graphics { - + Canvas::Canvas() + : Texture() + , mWidth(0) + , mHeight(0) + { + glGenFramebuffers(1, &mFBO); + GLint current_fbo; + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, ¤t_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, mFBO); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTextureHandle, 0); + glBindFramebuffer(GL_FRAMEBUFFER, current_fbo); + } + + void Canvas::SetSize(uint w, uint h) + { + GLint current_tex; + glGetIntegerv(GL_TEXTURE_BINDING_2D, ¤t_tex); + glBindTexture(GL_TEXTURE_2D, mTextureHandle); + + glBindTexture(GL_TEXTURE_2D, current_tex); + } + + void Canvas::Bind() + { + + } + + void Canvas::Unbind() + { + + } } }
\ No newline at end of file diff --git a/Source/Asura.Engine/Graphics/Canvas.h b/Source/Asura.Engine/Graphics/Canvas.h index c8faacf..c258793 100644 --- a/Source/Asura.Engine/Graphics/Canvas.h +++ b/Source/Asura.Engine/Graphics/Canvas.h @@ -3,8 +3,8 @@ #include <Scripting/Luax.hpp> +#include "Math/Rect.hpp" #include "GL.h" -#include "SimClass.h" #include "Texture.h" #include "RenderTarget.h" @@ -16,35 +16,34 @@ namespace AsuraEngine /// /// CanvasҲԳΪrender textureҲΪtextureȾ /// - class Canvas final : public Texture, public RenderTarget, public SimClass + class Canvas final : public Drawable, public RenderTarget, public Scripting::Portable { public: Canvas(); - /// - /// render textureĴС - /// - void Init(uint w, uint h); + ~Canvas(); /// - /// canvasΪ + /// render textureĴС /// - void Bind(); + void SetSize(uint w, uint h); /// - /// Ϊǻscreen + /// ȾtexturertϣԭϽǣң /// - void Unbind(); + void Render(const RenderTarget* rt, const Math::Vector2i& pos, const Math::Vector2i& scale, const Math::Vector2i& center, float rot); - void Render(int x, int y, int sx, int sy, int ox, int oy, int r) override; - - void Render(const Math::Rect& quad, int x, int y, int sx, int sy, int ox, int oy, int r) override; + /// + /// ȾtextureһֵrtϣԭϽǣң졣 + /// + void Render(const RenderTarget* rt, const Math::Rectf& quad, const Math::Vector2i& pos, const Math::Vector2i& scale, const Math::Vector2i& center, float rot); //---------------------------------------------------------------------------------------------------------- LUAX_DECL_FACTORY(SimCanvas); + LUAX_DECL_METHOD(l_SetSize); LUAX_DECL_METHOD(l_Bind); LUAX_DECL_METHOD(l_Unbind); @@ -57,6 +56,11 @@ namespace AsuraEngine /// GLuint mFBO; + /// + /// canvasĴС + /// + uint mWidth, mHeight; + }; /// diff --git a/Source/Asura.Engine/Graphics/Color.h b/Source/Asura.Engine/Graphics/Color.h index 6383602..d8f39a0 100644 --- a/Source/Asura.Engine/Graphics/Color.h +++ b/Source/Asura.Engine/Graphics/Color.h @@ -52,6 +52,12 @@ namespace AsuraEngine { public: + static Color Black; + static Color White; + static Color Red; + static Color Green; + static Color Blue; + Color(); Color(const Color& c); @@ -68,12 +74,9 @@ namespace AsuraEngine LUAX_DECL_FACTORY(Color); - LUAX_DECL_METHOD(l_GetRed); // color.r - LUAX_DECL_METHOD(l_GetGreen); // color.g - LUAX_DECL_METHOD(l_GetBlue); // color.b - LUAX_DECL_METHOD(l_GetAlpha); // color.a + LUAX_DECL_METHOD(l_SetColor); + LUAX_DECL_METHOD(l_GetColor); LUAX_DECL_METHOD(l_Multiply); // ɫ˷ - LUAX_DECL_METHOD(l_NewIndex); // r,g,b,a //---------------------------------------------------------------------------------------------------------- diff --git a/Source/Asura.Engine/Graphics/Image.cpp b/Source/Asura.Engine/Graphics/Image.cpp index e19d57f..8287d76 100644 --- a/Source/Asura.Engine/Graphics/Image.cpp +++ b/Source/Asura.Engine/Graphics/Image.cpp @@ -1,4 +1,5 @@ #include "Image.h" +#include "GL.h" namespace AsuraEngine { @@ -7,30 +8,25 @@ namespace AsuraEngine Image::Image() : Texture() - , mPixels(nullptr) - , mWidth(0) - , mHeight(0) { } Image::~Image() { - delete mPixels; } - bool Image::Load(const void* data, size_t size) + //\Ϣ + bool Image::Load(const ImageData* data) { - - } - - void Image::Render(int x, int y, int sx, int sy, int ox, int oy, int r) - { - - } - - void Image::Render(const Math::Rect& quad, int x, int y, int sx, int sy, int ox, int oy, int r) - { - + if (!data) + return false; + if (mImageData) + delete mImageData; + mImageData = data; + glBindTexture(GL_TEXTURE_2D, mTextureHandle); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, data->width, data->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data->pixels); + glBindTexture(GL_TEXTURE_2D, 0); + return true; } } diff --git a/Source/Asura.Engine/Graphics/Image.h b/Source/Asura.Engine/Graphics/Image.h index 391d1c4..da881aa 100644 --- a/Source/Asura.Engine/Graphics/Image.h +++ b/Source/Asura.Engine/Graphics/Image.h @@ -1,13 +1,12 @@ #ifndef __ASURA_ENGINE_IMAGE_H__ #define __ASURA_ENGINE_IMAGE_H__ -#include "Math/Vector2.h" #include "StringMap.hpp" #include "Manager.hpp" #include "Texture.h" #include "Color.h" #include "Factory.h" -#include "SimClass.h" +#include "ImageData.h" namespace AsuraEngine { @@ -20,7 +19,7 @@ namespace AsuraEngine /// ImageͼƬڴȡϷĽһImageڴ桢ԴֻᱣһݣҪ /// imageêλãźתǶȣʹspriteһֻࡣҪǿǵeditorengineʹòͬķװ /// - class Image final : public Texture, public SimClass + class Image final : public Drawable, public Scripting::Portable { public: @@ -28,10 +27,10 @@ namespace AsuraEngine ~Image(); /// - /// bufferimagemPixelsΪգݡ¹imageʹglTexImage2Dύimage - /// ݡ + /// bufferimageϢmPixelsΪգݡ¹imageʹglTexImage2Dύimage + /// ݡ /// - bool Load(const void* data, size_t size); + bool Load(const ImageData* data); uint GetWidth(); uint GetHeight(); @@ -39,11 +38,11 @@ namespace AsuraEngine /// /// ijһλõ /// - Color GetPixel(uint x, uint y); + Color32 GetPixel(uint x, uint y); - void Render(int x, int y, int sx, int sy, int ox, int oy, int r) override; + virtual void Render(const RenderTarget* rt, const RenderState& state) override; - void Render(const Math::Rect& quad, int x, int y, int sx, int sy, int ox, int oy, int r) override; + virtual void Render(const RenderTarget* rt, const Math::Rectf& quad, const RenderState& state) override; //---------------------------------------------------------------------------------------------------------- @@ -58,12 +57,7 @@ namespace AsuraEngine private: - /// - /// СΪλ - /// - uint mWidth, mHeight; - - Color* mPixels; + const ImageData* mImageData; }; diff --git a/Source/Asura.Engine/Graphics/ImageData.cpp b/Source/Asura.Engine/Graphics/ImageData.cpp new file mode 100644 index 0000000..af448bd --- /dev/null +++ b/Source/Asura.Engine/Graphics/ImageData.cpp @@ -0,0 +1,56 @@ +#include "ImageData.h" +#include "PNGDecoder.h" +#include "STBDecoder.h" + +namespace AsuraEngine +{ + namespace Graphics + { + + using namespace std; + + // imagedecoderΪԡ + list<ImageDecoder*> ImageData::ImageDecoders = { + new PNGDecoder(), // png + new STBDecoder() // jpeg, tga, bmp + }; + + void ImageData::ReleaseAllDecoders() + { + for (ImageDecoder* decoder : ImageDecoders) + decoder->Release(); + } + + ImageData::ImageData(const Filesystem::DataBuffer* buffer) + : DecodedData(buffer) + { + } + + ImageData::~ImageData() + { + if (pixels) + delete[] pixels; + } + + /// + /// ɹ׳쳣 + /// + void ImageData::Decode(const Filesystem::DataBuffer* buffer) + { + for (ImageDecoder* decoder : ImageDecoders) + { + if (decoder->CanDecode(buffer)) + { + decoder->Decode(buffer, this); + return; + } + } + } + + Color ImageData::GetPixel(uint x, uint y) + { + + } + + } +}
\ No newline at end of file diff --git a/Source/Asura.Engine/Graphics/ImageData.h b/Source/Asura.Engine/Graphics/ImageData.h new file mode 100644 index 0000000..925a5a0 --- /dev/null +++ b/Source/Asura.Engine/Graphics/ImageData.h @@ -0,0 +1,58 @@ +#ifndef __ASURA_ENGINE_IMAGEDATA_H__ +#define __ASURA_ENGINE_IMAGEDATA_H__ + +#include <list> + +#include "Scripting/Luax.hpp" +#include "Filesystem/DecodedData.h" +#include "ImageDecoder.h" +#include "PixelFormat.h" +#include "Color.h" + +namespace AsuraEngine +{ + namespace Graphics + { + + class ImageData final : public Filesystem::DecodedData, public Scripting::Portable + { + public: + + /// + /// ͼƬļϢʧܣ׳쳣 + /// + ImageData(const Filesystem::DataBuffer* buffer); + ~ImageData(); + + Color GetPixel(uint x, uint y); + + uint width, height; + PixelFormat format; + std::size_t size; + byte* pixels; + + //---------------------------------------------------------------------------------------------------------- + + LUAX_DECL_FACTORY(ImageData); + + LUAX_DECL_METHOD(l_GetPixel); + LUAX_DECL_METHOD(l_GetSize); + + //---------------------------------------------------------------------------------------------------------- + + private: + + // stbJPEGTGABMP,lodePNGpngͼƬ + void Decode(const Filesystem::DataBuffer* buffer) override; + + /// + /// ڵһimage dataʱṩdecoderڼdecodersмѡԡ + /// + static std::list<ImageDecoder*> ImageDecoders; + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Graphics/ImageDecoder.h b/Source/Asura.Engine/Graphics/ImageDecoder.h new file mode 100644 index 0000000..9dc2188 --- /dev/null +++ b/Source/Asura.Engine/Graphics/ImageDecoder.h @@ -0,0 +1,35 @@ +#ifndef __ASURA_ENGINE_IMAGE_DECODER_H__ +#define __ASURA_ENGINE_IMAGE_DECODER_H__ + +#include "FileSystem/DataBuffer.h" + +#include "ImageData.h" + +namespace AsuraEngine +{ + namespace Graphics + { + + class ImageDecoder : virtual public Object + { + public: + + ImageDecoder(); + virtual ~ImageDecoder(); + + /// + /// жڴǷñdecoderѹ + /// + virtual bool CanDecode(const Filesystem::DataBuffer* buffer) = 0; + + /// + /// һڴ棬һѹImage dataѹʧܷnullptr + /// + virtual void Decode(const Filesystem::DataBuffer* buffer, ImageData* data) = 0; + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Graphics/Mesh2D.h b/Source/Asura.Engine/Graphics/Mesh2D.h index 921ae80..cadbec1 100644 --- a/Source/Asura.Engine/Graphics/Mesh2D.h +++ b/Source/Asura.Engine/Graphics/Mesh2D.h @@ -1,8 +1,9 @@ #ifndef __ASURA_ENGINE_MESH2D_H__ #define __ASURA_ENGINE_MESH2D_H__ +#include "Scripting/Luax.hpp" + #include "Object.h" -#include "SimClass.h" namespace AsuraEngine { @@ -12,7 +13,7 @@ namespace AsuraEngine /// /// 2D meshһЩ㶯 /// - class Mesh2D : virtual public Object, public SimClass + class Mesh2D : virtual public Object, public Scripting::Portable { }; diff --git a/Source/Asura.Engine/Graphics/PNGDecoder.cpp b/Source/Asura.Engine/Graphics/PNGDecoder.cpp new file mode 100644 index 0000000..363d478 --- /dev/null +++ b/Source/Asura.Engine/Graphics/PNGDecoder.cpp @@ -0,0 +1,19 @@ +#include "PNGDecoder.h" + +namespace AsuraEngine +{ + namespace Graphics + { + + bool PNGDecoder::CanDecode(const Filesystem::DataBuffer* buffer) + { + return false; + } + + void PNGDecoder::Decode(const Filesystem::DataBuffer* buffer, ImageData* data) + { + + } + + } +} diff --git a/Source/Asura.Engine/Graphics/PNGDecoder.h b/Source/Asura.Engine/Graphics/PNGDecoder.h new file mode 100644 index 0000000..dc5bb60 --- /dev/null +++ b/Source/Asura.Engine/Graphics/PNGDecoder.h @@ -0,0 +1,27 @@ +#ifndef __ASURA_ENGINE_PNGDECODER_H__ +#define __ASURA_ENGINE_PNGDECODER_H__ + +#include "ImageDecoder.h" + +namespace AsuraEngine +{ + namespace Graphics + { + + /// + /// ʹlodepngѹpngļ + /// + class PNGDecoder final : public ImageDecoder + { + public: + + bool CanDecode(const Filesystem::DataBuffer* buffer) override; + + void Decode(const Filesystem::DataBuffer* buffer, ImageData* data) override; + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Graphics/PixelFormat.h b/Source/Asura.Engine/Graphics/PixelFormat.h new file mode 100644 index 0000000..8df07d5 --- /dev/null +++ b/Source/Asura.Engine/Graphics/PixelFormat.h @@ -0,0 +1,91 @@ +namespace AsuraEngine +{ + namespace Graphics + { + + /// + /// ظʽ + /// + enum PixelFormat + { + PIXELFORMAT_UNKNOWN, + + // these are converted to an actual format by love + PIXELFORMAT_NORMAL, + PIXELFORMAT_HDR, + + // "regular" formats + PIXELFORMAT_R8, + PIXELFORMAT_RG8, + PIXELFORMAT_RGBA8, + PIXELFORMAT_sRGBA8, + PIXELFORMAT_R16, + PIXELFORMAT_RG16, + PIXELFORMAT_RGBA16, + PIXELFORMAT_R16F, + PIXELFORMAT_RG16F, + PIXELFORMAT_RGBA16F, + PIXELFORMAT_R32F, + PIXELFORMAT_RG32F, + PIXELFORMAT_RGBA32F, + + PIXELFORMAT_LA8, // Same as RG8, but accessed as (L, L, L, A) + + // packed formats + PIXELFORMAT_RGBA4, + PIXELFORMAT_RGB5A1, + PIXELFORMAT_RGB565, + PIXELFORMAT_RGB10A2, + PIXELFORMAT_RG11B10F, + + // depth/stencil formats + PIXELFORMAT_STENCIL8, + PIXELFORMAT_DEPTH16, + PIXELFORMAT_DEPTH24, + PIXELFORMAT_DEPTH32F, + PIXELFORMAT_DEPTH24_STENCIL8, + PIXELFORMAT_DEPTH32F_STENCIL8, + + // compressed formats + PIXELFORMAT_DXT1, + PIXELFORMAT_DXT3, + PIXELFORMAT_DXT5, + PIXELFORMAT_BC4, + PIXELFORMAT_BC4s, + PIXELFORMAT_BC5, + PIXELFORMAT_BC5s, + PIXELFORMAT_BC6H, + PIXELFORMAT_BC6Hs, + PIXELFORMAT_BC7, + PIXELFORMAT_PVR1_RGB2, + PIXELFORMAT_PVR1_RGB4, + PIXELFORMAT_PVR1_RGBA2, + PIXELFORMAT_PVR1_RGBA4, + PIXELFORMAT_ETC1, + PIXELFORMAT_ETC2_RGB, + PIXELFORMAT_ETC2_RGBA, + PIXELFORMAT_ETC2_RGBA1, + PIXELFORMAT_EAC_R, + PIXELFORMAT_EAC_Rs, + PIXELFORMAT_EAC_RG, + PIXELFORMAT_EAC_RGs, + PIXELFORMAT_ASTC_4x4, + PIXELFORMAT_ASTC_5x4, + PIXELFORMAT_ASTC_5x5, + PIXELFORMAT_ASTC_6x5, + PIXELFORMAT_ASTC_6x6, + PIXELFORMAT_ASTC_8x5, + PIXELFORMAT_ASTC_8x6, + PIXELFORMAT_ASTC_8x8, + PIXELFORMAT_ASTC_10x5, + PIXELFORMAT_ASTC_10x6, + PIXELFORMAT_ASTC_10x8, + PIXELFORMAT_ASTC_10x10, + PIXELFORMAT_ASTC_12x10, + PIXELFORMAT_ASTC_12x12, + + PIXELFORMAT_MAX_ENUM + }; + + } +} diff --git a/Source/Asura.Engine/Math/Rect.cpp b/Source/Asura.Engine/Graphics/Port/ImageData.cpp index e69de29..e69de29 100644 --- a/Source/Asura.Engine/Math/Rect.cpp +++ b/Source/Asura.Engine/Graphics/Port/ImageData.cpp diff --git a/Source/Asura.Engine/Math/Vector2.cpp b/Source/Asura.Engine/Graphics/Port/Mesh2DData.cpp index e69de29..e69de29 100644 --- a/Source/Asura.Engine/Math/Vector2.cpp +++ b/Source/Asura.Engine/Graphics/Port/Mesh2DData.cpp diff --git a/Source/Asura.Engine/Graphics/Port/Shader.cpp b/Source/Asura.Engine/Graphics/Port/Shader.cpp index 887cfae..2253a56 100644 --- a/Source/Asura.Engine/Graphics/Port/Shader.cpp +++ b/Source/Asura.Engine/Graphics/Port/Shader.cpp @@ -12,7 +12,7 @@ namespace AsuraEngine /// int Shader::l_Use(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } @@ -21,7 +21,7 @@ namespace AsuraEngine /// int Shader::l_Unuse(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } @@ -30,7 +30,7 @@ namespace AsuraEngine /// int Shader::l_Load(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } @@ -39,7 +39,7 @@ namespace AsuraEngine /// int Shader::l_HasUniform(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } @@ -48,56 +48,56 @@ namespace AsuraEngine /// int Shader::l_GetUniformLocation(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } int Shader::l_SetBuiltInUniforms(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } int Shader::l_SetUniformFloat(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } int Shader::l_SetUniformTexture(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } int Shader::l_SetUniformVector2(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } int Shader::l_SetUniformVector3(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } int Shader::l_SetUniformVector4(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } int Shader::l_SetUniformColor(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } //עluaijԱ int Shader::RegisterLuaClass(lua_State* L) { - LuaxState state = LuaxState(L); + LuaxState state(L); } diff --git a/Source/Asura.Engine/Graphics/RenderState.h b/Source/Asura.Engine/Graphics/RenderState.h new file mode 100644 index 0000000..9524ef6 --- /dev/null +++ b/Source/Asura.Engine/Graphics/RenderState.h @@ -0,0 +1,52 @@ +#ifndef __ASURA_ENGINE_RENDER_STATE_H__ +#define __ASURA_ENGINE_RENDER_STATE_H__ + +#include "Math/Vector2.hpp" +#include "Math/Rect.hpp" +#include "Math/Transform.h" + +#include "Shader.h" +#include "BlendMode.h" + +namespace AsuraEngine +{ + namespace Graphics + { + + /// + /// Ⱦǰķʽ + /// + class RenderState final : virtual public Object + { + public: + + /// + /// Ĭϵrender state + /// + static RenderState Default; + + RenderState(); + ~RenderState(); + + /// + /// λášλúת + /// + + Math::Transform transform; + + /// + /// ɫ + /// + Shader* shader; + + /// + /// Ϸʽ + /// + BlendMode blendMode; + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Graphics/RenderTarget.h b/Source/Asura.Engine/Graphics/RenderTarget.h index 05d7068..afa8967 100644 --- a/Source/Asura.Engine/Graphics/RenderTarget.h +++ b/Source/Asura.Engine/Graphics/RenderTarget.h @@ -1,20 +1,24 @@ #ifndef __ASURA_ENGINE_RENDERTARGET_H__ #define __ASURA_ENGINE_RENDERTARGET_H__ +#include "Math/Rect.hpp" #include "Texture.h" #include "Object.h" +#include "Color.h" namespace AsuraEngine { namespace Graphics { + class Drawable; + /// /// ɱΪȾĿ࣬ /// Canvas(RenderTexture) /// Window(RenderWindow) /// - class RenderTarget : virtual public Object + class RenderTarget : virtual public Object { public: @@ -22,6 +26,26 @@ namespace AsuraEngine virtual ~RenderTarget() {}; + /// + /// ɫcolRT + /// + virtual void Clear(const Color& col = Color::Black) = 0; + + /// + /// ɫcolղRT + /// + virtual void Clear(const Math::Recti& quad, const Color& col = Color::Black) = 0; + + /// + /// textureRT + /// + virtual void Draw(const Drawable* texture, const RenderState& state) = 0; + + /// + /// һtextureRT + /// + virtual void Draw(const Drawable* texture, const Math::Recti& quad, const RenderState& state) = 0; + }; } diff --git a/Source/Asura.Engine/Graphics/STBDecoder.cpp b/Source/Asura.Engine/Graphics/STBDecoder.cpp new file mode 100644 index 0000000..d4d578f --- /dev/null +++ b/Source/Asura.Engine/Graphics/STBDecoder.cpp @@ -0,0 +1,69 @@ +#include "STBDecoder.h" + +#include "Exceptions/Exception.h" +#include "stb/stb_image.h" + +namespace AsuraEngine +{ + namespace Graphics + { + + bool STBDecoder::CanDecode(const Filesystem::DataBuffer* buffer) + { + int w = 0; + int h = 0; + int comp = 0; + + int status = stbi_info_from_memory((const stbi_uc *)buffer->data, buffer->size, &w, &h, &comp); + + return status == 1 && w > 0 && h > 0; + } + + void STBDecoder::Decode(const Filesystem::DataBuffer* db, ImageData* imageData) + { + if (!db) + throw Exception("Could not decode image with stb decoder because of null databuffer."); + if (!imageData) + throw Exception("Could not decode image with stb decoder because of null output image data."); + const stbi_uc *buffer = (const stbi_uc *)db->data; + int bufferlen = db->size; + int width, height; + int comp = 0; + byte* data = nullptr; + PixelFormat format = PIXELFORMAT_UNKNOWN; + std::size_t size = 0; + + if (stbi_is_hdr_from_memory(buffer, bufferlen)) + { + // 4channelfloat + data = (byte*)stbi_loadf_from_memory(buffer, bufferlen, &width, &height, &comp, STBI_rgb_alpha); + format = PIXELFORMAT_RGBA32F; + size = width * height * 4 * sizeof(float); + } + else + { + data = (byte*)stbi_load_from_memory(buffer, bufferlen, &width, &height, &comp, STBI_rgb_alpha); + format = PIXELFORMAT_ASTC_8x5; + size = width * height * 4; + } + if (data) + { + // ֤ڴ汻ͷţһϲûͷŵΪimage dataһԵģimageǶεġ + if (imageData->pixels) + delete[] imageData->pixels; + imageData->pixels = (byte*)data; + imageData->format = format; + imageData->width = width; + imageData->height = height; + } + else + { + const char *err = stbi_failure_reason(); + if (err == nullptr) + err = "unknown error"; + throw Exception("Could not decode image with stb_image (%s).", err); + } + } + + } +}
\ No newline at end of file diff --git a/Source/Asura.Engine/Graphics/STBDecoder.h b/Source/Asura.Engine/Graphics/STBDecoder.h new file mode 100644 index 0000000..d89042e --- /dev/null +++ b/Source/Asura.Engine/Graphics/STBDecoder.h @@ -0,0 +1,27 @@ +#ifndef __ASURA_ENGINE_STBDECODER_H__ +#define __ASURA_ENGINE_STBDECODER_H__ + +#include "ImageDecoder.h" + +namespace AsuraEngine +{ + namespace Graphics + { + + /// + /// ʹstb_imageѹJPEGTGABMPļ + /// + class STBDecoder final : public ImageDecoder + { + public: + + bool CanDecode(const Filesystem::DataBuffer* buffer) override; + + void Decode(const Filesystem::DataBuffer* buffer, ImageData* data) override; + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Graphics/Shader.cpp b/Source/Asura.Engine/Graphics/Shader.cpp index 6cf65e0..1a85866 100644 --- a/Source/Asura.Engine/Graphics/Shader.cpp +++ b/Source/Asura.Engine/Graphics/Shader.cpp @@ -55,17 +55,17 @@ namespace AsuraEngine } - void Shader::SetUniformVector2(uint loc, const Math::Vector2& vec2) + void Shader::SetUniformVector2(uint loc, const Math::Vector2f& vec2) { } - void Shader::SetUniformVector3(uint loc, const Math::Vector3& vec3) + void Shader::SetUniformVector3(uint loc, const Math::Vector3f& vec3) { } - void Shader::SetUniformVector4(uint loc, const Math::Vector4& vec4) + void Shader::SetUniformVector4(uint loc, const Math::Vector4f& vec4) { } diff --git a/Source/Asura.Engine/Graphics/Shader.h b/Source/Asura.Engine/Graphics/Shader.h index 2cc482b..831f5c3 100644 --- a/Source/Asura.Engine/Graphics/Shader.h +++ b/Source/Asura.Engine/Graphics/Shader.h @@ -5,8 +5,8 @@ #include <string> #include "Scripting/Luax.hpp" -#include "Math/Vector2.h" -#include "Math/Vector3.h" +#include "Math/Vector2.hpp" +#include "Math/Vector3.hpp" #include "Math/Vector4.h" #include "Math/Matrix44.h" #include "StringMap.hpp" @@ -15,7 +15,6 @@ #include "Manager.hpp" #include "Texture.h" #include "GL.h" -#include "SimClass.h" namespace AsuraEngine { @@ -26,7 +25,7 @@ namespace AsuraEngine /// һshaderһڲʼ乲ijShaderuniformsͶݣֻṩuniformsuseɫķ༭ /// ÿshaderͨshaderҵuniforms¶frameworkmaterialá /// - class Shader final : virtual public Object, public SimClass + class Shader final : virtual public Object { public: @@ -54,9 +53,9 @@ namespace AsuraEngine /// void SetUniformFloat(uint loc, float value); void SetUniformTexture(uint loc, const Texture& texture); - void SetUniformVector2(uint loc, const Math::Vector2& vec2); - void SetUniformVector3(uint loc, const Math::Vector3& vec3); - void SetUniformVector4(uint loc, const Math::Vector4& vec4); + void SetUniformVector2(uint loc, const Math::Vector2f& vec2); + void SetUniformVector3(uint loc, const Math::Vector3f& vec3); + void SetUniformVector4(uint loc, const Math::Vector4f& vec4); void SetUniformColor(uint loc, const Color& color); void SetUniformMatrix44(uint loc, const Math::Matrix44& mat44); diff --git a/Source/Asura.Engine/Graphics/Texture.h b/Source/Asura.Engine/Graphics/Texture.h index e5b713c..c1411fc 100644 --- a/Source/Asura.Engine/Graphics/Texture.h +++ b/Source/Asura.Engine/Graphics/Texture.h @@ -1,9 +1,10 @@ #ifndef __ASURA_ENGINE_TEXTURE_H__ #define __ASURA_ENGINE_TEXTURE_H__ -#include "Math/Rect.h" +#include "Math/Rect.hpp" +#include "Math/Vector2.hpp" #include "Scripting/Luax.hpp" -#include "Object.h" +#include "RenderState.h" #include "GL.h" namespace AsuraEngine @@ -11,6 +12,8 @@ namespace AsuraEngine namespace Graphics { + class RenderTarget; + /// /// 2D࣬2d meshrender targetбʹáTextureȾԭϽǣϷϲԵѿϵΪ /// EditorҲϽΪԭ㣬Ϊ˷㡣 @@ -28,12 +31,22 @@ namespace AsuraEngine /// /// ȾtexturertϣԭϽǣң /// - virtual void Render(int x, int y, int sx, int sy, int ox, int oy, int r) = 0; + virtual void Render(const RenderTarget* rt, const RenderState& state) = 0; /// /// ȾtextureһֵrtϣԭϽǣң졣 /// - virtual void Render(const Math::Rect& quad, int x, int y, int sx, int sy, int ox, int oy, int r) = 0; + virtual void Render(const RenderTarget* rt, const Math::Rectf& quad, const RenderState& state) = 0; + + /// + /// ù˷ʽ + /// + void SetSmooth(bool smooth); + + /// + /// ظʽ + /// + void SetRepeated(); protected: @@ -44,7 +57,7 @@ namespace AsuraEngine }; - using Renderable = Texture; + using Drawable = Texture; } } diff --git a/Source/Asura.Engine/Math/Rect.h b/Source/Asura.Engine/Math/Rect.h deleted file mode 100644 index 5dd8631..0000000 --- a/Source/Asura.Engine/Math/Rect.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __ASURA_ENGINE_RECT_H__ -#define __ASURA_ENGINE_RECT_H__ - -namespace AsuraEngine -{ - namespace Math - { - - struct Rect - { - int x, y, w, h; - }; - - } -} - -#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Math/Rect.hpp b/Source/Asura.Engine/Math/Rect.hpp new file mode 100644 index 0000000..f635007 --- /dev/null +++ b/Source/Asura.Engine/Math/Rect.hpp @@ -0,0 +1,32 @@ +#ifndef __ASURA_ENGINE_RECT_H__ +#define __ASURA_ENGINE_RECT_H__ + +namespace AsuraEngine +{ + namespace Math + { + + template<typename T> + struct Rect + { + public: + Rect(); + ~Rect(T x, T y, T w, T h); + + template <typename U> + explicit Rect(const Rect<U>& rect); + + T x, y, w, h; + }; + +#include "Rect.inl" + + // Define the most common types + typedef Rect<int> Recti; + typedef Rect<unsigned int> Rectu; + typedef Rect<float> Rectf; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Math/Rect.inl b/Source/Asura.Engine/Math/Rect.inl new file mode 100644 index 0000000..891a3f8 --- /dev/null +++ b/Source/Asura.Engine/Math/Rect.inl @@ -0,0 +1,19 @@ +template <typename T> +inline Rect<T>::Rect() + : x(0) + , y(0) + , w(0) + , h(0) +{ + +} + +template <typename T> +inline Rect<T>::Rect(T X, T Y, T W, T H) + : x(X) + , y(Y) + , w(W) + , h(H) +{ + +} diff --git a/Source/Asura.Engine/Math/Vector3.cpp b/Source/Asura.Engine/Math/Transform.cpp index e69de29..e69de29 100644 --- a/Source/Asura.Engine/Math/Vector3.cpp +++ b/Source/Asura.Engine/Math/Transform.cpp diff --git a/Source/Asura.Engine/Math/Transform.h b/Source/Asura.Engine/Math/Transform.h new file mode 100644 index 0000000..aafb66c --- /dev/null +++ b/Source/Asura.Engine/Math/Transform.h @@ -0,0 +1,30 @@ +#ifndef __ASURA_ENGINE_TRANSFORM_H__ +#define __ASURA_ENGINE_TRANSFORM_H__ + +#include "Object.h" + +namespace AsuraEngine +{ + namespace Math + { + + class Transform : virtual public Object + { + public: + + void Set(float x, float y, float sx, float sy, float ox, float oy, float r); + + void LoadIdentity(); + + void Move(float dx = 0, float dy = 0); + void Rotate(float r); + void Scale(float sx, float sy); + + float m[16]; //4x4 matrix + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Math/Vector2.h b/Source/Asura.Engine/Math/Vector2.h deleted file mode 100644 index 875cef3..0000000 --- a/Source/Asura.Engine/Math/Vector2.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __ASURA_ENGINE_VECTOR2_H__ -#define __ASURA_ENGINE_VECTOR2_H__ - -namespace AsuraEngine -{ - namespace Math - { - - class Vector2 - { - - }; - - } -} - -#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Math/Vector2.hpp b/Source/Asura.Engine/Math/Vector2.hpp new file mode 100644 index 0000000..df78255 --- /dev/null +++ b/Source/Asura.Engine/Math/Vector2.hpp @@ -0,0 +1,70 @@ +#ifndef __ASURA_ENGINE_VECTOR2_H__ +#define __ASURA_ENGINE_VECTOR2_H__ + +namespace AsuraEngine +{ + namespace Math + { + template <typename T> + class Vector2 + { + public: + Vector2(); + Vector2(T X, T Y); + + template <typename U> + explicit Vector2(const Vector2<U>& vector); + + Set(T X, T Y); + + T x; ///< X coordinate of the vector + T y; ///< Y coordinate of the vector + }; + + template <typename T> + Vector2<T> operator -(const Vector2<T>& right); + + template <typename T> + Vector2<T>& operator +=(Vector2<T>& left, const Vector2<T>& right); + + template <typename T> + Vector2<T>& operator -=(Vector2<T>& left, const Vector2<T>& right); + + template <typename T> + Vector2<T> operator +(const Vector2<T>& left, const Vector2<T>& right); + + template <typename T> + Vector2<T> operator -(const Vector2<T>& left, const Vector2<T>& right); + + template <typename T> + Vector2<T> operator *(const Vector2<T>& left, T right); + + template <typename T> + Vector2<T> operator *(T left, const Vector2<T>& right); + + template <typename T> + Vector2<T>& operator *=(Vector2<T>& left, T right); + + template <typename T> + Vector2<T> operator /(const Vector2<T>& left, T right); + + template <typename T> + Vector2<T>& operator /=(Vector2<T>& left, T right); + + template <typename T> + bool operator ==(const Vector2<T>& left, const Vector2<T>& right); + + template <typename T> + bool operator !=(const Vector2<T>& left, const Vector2<T>& right); + +#include "Vector2.inl" + + // Define the most common types + typedef Vector2<int> Vector2i; + typedef Vector2<unsigned int> Vector2u; + typedef Vector2<float> Vector2f; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Math/Vector2.inl b/Source/Asura.Engine/Math/Vector2.inl new file mode 100644 index 0000000..9e131a7 --- /dev/null +++ b/Source/Asura.Engine/Math/Vector2.inl @@ -0,0 +1,114 @@ +template <typename T> +inline Vector2<T>::Vector2() : + x(0), + y(0) +{ + +} + +template <typename T> +inline Vector2<T>::Vector2(T X, T Y) : + x(X), + y(Y) +{ + +} + +template <typename T> +template <typename U> +inline Vector2<T>::Vector2(const Vector2<U>& vector) : + x(static_cast<T>(vector.x)), + y(static_cast<T>(vector.y)) +{ +} + +template <typename T> +inline Vector2<T>::Set(T X, T Y) +{ + x = X; + y = Y; +} + +template <typename T> +inline Vector2<T> operator -(const Vector2<T>& right) +{ + return Vector2<T>(-right.x, -right.y); +} + +template <typename T> +inline Vector2<T>& operator +=(Vector2<T>& left, const Vector2<T>& right) +{ + left.x += right.x; + left.y += right.y; + + return left; +} + +template <typename T> +inline Vector2<T>& operator -=(Vector2<T>& left, const Vector2<T>& right) +{ + left.x -= right.x; + left.y -= right.y; + + return left; +} + +template <typename T> +inline Vector2<T> operator +(const Vector2<T>& left, const Vector2<T>& right) +{ + return Vector2<T>(left.x + right.x, left.y + right.y); +} + +template <typename T> +inline Vector2<T> operator -(const Vector2<T>& left, const Vector2<T>& right) +{ + return Vector2<T>(left.x - right.x, left.y - right.y); +} + +template <typename T> +inline Vector2<T> operator *(const Vector2<T>& left, T right) +{ + return Vector2<T>(left.x * right, left.y * right); +} + +template <typename T> +inline Vector2<T> operator *(T left, const Vector2<T>& right) +{ + return Vector2<T>(right.x * left, right.y * left); +} + +template <typename T> +inline Vector2<T>& operator *=(Vector2<T>& left, T right) +{ + left.x *= right; + left.y *= right; + + return left; +} + +template <typename T> +inline Vector2<T> operator /(const Vector2<T>& left, T right) +{ + return Vector2<T>(left.x / right, left.y / right); +} + +template <typename T> +inline Vector2<T>& operator /=(Vector2<T>& left, T right) +{ + left.x /= right; + left.y /= right; + + return left; +} + +template <typename T> +inline bool operator ==(const Vector2<T>& left, const Vector2<T>& right) +{ + return (left.x == right.x) && (left.y == right.y); +} + +template <typename T> +inline bool operator !=(const Vector2<T>& left, const Vector2<T>& right) +{ + return (left.x != right.x) || (left.y != right.y); +} diff --git a/Source/Asura.Engine/Math/Vector3.h b/Source/Asura.Engine/Math/Vector3.h deleted file mode 100644 index f0e48e4..0000000 --- a/Source/Asura.Engine/Math/Vector3.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __ASURA_ENGINE_VECTOR3_H__ -#define __ASURA_ENGINE_VECTOR3_H__ - -namespace AsuraEngine -{ - namespace Math - { - - class Vector3 - { - - }; - - } -} - -#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Math/Vector3.hpp b/Source/Asura.Engine/Math/Vector3.hpp new file mode 100644 index 0000000..2b23406 --- /dev/null +++ b/Source/Asura.Engine/Math/Vector3.hpp @@ -0,0 +1,233 @@ +#ifndef __ASURA_ENGINE_VECTOR3_H__ +#define __ASURA_ENGINE_VECTOR3_H__ + +namespace AsuraEngine +{ + namespace Math + { + template <typename T> + class Vector3 + { + public: + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates a Vector3(0, 0, 0). + /// + //////////////////////////////////////////////////////////// + Vector3(); + + //////////////////////////////////////////////////////////// + /// \brief Construct the vector from its coordinates + /// + /// \param X X coordinate + /// \param Y Y coordinate + /// \param Z Z coordinate + /// + //////////////////////////////////////////////////////////// + Vector3(T X, T Y, T Z); + + //////////////////////////////////////////////////////////// + /// \brief Construct the vector from another type of vector + /// + /// This constructor doesn't replace the copy constructor, + /// it's called only when U != T. + /// A call to this constructor will fail to compile if U + /// is not convertible to T. + /// + /// \param vector Vector to convert + /// + //////////////////////////////////////////////////////////// + template <typename U> + explicit Vector3(const Vector3<U>& vector); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + T x; ///< X coordinate of the vector + T y; ///< Y coordinate of the vector + T z; ///< Z coordinate of the vector + }; + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of unary operator - + /// + /// \param left Vector to negate + /// + /// \return Memberwise opposite of the vector + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector3<T> operator -(const Vector3<T>& left); + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of binary operator += + /// + /// This operator performs a memberwise addition of both vectors, + /// and assigns the result to \a left. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return Reference to \a left + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector3<T>& operator +=(Vector3<T>& left, const Vector3<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of binary operator -= + /// + /// This operator performs a memberwise subtraction of both vectors, + /// and assigns the result to \a left. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return Reference to \a left + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector3<T>& operator -=(Vector3<T>& left, const Vector3<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of binary operator + + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return Memberwise addition of both vectors + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector3<T> operator +(const Vector3<T>& left, const Vector3<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of binary operator - + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return Memberwise subtraction of both vectors + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector3<T> operator -(const Vector3<T>& left, const Vector3<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of binary operator * + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a scalar value) + /// + /// \return Memberwise multiplication by \a right + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector3<T> operator *(const Vector3<T>& left, T right); + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of binary operator * + /// + /// \param left Left operand (a scalar value) + /// \param right Right operand (a vector) + /// + /// \return Memberwise multiplication by \a left + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector3<T> operator *(T left, const Vector3<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of binary operator *= + /// + /// This operator performs a memberwise multiplication by \a right, + /// and assigns the result to \a left. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a scalar value) + /// + /// \return Reference to \a left + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector3<T>& operator *=(Vector3<T>& left, T right); + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of binary operator / + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a scalar value) + /// + /// \return Memberwise division by \a right + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector3<T> operator /(const Vector3<T>& left, T right); + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of binary operator /= + /// + /// This operator performs a memberwise division by \a right, + /// and assigns the result to \a left. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a scalar value) + /// + /// \return Reference to \a left + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector3<T>& operator /=(Vector3<T>& left, T right); + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of binary operator == + /// + /// This operator compares strict equality between two vectors. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return True if \a left is equal to \a right + /// + //////////////////////////////////////////////////////////// + template <typename T> + bool operator ==(const Vector3<T>& left, const Vector3<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector3 + /// \brief Overload of binary operator != + /// + /// This operator compares strict difference between two vectors. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return True if \a left is not equal to \a right + /// + //////////////////////////////////////////////////////////// + template <typename T> + bool operator !=(const Vector3<T>& left, const Vector3<T>& right); + +#include "Vector3.inl" + + // Define the most common types + typedef Vector3<int> Vector3i; + typedef Vector3<float> Vector3f; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Math/Vector3.inl b/Source/Asura.Engine/Math/Vector3.inl new file mode 100644 index 0000000..3a2aa93 --- /dev/null +++ b/Source/Asura.Engine/Math/Vector3.inl @@ -0,0 +1,145 @@ + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T>::Vector3() : + x(0), + y(0), + z(0) +{ + +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T>::Vector3(T X, T Y, T Z) : + x(X), + y(Y), + z(Z) +{ + +} + + +//////////////////////////////////////////////////////////// +template <typename T> +template <typename U> +inline Vector3<T>::Vector3(const Vector3<U>& vector) : + x(static_cast<T>(vector.x)), + y(static_cast<T>(vector.y)), + z(static_cast<T>(vector.z)) +{ +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T> operator -(const Vector3<T>& left) +{ + return Vector3<T>(-left.x, -left.y, -left.z); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T>& operator +=(Vector3<T>& left, const Vector3<T>& right) +{ + left.x += right.x; + left.y += right.y; + left.z += right.z; + + return left; +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T>& operator -=(Vector3<T>& left, const Vector3<T>& right) +{ + left.x -= right.x; + left.y -= right.y; + left.z -= right.z; + + return left; +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T> operator +(const Vector3<T>& left, const Vector3<T>& right) +{ + return Vector3<T>(left.x + right.x, left.y + right.y, left.z + right.z); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T> operator -(const Vector3<T>& left, const Vector3<T>& right) +{ + return Vector3<T>(left.x - right.x, left.y - right.y, left.z - right.z); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T> operator *(const Vector3<T>& left, T right) +{ + return Vector3<T>(left.x * right, left.y * right, left.z * right); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T> operator *(T left, const Vector3<T>& right) +{ + return Vector3<T>(right.x * left, right.y * left, right.z * left); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T>& operator *=(Vector3<T>& left, T right) +{ + left.x *= right; + left.y *= right; + left.z *= right; + + return left; +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T> operator /(const Vector3<T>& left, T right) +{ + return Vector3<T>(left.x / right, left.y / right, left.z / right); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector3<T>& operator /=(Vector3<T>& left, T right) +{ + left.x /= right; + left.y /= right; + left.z /= right; + + return left; +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline bool operator ==(const Vector3<T>& left, const Vector3<T>& right) +{ + return (left.x == right.x) && (left.y == right.y) && (left.z == right.z); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline bool operator !=(const Vector3<T>& left, const Vector3<T>& right) +{ + return (left.x != right.x) || (left.y != right.y) || (left.z != right.z); +} diff --git a/Source/Asura.Engine/Math/Vector4.cpp b/Source/Asura.Engine/Math/Vector4.cpp deleted file mode 100644 index e69de29..0000000 --- a/Source/Asura.Engine/Math/Vector4.cpp +++ /dev/null diff --git a/Source/Asura.Engine/Math/Vector4.h b/Source/Asura.Engine/Math/Vector4.h index 3739f83..13a9d8a 100644 --- a/Source/Asura.Engine/Math/Vector4.h +++ b/Source/Asura.Engine/Math/Vector4.h @@ -5,12 +5,229 @@ namespace AsuraEngine { namespace Math { - + template <typename T> class Vector4 { + public: + + //////////////////////////////////////////////////////////// + /// \brief Default constructor + /// + /// Creates a Vector4(0, 0, 0). + /// + //////////////////////////////////////////////////////////// + Vector4(); + + //////////////////////////////////////////////////////////// + /// \brief Construct the vector from its coordinates + /// + /// \param X X coordinate + /// \param Y Y coordinate + /// \param Z Z coordinate + /// + //////////////////////////////////////////////////////////// + Vector4(T X, T Y, T Z, T W); + //////////////////////////////////////////////////////////// + /// \brief Construct the vector from another type of vector + /// + /// This constructor doesn't replace the copy constructor, + /// it's called only when U != T. + /// A call to this constructor will fail to compile if U + /// is not convertible to T. + /// + /// \param vector Vector to convert + /// + //////////////////////////////////////////////////////////// + template <typename U> + explicit Vector4(const Vector4<U>& vector); + + //////////////////////////////////////////////////////////// + // Member data + //////////////////////////////////////////////////////////// + T x; ///< X coordinate of the vector + T y; ///< Y coordinate of the vector + T z; ///< Z coordinate of the vector + T w; ///< W coordinate of the vector }; + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of unary operator - + /// + /// \param left Vector to negate + /// + /// \return Memberwise opposite of the vector + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector4<T> operator -(const Vector4<T>& left); + + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of binary operator += + /// + /// This operator performs a memberwise addition of both vectors, + /// and assigns the result to \a left. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return Reference to \a left + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector4<T>& operator +=(Vector4<T>& left, const Vector4<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of binary operator -= + /// + /// This operator performs a memberwise subtraction of both vectors, + /// and assigns the result to \a left. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return Reference to \a left + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector4<T>& operator -=(Vector4<T>& left, const Vector4<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of binary operator + + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return Memberwise addition of both vectors + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector4<T> operator +(const Vector4<T>& left, const Vector4<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of binary operator - + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return Memberwise subtraction of both vectors + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector4<T> operator -(const Vector4<T>& left, const Vector4<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of binary operator * + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a scalar value) + /// + /// \return Memberwise multiplication by \a right + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector4<T> operator *(const Vector4<T>& left, T right); + + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of binary operator * + /// + /// \param left Left operand (a scalar value) + /// \param right Right operand (a vector) + /// + /// \return Memberwise multiplication by \a left + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector4<T> operator *(T left, const Vector4<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of binary operator *= + /// + /// This operator performs a memberwise multiplication by \a right, + /// and assigns the result to \a left. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a scalar value) + /// + /// \return Reference to \a left + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector4<T>& operator *=(Vector4<T>& left, T right); + + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of binary operator / + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a scalar value) + /// + /// \return Memberwise division by \a right + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector4<T> operator /(const Vector4<T>& left, T right); + + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of binary operator /= + /// + /// This operator performs a memberwise division by \a right, + /// and assigns the result to \a left. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a scalar value) + /// + /// \return Reference to \a left + /// + //////////////////////////////////////////////////////////// + template <typename T> + Vector4<T>& operator /=(Vector4<T>& left, T right); + + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of binary operator == + /// + /// This operator compares strict equality between two vectors. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return True if \a left is equal to \a right + /// + //////////////////////////////////////////////////////////// + template <typename T> + bool operator ==(const Vector4<T>& left, const Vector4<T>& right); + + //////////////////////////////////////////////////////////// + /// \relates Vector4 + /// \brief Overload of binary operator != + /// + /// This operator compares strict difference between two vectors. + /// + /// \param left Left operand (a vector) + /// \param right Right operand (a vector) + /// + /// \return True if \a left is not equal to \a right + /// + //////////////////////////////////////////////////////////// + template <typename T> + bool operator !=(const Vector4<T>& left, const Vector4<T>& right); + +#include "Vector4.inl" + + // Define the most common types + typedef Vector4<int> Vector4i; + typedef Vector4<float> Vector4f; + } } diff --git a/Source/Asura.Engine/Math/Vector4.inl b/Source/Asura.Engine/Math/Vector4.inl new file mode 100644 index 0000000..025bfcc --- /dev/null +++ b/Source/Asura.Engine/Math/Vector4.inl @@ -0,0 +1,152 @@ + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T>::Vector4() : + x(0), + y(0), + z(0), + w(0) +{ + +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T>::Vector4(T X, T Y, T Z) : + x(X), + y(Y), + z(Z), + w(0) +{ + +} + + +//////////////////////////////////////////////////////////// +template <typename T> +template <typename U> +inline Vector4<T>::Vector4(const Vector4<U>& vector) : + x(static_cast<T>(vector.x)), + y(static_cast<T>(vector.y)), + z(static_cast<T>(vector.z)) + w(static_cast<T>(vector.w)) +{ +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T> operator -(const Vector4<T>& left) +{ + return Vector4<T>(-left.x, -left.y, -left.z, -left.w); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T>& operator +=(Vector4<T>& left, const Vector4<T>& right) +{ + left.x += right.x; + left.y += right.y; + left.z += right.z; + left.w += right.w; + + return left; +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T>& operator -=(Vector4<T>& left, const Vector4<T>& right) +{ + left.x -= right.x; + left.y -= right.y; + left.z -= right.z; + left.w -= right.w; + + return left; +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T> operator +(const Vector4<T>& left, const Vector4<T>& right) +{ + return Vector4<T>(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T> operator -(const Vector4<T>& left, const Vector4<T>& right) +{ + return Vector4<T>(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T> operator *(const Vector4<T>& left, T right) +{ + return Vector4<T>(left.x * right, left.y * right, left.z * right, left.w * right); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T> operator *(T left, const Vector4<T>& right) +{ + return Vector4<T>(right.x * left, right.y * left, right.z * left, right.w * left); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T>& operator *=(Vector4<T>& left, T right) +{ + left.x *= right; + left.y *= right; + left.z *= right; + left.w *= right; + + return left; +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T> operator /(const Vector4<T>& left, T right) +{ + return Vector4<T>(left.x / right, left.y / right, left.z / right, left.w / right); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline Vector4<T>& operator /=(Vector4<T>& left, T right) +{ + left.x /= right; + left.y /= right; + left.z /= right; + left.w /= right; + + return left; +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline bool operator ==(const Vector4<T>& left, const Vector4<T>& right) +{ + return (left.x == right.x) && (left.y == right.y) && (left.z == right.z) && (left.w == right.w); +} + + +//////////////////////////////////////////////////////////// +template <typename T> +inline bool operator !=(const Vector4<T>& left, const Vector4<T>& right) +{ + return (left.x != right.x) || (left.y != right.y) || (left.z != right.z) || (left.w != right.w); +} diff --git a/Source/Asura.Engine/Object.h b/Source/Asura.Engine/Object.h index 4f00d64..352a61b 100644 --- a/Source/Asura.Engine/Object.h +++ b/Source/Asura.Engine/Object.h @@ -22,9 +22,12 @@ public: static void Release(Object* obj); + void Retain(); + void Release(); + private: - int mRC; // ߳ + unsigned int mRC; // ü }; diff --git a/Source/Asura.Engine/Scripting/Luax.hpp b/Source/Asura.Engine/Scripting/Luax.hpp index 5d01997..e1407ee 100644 --- a/Source/Asura.Engine/Scripting/Luax.hpp +++ b/Source/Asura.Engine/Scripting/Luax.hpp @@ -4,9 +4,13 @@ /// /// Scripting with Lua. /// - #include "Lua51/lua.h" #include "Lua51/lauxlib.h" #include "Luax/luax.h" +/// +/// ¶luapoartable +/// +#include "Portable.h" + #endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Scripting/Portable.h b/Source/Asura.Engine/Scripting/Portable.h new file mode 100644 index 0000000..ae36cc2 --- /dev/null +++ b/Source/Asura.Engine/Scripting/Portable.h @@ -0,0 +1,23 @@ +#ifndef __ASURA_ENGINE_PORTABLE_H__ +#define __ASURA_ENGINE_PORTABLE_H__ + +#include "Object.h" + +namespace AsuraEngine +{ + namespace Scripting + { + + class Portable : virtual public Object + { + public: + + Portable(); + virtual ~Portable(); + + }; + + } +} + +#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/SimClass.h b/Source/Asura.Engine/SimClass.h deleted file mode 100644 index 5901fd5..0000000 --- a/Source/Asura.Engine/SimClass.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef __ASURA_ENGINE_SIMCLASS_H__ -#define __ASURA_ENGINE_SIMCLASS_H__ - -namespace AsuraEngine -{ - - /// - /// - /// - class SimClass - { - public: - - SimClass() {}; - - virtual ~SimClass() {}; - - }; - -} - -#endif
\ No newline at end of file diff --git a/Source/Asura.Engine/Type.h b/Source/Asura.Engine/Type.h index 5159ee2..a956a4b 100644 --- a/Source/Asura.Engine/Type.h +++ b/Source/Asura.Engine/Type.h @@ -8,7 +8,7 @@ namespace AsuraEngine typedef int8_t int8; typedef uint8_t uint8; - typedef uint8 byte; + typedef uint8 byte; typedef int16_t int16; typedef uint16_t uint16; typedef int32_t int32; diff --git a/Source/Asura.Engine/Window/Window.cpp b/Source/Asura.Engine/Window/Window.cpp index e69de29..5e800e0 100644 --- a/Source/Asura.Engine/Window/Window.cpp +++ b/Source/Asura.Engine/Window/Window.cpp @@ -0,0 +1,19 @@ +#include "Window.h" + +namespace AsuraEngine +{ + namespace Graphics + { + + Window::Window() + { + + } + + Window::~Window() + { + + } + + } +} diff --git a/Source/Asura.Engine/Window/Window.h b/Source/Asura.Engine/Window/Window.h index 4e16e79..3456d75 100644 --- a/Source/Asura.Engine/Window/Window.h +++ b/Source/Asura.Engine/Window/Window.h @@ -1,13 +1,20 @@ #ifndef __ASURA_ENGINE_WINDOW_H__ #define __ASURA_ENGINE_WINDOW_H__ +#include "SDL2/Sdl.h" #include "Graphics/RenderTarget.h" +#include "Math/Vector2.hpp" namespace AsuraEngine { namespace Graphics { + enum WindowStyle + { + + }; + /// /// ڣֶ֧രڡڱ༭Ҫ֧֣runnerֻҪһڡ /// @@ -19,8 +26,35 @@ namespace AsuraEngine ~Window(); + SDL_Window* GetSDLHandle(); + + void SetSize(uint width, uint height); + + void SetPosition(int x, int y); + + void SetTitle(const std::string& title); + + void SetWindowStyle(WindowStyle style); + + void Show(); + + void Hide(); + + /// + /// ǿ˫ĴڣҪչʾǰ̨ + /// + void SwapRenderBuffer(); + private: + /// + /// SDL window handle. + /// + SDL_Window* mWindowHandle; + + Math::Vector2i mPosition; + + Math::Vector2i mSize; }; diff --git a/Source/Asura.Framework/scripts/graphics/shader.lua b/Source/Asura.Framework/scripts/graphics/shader.lua index 86ed8bb..1bbf1fa 100644 --- a/Source/Asura.Framework/scripts/graphics/shader.lua +++ b/Source/Asura.Framework/scripts/graphics/shader.lua @@ -12,7 +12,11 @@ end --编译shader function Shader.Load(self, vert, frag) self.uniforms = {} - self.simShader = AsuraEngine.SimShader.New(vert, frag) + if self.simShader == nil then + self.simShader = AsuraEngine.SimShader.New(vert, frag) + else + self.simShader:Load(vert, frag) + end if self.simShader == nil then --shader编译错误 return diff --git a/Source/Asura.Runner/main.cpp b/Source/Asura.Runner/main.cpp index 2501188..96b17bb 100644 --- a/Source/Asura.Runner/main.cpp +++ b/Source/Asura.Runner/main.cpp @@ -1,8 +1,11 @@ -#include "SDl2/SDL.h" +#include "Asura.h" // ϷᱻһԴļrunnerȡݣϷ // RunnerֻܶȡpackerϷļeditorֱϷassetsRunnerһСл int main() { + + Applicatoin runner; + runner.Init(ASURAMODULE_ALL); }
\ No newline at end of file |